PageRenderTime 142ms CodeModel.GetById 104ms app.highlight 32ms RepoModel.GetById 0ms app.codeStats 1ms

/library/Oray/Seccode/Image.php

https://github.com/polokk/tudu-web-1
PHP | 608 lines | 380 code | 91 blank | 137 comment | 95 complexity | bd74fbe128de156611e1d185819a3d69 MD5 | raw file
  1<?php
  2/**
  3 * Oray Framework
  4 *
  5 * LICENSE
  6 *
  7 *
  8 * @category   Oray
  9 * @package    Oray_Seccode
 10 * @copyright  Copyright (c) 2009-2011 Shanghai Best Oray Information S&T CO., Ltd.
 11 * @link       http://www.oray.com/
 12 * @version    $Id$
 13 */
 14
 15/**
 16 * @see Oray_Seccode_Abstract
 17 */
 18require_once 'Oray/Seccode/Abstract.php';
 19
 20/**
 21 * @category   Oray
 22 * @package    Oray_Seccode
 23 * @subpackage Image
 24 * @copyright  Copyright (c) 2009-2011 Shanghai Best Oray Information S&T CO., Ltd.
 25 */
 26class Oray_Seccode_Image extends Oray_Seccode_Abstract
 27{
 28    /**
 29     * 验证码长度
 30     *
 31     * @var int
 32     */
 33    private $_length = 4;
 34
 35    /**
 36     * 是否纯英文字符
 37     *
 38     * @var boolean
 39     */
 40    private $_isEn;
 41
 42    /**
 43     * 字条颜色
 44     *
 45     * @var array
 46     */
 47    private $_fontColor = array();
 48
 49    /**
 50     * Resource of image
 51     *
 52     * @var image
 53     */
 54    private $_im;
 55
 56    public $background = true;    // 随机图片背景
 57    public $adulterate = true;    // 随机背景掺杂
 58    public $ttf 	   = true;    // 随机 TTF 字体
 59    public $angle      = true;    // 随机倾斜度
 60    public $color      = true;    // 随机颜色
 61    public $size       = true;    // 随机大小
 62    public $shadow     = true;    // 文字阴影
 63    public $animator   = false;   // GIF 动画
 64
 65    /**
 66     * 显示验证码
 67     *
 68     * @param string $code
 69     * @return void
 70     */
 71    public function display($code) {
 72
 73        $this->_code = $code;
 74        $this->_isEn = (boolean) preg_match('/^[\x{01}-\x{ff}]+$/u', $code);
 75
 76        $this->width  = ($this->width > 0 && $this->width <= Oray_Seccode::WIDTH_MAX) ? $this->width : Oray_Seccode::WIDTH_DEFAULT;
 77        $this->height = ($this->height > 0 && $this->height <= Oray_Seccode::HEIGHT_MAX) ? $this->height : Oray_Seccode::HEIGHT_DEFAULT;
 78
 79        if (function_exists('imagecreate')
 80            && function_exists('imagecolorset')
 81            && function_exists('imagecopyresized')
 82            && function_exists('imagecolorallocate')
 83            && function_exists('imagechar')
 84            && function_exists('imagecolorsforindex')
 85            && function_exists('imageline')
 86            && function_exists('imagecreatefromstring')
 87            && (function_exists('imagegif')
 88                || function_exists('imagepng')
 89                || function_exists('imagejpeg'))) {
 90            $this->_image();
 91        } else {
 92            $this->_bitmap();
 93        }
 94    }
 95
 96    /**
 97     * 图片验证码
 98     *
 99     */
100    private function _image()
101    {
102        // 创建图像
103        $this->_createBackground();
104
105        // GIF动画
106        if ($this->animator && function_exists('imagegif')) {
107
108            $trueframe = mt_rand(1, 9);
109
110            for($i = 0; $i <= 9; $i++) {
111
112                // 新建一个真彩色图像
113                $im = imagecreatetruecolor($this->width, $this->height);
114
115                // 拷贝背景到新的图像
116                imagecopy($im, $this->_im, 0, 0, 0, 0, $this->width, $this->height);
117
118                // 随机背景掺杂
119                if ($this->adulterate) {
120                    $this->_adulterate($im);
121                }
122
123                $x[$i] = $y[$i] = 0;
124
125                if ($i == $trueframe) {
126                    if ($this->ttf && function_exists('imagettftext')) {
127                        $this->_ttfFont($im);
128                    } else {
129                        $this->_gifFont($im);
130                    }
131                    $d[$i] = mt_rand(250, 400);
132                } else {
133                    $this->_randomFont($im);
134                    $d[$i] = mt_rand(5, 15);
135                }
136
137                ob_start();
138                imagegif($im);
139                imagedestroy($im);
140                $frame[$i] = ob_get_contents();
141                ob_end_clean();
142            }
143            imagedestroy($this->_im);
144
145            require_once 'Oray/Seccode/GifMerge.php';
146            $anim = new Oray_Seccode_GifMerge($frame, 255, 255, 255, 0, $d, $x, $y, 'C_MEMORY');
147            header('Content-type: image/gif');
148            echo $anim->getAnimation();
149
150        } else {
151
152            // 随机背景掺杂
153            if ($this->adulterate) {
154                $this->_adulterate($this->_im);
155            }
156
157            if ($this->ttf && function_exists('imagettftext')) {
158                $this->_ttfFont($this->_im);
159            } else {
160                $this->_gifFont($this->_im);
161            }
162
163            // 输出图片
164            if (function_exists('imagepng')) {
165                header('Content-type: image/png');
166                imagepng($this->_im);
167            } else {
168                header('Content-type: image/jpeg');
169                imagejpeg($this->_im, '', 75);
170            }
171
172            imagedestroy($this->_im);
173        }
174    }
175
176    /**
177     * 返回文件后缀
178     *
179     * @param string $filename
180     * @return string
181     */
182    private function _fileExt($filename) {
183        return trim(substr(strrchr($filename, '.'), 1, 10));
184    }
185
186    /**
187     * 生成图片背景内容
188     *
189     * @return void
190     */
191    private function _createBackground() {
192
193        // 新建一个真彩色图像
194        $this->_im = imagecreatetruecolor($this->width, $this->height);
195
196        // 为一幅图像分配颜色,第一次对 imagecolorallocate() 的调用会填充背景色
197        $backgroundColor = imagecolorallocate($this->_im, 255, 255, 255);
198
199        $backgrounds = $color = array();
200
201        // 随机背景
202        if ($this->background && function_exists('imagecreatefromjpeg')
203            && function_exists('imagecolorat') && function_exists('imagecopymerge')
204            && function_exists('imagesetpixel') && function_exists('imageSX')
205            && function_exists('imageSY')) {
206            if ($handle = opendir($this->dataPath . '/background/')) {
207                while ($bgfile = readdir($handle)) {
208                    if (preg_match('/\.jpg$/i', $bgfile)) {
209                        $backgrounds[] = $this->dataPath . '/background/' . $bgfile;
210                    }
211                }
212                closedir($handle);
213            }
214
215            if ($backgrounds) {
216
217                // 从 URL 新建一图像
218                $im = imagecreatefromjpeg($backgrounds[array_rand($backgrounds)]);
219
220                // 取得某像素的颜色索引值
221                $rgb = imagecolorat($im, 0, 0);
222
223                // 取得某索引的颜色
224                $color = imagecolorsforindex($im, $rgb);
225
226                // 取得某像素的颜色索引值
227                $rgb = imagecolorat($im, 1, 0);
228
229                // 画一个单一像素
230                imagesetpixel($im, 0, 0, $rgb);
231
232                // ??
233                $color[0] = $color['red'];
234                $color[1] = $color['green'];
235                $color[2] = $color['blue'];
236
237                // 拷贝并合并图像的一部分
238                imagecopymerge($this->_im,
239                               $im,
240                               0,
241                               0,
242                               mt_rand(0, Oray_Seccode::WIDTH_MAX - $this->width),
243                               mt_rand(0, Oray_Seccode::HEIGHT_MAX - $this->height),
244                               imageSX($im),
245                               imageSY($im),
246                               100);
247
248                // 销毁一图像
249                imagedestroy($im);
250            }
251        }
252
253        if (!$this->background || !$backgrounds) {
254            for($i = 0; $i < 3; $i++) {
255                $start[$i] = mt_rand(200, 255);
256                $end[$i] = mt_rand(100, 150);
257                $step[$i] = ($end[$i] - $start[$i]) / $this->width;
258                $color[$i] = $start[$i];
259            }
260            for($i = 0; $i < $this->width; $i++) {
261
262                // 获取给定的 RGB 成分组成的颜色标识符
263                $rgb = imagecolorallocate($this->_im, $color[0], $color[1], $color[2]);
264
265                // 画一条线段
266                imageline($this->_im, $i, 0, $i, $this->height, $rgb);
267
268                $color[0] += $step[0];
269                $color[1] += $step[1];
270                $color[2] += $step[2];
271            }
272
273            $color[0] -= 20;
274            $color[1] -= 20;
275            $color[2] -= 20;
276        }
277
278        $this->_fontColor = $color;
279
280        //return $this->_im;
281    }
282
283    /**
284     * 背景图片掺杂
285     *
286     * @param gd $im
287     */
288    private function _adulterate($im) {
289        $amount = $this->height / 10;
290        for($i = 0; $i <= $amount; $i++) {
291
292            // 随机颜色
293            $fontColor = $this->_getFontColor();
294
295            $color = imagecolorallocate($im, $fontColor[0], $fontColor[1], $fontColor[2]);
296
297            $x = mt_rand(0, $this->width);
298            $y = mt_rand(0, $this->height);
299
300            if (mt_rand(0, 1)) {
301
302                // 画椭圆弧
303                imagearc($im, $x, $y, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, 360), mt_rand(0, 360), $color);
304            } else {
305
306                // 画一条线段
307                imageline($im, $x, $y, mt_rand(0, $this->width), mt_rand(0, mt_rand($this->height, $this->width)), $color);
308            }
309        }
310    }
311
312    /**
313     * 使用随机字体
314     *
315     * @param gd $im
316     */
317    private function _randomFont($im)
318    {
319        $units = 'BCEFGHJKMPQRTVWXY2346789';
320        $x = $this->width / 4;
321        $y = $this->height / 10;
322
323        $color = imagecolorallocate($im, $this->_fontColor[0], $this->_fontColor[1], $this->_fontColor[2]);
324
325        for($i = 0; $i <= 3; $i++) {
326            $code = $units[mt_rand(0, 23)];
327            imagechar($im, 5, $x * $i + mt_rand(0, $x - 10), mt_rand($y, $this->height - 10 - $y), $code, $color);
328        }
329    }
330
331    /**
332     * 使用TTF字体
333     *
334     * @return void
335     */
336    private function _ttfFont($im) {
337        $seccode = $this->_code;
338        $charset = strtolower($this->_charset);
339        $ttfs    = array();
340
341        if ($this->_isEn) {
342            $folder = $this->fontPath . '/en/';
343        } else {
344            $folder = $this->fontPath . '/ch/';
345        }
346
347        $dirs = opendir($folder);
348        while($filename = readdir($dirs)) {
349            if ($filename != '.' && $filename != '..'
350                && in_array(strtolower($this->_fileExt($filename)),
351                            array('ttf', 'ttc'))) {
352                $ttfs[] = $filename;
353            }
354        }
355
356        // 字体为空时,采用GIF图片输出
357        if (empty($ttfs)) {
358            $this->_gifFont();
359            return;
360        }
361
362        $length = $this->_length;
363
364        if (!$this->_isEn && !empty($ttfs)) {
365            if ($charset != 'utf-8') {
366                $seccode = mb_convert_encoding($seccode, 'utf-8', $charset);
367            }
368            $seccode = str_split($seccode, 3);
369            $length = count($seccode);
370        }
371
372        $totalWidth = 0;
373
374        for($i = 0; $i < $length; $i++) {
375            $font[$i]['font']  = $folder . $ttfs[array_rand($ttfs)];
376            $font[$i]['angle'] = $this->angle ? mt_rand(-30, 30) : 0;
377            $font[$i]['size']  = $this->_getFontSize();
378
379            // 取得使用 TrueType 字体的文本的范围
380            $box = imagettfbbox($font[$i]['size'], 0, $font[$i]['font'], $seccode[$i]);
381            $font[$i]['zheight'] = max($box[1], $box[3]) - min($box[5], $box[7]);
382            $box = imagettfbbox($font[$i]['size'], $font[$i]['angle'], $font[$i]['font'], $seccode[$i]);
383            $font[$i]['height'] = max($box[1], $box[3]) - min($box[5], $box[7]);
384            $font[$i]['hd'] = $font[$i]['height'] - $font[$i]['zheight'];
385            $font[$i]['width'] = (max($box[2], $box[4]) - min($box[0], $box[6])) + mt_rand(0, $this->width / 8);
386            if ($font[$i]['width'] > $this->width / $length) {
387                $font[$i]['width'] = $this->width / $length;
388            }
389            $totalWidth += $font[$i]['width'];
390        }
391
392        // X坐标
393        $x = $font[0]['angle'] > 0
394             ? $this->_mt_rand(cos(deg2rad(90 - $font[0]['angle'])) * $font[0]['zheight'], $this->width - $totalWidth)
395             : $this->_mt_rand(1, $this->width - $totalWidth);
396
397        if (!$this->color) {
398            $textColor = imagecolorallocate($im, $this->_fontColor[0], $this->_fontColor[1], $this->_fontColor[2]);
399        }
400
401        for($i = 0; $i < $length; $i++) {
402
403            // 随机字体颜色
404            if ($this->color) {
405                $this->_fontColor = $this->_getFontColor();
406                $textColor = imagecolorallocate($im, $this->_fontColor[0], $this->_fontColor[1], $this->_fontColor[2]);
407            }
408
409            // Y坐标
410            $y = $font[$i]['angle'] > 0
411                 ? $this->_mt_rand($font[$i]['height'], $this->height)
412                 : $this->_mt_rand($font[$i]['height'] - $font[$i]['hd'], $this->height - $font[$i]['hd']);
413
414            // 文字阴影
415            if ($this->shadow) {
416                $shadowColor = imagecolorallocate($im, 255 - $this->_fontColor[0], 255 - $this->_fontColor[1], 255 - $this->_fontColor[2]);
417                imagettftext($im, $font[$i]['size'], $font[$i]['angle'], $x + 1, $y + 1, $shadowColor, $font[$i]['font'], $seccode[$i]);
418            }
419
420            // 用 TrueType 字体向图像写入文本
421            imagettftext($im, $font[$i]['size'], $font[$i]['angle'], $x, $y, $textColor, $font[$i]['font'], $seccode[$i]);
422
423            $x += $font[$i]['width'];
424        }
425    }
426
427    /**
428     * 使用GIF图片字体
429     *
430     * @return void
431     */
432    private function _gifFont($im) {
433        $seccode = $this->_code;
434
435        $seccodedir = array();
436
437        if (function_exists('imagecreatefromgif')) {
438            $folder = $this->dataPath . '/gif/';
439            $dirs = opendir($folder);
440            while($dir = readdir($dirs)) {
441                if ($dir != '.' && $dir != '..' && file_exists($folder.$dir.'/9.gif')) {
442                    $seccodedir[] = $dir;
443                }
444            }
445        }
446        $widthtotal = 0;
447
448        for($i = 0; $i <= 3; $i++) {
449            $this->imcodefile = $seccodedir ? $folder.$seccodedir[array_rand($seccodedir)].'/'.strtolower($seccode[$i]).'.gif' : '';
450            if (!empty($this->imcodefile) && file_exists($this->imcodefile)) {
451                $font[$i]['file'] = $this->imcodefile;
452                $font[$i]['data'] = getimagesize($this->imcodefile);
453                $font[$i]['width'] = $font[$i]['data'][0] + mt_rand(0, 6) - 4;
454                $font[$i]['height'] = $font[$i]['data'][1] + mt_rand(0, 6) - 4;
455                $font[$i]['width'] += mt_rand(0, $this->width / 5 - $font[$i]['width']);
456                $widthtotal += $font[$i]['width'];
457            } else {
458                $font[$i]['file'] = '';
459                $font[$i]['width'] = 8 + mt_rand(0, $this->width / 5 - 5);
460                $widthtotal += $font[$i]['width'];
461            }
462        }
463        $x = mt_rand(1, $this->width - $widthtotal);
464        for($i = 0; $i <= 3; $i++) {
465            $this->color && $this->_fontColor = array(mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
466            if ($font[$i]['file']) {
467                $this->imcode = imagecreatefromgif ($font[$i]['file']);
468                if ($this->size) {
469                    $font[$i]['width'] = mt_rand($font[$i]['width'] - $this->width / 20, $font[$i]['width'] + $this->width / 20);
470                    $font[$i]['height'] = mt_rand($font[$i]['height'] - $this->width / 20, $font[$i]['height'] + $this->width / 20);
471                }
472                $y = mt_rand(0, $this->height - $font[$i]['height']);
473                if ($this->shadow) {
474                    $this->imcodeshadow = $this->imcode;
475                    imagecolorset($this->imcodeshadow, 0 , 255 - $this->_fontColor[0], 255 - $this->_fontColor[1], 255 - $this->_fontColor[2]);
476                    imagecopyresized($im, $this->imcodeshadow, $x + 1, $y + 1, 0, 0, $font[$i]['width'], $font[$i]['height'], $font[$i]['data'][0], $font[$i]['data'][1]);
477                }
478                imagecolorset($this->imcode, 0 , $this->_fontColor[0], $this->_fontColor[1], $this->_fontColor[2]);
479                imagecopyresized($im, $this->imcode, $x, $y, 0, 0, $font[$i]['width'], $font[$i]['height'], $font[$i]['data'][0], $font[$i]['data'][1]);
480            } else {
481                $y = mt_rand(0, $this->height - 20);
482                if ($this->shadow) {
483                    $text_shadowcolor = imagecolorallocate($im, 255 - $this->_fontColor[0], 255 - $this->_fontColor[1], 255 - $this->_fontColor[2]);
484                    imagechar($im, 5, $x + 1, $y + 1, $seccode[$i], $text_shadowcolor);
485                }
486                $text_color = imagecolorallocate($im, $this->_fontColor[0], $this->_fontColor[1], $this->_fontColor[2]);
487                imagechar($im, 5, $x, $y, $seccode[$i], $text_color);
488            }
489            $x += $font[$i]['width'];
490        }
491    }
492
493    /**
494     * 获取颜色
495     *
496     * @return array
497     */
498    private function _getFontColor()
499    {
500        // 随机颜色
501        if ($this->color) {
502            $color = array(mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
503        } else {
504            $color = $this->_fontColor;
505        }
506        return $color;
507    }
508
509    /**
510     * 获取字体大小
511     *
512     * @return int
513     */
514    private function _getFontSize()
515    {
516        $size = $this->_isEn ? $this->width / 6 : $this->width / 7;
517
518        // 随机字体大小
519        if ($this->size) {
520            $size = mt_rand(($size - $this->width / 40), ($size + $this->width / 20));
521        }
522
523        return $size;
524    }
525
526    /**
527     * 生成BMP图片
528     *
529     * @return void
530     */
531    private function _bitmap() {
532        $numbers = array (
533            'B' => array('00','fc','66','66','66','7c','66','66','fc','00'),
534            'C' => array('00','38','64','c0','c0','c0','c4','64','3c','00'),
535            'E' => array('00','fe','62','62','68','78','6a','62','fe','00'),
536            'F' => array('00','f8','60','60','68','78','6a','62','fe','00'),
537            'G' => array('00','78','cc','cc','de','c0','c4','c4','7c','00'),
538            'H' => array('00','e7','66','66','66','7e','66','66','e7','00'),
539            'J' => array('00','f8','cc','cc','cc','0c','0c','0c','7f','00'),
540            'K' => array('00','f3','66','66','7c','78','6c','66','f7','00'),
541            'M' => array('00','f7','63','6b','6b','77','77','77','e3','00'),
542            'P' => array('00','f8','60','60','7c','66','66','66','fc','00'),
543            'Q' => array('00','78','cc','cc','cc','cc','cc','cc','78','00'),
544            'R' => array('00','f3','66','6c','7c','66','66','66','fc','00'),
545            'T' => array('00','78','30','30','30','30','b4','b4','fc','00'),
546            'V' => array('00','1c','1c','36','36','36','63','63','f7','00'),
547            'W' => array('00','36','36','36','77','7f','6b','63','f7','00'),
548            'X' => array('00','f7','66','3c','18','18','3c','66','ef','00'),
549            'Y' => array('00','7e','18','18','18','3c','24','66','ef','00'),
550            '2' => array('fc','c0','60','30','18','0c','cc','cc','78','00'),
551            '3' => array('78','8c','0c','0c','38','0c','0c','8c','78','00'),
552            '4' => array('00','3e','0c','fe','4c','6c','2c','3c','1c','1c'),
553            '6' => array('78','cc','cc','cc','ec','d8','c0','60','3c','00'),
554            '7' => array('30','30','38','18','18','18','1c','8c','fc','00'),
555            '8' => array('78','cc','cc','cc','78','cc','cc','cc','78','00'),
556            '9' => array('f0','18','0c','6c','dc','cc','cc','cc','78','00')
557        );
558
559        foreach($numbers as $i => $number) {
560            for($j = 0; $j < 6; $j++) {
561                $a1 = substr('012', mt_rand(0, 2), 1) . substr('012345', mt_rand(0, 5), 1);
562                $a2 = substr('012345', mt_rand(0, 5), 1) . substr('0123', mt_rand(0, 3), 1);
563                mt_rand(0, 1) == 1 ? array_push($numbers[$i], $a1) : array_unshift($numbers[$i], $a1);
564                mt_rand(0, 1) == 0 ? array_push($numbers[$i], $a1) : array_unshift($numbers[$i], $a2);
565            }
566        }
567
568        $bitmap = array();
569        for($i = 0; $i < 20; $i++) {
570            for($j = 0; $j <= 3; $j++) {
571                $bytes = $numbers[$this->_code[$j]][$i];
572                $a = mt_rand(0, 14);
573                array_push($bitmap, $bytes);
574            }
575        }
576
577        for($i = 0; $i < 8; $i++) {
578            $a = substr('012345', mt_rand(0, 2), 1) . substr('012345', mt_rand(0, 5), 1);
579            array_unshift($bitmap, $a);
580            array_push($bitmap, $a);
581        }
582
583        $image = pack('H*', '424d9e000000000000003e0000002800000020000000180000'
584                          . '00010001000000000060000000000000000000000000000000'
585                          . '0000000000000000FFFFFF00' . implode('', $bitmap));
586
587        header('Content-Type: image/bmp');
588        echo $image;
589    }
590
591    /**
592     * 随机获取范围值
593     *
594     * @param int $min
595     * @param int $max
596     * @return int
597     */
598    private function _mt_rand($min, $max)
599    {
600        if ($min > $max) {
601            $tmp = $max;
602            $max = $min;
603            $min = $tmp;
604        }
605
606        return mt_rand($min, $max);
607    }
608}