depage-graphics
Loading...
Searching...
No Matches
Imagemagick.php
Go to the documentation of this file.
1<?php
2
10
11namespace Depage\Graphics\Providers;
12
23{
27 protected $command = '';
31 protected $executable;
35 protected $timeout = 0;
41 public function __construct($options = array())
42 {
43 parent::__construct($options);
44
45 $this->executable = isset($options['executable']) ? $options['executable'] : null;
46 $this->timeout = isset($options['timeout']) ? $options['timeout'] : 0;
47 }
48
55 public function canRead($ext)
56 {
57 return parent::canRead($ext) || in_array($ext, ['tif', 'tiff', 'pdf', 'eps']);
58 }
59
71 protected function crop($width, $height, $x = 0, $y = 0)
72 {
73 if (!$this->bypassTest($width, $height, $x, $y)) {
74 // '+' for positive offset (the '-' is already there)
75 $x = ($x < 0) ? $x : '+' . $x;
76 $y = ($y < 0) ? $y : '+' . $y;
77
78 $this->command .= " -gravity NorthWest -crop {$width}x{$height}{$x}{$y}! -flatten";
79 $this->size = array($width, $height);
80 }
81 }
82
91 protected function resize($width, $height)
92 {
93 $newSize = $this->dimensions($width, $height);
94
95 if (!$this->bypassTest($newSize[0], $newSize[1])) {
96 $resizeAction = $this->getResizeAction($newSize[0], $newSize[1]);
97
98 $this->command .= " $resizeAction {$newSize[0]}x{$newSize[1]}!";
99
100 $this->size = $newSize;
101 }
102 }
103
112 protected function thumb($width, $height)
113 {
114 if (!$this->bypassTest($width, $height)) {
115 $resizeAction = $this->getResizeAction($width, $height);
116
117 $this->command .= " -gravity Center $resizeAction {$width}x{$height} -extent {$width}x{$height}";
118 $this->size = array($width, $height);
119 }
120 }
121
132 protected function thumbfill($width, $height, $centerX = 50, $centerY = 50)
133 {
134 if (!$this->bypassTest($width, $height)) {
135 $newSize = $this->dimensions($width, null);
136 $centerX = $centerX / -100 + 0.5;
137 $centerY = $centerY / -100 + 0.5;
138
139 if ($newSize[1] < $height) {
140 $newSize = $this->dimensions(null, $height);
141 $x = round(($width - $newSize[0]) * $centerX);
142 $y = 0;
143 } else {
144 $x = 0;
145 $y = round(($height - $newSize[1]) * $centerY);
146 }
147 $x = ($x < 0) ? $x : '+' . $x;
148 $y = ($y < 0) ? $y : '+' . $y;
149
150 $resizeAction = $this->getResizeAction($width, $height);
151
152 $this->command .= " -gravity Center $resizeAction {$width}x{$height}^ -extent {$width}x{$height}{$x}{$y}";
153 $this->size = array($width, $height);
154 }
155 }
156
162 protected function getImageSize()
163 {
164 $imageSize = false;
165 if (is_callable('getimagesize')) {
166 $imageSize = getimagesize($this->input);
167 }
168 if (!$imageSize) {
169 $pageNumber = $this->getPageNumber();
170 $identify = preg_replace('/convert$/', 'identify', $this->executable);
171 $command = "{$identify} -ping -format \"%wx%h\" " . escapeshellarg($this->input) . $pageNumber;
172 $escapedCommand = str_replace('!', '\!', escapeshellcmd($command));
173
174 exec($escapedCommand . ' 2>&1', $commandOutput, $returnStatus);
175 if ($returnStatus === 0) {
176 $imageSize = explode('x', $commandOutput[0]);
177 } else {
178 $this->unlock();
179
180 throw new \Depage\Graphics\Exceptions\Exception(implode("\n", $commandOutput));
181 }
182 }
183
184 return $imageSize;
185 }
186
196 public function render($input, $output = null)
197 {
198 parent::render($input, $output);
199
200 $this->command = '';
201 $this->processQueue();
202
203 if ($this->otherRender && file_exists($this->output)) {
204 // do nothing file is already generated
205 } elseif (
206 $this->bypass
207 && $this->inputFormat == $this->outputFormat
208 ) {
209 $this->bypass();
210 } else {
211 $background = $this->getBackground();
212 $quality = $this->getQuality();
213 $optimize = $this->getOptimize();
214 $pageNumber = $this->getPageNumber();
215
216 $this->command = "{$this->executable} {$background} ( -auto-orient '+profile' '*' -auto-orient " . escapeshellarg($this->input) . "{$pageNumber}{$this->command}";
217 $this->command .= " ) -colorspace sRGB -flatten {$quality}{$optimize}";
218
219 $this->command .= " {$this->outputFormat}:" . escapeshellarg($this->output);
220
221 $this->execCommand();
222
223 if ($this->optimize) {
224 $this->optimizeImage($this->output);
225 }
226 }
227
228 parent::renderFinished();
229 }
230
238 protected function execCommand()
239 {
240 $command = str_replace('!', '\!', escapeshellcmd($this->command));
241
242 $descriptorspec = array(
243 0 => array("pipe", "r"), // stdin is a pipe that the child will read from
244 1 => array("pipe", "w"), // stdout is a pipe that the child will write to
245 2 => array("pipe", "w") // stderr is pipe that the child will write errors to
246 );
247 $process = proc_open("exec " . $command, $descriptorspec, $pipes);
248
249 // set pipes non blocking
250 stream_set_blocking($pipes[1], false);
251 stream_set_blocking($pipes[2], false);
252
253 $startTime = time();
254 $output = array(1 => "", 2 => "");
255 $terminated = false;
256
257 if (is_resource($process)) {
258 // read stdin and stderr
259 while (!feof($pipes[1]) && !feof($pipes[2])) {
260 for ($i = 1; $i < 3; $i++) {
261 $s = fgets($pipes[$i]);
262 $output[$i] .= $s;
263 }
264
265 usleep(10000);
266 if ($this->timeout > 0 && time() - $startTime > $this->timeout) {
267 // terminate process of takes longer than timeout
268 proc_terminate($process);
269 $terminated = true;
270 }
271 }
272
273 for ($i = 0; $i < 3; $i++) {
274 fclose($pipes[$i]);
275 }
276 $returnStatus = proc_close($process);
277 }
278
279 if ($terminated) {
280 $this->unlock();
281
282 throw new \Depage\Graphics\Exceptions\Exception("Conversion over timeout");
283 } elseif ($returnStatus != 0) {
284 $this->unlock();
285
286 throw new \Depage\Graphics\Exceptions\Exception($output[2]);
287 }
288 }
289
295 protected function getBackground()
296 {
297 $background = "-size {$this->size[0]}x{$this->size[1]}";
298
299 if ($this->background[0] === '#') {
300 $background .= " -background {$this->background}";
301 } elseif ($this->background == 'checkerboard') {
302 $background .= " -background none pattern:checkerboard";
303 } else {
304 if ($this->outputFormat == 'jpg') {
305 $background .= " -background #FFF";
306 } else {
307 $background .= " -background none";
308 }
309 }
310
311 return $background;
312 }
313
318 protected function getQuality()
319 {
320 if (
321 $this->outputFormat == 'jpg'
322 || $this->outputFormat == 'png'
323 || $this->outputFormat == 'webp'
324 ) {
325 return '-quality ' . parent::getQuality();
326 } else {
327 return '';
328 }
329 }
330
335 protected function getOptimize()
336 {
337 $param = " -strip";
338
339 if ($this->outputFormat == 'jpg') {
340 $param .= " -interlace Plane";
341 } elseif ($this->outputFormat == 'png') {
342 $param .= " -define png:format=png00";
343 } elseif ($this->outputFormat == "webp" && $this->inputFormat == "png") {
344 $param .= " -define webp:lossless=true -define webp:image-hint=graph";
345 }
346
347 return $param;
348 }
349
357 protected function getResizeAction($width, $height)
358 {
359 if ($width <= 160 && $height <= 160) {
360 return "-thumbnail";
361 } else {
362 return "-resize";
363 }
364 }
365}
366
367/* vim:set ft=php sw=4 sts=4 fdm=marker et : */
Main graphics class.
Definition Graphics.php:42
$background
Image background string.
Definition Graphics.php:74
$quality
Image quality string.
Definition Graphics.php:78
processQueue()
Process action queue.
Definition Graphics.php:322
bypassTest($width, $height, $x=0, $y=0)
Tests if action would change current image.
Definition Graphics.php:551
$input
Input filename.
Definition Graphics.php:46
$output
Output filename.
Definition Graphics.php:50
getPageNumber()
getPageNumber
Definition Graphics.php:533
dimensions($width, $height)
Scales image dimensions.
Definition Graphics.php:301
optimizeImage($filename)
Opimizes final image through one of the optimization programs.
Definition Graphics.php:375
bypass()
Runs bypass (copies file)
Definition Graphics.php:579
$optimize
Optimize output images.
Definition Graphics.php:82
getBackground()
Generates background command.
thumbfill($width, $height, $centerX=50, $centerY=50)
Thumb action.
getQuality()
Generates quality command.
$executable
Imagemagick executable path.
canRead($ext)
Checks if extension support reading file type.
$timeout
timeout after which the image conversion will be canceled
getImageSize()
Determine size of input image.
render($input, $output=null)
Main method for image handling.
$command
Imagemagick command string.
getResizeAction($width, $height)
Gets the resize action depending on target size.
resize($width, $height)
Resize action.
__construct($options=array())
graphics_graphicsmagick class constructor
crop($width, $height, $x=0, $y=0)
Crop action.
thumb($width, $height)
Thumb action.
getOptimize()
Generates optimization parameters.
execCommand()
Executes ImageMagick command.