CaptchaImageEngine.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <?php
  2. /**
  3. * Lotus Captcha component is inspired by "cool-php-captcha" project
  4. * http://code.google.com/p/cool-php-captcha
  5. */
  6. class LtCaptchaImageEngine
  7. {
  8. public $conf;
  9. protected $maxWordLength = 9;
  10. /**
  11. * * Background color in RGB-array
  12. */
  13. protected $backgroundColor = array(255, 255, 255);
  14. /**
  15. * * Foreground colors in RGB-array
  16. */
  17. protected $colors = array(
  18. array(27, 78, 181), // blue
  19. array(22, 163, 35), // green
  20. array(214, 36, 7), // red
  21. );
  22. /**
  23. * * Shadow color in RGB-array or null
  24. */
  25. protected $shadowColor = null; //array(0, 0, 0);
  26. /**
  27. * Font configuration
  28. *
  29. * - font: TTF file
  30. * - spacing: relative pixel space between character
  31. * - minSize: min font size
  32. * - maxSize: max font size
  33. */
  34. protected $fonts = array('Antykwa' => array('spacing' => -3, 'minSize' => 27, 'maxSize' => 30, 'font' => 'AntykwaBold.ttf'),
  35. 'Candice' => array('spacing' => -1.5, 'minSize' => 28, 'maxSize' => 31, 'font' => 'Candice.ttf'),
  36. 'DingDong' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 30, 'font' => 'Ding-DongDaddyO.ttf'),
  37. 'Duality' => array('spacing' => -2, 'minSize' => 30, 'maxSize' => 38, 'font' => 'Duality.ttf'),
  38. 'Jura' => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 32, 'font' => 'Jura.ttf'),
  39. 'StayPuft' => array('spacing' => -1.5, 'minSize' => 28, 'maxSize' => 32, 'font' => 'StayPuft.ttf'),
  40. 'Times' => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 34, 'font' => 'TimesNewRomanBold.ttf'),
  41. 'VeraSans' => array('spacing' => -1, 'minSize' => 20, 'maxSize' => 28, 'font' => 'VeraSansBold.ttf'),
  42. );
  43. /**
  44. * * Wave configuracion in X and Y axes
  45. */
  46. protected $Yperiod = 12;
  47. protected $Yamplitude = 14;
  48. protected $Xperiod = 11;
  49. protected $Xamplitude = 5;
  50. /**
  51. * * GD image
  52. */
  53. protected $im;
  54. public function drawImage($text)
  55. {
  56. /**
  57. * * Initialization
  58. */
  59. $this->ImageAllocate();
  60. $fontcfg = $this->fonts[array_rand($this->fonts)];
  61. $this->WriteText($text, $fontcfg);
  62. /**
  63. * * Transformations
  64. */
  65. $this->WaveImage();
  66. if ($this->conf['blur'] && function_exists('imagefilter'))
  67. {
  68. imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
  69. }
  70. $this->ReduceImage();
  71. return $this->im;
  72. }
  73. /**
  74. * Creates the image resources
  75. */
  76. protected function ImageAllocate()
  77. {
  78. // Cleanup
  79. if (!empty($this->im))
  80. {
  81. imagedestroy($this->im);
  82. }
  83. $this->im = imagecreatetruecolor($this->conf['width'] * $this->conf['scale'], $this->conf['height'] * $this->conf['scale']);
  84. // Background color
  85. $this->GdBgColor = imagecolorallocate($this->im,
  86. $this->backgroundColor[0],
  87. $this->backgroundColor[1],
  88. $this->backgroundColor[2]
  89. );
  90. imagefilledrectangle($this->im, 0, 0, $this->conf['width'] * $this->conf['scale'], $this->conf['height'] * $this->conf['scale'], $this->GdBgColor);
  91. // Foreground color
  92. $color = $this->colors[mt_rand(0, sizeof($this->colors)-1)];
  93. $this->GdFgColor = imagecolorallocate($this->im, $color[0], $color[1], $color[2]);
  94. // Shadow color
  95. if (!empty($this->shadowColor) && is_array($this->shadowColor) && sizeof($this->shadowColor) >= 3)
  96. {
  97. $this->GdShadowColor = imagecolorallocate($this->im,
  98. $this->shadowColor[0],
  99. $this->shadowColor[1],
  100. $this->shadowColor[2]
  101. );
  102. }
  103. }
  104. /**
  105. * Text insertion
  106. */
  107. protected function WriteText($text, $fontcfg = array())
  108. {
  109. if (empty($fontcfg))
  110. {
  111. // Select the font configuration
  112. $fontcfg = $this->fonts[array_rand($this->fonts)];
  113. }
  114. // Full path of font file
  115. $fontfile = dirname(__FILE__) . '/fonts/' . $fontcfg['font'];
  116. /**
  117. * * Increase font-size for shortest words: 9% for each glyp missing
  118. */
  119. $lettersMissing = $this->maxWordLength - strlen($text);
  120. $fontSizefactor = 1 + ($lettersMissing * 0.09);
  121. // Text generation (char by char)
  122. $x = 20 * $this->conf['scale'];
  123. $y = round(($this->conf['height'] * 27 / 40) * $this->conf['scale']);
  124. $length = strlen($text);
  125. for ($i = 0; $i < $length; $i++)
  126. {
  127. $degree = rand($this->conf['max_rotation'] * -1, $this->conf['max_rotation']);
  128. $fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize']) * $this->conf['scale'] * $fontSizefactor;
  129. $letter = substr($text, $i, 1);
  130. if ($this->shadowColor)
  131. {
  132. $coords = imagettftext($this->im, $fontsize, $degree,
  133. $x + $this->conf['scale'], $y + $this->conf['scale'],
  134. $this->GdShadowColor, $fontfile, $letter);
  135. }
  136. $coords = imagettftext($this->im, $fontsize, $degree,
  137. $x, $y,
  138. $this->GdFgColor, $fontfile, $letter);
  139. $x += ($coords[2] - $x) + ($fontcfg['spacing'] * $this->conf['scale']);
  140. }
  141. }
  142. /**
  143. * Wave filter
  144. */
  145. protected function WaveImage()
  146. {
  147. // X-axis wave generation
  148. $xp = $this->conf['scale'] * $this->Xperiod * rand(1, 3);
  149. $k = rand(0, 100);
  150. for ($i = 0; $i < ($this->conf['width'] * $this->conf['scale']); $i++)
  151. {
  152. imagecopy($this->im, $this->im,
  153. $i-1, sin($k + $i / $xp) * ($this->conf['scale'] * $this->Xamplitude),
  154. $i, 0, 1, $this->conf['height'] * $this->conf['scale']);
  155. }
  156. // Y-axis wave generation
  157. $k = rand(0, 100);
  158. $yp = $this->conf['scale'] * $this->Yperiod * rand(1, 2);
  159. for ($i = 0; $i < ($this->conf['height'] * $this->conf['scale']); $i++)
  160. {
  161. imagecopy($this->im, $this->im,
  162. sin($k + $i / $yp) * ($this->conf['scale'] * $this->Yamplitude), $i-1,
  163. 0, $i, $this->conf['width'] * $this->conf['scale'], 1);
  164. }
  165. }
  166. /**
  167. * Reduce the image to the final size
  168. */
  169. protected function ReduceImage()
  170. {
  171. $imResampled = imagecreatetruecolor($this->conf['width'], $this->conf['height']);
  172. imagecopyresampled($imResampled, $this->im,
  173. 0, 0, 0, 0,
  174. $this->conf['width'], $this->conf['height'],
  175. $this->conf['width'] * $this->conf['scale'], $this->conf['height'] * $this->conf['scale']
  176. );
  177. imagedestroy($this->im);
  178. $this->im = $imResampled;
  179. }
  180. }