PageRenderTime 39ms CodeModel.GetById 21ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/Ojoo/Lib/captcha_1.php

https://bitbucket.org/PhEnIxMo_On/ojoo
PHP | 539 lines | 224 code | 186 blank | 129 comment | 34 complexity | 740eb1d550c44834744b60cf97453e72 MD5 | raw file
  1<?php
  2/**
  3 * Script para la generaci´┐Żn de CAPTCHAS
  4 *
  5 * @author  Jose Rodriguez <jose.rodriguez@exec.cl>
  6 * @license GPLv3
  7 * @link    http://code.google.com/p/cool-php-captcha
  8 * @package captcha
  9 * @version 0.3
 10 *
 11 */
 12
 13
 14session_start();
 15
 16
 17
 18$captcha = new SimpleCaptcha();
 19
 20
 21
 22// OPTIONAL Change configuration...
 23//$captcha->wordsFile = 'words/es.php';
 24//$captcha->session_var = 'secretword';
 25//$captcha->imageFormat = 'png';
 26//$captcha->lineWidth = 3;
 27//$captcha->scale = 3; $captcha->blur = true;
 28//$captcha->resourcesPath = "/var/cool-php-captcha/resources";
 29
 30// OPTIONAL Simple autodetect language example
 31/*
 32if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
 33    $langs = array('en', 'es');
 34    $lang  = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
 35    if (in_array($lang, $langs)) {
 36        $captcha->wordsFile = "words/$lang.php";
 37    }
 38}
 39*/
 40
 41
 42
 43// Image generation
 44$captcha->CreateImage();
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74/**
 75 * SimpleCaptcha class
 76 *
 77 */
 78class SimpleCaptcha {
 79
 80    /** Width of the image */
 81    public $width  = 200;
 82
 83    /** Height of the image */
 84    public $height = 70;
 85
 86    /** Dictionary word file (empty for random text) */
 87    public $wordsFile = 'words/en.php';
 88
 89    /**
 90     * Path for resource files (fonts, words, etc.)
 91     *
 92     * "resources" by default. For security reasons, is better move this
 93     * directory to another location outise the web server
 94     *
 95     */
 96    public $resourcesPath = 'resources';
 97
 98    /** Min word length (for non-dictionary random text generation) */
 99    public $minWordLength = 5;
100
101    /**
102     * Max word length (for non-dictionary random text generation)
103     * 
104     * Used for dictionary words indicating the word-length
105     * for font-size modification purposes
106     */
107    public $maxWordLength = 8;
108
109    /** Sessionname to store the original text */
110    public $session_var = 'captcha';
111
112    /** Background color in RGB-array */
113    public $backgroundColor = array(255, 255, 255);
114
115    /** Foreground colors in RGB-array */
116    public $colors = array(
117        array(27,78,181), // blue
118        array(22,163,35), // green
119        array(214,36,7),  // red
120    );
121
122    /** Shadow color in RGB-array or null */
123    public $shadowColor = null; //array(0, 0, 0);
124
125    /** Horizontal line through the text */
126    public $lineWidth = 0;
127
128    /**
129     * Font configuration
130     *
131     * - font: TTF file
132     * - spacing: relative pixel space between character
133     * - minSize: min font size
134     * - maxSize: max font size
135     */
136    public $fonts = array(
137        'Antykwa'  => array('spacing' => -3, 'minSize' => 27, 'maxSize' => 30, 'font' => 'AntykwaBold.ttf'),
138        'Candice'  => array('spacing' =>-1.5,'minSize' => 28, 'maxSize' => 31, 'font' => 'Candice.ttf'),
139        'DingDong' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 30, 'font' => 'Ding-DongDaddyO.ttf'),
140        'Duality'  => array('spacing' => -2, 'minSize' => 30, 'maxSize' => 38, 'font' => 'Duality.ttf'),
141        'Heineken' => array('spacing' => -2, 'minSize' => 24, 'maxSize' => 34, 'font' => 'Heineken.ttf'),
142        'Jura'     => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 32, 'font' => 'Jura.ttf'),
143        'StayPuft' => array('spacing' =>-1.5,'minSize' => 28, 'maxSize' => 32, 'font' => 'StayPuft.ttf'),
144        'Times'    => array('spacing' => -2, 'minSize' => 28, 'maxSize' => 34, 'font' => 'TimesNewRomanBold.ttf'),
145        'VeraSans' => array('spacing' => -1, 'minSize' => 20, 'maxSize' => 28, 'font' => 'VeraSansBold.ttf'),
146    );
147
148    /** Wave configuracion in X and Y axes */
149    public $Yperiod    = 12;
150    public $Yamplitude = 14;
151    public $Xperiod    = 11;
152    public $Xamplitude = 5;
153
154    /** letter rotation clockwise */
155    public $maxRotation = 8;
156
157    /**
158     * Internal image size factor (for better image quality)
159     * 1: low, 2: medium, 3: high
160     */
161    public $scale = 2;
162
163    /** 
164     * Blur effect for better image quality (but slower image processing).
165     * Better image results with scale=3
166     */
167    public $blur = false;
168
169    /** Debug? */
170    public $debug = false;
171    
172    /** Image format: jpeg or png */
173    public $imageFormat = 'jpeg';
174
175
176    /** GD image */
177    public $im;
178
179
180
181
182
183
184
185
186
187
188    public function __construct($config = array()) {
189    }
190
191
192
193
194
195
196
197    public function CreateImage() {
198        $ini = microtime(true);
199
200        /** Initialization */
201        $this->ImageAllocate();
202        
203        /** Text insertion */
204        $text = $this->GetCaptchaText();
205        $fontcfg  = $this->fonts[array_rand($this->fonts)];
206        $this->WriteText($text, $fontcfg);
207
208        $_SESSION[$this->session_var] = $text;
209
210        /** Transformations */
211        if (!empty($this->lineWidth)) {
212            $this->WriteLine();
213        }
214        $this->WaveImage();
215        if ($this->blur && function_exists('imagefilter')) {
216            imagefilter($this->im, IMG_FILTER_GAUSSIAN_BLUR);
217        }
218        $this->ReduceImage();
219
220
221        if ($this->debug) {
222            imagestring($this->im, 1, 1, $this->height-8,
223                "$text {$fontcfg['font']} ".round((microtime(true)-$ini)*1000)."ms",
224                $this->GdFgColor
225            );
226        }
227
228
229        /** Output */
230        $this->WriteImage();
231        $this->Cleanup();
232    }
233
234
235
236
237
238
239
240
241
242    /**
243     * Creates the image resources
244     */
245    protected function ImageAllocate() {
246        // Cleanup
247        if (!empty($this->im)) {
248            imagedestroy($this->im);
249        }
250
251        $this->im = imagecreatetruecolor($this->width*$this->scale, $this->height*$this->scale);
252
253        // Background color
254        $this->GdBgColor = imagecolorallocate($this->im,
255            $this->backgroundColor[0],
256            $this->backgroundColor[1],
257            $this->backgroundColor[2]
258        );
259        imagefilledrectangle($this->im, 0, 0, $this->width*$this->scale, $this->height*$this->scale, $this->GdBgColor);
260
261        // Foreground color
262        $color           = $this->colors[mt_rand(0, sizeof($this->colors)-1)];
263        $this->GdFgColor = imagecolorallocate($this->im, $color[0], $color[1], $color[2]);
264
265        // Shadow color
266        if (!empty($this->shadowColor) && is_array($this->shadowColor) && sizeof($this->shadowColor) >= 3) {
267            $this->GdShadowColor = imagecolorallocate($this->im,
268                $this->shadowColor[0],
269                $this->shadowColor[1],
270                $this->shadowColor[2]
271            );
272        }
273    }
274
275
276
277
278
279    /**
280     * Text generation
281     *
282     * @return string Text
283     */
284    protected function GetCaptchaText() {
285        $text = $this->GetDictionaryCaptchaText();
286        if (!$text) {
287            $text = $this->GetRandomCaptchaText();
288        }
289        return $text;
290    }
291
292
293
294
295
296
297    /**
298     * Random text generation
299     *
300     * @return string Text
301     */
302    protected function GetRandomCaptchaText($length = null) {
303        if (empty($length)) {
304            $length = rand($this->minWordLength, $this->maxWordLength);
305        }
306
307        $words  = "abcdefghijlmnopqrstvwyz";
308        $vocals = "aeiou";
309
310        $text  = "";
311        $vocal = rand(0, 1);
312        for ($i=0; $i<$length; $i++) {
313            if ($vocal) {
314                $text .= substr($vocals, mt_rand(0, 4), 1);
315            } else {
316                $text .= substr($words, mt_rand(0, 22), 1);
317            }
318            $vocal = !$vocal;
319        }
320        return $text;
321    }
322
323
324
325
326
327
328
329
330
331    /**
332     * Random dictionary word generation
333     *
334     * @param boolean $extended Add extended "fake" words
335     * @return string Word
336     */
337    function GetDictionaryCaptchaText($extended = false) {
338        if (empty($this->wordsFile)) {
339            return false;
340        }
341
342        // Full path of words file
343        if (substr($this->wordsFile, 0, 1) == '/') {
344            $wordsfile = $this->wordsFile;
345        } else {
346            $wordsfile = $this->resourcesPath.'/'.$this->wordsFile;
347        }
348
349        if (!file_exists($wordsfile)) {
350            return false;
351        }
352
353        $fp     = fopen($wordsfile, "r");
354        $length = strlen(fgets($fp));
355        if (!$length) {
356            return false;
357        }
358        $line   = rand(1, (filesize($wordsfile)/$length)-2);
359        if (fseek($fp, $length*$line) == -1) {
360            return false;
361        }
362        $text = trim(fgets($fp));
363        fclose($fp);
364
365
366        /** Change ramdom volcals */
367        if ($extended) {
368            $text   = preg_split('//', $text, -1, PREG_SPLIT_NO_EMPTY);
369            $vocals = array('a', 'e', 'i', 'o', 'u');
370            foreach ($text as $i => $char) {
371                if (mt_rand(0, 1) && in_array($char, $vocals)) {
372                    $text[$i] = $vocals[mt_rand(0, 4)];
373                }
374            }
375            $text = implode('', $text);
376        }
377
378        return $text;
379    }
380
381
382
383
384
385
386
387
388    /**
389     * Horizontal line insertion
390     */
391    protected function WriteLine() {
392
393        $x1 = $this->width*$this->scale*.15;
394        $x2 = $this->textFinalX;
395        $y1 = rand($this->height*$this->scale*.40, $this->height*$this->scale*.65);
396        $y2 = rand($this->height*$this->scale*.40, $this->height*$this->scale*.65);
397        $width = $this->lineWidth/2*$this->scale;
398
399        for ($i = $width*-1; $i <= $width; $i++) {
400            imageline($this->im, $x1, $y1+$i, $x2, $y2+$i, $this->GdFgColor);
401        }
402    }
403
404
405
406
407    /**
408     * Text insertion
409     */
410    protected function WriteText($text, $fontcfg = array()) {
411        if (empty($fontcfg)) {
412            // Select the font configuration
413            $fontcfg  = $this->fonts[array_rand($this->fonts)];
414        }
415
416        // Full path of font file
417        $fontfile = $this->resourcesPath.'/fonts/'.$fontcfg['font'];
418
419
420        /** Increase font-size for shortest words: 9% for each glyp missing */
421        $lettersMissing = $this->maxWordLength-strlen($text);
422        $fontSizefactor = 1+($lettersMissing*0.09);
423
424        // Text generation (char by char)
425        $x      = 20*$this->scale;
426        $y      = round(($this->height*27/40)*$this->scale);
427        $length = strlen($text);
428        for ($i=0; $i<$length; $i++) {
429            $degree   = rand($this->maxRotation*-1, $this->maxRotation);
430            $fontsize = rand($fontcfg['minSize'], $fontcfg['maxSize'])*$this->scale*$fontSizefactor;
431            $letter   = substr($text, $i, 1);
432
433            if ($this->shadowColor) {
434                $coords = imagettftext($this->im, $fontsize, $degree,
435                    $x+$this->scale, $y+$this->scale,
436                    $this->GdShadowColor, $fontfile, $letter);
437            }
438            $coords = imagettftext($this->im, $fontsize, $degree,
439                $x, $y,
440                $this->GdFgColor, $fontfile, $letter);
441            $x += ($coords[2]-$x) + ($fontcfg['spacing']*$this->scale);
442        }
443
444        $this->textFinalX = $x;
445    }
446
447
448
449    /**
450     * Wave filter
451     */
452    protected function WaveImage() {
453        // X-axis wave generation
454        $xp = $this->scale*$this->Xperiod*rand(1,3);
455        $k = rand(0, 100);
456        for ($i = 0; $i < ($this->width*$this->scale); $i++) {
457            imagecopy($this->im, $this->im,
458                $i-1, sin($k+$i/$xp) * ($this->scale*$this->Xamplitude),
459                $i, 0, 1, $this->height*$this->scale);
460        }
461
462        // Y-axis wave generation
463        $k = rand(0, 100);
464        $yp = $this->scale*$this->Yperiod*rand(1,2);
465        for ($i = 0; $i < ($this->height*$this->scale); $i++) {
466            imagecopy($this->im, $this->im,
467                sin($k+$i/$yp) * ($this->scale*$this->Yamplitude), $i-1,
468                0, $i, $this->width*$this->scale, 1);
469        }
470    }
471
472
473
474
475    /**
476     * Reduce the image to the final size
477     */
478    protected function ReduceImage() {
479        // Reduzco el tama´┐Żo de la imagen
480        $imResampled = imagecreatetruecolor($this->width, $this->height);
481        imagecopyresampled($imResampled, $this->im,
482            0, 0, 0, 0,
483            $this->width, $this->height,
484            $this->width*$this->scale, $this->height*$this->scale
485        );
486        imagedestroy($this->im);
487        $this->im = $imResampled;
488    }
489
490
491
492
493
494
495
496
497    /**
498     * File generation
499     */
500    protected function WriteImage() {
501        if ($this->imageFormat == 'png' && function_exists('imagepng')) {
502            header("Content-type: image/png");
503            imagepng($this->im);
504        } else {
505            header("Content-type: image/jpeg");
506            imagejpeg($this->im, null, 80);
507        }
508    }
509
510
511
512
513
514
515
516    /**
517     * Cleanup
518     */
519    protected function Cleanup() {
520        imagedestroy($this->im);
521    }
522}
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539?>