3 * Require Image_Text class for generating the text.
5 require_once 'Text/CAPTCHA.php';
6 require_once 'Image/Text.php';
9 * Text_CAPTCHA_Driver_Image - Text_CAPTCHA driver graphical CAPTCHAs
11 * Class to create a graphical Turing test
13 * @author Christian Wenz <wenz@php.net>
14 * @license BSD License
15 * @todo refine the obfuscation algorithm :-)
16 * @todo consider removing Image_Text dependency
19 class Text_CAPTCHA_Driver_Image extends Text_CAPTCHA
55 * CAPTCHA output format
63 * Further options (here: for Image_Text)
68 var $_imageOptions = array(
71 'font_file' => 'COUR.TTF',
72 'text_color' => '#000000',
73 'lines_color' => '#CACACA',
74 'background_color' => '#555555',
75 'antialias' => false);
78 * Whether the immage resource has been created
83 var $_created = false;
96 * Initializes the new Text_CAPTCHA_Driver_Image object and creates a GD image
98 * @param array $options CAPTCHA options
101 * @return mixed true upon success, PEAR error otherwise
103 function init($options = array())
106 if (!is_array($options)) {
107 // Compatibility mode ... in future versions, these two
108 // lines of code will be used:
109 // $this->_error = PEAR::raiseError('You need to provide a set of CAPTCHA options!');
110 // return $this->_error;
112 $args = func_get_args();
113 if (isset($args[0])) {
114 $o['width'] = $args[0];
116 if (isset($args[1])) {
117 $o['height'] = $args[1];
119 if (isset($args[2]) && $args[2] != null) {
120 $o['phrase'] = $args[2];
122 if (isset($args[3]) && is_array($args[3])) {
123 $o['imageOptions'] = $args[3];
127 if (is_array($options)) {
128 if (isset($options['width']) && is_int($options['width'])) {
129 $this->_width = $options['width'];
133 if (isset($options['height']) && is_int($options['height'])) {
134 $this->_height = $options['height'];
138 if (!isset($options['phrase']) || empty($options['phrase'])) {
139 $phraseoptions = (isset($options['phraseOptions']) && is_array($options['phraseOptions'])) ? $options['phraseOptions'] : array();
140 $this->_createPhrase($phraseoptions);
142 $this->_phrase = $options['phrase'];
144 if (!isset($options['output']) || empty($options['output'])) {
145 $this->_output = 'resource';
147 $this->_output = $options['output'];
149 if (isset($options['imageOptions']) && is_array($options['imageOptions']) && count($options['imageOptions']) > 0) {
150 $this->_imageOptions = array_merge($this->_imageOptions, $options['imageOptions']);
159 * Create random CAPTCHA phrase, Image edition (with size check)
161 * This method creates a random phrase, maximum 8 characters or width / 25, whatever is smaller
163 * @param array $options Optionally supply advanced options for the phrase creation
168 function _createPhrase($options = array())
170 $len = intval(min(8, $this->_width / 25));
171 if (!is_array($options) || count($options) === 0) {
172 $this->_phrase = Text_Password::create($len);
174 if (count($options) === 1) {
175 $this->_phrase = Text_Password::create($len, $options[0]);
177 $this->_phrase = Text_Password::create($len, $options[0], $options[1]);
180 $this->_created = false;
184 * Create CAPTCHA image
186 * This method creates a CAPTCHA image
189 * @return void PEAR_Error on error
191 function _createCAPTCHA()
194 return $this->_error;
196 if ($this->_created) {
199 $options['canvas'] = array(
200 'width' => $this->_width,
201 'height' => $this->_height
203 $options['width'] = $this->_width - 20;
204 $options['height'] = $this->_height - 20;
205 $options['cx'] = ceil(($this->_width) / 2 + 10);
206 $options['cy'] = ceil(($this->_height) / 2 + 10);
207 $options['angle'] = rand(0, 30) - 15;
208 $options['font_size'] = $this->_imageOptions['font_size'];
209 $options['font_path'] = $this->_imageOptions['font_path'];
210 $options['font_file'] = $this->_imageOptions['font_file'];
211 $options['color'] = array($this->_imageOptions['text_color']);
212 $options['background_color'] = $this->_imageOptions['background_color'];
213 $options['max_lines'] = 1;
214 $options['mode'] = 'auto';
216 $this->_imt = new Image_Text(
222 if (PEAR::isError($e = $this->_imt->init())) {
223 $this->_error = PEAR::staticRaiseError(
224 sprintf('Error initializing Image_Text (%s)', $e->getMessage()));
225 return $this->_error;
227 $this->_created = true;
229 $result = $this->_imt->measurize();
230 } while ($result === false && --$options['font_size'] > 0);
231 if ($result === false) {
232 $this->_error = PEAR::raiseError('The text provided does not fit in the image dimensions');
233 return $this->_error;
235 $this->_imt->render();
236 $this->_im =& $this->_imt->getImg();
238 if (isset($this->_imageOptions['antialias']) && $this->_imageOptions['antialias'] === true && function_exists('imageantialias')) {
239 imageantialias($this->_im, true);
242 $colors = $this->_imt->_convertString2RGB($this->_imageOptions['lines_color']);
243 $lines_color = imagecolorallocate($this->_im, $colors['r'], $colors['g'], $colors['b']);
245 for ($i = 0; $i < 3; $i++) {
246 $x1 = rand(0, $this->_width - 1);
247 $y1 = rand(0, round($this->_height / 10, 0));
248 $x2 = rand(0, round($this->_width / 10, 0));
249 $y2 = rand(0, $this->_height - 1);
250 imageline($this->_im, $x1, $y1, $x2, $y2, $lines_color);
251 $x1 = rand(0, $this->_width - 1);
252 $y1 = $this->_height - rand(1, round($this->_height / 10, 0));
253 $x2 = $this->_width - rand(1, round($this->_width / 10, 0));
254 $y2 = rand(0, $this->_height - 1);
255 imageline($this->_im, $x1, $y1, $x2, $y2, $lines_color);
256 $cx = rand(0, $this->_width - 50) + 25;
257 $cy = rand(0, $this->_height - 50) + 25;
259 imagearc($this->_im, $cx, $cy, $w, $w, 0, 360, $lines_color);
264 * Return CAPTCHA as image resource
266 * This method returns the CAPTCHA depending on the output format
269 * @return mixed image resource or PEAR error
271 function getCAPTCHA()
273 $retval = $this->_createCAPTCHA();
274 if (PEAR::isError($retval)) {
275 return PEAR::staticRaiseError($retval->getMessage());
278 if ($this->_output == 'gif' && !function_exists('imagegif')) {
279 $this->_output = 'png';
282 switch ($this->_output) {
284 return $this->getCAPTCHAAsPNG();
288 return $this->getCAPTCHAAsJPEG();
291 return $this->getCAPTCHAAsGIF();
300 * Return CAPTCHA as PNG
302 * This method returns the CAPTCHA as PNG
305 * @return mixed image contents or PEAR error
307 function getCAPTCHAAsPNG()
309 $retval = $this->_createCAPTCHA();
310 if (PEAR::isError($retval)) {
311 return PEAR::staticRaiseError($retval->getMessage());
313 if (is_object($this->_im)) {
315 imagepng($this->_im);
316 $data = ob_get_contents();
320 $this->_error = PEAR::staticRaiseError('Error creating CAPTCHA image (font missing?!)');
321 return $this->_error;
326 * Return CAPTCHA as JPEG
328 * This method returns the CAPTCHA as JPEG
331 * @return mixed image contents or PEAR error
333 function getCAPTCHAAsJPEG()
335 $retval = $this->_createCAPTCHA();
336 if (PEAR::isError($retval)) {
337 return PEAR::raiseError($retval->getMessage());
340 if (is_object($this->_im)) {
342 imagejpeg($this->_im);
343 $data = ob_get_contents();
347 $this->_error = PEAR::raiseError('Error creating CAPTCHA image (font missing?!)');
348 return $this->_error;
353 * Return CAPTCHA as GIF
355 * This method returns the CAPTCHA as GIF
358 * @return mixed image contents or PEAR error
360 function getCAPTCHAAsGIF()
362 $retval = $this->_createCAPTCHA();
363 if (PEAR::isError($retval)) {
364 return PEAR::raiseError($retval->getMessage());
367 if (is_object($this->_im)) {
369 imagegif($this->_im);
370 $data = ob_get_contents();
374 $this->_error = PEAR::raiseError('Error creating CAPTCHA image (font missing?!)');
375 return $this->_error;
380 * __wakeup method (PHP 5 only)
386 $this->_created = false;