source: trunk/lib/ImageThumb.inc.php @ 224

Last change on this file since 224 was 224, checked in by quinn, 17 years ago

fixed bug: PHP Notice: Undefined index: authenticated.

File size: 34.5 KB
Line 
1<?php
2/**
3 * ImageThumb.inc.php
4 * code by strangecode :: www.strangecode.com :: this document contains copyrighted information
5 *
6 * @author   Quinn Comendant <quinn@strangecode.com>
7 * @requires Netpbm <http://sourceforge.net/projects/netpbm/> and libjpeg or GD.
8 * @version  2.0
9 */
10
11// Image proprtion options.
12define('IMAGETHUMB_FIT_WIDTH', 1);
13define('IMAGETHUMB_FIT_HEIGHT', 2);
14define('IMAGETHUMB_FIT_LARGER', 3);
15define('IMAGETHUMB_STRETCH', 4);
16define('IMAGETHUMB_NO_SCALE', 5);
17
18// Image resize options.
19define('IMAGETHUMB_METHOD_NETPBM', 6);
20define('IMAGETHUMB_METHOD_GD', 7);
21
22class ImageThumb {
23   
24    // General object parameters.
25    var $_params = array(
26        // The location for images to create thumbnails from.
27        'source_dir' => null,
28
29        // Existing files will be overwritten when there is a name conflict?
30        'allow_overwriting' => false,
31
32        // The file permissions of the uploaded files. Remember, files will be owned by the web server user.
33        'dest_file_perms' => 0600,
34
35        // Permissions of autocreated directories. Must be at least 0700 with owner=apache.
36        'dest_dir_perms' => 0700,
37
38        // Require file to have one of the following file name extensions.
39        'valid_file_extensions' => array('jpg', 'jpeg', 'gif', 'png'),
40       
41        // Method to use for resizing. (IMAGETHUMB_METHOD_NETPBM or IMAGETHUMB_METHOD_GD)
42        'resize_method' => IMAGETHUMB_METHOD_NETPBM,
43
44        // Netpbm and libjpeg binary locations.
45        'anytopnm_binary' => '/usr/bin/anytopnm',
46        'pnmscale_binary' => '/usr/bin/pnmscale',
47        'cjpeg_binary' => '/usr/bin/cjpeg',
48
49        // Which messages do we pass to raiseMsg? Use one of the MSG_* constants or false to disable.
50        'display_messages' => MSG_ALL,
51    );
52   
53    // Default image size specs.
54    var $_default_image_specs = array(
55        // The destination for an image thumbnail size.
56        // Use initial / to specify absolute paths, leave off to specify a path relative to source_dir (eg: ../thumbs).
57        'dest_dir' => null,
58       
59        // Destination file types. (IMG_JPG, IMG_PNG, IMG_GIF, IMG_WBMP)
60        'dest_file_type' => IMG_JPG,
61
62        // Destination file types. ('jpg', 'png', 'gif', 'wbmp')
63        'dest_file_extension' => 'jpg',
64       
65        // Type of scaling to perform, and sizes used to calculate max dimentions.
66        'scaling_type' => IMAGETHUMB_FIT_LARGER,
67        'width' => null,
68        'height' => null,
69
70        // Percentage quality of image compression output 0-100.
71        'quality' => 65,
72
73        // Create progressive jpegs?
74        'progressive' => false,
75
76        // If using GD method, apply sharpen filter. Requires PHP > 5.1.
77        'sharpen' => true,
78       
79        // Integers between 1-100, useful values are 65-85.
80        'sharpen_value' => 75,
81
82        // If source image is smaller than thumbnail, allow upscaling?
83        'allow_upscaling' => false,
84
85        // If thumb exists and filesize is smaller than this, do not overwrite the thumb.
86        'keep_filesize' => null,
87    );
88
89    // Final specifications for image sizes, set with setSpec().
90    var $_image_specs = array();
91
92    /**
93     * Set (or overwrite existing) parameters by passing an array of new parameters.
94     *
95     * @access public
96     * @param  array    $params     Array of parameters (key => val pairs).
97     */
98    function setParam($params)
99    {
100        $app =& App::getInstance();
101
102        if (isset($params) && is_array($params)) {
103
104            // Enforce valid source_dir parameter.
105            if (isset($params['source_dir'])) {
106                $params['source_dir'] = realpath($params['source_dir']);
107                // Source must be directory.
108                if (!is_dir($params['source_dir'])) {
109                    $app->logMsg(sprintf('Attempting to auto-create source directory: %s', $params['source_dir']), LOG_NOTICE, __FILE__, __LINE__);
110                    if (phpversion() > '5') {
111                        // Recursive.
112                        mkdir($params['source_dir'], isset($params['dest_dir_perms']) ? $params['dest_dir_perms'] : $this->getParam('dest_dir_perms'), true);
113                    } else {
114                        mkdir($params['source_dir'], isset($params['dest_dir_perms']) ? $params['dest_dir_perms'] : $this->getParam('dest_dir_perms'));
115                    }
116                    if (!is_dir($params['source_dir'])) {
117                        $app->logMsg(sprintf('Source directory invalid: %s', $params['source_dir']), LOG_ERR, __FILE__, __LINE__);
118                        trigger_error(sprintf('Source directory invalid: %s', $params['source_dir']), E_USER_ERROR);
119                    }
120                }
121                // Source must be readable.
122                if (!is_readable($params['source_dir'])) {
123                    $app->logMsg(sprintf('Source directory not readable: %s', $params['source_dir']), LOG_ERR, __FILE__, __LINE__);
124                    trigger_error(sprintf('Source directory not readable: %s', $params['source_dir']), E_USER_ERROR);
125                }
126            }
127
128            // Merge new parameters with old overriding only those passed.
129            $this->_params = array_merge($this->_params, $params);
130        } else {
131            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
132        }
133    }
134
135    /**
136     * Return the value of a parameter, if it exists.
137     *
138     * @access public
139     * @param string $param        Which parameter to return.
140     * @return mixed               Configured parameter value.
141     */
142    function getParam($param)
143    {
144        $app =& App::getInstance();
145   
146        if (isset($this->_params[$param])) {
147            return $this->_params[$param];
148        } else {
149            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
150            return null;
151        }
152    }
153
154    /**
155     * Set the specification of thumbnails.
156     *
157     * @access  public
158     * @param   array $spec The specifications for a size of output image.
159     */
160    function setSpec($spec, $index=null)
161    {
162        $app =& App::getInstance();
163
164        // A little sanity checking.
165        if (!isset($spec['dest_dir']) || '' == trim($spec['dest_dir'])) {
166            $app->logMsg('setSpec error: dest_dir not specified.', LOG_ERR, __FILE__, __LINE__);
167        } else {
168            $spec['dest_dir'] = trim($spec['dest_dir']);           
169        }
170        if (isset($spec['dest_file_type'])) {
171            switch ($spec['dest_file_type']) {
172            case IMG_JPG :
173                if (imagetypes() & IMG_JPG == 0) {
174                    $app->logMsg(sprintf('IMG_JPG is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
175                }
176                $spec['dest_file_extension'] = 'jpg';
177                break;
178            case IMG_PNG :
179                if (imagetypes() & IMG_PNG == 0) {
180                    $app->logMsg(sprintf('IMG_PNG is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
181                }
182                $spec['dest_file_extension'] = 'png';
183                break;
184            case IMG_GIF :
185                if (imagetypes() & IMG_GIF == 0) {
186                    $app->logMsg(sprintf('IMG_GIF is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
187                }
188                $spec['dest_file_extension'] = 'gif';
189                break;
190            case IMG_WBMP :
191                if (imagetypes() & IMG_WBMP == 0) {
192                    $app->logMsg(sprintf('IMG_WBMP is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
193                }
194                $spec['dest_file_extension'] = 'wbmp';
195                break;
196            default :
197                $app->logMsg(sprintf('Invalid dest_file_type: %s', $spec['dest_file_type']), LOG_ERR, __FILE__, __LINE__);
198                break;
199            }
200        }
201        if (!isset($spec['width']) || !is_int($spec['width'])) {
202            $app->logMsg('setSpec error: width not specified.', LOG_ERR, __FILE__, __LINE__);
203        }
204        if (!isset($spec['height']) || !is_int($spec['height'])) {
205            $app->logMsg('setSpec error: height not specified.', LOG_ERR, __FILE__, __LINE__);
206        }
207        if (isset($spec['quality']) && IMG_JPG != $spec['dest_file_type']) {
208            $app->logMsg('The "quality" specification is not used unless IMG_JPG is the dest_file_type.', LOG_INFO, __FILE__, __LINE__);
209        }
210        if (isset($spec['progressive']) && IMG_JPG != $spec['dest_file_type']) {
211            $app->logMsg('The "progressive" specification is not used unless IMG_JPG is the dest_file_type.', LOG_INFO, __FILE__, __LINE__);
212        }
213       
214        // Add to _image_specs array.
215        if (isset($index) && isset($this->_image_specs[$index])) {
216            // Merge with existing spec if index is provided.
217            $final_spec = array_merge($this->_image_specs[$index], $spec);
218            $this->_image_specs[$index] = $final_spec;
219        } else {
220            // Merge with spec defaults.
221            $final_spec = array_merge($this->_default_image_specs, $spec);           
222            $this->_image_specs[] = $final_spec;
223        }
224       
225        return $final_spec;
226    }
227
228    /**
229     * Process an entire directory of images.
230     *
231     * @access  public
232     * @return  bool true on success, false on failure.
233     */
234    function processAll($runtime_specs=null)
235    {
236        $app =& App::getInstance();
237
238        // Ensure we have a source.
239        if ('' == $this->getParam('source_dir')) {
240            $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
241            return false;
242        }
243
244        // Get all files in source directory.
245        $dir_handle = opendir($this->getParam('source_dir'));
246        while ($dir_handle && ($file = readdir($dir_handle)) !== false) {
247            // If the file name does not start with a dot (. or .. or .htaccess).
248            if (!preg_match('/^\./', $file) && in_array(strtolower(substr($file, strrpos($file, '.') + 1)), $this->getParam('valid_file_extensions'))) {
249                $files[] = $file;
250            }
251        }
252
253        // Process each found file.
254        if (is_array($files) && !empty($files)) {
255            $return_val = 0;
256            foreach ($files as $file_name) {
257                $return_val += $this->processFile($file_name, $runtime_specs);
258            }
259            $this->_raiseMsg(sprintf(_("Resized %s images."), sizeof($files)), MSG_SUCCESS, __FILE__, __LINE__);
260            return 0 === $return_val;
261        } else {
262            $app->logMsg(sprintf('No source images found in directory: %s', $this->getParam('source_dir')), LOG_NOTICE, __FILE__, __LINE__);
263            return false;
264        }
265    }
266
267    /**
268     * Generate thumbnails for the specified file.
269     *
270     * @access  public
271     * @param   string $file_name Name of file with extension.
272     * @param   array $runtime_specs Array of specifications that will override all configured specifications.
273     * @return  bool true on success, false on failure.
274     */
275    function processFile($file_name, $runtime_specs=null)
276    {
277        $app =& App::getInstance();
278
279        // Source file determinted by provided file_name.
280        $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name));
281       
282        // Ensure we have a source.
283        if (sizeof($this->_image_specs) < 1) {
284            if (is_array($runtime_specs)) {
285                $this->setSpec($runtime_specs, 0);
286            } else {
287                $app->logMsg(sprintf('Image specifications not set before processing.'), LOG_ERR, __FILE__, __LINE__);
288                return false;               
289            }
290        }
291
292        // Ensure we have a source.
293        if ('' == $this->getParam('source_dir')) {
294            $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
295            return false;
296        }
297
298        // Confirm source image exists.
299        if (!file_exists($source_file)) {
300            $this->_raiseMsg(sprintf(_("Image resizing failed: source image <em>%s</em> was not found."), $file_name), MSG_ERR, __FILE__, __LINE__);
301            $app->logMsg(sprintf('Source image not found: %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
302            return false;
303        }
304
305        // Confirm source image is readable.
306        if (!is_readable($source_file)) {
307            $this->_raiseMsg(sprintf(_("Image resizing failed: source image <em>%s</em> is not readable."), $file_name), MSG_ERR, __FILE__, __LINE__);
308            $app->logMsg(sprintf('Source image not readable: %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
309            return false;
310        }
311
312        // Confirm source image contains data.
313        if (filesize($source_file) <= 0) {
314            $this->_raiseMsg(sprintf(_("Image resizing failed: source image <em>%s</em> is zero bytes."), $file_name), MSG_ERR, __FILE__, __LINE__);
315            $app->logMsg(sprintf('Source image is zero bytes: %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
316            return false;
317        }
318
319        // Confirm source image has a valid file extension.
320        if (!$this->_validFileExtension($file_name)) {
321            $this->_raiseMsg(sprintf(_("Image resizing failed: source image <em>%s</em> not a valid type. It must have one of the following file name extensions: %s"), $file_name, join(', ', $this->getParam('valid_file_extensions'))), MSG_ERR, __FILE__, __LINE__);
322            $app->logMsg(sprintf('Image resizing failed: source image not of valid type: %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
323            return false;
324        }
325
326        // Ensure destination directories are created. This will only be called once per page load.
327        if (!$this->_createDestDirs()) {
328            return false;
329        }
330       
331        // To keep this script running even if user tries to stop browser.
332        ignore_user_abort(true);
333        ini_set('max_execution_time', 300);
334        ini_set('max_input_time', 300);
335
336        // This remains zero until something goes wrong.
337        $return_val = 0;
338
339        foreach ($this->_image_specs as $index => $spec) {
340           
341            if (is_array($runtime_specs)) {
342                // Override with runtime specs.
343                $spec = $this->setSpec($runtime_specs, $index);
344            }
345           
346            // Destination filename uses the extension defined by dest_file_extension.
347            if ('/' == $spec['dest_dir']{0}) {
348                // Absolute path.
349                $dest_file = sprintf('%s/%s.%s', $spec['dest_dir'], substr($file_name, 0, strrpos($file_name, '.')), $spec['dest_file_extension']);
350            } else {
351                // Relative path.
352                $dest_file = sprintf('%s/%s/%s.%s', $this->getParam('source_dir'), $spec['dest_dir'], substr($file_name, 0, strrpos($file_name, '.')), $spec['dest_file_extension']);
353            }
354                 
355            // Ensure destination directory exists and is writable.
356            if (!is_dir(dirname($dest_file)) || !is_writable(dirname($dest_file))) {
357                $this->_createDestDirs($dest_file);
358                if (!is_dir(dirname($dest_file)) || !is_writable(dirname($dest_file))) {
359                    $app->logMsg(sprintf('Image resizing failed, dest_dir invalid: %s', dirname($dest_file)), LOG_ERR, __FILE__, __LINE__);
360                    $return_val++;
361                    continue;
362                }
363            }
364
365            // Skip existing thumbnails with file size below $spec['keep_filesize'].
366            if (isset($spec['keep_filesize']) && file_exists($dest_file)) {
367                $file_size = filesize($dest_file);
368                if (false !== $file_size && $file_size < $spec['keep_filesize']) {
369                    $app->logMsg(sprintf('Skipping thumbnail %s. File already exists and file size is less than %s bytes.', $spec['dest_dir'] . '/' . $file_name, $spec['keep_filesize']), LOG_INFO, __FILE__, __LINE__);
370                    continue;
371                }
372            }
373
374            // Determine if original file size is smaller than specified thumbnail size. Do not scale-up if $spec['allow_upscaling'] config is set to false.
375            $image_size = getimagesize($source_file);
376            if ($image_size['0'] <= $spec['width'] && $image_size['1'] <= $spec['height'] && !$spec['allow_upscaling']) {
377                $spec['scaling_type'] = IMAGETHUMB_NO_SCALE;
378                $app->logMsg(sprintf('Image %s smaller than specified %s thumbnail size. Keeping original size.', $file_name, basename($spec['dest_dir'])), LOG_INFO, __FILE__, __LINE__);
379            }
380
381            // DO IT! Based on available method.
382            if (IMAGETHUMB_METHOD_NETPBM === $this->getParam('resize_method') && file_exists($this->getParam('anytopnm_binary')) && file_exists($this->getParam('pnmscale_binary')) && file_exists($this->getParam('cjpeg_binary'))) {
383                // Resize using Netpbm binaries.
384                $app->logMsg(sprintf('Resizing with Netpbm: %s', $source_file), LOG_DEBUG, __FILE__, __LINE__);
385                $return_val += $this->_resizeWithNetpbm($source_file, $dest_file, $spec);
386            } else if (IMAGETHUMB_METHOD_GD === $this->getParam('resize_method') && extension_loaded('gd')) {
387                // Resize with GD.
388                $app->logMsg(sprintf('Resizing with GD: %s', $source_file), LOG_DEBUG, __FILE__, __LINE__);
389                $return_val += $this->_resizeWithGD($source_file, $dest_file, $spec);
390            } else {
391                $app->logMsg(sprintf('Image thumbnailing canceled. Neither Netpbm or GD is available.', null), LOG_ERR, __FILE__, __LINE__);
392                return false;
393            }
394        }
395
396        // If > 0, there was a problem thumbnailing.
397        return 0 === $return_val;
398    }
399   
400    /*
401    * Use the Netpbm and libjpg cjpeg tools to generate a rescaled compressed image.
402    * This is the preferred method over GD which has (supposedly) less quality.
403    *
404    * @access   private
405    * @param    string  $source_file    Full path to source image file.
406    * @param    string  $dest_file      Full path to destination image file.
407    * @param    array   $spec           Array of image size specifications.
408    * @return   int                     0 if no error, n > 0 if errors.
409    * @author   Quinn Comendant <quinn@strangecode.com>
410    * @version  1.0
411    * @since    19 May 2006 13:55:46
412    */
413    function _resizeWithNetpbm($source_file, $dest_file, $spec)
414    {
415        $app =& App::getInstance();
416
417        // Define pnmscale arguments.
418        switch ($spec['scaling_type']) {
419        case IMAGETHUMB_FIT_WIDTH :
420            $pnmscale_args = sprintf(' -width %s ', escapeshellarg($spec['width']));
421            break;
422        case IMAGETHUMB_FIT_HEIGHT :
423            $pnmscale_args = sprintf(' -height %s ', escapeshellarg($spec['height']));
424            break;
425        case IMAGETHUMB_FIT_LARGER :
426            $pnmscale_args = sprintf(' -xysize %s %s ', escapeshellarg($spec['width']), escapeshellarg($spec['height']));
427            break;
428        case IMAGETHUMB_STRETCH :
429            $pnmscale_args = sprintf(' -width %s -height %s ', escapeshellarg($spec['width']), escapeshellarg($spec['height']));
430            break;
431        case IMAGETHUMB_NO_SCALE :
432        default :
433            $pnmscale_args = ' 1 ';
434            break;
435        }
436
437        // Define cjpeg arguments.
438        $cjpeg_args = sprintf(' -optimize -quality %s ', escapeshellarg($spec['quality']));
439        $cjpeg_args .= (true === $spec['progressive']) ? ' -progressive ' : '';
440
441        // Format the command that creates the thumbnail.
442        $command = sprintf('%s %s | %s %s | %s %s > %s',
443            escapeshellcmd($this->getParam('anytopnm_binary')),
444            escapeshellcmd($source_file),
445            escapeshellcmd($this->getParam('pnmscale_binary')),
446            escapeshellcmd($pnmscale_args),
447            escapeshellcmd($this->getParam('cjpeg_binary')),
448            escapeshellcmd($cjpeg_args),
449            escapeshellcmd($dest_file)
450        );
451        $app->logMsg(sprintf('ImageThumb Netpbm command: %s', $command), LOG_DEBUG, __FILE__, __LINE__);
452       
453        // Execute!
454        exec($command, $output, $return_val);
455
456        if (0 === $return_val) {
457            // Success!
458            // Make the thumbnail writable so the user can delete it over ftp without being 'apache'.
459            chmod($dest_file, $this->getParam('dest_file_perms'));
460            $app->logMsg(sprintf('Successfully resized image %s', $spec['dest_dir'] . '/' . basename($dest_file), $return_val), LOG_DEBUG, __FILE__, __LINE__);
461        } else {
462            // An error occurred.
463            $app->logMsg(sprintf('Image %s failed resizing with return value: %s%s', $spec['dest_dir'] . '/' . basename($dest_file), $return_val, empty($output) ? '' : ' (' . getDump($output) . ')'), LOG_ERR, __FILE__, __LINE__);
464        }
465
466        // Return from the command will be > 0 if there was an error.
467        return $return_val;
468    }
469
470    /*
471    * Use PHP's built-in GD tools to generate a rescaled compressed image.
472    *
473    * @access   private
474    * @param    string  $source_file    Full path to source image file.
475    * @param    string  $dest_file      Full path to destination image file.
476    * @param    array   $spec           Array of image size specifications.
477    * @return   int                     0 if no error, n > 0 if errors.
478    * @author   Quinn Comendant <quinn@strangecode.com>
479    * @version  1.0
480    * @since    19 May 2006 15:46:02
481    */
482    function _resizeWithGD($source_file, $dest_file, $spec)
483    {
484        $app =& App::getInstance();
485
486        // Get original file dimensions and type.
487        list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_file);
488
489        // Define destination image dimentions.
490        switch ($spec['scaling_type']) {
491        case IMAGETHUMB_FIT_WIDTH :
492            $dest_image_width = $spec['width'];
493            $dest_image_height = $source_image_height * ($spec['width'] / $source_image_width);
494            break;
495        case IMAGETHUMB_FIT_HEIGHT :
496            $dest_image_height = $spec['height'];
497            $dest_image_width = $source_image_width * ($spec['height'] / $source_image_height);
498            break;
499        case IMAGETHUMB_FIT_LARGER :
500            if (($source_image_width * ($spec['height'] / $source_image_height)) <= $spec['width']) {
501                // Height is larger.
502                $dest_image_height = $spec['height'];
503                $dest_image_width = $source_image_width * ($spec['height'] / $source_image_height);
504            } else {
505                // Width is larger.
506                $dest_image_width = $spec['width'];
507                $dest_image_height = $source_image_height * ($spec['width'] / $source_image_width);
508            }
509            break;
510        case IMAGETHUMB_STRETCH :
511            $dest_image_width = $spec['width'];
512            $dest_image_height = $spec['height'];
513            break;
514        case IMAGETHUMB_NO_SCALE :
515        default :
516            $dest_image_width = $source_image_width;
517            $dest_image_height = $source_image_height;
518            break;
519        }
520
521        // Create source image data in memory.
522        switch ($source_image_type) {
523        case IMAGETYPE_JPEG : // = 2
524            $source_image_resource = imagecreatefromjpeg($source_file);
525            break;
526        case IMAGETYPE_PNG : // = 3
527            $source_image_resource = imagecreatefrompng($source_file);
528            break;
529        case IMAGETYPE_GIF : // = 1
530            $source_image_resource = imagecreatefromgif($source_file);
531            break;
532        case IMAGETYPE_WBMP : // = 15
533            $source_image_resource = imagecreatefromwbmp($source_file);
534        case IMAGETYPE_SWF : // = 4
535        case IMAGETYPE_PSD : // = 5
536        case IMAGETYPE_BMP : // = 6
537        case IMAGETYPE_TIFF_II : // = 7
538        case IMAGETYPE_TIFF_MM : // = 8
539        case IMAGETYPE_JPC : // = 9
540        case IMAGETYPE_JP2 : // = 10
541        case IMAGETYPE_JPX : // = 11
542        case IMAGETYPE_JB2 : // = 12
543        case IMAGETYPE_SWC : // = 13
544        case IMAGETYPE_IFF : // = 14
545        case IMAGETYPE_XBM : // = 16
546        default :
547            $app->logMsg(sprintf('Source image type %s not supported.', $source_image_type), LOG_WARNING, __FILE__, __LINE__);
548            return 1;
549            break;
550        }
551        if (!$source_image_resource) {
552            $app->logMsg(sprintf('Error creating %s image in memory from %s', $source_image_type, $source_file), LOG_WARNING, __FILE__, __LINE__);
553            return 1;
554        }
555       
556        // Create destination image data in memory.
557        $dest_image_resource = imagecreatetruecolor($dest_image_width, $dest_image_height);
558
559        // Resample!
560        if (!imagecopyresampled($dest_image_resource, $source_image_resource, 0, 0, 0, 0, $dest_image_width, $dest_image_height, $source_image_width, $source_image_height)) {
561            $app->logMsg(sprintf('Error resampling image %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
562            // Always cleanup images from memory.
563            imagedestroy($source_image_resource);
564            imagedestroy($dest_image_resource);
565            return 1;
566        }
567       
568        // Sharpen image using a custom filter matrix.
569        if (phpversion() > '5.1' && true === $spec['sharpen'] && $spec['sharpen_value'] > 0) {
570            $sharpen_value = round((((48 - 10) / (100 - 1)) * (100 - $spec['sharpen_value'])) + 10);
571            imageconvolution($dest_image_resource, array(array(-1,-1,-1),array(-1,$sharpen_value,-1),array(-1,-1,-1)), ($sharpen_value - 8), 0);
572        }
573
574        // Save image.
575        $return_val = true;
576        switch ($spec['dest_file_type']) {
577        case IMG_JPG :
578            imageinterlace($dest_image_resource, (true == $spec['progressive'] ? 1 : 0));
579            $return_val = imagejpeg($dest_image_resource, $dest_file, $spec['quality']);
580            break;
581        case IMG_PNG :
582            $return_val = imagepng($dest_image_resource, $dest_file);
583            break;
584        case IMG_GIF :
585            $return_val = imagegif($dest_image_resource, $dest_file);
586            break;
587        case IMG_WBMP :
588            $return_val = imagewbmp($dest_image_resource, $dest_file);
589            break;
590        default :
591            $app->logMsg(sprintf('Destination image type %s not supported for image %s.', $spec['dest_file_type'], $dest_file), LOG_WARNING, __FILE__, __LINE__);
592            // Always cleanup images from memory.
593            imagedestroy($source_image_resource);
594            imagedestroy($dest_image_resource);
595            return 1;
596            break;
597        }
598
599        // Always cleanup images from memory.
600        imagedestroy($source_image_resource);
601        imagedestroy($dest_image_resource);
602
603        if ($return_val) {
604            // Success!
605            // Make the thumbnail writable so the user can delete it over ftp without being 'apache'.
606            if (!chmod($dest_file, $this->getParam('dest_file_perms'))) {
607                $app->logMsg(sprintf('chmod failed on file: %s', $dest_file), LOG_ERR, __FILE__, __LINE__);
608            }
609            $app->logMsg(sprintf('Successfully resized image: %s', $dest_file), LOG_DEBUG, __FILE__, __LINE__);
610            return 0;
611        } else {
612            // An error occurred.
613            $app->logMsg(sprintf('Failed resizing image: %s', $dest_file), LOG_ERR, __FILE__, __LINE__);
614            return 1;
615        }
616    }
617
618    /**
619     * Delete the thumbnails for the specified file name.
620     *
621     * @access  public
622     * @param   string $file_name The file name to delete, with extension.
623     * @return  bool true on success, false on failure.
624     */
625    function deleteThumbs($file_name)
626    {
627        $app =& App::getInstance();
628
629        // Ensure we have a source.
630        if ('' == $this->getParam('source_dir')) {
631            $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
632            return false;
633        }
634
635        $return_val = 0;
636        foreach ($this->_image_specs as $spec) {
637            if ('/' == $spec['dest_dir']{0}) {
638                // Absolute path.
639                $dest_file = realpath(sprintf('%s/%s.%s', $spec['dest_dir'], substr($file_name, 0, strrpos($file_name, '.')), $spec['dest_file_extension']));               
640            } else {
641                // Relative path.
642                $dest_file = realpath(sprintf('%s/%s/%s.%s', $this->getParam('source_dir'), $spec['dest_dir'], substr($file_name, 0, strrpos($file_name, '.')), $spec['dest_file_extension']));
643            }
644            if (file_exists($dest_file)) {
645                if (!unlink($dest_file)) {
646                    $return_val++;
647                    $app->logMsg(sprintf('Delete thumbs failed: %s', $dest_file), LOG_WARNING, __FILE__, __LINE__);
648                }
649            }
650        }
651        $app->logMsg(sprintf('Thumbnails deleted for file: %s', $file_name), LOG_INFO, __FILE__, __LINE__);
652        return 0 === $return_val;
653    }
654
655    /**
656     * Delete the source image with the specified file name.
657     *
658     * @access  public
659     * @param   string $file_name The file name to delete, with extension.
660     * @return  bool true on success, false on failure.
661     */
662    function deleteOriginal($file_name)
663    {
664        $app =& App::getInstance();
665
666        // Ensure we have a source.
667        if ('' == $this->getParam('source_dir')) {
668            $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
669            return false;
670        }
671
672        // Ensure we have a source.
673        if ('' == $file_name) {
674            $app->logMsg(sprintf('Cannot delete, filename empty.'), LOG_WARNING, __FILE__, __LINE__);
675            return false;
676        }
677
678        $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name));
679        if (is_file($source_file) && unlink($source_file)) {
680            $app->logMsg(sprintf('Original file successfully deleted: %s', $file_name), LOG_INFO, __FILE__, __LINE__);
681            return true;
682        } else {
683            $app->logMsg(sprintf('Delete original failed: %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
684            return false;
685        }
686    }
687
688    /**
689     * Returns true if file exists.
690     *
691     * @access  public
692     * @param   string $file_name The file name to test, with extension.
693     * @return  bool true on success, false on failure.
694     */
695    function exists($file_name)
696    {
697        $app =& App::getInstance();
698
699        // Ensure we have a source.
700        if ('' == $this->getParam('source_dir')) {
701            $app->logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
702            return false;
703        }
704
705        $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name));
706        return file_exists($source_file);
707    }
708
709    /**
710     * Tests if extension of $file_name is in the array valid_file_extensions.
711     *
712     * @access  public
713     * @param   string  $file_name  A file name.
714     * @return  bool    True on success, false on failure.
715     */
716    function _validFileExtension($file_name)
717    {
718        preg_match('/.*?\.(\w+)$/i', $file_name, $ext);
719        return !empty($ext) && in_array(strtolower($ext[1]), $this->getParam('valid_file_extensions'));       
720    }
721
722    /**
723     * Make directory for each specified thumbnail size, if it doesn't exist.
724     *
725     * @access  public
726     * @return  bool true on success, false on failure.
727     */
728    function _createDestDirs($filename=null)
729    {
730        $app =& App::getInstance();
731
732        // Keep track of directories we've already created.
733        $dd_hash = md5(isset($filename) ? dirname($filename) : 'none');
734        static $already_checked = array();
735
736        $return_val = 0;
737
738        if (!isset($already_checked[$dd_hash])) {
739            // Ensure we have a source.
740            if ('' == $this->getParam('source_dir')) {
741                $app->logMsg(sprintf('Source directory not set before creating destination directories.'), LOG_ERR, __FILE__, __LINE__);
742                return false;
743            }
744       
745            // Loop through specs and ensure all dirs are created.
746            foreach ($this->_image_specs as $spec) {
747                if (isset($filename)) {
748                    $dest_dir = dirname($filename);
749                } else {
750                    if ('/' == $spec['dest_dir']{0}) {
751                        // Absolute path.
752                        $dest_dir = $spec['dest_dir'];
753                    } else {
754                        // Relative path.
755                        $dest_dir = sprintf('%s/%s', $this->getParam('source_dir'), $spec['dest_dir']);
756                    }
757                }
758                if (!file_exists($dest_dir)) {
759                    if (phpversion() > '5') {
760                        // Recursive.
761                        if (!file_exists($dest_dir) && !($ret = mkdir($dest_dir, $this->getParam('dest_dir_perms'), true))) {
762                            $return_val++;
763                            $app->logMsg(sprintf('mkdir failure: %s', $dest_dir), LOG_ERR, __FILE__, __LINE__);
764                        }
765                    } else {
766                        // Recursive mkdir for php 4.
767                        $path = '';
768                        foreach (array_diff(explode('/', $dest_dir), array('')) as $dir) {
769                            $path .= '/' . $dir;
770                            if (! ($ret = file_exists($path) ? true : mkdir($path, $this->getParam('dest_dir_perms')))) {
771                                $return_val++;
772                                $app->logMsg(sprintf('mkdir failure: %s', $path), LOG_ERR, __FILE__, __LINE__);
773                                break;
774                            }
775                        }
776                    }
777
778                    if ($ret) {
779                        $app->logMsg(sprintf('mkdir success: %s', $dest_dir), LOG_DEBUG, __FILE__, __LINE__);                       
780                    }
781                }
782            }
783        }
784
785        $already_checked[$dd_hash] = true;
786
787        // If > 0, there was a problem creating dest dirs.
788        return 0 === $return_val;
789    }
790
791    /**
792     * An alias for $app->raiseMsg that only sends messages configured by display_messages.
793     *
794     * @access public
795     *
796     * @param string $message The text description of the message.
797     * @param int    $type    The type of message: MSG_NOTICE,
798     *                        MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
799     * @param string $file    __FILE__.
800     * @param string $line    __LINE__.
801     */
802    function _raiseMsg($message, $type, $file, $line)
803    {
804        $app =& App::getInstance();
805
806        if ($this->getParam('display_messages') === true || (is_int($this->getParam('display_messages')) && $this->getParam('display_messages') & $type > 0)) {
807            $app->raiseMsg($message, $type, $file, $line);
808        }
809    }
810
811} // End of class.
812?>
Note: See TracBrowser for help on using the repository browser.