Ignore:
Timestamp:
May 20, 2006 2:12:12 AM (18 years ago)
Author:
scdev
Message:

Q - making codebase 2 work with php5. Rewrote ImageThumb? class to work with GD

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/ImageThumb.inc.php

    r42 r119  
    33 * ImageThumb.inc.php
    44 * Code by Strangecode :: www.strangecode.com :: This document contains copyrighted information
    5  */
    6 
    7 /**
    8  * The ImageThumb class resizes images.
    95 *
    106 * @author   Quinn Comendant <quinn@strangecode.com>
    11  * @requires Netpbm binaries from http://sourceforge.net/projects/netpbm/
    12  * @version  1.1
     7 * @requires Netpbm <http://sourceforge.net/projects/netpbm/> and libjpeg or GD.
     8 * @version  2.0
    139 */
    1410
     11// Image resize options.
    1512define('IMAGETHUMB_FIT_WIDTH', 1);
    1613define('IMAGETHUMB_FIT_HEIGHT', 2);
     
    1815define('IMAGETHUMB_STRETCH', 4);
    1916define('IMAGETHUMB_NO_SCALE', 5);
     17define('IMAGETHUMB_METHOD_NETPBM', 6);
     18define('IMAGETHUMB_METHOD_GD', 7);
    2019
    2120class ImageThumb {
    22 
    23     // The location for images to create thumbnails from.
    24     var $source_dir = null;
    25 
    26     // Specifications for thumbnail images.
    27     var $spec;
    28 
    29     // Array of acceptable file extensions (lowercase).
    30     var $valid_file_extensions = array('jpg', 'jpeg', 'gif', 'png');
    31 
    32     // The uploaded files will be owned by user 'apache'. Set world-read/write
    33     // if the website admin needs to read/delete these files. Must be at least 0400 with owner=apache.
    34     var $dest_file_perms = 0644;
    35 
    36     // Must be at least 0700 with owner=apache.
    37     var $dest_dir_perms = 0777;
    38 
    39     // Executable binary locations.
    40     var $anytopnm_binary = '/usr/bin/anytopnm';
    41     var $pnmscale_binary = '/usr/bin/pnmscale';
    42     var $cjpeg_binary = '/usr/bin/cjpeg';
    43     var $_valid_binaries = true;
    44 
    45     // Display messages raised in this object?
    46     var $display_messages = true;
    47 
    48     /**
    49      * Constructor.
    50      *
    51      * @access  public
    52      */
    53     function ImageThumb()
    54     {
    55         if (!file_exists($this->anytopnm_binary)) {
    56             App::logMsg(sprintf('ImageThumb error: anytopnm binary %s not found.', $this->anytopnm_binary), LOG_ERR, __FILE__, __LINE__);
    57             $this->_valid_binaries = false;
    58         }
    59         if (!file_exists($this->pnmscale_binary)) {
    60             App::logMsg(sprintf('ImageThumb error: pnmscale binary %s not found.', $this->pnmscale_binary), LOG_ERR, __FILE__, __LINE__);
    61             $this->_valid_binaries = false;
    62         }
    63         if (!file_exists($this->cjpeg_binary)) {
    64             App::logMsg(sprintf('ImageThumb error: cjpeg binary %s not found.', $this->cjpeg_binary), LOG_ERR, __FILE__, __LINE__);
    65             $this->_valid_binaries = false;
    66         }
    67     }
    68 
    69     /**
    70      * Set the directory of the source images.
    71      *
    72      * @access  public
    73      * @param   string $source_dir The full directory path of the source images.
     21   
     22    // General object parameters.
     23    var $_params = array(
     24        // The location for images to create thumbnails from.
     25        'source_dir' => null,
     26
     27        // Existing files will be overwritten when there is a name conflict?
     28        'allow_overwriting' => false,
     29
     30        // The file permissions of the uploaded files. Remember, files will be owned by the web server user.
     31        'dest_file_perms' => 0600,
     32
     33        // Permissions of autocreated directories. Must be at least 0700 with owner=apache.
     34        'dest_dir_perms' => 0777,
     35       
     36        // Destination file types. (IMG_JPG, IMG_PNG, IMG_GIF, IMG_WBMP)
     37        'dest_file_type' => IMG_JPG,
     38
     39        // Destination file types. ('jpg', 'png', 'gif', 'wbmp')
     40        'dest_file_extention' => 'jpg',
     41
     42        // Require file to have one of the following file name extentions.
     43        'valid_file_extensions' => array('jpg', 'jpeg', 'gif', 'png'),
     44       
     45        // Method to use for resizing. (IMAGETHUMB_METHOD_NETPBM or IMAGETHUMB_METHOD_GD)
     46        'resize_method' => IMAGETHUMB_METHOD_NETPBM,
     47
     48        // Netpbm and libjpeg binary locations.
     49        'anytopnm_binary' => '/usr/bin/anytopnm',
     50        'pnmscale_binary' => '/usr/bin/pnmscale',
     51        'cjpeg_binary' => '/usr/bin/cjpeg',
     52
     53        // Which messages do we pass to raiseMsg? Use one of the MSG_* constants or false to disable.
     54        'display_messages' => MSG_ALL,
     55    );
     56   
     57    // Default image size specs.
     58    var $default_image_specs = array(
     59        // The destination for an image thumbnail size. Path relative to source_dir (eg: ../thumbs).
     60        'dest_dir' => null,
     61        // Type of scaling to perform, and sizes used to calculate max dimentions.
     62        'scaling_type' => IMAGETHUMB_FIT_LARGER,
     63        'width' => null,
     64        'height' => null,
     65        // Percentage quality of image compression output 0-100.
     66        'quality' => 65,
     67        // Create progressive jpegs?
     68        'progressive' => false,
     69        // If source image is smaller than thumbnail, allow upscaling?
     70        'allow_upscaling' => false,
     71        // If thumb exists and filesize is smaller than this, do not overwrite the thumb.
     72        'keep_filesize' => null,
     73    );
     74
     75    // Final specifications for image sizes, set with setSpec().
     76    var $image_specs = array();
     77
     78    /**
     79     * Set (or overwrite existing) parameters by passing an array of new parameters.
     80     *
     81     * @access public
     82     * @param  array    $params     Array of parameters (key => val pairs).
     83     */
     84    function setParam($params)
     85    {
     86        if (isset($params) && is_array($params)) {
     87
     88            // Enforce valid upload_path parameter.
     89            if (isset($params['source_dir'])) {
     90                $params['source_dir'] = realpath($params['source_dir']);
     91                // Must be directory.
     92                if (!is_dir($params['source_dir'])) {
     93                    App::logMsg(sprintf('Source directory invalid: %s', $params['source_dir']), LOG_ERR, __FILE__, __LINE__);
     94                    trigger_error(sprintf('Source directory invalid: %s', $params['source_dir']), E_USER_ERROR);
     95                }
     96                // Must be readable.
     97                if (!is_readable($params['source_dir'])) {
     98                    App::logMsg(sprintf('Source directory not readable: %s', $params['source_dir']), LOG_ERR, __FILE__, __LINE__);
     99                    trigger_error(sprintf('Source directory not readable: %s', $params['source_dir']), E_USER_ERROR);
     100                }
     101            }
     102           
     103            if (isset($params['dest_file_type'])) {
     104                switch ($params['dest_file_type']) {
     105                case IMG_JPG :
     106                    if (imagetypes() & IMG_JPG == 0) {
     107                        App::logMsg(sprintf('IMG_JPG is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
     108                    }
     109                    $params['dest_file_extention'] = 'jpg';
     110                    break;
     111                case IMG_PNG :
     112                    if (imagetypes() & IMG_PNG == 0) {
     113                        App::logMsg(sprintf('IMG_PNG is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
     114                    }
     115                    $params['dest_file_extention'] = 'png';
     116                    break;
     117                case IMG_GIF :
     118                    if (imagetypes() & IMG_GIF == 0) {
     119                        App::logMsg(sprintf('IMG_GIF is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
     120                    }
     121                    $params['dest_file_extention'] = 'gif';
     122                    break;
     123                case IMG_WBMP :
     124                    if (imagetypes() & IMG_WBMP == 0) {
     125                        App::logMsg(sprintf('IMG_WBMP is not supported by this version of PHP GD.', null), LOG_ERR, __FILE__, __LINE__);
     126                    }
     127                    $params['dest_file_extention'] = 'wbmp';
     128                    break;
     129                default :
     130                    App::logMsg(sprintf('Invalid dest_file_type: %s', $params['dest_file_type']), LOG_ERR, __FILE__, __LINE__);
     131                    break;
     132                }
     133            }
     134
     135            // Merge new parameters with old overriding only those passed.
     136            $this->_params = array_merge($this->_params, $params);
     137        } else {
     138            App::logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
     139        }
     140    }
     141
     142    /**
     143     * Return the value of a parameter, if it exists.
     144     *
     145     * @access public
     146     * @param string $param        Which parameter to return.
     147     * @return mixed               Configured parameter value.
     148     */
     149    function getParam($param)
     150    {
     151        if (isset($this->_params[$param])) {
     152            return $this->_params[$param];
     153        } else {
     154            App::logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
     155            return null;
     156        }
     157    }
     158
     159    /**
     160     * Set the specification of thumbnails.
     161     *
     162     * @access  public
     163     * @param   array $spec The specifications for a size of output image.
     164     */
     165    function setSpec($spec)
     166    {
     167        // A little sanity checking.
     168        if (!isset($spec['dest_dir']) || '' == $spec['dest_dir']) {
     169            App::logMsg('setSpec error: dest_dir not specified.', LOG_ERR, __FILE__, __LINE__);
     170            return false;
     171        }
     172        if (!isset($spec['width']) || !is_int($spec['width'])) {
     173            App::logMsg('setSpec error: width not specified.', LOG_ERR, __FILE__, __LINE__);
     174            return false;
     175        }
     176        if (!isset($spec['height']) || !is_int($spec['height'])) {
     177            App::logMsg('setSpec error: height not specified.', LOG_ERR, __FILE__, __LINE__);
     178            return false;
     179        }
     180        if (isset($spec['quality']) && IMG_JPG != $this->getParam('dest_file_type')) {
     181            App::logMsg('The "quality" specification is not used unless IMG_JPG is the dest_file_type.', LOG_INFO, __FILE__, __LINE__);
     182        }
     183        if (isset($spec['progressive']) && IMG_JPG != $this->getParam('dest_file_type')) {
     184            App::logMsg('The "progressive" specification is not used unless IMG_JPG is the dest_file_type.', LOG_INFO, __FILE__, __LINE__);
     185        }
     186        // Merge defaults with provided.
     187        $this->image_specs[] = array_merge($this->default_image_specs, $spec);
     188    }
     189
     190    /**
     191     * Process an entire directory of images.
     192     *
     193     * @access  public
    74194     * @return  bool true on success, false on failure.
    75195     */
    76     function setSourceDirectory($source_dir)
    77     {
    78 
    79         // Set the source directory path, stripping any extra slashes if needed.
    80         $this->source_dir = preg_replace('!/+$!', '', $source_dir);
    81 
    82         if (!is_dir($this->source_dir)) {
    83             App::logMsg(sprintf('ImageThumb error: source directory not found: %s', $this->source_dir), LOG_ERR, __FILE__, __LINE__);
    84             return false;
    85         }
    86         if (!is_readable($this->source_dir)) {
    87             App::logMsg(sprintf('ImageThumb error: source directory not readable: %s', $this->source_dir), LOG_ERR, __FILE__, __LINE__);
    88             return false;
    89         }
    90         return true;
    91     }
    92 
    93     /**
    94      * Set the specification of thumbnails.
    95      *
    96      * @access  public
    97      * @param   array $spec The specifications for each size of output image.
    98      * @return  bool true on success, false on failure.
    99      */
    100     function setSpec($spec = array())
    101     {
    102         $dest_dir        = preg_replace('!/+$!', '', $spec['dest_dir']);
    103         $width           = $spec['width'];
    104         $height          = $spec['height'];
    105         $scaling_type    = $spec['scaling_type'];
    106         $quality         = isset($spec['quality']) ? $spec['quality'] : 75;
    107         $progressive     = isset($spec['progressive']) ? $spec['progressive'] : false;
    108         $allow_upscaling = isset($spec['allow_upscaling']) ? $spec['allow_upscaling'] : false;
    109         $keep_filesize   = isset($spec['keep_filesize']) ? $spec['keep_filesize'] : null;
    110 
    111         // Define pnmscale arguments.
    112         switch ($scaling_type) {
    113         case IMAGETHUMB_FIT_WIDTH :
    114             if (empty($width)) {
    115                 App::logMsg('ImageThumb error: width not specified for IMAGETHUMB_FIT_WIDTH.', LOG_ERR, __FILE__, __LINE__);
    116                 return false;
    117             }
    118             $pnmscale_args = sprintf(' -width %s ', escapeshellcmd($width));
    119             break;
    120         case IMAGETHUMB_FIT_HEIGHT :
    121             if (empty($height)) {
    122                 App::logMsg('ImageThumb error: height not specified for IMAGETHUMB_FIT_HEIGHT.', LOG_ERR, __FILE__, __LINE__);
    123                 return false;
    124             }
    125             $pnmscale_args = sprintf(' -height %s ', escapeshellcmd($height));
    126             break;
    127         case IMAGETHUMB_FIT_LARGER :
    128             if (empty($width) || empty($height)) {
    129                 App::logMsg('ImageThumb error: width or height not specified for IMAGETHUMB_FIT_LARGER.', LOG_ERR, __FILE__, __LINE__);
    130                 return false;
    131             }
    132             $pnmscale_args = sprintf(' -xysize %s %s ', escapeshellcmd($width), escapeshellcmd($height));
    133             break;
    134         case IMAGETHUMB_STRETCH :
    135             if (empty($width) || empty($height)) {
    136                 App::logMsg('ImageThumb error: width or height not specified for IMAGETHUMB_STRETCH.', LOG_ERR, __FILE__, __LINE__);
    137                 return false;
    138             }
    139             $pnmscale_args = sprintf(' -width %s -height %s ', escapeshellcmd($width), escapeshellcmd($height));
    140             break;
    141         case IMAGETHUMB_NO_SCALE :
    142         default :
    143             $pnmscale_args = ' 1 ';
    144             break;
    145         }
    146 
    147         // Define cjpeg arguments.
    148         $cjpeg_args = sprintf(' -optimize -quality %s ', escapeshellcmd($quality));
    149         $cjpeg_args .= (true === $progressive) ? ' -progressive ' : '';
    150 
    151         $this->spec[] = array(
    152             'dest_dir' => $dest_dir,
    153             'width' => $width,
    154             'height' => $height,
    155             'scaling_type' => $scaling_type,
    156             'quality' => $quality,
    157             'progressive' => $progressive,
    158             'pnmscale_args' => $pnmscale_args,
    159             'cjpeg_args' => $cjpeg_args,
    160             'allow_upscaling' => $allow_upscaling,
    161             'keep_filesize' => $keep_filesize,
    162         );
    163     }
    164 
    165     /**
    166      * Make directory for each specified thumbnail size, if it doesn't exist.
    167      *
    168      * @access  public
    169      * @return  bool true on success, false on failure.
    170      */
    171     function createDestDirs()
     196    function processAll()
    172197    {
    173198        // Ensure we have a source.
    174         if (!isset($this->source_dir)) {
    175             App::logMsg(sprintf('Source directory not set before creating destination directories.'), LOG_ERR, __FILE__, __LINE__);
    176             return false;
    177         }
    178         $return_val = 0;
    179         foreach ($this->spec as $s) {
    180             if (!is_dir($this->source_dir . '/' . $s['dest_dir'])) {
    181                 if (!mkdir($this->source_dir . '/' . $s['dest_dir'], $this->dest_dir_perms)) {
    182                     $return_val += 1;
    183                     App::logMsg(sprintf('mkdir failure: %s', $this->source_dir . '/' . $s['dest_dir']), LOG_ERR, __FILE__, __LINE__);
    184                 }
    185             }
    186         }
    187 
    188         // If > 0, there was a problem creating dest dirs.
    189         return (0 == $return_val);
     199        if ('' == $this->getParam('source_dir')) {
     200            App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
     201            return false;
     202        }
     203
     204        // Get all files in source directory.
     205        $dir_handle = opendir($this->getParam('source_dir'));
     206        while ($dir_handle && ($file = readdir($dir_handle)) !== false) {
     207            // If the file name does not start with a dot (. or .. or .htaccess).
     208            if (!preg_match('/^\./', $file) && in_array(strtolower(substr($file, strrpos($file, '.') + 1)), $this->getParam('valid_file_extensions'))) {
     209                $files[] = $file;
     210            }
     211        }
     212
     213        // Process each found file.
     214        if (is_array($files) && !empty($files)) {
     215            $return_val = 0;
     216            foreach ($files as $file_name) {
     217                $return_val += $this->processFile($file_name);
     218            }
     219            return 0 === $return_val;
     220        } else {
     221            App::logMsg(sprintf('No images found to thumbnail in directory %s.', $this->getParam('source_dir')), LOG_NOTICE, __FILE__, __LINE__);
     222            return false;
     223        }
    190224    }
    191225
     
    199233    function processFile($file_name)
    200234    {
    201         // Ensure we have valid binaries.
    202         if (!$this->_valid_binaries) {
    203             return false;
    204         }
    205 
     235        // Source file determinted by provided file_name.
     236        $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name));
     237       
    206238        // Ensure we have a source.
    207         if (!isset($this->source_dir)) {
     239        if (sizeof($this->image_specs) < 1) {
     240            App::logMsg(sprintf('Image specifications not set before processing.'), LOG_ERR, __FILE__, __LINE__);
     241            return false;
     242        }
     243
     244        // Ensure we have a source.
     245        if ('' == $this->getParam('source_dir')) {
    208246            App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
    209247            return false;
    210248        }
    211249
     250        // Confirm source image exists.
     251        if (!file_exists($source_file)) {
     252            $this->_raiseMsg(sprintf(_("Image resizing failed: source image not found: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
     253            App::logMsg(sprintf('Source image not found: %s', $file_name), LOG_ALERT, __FILE__, __LINE__);
     254            return false;
     255        }
     256
     257        // Confirm source image is readable.
     258        if (!is_readable($source_file)) {
     259            $this->_raiseMsg(sprintf(_("Image resizing failed: source image not readable: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
     260            App::logMsg(sprintf('Source image not readable: %s', $file_name), LOG_ALERT, __FILE__, __LINE__);
     261            return false;
     262        }
     263
     264        // Confirm source image contains data.
     265        if (filesize($source_file) <= 0) {
     266            $this->_raiseMsg(sprintf(_("Image resizing failed: source image corrupt: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
     267            App::logMsg(sprintf('Source image is zero bytes: %s', $file_name), LOG_ALERT, __FILE__, __LINE__);
     268            return false;
     269        }
     270
     271        // Confirm source image has a valid file extension.
     272        if (!$this->_validFileExtension($file_name)) {
     273            $this->_raiseMsg(sprintf(_("Image resizing failed: source image not of valid type: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
     274            App::logMsg(sprintf('Image resizing failed: source image not of valid type: %s', $file_name), LOG_ERR, __FILE__, __LINE__);
     275            return false;
     276        }
     277
     278        // Ensure destination directories are created. This will only be called once per page load.
     279        $this->_createDestDirs();
     280       
    212281        // To keep this script running even if user tries to stop browser.
    213282        ignore_user_abort(true);
     
    216285        }
    217286
    218         // Confirm source image exists.
    219         if (!file_exists($this->source_dir . '/' . $file_name)) {
    220             $this->raiseMsg(sprintf(_("Image resizing failed: source image not found: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
    221             App::logMsg(sprintf('Source image not found: %s', $file_name), LOG_ALERT, __FILE__, __LINE__);
    222             return false;
    223         }
    224 
    225         // Confirm source image is readable.
    226         if (!is_readable($this->source_dir . '/' . $file_name)) {
    227             $this->raiseMsg(sprintf(_("Image resizing failed: source image not readable: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
    228             App::logMsg(sprintf('Source image not readable: %s', $file_name), LOG_ALERT, __FILE__, __LINE__);
    229             return false;
    230         }
    231 
    232         // Confirm source image contains data.
    233         if (filesize($this->source_dir . '/' . $file_name) < 1) {
    234             $this->raiseMsg(sprintf(_("Image resizing failed: source image corrupt: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
    235             App::logMsg(sprintf('Source image is zero bytes: %s', $file_name), LOG_ALERT, __FILE__, __LINE__);
    236             return false;
    237         }
    238 
    239         // Confirm source image has a valid file extension.
    240         if (!$this->validFileExtension($file_name)) {
    241             $this->raiseMsg(sprintf(_("Image resizing failed: source image not of valid type: <strong>%s</strong>"), $file_name), MSG_ERR, __FILE__, __LINE__);
    242             App::logMsg(sprintf('Image resizing failed: source image not of valid type: %s', $file_name), LOG_ERR, __FILE__, __LINE__);
    243             return false;
    244         }
    245 
    246         // Output file will be a jpg. Set file extension.
    247         $file_name = substr($file_name, 0, strrpos($file_name, '.')) . '.jpg';
    248 
    249287        // This remains zero until something goes wrong.
    250         $final_return_val = 0;
    251 
    252         foreach ($this->spec as $s) {
    253 
    254             // Skip existing thumbnails with file size below $s['keep_filesize'].
    255             if (file_exists(realpath($this->source_dir . '/' . $s['dest_dir'] . '/' . $file_name)) && isset($s['keep_filesize'])) {
    256                 $file_size = filesize(realpath($this->source_dir . '/' . $s['dest_dir'] . '/' . $file_name));
    257                 if ($file_size && $file_size < $s['keep_filesize']) {
    258                     App::logMsg(sprintf('Skipping thumbnail %s. File already exists and file size is less than %s bytes.', $s['dest_dir'] . '/' . $file_name, $s['keep_filesize']), LOG_DEBUG, __FILE__, __LINE__);
     288        $return_val = 0;
     289
     290        foreach ($this->image_specs as $spec) {
     291           
     292            // Destination filename uses the extention defined by dest_file_extention.
     293            $dest_file = realpath(sprintf('%s/%s/%s.%s', $this->getParam('source_dir'), $spec['dest_dir'], substr($file_name, 0, strrpos($file_name, '.')), $this->getParam('dest_file_extention')));
     294
     295            // Skip existing thumbnails with file size below $spec['keep_filesize'].
     296            if (isset($spec['keep_filesize']) && file_exists($dest_file)) {
     297                $file_size = filesize($dest_file);
     298                if (false !== $file_size && $file_size < $spec['keep_filesize']) {
     299                    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_DEBUG, __FILE__, __LINE__);
    259300                    continue;
    260301                }
    261302            }
    262303
    263             // Determine if original file size is smaller than specified thumbnail size. Do not scale-up if allow_upscaling config is set to false.
    264             $image_size = getimagesize(realpath($this->source_dir . '/' . $file_name));
    265             if ($image_size['0'] <= $s['width'] && $image_size['1'] <= $s['height'] && !$s['allow_upscaling']) {
    266                 $pnmscale_args = ' 1 ';
    267                 App::logMsg(sprintf('Image %s smaller than specified %s thumbnail size. Keeping original size.', $file_name, $s['dest_dir']), LOG_DEBUG, __FILE__, __LINE__);
     304            // Determine if original file size is smaller than specified thumbnail size. Do not scale-up if $spec['allow_upscaling'] config is set to false.
     305            $image_size = getimagesize($source_file);
     306            if ($image_size['0'] <= $spec['width'] && $image_size['1'] <= $spec['height'] && !$spec['allow_upscaling']) {
     307                $spec['scaling_type'] = IMAGETHUMB_NO_SCALE;
     308                App::logMsg(sprintf('Image %s smaller than specified %s thumbnail size. Keeping original size.', $file_name, $spec['dest_dir']), LOG_DEBUG, __FILE__, __LINE__);
     309            }
     310
     311            // DO IT! Based on available method.
     312            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'))) {
     313                // Resize using Netpbm binaries.
     314                App::logMsg(sprintf('Resizing with Netpbm...', null), LOG_DEBUG, __FILE__, __LINE__);
     315                $return_val += $this->_resizeWithNetpbm($source_file, $dest_file, $spec);
     316            } else if (IMAGETHUMB_METHOD_GD === $this->getParam('resize_method') && extension_loaded('gd')) {
     317                // Resize with GD.
     318                App::logMsg(sprintf('Resizing with GD...', null), LOG_DEBUG, __FILE__, __LINE__);
     319                $return_val += $this->_resizeWithGD($source_file, $dest_file, $spec);
    268320            } else {
    269                 $pnmscale_args = $s['pnmscale_args'];
    270             }
    271 
    272             // Execute the command that creates the thumbnail.
    273             $command = sprintf('%s %s/%s | %s %s | %s %s > %s/%s',
    274                 escapeshellcmd($this->anytopnm_binary),
    275                 escapeshellcmd($this->source_dir),
    276                 escapeshellcmd($file_name),
    277                 escapeshellcmd($this->pnmscale_binary),
    278                 escapeshellcmd($pnmscale_args),
    279                 escapeshellcmd($this->cjpeg_binary),
    280                 escapeshellcmd($s['cjpeg_args']),
    281                 escapeshellcmd(realpath($this->source_dir . '/' . $s['dest_dir'])),
    282                 escapeshellcmd($file_name)
    283             );
    284             App::logMsg(sprintf('ImageThumb command: %s', $command), LOG_DEBUG, __FILE__, __LINE__);
    285             exec($command, $output, $return_val);
    286 
    287             if (0 == $return_val) {
    288                 // Make the thumbnail writable so the user can delete it over ftp without being 'apache'.
    289                 chmod(realpath($this->source_dir . '/' . $s['dest_dir'] . '/' . $file_name), $this->dest_file_perms);
    290                 App::logMsg(sprintf('Successfully resized image %s', $s['dest_dir'] . '/' . $file_name, $return_val), LOG_DEBUG, __FILE__, __LINE__);
     321                App::logMsg(sprintf('Image thumbnailing failed. Neither Netpbm or GD is available.', null), LOG_DEBUG, __FILE__, __LINE__);
     322                return false;
     323            }
     324        }
     325
     326        // If > 0, there was a problem thumb-nailing.
     327        return 0 === $return_val;
     328    }
     329   
     330    /*
     331    * Use the Netpbm and libjpg cjpeg tools to generate a rescaled compressed image.
     332    * This is the preferred method over GD which has (supposedly) less quality.
     333    *
     334    * @access   private
     335    * @param    string  $source_file    Full path to source image file.
     336    * @param    string  $dest_file      Full path to destination image file.
     337    * @param    array   $spec           Array of image size specifications.
     338    * @return   bool                    Success value.
     339    * @author   Quinn Comendant <quinn@strangecode.com>
     340    * @version  1.0
     341    * @since    19 May 2006 13:55:46
     342    */
     343    function _resizeWithNetpbm($source_file, $dest_file, $spec)
     344    {
     345        // Define pnmscale arguments.
     346        switch ($spec['scaling_type']) {
     347        case IMAGETHUMB_FIT_WIDTH :
     348            $pnmscale_args = sprintf(' -width %s ', escapeshellarg($spec['width']));
     349            break;
     350        case IMAGETHUMB_FIT_HEIGHT :
     351            $pnmscale_args = sprintf(' -height %s ', escapeshellarg($spec['height']));
     352            break;
     353        case IMAGETHUMB_FIT_LARGER :
     354            $pnmscale_args = sprintf(' -xysize %s %s ', escapeshellarg($spec['width']), escapeshellarg($spec['height']));
     355            break;
     356        case IMAGETHUMB_STRETCH :
     357            $pnmscale_args = sprintf(' -width %s -height %s ', escapeshellarg($spec['width']), escapeshellarg($spec['height']));
     358            break;
     359        case IMAGETHUMB_NO_SCALE :
     360        default :
     361            $pnmscale_args = ' 1 ';
     362            break;
     363        }
     364
     365        // Define cjpeg arguments.
     366        $cjpeg_args = sprintf(' -optimize -quality %s ', escapeshellarg($spec['quality']));
     367        $cjpeg_args .= (true === $spec['progressive']) ? ' -progressive ' : '';
     368
     369        // Format the command that creates the thumbnail.
     370        $command = sprintf('%s %s | %s %s | %s %s > %s/%s',
     371            escapeshellcmd($this->getParam('anytopnm_binary')),
     372            escapeshellcmd($source_file),
     373            escapeshellcmd($this->getParam('pnmscale_binary')),
     374            escapeshellcmd($pnmscale_args),
     375            escapeshellcmd($this->getParam('cjpeg_binary')),
     376            escapeshellcmd($cjpeg_args),
     377            escapeshellcmd($dest_file),
     378            escapeshellcmd($file_name)
     379        );
     380        App::logMsg(sprintf('ImageThumb Netpbm command: %s', $command), LOG_DEBUG, __FILE__, __LINE__);
     381       
     382        // Execute!
     383        exec($command, $output, $return_val);
     384
     385        if (0 === $return_val) {
     386            // Success!
     387            // Make the thumbnail writable so the user can delete it over ftp without being 'apache'.
     388            chmod($dest_file, $this->getParam('dest_file_perms'));
     389            App::logMsg(sprintf('Successfully resized image %s', $spec['dest_dir'] . '/' . $file_name, $return_val), LOG_DEBUG, __FILE__, __LINE__);
     390        } else {
     391            // An error occurred.
     392            App::logMsg(sprintf('Image %s failed resizing with return value: %s%s', $spec['dest_dir'] . '/' . $file_name, $return_val, empty($output) ? '' : ' (' . getDump($output) . ')'), LOG_ERR, __FILE__, __LINE__);
     393        }
     394
     395        // Return from the command will be > 0 if there was an error.
     396        return $return_val;
     397    }
     398
     399    /*
     400    * Use PHP's built-in GD tools to generate a rescaled compressed image.
     401    *
     402    * @access   private
     403    * @param    string  $source_file    Full path to source image file.
     404    * @param    string  $dest_file      Full path to destination image file.
     405    * @param    array   $spec           Array of image size specifications.
     406    * @return   bool                    Success value.
     407    * @author   Quinn Comendant <quinn@strangecode.com>
     408    * @version  1.0
     409    * @since    19 May 2006 15:46:02
     410    */
     411    function _resizeWithGD($source_file, $dest_file, $spec)
     412    {
     413        // Get original file dimensions and type.
     414        list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_file);
     415
     416        // Define destination image dimentions.
     417        switch ($spec['scaling_type']) {
     418        case IMAGETHUMB_FIT_WIDTH :
     419            $dest_image_height = $source_image_height * ($spec['width'] / $source_image_width);
     420            $dest_image_width = $spec['width'];
     421            break;
     422        case IMAGETHUMB_FIT_HEIGHT :
     423            $dest_image_width = $source_image_width * ($spec['height'] / $source_image_height);
     424            $dest_image_height = $spec['height'];
     425            break;
     426        case IMAGETHUMB_FIT_LARGER :
     427            if ($source_image_width < $source_image_height) {
     428               $dest_image_width = $source_image_width * ($spec['height'] / $source_image_height);
     429               $dest_image_height = $source_image_height;
    291430            } else {
    292                 App::logMsg(sprintf('Image %s failed resizing with return value: %s%s', $s['dest_dir'] . '/' . $file_name, $return_val, empty($output) ? '' : ' (' . getDump($output) . ')'), LOG_ERR, __FILE__, __LINE__);
    293             }
    294 
    295             // Return from the command will be > 0 if there was an error.
    296             $final_return_val += $return_val;
    297         }
    298 
    299         // If > 0, there was a problem thumbnailing.
    300         return (0 == $final_return_val);
    301     }
    302 
    303     /**
    304      * Process an entire directory of images.
    305      *
    306      * @access  public
    307      * @return  bool true on success, false on failure.
    308      */
    309     function processAll()
    310     {
    311         // Ensure we have a source.
    312         if (!isset($this->source_dir)) {
    313             App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
    314             return false;
    315         }
    316 
    317         // Get all files in source directory.
    318         $dir_handle = opendir($this->source_dir);
    319         while ($dir_handle && ($file = readdir($dir_handle)) !== false) {
    320             // If the file name does not start with a dot (. or .. or .htaccess).
    321             if (!preg_match('/^\./', $file) && in_array(strtolower(substr($file, strrpos($file, '.') + 1)), $this->valid_file_extensions)) {
    322                 $files[] = $file;
    323             }
    324         }
    325 
    326         // Process each found file.
    327         if (is_array($files) && !empty($files)) {
    328             foreach ($files as $file_name) {
    329                 $this->processFile($file_name);
    330             }
    331             return sizeof($files);
     431               $dest_image_height = $source_image_height * ($spec['width'] / $source_image_width);
     432               $dest_image_width = $source_image_width;
     433            }
     434            break;
     435        case IMAGETHUMB_STRETCH :
     436            $dest_image_width = $spec['width'];
     437            $dest_image_height = $spec['height'];
     438            break;
     439        case IMAGETHUMB_NO_SCALE :
     440        default :
     441            $dest_image_width = $source_image_width;
     442            $dest_image_height = $source_image_height;
     443            break;
     444        }
     445       
     446        echo '<li> sourceWidth: ' . $source_image_width;
     447        echo '<li> sourceHeight: ' . $source_image_height;
     448        echo '<li> Width: ' . $dest_image_width;
     449        echo '<li> Height: ' . $dest_image_height;
     450        die;///
     451
     452        // Create source image data in memory.
     453        switch ($source_image_type) {
     454        case IMAGETYPE_JPEG :
     455            $source_image_data = imagecreatefromjpeg($source_file);
     456            break;
     457        case IMAGETYPE_PNG :
     458            $source_image_data = imagecreatefrompng($source_file);
     459            break;
     460        case IMAGETYPE_GIF :
     461            $source_image_data = imagecreatefromgif($source_file);
     462            break;
     463        case IMAGETYPE_WBMP :
     464            $source_image_data = imagecreatefromwbmp($source_file);
     465        default :
     466            App::logMsg(sprintf('Source image type %s not supported.', $source_image_type), LOG_WARNING, __FILE__, __LINE__);
     467            return false;
     468            break;
     469        }
     470        if (!$source_image_data) {
     471            App::logMsg(sprintf('Error creating %s image in memory from %s', $source_image_type, $source_file), LOG_WARNING, __FILE__, __LINE__);
     472            return false;
     473        }
     474       
     475        // Create destination image data in memory.
     476        $dest_image_data = imagecreatetruecolor($dest_image_width, $dest_image_height);
     477
     478        // Resample!
     479        if (!imagecopyresampled($dest_image_data, $source_image_data, 0, 0, 0, 0, $dest_image_width, $dest_image_height, $source_image_width, $source_image_height)) {
     480            App::logMsg(sprintf('Error resampling image %s', $source_file), LOG_WARNING, __FILE__, __LINE__);
     481            return false;
     482        }
     483
     484        // Save image.
     485        $return_val = true;
     486        switch ($this->getParam('dest_file_type')) {
     487        case IMG_JPG :
     488            imageinterlace($dest_image_data, (true == $spec['progressive'] ? 1 : 0));
     489            $return_val = imagejpeg($dest_image_data, $dest_file, $spec['quality']);
     490            break;
     491        case IMG_PNG :
     492            $return_val = imagepng($dest_image_data, $dest_file);
     493            break;
     494        case IMG_GIF :
     495            $return_val = imagegif($dest_image_data, $dest_file);
     496            break;
     497        case IMG_WBMP :
     498            $return_val = imagewbmp($dest_image_data, $dest_file);
     499            break;
     500        default :
     501            App::logMsg(sprintf('Destination image type %s not supported for image %s.', $this->getParam('dest_file_type'), $dest_file), LOG_WARNING, __FILE__, __LINE__);
     502            return false;
     503            break;
     504        }
     505
     506        if ($return_val) {
     507            // Success!
     508            // Make the thumbnail writable so the user can delete it over ftp without being 'apache'.
     509            chmod($source_file, $this->getParam('dest_file_perms'));
     510            App::logMsg(sprintf('Successfully resized image %s', $dest_file), LOG_DEBUG, __FILE__, __LINE__);
     511            return true;
    332512        } else {
    333             App::logMsg(sprintf('No images found to thumbnail in directory %s.', $this->source_dir), LOG_NOTICE, __FILE__, __LINE__);
    334             return 0;
     513            // An error occurred.
     514            App::logMsg(sprintf('Image %s failed resizing.', $dest_file), LOG_ERR, __FILE__, __LINE__);
     515            return false;
    335516        }
    336517    }
     
    346527    {
    347528        // Ensure we have a source.
    348         if (!isset($this->source_dir)) {
     529        if ('' == $this->getParam('source_dir')) {
    349530            App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
    350531            return false;
    351532        }
    352533
    353         $ret = 0;
    354         foreach ($this->spec as $s) {
    355             $file_path_name = realpath($this->source_dir . '/' . $s['dest_dir'] . '/' . $file_name);
    356             if (file_exists($file_path_name)) {
    357                 if (!unlink($file_path_name)) {
    358                     $ret++;
    359                     App::logMsg(sprintf(_("Delete thumbs failed: %s"), $file_path_name), LOG_WARNING, __FILE__, __LINE__);
     534        $return_val = 0;
     535        foreach ($this->image_specs as $spec) {
     536            $dest_file = realpath(sprintf('%s/%s/%s.%s', $this->getParam('source_dir'), $spec['dest_dir'], substr($file_name, 0, strrpos($file_name, '.')), $this->getParam('dest_file_extention')));
     537            if (file_exists($dest_file)) {
     538                if (!unlink($dest_file)) {
     539                    $return_val++;
     540                    App::logMsg(sprintf(_("Delete thumbs failed: %s"), $dest_file), LOG_WARNING, __FILE__, __LINE__);
    360541                }
    361542            }
    362543        }
    363         $this->raiseMsg(sprintf(_("The thumbnails for file <strong>%s</strong> have been deleted."), $file_name), MSG_SUCCESS, __FILE__, __LINE__);
    364         return (0 == $ret);
     544        $this->_raiseMsg(sprintf(_("The thumbnails for file <strong>%s</strong> have been deleted."), $file_name), MSG_SUCCESS, __FILE__, __LINE__);
     545        return 0 === $return_val;
    365546    }
    366547
     
    375556    {
    376557        // Ensure we have a source.
    377         if (!isset($this->source_dir)) {
     558        if ('' == $this->getParam('source_dir')) {
    378559            App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
    379560            return false;
    380561        }
    381562
    382         $file_path_name = $this->source_dir . '/' . $file_name;
    383         if (!unlink($file_path_name)) {
    384             App::logMsg(sprintf(_("Delete original failed: %s"), $file_path_name), LOG_WARNING, __FILE__, __LINE__);
    385             return false;
    386         }
    387         $this->raiseMsg(sprintf(_("The original file <strong>%s</strong> has been deleted."), $file_name), MSG_SUCCESS, __FILE__, __LINE__);
     563        $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name));
     564        if (!unlink($source_file)) {
     565            App::logMsg(sprintf(_("Delete original failed: %s"), $source_file), LOG_WARNING, __FILE__, __LINE__);
     566            return false;
     567        }
     568        $this->_raiseMsg(sprintf(_("The original file <strong>%s</strong> has been deleted."), $file_name), MSG_SUCCESS, __FILE__, __LINE__);
    388569        return true;
    389570    }
     
    399580    {
    400581        // Ensure we have a source.
    401         if (!isset($this->source_dir)) {
     582        if ('' == $this->getParam('source_dir')) {
    402583            App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__);
    403584            return false;
    404585        }
    405586
    406         return file_exists($this->source_dir . '/' . $file_name);
     587        $source_file = realpath(sprintf('%s/%s', $this->getParam('source_dir'), $file_name));
     588        return file_exists($source_file);
    407589    }
    408590
     
    414596     * @return  bool    True on success, false on failure.
    415597     */
    416     function validFileExtension($file_name)
     598    function _validFileExtension($file_name)
    417599    {
    418600        preg_match('/.*?\.(\w+)$/i', $file_name, $ext);
    419         return in_array(strtolower($ext[1]), $this->valid_file_extensions);
    420     }
    421 
    422     /**
    423      * An alias for App::raiseMsg that only sends messages if display_messages is true.
     601        return in_array(strtolower($ext[1]), $this->getParam('valid_file_extensions'));
     602    }
     603
     604    /**
     605     * Make directory for each specified thumbnail size, if it doesn't exist.
     606     *
     607     * @access  public
     608     * @return  bool true on success, false on failure.
     609     */
     610    function _createDestDirs()
     611    {
     612        static $already_checked = false;
     613
     614        if (!$already_checked) {
     615            // Ensure we have a source.
     616            if ('' == $this->getParam('source_dir')) {
     617                App::logMsg(sprintf('Source directory not set before creating destination directories.'), LOG_ERR, __FILE__, __LINE__);
     618                return false;
     619            }
     620       
     621            // Loop through specs and ensure all dirs are created.
     622            $return_val = 0;
     623            foreach ($this->image_specs as $spec) {
     624                if (!file_exists($this->getParam('source_dir') . '/' . $spec['dest_dir'])) {
     625                    if (!mkdir($this->getParam('source_dir') . '/' . $spec['dest_dir'], $this->getParam('dest_dir_perms'))) {
     626                        $return_val++;
     627                        App::logMsg(sprintf('mkdir failure: %s', $this->getParam('source_dir') . '/' . $spec['dest_dir']), LOG_ERR, __FILE__, __LINE__);
     628                    }
     629                }
     630            }
     631
     632            // If > 0, there was a problem creating dest dirs.
     633            return 0 === $return_val;
     634        }
     635
     636        $already_checked = true;
     637    }
     638
     639    /**
     640     * An alias for App::raiseMsg that only sends messages configured by display_messages.
    424641     *
    425642     * @access public
     
    431648     * @param string $line    __LINE__.
    432649     */
    433     function raiseMsg($message, $type, $file, $line)
    434     {
    435         if ($this->display_messages) {
     650    function _raiseMsg($message, $type, $file, $line)
     651    {
     652        if ($this->getParam('display_messages') === true || (is_int($this->getParam('display_messages')) && $this->getParam('display_messages') & $type > 0)) {
    436653            App::raiseMsg($message, $type, $file, $line);
    437654        }
Note: See TracChangeset for help on using the changeset viewer.