* @license GPLv3 * @link https://github.com/josecl/cool-php-captcha * @package captcha * @version 0.3.2 * */ session_start(); $captcha = new SimpleCaptcha(); $captcha->blur = true; $captcha->wordsFile = null; // OPTIONAL Change configuration... //$captcha->wordsFile = 'words/es.php'; //$captcha->session_var = 'secretword'; //$captcha->imageFormat = 'png'; //$captcha->lineWidth = 3; //$captcha->scale = 3; $captcha->blur = true; //$captcha->resourcesPath = "/var/cool-php-captcha/resources"; // OPTIONAL Simple autodetect language example /* if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $langs = array('en', 'es'); $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); if (in_array($lang, $langs)) { $captcha->wordsFile = "words/$lang.php"; } } */ // Image generation $captcha->CreateImage(); /** * SimpleCaptcha class * */ class SimpleCaptcha { /** * Difficulty level (if used, keep 'Wave configuracion' on default) * normal = 1 * closer to 0 is more easy, for ex: 0.4 * closer to 2 is more hard, for ex: 1.8 * smaller then 0 and bigger than 2 is caped to min or max */ public $difficulty = 1.65; /** Width of the image */ public $width = 150; /** Height of the image */ public $height = 51; /** Dictionary word file (empty for random text) */ public $wordsFile = ''; /** * Path for resource files (fonts, words, etc.) * * "resources" by default. For security reasons, is better move this * directory to another location outise the web server * */ public $resourcesPath = 'resources'; /** Min word length (for non-dictionary random text generation) */ public $minWordLength = 6; /** * Max word length (for non-dictionary random text generation) * * Used for dictionary words indicating the word-length * for font-size modification purposes */ public $maxWordLength = 7; /** Sessionname to store the original text */ public $session_var = 'dig_captcha'; /** Background color in RGB-array */ public $backgroundColor = array(255, 255, 255); /** Foreground colors in RGB-array */ public $colors = array( array(27,78,181), // blue array(22,163,35), // green array(214,36,7), // red ); /** Shadow color in RGB-array or null */ public $shadowColor = null; //array(0, 0, 0); /** Horizontal line through the text */ public $lineWidth = 1; /** * Font configuration * * - font: TTF file * - spacing: relative pixel space between character * - minSize: min font size * - maxSize: max font size */ public $fonts = array( 'Antykwa' => array('spacing' => -3, 'minSize' => 27, 'maxSize' => 30, 'font' => 'AntykwaBold.ttf'), 'Candice' => array('spacing' =>-1.5,'minSize' => 28, 'maxSize' => 31, 'font' => 'Candice.ttf'), 'DingDong' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 30, 'font' => 'Ding-DongDaddyO.ttf'), 'Duality' => array('spacing' => -2, 'minSize' => 30, 'maxSize' => 38, 'font' => 'Duality.ttf'), 'Heineken' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 34, 'font' => 'Heineken.ttf'), 'Jura' => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 32, 'font' => 'Jura.ttf'), 'StayPuft' => array('spacing' =>-1.5,'minSize' => 28, 'maxSize' => 32, 'font' => 'StayPuft.ttf'), 'Times' => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 34, 'font' => 'TimesNewRomanBold.ttf'), 'VeraSans' => array('spacing' => -1, 'minSize' => 20, 'maxSize' => 28, 'font' => 'VeraSansBold.ttf'), ); /** Wave configuracion in X and Y axes */ public $Yperiod = 12; public $Yamplitude = 14; public $Xperiod = 11; public $Xamplitude = 5; /** letter rotation clockwise */ public $maxRotation = 8; /** * Internal image size factor (for better image quality) * 1: low, 2: medium, 3: high */ public $scale = 1.8; /** * Blur effect for better image quality (but slower image processing). * Better image results with scale=3 */ public $blur = false; /** Debug? */ public $debug = false; /** Image format: jpeg or png */ public $imageFormat = 'png'; /** GD image */ public $im; public function __construct($config = array()) { } public function CreateImage() { $ini = microtime(true); // cap difficulty if($this->difficulty >2) $this->difficulty = 2; if($this->difficulty<=0) $this->difficulty = 0.1; /** Initialization */ $this->ImageAllocate(); /** Text insertion */ $text = $this->GetCaptchaText(); $fontcfg = $this->fonts[array_rand($this->fonts)]; $this->WriteText($text, $fontcfg); $ses = filter_var($_GET['r'],FILTER_SANITIZE_NUMBER_FLOAT); if(isset($_GET['pr'])) { $prev_ses = filter_var($_GET['pr'], FILTER_SANITIZE_NUMBER_FLOAT); if (isset($_SESSION[$this->session_var . $prev_ses])) unset($_SESSION[$this->session_var . $prev_ses]); } $_SESSION[$this->session_var.$ses] = $text; /** Transformations */ if (!empty($this->lineWidth)) { $this->WriteLine(); } //$this->WaveImage(); if ($this->blur && function_exists('imagefilter')) { imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR); } //$this->ReduceImage(); if ($this->debug) { imagestring($this->im, 1, 1, $this->height-8, "$text {$fontcfg['font']} ".round((microtime(true)-$ini)*1000)."ms", $this->GdFgColor ); } /** Output */ $this->WriteImage(); $this->Cleanup(); } /** * Creates the image resources */ protected function ImageAllocate() { // Cleanup if (!empty($this->im)) { imagedestroy($this->im); } $this->im = imagecreatetruecolor($this->width*$this->scale, $this->height*$this->scale); // Background color $this->GdBgColor = imagecolorallocatealpha($this->im, $this->backgroundColor[0], $this->backgroundColor[1], $this->backgroundColor[2],127 ); imagealphablending($this->im, false); imagesavealpha($this->im, true); imagefilledrectangle($this->im, 0, 0, $this->width * $this->scale, $this->height * $this->scale, $this->GdBgColor); imagealphablending($this->im, true); // Foreground color $color = $this->colors[mt_rand(0, sizeof($this->colors) - 1)]; $this->GdFgColor = imagecolorallocatealpha($this->im, $color[0], $color[1], $color[2], 30); // Shadow color if (!empty($this->shadowColor) && is_array($this->shadowColor) && sizeof($this->shadowColor) >= 3) { $this->GdShadowColor = imagecolorallocate($this->im, $this->shadowColor[0], $this->shadowColor[1], $this->shadowColor[2] ); } } /** * Text generation * * @return string Text */ protected function GetCaptchaText() { $text = $this->GetDictionaryCaptchaText(); if (!$text) { $text = $this->GetRandomCaptchaText(); } return $text; } /** * Random dictionary word generation * * @param boolean $extended Add extended "fake" words * @return string Word */ function GetDictionaryCaptchaText($extended = false) { if (empty($this->wordsFile)) { return false; } // Full path of words file if (substr($this->wordsFile, 0, 1) == '/') { $wordsfile = $this->wordsFile; } else { $wordsfile = $this->resourcesPath.'/'.$this->wordsFile; } if (!file_exists($wordsfile)) { return false; } $fp = fopen($wordsfile, "r"); $length = strlen(fgets($fp)); if (!$length) { return false; } $line = rand(1, (filesize($wordsfile)/$length)-2); if (fseek($fp, $length*$line) == -1) { return false; } $text = trim(fgets($fp)); fclose($fp); /** Change ramdom volcals */ if ($extended) { $text = preg_split('//', $text, -1, PREG_SPLIT_NO_EMPTY); $vocals = array('a', 'e', 'i', 'o', 'u'); foreach ($text as $i => $char) { if (mt_rand(0, 1) && in_array($char, $vocals)) { $text[$i] = $vocals[mt_rand(0, 4)]; } } $text = implode('', $text); } return $text; } /** * Random text generation * * @return string Text */ protected function GetRandomCaptchaText($length = null) { if (empty($length)) { $length = rand($this->minWordLength, $this->maxWordLength); } $words = "abcdefghijlmnopqrstvwyz"; $vocals = "aeiou"; $text = ""; $vocal = rand(0, 1); for ($i=0; $i<$length; $i++) { if ($vocal) { $text .= substr($vocals, mt_rand(0, 4), 1); } else { $text .= substr($words, mt_rand(0, 22), 1); } $vocal = !$vocal; } return $text; } /** * Text insertion */ protected function WriteText($text, $fontcfg = array()) { if (empty($fontcfg)) { // Select the font configuration $fontcfg = $this->fonts[array_rand($this->fonts)]; } // Full path of font file $fontfile = $this->resourcesPath.'/fonts/'.$fontcfg['font']; /** Increase font-size for shortest words: 9% for each glyp missing */ $lettersMissing = $this->maxWordLength-strlen($text); $fontSizefactor = 1+($lettersMissing*0.09); // Text generation (char by char) $x = 20*$this->scale; $y = round(($this->height*27/40)*$this->scale); $length = strlen($text); for ($i=0; $i<$length; $i++) { $degree = rand($this->maxRotation*-1, $this->maxRotation)*$this->difficulty; $fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize'])*$this->scale*$fontSizefactor; $letter = substr($text, $i, 1); if ($this->shadowColor) { $coords = imagettftext($this->im, $fontsize, $degree, $x+$this->scale, $y+$this->scale, $this->GdShadowColor, $fontfile, $letter); } $coords = imagettftext($this->im, $fontsize, $degree, $x, $y, $this->GdFgColor, $fontfile, $letter); $x += ($coords[2]-$x) + ($fontcfg['spacing']*$this->scale); } $this->textFinalX = $x; } /** * Horizontal line insertion */ protected function WriteLine() { $x1 = $this->width*$this->scale*.15; $x2 = $this->textFinalX; $y1 = rand($this->height*$this->scale*.40, $this->height*$this->scale*.65); $y2 = rand($this->height*$this->scale*.40, $this->height*$this->scale*.65); $width = $this->lineWidth/2*$this->scale; for ($i = $width*-1; $i <= $width; $i++) { imageline($this->im, $x1, $y1+$i, $x2, $y2+$i, $this->GdFgColor); } } /** * File generation */ protected function WriteImage() { if ($this->imageFormat == 'png' && function_exists('imagepng')) { header("Content-type: image/png"); header("Expires: 0"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); imagepng($this->im); } else { header("Content-type: image/jpeg"); header("Expires: 0"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); imagejpeg($this->im, null, 80); } } /** * Cleanup */ protected function Cleanup() { imagedestroy($this->im); } /** * Wave filter */ protected function WaveImage() { // create wave difficulty $wdf = 1; if($this->difficulty<1) $wdf = 1/$this->difficulty*(0.9/$this->difficulty); if($this->difficulty>1) $wdf = (1/($this->difficulty*1.7))+0.5; // X-axis wave generation $xp = $this->scale*$this->Xperiod*rand(1,3) * $wdf; $k = rand(1, 100); for ($i = 0; $i < ($this->width*$this->scale); $i++) { imagecopy($this->im, $this->im, $i-1, sin($k+$i/$xp) * ($this->scale*$this->Xamplitude), $i, 0, 1, $this->height*$this->scale); } // Y-axis wave generation $k = rand(0, 100); $yp = $this->scale*($this->Yperiod)*rand(1,2) * $wdf; for ($i = 0; $i < ($this->height*$this->scale); $i++) { imagecopy($this->im, $this->im, sin($k+$i/$yp) * ($this->scale*$this->Yamplitude), $i-1, 0, $i, $this->width*$this->scale, 1); } } /** * Reduce the image to the final size */ protected function ReduceImage() { // Reduzco el tamao de la imagen $imResampled = imagecreatetruecolor($this->width, $this->height); imagecopyresampled($imResampled, $this->im, 0, 0, 0, 0, $this->width, $this->height, $this->width*$this->scale, $this->height*$this->scale ); imagedestroy($this->im); $this->im = $imResampled; } } ?>