Graphics.php
Go to the documentation of this file.
1 <?php
14 namespace Depage\Graphics;
15 
21 function autoload($class)
22 {
23  if (strpos($class, __NAMESPACE__ . '\\') == 0) {
24  $class = str_replace('\\', '/', str_replace(__NAMESPACE__ . '\\', '', $class));
25  $file = __DIR__ . '/' . $class . '.php';
26 
27  if (file_exists($file)) {
28  require_once($file);
29  }
30  }
31 }
32 
33 spl_autoload_register(__NAMESPACE__ . '\autoload');
34 
40 class Graphics
41 {
45  protected $input;
49  protected $output;
53  protected $outputLockFp = null;
57  protected $otherRender = false;
61  protected $queue = array();
65  protected $size = array();
69  protected $background;
73  protected $quality = '';
77  protected $optimize;
81  protected $optimizers;
85  protected $inputFormat;
89  protected $outputFormat;
93  protected $bypass = true;
97  private $oldIgnoreUserAbort = false;
107  public static function factory($options = array())
108  {
109  $extension = (isset($options['extension'])) ? $options['extension'] : 'gd';
110 
111  if ($extension == 'imagick' && extension_loaded('imagick')) {
112  return new Providers\Imagick($options);
113  } elseif ($extension == 'im' || $extension == 'imagemagick') {
114  if (isset($options['executable'])) {
115  return new Providers\Imagemagick($options);
116  } else {
117  $executable = Graphics::which('convert');
118  if ($executable == null) {
119  trigger_error("Cannot find ImageMagick, falling back to GD", E_USER_WARNING);
120  } else {
121  $options['executable'] = $executable;
122 
123  return new Providers\Imagemagick($options);
124  }
125  }
126  } elseif ($extension == 'gm' || $extension == 'graphicsmagick') {
127  if (isset($options['executable'])) {
128  return new Providers\Graphicsmagick($options);
129  } else {
130  $executable = Graphics::which('gm');
131  if ($executable == null) {
132  trigger_error("Cannot find GraphicsMagick, falling back to GD", E_USER_WARNING);
133  } else {
134  $options['executable'] = $executable;
135 
136  return new Providers\Graphicsmagick($options);
137  }
138  }
139  }
140 
141  return new Providers\Gd($options);
142  }
148  public function __construct($options = array())
149  {
150  $this->background = (isset($options['background'])) ? $options['background'] : 'transparent';
151  $this->quality = (isset($options['quality'])) ? intval($options['quality']) : null;
152  $this->format = (isset($options['format'])) ? $options['format'] : null;
153  $this->optimize = (isset($options['optimize'])) ? $options['optimize'] : false;
154  $this->optimizers = (isset($options['optimizers'])) ? $options['optimizers'] : array();
155  }
156 
165  public function addBackground($background)
166  {
167  $this->background = $background;
168 
169  return $this;
170  }
182  public function addCrop($width, $height, $x = 0, $y = 0)
183  {
184  $this->queue[] = array('crop', func_get_args());
185 
186  return $this;
187  }
197  public function addResize($width, $height)
198  {
199  $this->queue[] = array('resize', func_get_args());
200 
201  return $this;
202  }
212  public function addThumb($width, $height)
213  {
214  $this->queue[] = array('thumb', func_get_args());
215 
216  return $this;
217  }
227  public function addThumbfill($width, $height)
228  {
229  $this->queue[] = array('thumbfill', func_get_args());
230 
231  return $this;
232  }
233 
242  protected function escapeNumber($number)
243  {
244  return (is_numeric($number)) ? intval($number) : null;
245  }
256  protected function dimensions($width, $height)
257  {
258  if (!is_numeric($width) && !is_numeric($height)) {
259  $width = null;
260  $height = null;
261  } elseif (!is_numeric($height)) {
262  $height = round(($this->size[1] / $this->size[0]) * $width);
263  } elseif (!is_numeric($width)) {
264  $width = round(($this->size[0] / $this->size[1]) * $height);
265  }
266 
267  return array($width, $height);
268  }
269 
277  protected function processQueue()
278  {
279  foreach ($this->queue as $task) {
280  $action = $task[0];
281  $arguments = array_map(array($this, 'escapeNumber'), $task[1]);
282 
283  call_user_func_array(array($this, $action), $arguments);
284  }
285  }
295  public function render($input, $output = null)
296  {
297  if (!file_exists($input)) throw new Exceptions\FileNotFound();
298 
299  $this->input = $input;
300  $this->output = ($output == null) ? $input : $output;
301  $this->inputFormat = $this->obtainFormat($this->input);
302  $this->outputFormat = ($this->format == null) ? $this->obtainFormat($this->output) : $this->format;
303  $this->size = $this->getImageSize();
304  $this->otherRender = false;
305 
306  $this->lock();
307 
308  $this->oldIgnoreUserAbort = ignore_user_abort();
309  ignore_user_abort(true);
310  }
316  public function renderFinished()
317  {
318  ignore_user_abort($this->oldIgnoreUserAbort);
319 
320  $this->unlock();
321  }
328  public function optimizeImage($filename)
329  {
330  if (!file_exists($filename)) {
331  return false;
332  }
333 
334  $optimizer = new Optimizers\Optimizer($this->optimizers);
335  $success = $optimizer->optimize($filename);
336 
337  return $success;
338  }
344  protected function lock()
345  {
346  // set lock
347  $this->outputLockFp = fopen($this->output . ".lock", 'w');
348  $locked = flock($this->outputLockFp, LOCK_EX | LOCK_NB, $wouldblock);
349 
350  if (!$locked && $wouldblock) {
351  $this->otherRender = true;
352  flock($this->outputLockFp, LOCK_EX);
353  }
354  }
360  protected function unlock()
361  {
362  // release lock
363  if (isset($this->outputLockFp)) {
364  flock($this->outputLockFp, LOCK_UN);
365  unlink($this->output . ".lock");
366 
367  $this->outputLockFp = null;
368  }
369  }
370 
377  protected function obtainFormat($fileName)
378  {
379  $parts = explode('.', $fileName);
380  $extension = strtolower(end($parts));
381 
382  if ($extension == 'jpeg') {
383  $extension = 'jpg';
384  } elseif (
385  $extension != 'jpg'
386  && $extension != 'png'
387  && $extension != 'gif'
388  ) {
389  if (is_callable('getimagesize') && file_exists($fileName)) {
390  $info = getimagesize($fileName);
391  if (isset($info[2])) {
392  $format = $info[2];
393 
394  if ($format == 1) {
395  $extension = 'gif';
396  } elseif ($format == 2) {
397  $extension = 'jpg';
398  } elseif ($format == 3) {
399  $extension = 'png';
400  }
401  }
402  }
403  }
404 
405  return $extension;
406  }
407 
416  public static function which($binary)
417  {
418  exec('which ' . $binary, $commandOutput, $returnStatus);
419  if ($returnStatus === 0) {
420  return $commandOutput[0];
421  } else {
422  return null;
423  }
424  }
425 
429  public function setQuality($quality)
430  {
431  $this->quality = $quality;
432  }
441  protected function getQuality()
442  {
443  if ($this->outputFormat == 'jpg') {
444  if (
445  is_numeric($this->quality)
446  && $this->quality >= 0
447  && $this->quality <= 100
448  ) {
450  } else {
451  $quality = 85;
452  }
453  } elseif ($this->outputFormat == "webp") {
454  if (
455  is_numeric($this->quality)
456  && $this->quality >= 0
457  && $this->quality <= 100
458  ) {
460  } else {
461  $quality = 75;
462  }
463  } elseif ($this->outputFormat == 'png') {
464  if (
465  is_numeric($this->quality)
466  && $this->quality >= 0
467  && $this->quality <= 95
468  && $this->quality % 10 <= 5
469  ) {
470  $quality = sprintf("%02d", $this->quality);
471  } else {
472  $quality = 95;
473  }
474  } else {
475  $quality = intval($this->quality);
476  }
477 
478  return (string) $quality;
479  }
480 
490  protected function bypassTest($width, $height, $x = 0, $y = 0)
491  {
492  if (
493  ($width !== null && $width < 1)
494  || ($height !== null && $height < 1)
495  || ($width == null && $height == null)
496  ) {
497  $this->unlock();
498 
499  throw new Exceptions\Exception('Invalid image size.');
500  }
501 
502  $bypass = (
503  $width == $this->size[0]
504  && $height == $this->size[1]
505  && $x == 0
506  && $y == 0
507  );
508 
509  $this->bypass = $this->bypass && $bypass;
510 
511  return $bypass;
512  }
518  protected function bypass()
519  {
520  if ($this->input != $this->output) {
521  copy($this->input, $this->output);
522  }
523  }
524 }
525 
526 /* vim:set ft=php sw=4 sts=4 fdm=marker et : */
autoload($class)
PHP autoloader.
Definition: Graphics.php:21
general graphics related exception class
Definition: Exception.php:17
Input file not found exception.
Main graphics class.
Definition: Graphics.php:41
$background
Image background string.
Definition: Graphics.php:69
$quality
Image quality string.
Definition: Graphics.php:73
processQueue()
Process action queue.
Definition: Graphics.php:277
bypassTest($width, $height, $x=0, $y=0)
Tests if action would change current image.
Definition: Graphics.php:490
$inputFormat
Input image format.
Definition: Graphics.php:85
getQuality()
Returns quality-index for current image format.
Definition: Graphics.php:441
addThumb($width, $height)
Adds thumb action.
Definition: Graphics.php:212
$queue
Action queue array.
Definition: Graphics.php:61
$input
Input filename.
Definition: Graphics.php:45
$otherRender
otherRender is set to true if another render process has already locked file
Definition: Graphics.php:57
$output
Output filename.
Definition: Graphics.php:49
render($input, $output=null)
Main method for image handling.
Definition: Graphics.php:295
addThumbfill($width, $height)
Adds thumb-fill action.
Definition: Graphics.php:227
static factory($options=array())
graphics object factory
Definition: Graphics.php:107
__construct($options=array())
graphics class constructor
Definition: Graphics.php:148
addCrop($width, $height, $x=0, $y=0)
Adds crop action.
Definition: Graphics.php:182
$outputFormat
Output image format.
Definition: Graphics.php:89
setQuality($quality)
Sets quality parameter.
Definition: Graphics.php:429
dimensions($width, $height)
Scales image dimensions.
Definition: Graphics.php:256
optimizeImage($filename)
Opimizes final image through one of the optimization programs.
Definition: Graphics.php:328
obtainFormat($fileName)
Determines image format from file extension.
Definition: Graphics.php:377
$optimizers
List of optimizer binaries.
Definition: Graphics.php:81
static which($binary)
Executes "which" command.
Definition: Graphics.php:416
$bypass
Process bypass bool.
Definition: Graphics.php:93
renderFinished()
Called after rendering has finished.
Definition: Graphics.php:316
addBackground($background)
Background "action".
Definition: Graphics.php:165
addResize($width, $height)
Adds resize action.
Definition: Graphics.php:197
bypass()
Runs bypass (copies file)
Definition: Graphics.php:518
escapeNumber($number)
Validates integers.
Definition: Graphics.php:242
$size
Image size array(width, height)
Definition: Graphics.php:65
$optimize
Optimize output images.
Definition: Graphics.php:77
PHP GD extension interface.
Definition: Gd.php:19
Imagick Class Imagick.
Definition: Imagick.php:19