--- textimage.module	2006-04-12 03:30:25.000000000 -0500
+++ textimage-new.module	2006-04-12 03:28:22.000000000 -0500
@@ -24,7 +24,7 @@
     '#required' => TRUE,
     '#validate' => array('_captcha_validate' => array()),
     '#description' => t('Please type in the letters/numbers that are shown in the image above.'),
-    '#prefix' => '<img src="' . url('_textimage/image/'.time()) . '"  alt="Captcha Image: you will need to recognize the text in it."/>',
+    '#prefix' => '<img src="' . url('_textimage/image/'.time()) . '" '.$imageSize[3].'  alt="Captcha Image: you will need to recognize the text in it."/>',
   );
 
   return $form;
@@ -51,7 +51,8 @@
 
   $suffix = '';
 
-  if (arg(2)!=null) $suffix='/'.arg(2);
+  if (arg(2)!=null) $suffix='/'.arg(2);
+
 
   $items[] = array(
     'path' => '_textimage/image'.$suffix, 'title' => t('textimage'),
@@ -69,7 +70,8 @@
 
 function textimage_settings() {
 	
-	$fonts_path = variable_get("textimage_fonts_path", "");
+	$fonts_path  = variable_get("textimage_fonts_path", "");
+	$images_path = variable_get("textimage_images_path", "");
 	
   //check for GD
   if (!function_exists(imagecreate)) 
@@ -82,12 +84,23 @@
 	//check for valid font path
 	elseif ($fonts_path!="" && !is_dir($fonts_path))
 		drupal_set_message(t('The current font path is invalid. The default font will be used.'));
-
+		
+	//check for valid image path
+	if ($images_path!="" && !is_dir($images_path))
+		drupal_set_message(t('The current images path is invalid. No images will be used.'));
+
+
   //Fonts settings
   $form['fonts'] = array(
                 '#type' => 'fieldset', '#title' => t('Fonts settings'),
                 '#collapsible' => TRUE, '#collapsed' => FALSE
               );
+              
+  $form['images'] = array(
+                '#type' => 'fieldset', '#title' => t('Image settings'),
+                '#collapsible' => TRUE, '#collapsed' => FALSE
+              );
+              
   $form['fonts']['textimage_fonts_path'] = array(
                                   '#type' => 'textfield',
                                   '#title' => t('TrueType Fonts Path'),
@@ -102,9 +115,88 @@
                                   '#default_value' =>  variable_get('captcha_font_size',24),
                                   '#size' => 5,
                                   '#maxlength' => 2,
-                                  '#description' => t('Font size of Captcha text. (Only work with non-default fonts)'),
-                                );
-
+                                  '#description' => t('Font size of Captcha text (in pixels).'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_font_size")),
+                                );
+  $form['fonts']['captcha_char_spacing_max'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Character Spacing'),
+                                  '#default_value' =>  variable_get('captcha_char_spacing_max',10),
+                                  '#size' => 5,
+                                  '#maxlength' => 4,
+                                  '#description' => t('Sets the kerning between letters in Captcha. Higher numbers indicate more spacing.'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_char_spacing_max")),
+                                );
+                                
+  $form['fonts']['captcha_char_jiggle_amount'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Character Jiggle'),
+                                  '#default_value' =>  variable_get('captcha_char_jiggle_amount',5),
+                                  '#size' => 5,
+                                  '#maxlength' => 2,
+                                  '#description' => t('Sets the amount of up and down movement in the Captcha letters. Higher numbers indicate more jiggling.'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_char_jiggle_amount")),
+                                );
+                                
+  $form['fonts']['captcha_char_rotate_amount'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Character Rotation'),
+                                  '#default_value' =>  variable_get('captcha_char_rotate_amount',5),
+                                  '#size' => 5,
+                                  '#maxlength' => 2,
+                                  '#description' => t('Sets the amount of rotation in the Captcha letters (in degrees, only works with non-default fonts).'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_char_rotate_amount")),
+                                );
+                                
+  $form['fonts']['captcha_char_size_amount'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Character Size Adjustment'),
+                                  '#default_value' =>  variable_get('captcha_char_size_amount',2),
+                                  '#size' => 5,
+                                  '#maxlength' => 2,
+                                  '#description' => t('Sets the amount of variation in size between the different letters in the Captcha (in pixels).'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_char_size_amount")),
+                                );
+                                
+  $form['images']['textimage_images_path'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Background Images Path'),
+                                  '#default_value' =>  $images_path,
+                                  '#size' => 30,
+                                  '#maxlength' => 255,
+                                  '#description' => t('Location of the directory where the background images are stored. If you do not provide a directory, solid colors will be used. Relative paths will be resolved relative to the Drupal installation directory.'),
+                                );
+                                
+  $form['images']['captcha_image_noise'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Image Noise (pixels)'),
+                                  '#default_value' =>  variable_get('captcha_image_noise',4),
+                                  '#size' => 5,
+                                  '#maxlength' => 4,
+                                  '#description' => t('Sets the amount of noise (random pixels) in the Captcha image. Higher numbers indicate more noise.'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_image_noise")),
+                                );
+                                
+  $form['images']['captcha_image_lines'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Image Noise (lines)'),
+                                  '#default_value' =>  variable_get('captcha_image_lines',4),
+                                  '#size' => 5,
+                                  '#maxlength' => 4,
+                                  '#description' => t('Sets the amount of noise (random lines) in the Captcha image. Higher numbers indicate more noise.'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_image_lines")),
+                                );
+                                
+  $form['images']['captcha_image_margin'] = array(
+                                  '#type' => 'textfield',
+                                  '#title' => t('Image Margin'),
+                                  '#default_value' =>  variable_get('captcha_image_margin',10),
+                                  '#size' => 5,
+                                  '#maxlength' => 4,
+                                  '#description' => t('Set a distance between the Captcha letters and the edges of the image.'),
+                                  '#validate' => array("_textimage_number_validate" => array("captcha_image_margin")),
+                                );
+
   $form['info'] = array(
                 '#type' => 'fieldset', '#title' => t('Image and font information'),
                 '#collapsible' => TRUE, '#collapsed' => FALSE
@@ -112,6 +204,9 @@
   if (isset($fonts_path)) {
     $imagefontinfo .= t('Number of fonts found: ').count(_textimage_font_list());
   }
+  if (isset($images_path)) {
+    $imagefontinfo .= '<br />'.t('Number of background images found: ').count(_textimage_image_list());
+  }
 
   $gdinfo = gd_info();
   $imagefontinfo .= '<br />'.t('GD Version: ').$gdinfo["GD Version"];
@@ -128,9 +223,18 @@
 }
 
 function textimage_settings_form_validate ($form_id,$form) {
-      //check for valid font path
-      if ($form['textimage_fonts_path'] !="" && !is_dir($form['textimage_fonts_path']))
-        form_set_error('textimage_fonts_path', t('The entered font path is invalid'));
+  //check for valid font path
+  if ($form['textimage_fonts_path'] !="" && !is_dir($form['textimage_fonts_path']))
+    form_set_error('textimage_fonts_path', t('The entered font path is invalid'));
+  //check for valid image path
+  if ($form['textimage_images_path'] !="" && !is_dir($form['textimage_images_path']))
+    form_set_error('textimage_images_path', t('The entered image path is invalid'));
+}
+
+function _textimage_number_validate ($field,$fieldName) {
+  if (!is_numeric($field['#value'])) {
+    form_set_error($fieldName,t("The value for")." ".t($field['#title'])." ".t("must be a number"));
+  }
 }
 
 /**
@@ -138,9 +242,6 @@
 */
 function _textimage_image() {
 
-    // there are a few hard coded functions I'd like to eliminate here,
-    // but for the time being we'll let them be.
-
     //if we don't have GD2 functions, we can't generate the image
     if (!function_exists('imagecreatetruecolor')) return;
 
@@ -154,82 +255,157 @@
 
     $string = _textimage_code();
 
-    // set up image, the first number is the width and the second is the height
-    $im = imagecreatetruecolor(180, 80);
-
-    // creates two variables to store color
-    $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250));
-    $foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80));
-
-    // fill image with bgcolor
-    imagefill($im, 0, 0, $background);
-
     // Get truetype font list
     $fonts = _textimage_font_list();
+    // Get the background images list
+    $images = _textimage_image_list();
 
+    // Randomization amounts:
+    $charSpacingMax   = variable_get('captcha_char_spacing_max',10);   // Letter spacing max (pixels)
+    $charSpacingMin   = max($charSpacingMax*.5,0);                     // Letter spacing minimum (pixels)
+    $charJiggleAmount = variable_get('captcha_char_jiggle_amount',5);  // Up and down randomization (pixels)
+    $charRotateAmount = variable_get('captcha_char_rotate_amount',5);  // Character rotation amount (degrees)
+    $charSizeAmount   = variable_get('captcha_char_size_amount',2);    // Character size amount (pixels)
+
+    $imageRotateAmount = variable_get('captcha_image_rotate_amount',12); // Image rotation amount (degrees)
+    
+    // Static amounts:
+    $charInitialSize   = variable_get('captcha_font_size',24);      // Initial Font 
+    $imageNoise        = variable_get('captcha_image_noise',4);    // Amount of noise added to image
+    $imageLines        = variable_get('captcha_image_lines',4);    // Amount of noise added to image
+    $imageMargin       = variable_get('captcha_image_margin',10);    // Margin around image (pixels)
+      
     // writes string
     if (function_exists(imagettftext) && count($fonts) > 0) {
-
       // write text using a truetype font
-      $charSize = variable_get('captcha_font_size',24);        // font size
-      $charWidth = 0;        // width of previous character
-      $x = 10;               // initial x position
-      $y = 30;
-
-      // iterate over characters
+      
+      // Initialize variables for the loop
+			$characterDetails = array(); // contains the final info about each character
+			      
+      // Build a list of character settings for the captcha string
       for ($i=0;$i<drupal_strlen($string);$i++) {
-        // define angle and position of character based on previous character dimension
-        $x += ($charWidth * rand(1.0, 1.6));
-        $y += rand(-5,5);
-        $charAngle = rand(-5,5);
-        $charSize += rand(-2,2);
+      	// Select our character
         $char = drupal_substr($string,$i,1);
-
-        // select random font
+      
+        // Define angle, size, and font of the character
+        $charAngle = rand(-$charRotateAmount,$charRotateAmount);
+        $charSize  = $charInitialSize + rand(-$charSizeAmount,$charSizeAmount);
         $font = $fonts[rand(0,count($fonts)-1)];
-
-        // draw character
-        imagettftext($im,$charSize,$charAngle,$x,$y,$foreground,$font,$char);
-
-        // capture character dimensions to increment x position
+        
+        // Find height and width of the character
         $bbox = imagettfbbox($charSize,$charAngle,$font,$char);
         $charWidth = max($bbox[0],$bbox[2],$bbox[4],$bbox[6]) - min($bbox[0],$bbox[2],$bbox[4],$bbox[6]);
+        $charHeight = abs(max($bbox[1],$bbox[3])) - min($bbox[5],$bbox[7]);
+        
+				// Define the x and y position
+				$charSpacing = rand($charSpacingMin, $charSpacingMax);
+        $x = $imageWidth + $charSpacing;
+        $y = ($charHeight + rand(0,$charJiggleAmount));
+
+				$characterDetails[] = array (
+					"size"  => $charSize,
+					"angle" => $charAngle,
+					"x"     => $x,
+					"y"     => $y,
+					"color" => $foreground,
+					"font"  => $font,
+					"char"  => $char
+				);
+				
+        // Increment the image size
+				$imageWidth  = $x + $charWidth;
+				$imageHeight = max($imageHeight,$y+$charJiggleAmount);
       }
-    }
+      
+			// Create the image based off the string length and margin
+      if (count($images) > 0) {
+        // We're going to be using an image, and need a tranparent background to start with
+        $im = _textimage_create_transparent_image($imageWidth+2*$imageMargin, $imageHeight+2*$imageMargin);
+        $noisecolor = imagecolorallocatealpha($im, 0, 0, 0, 127);
+      } else {
+        // Just make a plain-jane color brackground
+        $im = imagecreatetruecolor($imageWidth+2*$imageMargin, $imageHeight+2*$imageMargin);
+        $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250));
+        $noisecolor = $background;
+        imagefill($im, 0, 0, $background);
+      }
+      
+      
+			// Specify colors to be used in the image
+			$foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80));
+      
+			foreach($characterDetails as $char) {
+				// draw character
+				imagettftext($im,$char['size'],$char['angle'],$char['x']+$imageMargin,$char['y']+$imageMargin,$foreground,$char['font'],$char['char']);
+			}
 
+    }
+		
     else {
       // write text using a built-in font
-      $x = 10;
+      $x = 0;
       $y = 0;
+      $imageWidth = 60 + drupal_strlen($string)*$charSpacingMax*.35;
+      $imageHeight = 30 + $charJiggleAmount;
 
+      // Create the image
+      if (count($images) > 0 && function_exists(imagecolorallocatealpha)) {
+        // We're going to be using an image, and need a tranparent background to start with
+        $im = _textimage_create_transparent_image($imageWidth, $imageHeight);
+        $noisecolor = imagecolorallocatealpha($im, 0, 0, 0, 127);
+      } else {
+        // Just make a plain-jane color brackground
+        $im = imagecreatetruecolor($imageWidth, $imageHeight);
+        $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250));
+        $noisecolor = $background;
+        imagefill($im, 0, 0, $background);
+      }
+      
+      // Add the text
       for ($i=0;$i<drupal_strlen($string);$i++) {
-        imagestring($im,5,$x,$y,drupal_substr($string,$i,1),$foreground);
-        $x += rand(10,15);
-        $y += rand(-4,4);
+        imagestring($im,max(2,5-rand(0,$charSizeAmount)),$x,$y,drupal_substr($string,$i,1),$foreground);
+        $x += rand(10+$charSpacingMin,10+$charSpacingMax);
+        $y = $imageMargin + rand(0,$charJiggleAmount);
       }
-
+      
+      // Scale the image according to the user defined font size and add the margin
+      $imageWidth  = imagesx($im) * ($charInitialSize * .08) + $imageMargin;
+      $imageHeight = imagesy($im) * ($charInitialSize * .08) + $imageMargin;
+     
+      if (count($images) > 0) {
+        // Prepare a larger image with a background image
+        $im2 = _textimage_create_transparent_image($imageWidth, $imageHeight);
+      } else {
+        // Prepare a larger image with a solid color
+        $im2 = imagecreatetruecolor($imageWidth, $imageHeight);
+        imagefill($im2, 0, 0, $background);
+      }
+      
+      $result = imagecopyresampled ($im2, $im, $imageMargin, $imageMargin, 0, 0, $imageWidth, $imageHeight, imagesx($im), imagesy($im));
+      $im = $im2;
     }
-
+		
     // strikethrough
     imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $foreground);
-
-    // rotate only if function is defined (many PHP installations have this function missing)
-    if (function_exists('imagerotate')) {
-      $im2 = imagerotate($im,rand(-20,45),$background);
-      imagedestroy($im);
-      $im = $im2;
-    }
-
-    // add cloud only if function is defined (many PHP installations have this function missing)
-    if (function_exists('imagecolorallocatealpha')) {
-      $middleground = imagecolorallocatealpha($im, rand(160, 200), rand(160, 200), rand(160, 200), 80);
-
-      // random shapes
-      for ($x=0; $x<50;$x++) {
-        imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $middleground);
-        imageellipse($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $middleground);
+    
+    // Add Noise
+    for ($x=0; $x<$imageWidth; $x++) {
+      for ($row=0; $row<$imageNoise;$row++) {
+        $y = rand(0,$imageHeight);
+        imagesetpixel($im, $x, $y, $noisecolor);
       }
     }
+    // Add Lines and Ellipses
+    for ($x=0; $x<$imageLines;$x++) {
+      imageline($im, rand(0, $imageWidth), rand(0, $imageHeight), rand(0, $imageWidth), rand(0, $imageHeight), $noisecolor);
+      imageellipse($im, rand(0, $imageWidth), rand(0, $imageHeight), rand(0, $imageWidth), rand(0, $imageHeight), $noisecolor);
+    }
+    
+    // Fill image with a random background image if available
+    if (count($images) > 0) {
+      $image = $images[rand(0,count($images)-1)];
+      _textimage_apply_background_image($im,$image);
+    }
 
     //output to browser
     imagepng($im);
@@ -252,7 +428,7 @@
     }
 
     $string = $const[0] . $vow[0] .$const[2] . $const[1] . $vow[1] . $const[3] . $vow[3] . $const[4];
-    $string = drupal_substr($string,0,rand(5,8));
+    $string = drupal_substr($string,0,rand(4,6));
 
     //everytime we create a new code, we write it to session
     $_SESSION['captcha'] = drupal_strtolower($string);
@@ -279,4 +455,63 @@
   return $filelist;
 }
 
+/**
+* Returns an array of files with jpg, png, and gif extensions in the specified directory.
+*/
+function _textimage_image_list() {
+  $imagesdir = variable_get("textimage_images_path", "");
+
+  $filelist = array();
+  if (is_dir($imagesdir) && $handle = opendir($imagesdir)) {
+    while ($file = readdir($handle)) {
+      if (preg_match("/\.gif|\.png|\.jpg$/i",$file) == 1)
+        $filelist[] = $imagesdir.'/'.$file;
+    }
+    closedir($handle);
+  }
+
+  return $filelist;
+}
+
+/**
+* Overlays an image to the supplied image resource
+*/
+function _textimage_apply_background_image (&$imageResource,$imageFile) {
+  $backgroundResource = image_gd_open($imageFile,substr($imageFile,-3));
+  // Copy the text onto the background
+  $backX = imagesx($backgroundResource);
+  $backY = imagesy($backgroundResource);
+  $textX = imagesx($imageResource);
+  $textY = imagesy($imageResource);
+  
+  $randomBackX = rand(0,$backX-$textX);
+  $randomBackY = rand(0,$backY-$textY);
+  
+  // Place the text onto a random location of the background image
+  imagecopyresampled($backgroundResource,$imageResource,$randomBackX,$randomBackY,0,0,$textX,$textY,$textX,$textY);
+  // Crop the background image to the original image size
+  imagecopyresampled($imageResource,$backgroundResource,0,0,$randomBackX,$randomBackY,$textX,$textY,$textX,$textY);
+}
+
+/**
+* Creates transparent image resources for images with graphic backgrounds
+*/
+function _textimage_create_transparent_image($x, $y) {
+  $i = imagecreatetruecolor($x, $y);
+  $b = imagecreatefromstring(base64_decode(_text_image_blankpng()));
+  imagealphablending($i, false);
+  imagesavealpha($i, true);
+  imagecopyresized($i, $b ,0 ,0 ,0 ,0 ,$x, $y, imagesx($b), imagesy($b));
+  return $i;
+}
+ 
+function _text_image_blankpng() {
+  $c  = "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m";
+  $c .= "dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBNCg";
+  $c .= "dyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAAN";
+  $c .= "egcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQ";
+  $c .= "oHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAA";
+  $c .= "DXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII=";
+  return $c;
+}
 ?>
