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 == 'im' || $extension == 'imagemagick') {
112  if (isset($options['executable'])) {
113  return new Providers\Imagemagick($options);
114  } else {
115  $executable = Graphics::which('convert');
116  if ($executable == null) {
117  trigger_error("Cannot find ImageMagick, falling back to GD", E_USER_WARNING);
118  } else {
119  $options['executable'] = $executable;
120 
121  return new Providers\Imagemagick($options);
122  }
123  }
124  } elseif ($extension == 'gm' || $extension == 'graphicsmagick') {
125  if (isset($options['executable'])) {
126  return new Providers\Graphicsmagick($options);
127  } else {
128  $executable = Graphics::which('gm');
129  if ($executable == null) {
130  trigger_error("Cannot find GraphicsMagick, falling back to GD", E_USER_WARNING);
131  } else {
132  $options['executable'] = $executable;
133 
134  return new Providers\Graphicsmagick($options);
135  }
136  }
137  }
138 
139  return new Providers\Gd($options);
140  }
146  public function __construct($options = array())
147  {
148  $this->background = (isset($options['background'])) ? $options['background'] : 'transparent';
149  $this->quality = (isset($options['quality'])) ? intval($options['quality']) : null;
150  $this->format = (isset($options['format'])) ? $options['format'] : null;
151  $this->optimize = (isset($options['optimize'])) ? $options['optimize'] : false;
152  $this->optimizers = (isset($options['optimizers'])) ? $options['optimizers'] : array();
153  }
154 
163  public function addBackground($background)
164  {
165  $this->background = $background;
166 
167  return $this;
168  }
180  public function addCrop($width, $height, $x = 0, $y = 0)
181  {
182  $this->queue[] = array('crop', func_get_args());
183 
184  return $this;
185  }
195  public function addResize($width, $height)
196  {
197  $this->queue[] = array('resize', func_get_args());
198 
199  return $this;
200  }
210  public function addThumb($width, $height)
211  {
212  $this->queue[] = array('thumb', func_get_args());
213 
214  return $this;
215  }
225  public function addThumbfill($width, $height)
226  {
227  $this->queue[] = array('thumbfill', func_get_args());
228 
229  return $this;
230  }
231 
240  protected function escapeNumber($number)
241  {
242  return (is_numeric($number)) ? intval($number) : null;
243  }
254  protected function dimensions($width, $height)
255  {
256  if (!is_numeric($width) && !is_numeric($height)) {
257  $width = null;
258  $height = null;
259  } elseif (!is_numeric($height)) {
260  $height = round(($this->size[1] / $this->size[0]) * $width);
261  } elseif (!is_numeric($width)) {
262  $width = round(($this->size[0] / $this->size[1]) * $height);
263  }
264 
265  return array($width, $height);
266  }
267 
275  protected function processQueue()
276  {
277  foreach ($this->queue as $task) {
278  $action = $task[0];
279  $arguments = array_map(array($this, 'escapeNumber'), $task[1]);
280 
281  call_user_func_array(array($this, $action), $arguments);
282  }
283  }
293  public function render($input, $output = null)
294  {
295  if (!file_exists($input)) throw new Exceptions\FileNotFound();
296 
297  $this->input = $input;
298  $this->output = ($output == null) ? $input : $output;
299  $this->inputFormat = $this->obtainFormat($this->input);
300  $this->outputFormat = ($this->format == null) ? $this->obtainFormat($this->output) : $this->format;
301  $this->size = $this->getImageSize();
302  $this->otherRender = false;
303 
304  $this->lock();
305 
306  $this->oldIgnoreUserAbort = ignore_user_abort();
307  ignore_user_abort(true);
308  }
314  public function renderFinished()
315  {
316  ignore_user_abort($this->oldIgnoreUserAbort);
317 
318  $this->unlock();
319  }
326  public function optimizeImage($filename)
327  {
328  if (!file_exists($filename)) {
329  return false;
330  }
331 
332  $optimizer = new Optimizers\Optimizer($this->optimizers);
333  $success = $optimizer->optimize($filename);
334 
335  return $success;
336  }
342  protected function lock()
343  {
344  // set lock
345  $this->outputLockFp = fopen($this->output . ".lock", 'w');
346  $locked = flock($this->outputLockFp, LOCK_EX | LOCK_NB, $wouldblock);
347 
348  if (!$locked && $wouldblock) {
349  $this->otherRender = true;
350  flock($this->outputLockFp, LOCK_EX);
351  }
352  }
358  protected function unlock()
359  {
360  // release lock
361  if (isset($this->outputLockFp)) {
362  flock($this->outputLockFp, LOCK_UN);
363  unlink($this->output . ".lock");
364 
365  $this->outputLockFp = null;
366  }
367  }
368 
375  protected function obtainFormat($fileName)
376  {
377  $parts = explode('.', $fileName);
378  $extension = strtolower(end($parts));
379 
380  if ($extension == 'jpeg') {
381  $extension = 'jpg';
382  } elseif (
383  $extension != 'jpg'
384  && $extension != 'png'
385  && $extension != 'gif'
386  ) {
387  if (is_callable('getimagesize') && file_exists($fileName)) {
388  $info = getimagesize($fileName);
389  if (isset($info[2])) {
390  $format = $info[2];
391 
392  if ($format == 1) {
393  $extension = 'gif';
394  } elseif ($format == 2) {
395  $extension = 'jpg';
396  } elseif ($format == 3) {
397  $extension = 'png';
398  }
399  }
400  }
401  }
402 
403  return $extension;
404  }
405 
414  public static function which($binary)
415  {
416  exec('which ' . $binary, $commandOutput, $returnStatus);
417  if ($returnStatus === 0) {
418  return $commandOutput[0];
419  } else {
420  return null;
421  }
422  }
423 
427  public function setQuality($quality)
428  {
429  $this->quality = $quality;
430  }
439  protected function getQuality()
440  {
441  if ($this->outputFormat == 'jpg') {
442  if (
443  is_numeric($this->quality)
444  && $this->quality >= 0
445  && $this->quality <= 100
446  ) {
448  } else {
449  $quality = 85;
450  }
451  } elseif ($this->outputFormat == "webp") {
452  if (
453  is_numeric($this->quality)
454  && $this->quality >= 0
455  && $this->quality <= 100
456  ) {
458  } else {
459  $quality = 75;
460  }
461  } elseif ($this->outputFormat == 'png') {
462  if (
463  is_numeric($this->quality)
464  && $this->quality >= 0
465  && $this->quality <= 95
466  && $this->quality % 10 <= 5
467  ) {
468  $quality = sprintf("%02d", $this->quality);
469  } else {
470  $quality = 95;
471  }
472  } else {
473  $quality = intval($this->quality);
474  }
475 
476  return (string) $quality;
477  }
478 
488  protected function bypassTest($width, $height, $x = 0, $y = 0)
489  {
490  if (
491  ($width !== null && $width < 1)
492  || ($height !== null && $height < 1)
493  || ($width == null && $height == null)
494  ) {
495  $this->unlock();
496 
497  throw new Exceptions\Exception('Invalid image size.');
498  }
499 
500  $bypass = (
501  $width == $this->size[0]
502  && $height == $this->size[1]
503  && $x == 0
504  && $y == 0
505  );
506 
507  $this->bypass = $this->bypass && $bypass;
508 
509  return $bypass;
510  }
516  protected function bypass()
517  {
518  if ($this->input != $this->output) {
519  copy($this->input, $this->output);
520  }
521  }
522 }
523 
524 /* 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:275
bypassTest($width, $height, $x=0, $y=0)
Tests if action would change current image.
Definition: Graphics.php:488
$inputFormat
Input image format.
Definition: Graphics.php:85
getQuality()
Returns quality-index for current image format.
Definition: Graphics.php:439
addThumb($width, $height)
Adds thumb action.
Definition: Graphics.php:210
$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:293
addThumbfill($width, $height)
Adds thumb-fill action.
Definition: Graphics.php:225
static factory($options=array())
graphics object factory
Definition: Graphics.php:107
__construct($options=array())
graphics class constructor
Definition: Graphics.php:146
addCrop($width, $height, $x=0, $y=0)
Adds crop action.
Definition: Graphics.php:180
$outputFormat
Output image format.
Definition: Graphics.php:89
setQuality($quality)
Sets quality parameter.
Definition: Graphics.php:427
dimensions($width, $height)
Scales image dimensions.
Definition: Graphics.php:254
optimizeImage($filename)
Opimizes final image through one of the optimization programs.
Definition: Graphics.php:326
obtainFormat($fileName)
Determines image format from file extension.
Definition: Graphics.php:375
$optimizers
List of optimizer binaries.
Definition: Graphics.php:81
static which($binary)
Executes "which" command.
Definition: Graphics.php:414
$bypass
Process bypass bool.
Definition: Graphics.php:93
renderFinished()
Called after rendering has finished.
Definition: Graphics.php:314
addBackground($background)
Background "action".
Definition: Graphics.php:163
addResize($width, $height)
Adds resize action.
Definition: Graphics.php:195
bypass()
Runs bypass (copies file)
Definition: Graphics.php:516
escapeNumber($number)
Validates integers.
Definition: Graphics.php:240
$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