Changeset 119 for trunk/lib/ImageThumb.inc.php
- Timestamp:
- May 20, 2006 2:12:12 AM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/lib/ImageThumb.inc.php
r42 r119 3 3 * ImageThumb.inc.php 4 4 * Code by Strangecode :: www.strangecode.com :: This document contains copyrighted information 5 */6 7 /**8 * The ImageThumb class resizes images.9 5 * 10 6 * @author Quinn Comendant <quinn@strangecode.com> 11 * @requires Netpbm binaries from http://sourceforge.net/projects/netpbm/12 * @version 1.17 * @requires Netpbm <http://sourceforge.net/projects/netpbm/> and libjpeg or GD. 8 * @version 2.0 13 9 */ 14 10 11 // Image resize options. 15 12 define('IMAGETHUMB_FIT_WIDTH', 1); 16 13 define('IMAGETHUMB_FIT_HEIGHT', 2); … … 18 15 define('IMAGETHUMB_STRETCH', 4); 19 16 define('IMAGETHUMB_NO_SCALE', 5); 17 define('IMAGETHUMB_METHOD_NETPBM', 6); 18 define('IMAGETHUMB_METHOD_GD', 7); 20 19 21 20 class 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 74 194 * @return bool true on success, false on failure. 75 195 */ 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() 172 197 { 173 198 // 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 } 190 224 } 191 225 … … 199 233 function processFile($file_name) 200 234 { 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 206 238 // 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')) { 208 246 App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); 209 247 return false; 210 248 } 211 249 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 212 281 // To keep this script running even if user tries to stop browser. 213 282 ignore_user_abort(true); … … 216 285 } 217 286 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 249 287 // 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__); 259 300 continue; 260 301 } 261 302 } 262 303 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); 268 320 } 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; 291 430 } 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; 332 512 } 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; 335 516 } 336 517 } … … 346 527 { 347 528 // Ensure we have a source. 348 if ( !isset($this->source_dir)) {529 if ('' == $this->getParam('source_dir')) { 349 530 App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); 350 531 return false; 351 532 } 352 533 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__); 360 541 } 361 542 } 362 543 } 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; 365 546 } 366 547 … … 375 556 { 376 557 // Ensure we have a source. 377 if ( !isset($this->source_dir)) {558 if ('' == $this->getParam('source_dir')) { 378 559 App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); 379 560 return false; 380 561 } 381 562 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__); 388 569 return true; 389 570 } … … 399 580 { 400 581 // Ensure we have a source. 401 if ( !isset($this->source_dir)) {582 if ('' == $this->getParam('source_dir')) { 402 583 App::logMsg(sprintf('Source directory not set before processing.'), LOG_ERR, __FILE__, __LINE__); 403 584 return false; 404 585 } 405 586 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); 407 589 } 408 590 … … 414 596 * @return bool True on success, false on failure. 415 597 */ 416 function validFileExtension($file_name)598 function _validFileExtension($file_name) 417 599 { 418 600 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. 424 641 * 425 642 * @access public … … 431 648 * @param string $line __LINE__. 432 649 */ 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)) { 436 653 App::raiseMsg($message, $type, $file, $line); 437 654 }
Note: See TracChangeset
for help on using the changeset viewer.