]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/class-wp-image-editor-imagick.php
WordPress 4.7.2-scripts
[autoinstalls/wordpress.git] / wp-includes / class-wp-image-editor-imagick.php
1 <?php
2 /**
3  * WordPress Imagick Image Editor
4  *
5  * @package WordPress
6  * @subpackage Image_Editor
7  */
8
9 /**
10  * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module
11  *
12  * @since 3.5.0
13  * @package WordPress
14  * @subpackage Image_Editor
15  * @uses WP_Image_Editor Extends class
16  */
17 class WP_Image_Editor_Imagick extends WP_Image_Editor {
18         /**
19          * Imagick object.
20          *
21          * @access protected
22          * @var Imagick
23          */
24         protected $image;
25
26         public function __destruct() {
27                 if ( $this->image instanceof Imagick ) {
28                         // we don't need the original in memory anymore
29                         $this->image->clear();
30                         $this->image->destroy();
31                 }
32         }
33
34         /**
35          * Checks to see if current environment supports Imagick.
36          *
37          * We require Imagick 2.2.0 or greater, based on whether the queryFormats()
38          * method can be called statically.
39          *
40          * @since 3.5.0
41          *
42          * @static
43          * @access public
44          *
45          * @param array $args
46          * @return bool
47          */
48         public static function test( $args = array() ) {
49
50                 // First, test Imagick's extension and classes.
51                 if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick', false ) || ! class_exists( 'ImagickPixel', false ) )
52                         return false;
53
54                 if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) )
55                         return false;
56
57                 $required_methods = array(
58                         'clear',
59                         'destroy',
60                         'valid',
61                         'getimage',
62                         'writeimage',
63                         'getimageblob',
64                         'getimagegeometry',
65                         'getimageformat',
66                         'setimageformat',
67                         'setimagecompression',
68                         'setimagecompressionquality',
69                         'setimagepage',
70                         'setoption',
71                         'scaleimage',
72                         'cropimage',
73                         'rotateimage',
74                         'flipimage',
75                         'flopimage',
76                         'readimage',
77                 );
78
79                 // Now, test for deep requirements within Imagick.
80                 if ( ! defined( 'imagick::COMPRESSION_JPEG' ) )
81                         return false;
82
83                 $class_methods = array_map( 'strtolower', get_class_methods( 'Imagick' ) );
84                 if ( array_diff( $required_methods, $class_methods ) ) {
85                         return false;
86                 }
87
88                 // HHVM Imagick does not support loading from URL, so fail to allow fallback to GD.
89                 if ( defined( 'HHVM_VERSION' ) && isset( $args['path'] ) && preg_match( '|^https?://|', $args['path'] ) ) {
90                         return false;
91                 }
92
93                 return true;
94         }
95
96         /**
97          * Checks to see if editor supports the mime-type specified.
98          *
99          * @since 3.5.0
100          *
101          * @static
102          * @access public
103          *
104          * @param string $mime_type
105          * @return bool
106          */
107         public static function supports_mime_type( $mime_type ) {
108                 $imagick_extension = strtoupper( self::get_extension( $mime_type ) );
109
110                 if ( ! $imagick_extension )
111                         return false;
112
113                 // setIteratorIndex is optional unless mime is an animated format.
114                 // Here, we just say no if you are missing it and aren't loading a jpeg.
115                 if ( ! method_exists( 'Imagick', 'setIteratorIndex' ) && $mime_type != 'image/jpeg' )
116                                 return false;
117
118                 try {
119                         return ( (bool) @Imagick::queryFormats( $imagick_extension ) );
120                 }
121                 catch ( Exception $e ) {
122                         return false;
123                 }
124         }
125
126         /**
127          * Loads image from $this->file into new Imagick Object.
128          *
129          * @since 3.5.0
130          * @access protected
131          *
132          * @return true|WP_Error True if loaded; WP_Error on failure.
133          */
134         public function load() {
135                 if ( $this->image instanceof Imagick )
136                         return true;
137
138                 if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) )
139                         return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
140
141                 /*
142                  * Even though Imagick uses less PHP memory than GD, set higher limit
143                  * for users that have low PHP.ini limits.
144                  */
145                 wp_raise_memory_limit( 'image' );
146
147                 try {
148                         $this->image = new Imagick();
149                         $file_extension = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) );
150                         $filename = $this->file;
151
152                         if ( 'pdf' == $file_extension ) {
153                                 $filename = $this->pdf_setup();
154                         }
155
156                         // Reading image after Imagick instantiation because `setResolution`
157                         // only applies correctly before the image is read.
158                         $this->image->readImage( $filename );
159
160                         if ( ! $this->image->valid() )
161                                 return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file);
162
163                         // Select the first frame to handle animated images properly
164                         if ( is_callable( array( $this->image, 'setIteratorIndex' ) ) )
165                                 $this->image->setIteratorIndex(0);
166
167                         $this->mime_type = $this->get_mime_type( $this->image->getImageFormat() );
168                 }
169                 catch ( Exception $e ) {
170                         return new WP_Error( 'invalid_image', $e->getMessage(), $this->file );
171                 }
172
173                 $updated_size = $this->update_size();
174                 if ( is_wp_error( $updated_size ) ) {
175                         return $updated_size;
176                 }
177
178                 return $this->set_quality();
179         }
180
181         /**
182          * Sets Image Compression quality on a 1-100% scale.
183          *
184          * @since 3.5.0
185          * @access public
186          *
187          * @param int $quality Compression Quality. Range: [1,100]
188          * @return true|WP_Error True if set successfully; WP_Error on failure.
189          */
190         public function set_quality( $quality = null ) {
191                 $quality_result = parent::set_quality( $quality );
192                 if ( is_wp_error( $quality_result ) ) {
193                         return $quality_result;
194                 } else {
195                         $quality = $this->get_quality();
196                 }
197
198                 try {
199                         if ( 'image/jpeg' == $this->mime_type ) {
200                                 $this->image->setImageCompressionQuality( $quality );
201                                 $this->image->setImageCompression( imagick::COMPRESSION_JPEG );
202                         }
203                         else {
204                                 $this->image->setImageCompressionQuality( $quality );
205                         }
206                 }
207                 catch ( Exception $e ) {
208                         return new WP_Error( 'image_quality_error', $e->getMessage() );
209                 }
210
211                 return true;
212         }
213
214         /**
215          * Sets or updates current image size.
216          *
217          * @since 3.5.0
218          * @access protected
219          *
220          * @param int $width
221          * @param int $height
222          *
223          * @return true|WP_Error
224          */
225         protected function update_size( $width = null, $height = null ) {
226                 $size = null;
227                 if ( !$width || !$height ) {
228                         try {
229                                 $size = $this->image->getImageGeometry();
230                         }
231                         catch ( Exception $e ) {
232                                 return new WP_Error( 'invalid_image', __( 'Could not read image size.' ), $this->file );
233                         }
234                 }
235
236                 if ( ! $width )
237                         $width = $size['width'];
238
239                 if ( ! $height )
240                         $height = $size['height'];
241
242                 return parent::update_size( $width, $height );
243         }
244
245         /**
246          * Resizes current image.
247          *
248          * At minimum, either a height or width must be provided.
249          * If one of the two is set to null, the resize will
250          * maintain aspect ratio according to the provided dimension.
251          *
252          * @since 3.5.0
253          * @access public
254          *
255          * @param  int|null $max_w Image width.
256          * @param  int|null $max_h Image height.
257          * @param  bool     $crop
258          * @return bool|WP_Error
259          */
260         public function resize( $max_w, $max_h, $crop = false ) {
261                 if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) )
262                         return true;
263
264                 $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
265                 if ( ! $dims )
266                         return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') );
267                 list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims;
268
269                 if ( $crop ) {
270                         return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h );
271                 }
272
273                 // Execute the resize
274                 $thumb_result = $this->thumbnail_image( $dst_w, $dst_h );
275                 if ( is_wp_error( $thumb_result ) ) {
276                         return $thumb_result;
277                 }
278
279                 return $this->update_size( $dst_w, $dst_h );
280         }
281
282         /**
283          * Efficiently resize the current image
284          *
285          * This is a WordPress specific implementation of Imagick::thumbnailImage(),
286          * which resizes an image to given dimensions and removes any associated profiles.
287          *
288          * @since 4.5.0
289          * @access protected
290          *
291          * @param int    $dst_w       The destination width.
292          * @param int    $dst_h       The destination height.
293          * @param string $filter_name Optional. The Imagick filter to use when resizing. Default 'FILTER_TRIANGLE'.
294          * @param bool   $strip_meta  Optional. Strip all profiles, excluding color profiles, from the image. Default true.
295          * @return bool|WP_Error
296          */
297         protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIANGLE', $strip_meta = true ) {
298                 $allowed_filters = array(
299                         'FILTER_POINT',
300                         'FILTER_BOX',
301                         'FILTER_TRIANGLE',
302                         'FILTER_HERMITE',
303                         'FILTER_HANNING',
304                         'FILTER_HAMMING',
305                         'FILTER_BLACKMAN',
306                         'FILTER_GAUSSIAN',
307                         'FILTER_QUADRATIC',
308                         'FILTER_CUBIC',
309                         'FILTER_CATROM',
310                         'FILTER_MITCHELL',
311                         'FILTER_LANCZOS',
312                         'FILTER_BESSEL',
313                         'FILTER_SINC',
314                 );
315
316                 /**
317                  * Set the filter value if '$filter_name' name is in our whitelist and the related
318                  * Imagick constant is defined or fall back to our default filter.
319                  */
320                 if ( in_array( $filter_name, $allowed_filters ) && defined( 'Imagick::' . $filter_name ) ) {
321                         $filter = constant( 'Imagick::' . $filter_name );
322                 } else {
323                         $filter = defined( 'Imagick::FILTER_TRIANGLE' ) ? Imagick::FILTER_TRIANGLE : false;
324                 }
325
326                 /**
327                  * Filters whether to strip metadata from images when they're resized.
328                  *
329                  * This filter only applies when resizing using the Imagick editor since GD
330                  * always strips profiles by default.
331                  *
332                  * @since 4.5.0
333                  *
334                  * @param bool $strip_meta Whether to strip image metadata during resizing. Default true.
335                  */
336                 if ( apply_filters( 'image_strip_meta', $strip_meta ) ) {
337                         $this->strip_meta(); // Fail silently if not supported.
338                 }
339
340                 try {
341                         /*
342                          * To be more efficient, resample large images to 5x the destination size before resizing
343                          * whenever the output size is less that 1/3 of the original image size (1/3^2 ~= .111),
344                          * unless we would be resampling to a scale smaller than 128x128.
345                          */
346                         if ( is_callable( array( $this->image, 'sampleImage' ) ) ) {
347                                 $resize_ratio = ( $dst_w / $this->size['width'] ) * ( $dst_h / $this->size['height'] );
348                                 $sample_factor = 5;
349
350                                 if ( $resize_ratio < .111 && ( $dst_w * $sample_factor > 128 && $dst_h * $sample_factor > 128 ) ) {
351                                         $this->image->sampleImage( $dst_w * $sample_factor, $dst_h * $sample_factor );
352                                 }
353                         }
354
355                         /*
356                          * Use resizeImage() when it's available and a valid filter value is set.
357                          * Otherwise, fall back to the scaleImage() method for resizing, which
358                          * results in better image quality over resizeImage() with default filter
359                          * settings and retains backward compatibility with pre 4.5 functionality.
360                          */
361                         if ( is_callable( array( $this->image, 'resizeImage' ) ) && $filter ) {
362                                 $this->image->setOption( 'filter:support', '2.0' );
363                                 $this->image->resizeImage( $dst_w, $dst_h, $filter, 1 );
364                         } else {
365                                 $this->image->scaleImage( $dst_w, $dst_h );
366                         }
367
368                         // Set appropriate quality settings after resizing.
369                         if ( 'image/jpeg' == $this->mime_type ) {
370                                 if ( is_callable( array( $this->image, 'unsharpMaskImage' ) ) ) {
371                                         $this->image->unsharpMaskImage( 0.25, 0.25, 8, 0.065 );
372                                 }
373
374                                 $this->image->setOption( 'jpeg:fancy-upsampling', 'off' );
375                         }
376
377                         if ( 'image/png' === $this->mime_type ) {
378                                 $this->image->setOption( 'png:compression-filter', '5' );
379                                 $this->image->setOption( 'png:compression-level', '9' );
380                                 $this->image->setOption( 'png:compression-strategy', '1' );
381                                 $this->image->setOption( 'png:exclude-chunk', 'all' );
382                         }
383
384                         /*
385                          * If alpha channel is not defined, set it opaque.
386                          *
387                          * Note that Imagick::getImageAlphaChannel() is only available if Imagick
388                          * has been compiled against ImageMagick version 6.4.0 or newer.
389                          */
390                         if ( is_callable( array( $this->image, 'getImageAlphaChannel' ) )
391                                 && is_callable( array( $this->image, 'setImageAlphaChannel' ) )
392                                 && defined( 'Imagick::ALPHACHANNEL_UNDEFINED' )
393                                 && defined( 'Imagick::ALPHACHANNEL_OPAQUE' )
394                         ) {
395                                 if ( $this->image->getImageAlphaChannel() === Imagick::ALPHACHANNEL_UNDEFINED ) {
396                                         $this->image->setImageAlphaChannel( Imagick::ALPHACHANNEL_OPAQUE );
397                                 }
398                         }
399
400                         // Limit the bit depth of resized images to 8 bits per channel.
401                         if ( is_callable( array( $this->image, 'getImageDepth' ) ) && is_callable( array( $this->image, 'setImageDepth' ) ) ) {
402                                 if ( 8 < $this->image->getImageDepth() ) {
403                                         $this->image->setImageDepth( 8 );
404                                 }
405                         }
406
407                         if ( is_callable( array( $this->image, 'setInterlaceScheme' ) ) && defined( 'Imagick::INTERLACE_NO' ) ) {
408                                 $this->image->setInterlaceScheme( Imagick::INTERLACE_NO );
409                         }
410
411                 }
412                 catch ( Exception $e ) {
413                         return new WP_Error( 'image_resize_error', $e->getMessage() );
414                 }
415         }
416
417         /**
418          * Resize multiple images from a single source.
419          *
420          * @since 3.5.0
421          * @access public
422          *
423          * @param array $sizes {
424          *     An array of image size arrays. Default sizes are 'small', 'medium', 'medium_large', 'large'.
425          *
426          *     Either a height or width must be provided.
427          *     If one of the two is set to null, the resize will
428          *     maintain aspect ratio according to the provided dimension.
429          *
430          *     @type array $size {
431          *         Array of height, width values, and whether to crop.
432          *
433          *         @type int  $width  Image width. Optional if `$height` is specified.
434          *         @type int  $height Image height. Optional if `$width` is specified.
435          *         @type bool $crop   Optional. Whether to crop the image. Default false.
436          *     }
437          * }
438          * @return array An array of resized images' metadata by size.
439          */
440         public function multi_resize( $sizes ) {
441                 $metadata = array();
442                 $orig_size = $this->size;
443                 $orig_image = $this->image->getImage();
444
445                 foreach ( $sizes as $size => $size_data ) {
446                         if ( ! $this->image )
447                                 $this->image = $orig_image->getImage();
448
449                         if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
450                                 continue;
451                         }
452
453                         if ( ! isset( $size_data['width'] ) ) {
454                                 $size_data['width'] = null;
455                         }
456                         if ( ! isset( $size_data['height'] ) ) {
457                                 $size_data['height'] = null;
458                         }
459
460                         if ( ! isset( $size_data['crop'] ) ) {
461                                 $size_data['crop'] = false;
462                         }
463
464                         $resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
465                         $duplicate = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );
466
467                         if ( ! is_wp_error( $resize_result ) && ! $duplicate ) {
468                                 $resized = $this->_save( $this->image );
469
470                                 $this->image->clear();
471                                 $this->image->destroy();
472                                 $this->image = null;
473
474                                 if ( ! is_wp_error( $resized ) && $resized ) {
475                                         unset( $resized['path'] );
476                                         $metadata[$size] = $resized;
477                                 }
478                         }
479
480                         $this->size = $orig_size;
481                 }
482
483                 $this->image = $orig_image;
484
485                 return $metadata;
486         }
487
488         /**
489          * Crops Image.
490          *
491          * @since 3.5.0
492          * @access public
493          *
494          * @param int  $src_x The start x position to crop from.
495          * @param int  $src_y The start y position to crop from.
496          * @param int  $src_w The width to crop.
497          * @param int  $src_h The height to crop.
498          * @param int  $dst_w Optional. The destination width.
499          * @param int  $dst_h Optional. The destination height.
500          * @param bool $src_abs Optional. If the source crop points are absolute.
501          * @return bool|WP_Error
502          */
503         public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) {
504                 if ( $src_abs ) {
505                         $src_w -= $src_x;
506                         $src_h -= $src_y;
507                 }
508
509                 try {
510                         $this->image->cropImage( $src_w, $src_h, $src_x, $src_y );
511                         $this->image->setImagePage( $src_w, $src_h, 0, 0);
512
513                         if ( $dst_w || $dst_h ) {
514                                 // If destination width/height isn't specified, use same as
515                                 // width/height from source.
516                                 if ( ! $dst_w )
517                                         $dst_w = $src_w;
518                                 if ( ! $dst_h )
519                                         $dst_h = $src_h;
520
521                                 $thumb_result = $this->thumbnail_image( $dst_w, $dst_h );
522                                 if ( is_wp_error( $thumb_result ) ) {
523                                         return $thumb_result;
524                                 }
525
526                                 return $this->update_size();
527                         }
528                 }
529                 catch ( Exception $e ) {
530                         return new WP_Error( 'image_crop_error', $e->getMessage() );
531                 }
532                 return $this->update_size();
533         }
534
535         /**
536          * Rotates current image counter-clockwise by $angle.
537          *
538          * @since 3.5.0
539          * @access public
540          *
541          * @param float $angle
542          * @return true|WP_Error
543          */
544         public function rotate( $angle ) {
545                 /**
546                  * $angle is 360-$angle because Imagick rotates clockwise
547                  * (GD rotates counter-clockwise)
548                  */
549                 try {
550                         $this->image->rotateImage( new ImagickPixel('none'), 360-$angle );
551
552                         // Since this changes the dimensions of the image, update the size.
553                         $result = $this->update_size();
554                         if ( is_wp_error( $result ) )
555                                 return $result;
556
557                         $this->image->setImagePage( $this->size['width'], $this->size['height'], 0, 0 );
558                 }
559                 catch ( Exception $e ) {
560                         return new WP_Error( 'image_rotate_error', $e->getMessage() );
561                 }
562                 return true;
563         }
564
565         /**
566          * Flips current image.
567          *
568          * @since 3.5.0
569          * @access public
570          *
571          * @param bool $horz Flip along Horizontal Axis
572          * @param bool $vert Flip along Vertical Axis
573          * @return true|WP_Error
574          */
575         public function flip( $horz, $vert ) {
576                 try {
577                         if ( $horz )
578                                 $this->image->flipImage();
579
580                         if ( $vert )
581                                 $this->image->flopImage();
582                 }
583                 catch ( Exception $e ) {
584                         return new WP_Error( 'image_flip_error', $e->getMessage() );
585                 }
586                 return true;
587         }
588
589         /**
590          * Saves current image to file.
591          *
592          * @since 3.5.0
593          * @access public
594          *
595          * @param string $destfilename
596          * @param string $mime_type
597          * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
598          */
599         public function save( $destfilename = null, $mime_type = null ) {
600                 $saved = $this->_save( $this->image, $destfilename, $mime_type );
601
602                 if ( ! is_wp_error( $saved ) ) {
603                         $this->file = $saved['path'];
604                         $this->mime_type = $saved['mime-type'];
605
606                         try {
607                                 $this->image->setImageFormat( strtoupper( $this->get_extension( $this->mime_type ) ) );
608                         }
609                         catch ( Exception $e ) {
610                                 return new WP_Error( 'image_save_error', $e->getMessage(), $this->file );
611                         }
612                 }
613
614                 return $saved;
615         }
616
617         /**
618          *
619          * @param Imagick $image
620          * @param string $filename
621          * @param string $mime_type
622          * @return array|WP_Error
623          */
624         protected function _save( $image, $filename = null, $mime_type = null ) {
625                 list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type );
626
627                 if ( ! $filename )
628                         $filename = $this->generate_filename( null, null, $extension );
629
630                 try {
631                         // Store initial Format
632                         $orig_format = $this->image->getImageFormat();
633
634                         $this->image->setImageFormat( strtoupper( $this->get_extension( $mime_type ) ) );
635                         $this->make_image( $filename, array( $image, 'writeImage' ), array( $filename ) );
636
637                         // Reset original Format
638                         $this->image->setImageFormat( $orig_format );
639                 }
640                 catch ( Exception $e ) {
641                         return new WP_Error( 'image_save_error', $e->getMessage(), $filename );
642                 }
643
644                 // Set correct file permissions
645                 $stat = stat( dirname( $filename ) );
646                 $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
647                 @ chmod( $filename, $perms );
648
649                 /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */
650                 return array(
651                         'path'      => $filename,
652                         'file'      => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ),
653                         'width'     => $this->size['width'],
654                         'height'    => $this->size['height'],
655                         'mime-type' => $mime_type,
656                 );
657         }
658
659         /**
660          * Streams current image to browser.
661          *
662          * @since 3.5.0
663          * @access public
664          *
665          * @param string $mime_type
666          * @return true|WP_Error
667          */
668         public function stream( $mime_type = null ) {
669                 list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type );
670
671                 try {
672                         // Temporarily change format for stream
673                         $this->image->setImageFormat( strtoupper( $extension ) );
674
675                         // Output stream of image content
676                         header( "Content-Type: $mime_type" );
677                         print $this->image->getImageBlob();
678
679                         // Reset Image to original Format
680                         $this->image->setImageFormat( $this->get_extension( $this->mime_type ) );
681                 }
682                 catch ( Exception $e ) {
683                         return new WP_Error( 'image_stream_error', $e->getMessage() );
684                 }
685
686                 return true;
687         }
688
689         /**
690          * Strips all image meta except color profiles from an image.
691          *
692          * @since 4.5.0
693          * @access protected
694          *
695          * @return true|WP_Error True if stripping metadata was successful. WP_Error object on error.
696          */
697         protected function strip_meta() {
698
699                 if ( ! is_callable( array( $this->image, 'getImageProfiles' ) ) ) {
700                         /* translators: %s: ImageMagick method name */
701                         return new WP_Error( 'image_strip_meta_error', sprintf( __( '%s is required to strip image meta.' ), '<code>Imagick::getImageProfiles()</code>' ) );
702                 }
703
704                 if ( ! is_callable( array( $this->image, 'removeImageProfile' ) ) ) {
705                         /* translators: %s: ImageMagick method name */
706                         return new WP_Error( 'image_strip_meta_error', sprintf( __( '%s is required to strip image meta.' ), '<code>Imagick::removeImageProfile()</code>' ) );
707                 }
708
709                 /*
710                  * Protect a few profiles from being stripped for the following reasons:
711                  *
712                  * - icc:  Color profile information
713                  * - icm:  Color profile information
714                  * - iptc: Copyright data
715                  * - exif: Orientation data
716                  * - xmp:  Rights usage data
717                  */
718                 $protected_profiles = array(
719                         'icc',
720                         'icm',
721                         'iptc',
722                         'exif',
723                         'xmp',
724                 );
725
726                 try {
727                         // Strip profiles.
728                         foreach ( $this->image->getImageProfiles( '*', true ) as $key => $value ) {
729                                 if ( ! in_array( $key, $protected_profiles ) ) {
730                                         $this->image->removeImageProfile( $key );
731                                 }
732                         }
733
734                 } catch ( Exception $e ) {
735                         return new WP_Error( 'image_strip_meta_error', $e->getMessage() );
736                 }
737
738                 return true;
739         }
740
741         /**
742          * Sets up Imagick for PDF processing.
743          * Increases rendering DPI and only loads first page.
744          *
745          * @since 4.7.0
746          * @access protected
747          *
748          * @return string|WP_Error File to load or WP_Error on failure.
749          */
750         protected function pdf_setup() {
751                 try {
752                         // By default, PDFs are rendered in a very low resolution.
753                         // We want the thumbnail to be readable, so increase the rendering DPI.
754                         $this->image->setResolution( 128, 128 );
755
756                         // Only load the first page.
757                         return $this->file . '[0]';
758                 }
759                 catch ( Exception $e ) {
760                         return new WP_Error( 'pdf_setup_failed', $e->getMessage(), $this->file );
761                 }
762         }
763
764 }