PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

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