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())
105 if (!is_array($options)) {
106 // Compatibility mode ... in future versions, these two
107 // lines of code will be used:
108 // $this->_error = PEAR::raiseError('You need to provide a set of CAPTCHA options!');
109 // return $this->_error;
111 $args = func_get_args();
112 if (isset($args[0])) {
113 $o['width'] = $args[0];
115 if (isset($args[1])) {
116 $o['height'] = $args[1];
118 if (isset($args[2]) && $args[2] != null) {
119 $o['phrase'] = $args[2];
121 if (isset($args[3]) && is_array($args[3])) {
122 $o['imageOptions'] = $args[3];
126 if (is_array($options)) {
127 if (isset($options['width']) && is_int($options['width'])) {
128 $this->_width = $options['width'];
132 if (isset($options['height']) && is_int($options['height'])) {
133 $this->_height = $options['height'];
137 if (!isset($options['phrase']) || empty($options['phrase'])) {
138 $phraseoptions = (isset($options['phraseOptions']) && is_array($options['phraseOptions'])) ? $options['phraseOptions'] : array();
139 $this->_createPhrase($phraseoptions);
141 $this->_phrase = $options['phrase'];
143 if (!isset($options['output']) || empty($options['output'])) {
144 $this->_output = 'resource';
146 $this->_output = $options['output'];
148 if (isset($options['imageOptions']) && is_array($options['imageOptions']) && count($options['imageOptions']) > 0) {
149 $this->_imageOptions = array_merge($this->_imageOptions, $options['imageOptions']);
156 * Create random CAPTCHA phrase, Image edition (with size check)
158 * This method creates a random phrase, maximum 8 characters or width / 25, whatever is smaller
160 * @param array $options Optionally supply advanced options for the phrase creation
165 function _createPhrase($options = array())
167 $len = intval(min(8, $this->_width / 25));
168 if (!is_array($options) || count($options) === 0) {
169 $this->_phrase = Text_Password::create($len);
171 if (count($options) === 1) {
172 $this->_phrase = Text_Password::create($len, $options[0]);
174 $this->_phrase = Text_Password::create($len, $options[0], $options[1]);
177 $this->_created = false;
181 * Create CAPTCHA image
183 * This method creates a CAPTCHA image
186 * @return void PEAR_Error on error
188 function _createCAPTCHA()
191 return $this->_error;
193 if ($this->_created) {
196 $options['canvas'] = array(
197 'width' => $this->_width,
198 'height' => $this->_height
200 $options['width'] = $this->_width - 20;
201 $options['height'] = $this->_height - 20;
202 $options['cx'] = ceil(($this->_width) / 2 + 10);
203 $options['cy'] = ceil(($this->_height) / 2 + 10);
204 $options['angle'] = rand(0, 30) - 15;
205 $options['font_size'] = $this->_imageOptions['font_size'];
206 $options['font_path'] = $this->_imageOptions['font_path'];
207 $options['font_file'] = $this->_imageOptions['font_file'];
208 $options['color'] = array($this->_imageOptions['text_color']);
209 $options['background_color'] = $this->_imageOptions['background_color'];
210 $options['max_lines'] = 1;
211 $options['mode'] = 'auto';
213 $this->_imt = new Image_Text(
217 if (PEAR::isError($e = $this->_imt->init())) {
218 $this->_error = PEAR::staticRaiseError(
219 sprintf('Error initializing Image_Text (%s)', $e->getMessage()));
220 return $this->_error;
222 $this->_created = true;
224 $result = $this->_imt->measurize();
225 } while ($result === false && --$options['font_size'] > 0);
226 if ($result === false) {
227 $this->_error = PEAR::raiseError('The text provided does not fit in the image dimensions');
228 return $this->_error;
230 $this->_imt->render();
231 $this->_im =& $this->_imt->getImg();
233 if (isset($this->_imageOptions['antialias']) && $this->_imageOptions['antialias'] === true && function_exists('imageantialias')) {
234 imageantialias($this->_im, true);
237 $colors = $this->_imt->_convertString2RGB($this->_imageOptions['lines_color']);
238 $lines_color = imagecolorallocate($this->_im, $colors['r'], $colors['g'], $colors['b']);
240 for ($i = 0; $i < 3; $i++) {
241 $x1 = rand(0, $this->_width - 1);
242 $y1 = rand(0, round($this->_height / 10, 0));
243 $x2 = rand(0, round($this->_width / 10, 0));
244 $y2 = rand(0, $this->_height - 1);
245 imageline($this->_im, $x1, $y1, $x2, $y2, $lines_color);
246 $x1 = rand(0, $this->_width - 1);
247 $y1 = $this->_height - rand(1, round($this->_height / 10, 0));
248 $x2 = $this->_width - rand(1, round($this->_width / 10, 0));
249 $y2 = rand(0, $this->_height - 1);
250 imageline($this->_im, $x1, $y1, $x2, $y2, $lines_color);
251 $cx = rand(0, $this->_width - 50) + 25;
252 $cy = rand(0, $this->_height - 50) + 25;
254 imagearc($this->_im, $cx, $cy, $w, $w, 0, 360, $lines_color);
259 * Return CAPTCHA as image resource
261 * This method returns the CAPTCHA depending on the output format
264 * @return mixed image resource or PEAR error
266 function getCAPTCHA()
268 $retval = $this->_createCAPTCHA();
269 if (PEAR::isError($retval)) {
270 return PEAR::staticRaiseError($retval->getMessage());
273 if ($this->_output == 'gif' && !function_exists('imagegif')) {
274 $this->_output = 'png';
277 switch ($this->_output) {
279 return $this->getCAPTCHAAsPNG();
283 return $this->getCAPTCHAAsJPEG();
286 return $this->getCAPTCHAAsGIF();
295 * Return CAPTCHA as PNG
297 * This method returns the CAPTCHA as PNG
300 * @return mixed image contents or PEAR error
302 function getCAPTCHAAsPNG()
304 $retval = $this->_createCAPTCHA();
305 if (PEAR::isError($retval)) {
306 return PEAR::staticRaiseError($retval->getMessage());
309 if (is_resource($this->_im)) {
311 imagepng($this->_im);
312 $data = ob_get_contents();
316 $this->_error = PEAR::raiseError('Error creating CAPTCHA image (font missing?!)');
317 return $this->_error;
322 * Return CAPTCHA as JPEG
324 * This method returns the CAPTCHA as JPEG
327 * @return mixed image contents or PEAR error
329 function getCAPTCHAAsJPEG()
331 $retval = $this->_createCAPTCHA();
332 if (PEAR::isError($retval)) {
333 return PEAR::raiseError($retval->getMessage());
336 if (is_resource($this->_im)) {
338 imagejpeg($this->_im);
339 $data = ob_get_contents();
343 $this->_error = PEAR::raiseError('Error creating CAPTCHA image (font missing?!)');
344 return $this->_error;
349 * Return CAPTCHA as GIF
351 * This method returns the CAPTCHA as GIF
354 * @return mixed image contents or PEAR error
356 function getCAPTCHAAsGIF()
358 $retval = $this->_createCAPTCHA();
359 if (PEAR::isError($retval)) {
360 return PEAR::raiseError($retval->getMessage());
363 if (is_resource($this->_im)) {
365 imagegif($this->_im);
366 $data = ob_get_contents();
370 $this->_error = PEAR::raiseError('Error creating CAPTCHA image (font missing?!)');
371 return $this->_error;
376 * __wakeup method (PHP 5 only)
382 $this->_created = false;