depage-graphics
Loading...
Searching...
No Matches
Graphics.php
Go to the documentation of this file.
1<?php
2
14
15namespace Depage\Graphics;
16
22function autoload($class)
23{
24 if (strpos($class, __NAMESPACE__ . '\\') == 0) {
25 $class = str_replace('\\', '/', str_replace(__NAMESPACE__ . '\\', '', $class));
26 $file = __DIR__ . '/' . $class . '.php';
27
28 if (file_exists($file)) {
29 require_once($file);
30 }
31 }
32}
33
34spl_autoload_register(__NAMESPACE__ . '\autoload');
35
41abstract class Graphics
42{
46 protected $input;
50 protected $output;
54 protected $format;
58 protected $outputLockFp = null;
62 protected $otherRender = false;
66 protected $queue = [];
70 protected $size = [];
74 protected $background;
78 protected $quality = '';
82 protected $optimize;
86 protected $optimizers;
90 protected $limits = [];
94 protected $inputFormat;
98 protected $outputFormat;
102 protected $bypass = true;
106 private $oldIgnoreUserAbort = false;
116 public static function factory($options = [])
117 {
118 $extension = (isset($options['extension'])) ? $options['extension'] : 'gd';
119
120 if ($extension == 'imagick' && extension_loaded('imagick')) {
121 return new Providers\Imagick($options);
122 } elseif ($extension == 'im' || $extension == 'imagemagick') {
123 if (isset($options['executable'])) {
124 return new Providers\Imagemagick($options);
125 } else {
126 $executable = Graphics::which('convert');
127 if ($executable == null) {
128 trigger_error("Cannot find ImageMagick, falling back to GD", E_USER_WARNING);
129 } else {
130 $options['executable'] = $executable;
131
132 return new Providers\Imagemagick($options);
133 }
134 }
135 } elseif ($extension == 'gm' || $extension == 'graphicsmagick') {
136 if (isset($options['executable'])) {
137 return new Providers\Graphicsmagick($options);
138 } else {
139 $executable = Graphics::which('gm');
140 if ($executable == null) {
141 trigger_error("Cannot find GraphicsMagick, falling back to GD", E_USER_WARNING);
142 } else {
143 $options['executable'] = $executable;
144
145 return new Providers\Graphicsmagick($options);
146 }
147 }
148 }
149
150 return new Providers\Gd($options);
151 }
152
157 public function __construct($options = [])
158 {
159 $this->background = (isset($options['background'])) ? $options['background'] : 'transparent';
160 $this->quality = (isset($options['quality'])) ? intval($options['quality']) : null;
161 $this->format = (isset($options['format'])) ? $options['format'] : null;
162 $this->optimize = (isset($options['optimize'])) ? $options['optimize'] : false;
163 $this->optimizers = (isset($options['optimizers'])) ? $options['optimizers'] : [];
164 $this->limits = (isset($options['limits'])) ? $options['limits'] : [
165 'memory' => "64MiB",
166 'map' => "128MiB",
167 ];
168 if (isset($this->limits['memory'])) {
169 putenv("MAGICK_MEMORY_LIMIT=" . $this->limits['memory']);
170 putenv("MAGICK_LIMIT_MEMORY=" . $this->limits['memory']);
171 }
172 if (isset($this->limits['map'])) {
173 putenv("MAGICK_MAP_LIMIT=" . $this->limits['map']);
174 putenv("MAGICK_LIMIT_MAP=" . $this->limits['map']);
175 }
176 }
177
184 public function canRead($ext)
185 {
186 return in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp']);
187 }
188
194 public function canWrite($ext)
195 {
196 return in_array($ext, ['png']);
197 return in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp']);
198 }
199
208 public function addBackground($background): self
209 {
210 $this->background = $background;
211
212 return $this;
213 }
214
225 public function addCrop($width, $height, $x = 0, $y = 0): self
226 {
227 $this->queue[] = ['crop', func_get_args()];
228
229 return $this;
230 }
231
240 public function addResize($width, $height): self
241 {
242 $this->queue[] = ['resize', func_get_args()];
243
244 return $this;
245 }
246
255 public function addThumb($width, $height): self
256 {
257 $this->queue[] = ['thumb', func_get_args()];
258
259 return $this;
260 }
261
272 public function addThumbfill($width, $height, $centerX = 50, $centerY = 50): self
273 {
274 $this->queue[] = ['thumbfill', func_get_args()];
275
276 return $this;
277 }
278
287 protected function escapeNumber($number): ?int
288 {
289 return (is_numeric($number)) ? intval($number) : null;
290 }
291
301 protected function dimensions($width, $height): array
302 {
303 if (!is_numeric($width) && !is_numeric($height)) {
304 $width = null;
305 $height = null;
306 } elseif (!is_numeric($height)) {
307 $height = round(($this->size[1] / $this->size[0]) * $width);
308 } elseif (!is_numeric($width)) {
309 $width = round(($this->size[0] / $this->size[1]) * $height);
310 }
311
312 return [$width, $height];
313 }
314
320 abstract protected function getImageSize(): array;
321
329 protected function processQueue(): void
330 {
331 foreach ($this->queue as $task) {
332 $action = $task[0];
333 $arguments = array_map([$this, 'escapeNumber'], $task[1]);
334
335 call_user_func_array([$this, $action], $arguments);
336 }
337 }
338
347 public function render($input, $output = null): void
348 {
349 if (!file_exists($input)) {
350 throw new Exceptions\FileNotFound();
351 }
352
353 $this->input = $input;
354 $this->output = ($output == null) ? $input : $output;
355 $this->inputFormat = $this->obtainFormat($this->input);
356 $this->outputFormat = ($this->format == null) ? $this->obtainFormat($this->output) : $this->format;
357 $this->size = $this->getImageSize();
358 $this->otherRender = false;
359
360 $this->lock();
361
362 $this->oldIgnoreUserAbort = ignore_user_abort();
363 ignore_user_abort(true);
364 }
365
370 public function renderFinished()
371 {
372 ignore_user_abort($this->oldIgnoreUserAbort);
373
374 $this->unlock();
375 }
376
382 public function optimizeImage($filename)
383 {
384 if (!file_exists($filename)) {
385 return false;
386 }
387
388 $optimizer = new Optimizers\Optimizer($this->optimizers);
389 $success = $optimizer->optimize($filename);
390
391 return $success;
392 }
393
398 protected function lock()
399 {
400 // set lock
401 $this->outputLockFp = fopen($this->output . ".lock", 'w');
402 $locked = flock($this->outputLockFp, LOCK_EX | LOCK_NB, $wouldblock);
403
404 if (!$locked && $wouldblock) {
405 $this->otherRender = true;
406 flock($this->outputLockFp, LOCK_EX);
407 }
408 }
409
414 protected function unlock()
415 {
416 // release lock
417 if (isset($this->outputLockFp)) {
418 flock($this->outputLockFp, LOCK_UN);
419 unlink($this->output . ".lock");
420
421 $this->outputLockFp = null;
422 }
423 }
424
431 protected function obtainFormat($fileName)
432 {
433 $parts = explode('.', $fileName);
434 $extension = strtolower(end($parts));
435
436 if ($extension == 'jpeg') {
437 $extension = 'jpg';
438 } elseif (
439 $extension != 'jpg'
440 && $extension != 'png'
441 && $extension != 'gif'
442 ) {
443 if (is_callable('getimagesize') && file_exists($fileName)) {
444 $info = getimagesize($fileName);
445 if (isset($info[2])) {
446 $format = $info[2];
447
448 if ($format == 1) {
449 $extension = 'gif';
450 } elseif ($format == 2) {
451 $extension = 'jpg';
452 } elseif ($format == 3) {
453 $extension = 'png';
454 }
455 }
456 }
457 }
458
459 return $extension;
460 }
461
470 public static function which($binary)
471 {
472 exec('which ' . $binary, $commandOutput, $returnStatus);
473 if ($returnStatus === 0) {
474 return $commandOutput[0];
475 } else {
476 return null;
477 }
478 }
479
483 public function setQuality($quality)
484 {
485 $this->quality = $quality;
486 }
487
495 protected function getQuality()
496 {
497 if ($this->outputFormat == 'jpg') {
498 if (
499 is_numeric($this->quality)
500 && $this->quality >= 0
501 && $this->quality <= 100
502 ) {
504 } else {
505 $quality = 85;
506 }
507 } elseif ($this->outputFormat == "webp") {
508 if (
509 is_numeric($this->quality)
510 && $this->quality >= 0
511 && $this->quality <= 100
512 ) {
514 } else {
515 $quality = 75;
516 }
517 } elseif ($this->outputFormat == 'png') {
518 if (
519 is_numeric($this->quality)
520 && $this->quality >= 0
521 && $this->quality <= 95
522 && $this->quality % 10 <= 5
523 ) {
524 $quality = sprintf("%02d", $this->quality);
525 } else {
526 $quality = 95;
527 }
528 } else {
529 $quality = intval($this->quality);
530 }
531
532 return (string) $quality;
533 }
534
540 public function getPageNumber()
541 {
542 if ($this->inputFormat == "pdf") {
543 return "[0]";
544 } else {
545 return "";
546 }
547 }
548
558 protected function bypassTest($width, $height, $x = 0, $y = 0)
559 {
560 if (
561 ($width !== null && $width < 1)
562 || ($height !== null && $height < 1)
563 || ($width == null && $height == null)
564 ) {
565 $this->unlock();
566
567 throw new Exceptions\Exception('Invalid image size.');
568 }
569
570 $bypass = (
571 $width == $this->size[0]
572 && $height == $this->size[1]
573 && $x == 0
574 && $y == 0
575 );
576
577 $this->bypass = $this->bypass && $bypass;
578
579 return $bypass;
580 }
581
586 protected function bypass()
587 {
588 if ($this->input != $this->output) {
589 copy($this->input, $this->output);
590 }
591 }
592}
593
594/* vim:set ft=php sw=4 sts=4 fdm=marker et : */
general graphics related exception class
Definition Exception.php:17
Input file not found exception.
Main graphics class.
Definition Graphics.php:42
$background
Image background string.
Definition Graphics.php:74
$quality
Image quality string.
Definition Graphics.php:78
canWrite($ext)
Checks if extension supports writing file type.
Definition Graphics.php:194
addThumbfill($width, $height, $centerX=50, $centerY=50)
Adds thumb-fill action.
Definition Graphics.php:272
$format
Output format.
Definition Graphics.php:54
processQueue()
Process action queue.
Definition Graphics.php:329
bypassTest($width, $height, $x=0, $y=0)
Tests if action would change current image.
Definition Graphics.php:558
$inputFormat
Input image format.
Definition Graphics.php:94
getQuality()
Returns quality-index for current image format.
Definition Graphics.php:495
addThumb($width, $height)
Adds thumb action.
Definition Graphics.php:255
$queue
Action queue array.
Definition Graphics.php:66
$input
Input filename.
Definition Graphics.php:46
$otherRender
otherRender is set to true if another render process has already locked file
Definition Graphics.php:62
$output
Output filename.
Definition Graphics.php:50
getPageNumber()
getPageNumber
Definition Graphics.php:540
canRead($ext)
Checks if extension support reading file type.
Definition Graphics.php:184
getImageSize()
Determine size of input image.
render($input, $output=null)
Main method for image handling.
Definition Graphics.php:347
$limits
limits (mainly for imagemagick and graphicsmagick)
Definition Graphics.php:90
static factory($options=[])
graphics object factory
Definition Graphics.php:116
addCrop($width, $height, $x=0, $y=0)
Adds crop action.
Definition Graphics.php:225
$outputFormat
Output image format.
Definition Graphics.php:98
setQuality($quality)
Sets quality parameter.
Definition Graphics.php:483
dimensions($width, $height)
Scales image dimensions.
Definition Graphics.php:301
optimizeImage($filename)
Opimizes final image through one of the optimization programs.
Definition Graphics.php:382
__construct($options=[])
graphics class constructor
Definition Graphics.php:157
obtainFormat($fileName)
Determines image format from file extension.
Definition Graphics.php:431
$optimizers
List of optimizer binaries.
Definition Graphics.php:86
static which($binary)
Executes "which" command.
Definition Graphics.php:470
$bypass
Process bypass bool.
Definition Graphics.php:102
renderFinished()
Called after rendering has finished.
Definition Graphics.php:370
addBackground($background)
Background "action".
Definition Graphics.php:208
addResize($width, $height)
Adds resize action.
Definition Graphics.php:240
bypass()
Runs bypass (copies file)
Definition Graphics.php:586
escapeNumber($number)
Validates integers.
Definition Graphics.php:287
$size
Image size array(width, height)
Definition Graphics.php:70
$optimize
Optimize output images.
Definition Graphics.php:82
PHP GD extension interface.
Definition Gd.php:20
Imagick Class Imagick.
Definition Imagick.php:20
autoload($class)
PHP autoloader.
Definition Graphics.php:22