PageRenderTime 66ms CodeModel.GetById 24ms RepoModel.GetById 2ms app.codeStats 0ms

/applications/core/lib/Zend/Captcha/Image.php

#
PHP | 594 lines | 274 code | 48 blank | 272 comment | 41 complexity | e68352d37034a764848f903d9d4d8730 MD5 | raw file
Possible License(s): CC0-1.0
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Captcha
  17. * @subpackage Adapter
  18. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Image.php 14290 2009-03-13 12:40:14Z alexander $
  21. */
  22. /** Zend_Captcha_Word */
  23. require_once 'Zend/Captcha/Word.php';
  24. /**
  25. * Image-based captcha element
  26. *
  27. * Generates image displaying random word
  28. *
  29. * @category Zend
  30. * @package Zend_Captcha
  31. * @subpackage Adapter
  32. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Captcha_Image extends Zend_Captcha_Word
  36. {
  37. /**
  38. * Directory for generated images
  39. *
  40. * @var string
  41. */
  42. protected $_imgDir = "./images/captcha/";
  43. /**
  44. * URL for accessing images
  45. *
  46. * @var string
  47. */
  48. protected $_imgUrl = "/images/captcha/";
  49. /**
  50. * Image's alt tag content
  51. *
  52. * @var string
  53. */
  54. protected $_imgAlt = "";
  55. /**
  56. * Image suffix (including dot)
  57. *
  58. * @var string
  59. */
  60. protected $_suffix = ".png";
  61. /**
  62. * Image width
  63. *
  64. * @var int
  65. */
  66. protected $_width = 200;
  67. /**
  68. * Image height
  69. *
  70. * @var int
  71. */
  72. protected $_height = 50;
  73. /**
  74. * Font size
  75. *
  76. * @var int
  77. */
  78. protected $_fsize = 24;
  79. /**
  80. * Image font file
  81. *
  82. * @var string
  83. */
  84. protected $_font;
  85. /**
  86. * Image to use as starting point
  87. * Default is blank image. If ptovided, should be PNG image.
  88. *
  89. * @var string
  90. */
  91. protected $_startImage;
  92. /**
  93. * How frequently to execute garbage collection
  94. *
  95. * @var int
  96. */
  97. protected $_gcFreq = 10;
  98. /**
  99. * How long to keep generated images
  100. *
  101. * @var int
  102. */
  103. protected $_expiration = 600;
  104. /**
  105. * Number of noise dots on image
  106. * Used twice - before and after transform
  107. *
  108. * @var int
  109. */
  110. protected $_dotNoiseLevel = 100;
  111. /**
  112. * Number of noise lines on image
  113. * Used twice - before and after transform
  114. *
  115. * @var int
  116. */
  117. protected $_lineNoiseLevel = 5;
  118. /**
  119. * @return string
  120. */
  121. public function getImgAlt ()
  122. {
  123. return $this->_imgAlt;
  124. }
  125. /**
  126. * @return string
  127. */
  128. public function getStartImage ()
  129. {
  130. return $this->_startImage;
  131. }
  132. /**
  133. * @return int
  134. */
  135. public function getDotNoiseLevel ()
  136. {
  137. return $this->_dotNoiseLevel;
  138. }
  139. /**
  140. * @return int
  141. */
  142. public function getLineNoiseLevel ()
  143. {
  144. return $this->_lineNoiseLevel;
  145. }
  146. /**
  147. * Get captcha expiration
  148. *
  149. * @return int
  150. */
  151. public function getExpiration()
  152. {
  153. return $this->_expiration;
  154. }
  155. /**
  156. * Get garbage collection frequency
  157. *
  158. * @return int
  159. */
  160. public function getGcFreq()
  161. {
  162. return $this->_gcFreq;
  163. }
  164. /**
  165. * Get font to use when generating captcha
  166. *
  167. * @return string
  168. */
  169. public function getFont()
  170. {
  171. return $this->_font;
  172. }
  173. /**
  174. * Get font size
  175. *
  176. * @return int
  177. */
  178. public function getFontSize()
  179. {
  180. return $this->_fsize;
  181. }
  182. /**
  183. * Get captcha image height
  184. *
  185. * @return int
  186. */
  187. public function getHeight()
  188. {
  189. return $this->_height;
  190. }
  191. /**
  192. * Get captcha image directory
  193. *
  194. * @return string
  195. */
  196. public function getImgDir()
  197. {
  198. return $this->_imgDir;
  199. }
  200. /**
  201. * Get captcha image base URL
  202. *
  203. * @return string
  204. */
  205. public function getImgUrl()
  206. {
  207. return $this->_imgUrl;
  208. }
  209. /**
  210. * Get captcha image file suffix
  211. *
  212. * @return string
  213. */
  214. public function getSuffix()
  215. {
  216. return $this->_suffix;
  217. }
  218. /**
  219. * Get captcha image width
  220. *
  221. * @return int
  222. */
  223. public function getWidth()
  224. {
  225. return $this->_width;
  226. }
  227. /**
  228. * @param string $startImage
  229. */
  230. public function setStartImage ($startImage)
  231. {
  232. $this->_startImage = $startImage;
  233. return $this;
  234. }
  235. /**
  236. * @param int $dotNoiseLevel
  237. */
  238. public function setDotNoiseLevel ($dotNoiseLevel)
  239. {
  240. $this->_dotNoiseLevel = $dotNoiseLevel;
  241. return $this;
  242. }
  243. /**
  244. * @param int $lineNoiseLevel
  245. */
  246. public function setLineNoiseLevel ($lineNoiseLevel)
  247. {
  248. $this->_lineNoiseLevel = $lineNoiseLevel;
  249. return $this;
  250. }
  251. /**
  252. * Set captcha expiration
  253. *
  254. * @param int $expiration
  255. * @return Zend_Captcha_Image
  256. */
  257. public function setExpiration($expiration)
  258. {
  259. $this->_expiration = $expiration;
  260. return $this;
  261. }
  262. /**
  263. * Set garbage collection frequency
  264. *
  265. * @param int $gcFreq
  266. * @return Zend_Captcha_Image
  267. */
  268. public function setGcFreq($gcFreq)
  269. {
  270. $this->_gcFreq = $gcFreq;
  271. return $this;
  272. }
  273. /**
  274. * Set captcha font
  275. *
  276. * @param string $font
  277. * @return Zend_Captcha_Image
  278. */
  279. public function setFont($font)
  280. {
  281. $this->_font = $font;
  282. return $this;
  283. }
  284. /**
  285. * Set captcha font size
  286. *
  287. * @param int $fsize
  288. * @return Zend_Captcha_Image
  289. */
  290. public function setFontSize($fsize)
  291. {
  292. $this->_fsize = $fsize;
  293. return $this;
  294. }
  295. /**
  296. * Set captcha image height
  297. *
  298. * @param int $height
  299. * @return Zend_Captcha_Image
  300. */
  301. public function setHeight($height)
  302. {
  303. $this->_height = $height;
  304. return $this;
  305. }
  306. /**
  307. * Set captcha image storage directory
  308. *
  309. * @param string $imgDir
  310. * @return Zend_Captcha_Image
  311. */
  312. public function setImgDir($imgDir)
  313. {
  314. $this->_imgDir = rtrim($imgDir, "/\\") . '/';
  315. return $this;
  316. }
  317. /**
  318. * Set captcha image base URL
  319. *
  320. * @param string $imgUrl
  321. * @return Zend_Captcha_Image
  322. */
  323. public function setImgUrl($imgUrl)
  324. {
  325. $this->_imgUrl = rtrim($imgUrl, "/\\") . '/';
  326. return $this;
  327. }
  328. /**
  329. * @param string $imgAlt
  330. */
  331. public function setImgAlt ($imgAlt)
  332. {
  333. $this->_imgAlt = $imgAlt;
  334. return $this;
  335. }
  336. /**
  337. * Set captch image filename suffix
  338. *
  339. * @param string $suffix
  340. * @return Zend_Captcha_Image
  341. */
  342. public function setSuffix($suffix)
  343. {
  344. $this->_suffix = $suffix;
  345. return $this;
  346. }
  347. /**
  348. * Set captcha image width
  349. *
  350. * @param int $width
  351. * @return Zend_Captcha_Image
  352. */
  353. public function setWidth($width)
  354. {
  355. $this->_width = $width;
  356. return $this;
  357. }
  358. /**
  359. * Generate random frequency
  360. *
  361. * @return float
  362. */
  363. protected function _randomFreq()
  364. {
  365. return mt_rand(700000, 1000000) / 15000000;
  366. }
  367. /**
  368. * Generate random phase
  369. *
  370. * @return float
  371. */
  372. protected function _randomPhase()
  373. {
  374. // random phase from 0 to pi
  375. return mt_rand(0, 3141592) / 1000000;
  376. }
  377. /**
  378. * Generate random character size
  379. *
  380. * @return int
  381. */
  382. protected function _randomSize()
  383. {
  384. return mt_rand(300, 700) / 100;
  385. }
  386. /**
  387. * Generate captcha
  388. *
  389. * @return string captcha ID
  390. */
  391. public function generate()
  392. {
  393. $id = parent::generate();
  394. $this->_generateImage($id, $this->getWord());
  395. if (mt_rand(1, $this->getGcFreq()) == 1) {
  396. $this->_gc();
  397. }
  398. return $id;
  399. }
  400. /**
  401. * Generate image captcha
  402. *
  403. * Override this function if you want different image generator
  404. * Wave transform from http://www.captcha.ru/captchas/multiwave/
  405. *
  406. * @param string $id Captcha ID
  407. * @param string $word Captcha word
  408. */
  409. protected function _generateImage($id, $word)
  410. {
  411. if (!extension_loaded("gd")) {
  412. require_once 'Zend/Captcha/Exception.php';
  413. throw new Zend_Captcha_Exception("Image CAPTCHA requires GD extension");
  414. }
  415. if (!function_exists("imagepng")) {
  416. require_once 'Zend/Captcha/Exception.php';
  417. throw new Zend_Captcha_Exception("Image CAPTCHA requires PNG support");
  418. }
  419. if (!function_exists("imageftbbox")) {
  420. require_once 'Zend/Captcha/Exception.php';
  421. throw new Zend_Captcha_Exception("Image CAPTCHA requires FT fonts support");
  422. }
  423. $font = $this->getFont();
  424. if (empty($font)) {
  425. require_once 'Zend/Captcha/Exception.php';
  426. throw new Zend_Captcha_Exception("Image CAPTCHA requires font");
  427. }
  428. $w = $this->getWidth();
  429. $h = $this->getHeight();
  430. $fsize = $this->getFontSize();
  431. $img_file = $this->getImgDir() . $id . $this->getSuffix();
  432. if(empty($this->_startImage)) {
  433. $img = imagecreatetruecolor($w, $h);
  434. } else {
  435. $img = imagecreatefrompng($this->_startImage);
  436. if(!$img) {
  437. require_once 'Zend/Captcha/Exception.php';
  438. throw new Zend_Captcha_Exception("Can not load start image");
  439. }
  440. $w = imagesx($img);
  441. $h = imagesy($img);
  442. }
  443. $text_color = imagecolorallocate($img, 0, 0, 0);
  444. $bg_color = imagecolorallocate($img, 255, 255, 255);
  445. imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bg_color);
  446. $textbox = imageftbbox($fsize, 0, $font, $word);
  447. $x = ($w - ($textbox[2] - $textbox[0])) / 2;
  448. $y = ($h - ($textbox[7] - $textbox[1])) / 2;
  449. imagefttext($img, $fsize, 0, $x, $y, $text_color, $font, $word);
  450. // generate noise
  451. for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
  452. imagefilledellipse($img, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
  453. }
  454. for($i=0; $i<$this->_lineNoiseLevel; $i++) {
  455. imageline($img, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
  456. }
  457. // transformed image
  458. $img2 = imagecreatetruecolor($w, $h);
  459. $bg_color = imagecolorallocate($img2, 255, 255, 255);
  460. imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bg_color);
  461. // apply wave transforms
  462. $freq1 = $this->_randomFreq();
  463. $freq2 = $this->_randomFreq();
  464. $freq3 = $this->_randomFreq();
  465. $freq4 = $this->_randomFreq();
  466. $ph1 = $this->_randomPhase();
  467. $ph2 = $this->_randomPhase();
  468. $ph3 = $this->_randomPhase();
  469. $ph4 = $this->_randomPhase();
  470. $szx = $this->_randomSize();
  471. $szy = $this->_randomSize();
  472. for ($x = 0; $x < $w; $x++) {
  473. for ($y = 0; $y < $h; $y++) {
  474. $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx;
  475. $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy;
  476. if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) {
  477. continue;
  478. } else {
  479. $color = (imagecolorat($img, $sx, $sy) >> 16) & 0xFF;
  480. $color_x = (imagecolorat($img, $sx + 1, $sy) >> 16) & 0xFF;
  481. $color_y = (imagecolorat($img, $sx, $sy + 1) >> 16) & 0xFF;
  482. $color_xy = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF;
  483. }
  484. if ($color == 255 && $color_x == 255 && $color_y == 255 && $color_xy == 255) {
  485. // ignore background
  486. continue;
  487. } elseif ($color == 0 && $color_x == 0 && $color_y == 0 && $color_xy == 0) {
  488. // transfer inside of the image as-is
  489. $newcolor = 0;
  490. } else {
  491. // do antialiasing for border items
  492. $frac_x = $sx-floor($sx);
  493. $frac_y = $sy-floor($sy);
  494. $frac_x1 = 1-$frac_x;
  495. $frac_y1 = 1-$frac_y;
  496. $newcolor = $color * $frac_x1 * $frac_y1
  497. + $color_x * $frac_x * $frac_y1
  498. + $color_y * $frac_x1 * $frac_y
  499. + $color_xy * $frac_x * $frac_y;
  500. }
  501. imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor));
  502. }
  503. }
  504. // generate noise
  505. for ($i=0; $i<$this->_dotNoiseLevel; $i++) {
  506. imagefilledellipse($img2, mt_rand(0,$w), mt_rand(0,$h), 2, 2, $text_color);
  507. }
  508. for ($i=0; $i<$this->_lineNoiseLevel; $i++) {
  509. imageline($img2, mt_rand(0,$w), mt_rand(0,$h), mt_rand(0,$w), mt_rand(0,$h), $text_color);
  510. }
  511. imagepng($img2, $img_file);
  512. imagedestroy($img);
  513. imagedestroy($img2);
  514. }
  515. /**
  516. * Remove old files from image directory
  517. *
  518. */
  519. protected function _gc()
  520. {
  521. $expire = time() - $this->getExpiration();
  522. $imgdir = $this->getImgDir();
  523. if(!$imgdir || strlen($imgdir) < 2) {
  524. // safety guard
  525. return;
  526. }
  527. foreach (new DirectoryIterator($imgdir) as $file) {
  528. if (!$file->isDot() && !$file->isDir()) {
  529. if ($file->getMTime() < $expire) {
  530. unlink($file->getPathname());
  531. }
  532. }
  533. }
  534. }
  535. /**
  536. * Display the captcha
  537. *
  538. * @param Zend_View_Interface $view
  539. * @param mixed $element
  540. * @return string
  541. */
  542. public function render(Zend_View_Interface $view, $element = null)
  543. {
  544. return '<img alt="'.$this->getImgAlt().'" src="' . $this->getImgUrl() . $this->getId() . $this->getSuffix() . '"/><br/>';
  545. }
  546. }