PluginProbe ʕ •ᴥ•ʔ
EWWW Image Optimizer / 8.5.0
EWWW Image Optimizer v8.5.0
8.7.0 8.6.0 trunk 5.6.2 5.7.1 5.8.2 6.0.3 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.1.9 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3.0 6.4.0 6.4.1 6.4.2 6.5.0 6.5.1 6.5.2 6.6.0 6.7.0 6.8.0 6.9.0 6.9.1 6.9.2 6.9.3 7.0.0 7.0.1 7.0.2 7.1.0 7.2.0 7.2.1 7.2.2 7.2.3 7.3.0 7.4.0 7.5.0 7.6.0 7.7.0 7.8.0 7.9.0 7.9.1 8.0.0 8.1.0 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.1 8.3.0 8.3.1 8.4.0 8.4.1 8.5.0
ewww-image-optimizer / unique.php
ewww-image-optimizer Last commit date
bin 9 years ago binaries 2 years ago classes 2 months ago docs 7 years ago images 3 months ago includes 2 months ago tests 2 months ago vendor 10 months ago .travis.yml 5 months ago aux-optimize.php 2 months ago bulk.php 2 months ago changelog.txt 2 months ago common.php 2 months ago composer.json 10 months ago composer.lock 3 years ago ewww-image-optimizer.php 2 months ago functions.php 6 months ago license.txt 7 years ago mwebp.php 2 months ago phpcs.ruleset.xml 6 months ago phpunit.xml 6 years ago readme.txt 2 months ago uninstall.php 7 years ago unique.php 3 months ago
unique.php
1908 lines
1 <?php
2 /**
3 * Unique functions for Standard EWWW IO plugins.
4 *
5 * This file contains functions that are unique to the regular EWWW IO plugin.
6 *
7 * @link https://ewww.io
8 * @package EWWW_Image_Optimizer
9 */
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 // Installation routine for PNGOUT.
16 add_action( 'admin_action_ewww_image_optimizer_install_pngout', 'ewww_image_optimizer_install_pngout_wrapper' );
17 // Installation routine for SVGCLEANER.
18 add_action( 'admin_action_ewww_image_optimizer_install_svgcleaner', 'ewww_image_optimizer_install_svgcleaner_wrapper' );
19 // Removes the binaries when the plugin is deactivated.
20 register_deactivation_hook( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE, 'ewww_image_optimizer_remove_binaries' );
21
22 /**
23 * Resizes an image with gifsicle to preserve animations.
24 *
25 * @param string $file The file to resize.
26 * @param int $dst_x X-coordinate of destination image (usually 0).
27 * @param int $dst_y Y-coordinate of destination image (usually 0).
28 * @param int $src_x X-coordinate of source image (usually 0 unless cropping).
29 * @param int $src_y Y-coordinate of source image (usually 0 unless cropping).
30 * @param int $dst_w Desired image width.
31 * @param int $dst_h Desired image height.
32 * @param int $src_w Source width.
33 * @param int $src_h Source height.
34 * @return string|WP_Error The image contents or the error message.
35 */
36 function ewww_image_optimizer_gifsicle_resize( $file, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) {
37 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
38 $tools = array(
39 'gifsicle' => ewwwio()->local->get_path( 'gifsicle' ),
40 );
41 if ( empty( $tools['gifsicle'] ) ) {
42 ewwwio_debug_message( 'no gifsicle found for resizing' );
43 return new WP_Error(
44 'image_resize_error',
45 /* translators: %s: name of a tool like jpegtran */
46 sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>gifsicle</em>' )
47 );
48 }
49 ewwwio_debug_message( "file: $file " );
50 ewwwio_debug_message( "width: $dst_w" );
51 ewwwio_debug_message( "height: $dst_h" );
52
53 list( $orig_w, $orig_h ) = ewwwio()->getimagesize( $file );
54
55 $outfile = "$file.tmp";
56 // Run gifsicle.
57 if ( (int) $orig_w !== (int) $src_w || (int) $orig_h !== (int) $src_h ) {
58 $dim_string = $dst_w . 'x' . $dst_h;
59 $crop_string = $src_x . ',' . $src_y . '+' . $src_w . 'x' . $src_h;
60 ewwwio_debug_message( "resize to $dim_string" );
61 ewwwio_debug_message( "crop to $crop_string" );
62 $cmd = "{$tools['gifsicle']} --crop $crop_string -o " . ewww_image_optimizer_escapeshellarg( $outfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file );
63 ewwwio_debug_message( "running: $cmd" );
64 exec( $cmd, $output, $exit );
65 $cmd = "{$tools['gifsicle']} --resize-fit $dim_string -b " . ewww_image_optimizer_escapeshellarg( $outfile );
66 ewwwio_debug_message( "running: $cmd" );
67 exec( $cmd, $output, $exit );
68 } else {
69 $dim_string = $dst_w . 'x' . $dst_h;
70 $cmd = "{$tools['gifsicle']} --resize-fit $dim_string -o " . ewww_image_optimizer_escapeshellarg( $outfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file );
71 ewwwio_debug_message( "running: $cmd" );
72 exec( $cmd, $output, $exit );
73 }
74 ewwwio_debug_message( "$file resized to $outfile" );
75
76 if ( ewwwio_is_file( $outfile ) ) {
77 $new_type = ewww_image_optimizer_mimetype( $outfile, 'i' );
78 // Check the filesize of the new JPG.
79 $new_size = ewww_image_optimizer_filesize( $outfile );
80 ewwwio_debug_message( "$outfile exists, testing type and size" );
81 } else {
82 return new WP_Error( 'image_resize_error', 'file does not exist' );
83 }
84
85 if ( ! $new_size || 'image/gif' !== $new_type ) {
86 unlink( $outfile );
87 return new WP_Error( 'image_resize_error', 'wrong type or zero bytes' );
88 }
89 ewwwio_debug_message( 'resize success' );
90 $image = file_get_contents( $outfile );
91 unlink( $outfile );
92 return $image;
93 }
94
95 /**
96 * Quantize and reduce the bit-depth of a PNG image using pngquant.
97 *
98 * @param string $file Name of the PNG image file.
99 * @param int $colors Maximum number of colors allowed.
100 * @return bool True if the reduction was successful.
101 */
102 function ewww_image_optimizer_pngquant_reduce_png( $file, $colors ) {
103 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
104 $tools['pngquant'] = ewwwio()->local->get_path( 'pngquant', true );
105 if ( empty( $tools['pngquant'] ) ) {
106 return false;
107 }
108 $colors = (int) min( $colors, 256 );
109 $cmd = $tools['pngquant'] . ' ' . $colors . ' --nofs --skip-if-larger ' . ewww_image_optimizer_escapeshellarg( $file );
110 ewwwio_debug_message( "running: $cmd" );
111 exec( $cmd, $output, $exit );
112 $quantfile = preg_replace( '/\.\w+$/', '-or8.png', $file );
113 if ( ewwwio_is_file( $quantfile ) && filesize( $file ) > filesize( $quantfile ) ) {
114 ewwwio_debug_message( 'PNG reduction is better: original - ' . filesize( $file ) . ' vs. lossy - ' . filesize( $quantfile ) );
115 rename( $quantfile, $file );
116 return true;
117 } elseif ( ewwwio_is_file( $quantfile ) ) {
118 ewwwio_debug_message( 'PNG reduction is worse: original - ' . filesize( $file ) . ' vs. lossy - ' . filesize( $quantfile ) );
119 ewwwio_delete_file( $quantfile );
120 } else {
121 ewwwio_debug_message( 'pngquant did not produce any output' );
122 }
123 return false;
124 }
125
126 /**
127 * Automatically corrects JPG rotation using local jpegtran tool.
128 *
129 * @param string $file Name of the file to fix.
130 * @param string $type File type of the file.
131 * @param int $orientation The EXIF orientation value.
132 * @return bool True if the rotation was successful.
133 */
134 function ewww_image_optimizer_jpegtran_autorotate( $file, $type, $orientation ) {
135 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
136 if ( 'image/jpeg' !== $type ) {
137 ewwwio_debug_message( 'not a JPG, go away!' );
138 return false;
139 }
140 if ( ! $orientation || 1 === (int) $orientation ) {
141 return false;
142 }
143 $nice = '';
144 if ( PHP_OS !== 'WINNT' && ! ewwwio()->cloud_mode && ewwwio()->local->exec_check() ) {
145 // Check to see if 'nice' exists.
146 $nice = ewwwio()->local->find_nix_binary( 'nice' );
147 }
148 $tools = array(
149 'jpegtran' => ewwwio()->local->get_path( 'jpegtran' ),
150 );
151 if ( empty( $tools['jpegtran'] ) ) {
152 return false;
153 }
154 switch ( $orientation ) {
155 case 1:
156 return false;
157 case 2:
158 $transform = '-flip horizontal';
159 break;
160 case 3:
161 $transform = '-rotate 180';
162 break;
163 case 4:
164 $transform = '-flip vertical';
165 break;
166 case 5:
167 $transform = '-transpose';
168 break;
169 case 6:
170 $transform = '-rotate 90';
171 break;
172 case 7:
173 $transform = '-transverse';
174 break;
175 case 8:
176 $transform = '-rotate 270';
177 break;
178 default:
179 return false;
180 }
181 $outfile = "$file.rotate";
182 // Run jpegtran.
183 $cmd = "$nice {$tools['jpegtran']} -trim -copy all $transform -outfile " . ewww_image_optimizer_escapeshellarg( $outfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file );
184 ewwwio_debug_message( "running: $cmd" );
185 exec( $cmd, $output, $exit );
186 ewwwio_debug_message( "$file rotated to $outfile" );
187
188 if ( ewwwio_is_file( $outfile ) ) {
189 $new_type = ewww_image_optimizer_mimetype( $outfile, 'i' );
190 // Check the filesize of the new JPG.
191 $new_size = filesize( $outfile );
192 ewwwio_debug_message( "$outfile exists, testing type and size" );
193 } else {
194 return false;
195 }
196
197 if ( ! $new_size || 'image/jpeg' !== $new_type ) {
198 unlink( $outfile );
199 return false;
200 }
201 ewwwio_debug_message( 'rotation success' );
202 rename( $outfile, $file );
203 return true;
204 }
205
206 /**
207 * Process an image.
208 *
209 * @param string $file Full absolute path to the image file.
210 * @param int $gallery_type 1=WordPress, 2=nextgen, 3=flagallery, 4=aux_images, 5=image editor,
211 * 6=imagestore.
212 * @param bool $converted True if this is a resize and the full image was converted to a
213 * new format. Deprecated, always false now.
214 * @param bool $new_image True if this is a new image, so it should attempt conversion regardless of
215 * previous results.
216 * @param bool $fullsize True if this is a full size (original) image.
217 * @return array {
218 * Status of the optimization attempt.
219 *
220 * @type string $file The filename or false on error.
221 * @type string $results The results of the optimization.
222 * @type bool $converted True if an image changes formats.
223 * @type string The original filename if converted.
224 * }
225 */
226 function ewww_image_optimizer( $file, $gallery_type = 4, $converted = false, $new_image = false, $fullsize = false ) {
227 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
228 session_write_close();
229 if ( function_exists( 'wp_raise_memory_limit' ) ) {
230 wp_raise_memory_limit( 'image' );
231 }
232 if ( apply_filters( 'ewww_image_optimizer_bypass', false, $file ) ) {
233 ewwwio_debug_message( "optimization bypassed: $file" );
234 // Tell the user optimization was skipped.
235 return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
236 }
237 global $ewww_image;
238 global $ewww_convert;
239 // Initialize the original filename.
240 $original = $file;
241 $result = '';
242 if ( false !== strpos( $file, '../' ) || false !== strpos( $file, '..\\' ) ) {
243 $msg = __( 'Path traversal in filename not allowed.', 'ewww-image-optimizer' );
244 ewwwio_debug_message( "file is using ../ potential path traversal blocked: $file" );
245 return array( false, $msg, $converted, $original );
246 }
247 if ( ! ewwwio_is_file( $file ) ) {
248 /* translators: %s: Image filename */
249 $msg = sprintf( __( 'Could not find %s', 'ewww-image-optimizer' ), $file );
250 ewwwio_debug_message( "file doesn't appear to exist: $file" );
251 return array( false, $msg, $converted, $original );
252 }
253 if ( ! is_writable( $file ) ) {
254 /* translators: %s: Image filename */
255 $msg = sprintf( __( '%s is not writable', 'ewww-image-optimizer' ), $file );
256 ewwwio_debug_message( "couldn't write to the file $file" );
257 return array( false, $msg, $converted, $original );
258 }
259 // Get the original image size.
260 $orig_size = ewww_image_optimizer_filesize( $file );
261 $type = ewww_image_optimizer_mimetype( $file, 'i' );
262 if ( ! $type ) {
263 ewwwio_debug_message( 'could not find mimetype' );
264 // Store a 'no savings' record to prevent this image from being processed without the 'force' flag set.
265 ewww_image_optimizer_update_table( $file, $orig_size, $orig_size );
266 // Otherwise we store an error message since we couldn't get the mime-type.
267 return array( false, __( 'Unknown file type', 'ewww-image-optimizer' ), $converted, $original );
268 }
269 // Not an image or pdf.
270 if ( strpos( $type, 'image' ) === false && strpos( $type, 'pdf' ) === false ) {
271 ewwwio_debug_message( "unsupported mimetype: $type" );
272 // Store a 'no savings' record to prevent this image from being processed without the 'force' flag set.
273 ewww_image_optimizer_update_table( $file, $orig_size, $orig_size );
274 return array( false, __( 'Unsupported file type', 'ewww-image-optimizer' ) . ": $type", $converted, $original );
275 }
276 if ( ! is_object( $ewww_image ) || ! $ewww_image instanceof EWWW_Image || $ewww_image->file !== $file ) {
277 $ewww_image = new EWWW_Image( 0, '', $file );
278 }
279 $nice = '';
280 if ( PHP_OS !== 'WINNT' && ! ewwwio()->cloud_mode && ewwwio()->local->exec_check() ) {
281 // Check to see if 'nice' exists.
282 $nice = ewwwio()->local->find_nix_binary( 'nice' );
283 }
284 $tools = array();
285 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_skip_full' ) && $fullsize ) {
286 $keep_metadata = true;
287 } else {
288 $keep_metadata = false;
289 }
290 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_lossy_skip_full' ) && $fullsize ) {
291 $skip_lossy = true;
292 } else {
293 $skip_lossy = false;
294 }
295 if ( ini_get( 'max_execution_time' ) && ini_get( 'max_execution_time' ) < 90 && ewww_image_optimizer_stl_check() ) {
296 set_time_limit( 0 );
297 }
298 // Get the original image size.
299 $orig_size = ewww_image_optimizer_filesize( $file );
300 ewwwio_debug_message( "original filesize: $orig_size" );
301 if ( $orig_size < ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_size' ) ) {
302 ewwwio_debug_message( "optimization bypassed due to filesize: $file" );
303 // Tell the user optimization was skipped.
304 return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
305 }
306 if ( 'image/png' === $type && ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) && $orig_size > ewww_image_optimizer_get_option( 'ewww_image_optimizer_skip_png_size' ) ) {
307 ewwwio_debug_message( "optimization bypassed due to filesize: $file" );
308 // Tell the user optimization was skipped.
309 return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
310 }
311 if ( 'image/bmp' === $type && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_bmp_convert' ) && empty( $ewww_convert ) ) {
312 ewwwio_debug_message( "BMP skipped, no conversion enabled: $file" );
313 return array( false, __( 'Optimization skipped', 'ewww-image-optimizer' ), $converted, $file );
314 }
315 $backup_hash = '';
316 $new_size = 0;
317 // Set the optimization process to OFF.
318 $optimize = false;
319 // Toggle the convert process to ON.
320 $convert = true;
321 // Allow other plugins to mangle the image however they like prior to optimization.
322 do_action( 'ewww_image_optimizer_pre_optimization', $file, $type, $fullsize );
323 // Run the appropriate optimization/conversion for the mime-type.
324 switch ( $type ) {
325 case 'image/bmp':
326 if (
327 1 === (int) $gallery_type &&
328 $fullsize &&
329 ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_bmp_convert' ) || ! empty( $ewww_convert ) ) &&
330 empty( ewwwio()->webp_only )
331 ) {
332 $jpgfile = ewww_image_optimizer_unique_filename( $file, '.jpg' );
333 } else {
334 $convert = false;
335 }
336 if ( $convert ) {
337 // We leave newfile (param #4) empty, to let the convert() method find the best filetype & corresponding extension.
338 // NOTE: at this point, conversion to PNG is disabled, but we'll keep it as is, just in case.
339 $new_file = $ewww_image->convert( $file, true, true );
340 $new_size = ewww_image_optimizer_filesize( $new_file );
341 if ( $new_file && $new_size && $new_size < $orig_size ) {
342 $file = $new_file;
343 $converted = true;
344 $results_msg = ewww_image_optimizer_update_table( $file, $new_size, $orig_size, $original );
345 // Update some of the EWWW_Image properties to prevent re-conversion.
346 $ewww_image->converted = $original;
347 $ewww_image->opt_size = $new_size;
348 // Then, make sure the optimization will not abort due to the record we just inserted.
349 $original_force = ewwwio()->force;
350 ewwwio()->force = true;
351 ewww_image_optimizer( $file, $gallery_type, false, $new_image, true );
352 ewwwio()->force = $original_force;
353 $new_size = ewww_image_optimizer_filesize( $file );
354 }
355 }
356 break;
357 case 'image/jpeg':
358 $png_size = 0;
359 // If jpg2png conversion is enabled, and this image is in the WordPress media library.
360 if (
361 1 === (int) $gallery_type &&
362 $fullsize &&
363 empty( $ewww_image->converted ) &&
364 ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_to_png' ) || ! empty( $ewww_convert ) ) &&
365 empty( ewwwio()->webp_only )
366 ) {
367 // Generate the filename for a PNG:
368 // If this is a resize version.
369 if ( $converted ) {
370 // just change the file extension.
371 $pngfile = preg_replace( '/\.\w+$/', '.png', $file );
372 } else {
373 // If this is a full size image.
374 // Get a unique filename for the png image.
375 $pngfile = ewww_image_optimizer_unique_filename( $file, '.png' );
376 }
377 } else {
378 // Otherwise, turn conversion OFF.
379 $convert = false;
380 $pngfile = '';
381 }
382 $compression_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' );
383 // Check for previous optimization, so long as the force flag is not on and this isn't a new image that needs converting.
384 if ( empty( ewwwio()->force ) && ! ( $new_image && $convert ) && empty( ewwwio()->webp_only ) ) {
385 $results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
386 $smart_reopt = ! empty( ewwwio()->force_smart ) && ewww_image_optimizer_level_mismatch( $ewww_image->level, $compression_level ) ? true : false;
387 if ( $smart_reopt ) {
388 ewwwio_debug_message( "smart re-opt found level mismatch for $file, db says " . $ewww_image->level . " vs. current $compression_level" );
389 // If the current compression level is less than what was previously used, and the previous level was premium (or premium plus).
390 if ( $compression_level && $compression_level < $ewww_image->level && $ewww_image->level > 20 ) {
391 ewwwio_debug_message( "smart re-opt triggering restoration for $file" );
392 ewww_image_optimizer_cloud_restore_single_image( $ewww_image->record );
393 }
394 } elseif ( $results_msg ) {
395 return array( $file, $results_msg, $converted, $original );
396 }
397 }
398 $ewww_image->level = $compression_level;
399 if ( $compression_level > 10 && empty( ewwwio()->webp_only ) ) {
400 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type, $convert, $pngfile, 'image/png', $skip_lossy );
401 if ( $converted ) {
402 // Check to see if the user wants the originals deleted.
403 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) ) {
404 // Delete the original JPG.
405 ewwwio_delete_file( $original );
406 }
407 $converted = true;
408 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, 'image/png', null, $orig_size !== $new_size );
409 } else {
410 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, null, $orig_size !== $new_size );
411 }
412 break;
413 }
414 $tools['jpegtran'] = ewwwio()->local->get_path( 'jpegtran' );
415 $tools['cwebp'] = ewwwio()->local->get_path( 'cwebp' );
416 if ( $convert ) {
417 $tools['optipng'] = ewwwio()->local->get_path( 'optipng' );
418 $tools['pngout'] = ewwwio()->local->get_path( 'pngout' );
419 $tools['pngquant'] = ewwwio()->local->get_path( 'pngquant' );
420 }
421 // For exec-deprived servers, or those where jpegtran doesn't want to work.
422 if ( 10 === (int) $compression_level && empty( $tools['jpegtran'] ) ) {
423 if ( empty( ewwwio()->webp_only ) ) {
424 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
425 }
426 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['cwebp'], $orig_size !== $new_size );
427 break;
428 }
429 // Get the (possibly new) original image size.
430 $orig_size = ewww_image_optimizer_filesize( $file );
431 if ( ! empty( ewwwio()->webp_only ) ) {
432 ewwwio_debug_message( 'creating webp only, skipping convert and optimize' );
433 $webp_result = ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tools['cwebp'] );
434 break;
435 } elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) ) {
436 // Store an appropriate message in $result.
437 $result = __( 'JPG optimization is disabled', 'ewww-image-optimizer' );
438 // Otherwise, if jpegtran doesn't exist.
439 } elseif ( empty( $tools['jpegtran'] ) ) {
440 /* translators: %s: name of a tool like jpegtran */
441 $result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>jpegtran</em>' );
442 // Otherwise, things should be good, so...
443 } else {
444 // Set the optimization process to ON.
445 $optimize = true;
446 }
447 // If we get this far, we are using local (jpegtran) optimization, so do an autorotate on the image.
448 ewww_image_optimizer_autorotate( $file );
449 // If local optimization is turned ON.
450 if ( $optimize ) {
451 ewwwio_debug_message( 'attempting to optimize JPG...' );
452 // Generate temporary file-name.
453 $progfile = $file . '.prog';
454 // Check to see if we are supposed to strip metadata.
455 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) && ! $keep_metadata ) {
456 // Don't copy metadata.
457 $copy_opt = 'none';
458 } else {
459 // Copy all the metadata.
460 $copy_opt = 'all';
461 }
462 if ( $orig_size > 10240 ) {
463 $progressive = '-progressive';
464 } else {
465 $progressive = '';
466 }
467 // Run jpegtran.
468 $cmd = "$nice " . $tools['jpegtran'] . " -copy $copy_opt -optimize $progressive -outfile " . ewww_image_optimizer_escapeshellarg( $progfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file );
469 ewwwio_debug_message( "running: $cmd" );
470 exec( $cmd, $output, $exit );
471 // Check the filesize of the new JPG.
472 $new_size = ewww_image_optimizer_filesize( $progfile );
473 ewwwio_debug_message( "optimized JPG size: $new_size" );
474 // If the best-optimized is smaller than the original JPG, and we didn't create an empty JPG.
475 if ( $new_size && $orig_size > $new_size && ewww_image_optimizer_mimetype( $progfile, 'i' ) === $type ) {
476 // Replace the original with the optimized file.
477 rename( $progfile, $file );
478 // Store the results of the optimization.
479 $result = "$orig_size vs. $new_size";
480 // If the optimization didn't produce a smaller JPG.
481 } else {
482 if ( ewwwio_is_file( $progfile ) ) {
483 // Delete the optimized file.
484 ewwwio_delete_file( $progfile );
485 }
486 // Store the results.
487 $result = 'unchanged';
488 $new_size = $orig_size;
489 }
490 } // End if().
491 // If the conversion process is turned ON, or if this is a resize and the full-size was converted.
492 if ( $convert ) {
493 ewwwio_debug_message( "attempting to convert JPG to PNG: $pngfile" );
494 if ( empty( $new_size ) ) {
495 $new_size = $orig_size;
496 }
497 // Convert the JPG to PNG.
498 if ( ewwwio()->gmagick_support() ) {
499 try {
500 $gmagick = new Gmagick( $file );
501 $gmagick->stripimage();
502 $gmagick->setimageformat( 'PNG' );
503 $gmagick->writeimage( $pngfile );
504 } catch ( Exception $gmagick_error ) {
505 ewwwio_debug_message( $gmagick_error->getMessage() );
506 }
507 $png_size = ewww_image_optimizer_filesize( $pngfile );
508 }
509 if ( ! $png_size && ewwwio()->imagick_support() ) {
510 try {
511 $imagick = new Imagick( $file );
512 $imagick->stripImage();
513 $imagick->setImageFormat( 'PNG' );
514 $imagick->writeImage( $pngfile );
515 } catch ( Exception $imagick_error ) {
516 ewwwio_debug_message( $imagick_error->getMessage() );
517 }
518 $png_size = ewww_image_optimizer_filesize( $pngfile );
519 }
520 if ( ! $png_size && ewwwio()->gd_support() ) {
521 ewwwio_debug_message( 'converting with GD' );
522 imagepng( imagecreatefromjpeg( $file ), $pngfile );
523 $png_size = ewww_image_optimizer_filesize( $pngfile );
524 }
525 // If lossy optimization is ON and full-size exclusion is not active.
526 if ( $png_size && 40 === (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) && $tools['pngquant'] && ! $skip_lossy ) {
527 ewwwio_debug_message( 'attempting lossy reduction' );
528 $cmd = "$nice " . $tools['pngquant'] . ' ' . ewww_image_optimizer_escapeshellarg( $pngfile );
529 ewwwio_debug_message( "running: $cmd" );
530 exec( $cmd, $output, $exit );
531 $quantfile = preg_replace( '/\.\w+$/', '-fs8.png', $pngfile );
532 if ( ewwwio_is_file( $quantfile ) && filesize( $pngfile ) > filesize( $quantfile ) ) {
533 ewwwio_debug_message( 'lossy reduction is better: original - ' . filesize( $pngfile ) . ' vs. lossy - ' . filesize( $quantfile ) );
534 rename( $quantfile, $pngfile );
535 } elseif ( ewwwio_is_file( $quantfile ) ) {
536 ewwwio_debug_message( 'lossy reduction is worse: original - ' . filesize( $pngfile ) . ' vs. lossy - ' . filesize( $quantfile ) );
537 ewwwio_delete_file( $quantfile );
538 } else {
539 ewwwio_debug_message( 'pngquant did not produce any output' );
540 }
541 }
542 // If optipng isn't disabled.
543 if ( $png_size && $tools['optipng'] ) {
544 // Retrieve the optipng optimization level.
545 $optipng_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
546 if (
547 ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) &&
548 preg_match( '/0.7/', ewwwio()->local->test_binary( $tools['optipng'], 'optipng' ) ) &&
549 ! $keep_metadata
550 ) {
551 $strip = '-strip all ';
552 } else {
553 $strip = '';
554 }
555 // If the PNG file was created.
556 if ( ewwwio_is_file( $pngfile ) ) {
557 ewwwio_debug_message( 'optimizing converted PNG with optipng' );
558 // Run optipng on the new PNG.
559 $cmd = "$nice " . $tools['optipng'] . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $pngfile );
560 ewwwio_debug_message( "running: $cmd" );
561 exec( $cmd, $output, $exit );
562 }
563 }
564 // If pngout isn't disabled.
565 if ( $png_size && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) ) {
566 // Retrieve the pngout optimization level.
567 $pngout_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
568 // If the PNG file was created.
569 if ( ewwwio_is_file( $pngfile ) ) {
570 ewwwio_debug_message( 'optimizing converted PNG with pngout' );
571 // Run pngout on the new PNG.
572 $cmd = "$nice " . $tools['pngout'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $pngfile );
573 ewwwio_debug_message( "running: $cmd" );
574 exec( $cmd, $output, $exit );
575 }
576 }
577 $png_size = ewww_image_optimizer_filesize( $pngfile );
578 ewwwio_debug_message( "converted PNG size: $png_size" );
579 // If the PNG is smaller than the original JPG, and we didn't end up with an empty file.
580 if ( $png_size && $new_size > $png_size && ewww_image_optimizer_mimetype( $pngfile, 'i' ) === 'image/png' ) {
581 ewwwio_debug_message( "converted PNG is better: $png_size vs. $new_size" );
582 // Store the size of the converted PNG.
583 $new_size = $png_size;
584 // Check to see if the user wants the originals deleted.
585 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) ) {
586 // Delete the original JPG.
587 ewwwio_delete_file( $file );
588 }
589 // Store the location of the PNG file.
590 $file = $pngfile;
591 // Let webp know what we're dealing with now.
592 $type = 'image/png';
593 // Successful conversion and we store the increment.
594 $converted = true;
595 } else {
596 ewwwio_debug_message( 'converted PNG is no good' );
597 // Otherwise delete the PNG.
598 $converted = false;
599 if ( ewwwio_is_file( $pngfile ) ) {
600 ewwwio_delete_file( $pngfile );
601 }
602 }
603 } // End if().
604 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['cwebp'], $orig_size !== $new_size );
605 break;
606 case 'image/png':
607 $jpg_size = 0;
608 // Png2jpg conversion is turned on, and the image is in the WordPress media library.
609 // We check for transparency later, after optimization, because optipng might fix an empty alpha channel.
610 $apng = ewww_image_optimizer_is_animated_png( $file );
611 if ( $apng ) {
612 $keep_metadata = true;
613 $skip_lossy = true;
614 }
615 if (
616 1 === (int) $gallery_type &&
617 $fullsize &&
618 empty( $ewww_image->converted ) &&
619 ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_to_jpg' ) || ! empty( $ewww_convert ) ) &&
620 ! $skip_lossy &&
621 empty( ewwwio()->webp_only )
622 ) {
623 ewwwio_debug_message( 'PNG to JPG conversion turned on' );
624 $cloud_background = '';
625 $r = '';
626 $g = '';
627 $b = '';
628 // If the user set a fill background for transparency.
629 $background = ewww_image_optimizer_jpg_background();
630 if ( $background ) {
631 $cloud_background = "#$background";
632 // Set background color for GD.
633 $r = hexdec( '0x' . strtoupper( substr( $background, 0, 2 ) ) );
634 $g = hexdec( '0x' . strtoupper( substr( $background, 2, 2 ) ) );
635 $b = hexdec( '0x' . strtoupper( substr( $background, 4, 2 ) ) );
636 // Set the background flag for 'convert'.
637 $background = '-background ' . '"' . "#$background" . '"';
638 }
639 $gquality = ewww_image_optimizer_jpg_quality();
640 $gquality = $gquality ? $gquality : '82';
641 // If this is a resize version.
642 if ( $converted ) {
643 // Just replace the file extension with a .jpg.
644 $jpgfile = preg_replace( '/\.\w+$/', '.jpg', $file );
645 // If this is a full version.
646 } else {
647 // Construct the filename for the new JPG.
648 $jpgfile = ewww_image_optimizer_unique_filename( $file, '.jpg' );
649 }
650 } else {
651 ewwwio_debug_message( 'PNG to JPG conversion turned off' );
652 // Turn the conversion process OFF.
653 $convert = false;
654 $jpgfile = '';
655 $r = null;
656 $g = null;
657 $b = null;
658 $cloud_background = '';
659 $gquality = null;
660 } // End if().
661 $compression_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' );
662 // Check for previous optimization, so long as the force flag is not on and this isn't a new image that needs converting.
663 if ( empty( ewwwio()->force ) && ! ( $new_image && $convert ) && empty( ewwwio()->webp_only ) ) {
664 $results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
665 $smart_reopt = ! empty( ewwwio()->force_smart ) && ewww_image_optimizer_level_mismatch( $ewww_image->level, $compression_level ) ? true : false;
666 if ( $smart_reopt ) {
667 ewwwio_debug_message( "smart re-opt found level mismatch for $file, db says " . $ewww_image->level . " vs. current $compression_level" );
668 // If the current compression level is less than what was previously used, and the previous level was premium (or premium plus).
669 if ( $compression_level && $compression_level < $ewww_image->level && $ewww_image->level > 20 ) {
670 ewwwio_debug_message( "smart re-opt triggering restoration for $file" );
671 ewww_image_optimizer_cloud_restore_single_image( $ewww_image->record );
672 }
673 } elseif ( $results_msg ) {
674 return array( $file, $results_msg, $converted, $original );
675 }
676 }
677 $ewww_image->level = $compression_level;
678 if (
679 $compression_level >= 20 &&
680 ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) &&
681 empty( ewwwio()->webp_only )
682 ) {
683 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer(
684 $file,
685 $type,
686 $convert,
687 $jpgfile,
688 'image/jpeg',
689 $skip_lossy,
690 $cloud_background,
691 $gquality
692 );
693 if ( $converted ) {
694 // Check to see if the user wants the originals deleted.
695 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) ) {
696 // Delete the original JPG.
697 ewwwio_delete_file( $original );
698 }
699 $converted = true;
700 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, 'image/jpeg', null, $orig_size !== $new_size );
701 } else {
702 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, null, $orig_size !== $new_size );
703 }
704 break;
705 }
706 $tools['optipng'] = ewwwio()->local->get_path( 'optipng' );
707 $tools['pngout'] = ewwwio()->local->get_path( 'pngout' );
708 $tools['pngquant'] = ewwwio()->local->get_path( 'pngquant' );
709 $tools['cwebp'] = ewwwio()->local->get_path( 'cwebp' );
710 if ( $convert ) {
711 $tools['jpegtran'] = ewwwio()->local->get_path( 'jpegtran' );
712 }
713 // Check if we can (and should) do local PNG optimization.
714 if ( ! empty( ewwwio()->webp_only ) ) {
715 ewwwio_debug_message( 'creating webp only, skipping convert and optimize' );
716 $webp_result = ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tools['cwebp'] );
717 break;
718 } elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) ) {
719 // Tell the user all PNG tools are disabled.
720 $result = __( 'PNG optimization is disabled', 'ewww-image-optimizer' );
721 // If the utility checking is on, optipng is enabled, but optipng cannot be found.
722 } elseif ( empty( $tools['optipng'] ) ) {
723 /* translators: %s: name of a tool like jpegtran */
724 $result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>optipng</em>' );
725 // If the utility checking is on, pngout is enabled, but pngout cannot be found.
726 } else {
727 // Turn optimization on if we made it through all the checks.
728 $optimize = true;
729 }
730 // If optimization is turned on.
731 if ( $optimize ) {
732 // If lossy optimization is ON and full-size exclusion is not active.
733 if ( 40 === (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_png_level' ) && $tools['pngquant'] && ! $skip_lossy ) {
734 ewwwio_debug_message( 'attempting lossy reduction' );
735 $cmd = "$nice " . $tools['pngquant'] . ' ' . ewww_image_optimizer_escapeshellarg( $file );
736 ewwwio_debug_message( "running: $cmd" );
737 exec( $cmd, $output, $exit );
738 $quantfile = preg_replace( '/\.\w+$/', '-fs8.png', $file );
739 if ( ewwwio_is_file( $quantfile ) && filesize( $file ) > filesize( $quantfile ) && ewww_image_optimizer_mimetype( $quantfile, 'i' ) === $type ) {
740 ewwwio_debug_message( 'lossy reduction is better: original - ' . filesize( $file ) . ' vs. lossy - ' . filesize( $quantfile ) );
741 rename( $quantfile, $file );
742 } elseif ( ewwwio_is_file( $quantfile ) ) {
743 ewwwio_debug_message( 'lossy reduction is worse: original - ' . filesize( $file ) . ' vs. lossy - ' . filesize( $quantfile ) );
744 ewwwio_delete_file( $quantfile );
745 } else {
746 ewwwio_debug_message( 'pngquant did not produce any output' );
747 }
748 }
749 $tempfile = $file . '.tmp.png';
750 copy( $file, $tempfile );
751 // If optipng is enabled.
752 if ( $tools['optipng'] ) {
753 // Retrieve the optimization level for optipng.
754 $optipng_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
755 $strip = '';
756 if (
757 ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) &&
758 preg_match( '/0.7/', ewwwio()->local->test_binary( $tools['optipng'], 'optipng' ) ) &&
759 ! $keep_metadata
760 ) {
761 $strip = '-strip all ';
762 }
763 // Run optipng on the PNG file.
764 $cmd = "$nice " . $tools['optipng'] . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $tempfile );
765 ewwwio_debug_message( "running: $cmd" );
766 exec( $cmd, $output, $exit );
767 }
768 // If pngout is enabled.
769 if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) ) {
770 // Retrieve the optimization level for pngout.
771 $pngout_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
772 $strip = '';
773 if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) || $keep_metadata ) {
774 $strip = '-k1';
775 }
776 // Run pngout on the PNG file.
777 $cmd = "$nice " . $tools['pngout'] . " -s$pngout_level -k1 -q " . ewww_image_optimizer_escapeshellarg( $tempfile );
778 ewwwio_debug_message( "running: $cmd" );
779 exec( $cmd, $output, $exit );
780 }
781 // Retrieve the filesize of the temporary PNG.
782 $new_size = ewww_image_optimizer_filesize( $tempfile );
783 // If the new PNG is smaller.
784 if ( $new_size && $orig_size > $new_size && ewww_image_optimizer_mimetype( $tempfile, 'i' ) === $type ) {
785 // Replace the original with the optimized file.
786 rename( $tempfile, $file );
787 // Store the results of the optimization.
788 $result = "$orig_size vs. $new_size";
789 // If the optimization didn't produce a smaller PNG.
790 } else {
791 if ( ewwwio_is_file( $tempfile ) ) {
792 // Delete the optimized file.
793 ewwwio_delete_file( $tempfile );
794 }
795 // Store the results.
796 $result = 'unchanged';
797 $new_size = $orig_size;
798 }
799 } // End if().
800 // Retrieve the new filesize of the PNG.
801 $new_size = ewww_image_optimizer_filesize( $file );
802 // Double check for png2jpg conversion to see if we have an alpha image.
803 if ( $convert && ewww_image_optimizer_png_alpha( $file ) && ! ewww_image_optimizer_jpg_background() ) {
804 ewwwio_debug_message( 'PNG to JPG conversion turned off due to alpha' );
805 $convert = false;
806 }
807 // If conversion is on and the PNG doesn't have transparency or the user set a background color to replace transparency.
808 if ( $convert ) {
809 ewwwio_debug_message( "attempting to convert PNG to JPG: $jpgfile" );
810 if ( empty( $new_size ) ) {
811 $new_size = $orig_size;
812 }
813 $magick_background = ewww_image_optimizer_jpg_background();
814 if ( empty( $magick_background ) ) {
815 $magick_background = '000000';
816 }
817 // Convert the PNG to a JPG with all the proper options.
818 if ( ewwwio()->gmagick_support() ) {
819 try {
820 if ( ewww_image_optimizer_png_alpha( $file ) ) {
821 $gmagick_overlay = new Gmagick( $file );
822 $gmagick = new Gmagick();
823 $gmagick->newimage( $gmagick_overlay->getimagewidth(), $gmagick_overlay->getimageheight(), '#' . $magick_background );
824 $gmagick->compositeimage( $gmagick_overlay, 1, 0, 0 );
825 } else {
826 $gmagick = new Gmagick( $file );
827 }
828 $gmagick->setimageformat( 'JPG' );
829 $gmagick->setcompressionquality( $gquality );
830 $gmagick->writeimage( $jpgfile );
831 } catch ( Exception $gmagick_error ) {
832 ewwwio_debug_message( $gmagick_error->getMessage() );
833 }
834 $jpg_size = ewww_image_optimizer_filesize( $jpgfile );
835 }
836 if ( ! $jpg_size && ewwwio()->imagick_support() ) {
837 try {
838 $imagick = new Imagick( $file );
839 if ( ewww_image_optimizer_png_alpha( $file ) ) {
840 $imagick->setImageBackgroundColor( new ImagickPixel( '#' . $magick_background ) );
841 $imagick->setImageAlphaChannel( imagick::ALPHACHANNEL_REMOVE );
842 }
843 $imagick->setImageFormat( 'JPG' );
844 $imagick->setImageCompressionQuality( $gquality );
845 $imagick->writeImage( $jpgfile );
846 } catch ( Exception $imagick_error ) {
847 ewwwio_debug_message( $imagick_error->getMessage() );
848 }
849 $jpg_size = ewww_image_optimizer_filesize( $jpgfile );
850 }
851 if ( ! $jpg_size && ewwwio()->gd_support() ) {
852 ewwwio_debug_message( 'converting with GD' );
853 // Retrieve the data from the PNG.
854 $input = imagecreatefrompng( $file );
855 // Retrieve the dimensions of the PNG.
856 list($width, $height) = ewwwio()->getimagesize( $file );
857 // Create a new image with those dimensions.
858 $output = imagecreatetruecolor( $width, $height );
859 if ( '' === $r ) {
860 $r = 255;
861 $g = 255;
862 $b = 255;
863 }
864 // Allocate the background color.
865 $rgb = imagecolorallocate( $output, $r, $g, $b );
866 // Fill the new image with the background color.
867 imagefilledrectangle( $output, 0, 0, $width, $height, $rgb );
868 // Copy the original image to the new image.
869 imagecopy( $output, $input, 0, 0, 0, 0, $width, $height );
870 // Output the JPG with the quality setting.
871 imagejpeg( $output, $jpgfile, $gquality );
872 }
873 $jpg_size = ewww_image_optimizer_filesize( $jpgfile );
874 if ( $jpg_size ) {
875 ewwwio_debug_message( "converted JPG filesize: $jpg_size" );
876 } else {
877 ewwwio_debug_message( 'unable to convert to JPG' );
878 }
879 // Next we need to optimize that JPG if jpegtran is enabled.
880 if ( 10 === (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpg_level' ) && ewwwio_is_file( $jpgfile ) && ! empty( $tools['jpegtran'] ) ) {
881 // Generate temporary file-name.
882 $progfile = $jpgfile . '.prog';
883 // Check to see if we are supposed to strip metadata.
884 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) && ! $keep_metadata ) {
885 // Don't copy metadata.
886 $copy_opt = 'none';
887 } else {
888 // Copy all the metadata.
889 $copy_opt = 'all';
890 }
891 if ( $jpg_size > 10240 ) {
892 $progressive = '-progressive';
893 } else {
894 $progressive = '';
895 }
896 // Run jpegtran.
897 $cmd = "$nice " . $tools['jpegtran'] . " -copy $copy_opt -optimize $progressive -outfile " . ewww_image_optimizer_escapeshellarg( $progfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $jpgfile );
898 ewwwio_debug_message( "running: $cmd" );
899 exec( $cmd, $output, $exit );
900 // Check the filesize of the new JPG.
901 $opt_jpg_size = ewww_image_optimizer_filesize( $progfile );
902 // If the best-optimized is smaller than the original JPG, and we didn't create an empty JPG.
903 if ( $opt_jpg_size && $jpg_size > $opt_jpg_size ) {
904 // Replace the original with the optimized file.
905 rename( $progfile, $jpgfile );
906 // Store the size of the optimized JPG.
907 $jpg_size = $opt_jpg_size;
908 ewwwio_debug_message( 'optimized JPG was smaller than un-optimized version' );
909 // If the optimization didn't produce a smaller JPG.
910 } elseif ( ewwwio_is_file( $progfile ) ) {
911 ewwwio_delete_file( $progfile );
912 }
913 }
914 ewwwio_debug_message( "converted JPG size: $jpg_size" );
915 // If the new JPG is smaller than the original PNG.
916 if ( $jpg_size && $new_size > $jpg_size && ewww_image_optimizer_mimetype( $jpgfile, 'i' ) === 'image/jpeg' ) {
917 // Store the size of the JPG as the new filesize.
918 $new_size = $jpg_size;
919 // If the user wants originals delted after a conversion.
920 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) ) {
921 // Delete the original PNG.
922 ewwwio_delete_file( $file );
923 }
924 // Update the $file location to the new JPG.
925 $file = $jpgfile;
926 // Let webp know what we're dealing with now.
927 $type = 'image/jpeg';
928 // Successful conversion, so we store the increment.
929 $converted = true;
930 } else {
931 $converted = false;
932 if ( ewwwio_is_file( $jpgfile ) ) {
933 // Otherwise delete the new JPG.
934 ewwwio_delete_file( $jpgfile );
935 }
936 }
937 } // End if().
938 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['cwebp'], $orig_size !== $new_size );
939 break;
940 case 'image/gif':
941 // If gif2png is turned on, and the image is in the WordPress media library.
942 if (
943 empty( ewwwio()->webp_only ) &&
944 1 === (int) $gallery_type &&
945 $fullsize &&
946 empty( $ewww_image->converted ) &&
947 ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_to_png' ) || ! empty( $ewww_convert ) ) &&
948 ! ewww_image_optimizer_is_animated( $file )
949 ) {
950 // Generate the filename for a PNG:
951 // if this is a resize version...
952 if ( $converted ) {
953 // just change the file extension.
954 $pngfile = preg_replace( '/\.\w+$/', '.png', $file );
955 } else {
956 // If this is the full version...
957 // construct the filename for the new PNG.
958 $pngfile = ewww_image_optimizer_unique_filename( $file, '.png' );
959 }
960 } else {
961 // Turn conversion OFF.
962 $convert = false;
963 $pngfile = '';
964 }
965 $compression_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' );
966 // Check for previous optimization, so long as the force flag is on and this isn't a new image that needs converting.
967 if ( empty( ewwwio()->force ) && ! ( $new_image && $convert ) && empty( ewwwio()->webp_only ) ) {
968 $results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
969 $smart_reopt = ! empty( ewwwio()->force_smart ) && ewww_image_optimizer_level_mismatch( $ewww_image->level, $compression_level ) ? true : false;
970 if ( $smart_reopt ) {
971 ewwwio_debug_message( "smart re-opt found level mismatch for $file, db says " . $ewww_image->level . " vs. current $compression_level" );
972 // If the current compression level is less than what was previously used, and the previous level was premium (or premium plus).
973 if ( $compression_level && $compression_level < $ewww_image->level && $ewww_image->level > 20 ) {
974 ewwwio_debug_message( "smart re-opt triggering restoration for $file" );
975 ewww_image_optimizer_cloud_restore_single_image( $ewww_image->record );
976 }
977 } elseif ( $results_msg ) {
978 return array( $file, $results_msg, $converted, $original );
979 }
980 }
981 $ewww_image->level = $compression_level;
982 if ( empty( ewwwio()->webp_only ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && 10 === $compression_level ) {
983 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type, $convert, $pngfile, 'image/png', $skip_lossy );
984 if ( $converted ) {
985 // Check to see if the user wants the originals deleted.
986 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) ) {
987 // Delete the original GIF.
988 ewwwio_delete_file( $original );
989 }
990 $converted = true;
991 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, 'image/png', null, $orig_size !== $new_size );
992 } else {
993 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, null, $orig_size !== $new_size );
994 }
995 break;
996 }
997 $tools['gifsicle'] = ewwwio()->local->get_path( 'gifsicle' );
998 if ( $convert ) {
999 // NOTE: we can only do local WebP if a GIF is converted to PNG.
1000 $tools['cwebp'] = ewwwio()->local->get_path( 'cwebp' );
1001 $tools['optipng'] = ewwwio()->local->get_path( 'optipng' );
1002 $tools['pngout'] = ewwwio()->local->get_path( 'pngout' );
1003 $tools['pngquant'] = ewwwio()->local->get_path( 'pngquant' );
1004 }
1005 // Check if we can (and should) do local GIF optimization.
1006 if ( ! empty( ewwwio()->webp_only ) ) {
1007 // This is for WebP-only mode, no conversion/optimization, and it'll be done via API.
1008 $webp_result = ewww_image_optimizer_webp_create( $file, $orig_size, $type, null );
1009 break;
1010 } elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_gif_level' ) ) {
1011 $result = __( 'GIF optimization is disabled', 'ewww-image-optimizer' );
1012 // If utility checking is on, and gifsicle is not installed.
1013 } elseif ( empty( $tools['gifsicle'] ) ) {
1014 /* translators: %s: name of a tool like jpegtran */
1015 $result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>gifsicle</em>' );
1016 } else {
1017 // Otherwise, turn optimization ON.
1018 $optimize = true;
1019 }
1020 // If local optimization is turned ON.
1021 if ( $optimize ) {
1022 $tempfile = $file . '.tmp'; // temporary GIF output.
1023 // Run gifsicle on the GIF.
1024 $cmd = "$nice " . $tools['gifsicle'] . ' -O3 --careful -o ' . ewww_image_optimizer_escapeshellarg( $tempfile ) . ' ' . ewww_image_optimizer_escapeshellarg( $file );
1025 ewwwio_debug_message( "running: $cmd" );
1026 exec( $cmd, $output, $exit );
1027 // Retrieve the filesize of the temporary GIF.
1028 $new_size = ewww_image_optimizer_filesize( $tempfile );
1029 // If the new GIF is smaller.
1030 if ( $new_size && $orig_size > $new_size && ewww_image_optimizer_mimetype( $tempfile, 'i' ) === $type ) {
1031 // Replace the original with the optimized file.
1032 rename( $tempfile, $file );
1033 // Store the results of the optimization.
1034 $result = "$orig_size vs. $new_size";
1035 // If the optimization didn't produce a smaller GIF.
1036 } else {
1037 if ( ewwwio_is_file( $tempfile ) ) {
1038 // Delete the optimized file.
1039 ewwwio_delete_file( $tempfile );
1040 }
1041 // Store the results.
1042 $result = 'unchanged';
1043 $new_size = $orig_size;
1044 }
1045 }
1046 // Get the new filesize for the GIF.
1047 $new_size = ewww_image_optimizer_filesize( $file );
1048 // If conversion is ON and the GIF isn't animated.
1049 if ( $convert && ! ewww_image_optimizer_is_animated( $file ) ) {
1050 if ( empty( $new_size ) ) {
1051 $new_size = $orig_size;
1052 }
1053 // If optipng is enabled.
1054 if ( $tools['optipng'] ) {
1055 // Retrieve the optipng optimization level.
1056 $optipng_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_optipng_level' );
1057 if (
1058 ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) &&
1059 preg_match( '/0.7/', ewwwio()->local->test_binary( $tools['optipng'], 'optipng' ) ) &&
1060 ! $keep_metadata
1061 ) {
1062 $strip = '-strip all ';
1063 } else {
1064 $strip = '';
1065 }
1066 // Run optipng on the GIF file.
1067 $cmd = "$nice " . $tools['optipng'] . ' -out ' . ewww_image_optimizer_escapeshellarg( $pngfile ) . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $file );
1068 ewwwio_debug_message( "running: $cmd" );
1069 exec( $cmd, $output, $exit );
1070 }
1071 // If pngout is enabled.
1072 if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_disable_pngout' ) && $tools['pngout'] ) {
1073 // Retrieve the pngout optimization level.
1074 $pngout_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pngout_level' );
1075 // Run pngout on the GIF file directly (if optipng didn't work or isn't available).
1076 $cmd = "$nice " . $tools['pngout'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $file ) . ' ' . ewww_image_optimizer_escapeshellarg( $pngfile );
1077 // BUT, if $pngfile exists, which means optipng was successful at converting the GIF.
1078 if ( ewwwio_is_file( $pngfile ) ) {
1079 // Run pngout on the PNG file.
1080 $cmd = "$nice " . $tools['pngout'] . " -s$pngout_level -q " . ewww_image_optimizer_escapeshellarg( $pngfile );
1081 }
1082 ewwwio_debug_message( "running: $cmd" );
1083 exec( $cmd, $output, $exit );
1084 }
1085 // Retrieve the filesize of the PNG.
1086 $png_size = ewww_image_optimizer_filesize( $pngfile );
1087 // If the new PNG is smaller than the original GIF.
1088 if ( $png_size && $new_size > $png_size && ewww_image_optimizer_mimetype( $pngfile, 'i' ) === 'image/png' ) {
1089 // Store the PNG size as the new filesize.
1090 $new_size = $png_size;
1091 // If the user wants original GIFs deleted after successful conversion.
1092 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_delete_originals' ) ) {
1093 // Delete the original GIF.
1094 ewwwio_delete_file( $file );
1095 }
1096 // Update the $file location with the new PNG.
1097 $file = $pngfile;
1098 // Let webp know what we're dealing with now.
1099 $type = 'image/png';
1100 // Normally this would be at the end of the section, but we only want to do webp if the image was successfully converted to a png.
1101 $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, $tools['cwebp'], $orig_size !== $new_size );
1102 // Successful conversion, so we store the increment.
1103 $converted = true;
1104 } else {
1105 $converted = false;
1106 if ( ewwwio_is_file( $pngfile ) ) {
1107 ewwwio_delete_file( $pngfile );
1108 }
1109 }
1110 } // End if().
1111 break;
1112 case 'application/pdf':
1113 if ( ! empty( ewwwio()->webp_only ) ) {
1114 break;
1115 }
1116 $compression_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_pdf_level' );
1117 if ( empty( ewwwio()->force ) ) {
1118 $results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
1119 $smart_reopt = ! empty( ewwwio()->force_smart ) && ewww_image_optimizer_level_mismatch( $ewww_image->level, $compression_level ) ? true : false;
1120 if ( $smart_reopt ) {
1121 ewwwio_debug_message( "smart re-opt found level mismatch for $file, db says " . $ewww_image->level . " vs. current $compression_level" );
1122 // If the current compression level is less than what was previously used, and the previous level was premium (or premium plus).
1123 if ( $compression_level && $compression_level < $ewww_image->level && $ewww_image->level > 20 ) {
1124 ewwwio_debug_message( "smart re-opt triggering restoration for $file" );
1125 ewww_image_optimizer_cloud_restore_single_image( $ewww_image->record );
1126 }
1127 } elseif ( $results_msg ) {
1128 return array( $file, $results_msg, $converted, $original );
1129 }
1130 }
1131 $ewww_image->level = $compression_level;
1132 if ( $compression_level > 0 ) {
1133 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
1134 }
1135 break;
1136 case 'image/svg+xml':
1137 if ( ! empty( ewwwio()->webp_only ) ) {
1138 break;
1139 }
1140 $compression_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_svg_level' );
1141 // Check for previous optimization, so long as the force flag is not on and this isn't a new image that needs converting.
1142 if ( empty( ewwwio()->force ) ) {
1143 $results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
1144 $smart_reopt = ! empty( ewwwio()->force_smart ) && ewww_image_optimizer_level_mismatch( $ewww_image->level, $compression_level ) ? true : false;
1145 if ( $smart_reopt ) {
1146 ewwwio_debug_message( "smart re-opt found level mismatch for $file, db says " . $ewww_image->level . " vs. current $compression_level" );
1147 // If the current compression level is less than what was previously used, and the previous level was premium (or premium plus).
1148 if ( $compression_level && $compression_level < $ewww_image->level && $ewww_image->level > 0 ) {
1149 ewwwio_debug_message( "smart re-opt triggering restoration for $file" );
1150 ewww_image_optimizer_cloud_restore_single_image( $ewww_image->record );
1151 }
1152 } elseif ( $results_msg ) {
1153 return array( $file, $results_msg, $converted, $original );
1154 }
1155 }
1156 $ewww_image->level = $compression_level;
1157 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) && $compression_level > 0 ) {
1158 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
1159 break;
1160 }
1161 $tools['svgcleaner'] = ewwwio()->local->get_path( 'svgcleaner' );
1162 // If svgcleaner is disabled.
1163 if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_svg_level' ) ) {
1164 $result = __( 'SVG optimization is disabled', 'ewww-image-optimizer' );
1165 } elseif ( empty( $tools['svgcleaner'] ) ) {
1166 /* translators: %s: name of a tool like jpegtran */
1167 $result = sprintf( __( '%s is missing', 'ewww-image-optimizer' ), '<em>svgcleaner</em>' );
1168 } else {
1169 // Otherwise, turn optimization ON.
1170 $optimize = true;
1171 }
1172 // If local optimization is turned ON.
1173 if ( $optimize ) {
1174 $tempfile = $file . '.tmp.svg'; // temporary SVG output (must end with .svg)
1175 // Run svgcleaner on the SVG.
1176 $svgcleaner_options = array(
1177 '--allow-bigger-file',
1178 '--quiet',
1179 );
1180 if ( 1 === $compression_level ) {
1181 array_push(
1182 $svgcleaner_options,
1183 '--paths-to-relative=no',
1184 '--remove-unused-segments=no',
1185 '--convert-segments=no',
1186 '--merge-gradients=no',
1187 '--trim-ids=no',
1188 '--trim-colors=no',
1189 '--simplify-transforms=no',
1190 '--resolve-use=no'
1191 );
1192 }
1193 $cmd = "$nice " . $tools['svgcleaner'] . ' ' . implode( ' ', $svgcleaner_options ) . ' ' . ewww_image_optimizer_escapeshellarg( $file ) . ' ' . ewww_image_optimizer_escapeshellarg( $tempfile );
1194 ewwwio_debug_message( "running: $cmd" );
1195 exec( $cmd, $output, $exit );
1196 // Retrieve the filesize of the temporary SVG.
1197 $new_size = ewww_image_optimizer_filesize( $tempfile );
1198 // If the new SVG is smaller.
1199 if ( $new_size && $orig_size > $new_size && ewww_image_optimizer_mimetype( $tempfile, 'i' ) === $type ) {
1200 // Replace the original with the optimized file.
1201 rename( $tempfile, $file );
1202 // Store the results of the optimization.
1203 $result = "$orig_size vs. $new_size";
1204 // If the optimization didn't produce a smaller SVG.
1205 } else {
1206 if ( ewwwio_is_file( $tempfile ) ) {
1207 // Delete the optimized file.
1208 ewwwio_delete_file( $tempfile );
1209 }
1210 // Store the results.
1211 $result = 'unchanged';
1212 $new_size = $orig_size;
1213 }
1214 }
1215 break;
1216 case 'image/webp':
1217 if ( ! empty( ewwwio()->webp_only ) ) {
1218 break;
1219 }
1220 $compression_level = (int) ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_level' );
1221 if ( empty( ewwwio()->force ) ) {
1222 $results_msg = ewww_image_optimizer_check_table( $file, $orig_size );
1223 if ( $results_msg ) {
1224 return array( $file, $results_msg, $converted, $original );
1225 }
1226 }
1227 $ewww_image->level = $compression_level;
1228 if ( $compression_level > 0 ) {
1229 list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
1230 }
1231 break;
1232 default:
1233 // If not a JPG, PNG, GIF, PDF or SVG tell the user we don't work with strangers.
1234 return array( false, __( 'Unsupported file type', 'ewww-image-optimizer' ) . ": $type", $converted, $original );
1235 } // End switch().
1236 // Allow other plugins to run operations on the images after optimization.
1237 // NOTE: it is recommended to do any image modifications prior to optimization, otherwise you risk un-optimizing your images here.
1238 do_action( 'ewww_image_optimizer_post_optimization', $file, $type, $fullsize );
1239 // If their cloud api license limit has been exceeded.
1240 if ( 'exceeded' === $result ) {
1241 return array( false, __( 'License exceeded', 'ewww-image-optimizer' ), $converted, $original );
1242 } elseif ( 'exceeded subkey' === $result ) {
1243 return array( false, __( 'Out of credits', 'ewww-image-optimizer' ), $converted, $original );
1244 } elseif ( 'exceeded quota' === $result ) {
1245 return array( false, __( 'Soft Quota Reached', 'ewww-image-optimizer' ), $converted, $original );
1246 }
1247 if ( ! empty( $new_size ) ) {
1248 // Set correct file permissions.
1249 $stat = stat( dirname( $file ) );
1250 ewwwio_debug_message( 'folder mode: ' . $stat['mode'] );
1251 $perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
1252 ewwwio_debug_message( "attempting chmod with $perms" );
1253 ewwwio_chmod( $file, $perms );
1254
1255 $results_msg = ewww_image_optimizer_update_table( $file, $new_size, $orig_size, $original, $backup_hash );
1256 if ( ! empty( $webp_result ) ) {
1257 $results_msg .= '<br>' . $webp_result;
1258 }
1259 ewwwio_memory( __FUNCTION__ );
1260 return array( $file, $results_msg, $converted, $original );
1261 }
1262 ewwwio_memory( __FUNCTION__ );
1263 // If this is WebP-only mode, and we got a WebP file.
1264 if ( ! empty( $webp_result ) && ! empty( ewwwio()->webp_only ) ) {
1265 $result = $webp_result;
1266 return array( true, $result, $converted, $original );
1267 }
1268 // Otherwise, send back the filename, the results (some sort of error message), the $converted flag, and the name of the original image.
1269 return array( false, $result, $converted, $original );
1270 }
1271
1272 /**
1273 * Returns the WebP file path of the given image based on naming mode.
1274 *
1275 * @param string $file The full filesystem path to the source image.
1276 * @return string The full filesystem path to the WebP image.
1277 */
1278 function ewww_image_optimizer_get_webp_path( $file ) {
1279 $naming_mode = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_naming_mode', 'append' );
1280 $info = pathinfo( $file );
1281
1282 if ( 'replace' === $naming_mode ) {
1283 if ( empty( $info['dirname'] ) || '.' === $info['dirname'] ) {
1284 $info['dirname'] = '';
1285 } else {
1286 $info['dirname'] = trailingslashit( $info['dirname'] );
1287 }
1288 $webp_path = $info['dirname'] . $info['filename'] . '.webp';
1289 } else {
1290 $webp_path = $file . '.webp';
1291 }
1292
1293 return apply_filters( 'ewww_image_optimizer_webp_path', $webp_path, $file, $naming_mode );
1294 }
1295
1296 /**
1297 * Returns all possible WebP file paths of the given image.
1298 *
1299 * @param string $path The full filesystem path to the source image.
1300 * @return array Returns array of both append and replace WebP naming paths.
1301 */
1302 function ewww_image_optimizer_get_all_webp_paths( $path ) {
1303 if ( empty( $path ) ) {
1304 return array( '', '' );
1305 }
1306 $naming_mode = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_naming_mode', 'append' );
1307 $append = $path . '.webp';
1308 $info = pathinfo( $path );
1309 if ( empty( $info['dirname'] ) || '.' === $info['dirname'] ) {
1310 $info['dirname'] = '';
1311 } else {
1312 $info['dirname'] = trailingslashit( $info['dirname'] );
1313 }
1314 $replace = $info['dirname'] . $info['filename'] . '.webp';
1315
1316 if ( 'append' === $naming_mode ) {
1317 return array( $append, $replace );
1318 }
1319 return array( $replace, $append );
1320 }
1321
1322 /**
1323 * Removes obsolete WebP files created with previous naming conventions.
1324 *
1325 * @param string $path The full filesystem path to the source image.
1326 */
1327 function ewww_image_optimizer_cleanup_legacy_webp( $path ) {
1328 $current = ewww_image_optimizer_get_webp_path( $path );
1329 $variants = ewww_image_optimizer_get_all_webp_paths( $path );
1330
1331 foreach ( $variants as $legacy ) {
1332 if ( $legacy !== $current && ewwwio_is_file( $legacy ) ) {
1333 ewwwio_debug_message( "removing legacy webp file: $legacy" );
1334 ewwwio_delete_file( $legacy );
1335 }
1336 }
1337 }
1338
1339 /**
1340 * Returns the WebP URL of the given image.
1341 *
1342 * @param string $path The full filesystem path to the source image.
1343 * @param string $url The full URL to the source image.
1344 * @return string URL to the existing WebP image.
1345 */
1346 function ewww_image_optimizer_get_webp_url( $path, $url ) {
1347 ewwwio_debug_message( "finding .webp path for source path $path and url $url" );
1348 $webp_urls = ewww_image_optimizer_get_all_webp_paths( $url );
1349 if ( empty( $path ) ) {
1350 ewwwio_debug_message( "no path, returning {$webp_urls[0]}" );
1351 return $webp_urls[0];
1352 }
1353 $webp_paths = ewww_image_optimizer_get_all_webp_paths( $path );
1354
1355 if ( ewwwio_is_file( $webp_paths[0] ) ) {
1356 ewwwio_debug_message( "{$webp_paths[0]} found, returning {$webp_urls[0]}" );
1357 return $webp_urls[0];
1358 } elseif ( ewwwio_is_file( $webp_paths[1] ) ) {
1359 ewwwio_debug_message( "{$webp_paths[1]} found, returning {$webp_urls[1]}" );
1360 return $webp_urls[1];
1361 }
1362 ewwwio_debug_message( "no local file found, returning {$webp_urls[0]}" );
1363 return $webp_urls[0];
1364 }
1365
1366 /**
1367 * Creates WebP images alongside JPG and PNG files.
1368 *
1369 * @param string $file The name of the JPG/PNG file.
1370 * @param int $orig_size The filesize of the JPG/PNG file.
1371 * @param string $type The mime-type of the incoming file.
1372 * @param string $tool The path to the cwebp binary, if installed.
1373 * @param bool $recreate True to re-generate the .webp image even if one exists, usually because the source image has been modified.
1374 * @return string Results of the WebP operation for display.
1375 */
1376 function ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tool, $recreate = false ) {
1377 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1378 $orig_size = ewww_image_optimizer_filesize( $file );
1379 $webpfile = ewww_image_optimizer_get_webp_path( $file );
1380 if ( apply_filters( 'ewww_image_optimizer_bypass_webp', false, $file ) ) {
1381 ewwwio_debug_message( "webp generation bypassed: $file" );
1382 return '';
1383 } elseif ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ) {
1384 return '';
1385 } elseif ( ! ewwwio_is_file( $file ) ) {
1386 ewwwio_debug_message( 'original file not found' );
1387 return esc_html__( 'Could not find file.', 'ewww-image-optimizer' );
1388 } elseif ( ! is_writable( $file ) ) {
1389 ewwwio_debug_message( 'original file not writable' );
1390 ewww_image_optimizer_update_webp_results( $file, 0, 2 );
1391 return ewww_image_optimizer_webp_error_message( 2 );
1392 } elseif ( ewwwio_is_file( $webpfile ) && empty( ewwwio()->force ) && ! $recreate ) {
1393 ewwwio_debug_message( 'webp file exists, not forcing or recreating' );
1394 return esc_html__( 'WebP image already exists.', 'ewww-image-optimizer' );
1395 } elseif ( 'image/png' === $type && ewww_image_optimizer_is_animated_png( $file ) ) {
1396 ewwwio_debug_message( 'APNG found, WebP not possible' );
1397 ewww_image_optimizer_update_webp_results( $file, 0, 3 );
1398 return ewww_image_optimizer_webp_error_message( 3 );
1399 }
1400 list( $width, $height ) = ewwwio()->getimagesize( $file );
1401 if ( $width > 16383 || $height > 16383 ) {
1402 ewww_image_optimizer_update_webp_results( $file, 0, 4 );
1403 return ewww_image_optimizer_webp_error_message( 4 );
1404 }
1405 if ( empty( $tool ) || 'image/gif' === $type ) {
1406 $use_cloud_webp = false;
1407 if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
1408 $use_cloud_webp = true;
1409 if (
1410 'local' === ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_conversion_method' ) &&
1411 'image/gif' !== $type &&
1412 ewwwio()->imagick_supports_webp()
1413 ) {
1414 $use_cloud_webp = false;
1415 }
1416 }
1417 if ( $use_cloud_webp ) {
1418 ewww_image_optimizer_cloud_optimizer( $file, $type, false, $webpfile, 'image/webp' );
1419 } elseif ( ewwwio()->imagick_supports_webp() ) {
1420 ewww_image_optimizer_imagick_create_webp( $file, $type, $webpfile );
1421 } elseif ( ewwwio()->gd_supports_webp() ) {
1422 ewww_image_optimizer_gd_create_webp( $file, $type, $webpfile );
1423 } else {
1424 ewww_image_optimizer_cloud_optimizer( $file, $type, false, $webpfile, 'image/webp' );
1425 }
1426 } elseif ( ewwwio()->imagick_supports_webp() ) {
1427 // Because we prefer Imagick for sharpening/quality over cwebp.
1428 ewww_image_optimizer_imagick_create_webp( $file, $type, $webpfile );
1429 } else {
1430 $nice = '';
1431 if ( PHP_OS !== 'WINNT' && ! ewwwio()->cloud_mode && ewwwio()->local->exec_check() ) {
1432 // Check to see if 'nice' exists.
1433 $nice = ewwwio()->local->find_nix_binary( 'nice' );
1434 }
1435 // Check to see if we are supposed to strip metadata.
1436 $copy_opt = ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) ? 'icc' : 'all';
1437 $quality = (int) apply_filters( 'webp_quality', 75, 'image/webp' );
1438 if ( $quality < 50 || $quality > 100 ) {
1439 $quality = 75;
1440 }
1441 $sharp_yuv = defined( 'EIO_WEBP_SHARP_YUV' ) && EIO_WEBP_SHARP_YUV ? '-sharp_yuv' : '';
1442 if ( empty( $sharp_yuv ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_sharpen' ) ) {
1443 $sharp_yuv = '-sharp_yuv';
1444 }
1445 $lossless = '-lossless';
1446 if ( defined( 'EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP' ) && EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP ) {
1447 $lossless = "-q $quality $sharp_yuv";
1448 }
1449 switch ( $type ) {
1450 case 'image/jpeg':
1451 $resize_string = '';
1452 $source_image = $file;
1453 global $ewww_image;
1454 if ( ! empty( $ewww_image->attachment_id ) ) {
1455 $original_image = ewwwio_get_original_image_path_from_thumb( $file, $ewww_image->attachment_id );
1456 if ( $original_image ) {
1457 $resize_string = ewww_image_optimizer_get_cwebp_resize_params( $file );
1458 if ( $resize_string && ewwwio_is_file( $original_image ) ) {
1459 $source_image = $original_image;
1460 }
1461 }
1462 }
1463 ewwwio_debug_message( "$nice " . $tool . " -q $quality $sharp_yuv $resize_string -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $source_image ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1' );
1464 exec( "$nice " . $tool . " -q $quality $sharp_yuv $resize_string -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $source_image ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1', $cli_output );
1465 if ( ! ewwwio_is_file( $webpfile ) && ewwwio()->imagick_supports_webp() && ewww_image_optimizer_is_cmyk( $source_image ) ) {
1466 ewwwio_debug_message( 'cmyk image skipped, trying imagick' );
1467 ewww_image_optimizer_imagick_create_webp( $file, $type, $webpfile );
1468 } elseif ( ewwwio_is_file( $webpfile ) && 'image/webp' !== ewww_image_optimizer_mimetype( $webpfile, 'i' ) ) {
1469 ewwwio_debug_message( 'non-webp file produced' );
1470 }
1471 break;
1472 case 'image/png':
1473 ewwwio_debug_message( "$nice " . $tool . " $lossless -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1' );
1474 exec( "$nice " . $tool . " $lossless -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1', $cli_output );
1475 break;
1476 }
1477 }
1478 $webp_size = ewww_image_optimizer_filesize( $webpfile );
1479 ewwwio_debug_message( "webp is $webp_size vs. $type is $orig_size" );
1480 if ( ewwwio_is_file( $webpfile ) && $orig_size < $webp_size && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
1481 ewwwio_debug_message( 'webp file was too big, deleting' );
1482 ewwwio_delete_file( $webpfile );
1483 ewww_image_optimizer_update_webp_results( $file, 0, 5 );
1484 return ewww_image_optimizer_webp_error_message( 5 );
1485 } elseif ( ewwwio_is_file( $webpfile ) && 'image/webp' === ewww_image_optimizer_mimetype( $webpfile, 'i' ) ) {
1486 // Set correct file permissions.
1487 $stat = stat( dirname( $webpfile ) );
1488 $perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
1489 ewwwio_chmod( $webpfile, $perms );
1490 ewww_image_optimizer_update_webp_results( $file, $webp_size );
1491 if ( $orig_size < $webp_size && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_force' ) ) {
1492 return esc_html__( 'WebP image larger than original, saved anyway with Force WebP option.', 'ewww-image-optimizer' );
1493 }
1494 return 'WebP: ' . ewww_image_optimizer_image_results( $orig_size, $webp_size );
1495 } elseif ( ewwwio_is_file( $webpfile ) ) {
1496 ewwwio_debug_message( 'webp file mimetype did not validate, deleting' );
1497 ewwwio_delete_file( $webpfile );
1498 ewww_image_optimizer_update_webp_results( $file, 0, 6 );
1499 return ewww_image_optimizer_webp_error_message( 6 );
1500 }
1501 ewww_image_optimizer_update_webp_results( $file, 0, 1 );
1502 return ewww_image_optimizer_webp_error_message( 1 );
1503 }
1504
1505 /**
1506 * Get resize/crop parameters for cwebp when converting a thumbnail image.
1507 *
1508 * @param string $file The path of the thumb to be converted.
1509 * @return string CLI args to use for resizing a thumb from the original, or an empty string.
1510 */
1511 function ewww_image_optimizer_get_cwebp_resize_params( $file ) {
1512 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1513 $resize_params = '';
1514 list( $webp_width, $webp_height, $webp_crop, $fullsize_image ) = ewww_image_optimizer_get_webp_resize_params( $file );
1515 if ( $webp_width && $webp_height && $fullsize_image ) {
1516 ewwwio_debug_message( 'building resize params' );
1517 if ( $webp_crop ) {
1518 ewwwio_debug_message( 'cropping' );
1519 list( $full_width, $full_height ) = ewwwio()->getimagesize( $fullsize_image );
1520 if ( ! empty( $full_width ) && ! empty( $full_height ) ) {
1521 ewwwio_debug_message( 'found full-size dims' );
1522 $dims = image_resize_dimensions( $full_width, $full_height, $webp_width, $webp_height, $webp_crop );
1523 if ( $dims && is_array( $dims ) ) {
1524 ewwwio_debug_message( 'image_resize_dimensions() returned: ' . implode( ', ', $dims ) );
1525 list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
1526 // Build it with both crop and resize args, the crop will chop off the edges for the needed aspect ratio, then resize scales it (if needed).
1527 $resize_params = "-resize $webp_width $webp_height -crop $src_x $src_y $src_w $src_h";
1528 }
1529 }
1530 } else {
1531 ewwwio_debug_message( 'scaling' );
1532 $resize_params = "-resize $webp_width $webp_height";
1533 }
1534 ewwwio_debug_message( "final CLI resize args: $resize_params" );
1535 }
1536 return $resize_params;
1537 }
1538
1539 /**
1540 * Get an error message for the given WebP status code.
1541 *
1542 * @param int $webp_code The error code for WebP conversion.
1543 * @return string The error message for the given status code.
1544 */
1545 function ewww_image_optimizer_webp_error_message( $webp_code = 0 ) {
1546 switch ( $webp_code ) {
1547 case 1:
1548 return __( 'Image could not be converted to WebP.', 'ewww-image-optimizer' );
1549 case 2:
1550 return __( 'File is not writable.', 'ewww-image-optimizer' );
1551 case 3:
1552 return __( 'APNG cannot be converted to WebP.', 'ewww-image-optimizer' );
1553 case 4:
1554 return __( 'Image dimensions too large for WebP conversion.', 'ewww-image-optimizer' );
1555 case 5:
1556 return __( 'WebP image was larger than original.', 'ewww-image-optimizer' );
1557 case 6:
1558 return __( 'WebP conversion error.', 'ewww-image-optimizer' );
1559 default:
1560 return '';
1561 }
1562 }
1563
1564 /**
1565 * Redirects back to previous page after PNGOUT installation.
1566 */
1567 function ewww_image_optimizer_install_pngout_wrapper() {
1568 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1569 check_admin_referer( 'ewww_image_optimizer_options-options' );
1570 if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_admin_permissions', '' ) ) ) {
1571 wp_die( esc_html__( 'You do not have permission to install image optimizer utilities.', 'ewww-image-optimizer' ) );
1572 }
1573 $sendback = ewww_image_optimizer_install_pngout();
1574 wp_safe_redirect( $sendback );
1575 ewwwio_memory( __FUNCTION__ );
1576 exit( 0 );
1577 }
1578
1579 /**
1580 * Installs pngout from the official site.
1581 *
1582 * @return string The url from whence we came (settings page), with success or error parameters added.
1583 */
1584 function ewww_image_optimizer_install_pngout() {
1585 if ( ! extension_loaded( 'zlib' ) || ! class_exists( 'PharData' ) ) {
1586 $pngout_error = __( 'zlib or phar extension missing from PHP', 'ewww-image-optimizer' );
1587 }
1588 if ( PHP_OS === 'Linux' ) {
1589 $os_string = 'linux';
1590 }
1591 if ( PHP_OS === 'FreeBSD' ) {
1592 $os_string = 'bsd';
1593 }
1594 $latest = '20200115';
1595 $tool_path = trailingslashit( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
1596 if ( empty( $pngout_error ) ) {
1597 if ( PHP_OS === 'Linux' || PHP_OS === 'FreeBSD' ) {
1598 $download_result = download_url( 'http://www.jonof.id.au/files/kenutils/pngout-' . $latest . '-' . $os_string . '-static.tar.gz' );
1599 if ( is_wp_error( $download_result ) ) {
1600 $pngout_error = $download_result->get_error_message();
1601 } else {
1602 if ( ! ewwwio_check_memory_available( filesize( $download_result ) + 1000 ) ) {
1603 $pngout_error = __( 'insufficient memory available for installation', 'ewww-image-optimizer' );
1604 } else {
1605 $arch_type = 'i686';
1606 if ( ewww_image_optimizer_function_exists( 'php_uname' ) ) {
1607 $arch_type = php_uname( 'm' );
1608 if ( 'x86_64' === $arch_type ) {
1609 $arch_type = 'amd64';
1610 }
1611 }
1612
1613 $tmpname = current( explode( '.', $download_result ) );
1614 $tmpname .= '-' . uniqid() . '.tar.gz';
1615 rename( $download_result, $tmpname );
1616 $download_result = $tmpname;
1617
1618 $pngout_gzipped = new PharData( $download_result );
1619 $pngout_tarball = $pngout_gzipped->decompress();
1620 $download_result = $pngout_tarball->getPath();
1621 $pngout_tarball->extractTo(
1622 EWWW_IMAGE_OPTIMIZER_BINARY_PATH,
1623 'pngout-' . $latest . '-' . $os_string . '-static/' . $arch_type . '/pngout-static',
1624 true
1625 );
1626
1627 if ( ewwwio_is_file( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-' . $os_string . '-static/' . $arch_type . '/pngout-static' ) ) {
1628 if ( ! rename( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-' . $os_string . '-static/' . $arch_type . '/pngout-static', $tool_path . 'pngout-static' ) ) {
1629 if ( empty( $pngout_error ) ) {
1630 $pngout_error = __( 'could not move pngout', 'ewww-image-optimizer' );
1631 }
1632 }
1633 if ( ! chmod( $tool_path . 'pngout-static', 0755 ) ) {
1634 if ( empty( $pngout_error ) ) {
1635 $pngout_error = __( 'could not set permissions', 'ewww-image-optimizer' );
1636 }
1637 }
1638 $pngout_version = ewwwio()->local->test_binary( ewww_image_optimizer_escapeshellarg( $tool_path ) . 'pngout-static', 'pngout' );
1639 } else {
1640 $pngout_error = __( 'extraction of files failed', 'ewww-image-optimizer' );
1641 }
1642 }
1643 }
1644 } elseif ( PHP_OS === 'Darwin' ) {
1645 $latest = '20200115';
1646 $os_ext = 'tar.gz';
1647 $os_ext = 'zip';
1648 $download_result = download_url( 'http://www.jonof.id.au/files/kenutils/pngout-' . $latest . '-macos.' . $os_ext );
1649 if ( is_wp_error( $download_result ) ) {
1650 $pngout_error = $download_result->get_error_message();
1651 } else {
1652 if ( ! ewwwio_check_memory_available( filesize( $download_result ) + 1000 ) ) {
1653 $pngout_error = __( 'insufficient memory available for installation', 'ewww-image-optimizer' );
1654 } else {
1655 $tmpname = current( explode( '.', $download_result ) );
1656 $tmpname .= '-' . uniqid() . '.' . $os_ext;
1657 rename( $download_result, $tmpname );
1658 $download_result = $tmpname;
1659
1660 if ( 'zip' === $os_ext ) {
1661 WP_Filesystem();
1662 $unzipped = unzip_file(
1663 $download_result,
1664 EWWW_IMAGE_OPTIMIZER_BINARY_PATH
1665 );
1666 } else {
1667 $pngout_gzipped = new PharData( $download_result );
1668 $pngout_tarball = $pngout_gzipped->decompress();
1669 $download_result = $pngout_tarball->getPath();
1670 $pngout_tarball->extractTo(
1671 EWWW_IMAGE_OPTIMIZER_BINARY_PATH,
1672 'pngout-' . $latest . '-darwin/pngout',
1673 true
1674 );
1675 }
1676 if ( ewwwio_is_file( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-macos/pngout' ) ) {
1677 if ( ! rename( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . 'pngout-' . $latest . '-macos/pngout', $tool_path . 'pngout-static' ) ) {
1678 if ( empty( $pngout_error ) ) {
1679 $pngout_error = __( 'could not move pngout', 'ewww-image-optimizer' );
1680 }
1681 }
1682 if ( ! chmod( $tool_path . 'pngout-static', 0755 ) ) {
1683 if ( empty( $pngout_error ) ) {
1684 $pngout_error = __( 'could not set permissions', 'ewww-image-optimizer' );
1685 }
1686 }
1687 $pngout_version = ewwwio()->local->test_binary( ewww_image_optimizer_escapeshellarg( $tool_path ) . 'pngout-static', 'pngout' );
1688 } elseif ( ! empty( $unzipped ) && is_wp_error( $unzipped ) ) {
1689 $pngout_error = $unzipped->get_error_message();
1690 } else {
1691 $pngout_error = __( 'extraction of files failed', 'ewww-image-optimizer' );
1692 }
1693 }
1694 }
1695 }
1696 } // End if().
1697 if ( PHP_OS === 'WINNT' ) {
1698 $download_result = download_url( 'http://advsys.net/ken/util/pngout.exe' );
1699 if ( is_wp_error( $download_result ) ) {
1700 $pngout_error = $download_result->get_error_message();
1701 } else {
1702 if ( ! rename( $download_result, $tool_path . 'pngout.exe' ) ) {
1703 if ( empty( $pngout_error ) ) {
1704 $pngout_error = __( 'could not move pngout', 'ewww-image-optimizer' );
1705 }
1706 }
1707 $pngout_version = ewwwio()->local->test_binary( '"' . $tool_path . 'pngout.exe"', 'pngout' );
1708 }
1709 }
1710 if ( is_string( $download_result ) && is_writable( $download_result ) ) {
1711 unlink( $download_result );
1712 }
1713 if ( ! empty( $pngout_version ) ) {
1714 $sendback = add_query_arg( 'ewww_pngout', 'success', remove_query_arg( array( 'ewww_pngout', 'ewww_error' ), wp_get_referer() ) );
1715 }
1716 if ( ! isset( $sendback ) ) {
1717 $sendback = add_query_arg(
1718 array(
1719 'ewww_pngout' => 'failed',
1720 'ewww_error' => urlencode( $pngout_error ),
1721 ),
1722 remove_query_arg( array( 'ewww_pngout', 'ewww_error' ), wp_get_referer() )
1723 );
1724 }
1725 return $sendback;
1726 }
1727
1728 /**
1729 * Redirects back to previous page after SVGCLEANER installation.
1730 */
1731 function ewww_image_optimizer_install_svgcleaner_wrapper() {
1732 ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
1733 check_admin_referer( 'ewww_image_optimizer_options-options' );
1734 if ( ! current_user_can( apply_filters( 'ewww_image_optimizer_admin_permissions', '' ) ) ) {
1735 wp_die( esc_html__( 'You do not have permission to install image optimizer utilities.', 'ewww-image-optimizer' ) );
1736 }
1737 $sendback = ewww_image_optimizer_install_svgcleaner();
1738 wp_safe_redirect( $sendback );
1739 ewwwio_memory( __FUNCTION__ );
1740 exit( 0 );
1741 }
1742
1743 /**
1744 * Installs svgcleaner from the official site.
1745 *
1746 * @return string The url from whence we came (settings page), with success or error parameters added.
1747 */
1748 function ewww_image_optimizer_install_svgcleaner() {
1749 if ( ! extension_loaded( 'zlib' ) || ! class_exists( 'PharData' ) ) {
1750 $download_error = __( 'zlib or phar extension missing from PHP', 'ewww-image-optimizer' );
1751 }
1752 if ( ! ewwwio()->local->exec_check() ) {
1753 $download_error = __( 'Your web server does not meet the requirements for free server-based compression with EWWW Image Optimizer.', 'ewww-image-optimizer' );
1754 }
1755 $os_chmod = true;
1756 $os_binary = 'svgcleaner';
1757 $os_ext = 'tar.gz';
1758 if ( PHP_OS === 'Linux' ) {
1759 $arch_type = 'x86_64';
1760 if ( ewww_image_optimizer_function_exists( 'php_uname' ) ) {
1761 if ( php_uname( 'm' ) !== $arch_type ) {
1762 $download_error = __( 'svgcleaner on Linux only supports the x86_64 architecture', 'ewww-image-optimizer' );
1763 }
1764 }
1765 $os_string = 'linux_' . $arch_type;
1766 } elseif ( PHP_OS === 'Darwin' ) {
1767 $os_string = 'macos';
1768 $os_ext = 'zip';
1769 } elseif ( PHP_OS === 'WINNT' ) {
1770 $os_chmod = false;
1771 $os_string = 'win32';
1772 $os_binary = 'svgcleaner.exe';
1773 $os_ext = 'zip';
1774 } elseif ( PHP_OS === 'FreeBSD' ) {
1775 $download_error = __( 'svgcleaner is not available for FreeBSD', 'ewww-image-optimizer' );
1776 }
1777 $latest = '0.9.5';
1778 $tool_path = trailingslashit( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
1779 if ( empty( $download_error ) ) {
1780 $download_result = download_url( 'https://github.com/RazrFalcon/svgcleaner/releases/download/v' . $latest . '/svgcleaner_' . $os_string . '_' . $latest . '.' . $os_ext );
1781 if ( is_wp_error( $download_result ) ) {
1782 $download_error = $download_result->get_error_message();
1783 } else {
1784 if ( ! ewwwio_check_memory_available( filesize( $download_result ) + 1000 ) ) {
1785 $download_error = __( 'insufficient memory available for installation', 'ewww-image-optimizer' );
1786 } else {
1787 $tmpname = current( explode( '.', $download_result ) );
1788 $tmpname .= '-' . uniqid() . '.' . $os_ext;
1789 rename( $download_result, $tmpname );
1790 $download_result = $tmpname;
1791
1792 if ( 'zip' === $os_ext ) {
1793 WP_Filesystem();
1794 $unzipped = unzip_file(
1795 $download_result,
1796 EWWW_IMAGE_OPTIMIZER_BINARY_PATH
1797 );
1798 if ( is_wp_error( $unzipped ) ) {
1799 $download_error = $unzipped->get_error_message();
1800 }
1801 } else {
1802 $pkg_gzipped = new PharData( $download_result );
1803 $pkg_tarball = $pkg_gzipped->decompress();
1804 $download_result = $pkg_tarball->getPath();
1805 $pkg_tarball->extractTo(
1806 EWWW_IMAGE_OPTIMIZER_BINARY_PATH,
1807 'svgcleaner',
1808 true
1809 );
1810 }
1811 if ( ewwwio_is_file( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . $os_binary ) ) {
1812 if ( ! rename( EWWW_IMAGE_OPTIMIZER_BINARY_PATH . $os_binary, $tool_path . $os_binary ) ) {
1813 if ( empty( $download_error ) ) {
1814 $download_error = __( 'could not move svgcleaner', 'ewww-image-optimizer' );
1815 }
1816 }
1817 if ( $os_chmod && ! chmod( $tool_path . $os_binary, 0755 ) ) {
1818 if ( empty( $download_error ) ) {
1819 $download_error = __( 'could not set permissions', 'ewww-image-optimizer' );
1820 }
1821 }
1822 if ( PHP_OS === 'WINNT' ) {
1823 $pkg_version = ewwwio()->local->test_binary( '"' . $tool_path . $os_binary . '"', 'svgcleaner' );
1824 } else {
1825 $pkg_version = ewwwio()->local->test_binary( ewww_image_optimizer_escapeshellarg( $tool_path ) . $os_binary, 'svgcleaner' );
1826 }
1827 } else {
1828 $download_error = __( 'extraction of files failed', 'ewww-image-optimizer' );
1829 }
1830 }
1831 }
1832 }
1833 if ( isset( $download_result ) && is_string( $download_result ) && is_writable( $download_result ) ) {
1834 unlink( $download_result );
1835 }
1836 if ( ! empty( $pkg_version ) ) {
1837 ewww_image_optimizer_set_option( 'ewww_image_optimizer_disable_svgcleaner', false );
1838 if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_svg_level' ) ) {
1839 ewww_image_optimizer_set_option( 'ewww_image_optimizer_svg_level', 10 );
1840 }
1841 $sendback = add_query_arg( 'ewww_svgcleaner', 'success', remove_query_arg( array( 'ewww_svgcleaner', 'ewww_error' ), wp_get_referer() ) );
1842 }
1843 if ( ! isset( $sendback ) ) {
1844 $sendback = add_query_arg(
1845 array(
1846 'ewww_svgcleaner' => 'failed',
1847 'ewww_error' => urlencode( $download_error ),
1848 ),
1849 remove_query_arg( array( 'ewww_svgcleaner', 'ewww_error' ), wp_get_referer() )
1850 );
1851 }
1852 return $sendback;
1853 }
1854
1855 /**
1856 * Checks availability of svgcleaner installer.
1857 */
1858 function ewww_image_optimizer_svgcleaner_installer_available() {
1859 if ( ! ewwwio()->local->exec_check() ) {
1860 return false;
1861 }
1862 if ( PHP_OS === 'Linux' ) {
1863 if ( ewww_image_optimizer_function_exists( 'php_uname' ) ) {
1864 if ( php_uname( 'm' ) !== 'x86_64' ) {
1865 return false;
1866 }
1867 }
1868 return true;
1869 } elseif ( PHP_OS === 'Darwin' ) {
1870 return true;
1871 } elseif ( PHP_OS === 'WINNT' ) {
1872 return true;
1873 }
1874 return false;
1875 }
1876
1877 /**
1878 * Removes any binaries that have been installed in the wp-content/ewww/ folder.
1879 */
1880 function ewww_image_optimizer_remove_binaries() {
1881 if ( ! class_exists( 'RecursiveIteratorIterator' ) ) {
1882 return;
1883 }
1884 if ( ! is_dir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ) ) {
1885 return;
1886 }
1887 $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( EWWW_IMAGE_OPTIMIZER_TOOL_PATH ), RecursiveIteratorIterator::CHILD_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD );
1888 foreach ( $iterator as $file ) {
1889 if ( $file->isFile() ) {
1890 $path = $file->getPathname();
1891 if ( strpos( $path, 'image-backup' ) ) {
1892 continue;
1893 }
1894 if ( is_writable( $path ) ) {
1895 unlink( $path );
1896 }
1897 }
1898 }
1899 if ( ! class_exists( 'FilesystemIterator' ) ) {
1900 return;
1901 }
1902 clearstatcache();
1903 $iterator = new FilesystemIterator( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
1904 if ( ! $iterator->valid() ) {
1905 rmdir( EWWW_IMAGE_OPTIMIZER_TOOL_PATH );
1906 }
1907 }
1908