Captcha from scratch (PHP / GD)

2010-08-11 07:51 by Ian

/**************************************************************************
* Author:    J. Ian Lindsay                                               *
* File:      captcha.php                                                  *
*                                                                         *
* Generates a captcha and updates the session. Ensures that the captcha   *
* is readable and recurses if it isn't.                                   *
**************************************************************************/
require_once("include/session.php");

define('RENDER_WIDTH',  350);                           // How big an image to generate?
define('RENDER_HEIGHT', 80);
define('TEXTURE_PATH',  "include/escher_grid_ps.jpg");  // File to draw background noise from.
define('ANGLE_DEV', 25);                                // Maximum deviation from horizontal, in degrees.
define('FONT_PATH', "include/courbd.ttf");              // Font to write the captcha text with.
define('TEXT_SIZE', 20);                                // Pixel height of the text.
define('DICT_PATH', "include/8-letter-words.txt");      // Path to the dictionary.
define('C_RANGE_LOW',   0);                             // The darkest any random color channel can be.
define('C_RANGE_HIGH',  190);                           // The brightest any random color channel can be.

// Return a random word from a comma-delimited dictionary file located at DICT_PATH.
function randomWordFromDict(){
    $read   =   file_get_contents(DICT_PATH);
    $words  =   explode(",", $read);
    return($words[rand(0,count($words)-1)]);
}
    
// Copy a region of the desired size from the source texture file at random. This will prevent
// bots from subtracting a known-texture to remove visual noise.
// If a texture file is  specified that is too small, it will not use a texture, but will return
// an image anyway.
function cropFromTextureFile(){
    $img    =   imagecreatefromjpeg(TEXTURE_PATH);
    $area   =   imagecreatetruecolor(RENDER_WIDTH, RENDER_HEIGHT);
    imagefill($img,0,0,imagecolorallocatealpha($img, 255,255,255,0));
    $tex_width  =   imagesx($img);
    $tex_height =   imagesy($img);
    if(($tex_width > RENDER_WIDTH) && ($tex_height > RENDER_HEIGHT)){
        imagecopy($area, $img, 0, 0, rand(0,$tex_width-RENDER_WIDTH), rand(0,$tex_height-RENDER_HEIGHT), RENDER_WIDTH, RENDER_HEIGHT);
    }
    imagerectangle($area, 0, 0, RENDER_WIDTH - 1, RENDER_HEIGHT - 1, imagecolorallocate($area, 0, 0, 0));
    return($area);
}
    
// Generates a captcha and updates a session variable accordingly.
function generateCaptcha(){
    $img        =   cropFromTextureFile();
    $image_text =   randomWordFromDict();
    $text_color =   imagecolorallocate($img,rand(C_RANGE_LOW,C_RANGE_HIGH),rand(C_RANGE_LOW,C_RANGE_HIGH),rand(C_RANGE_LOW,C_RANGE_HIGH));
    $output     =   imagettftext($img, TEXT_SIZE, (rand((0-ANGLE_DEV),ANGLE_DEV)), rand(0,RENDER_WIDTH-(TEXT_SIZE*strlen($image_text))), rand(TEXT_SIZE,RENDER_HEIGHT-TEXT_SIZE), $text_color,FONT_PATH, $image_text);
    $_SESSION['captcha']    =   $image_text;    // Must happen before recursion.
    if(($output[2] > RENDER_WIDTH) || ($output[4] > RENDER_WIDTH) || (($output[3]+(TEXT_SIZE/2)) > RENDER_HEIGHT) || ($output[5] < (TEXT_SIZE/2))){
        $img    =   generateCaptcha();
    }
    return($img);
}

header("Content-type:image/jpeg");
header("Content-Disposition:inline ; filename=secure.jpg");
imagejpeg(generateCaptcha());

Click the image for a different word.

Previous:
Next: