fix calendar css, take 2. (#213)
[openemr.git] / interface / modules / zend_modules / library / Zend / Captcha / Image.php
blobb95550d07acdc4e9d7bd7dd3857957ffe1102c88
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Captcha;
12 use DirectoryIterator;
13 use Zend\Stdlib\ErrorHandler;
15 /**
16 * Image-based captcha element
18 * Generates image displaying random word
20 class Image extends AbstractWord
22 /**
23 * Directory for generated images
25 * @var string
27 protected $imgDir = "public/images/captcha/";
29 /**
30 * URL for accessing images
32 * @var string
34 protected $imgUrl = "/images/captcha/";
36 /**
37 * Image's alt tag content
39 * @var string
41 protected $imgAlt = "";
43 /**
44 * Image suffix (including dot)
46 * @var string
48 protected $suffix = ".png";
50 /**
51 * Image width
53 * @var int
55 protected $width = 200;
57 /**
58 * Image height
60 * @var int
62 protected $height = 50;
64 /**
65 * Font size
67 * @var int
69 protected $fsize = 24;
71 /**
72 * Image font file
74 * @var string
76 protected $font;
78 /**
79 * Image to use as starting point
80 * Default is blank image. If provided, should be PNG image.
82 * @var string
84 protected $startImage;
86 /**
87 * How frequently to execute garbage collection
89 * @var int
91 protected $gcFreq = 10;
93 /**
94 * How long to keep generated images
96 * @var int
98 protected $expiration = 600;
101 * Number of noise dots on image
102 * Used twice - before and after transform
104 * @var int
106 protected $dotNoiseLevel = 100;
109 * Number of noise lines on image
110 * Used twice - before and after transform
112 * @var int
114 protected $lineNoiseLevel = 5;
117 * Constructor
119 * @param array|\Traversable $options
120 * @throws Exception\ExtensionNotLoadedException
122 public function __construct($options = null)
124 if (!extension_loaded("gd")) {
125 throw new Exception\ExtensionNotLoadedException("Image CAPTCHA requires GD extension");
128 if (!function_exists("imagepng")) {
129 throw new Exception\ExtensionNotLoadedException("Image CAPTCHA requires PNG support");
132 if (!function_exists("imageftbbox")) {
133 throw new Exception\ExtensionNotLoadedException("Image CAPTCHA requires FT fonts support");
136 parent::__construct($options);
140 * @return string
142 public function getImgAlt()
144 return $this->imgAlt;
148 * @return string
150 public function getStartImage()
152 return $this->startImage;
156 * @return int
158 public function getDotNoiseLevel()
160 return $this->dotNoiseLevel;
164 * @return int
166 public function getLineNoiseLevel()
168 return $this->lineNoiseLevel;
172 * Get captcha expiration
174 * @return int
176 public function getExpiration()
178 return $this->expiration;
182 * Get garbage collection frequency
184 * @return int
186 public function getGcFreq()
188 return $this->gcFreq;
192 * Get font to use when generating captcha
194 * @return string
196 public function getFont()
198 return $this->font;
202 * Get font size
204 * @return int
206 public function getFontSize()
208 return $this->fsize;
212 * Get captcha image height
214 * @return int
216 public function getHeight()
218 return $this->height;
222 * Get captcha image directory
224 * @return string
226 public function getImgDir()
228 return $this->imgDir;
232 * Get captcha image base URL
234 * @return string
236 public function getImgUrl()
238 return $this->imgUrl;
242 * Get captcha image file suffix
244 * @return string
246 public function getSuffix()
248 return $this->suffix;
252 * Get captcha image width
254 * @return int
256 public function getWidth()
258 return $this->width;
262 * @param string $startImage
263 * @return Image
265 public function setStartImage($startImage)
267 $this->startImage = $startImage;
268 return $this;
272 * @param int $dotNoiseLevel
273 * @return Image
275 public function setDotNoiseLevel($dotNoiseLevel)
277 $this->dotNoiseLevel = $dotNoiseLevel;
278 return $this;
282 * @param int $lineNoiseLevel
283 * @return Image
285 public function setLineNoiseLevel($lineNoiseLevel)
287 $this->lineNoiseLevel = $lineNoiseLevel;
288 return $this;
292 * Set captcha expiration
294 * @param int $expiration
295 * @return Image
297 public function setExpiration($expiration)
299 $this->expiration = $expiration;
300 return $this;
304 * Set garbage collection frequency
306 * @param int $gcFreq
307 * @return Image
309 public function setGcFreq($gcFreq)
311 $this->gcFreq = $gcFreq;
312 return $this;
316 * Set captcha font
318 * @param string $font
319 * @return Image
321 public function setFont($font)
323 $this->font = $font;
324 return $this;
328 * Set captcha font size
330 * @param int $fsize
331 * @return Image
333 public function setFontSize($fsize)
335 $this->fsize = $fsize;
336 return $this;
340 * Set captcha image height
342 * @param int $height
343 * @return Image
345 public function setHeight($height)
347 $this->height = $height;
348 return $this;
352 * Set captcha image storage directory
354 * @param string $imgDir
355 * @return Image
357 public function setImgDir($imgDir)
359 $this->imgDir = rtrim($imgDir, "/\\") . '/';
360 return $this;
364 * Set captcha image base URL
366 * @param string $imgUrl
367 * @return Image
369 public function setImgUrl($imgUrl)
371 $this->imgUrl = rtrim($imgUrl, "/\\") . '/';
372 return $this;
376 * @param string $imgAlt
377 * @return Image
379 public function setImgAlt($imgAlt)
381 $this->imgAlt = $imgAlt;
382 return $this;
386 * Set captcha image filename suffix
388 * @param string $suffix
389 * @return Image
391 public function setSuffix($suffix)
393 $this->suffix = $suffix;
394 return $this;
398 * Set captcha image width
400 * @param int $width
401 * @return Image
403 public function setWidth($width)
405 $this->width = $width;
406 return $this;
410 * Generate random frequency
412 * @return float
414 protected function randomFreq()
416 return mt_rand(700000, 1000000) / 15000000;
420 * Generate random phase
422 * @return float
424 protected function randomPhase()
426 // random phase from 0 to pi
427 return mt_rand(0, 3141592) / 1000000;
431 * Generate random character size
433 * @return int
435 protected function randomSize()
437 return mt_rand(300, 700) / 100;
441 * Generate captcha
443 * @return string captcha ID
445 public function generate()
447 $id = parent::generate();
448 $tries = 5;
450 // If there's already such file, try creating a new ID
451 while ($tries-- && file_exists($this->getImgDir() . $id . $this->getSuffix())) {
452 $id = $this->generateRandomId();
453 $this->setId($id);
455 $this->generateImage($id, $this->getWord());
457 if (mt_rand(1, $this->getGcFreq()) == 1) {
458 $this->gc();
461 return $id;
465 * Generate image captcha
467 * Override this function if you want different image generator
468 * Wave transform from http://www.captcha.ru/captchas/multiwave/
470 * @param string $id Captcha ID
471 * @param string $word Captcha word
472 * @throws Exception\NoFontProvidedException if no font was set
473 * @throws Exception\ImageNotLoadableException if start image cannot be loaded
475 protected function generateImage($id, $word)
477 $font = $this->getFont();
479 if (empty($font)) {
480 throw new Exception\NoFontProvidedException('Image CAPTCHA requires font');
483 $w = $this->getWidth();
484 $h = $this->getHeight();
485 $fsize = $this->getFontSize();
487 $imgFile = $this->getImgDir() . $id . $this->getSuffix();
489 if (empty($this->startImage)) {
490 $img = imagecreatetruecolor($w, $h);
491 } else {
492 // Potential error is change to exception
493 ErrorHandler::start();
494 $img = imagecreatefrompng($this->startImage);
495 $error = ErrorHandler::stop();
496 if (!$img || $error) {
497 throw new Exception\ImageNotLoadableException(
498 "Can not load start image '{$this->startImage}'",
500 $error
503 $w = imagesx($img);
504 $h = imagesy($img);
507 $textColor = imagecolorallocate($img, 0, 0, 0);
508 $bgColor = imagecolorallocate($img, 255, 255, 255);
509 imagefilledrectangle($img, 0, 0, $w-1, $h-1, $bgColor);
510 $textbox = imageftbbox($fsize, 0, $font, $word);
511 $x = ($w - ($textbox[2] - $textbox[0])) / 2;
512 $y = ($h - ($textbox[7] - $textbox[1])) / 2;
513 imagefttext($img, $fsize, 0, $x, $y, $textColor, $font, $word);
515 // generate noise
516 for ($i=0; $i < $this->dotNoiseLevel; $i++) {
517 imagefilledellipse($img, mt_rand(0, $w), mt_rand(0, $h), 2, 2, $textColor);
519 for ($i=0; $i < $this->lineNoiseLevel; $i++) {
520 imageline($img, mt_rand(0, $w), mt_rand(0, $h), mt_rand(0, $w), mt_rand(0, $h), $textColor);
523 // transformed image
524 $img2 = imagecreatetruecolor($w, $h);
525 $bgColor = imagecolorallocate($img2, 255, 255, 255);
526 imagefilledrectangle($img2, 0, 0, $w-1, $h-1, $bgColor);
528 // apply wave transforms
529 $freq1 = $this->randomFreq();
530 $freq2 = $this->randomFreq();
531 $freq3 = $this->randomFreq();
532 $freq4 = $this->randomFreq();
534 $ph1 = $this->randomPhase();
535 $ph2 = $this->randomPhase();
536 $ph3 = $this->randomPhase();
537 $ph4 = $this->randomPhase();
539 $szx = $this->randomSize();
540 $szy = $this->randomSize();
542 for ($x = 0; $x < $w; $x++) {
543 for ($y = 0; $y < $h; $y++) {
544 $sx = $x + (sin($x*$freq1 + $ph1) + sin($y*$freq3 + $ph3)) * $szx;
545 $sy = $y + (sin($x*$freq2 + $ph2) + sin($y*$freq4 + $ph4)) * $szy;
547 if ($sx < 0 || $sy < 0 || $sx >= $w - 1 || $sy >= $h - 1) {
548 continue;
549 } else {
550 $color = (imagecolorat($img, $sx, $sy) >> 16) & 0xFF;
551 $colorX = (imagecolorat($img, $sx + 1, $sy) >> 16) & 0xFF;
552 $colorY = (imagecolorat($img, $sx, $sy + 1) >> 16) & 0xFF;
553 $colorXY = (imagecolorat($img, $sx + 1, $sy + 1) >> 16) & 0xFF;
556 if ($color == 255 && $colorX == 255 && $colorY == 255 && $colorXY == 255) {
557 // ignore background
558 continue;
559 } elseif ($color == 0 && $colorX == 0 && $colorY == 0 && $colorXY == 0) {
560 // transfer inside of the image as-is
561 $newcolor = 0;
562 } else {
563 // do antialiasing for border items
564 $fracX = $sx - floor($sx);
565 $fracY = $sy - floor($sy);
566 $fracX1 = 1 - $fracX;
567 $fracY1 = 1 - $fracY;
569 $newcolor = $color * $fracX1 * $fracY1
570 + $colorX * $fracX * $fracY1
571 + $colorY * $fracX1 * $fracY
572 + $colorXY * $fracX * $fracY;
575 imagesetpixel($img2, $x, $y, imagecolorallocate($img2, $newcolor, $newcolor, $newcolor));
579 // generate noise
580 for ($i=0; $i<$this->dotNoiseLevel; $i++) {
581 imagefilledellipse($img2, mt_rand(0, $w), mt_rand(0, $h), 2, 2, $textColor);
584 for ($i=0; $i<$this->lineNoiseLevel; $i++) {
585 imageline($img2, mt_rand(0, $w), mt_rand(0, $h), mt_rand(0, $w), mt_rand(0, $h), $textColor);
588 imagepng($img2, $imgFile);
589 imagedestroy($img);
590 imagedestroy($img2);
594 * Remove old files from image directory
597 protected function gc()
599 $expire = time() - $this->getExpiration();
600 $imgdir = $this->getImgDir();
601 if (!$imgdir || strlen($imgdir) < 2) {
602 // safety guard
603 return;
606 $suffixLength = strlen($this->suffix);
607 foreach (new DirectoryIterator($imgdir) as $file) {
608 if (!$file->isDot() && !$file->isDir()) {
609 if (file_exists($file->getPathname()) && $file->getMTime() < $expire) {
610 // only deletes files ending with $this->suffix
611 if (substr($file->getFilename(), -($suffixLength)) == $this->suffix) {
612 unlink($file->getPathname());
620 * Get helper name used to render captcha
622 * @return string
624 public function getHelperName()
626 return 'captcha/image';