PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.3
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.3
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.3, at vendor/rosell-dk/webp-convert/src/Convert/Converters/ImageMagick.php

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