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

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

Minor bug fixes.

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