PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.22
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.22
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / rosell-dk / webp-convert / src / Convert / Converters / ImageMagick.php

ImageMagick.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.22, at vendor/rosell-dk/webp-convert/src/Convert/Converters/ImageMagick.php

276 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WebPConvert\Convert\Converters;
4
5 use ExecWithFallback\ExecWithFallback;
6 use LocateBinaries\LocateBinaries;
7
8 use WebPConvert\Convert\Converters\AbstractConverter;
9 use WebPConvert\Convert\Converters\ConverterTraits\ExecTrait;
10 use WebPConvert\Convert\Converters\ConverterTraits\EncodingAutoTrait;
11 use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
12 use WebPConvert\Convert\Exceptions\ConversionFailedException;
13 use WebPConvert\Options\OptionFactory;
14
15 //use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\TargetNotFoundException;
16
17 /**
18 * Convert images to webp by calling imagemagick binary.
19 *
20 * @package WebPConvert
21 * @author Bjørn Rosell <[email protected]>
22 * @since Class available since Release 2.0.0
23 */
24 class ImageMagick extends AbstractConverter
25 {
26 use ExecTrait;
27 use EncodingAutoTrait;
28
29 protected function getUnsupportedDefaultOptions()
30 {
31 return [
32 'size-in-percentage',
33 ];
34 }
35
36 /**
37 * Get the options unique for this converter
38 *
39 * @return array Array of options
40 */
41 public function getUniqueOptions($imageType)
42 {
43 return OptionFactory::createOptions([
44 self::niceOption(),
45 ['try-common-system-paths', 'boolean', [
46 'title' => 'Try locating ImageMagick in common system paths',
47 'description' =>
48 'If set, the converter will look for a ImageMagick binaries residing in common system locations ' .
49 'such as "/usr/bin/convert". ' .
50 'If such exist, it is assumed that they are valid ImageMagick binaries. ',
51 'default' => true,
52 'ui' => [
53 'component' => 'checkbox',
54 'advanced' => true
55 ]
56 ]],
57 ]);
58 }
59
60 // To futher improve this converter, I could check out:
61 // https://github.com/Orbitale/ImageMagickPHP
62
63 private function getPath()
64 {
65 if (defined('WEBPCONVERT_IMAGEMAGICK_PATH')) {
66 return constant('WEBPCONVERT_IMAGEMAGICK_PATH');
67 }
68 if (!empty(getenv('WEBPCONVERT_IMAGEMAGICK_PATH'))) {
69 return getenv('WEBPCONVERT_IMAGEMAGICK_PATH');
70 }
71
72 if ($this->options['try-common-system-paths']) {
73 $binaries = LocateBinaries::locateInCommonSystemPaths('convert');
74 if (!empty($binaries)) {
75 return $binaries[0];
76 }
77 }
78
79 return 'convert';
80 }
81
82 private function getVersion()
83 {
84 ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
85 if (($returnCode == 0) && isset($output[0])) {
86 return $output[0];
87 } else {
88 return 'unknown';
89 }
90 }
91
92 public function isInstalled()
93 {
94 ExecWithFallback::exec($this->getPath() . ' -version 2>&1', $output, $returnCode);
95 return ($returnCode == 0);
96 }
97
98 // Check if webp delegate is installed
99 public function isWebPDelegateInstalled()
100 {
101 ExecWithFallback::exec($this->getPath() . ' -list delegate 2>&1', $output, $returnCode);
102 foreach ($output as $line) {
103 if (preg_match('#webp\\s*=#i', $line)) {
104 return true;
105 }
106 }
107
108 // try other command
109 ExecWithFallback::exec($this->getPath() . ' -list configure 2>&1', $output, $returnCode);
110 foreach ($output as $line) {
111 if (preg_match('#DELEGATE.*webp#i', $line)) {
112 return true;
113 }
114 }
115
116 return false;
117
118 // PS, convert -version does not output delegates on travis, so it is not reliable
119 }
120
121 /**
122 * Check (general) operationality of imagack converter executable
123 *
124 * @throws SystemRequirementsNotMetException if system requirements are not met
125 */
126 public function checkOperationality()
127 {
128 $this->checkOperationalityExecTrait();
129
130 if (!$this->isInstalled()) {
131 throw new SystemRequirementsNotMetException(
132 'imagemagick is not installed (cannot execute: "' . $this->getPath() . '")'
133 );
134 }
135 if (!$this->isWebPDelegateInstalled()) {
136 throw new SystemRequirementsNotMetException('webp delegate missing');
137 }
138 }
139
140 /**
141 * Build command line options
142 *
143 * @param string $versionNumber. Ie "6.9.10-23"
144 * @return string
145 */
146 private function createCommandLineOptions($versionNumber = 'unknown')
147 {
148 // Available webp options for imagemagick are documented here:
149 // - https://imagemagick.org/script/webp.php
150 // - https://github.com/ImageMagick/ImageMagick/blob/main/coders/webp.c
151
152 // We should perhaps implement low-memory. Its already in cwebp, it
153 // could perhaps be promoted to a general option
154
155 $commandArguments = [];
156 if ($this->isQualityDetectionRequiredButFailing()) {
157 // quality:auto was specified, but could not be determined.
158 // we cannot apply the max-quality logic, but we can provide auto quality
159 // simply by not specifying the quality option.
160 } else {
161 $commandArguments[] = '-quality ' . escapeshellarg($this->getCalculatedQuality());
162 }
163
164 $options = $this->options;
165
166 if (!is_null($options['preset'])) {
167 // "image-hint" is at least available from 6.9.4-0 (I can't see further back)
168 if ($options['preset'] != 'none') {
169 $imageHint = $options['preset'];
170 switch ($imageHint) {
171 case 'drawing':
172 case 'icon':
173 case 'text':
174 $imageHint = 'graph';
175 $this->logLn(
176 'The "preset" value was mapped to "graph" because imagemagick does not support "drawing",' .
177 ' "icon" and "text", but grouped these into one option: "graph".'
178 );
179 }
180 $commandArguments[] = '-define webp:image-hint=' . escapeshellarg($imageHint);
181 }
182 }
183
184 if ($options['encoding'] == 'lossless') {
185 // lossless is at least available from 6.9.4-0 (I can't see further back)
186 $commandArguments[] = '-define webp:lossless=true';
187 }
188
189 if ($options['low-memory']) {
190 // low-memory is at least available from 6.9.4-0 (I can't see further back)
191 $commandArguments[] = '-define webp:low-memory=true';
192 }
193
194 if ($options['auto-filter'] === true) {
195 // auto-filter is at least available from 6.9.4-0 (I can't see further back)
196 $commandArguments[] = '-define webp:auto-filter=true';
197 }
198
199 if ($options['metadata'] == 'none') {
200 $commandArguments[] = '-strip';
201 }
202
203 if ($options['alpha-quality'] !== 100) {
204 // alpha-quality is at least available from 6.9.4-0 (I can't see further back)
205 $commandArguments[] = '-define webp:alpha-quality=' . strval($options['alpha-quality']);
206 }
207
208 if ($options['sharp-yuv'] === true) {
209 if (version_compare($versionNumber, '7.0.8-26', '>=')) {
210 $commandArguments[] = '-define webp:use-sharp-yuv=true';
211 } else {
212 $this->logLn(
213 'Note: "sharp-yuv" option is not supported in your version of ImageMagick. ' .
214 'ImageMagic >= 7.0.8-26 is required',
215 'italic'
216 );
217 }
218 }
219
220 if ($options['near-lossless'] != 100) {
221 if (version_compare($versionNumber, '7.0.10-54', '>=')) { // #299
222 $commandArguments[] = '-define webp:near-lossless=' . escapeshellarg($options['near-lossless']);
223 } else {
224 $this->logLn(
225 'Note: "near-lossless" option is not supported in your version of ImageMagick. ' .
226 'ImageMagic >= 7.0.10-54 is required',
227 'italic'
228 );
229 }
230 }
231
232 // "method" is at least available from 6.9.4-0 (I can't see further back)
233 $commandArguments[] = '-define webp:method=' . $options['method'];
234
235 $commandArguments[] = escapeshellarg($this->source);
236 $commandArguments[] = escapeshellarg('webp:' . $this->destination);
237
238 return implode(' ', $commandArguments);
239 }
240
241 protected function doActualConvert()
242 {
243 $version = $this->getVersion();
244
245 $this->logLn($version);
246
247 preg_match('#\d+\.\d+\.\d+[\d\.\-]+#', $version, $matches);
248 $versionNumber = (isset($matches[0]) ? $matches[0] : 'unknown');
249
250 $this->logLn('Extracted version number: ' . $versionNumber);
251
252 $command = $this->getPath() . ' ' . $this->createCommandLineOptions($versionNumber) . ' 2>&1';
253
254 $useNice = ($this->options['use-nice'] && $this->checkNiceSupport());
255 if ($useNice) {
256 $command = 'nice ' . $command;
257 }
258 $this->logLn('Executing command: ' . $command);
259 ExecWithFallback::exec($command, $output, $returnCode);
260
261 $this->logExecOutput($output);
262 if ($returnCode == 0) {
263 $this->logLn('success');
264 } else {
265 $this->logLn('return code: ' . $returnCode);
266 }
267
268 if ($returnCode == 127) {
269 throw new SystemRequirementsNotMetException('imagemagick is not installed');
270 }
271 if ($returnCode != 0) {
272 throw new SystemRequirementsNotMetException('The exec call failed');
273 }
274 }
275 }
276