WordPress 3.8
[autoinstalls/wordpress.git] / wp-includes / class-wp-image-editor.php
1 <?php
2 /**
3  * Base WordPress Image Editor
4  *
5  * @package WordPress
6  * @subpackage Image_Editor
7  */
8
9 /**
10  * Base image editor class from which implementations extend
11  *
12  * @since 3.5.0
13  */
14 abstract class WP_Image_Editor {
15         protected $file = null;
16         protected $size = null;
17         protected $mime_type = null;
18         protected $default_mime_type = 'image/jpeg';
19         protected $quality = 90;
20
21         /**
22          * Each instance handles a single file.
23          */
24         public function __construct( $file ) {
25                 $this->file = $file;
26         }
27
28         /**
29          * Checks to see if current environment supports the editor chosen.
30          * Must be overridden in a sub-class.
31          *
32          * @since 3.5.0
33          * @access public
34          * @abstract
35          *
36          * @param array $args
37          * @return boolean
38          */
39         public static function test( $args = array() ) {
40                 return false;
41         }
42
43         /**
44          * Checks to see if editor supports the mime-type specified.
45          * Must be overridden in a sub-class.
46          *
47          * @since 3.5.0
48          * @access public
49          * @abstract
50          *
51          * @param string $mime_type
52          * @return boolean
53          */
54         public static function supports_mime_type( $mime_type ) {
55                 return false;
56         }
57
58         /**
59          * Loads image from $this->file into editor.
60          *
61          * @since 3.5.0
62          * @access protected
63          * @abstract
64          *
65          * @return boolean|WP_Error True if loaded; WP_Error on failure.
66          */
67         abstract public function load();
68
69         /**
70          * Saves current image to file.
71          *
72          * @since 3.5.0
73          * @access public
74          * @abstract
75          *
76          * @param string $destfilename
77          * @param string $mime_type
78          * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string}
79          */
80         abstract public function save( $destfilename = null, $mime_type = null );
81
82         /**
83          * Resizes current image.
84          *
85          * @since 3.5.0
86          * @access public
87          * @abstract
88          *
89          * @param int $max_w
90          * @param int $max_h
91          * @param boolean $crop
92          * @return boolean|WP_Error
93          */
94         abstract public function resize( $max_w, $max_h, $crop = false );
95
96         /**
97          * Resize multiple images from a single source.
98          *
99          * @since 3.5.0
100          * @access public
101          * @abstract
102          *
103          * @param array $sizes {
104          *     An array of image size arrays. Default sizes are 'small', 'medium', 'large'.
105          *
106          *     @type array $size {
107          *         @type int  $width  Image width.
108          *         @type int  $height Image height.
109          *         @type bool $crop   Optional. Whether to crop the image. Default false.
110          *     }
111          * }
112          * @return array An array of resized images metadata by size.
113          */
114         abstract public function multi_resize( $sizes );
115
116         /**
117          * Crops Image.
118          *
119          * @since 3.5.0
120          * @access public
121          * @abstract
122          *
123          * @param string|int $src The source file or Attachment ID.
124          * @param int $src_x The start x position to crop from.
125          * @param int $src_y The start y position to crop from.
126          * @param int $src_w The width to crop.
127          * @param int $src_h The height to crop.
128          * @param int $dst_w Optional. The destination width.
129          * @param int $dst_h Optional. The destination height.
130          * @param boolean $src_abs Optional. If the source crop points are absolute.
131          * @return boolean|WP_Error
132          */
133         abstract public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false );
134
135         /**
136          * Rotates current image counter-clockwise by $angle.
137          *
138          * @since 3.5.0
139          * @access public
140          * @abstract
141          *
142          * @param float $angle
143          * @return boolean|WP_Error
144          */
145         abstract public function rotate( $angle );
146
147         /**
148          * Flips current image.
149          *
150          * @since 3.5.0
151          * @access public
152          * @abstract
153          *
154          * @param boolean $horz Flip along Horizontal Axis
155          * @param boolean $vert Flip along Vertical Axis
156          * @return boolean|WP_Error
157          */
158         abstract public function flip( $horz, $vert );
159
160         /**
161          * Streams current image to browser.
162          *
163          * @since 3.5.0
164          * @access public
165          * @abstract
166          *
167          * @param string $mime_type
168          * @return boolean|WP_Error
169          */
170         abstract public function stream( $mime_type = null );
171
172         /**
173          * Gets dimensions of image.
174          *
175          * @since 3.5.0
176          * @access public
177          *
178          * @return array {'width'=>int, 'height'=>int}
179          */
180         public function get_size() {
181                 return $this->size;
182         }
183
184         /**
185          * Sets current image size.
186          *
187          * @since 3.5.0
188          * @access protected
189          *
190          * @param int $width
191          * @param int $height
192          */
193         protected function update_size( $width = null, $height = null ) {
194                 $this->size = array(
195                         'width' => (int) $width,
196                         'height' => (int) $height
197                 );
198                 return true;
199         }
200
201         /**
202          * Sets Image Compression quality on a 1-100% scale.
203          *
204          * @since 3.5.0
205          * @access public
206          *
207          * @param int $quality Compression Quality. Range: [1,100]
208          * @return boolean|WP_Error True if set successfully; WP_Error on failure.
209          */
210         public function set_quality( $quality = null ) {
211                 if ( $quality == null ) {
212                         $quality = $this->quality;
213                 }
214
215                 /**
216                  * Filter the default image compression quality setting.
217                  *
218                  * @since 3.5.0
219                  *
220                  * @param int    $quality   Quality level between 1 (low) and 100 (high).
221                  * @param string $mime_type Image mime type.
222                  */
223                 $quality = apply_filters( 'wp_editor_set_quality', $quality, $this->mime_type );
224
225                 if ( 'image/jpeg' == $this->mime_type ) {
226                         /**
227                          * Filter the JPEG compression quality for backward-compatibility.
228                          *
229                          * The filter is evaluated under two contexts: 'image_resize', and 'edit_image',
230                          * (when a JPEG image is saved to file).
231                          *
232                          * @since 2.5.0
233                          *
234                          * @param int    $quality Quality level between 0 (low) and 100 (high) of the JPEG.
235                          * @param string $context Context of the filter.
236                          */
237                         $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' );
238
239                         // Allow 0, but squash to 1 due to identical images in GD, and for backwards compatibility.
240                         if ( $quality == 0 ) {
241                                 $quality = 1;
242                         }
243                 }
244
245                 if ( ( $quality >= 1 ) && ( $quality <= 100 ) ){
246                         $this->quality = $quality;
247                         return true;
248                 } else {
249                         return new WP_Error( 'invalid_image_quality', __('Attempted to set image quality outside of the range [1,100].') );
250                 }
251         }
252
253         /**
254          * Returns preferred mime-type and extension based on provided
255          * file's extension and mime, or current file's extension and mime.
256          *
257          * Will default to $this->default_mime_type if requested is not supported.
258          *
259          * Provides corrected filename only if filename is provided.
260          *
261          * @since 3.5.0
262          * @access protected
263          *
264          * @param string $filename
265          * @param string $mime_type
266          * @return array { filename|null, extension, mime-type }
267          */
268         protected function get_output_format( $filename = null, $mime_type = null ) {
269                 $new_ext = $file_ext = null;
270                 $file_mime = null;
271
272                 // By default, assume specified type takes priority
273                 if ( $mime_type ) {
274                         $new_ext = $this->get_extension( $mime_type );
275                 }
276
277                 if ( $filename ) {
278                         $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
279                         $file_mime = $this->get_mime_type( $file_ext );
280                 }
281                 else {
282                         // If no file specified, grab editor's current extension and mime-type.
283                         $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) );
284                         $file_mime = $this->mime_type;
285                 }
286
287                 // Check to see if specified mime-type is the same as type implied by
288                 // file extension.  If so, prefer extension from file.
289                 if ( ! $mime_type || ( $file_mime == $mime_type ) ) {
290                         $mime_type = $file_mime;
291                         $new_ext = $file_ext;
292                 }
293
294                 // Double-check that the mime-type selected is supported by the editor.
295                 // If not, choose a default instead.
296                 if ( ! $this->supports_mime_type( $mime_type ) ) {
297                         /**
298                          * Filter default mime type prior to getting the file extension.
299                          *
300                          * @see wp_get_mime_types()
301                          *
302                          * @since 3.5.0
303                          *
304                          * @param string $mime_type Mime type string.
305                          */
306                         $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type );
307                         $new_ext = $this->get_extension( $mime_type );
308                 }
309
310                 if ( $filename ) {
311                         $ext = '';
312                         $info = pathinfo( $filename );
313                         $dir  = $info['dirname'];
314
315                         if( isset( $info['extension'] ) )
316                                 $ext = $info['extension'];
317
318                         $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}";
319                 }
320
321                 return array( $filename, $new_ext, $mime_type );
322         }
323
324         /**
325          * Builds an output filename based on current file, and adding proper suffix
326          *
327          * @since 3.5.0
328          * @access public
329          *
330          * @param string $suffix
331          * @param string $dest_path
332          * @param string $extension
333          * @return string filename
334          */
335         public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) {
336                 // $suffix will be appended to the destination filename, just before the extension
337                 if ( ! $suffix )
338                         $suffix = $this->get_suffix();
339
340                 $info = pathinfo( $this->file );
341                 $dir  = $info['dirname'];
342                 $ext  = $info['extension'];
343
344                 $name = wp_basename( $this->file, ".$ext" );
345                 $new_ext = strtolower( $extension ? $extension : $ext );
346
347                 if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) )
348                         $dir = $_dest_path;
349
350                 return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}";
351         }
352
353         /**
354          * Builds and returns proper suffix for file based on height and width.
355          *
356          * @since 3.5.0
357          * @access public
358          *
359          * @return string suffix
360          */
361         public function get_suffix() {
362                 if ( ! $this->get_size() )
363                         return false;
364
365                 return "{$this->size['width']}x{$this->size['height']}";
366         }
367
368         /**
369          * Either calls editor's save function or handles file as a stream.
370          *
371          * @since 3.5.0
372          * @access protected
373          *
374          * @param string|stream $filename
375          * @param callable $function
376          * @param array $arguments
377          * @return boolean
378          */
379         protected function make_image( $filename, $function, $arguments ) {
380                 if ( $stream = wp_is_stream( $filename ) ) {
381                         ob_start();
382                 } else {
383                         // The directory containing the original file may no longer exist when using a replication plugin.
384                         wp_mkdir_p( dirname( $filename ) );
385                 }
386
387                 $result = call_user_func_array( $function, $arguments );
388
389                 if ( $result && $stream ) {
390                         $contents = ob_get_contents();
391
392                         $fp = fopen( $filename, 'w' );
393
394                         if ( ! $fp )
395                                 return false;
396
397                         fwrite( $fp, $contents );
398                         fclose( $fp );
399                 }
400
401                 if ( $stream ) {
402                         ob_end_clean();
403                 }
404
405                 return $result;
406         }
407
408         /**
409          * Returns first matched mime-type from extension,
410          * as mapped from wp_get_mime_types()
411          *
412          * @since 3.5.0
413          * @access protected
414          *
415          * @param string $extension
416          * @return string|boolean
417          */
418         protected static function get_mime_type( $extension = null ) {
419                 if ( ! $extension )
420                         return false;
421
422                 $mime_types = wp_get_mime_types();
423                 $extensions = array_keys( $mime_types );
424
425                 foreach( $extensions as $_extension ) {
426                         if ( preg_match( "/{$extension}/i", $_extension ) ) {
427                                 return $mime_types[$_extension];
428                         }
429                 }
430
431                 return false;
432         }
433
434         /**
435          * Returns first matched extension from Mime-type,
436          * as mapped from wp_get_mime_types()
437          *
438          * @since 3.5.0
439          * @access protected
440          *
441          * @param string $mime_type
442          * @return string|boolean
443          */
444         protected static function get_extension( $mime_type = null ) {
445                 $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types() ) );
446
447                 if ( empty( $extensions[0] ) )
448                         return false;
449
450                 return $extensions[0];
451         }
452 }
453