1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4: */ 4 5/** 6 * ImageMagick binaries implementation for Image_Transform package 7 * 8 * PHP versions 4 and 5 9 * 10 * LICENSE: This source file is subject to version 3.0 of the PHP license 11 * that is available through the world-wide-web at the following URI: 12 * http://www.php.net/license/3_0.txt. If you did not receive a copy of 13 * the PHP License and are unable to obtain it through the web, please 14 * send a note to license@php.net so we can mail you a copy immediately. 15 * 16 * @category Image 17 * @package Image_Transform 18 * @author Peter Bowyer <peter@mapledesign.co.uk> 19 * @author Philippe Jausions <Philippe.Jausions@11abacus.com> 20 * @copyright 2002-2005 The PHP Group 21 * @license http://www.php.net/license/3_0.txt PHP License 3.0 22 * @version CVS: $Id: IM.php 266859 2008-09-30 22:28:47Z dufuz $ 23 * @link http://pear.php.net/package/Image_Transform 24 */ 25 26require_once 'Image/Transform.php'; 27require_once 'System.php'; 28 29/** 30 * ImageMagick binaries implementation for Image_Transform package 31 * 32 * @category Image 33 * @package Image_Transform 34 * @subpackage Image_Transform_Driver_IM 35 * @author Peter Bowyer <peter@mapledesign.co.uk> 36 * @author Philippe Jausions <Philippe.Jausions@11abacus.com> 37 * @copyright 2002-2005 The PHP Group 38 * @license http://www.php.net/license/3_0.txt PHP License 3.0 39 * @version Release: @package_version@ 40 * @link http://pear.php.net/package/Image_Transform 41 * @link http://www.imagemagick.org/ 42 **/ 43class Image_Transform_Driver_IM extends Image_Transform 44{ 45 /** 46 * associative array commands to be executed 47 * @var array 48 * @access private 49 */ 50 var $command; 51 52 /** 53 * Class constructor 54 */ 55 function Image_Transform_Driver_IM() 56 { 57 $this->__construct(); 58 } // End Image_IM 59 60 /** 61 * Class constructor 62 */ 63 function __construct() 64 { 65 $this->_init(); 66 if (!defined('IMAGE_TRANSFORM_IM_PATH')) { 67 $path = dirname(System::which('convert')) 68 . DIRECTORY_SEPARATOR; 69 define('IMAGE_TRANSFORM_IM_PATH', $path); 70 } 71 if (System::which(IMAGE_TRANSFORM_IM_PATH . 'convert' . ((OS_WINDOWS) ? '.exe' : ''))) { 72 include 'Image/Transform/Driver/Imagick/ImageTypes.php'; 73 } else { 74 $this->isError(PEAR::raiseError('Couldn\'t find "convert" binary', 75 IMAGE_TRANSFORM_ERROR_UNSUPPORTED)); 76 } 77 } // End Image_IM 78 79 /** 80 * Initialize the state of the object 81 **/ 82 function _init() 83 { 84 $this->command = array(); 85 } 86 87 /** 88 * Load an image. 89 * 90 * This method doesn't support remote files. 91 * 92 * @param string filename 93 * 94 * @return mixed TRUE or a PEAR error object on error 95 * @see PEAR::isError() 96 */ 97 function load($image) 98 { 99 $this->_init(); 100 if (!file_exists($image)) { 101 return PEAR::raiseError('The image file ' . $image 102 . ' doesn\'t exist', IMAGE_TRANSFORM_ERROR_IO); 103 } 104 $this->image = $image; 105 $result = $this->_get_image_details($image); 106 if (PEAR::isError($result)) { 107 return $result; 108 } 109 return true; 110 111 } // End load 112 113 /** 114 * Image_Transform_Driver_IM::_get_image_details() 115 * 116 * @param string $image the path and name of the image file 117 * @return none 118 */ 119 function _get_image_details($image) 120 { 121 $retval = Image_Transform::_get_image_details($image); 122 if (PEAR::isError($retval)) { 123 unset($retval); 124 125 if (!System::which(IMAGE_TRANSFORM_IM_PATH . 'identify' . ((OS_WINDOWS) ? '.exe' : ''))) { 126 $this->isError(PEAR::raiseError('Couldn\'t find "identify" binary', IMAGE_TRANSFORM_ERROR_UNSUPPORTED)); 127 } 128 $cmd = $this->_prepare_cmd(IMAGE_TRANSFORM_IM_PATH, 129 'identify', 130 '-format %w:%h:%m ' . escapeshellarg($image)); 131 exec($cmd, $res, $exit); 132 133 if ($exit != 0) { 134 return PEAR::raiseError("Cannot fetch image or images details.", true); 135 } 136 137 $data = explode(':', $res[0]); 138 $this->img_x = $data[0]; 139 $this->img_y = $data[1]; 140 $this->type = strtolower($data[2]); 141 $retval = true; 142 143 } 144 145 return $retval; 146 } 147 148 /** 149 * Resize the image. 150 * 151 * @access private 152 * 153 * @param int $new_x New width 154 * @param int $new_y New height 155 * @param mixed $options Optional parameters 156 * 157 * @return true on success or PEAR Error object on error 158 * @see PEAR::isError() 159 */ 160 function _resize($new_x, $new_y, $options = null) 161 { 162 if (isset($this->command['resize'])) { 163 return PEAR::raiseError('You cannot scale or resize an image more than once without calling save() or display()', true); 164 } 165 $this->command['resize'] = '-geometry ' 166 . ((int) $new_x) . 'x' . ((int) $new_y) . '!'; 167 168 $this->new_x = $new_x; 169 $this->new_y = $new_y; 170 171 return true; 172 } // End resize 173 174 /** 175 * rotate 176 * 177 * @param int angle rotation angle 178 * @param array options no option allowed 179 * @return mixed TRUE or a PEAR error object on error 180 */ 181 function rotate($angle, $options = null) 182 { 183 $angle = $this->_rotation_angle($angle); 184 if ($angle % 360) { 185 $this->command['rotate'] = '-rotate ' . (float) $angle; 186 } 187 return true; 188 189 } // End rotate 190 191 /** 192 * Crop image 193 * 194 * @author Ian Eure <ieure@websprockets.com> 195 * @since 0.8 196 * 197 * @param int width Cropped image width 198 * @param int height Cropped image height 199 * @param int x X-coordinate to crop at 200 * @param int y Y-coordinate to crop at 201 * 202 * @return mixed TRUE or a PEAR error object on error 203 */ 204 function crop($width, $height, $x = 0, $y = 0) { 205 // Do we want a safety check - i.e. if $width+$x > $this->img_x then we 206 // raise a warning? [and obviously same for $height+$y] 207 $this->command['crop'] = '-crop ' 208 . ((int) $width) . 'x' . ((int) $height) 209 . '+' . ((int) $x) . '+' . ((int) $y) . '!'; 210 211 // I think that setting img_x/y is wrong, but scaleByLength() & friends 212 // mess up the aspect after a crop otherwise. 213 $this->new_x = $this->img_x = $width - $x; 214 $this->new_y = $this->img_y = $height - $y; 215 216 return true; 217 } 218 219 /** 220 * addText 221 * 222 * @param array options Array contains options 223 * array( 224 * 'text' The string to draw 225 * 'x' Horizontal position 226 * 'y' Vertical Position 227 * 'Color' Font color 228 * 'font' Font to be used 229 * 'size' Size of the fonts in pixel 230 * 'resize_first' Tell if the image has to be resized 231 * before drawing the text 232 * ) 233 * 234 * @return mixed TRUE or a PEAR error object on error 235 * @see PEAR::isError() 236 */ 237 function addText($params) 238 { 239 $this->old_image = $this->imageHandle; 240 $params = array_merge($this->_get_default_text_params(), $params); 241 extract($params); 242 243 if (true === $resize_first) { 244 // Set the key so that this will be the last item in the array 245 $key = 'ztext'; 246 } else { 247 $key = 'text'; 248 } 249 $this->command[$key] = '-font ' . escapeshellarg($font) 250 . ' -fill ' . escapeshellarg($color) 251 . ' -draw \'text ' . escapeshellarg($x . ',' . $y) 252 . ' "' . escapeshellarg($text) . '"\''; 253 // Producing error: gs: not found gs: not found convert: Postscript delegate failed [No such file or directory]. 254 return true; 255 256 } // End addText 257 258 /** 259 * Adjust the image gamma 260 * 261 * @access public 262 * @param float $outputgamma 263 * @return mixed TRUE or a PEAR error object on error 264 */ 265 function gamma($outputgamma = 1.0) { 266 if ($outputgamme != 1.0) { 267 $this->command['gamma'] = '-gamma ' . (float) $outputgamma; 268 } 269 return true; 270 } 271 272 /** 273 * Convert the image to greyscale 274 * 275 * @access public 276 * @return mixed TRUE or a PEAR error object on error 277 */ 278 function greyscale() { 279 $this->command['type'] = '-type Grayscale'; 280 return true; 281 } 282 283 /** 284 * Horizontal mirroring 285 * 286 * @access public 287 * @return TRUE or PEAR Error object on error 288 */ 289 function mirror() { 290 // We can only apply "flop" once 291 if (isset($this->command['flop'])) { 292 unset($this->command['flop']); 293 } else { 294 $this->command['flop'] = '-flop'; 295 } 296 return true; 297 } 298 299 /** 300 * Vertical mirroring 301 * 302 * @access public 303 * @return TRUE or PEAR Error object on error 304 */ 305 function flip() { 306 // We can only apply "flip" once 307 if (isset($this->command['flip'])) { 308 unset($this->command['flip']); 309 } else { 310 $this->command['flip'] = '-flip'; 311 } 312 return true; 313 } 314 315 /** 316 * Save the image file 317 * 318 * @access public 319 * 320 * @param $filename string the name of the file to write to 321 * @param $quality quality image dpi, default=75 322 * @param $type string (JPEG, PNG...) 323 * 324 * @return mixed TRUE or a PEAR error object on error 325 */ 326 function save($filename, $type = '', $quality = null) 327 { 328 $type = strtoupper(($type == '') ? $this->type : $type); 329 switch ($type) { 330 case 'JPEG': 331 $type = 'JPG'; 332 break; 333 } 334 $options = array(); 335 if (!is_null($quality)) { 336 $options['quality'] = $quality; 337 } 338 $quality = $this->_getOption('quality', $options, 75); 339 340 $cmd = $this->_prepare_cmd( 341 IMAGE_TRANSFORM_IM_PATH, 342 'convert', 343 implode(' ', $this->command) 344 . ' -quality ' . ((int) $quality) . ' ' 345 . escapeshellarg($this->image) . ' ' . $type . ':' 346 . escapeshellarg($filename) . ' 2>&1'); 347 exec($cmd, $res, $exit); 348 349 if (!$this->keep_settings_on_save) { 350 $this->free(); 351 } 352 353 return ($exit == 0) ? true : PEAR::raiseError(implode('. ', $res), 354 IMAGE_TRANSFORM_ERROR_IO); 355 } // End save 356 357 /** 358 * Display image without saving and lose changes 359 * 360 * This method adds the Content-type HTTP header 361 * 362 * @access public 363 * 364 * @param string type (JPEG,PNG...); 365 * @param int quality 75 366 * 367 * @return mixed TRUE or a PEAR error object on error 368 */ 369 function display($type = '', $quality = null) 370 { 371 $type = strtoupper(($type == '') ? $this->type : $type); 372 switch ($type) { 373 case 'JPEG': 374 $type = 'JPG'; 375 break; 376 } 377 $options = array(); 378 if (!is_null($quality)) { 379 $options['quality'] = $quality; 380 } 381 $quality = $this->_getOption('quality', $options, 75); 382 383 $this->_send_display_headers($type); 384 385 $cmd = $this->_prepare_cmd( 386 IMAGE_TRANSFORM_IM_PATH, 387 'convert', 388 implode(' ', $this->command) . " -quality $quality " . 389 $this->image . ' ' . $type . ":-"); 390 passthru($cmd); 391 392 if (!$this->keep_settings_on_save) { 393 $this->free(); 394 } 395 return true; 396 } 397 398 /** 399 * Destroy image handle 400 * 401 * @return void 402 */ 403 function free() 404 { 405 $this->command = array(); 406 $this->image = ''; 407 $this->type = ''; 408 } 409 410} // End class ImageIM 411