changeset 16 | a86126ab1dd4 |
parent 9 | 177826044cd9 |
child 18 | be944660c56a |
15:3d4e9c994f10 | 16:a86126ab1dd4 |
---|---|
5 * @package WordPress |
5 * @package WordPress |
6 * @subpackage Administration |
6 * @subpackage Administration |
7 */ |
7 */ |
8 |
8 |
9 /** |
9 /** |
10 * Crop an Image to a given size. |
10 * Crops an image to a given size. |
11 * |
11 * |
12 * @since 2.1.0 |
12 * @since 2.1.0 |
13 * |
13 * |
14 * @param string|int $src The source file or Attachment ID. |
14 * @param string|int $src The source file or Attachment ID. |
15 * @param int $src_x The start x position to crop from. |
15 * @param int $src_x The start x position to crop from. |
16 * @param int $src_y The start y position to crop from. |
16 * @param int $src_y The start y position to crop from. |
17 * @param int $src_w The width to crop. |
17 * @param int $src_w The width to crop. |
18 * @param int $src_h The height to crop. |
18 * @param int $src_h The height to crop. |
19 * @param int $dst_w The destination width. |
19 * @param int $dst_w The destination width. |
20 * @param int $dst_h The destination height. |
20 * @param int $dst_h The destination height. |
21 * @param int $src_abs Optional. If the source crop points are absolute. |
21 * @param bool $src_abs Optional. If the source crop points are absolute. |
22 * @param string $dst_file Optional. The destination file to write to. |
22 * @param string $dst_file Optional. The destination file to write to. |
23 * @return string|WP_Error New filepath on success, WP_Error on failure. |
23 * @return string|WP_Error New filepath on success, WP_Error on failure. |
24 */ |
24 */ |
25 function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) { |
25 function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) { |
26 $src_file = $src; |
26 $src_file = $src; |
27 if ( is_numeric( $src ) ) { // Handle int as attachment ID |
27 if ( is_numeric( $src ) ) { // Handle int as attachment ID. |
28 $src_file = get_attached_file( $src ); |
28 $src_file = get_attached_file( $src ); |
29 |
29 |
30 if ( ! file_exists( $src_file ) ) { |
30 if ( ! file_exists( $src_file ) ) { |
31 // If the file doesn't exist, attempt a URL fopen on the src link. |
31 // If the file doesn't exist, attempt a URL fopen on the src link. |
32 // This can occur with certain file replication plugins. |
32 // This can occur with certain file replication plugins. |
65 |
65 |
66 return $dst_file; |
66 return $dst_file; |
67 } |
67 } |
68 |
68 |
69 /** |
69 /** |
70 * Generate post thumbnail attachment meta data. |
70 * Compare the existing image sub-sizes (as saved in the attachment meta) |
71 * to the currently registered image sub-sizes, and return the difference. |
|
72 * |
|
73 * Registered sub-sizes that are larger than the image are skipped. |
|
74 * |
|
75 * @since 5.3.0 |
|
76 * |
|
77 * @param int $attachment_id The image attachment post ID. |
|
78 * @return array An array of the image sub-sizes that are currently defined but don't exist for this image. |
|
79 */ |
|
80 function wp_get_missing_image_subsizes( $attachment_id ) { |
|
81 if ( ! wp_attachment_is_image( $attachment_id ) ) { |
|
82 return array(); |
|
83 } |
|
84 |
|
85 $registered_sizes = wp_get_registered_image_subsizes(); |
|
86 $image_meta = wp_get_attachment_metadata( $attachment_id ); |
|
87 |
|
88 // Meta error? |
|
89 if ( empty( $image_meta ) ) { |
|
90 return $registered_sizes; |
|
91 } |
|
92 |
|
93 // Use the originally uploaded image dimensions as full_width and full_height. |
|
94 if ( ! empty( $image_meta['original_image'] ) ) { |
|
95 $image_file = wp_get_original_image_path( $attachment_id ); |
|
96 $imagesize = @getimagesize( $image_file ); |
|
97 } |
|
98 |
|
99 if ( ! empty( $imagesize ) ) { |
|
100 $full_width = $imagesize[0]; |
|
101 $full_height = $imagesize[1]; |
|
102 } else { |
|
103 $full_width = (int) $image_meta['width']; |
|
104 $full_height = (int) $image_meta['height']; |
|
105 } |
|
106 |
|
107 $possible_sizes = array(); |
|
108 |
|
109 // Skip registered sizes that are too large for the uploaded image. |
|
110 foreach ( $registered_sizes as $size_name => $size_data ) { |
|
111 if ( image_resize_dimensions( $full_width, $full_height, $size_data['width'], $size_data['height'], $size_data['crop'] ) ) { |
|
112 $possible_sizes[ $size_name ] = $size_data; |
|
113 } |
|
114 } |
|
115 |
|
116 if ( empty( $image_meta['sizes'] ) ) { |
|
117 $image_meta['sizes'] = array(); |
|
118 } |
|
119 |
|
120 /* |
|
121 * Remove sizes that already exist. Only checks for matching "size names". |
|
122 * It is possible that the dimensions for a particular size name have changed. |
|
123 * For example the user has changed the values on the Settings -> Media screen. |
|
124 * However we keep the old sub-sizes with the previous dimensions |
|
125 * as the image may have been used in an older post. |
|
126 */ |
|
127 $missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] ); |
|
128 |
|
129 /** |
|
130 * Filters the array of missing image sub-sizes for an uploaded image. |
|
131 * |
|
132 * @since 5.3.0 |
|
133 * |
|
134 * @param array $missing_sizes Array with the missing image sub-sizes. |
|
135 * @param array $image_meta The image meta data. |
|
136 * @param int $attachment_id The image attachment post ID. |
|
137 */ |
|
138 return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id ); |
|
139 } |
|
140 |
|
141 /** |
|
142 * If any of the currently registered image sub-sizes are missing, |
|
143 * create them and update the image meta data. |
|
144 * |
|
145 * @since 5.3.0 |
|
146 * |
|
147 * @param int $attachment_id The image attachment post ID. |
|
148 * @return array|WP_Error The updated image meta data array or WP_Error object |
|
149 * if both the image meta and the attached file are missing. |
|
150 */ |
|
151 function wp_update_image_subsizes( $attachment_id ) { |
|
152 $image_meta = wp_get_attachment_metadata( $attachment_id ); |
|
153 $image_file = wp_get_original_image_path( $attachment_id ); |
|
154 |
|
155 if ( empty( $image_meta ) || ! is_array( $image_meta ) ) { |
|
156 // Previously failed upload? |
|
157 // If there is an uploaded file, make all sub-sizes and generate all of the attachment meta. |
|
158 if ( ! empty( $image_file ) ) { |
|
159 $image_meta = wp_create_image_subsizes( $image_file, $attachment_id ); |
|
160 } else { |
|
161 return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) ); |
|
162 } |
|
163 } else { |
|
164 $missing_sizes = wp_get_missing_image_subsizes( $attachment_id ); |
|
165 |
|
166 if ( empty( $missing_sizes ) ) { |
|
167 return $image_meta; |
|
168 } |
|
169 |
|
170 // This also updates the image meta. |
|
171 $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id ); |
|
172 } |
|
173 |
|
174 /** This filter is documented in wp-admin/includes/image.php */ |
|
175 $image_meta = apply_filters( 'wp_generate_attachment_metadata', $image_meta, $attachment_id, 'update' ); |
|
176 |
|
177 // Save the updated metadata. |
|
178 wp_update_attachment_metadata( $attachment_id, $image_meta ); |
|
179 |
|
180 return $image_meta; |
|
181 } |
|
182 |
|
183 /** |
|
184 * Updates the attached file and image meta data when the original image was edited. |
|
185 * |
|
186 * @since 5.3.0 |
|
187 * @access private |
|
188 * |
|
189 * @param array $saved_data The data returned from WP_Image_Editor after successfully saving an image. |
|
190 * @param string $original_file Path to the original file. |
|
191 * @param array $image_meta The image meta data. |
|
192 * @param int $attachment_id The attachment post ID. |
|
193 * @return array The updated image meta data. |
|
194 */ |
|
195 function _wp_image_meta_replace_original( $saved_data, $original_file, $image_meta, $attachment_id ) { |
|
196 $new_file = $saved_data['path']; |
|
197 |
|
198 // Update the attached file meta. |
|
199 update_attached_file( $attachment_id, $new_file ); |
|
200 |
|
201 // Width and height of the new image. |
|
202 $image_meta['width'] = $saved_data['width']; |
|
203 $image_meta['height'] = $saved_data['height']; |
|
204 |
|
205 // Make the file path relative to the upload dir. |
|
206 $image_meta['file'] = _wp_relative_upload_path( $new_file ); |
|
207 |
|
208 // Store the original image file name in image_meta. |
|
209 $image_meta['original_image'] = wp_basename( $original_file ); |
|
210 |
|
211 return $image_meta; |
|
212 } |
|
213 |
|
214 /** |
|
215 * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata. |
|
216 * |
|
217 * Intended for use after an image is uploaded. Saves/updates the image metadata after each |
|
218 * sub-size is created. If there was an error, it is added to the returned image metadata array. |
|
219 * |
|
220 * @since 5.3.0 |
|
221 * |
|
222 * @param string $file Full path to the image file. |
|
223 * @param int $attachment_id Attachment Id to process. |
|
224 * @return array The image attachment meta data. |
|
225 */ |
|
226 function wp_create_image_subsizes( $file, $attachment_id ) { |
|
227 $imagesize = @getimagesize( $file ); |
|
228 |
|
229 if ( empty( $imagesize ) ) { |
|
230 // File is not an image. |
|
231 return array(); |
|
232 } |
|
233 |
|
234 // Default image meta. |
|
235 $image_meta = array( |
|
236 'width' => $imagesize[0], |
|
237 'height' => $imagesize[1], |
|
238 'file' => _wp_relative_upload_path( $file ), |
|
239 'sizes' => array(), |
|
240 ); |
|
241 |
|
242 // Fetch additional metadata from EXIF/IPTC. |
|
243 $exif_meta = wp_read_image_metadata( $file ); |
|
244 |
|
245 if ( $exif_meta ) { |
|
246 $image_meta['image_meta'] = $exif_meta; |
|
247 } |
|
248 |
|
249 // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. |
|
250 if ( 'image/png' !== $imagesize['mime'] ) { |
|
251 |
|
252 /** |
|
253 * Filters the "BIG image" threshold value. |
|
254 * |
|
255 * If the original image width or height is above the threshold, it will be scaled down. The threshold is |
|
256 * used as max width and max height. The scaled down image will be used as the largest available size, including |
|
257 * the `_wp_attached_file` post meta value. |
|
258 * |
|
259 * Returning `false` from the filter callback will disable the scaling. |
|
260 * |
|
261 * @since 5.3.0 |
|
262 * |
|
263 * @param int $threshold The threshold value in pixels. Default 2560. |
|
264 * @param array $imagesize { |
|
265 * Indexed array of the image width and height in pixels. |
|
266 * |
|
267 * @type int $0 The image width. |
|
268 * @type int $1 The image height. |
|
269 * } |
|
270 * @param string $file Full path to the uploaded image file. |
|
271 * @param int $attachment_id Attachment post ID. |
|
272 */ |
|
273 $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); |
|
274 |
|
275 // If the original image's dimensions are over the threshold, |
|
276 // scale the image and use it as the "full" size. |
|
277 if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { |
|
278 $editor = wp_get_image_editor( $file ); |
|
279 |
|
280 if ( is_wp_error( $editor ) ) { |
|
281 // This image cannot be edited. |
|
282 return $image_meta; |
|
283 } |
|
284 |
|
285 // Resize the image. |
|
286 $resized = $editor->resize( $threshold, $threshold ); |
|
287 $rotated = null; |
|
288 |
|
289 // If there is EXIF data, rotate according to EXIF Orientation. |
|
290 if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { |
|
291 $resized = $editor->maybe_exif_rotate(); |
|
292 $rotated = $resized; |
|
293 } |
|
294 |
|
295 if ( ! is_wp_error( $resized ) ) { |
|
296 // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". |
|
297 // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). |
|
298 $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); |
|
299 |
|
300 if ( ! is_wp_error( $saved ) ) { |
|
301 $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); |
|
302 |
|
303 // If the image was rotated update the stored EXIF data. |
|
304 if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { |
|
305 $image_meta['image_meta']['orientation'] = 1; |
|
306 } |
|
307 } else { |
|
308 // TODO: Log errors. |
|
309 } |
|
310 } else { |
|
311 // TODO: Log errors. |
|
312 } |
|
313 } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { |
|
314 // Rotate the whole original image if there is EXIF data and "orientation" is not 1. |
|
315 |
|
316 $editor = wp_get_image_editor( $file ); |
|
317 |
|
318 if ( is_wp_error( $editor ) ) { |
|
319 // This image cannot be edited. |
|
320 return $image_meta; |
|
321 } |
|
322 |
|
323 // Rotate the image. |
|
324 $rotated = $editor->maybe_exif_rotate(); |
|
325 |
|
326 if ( true === $rotated ) { |
|
327 // Append `-rotated` to the image file name. |
|
328 $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); |
|
329 |
|
330 if ( ! is_wp_error( $saved ) ) { |
|
331 $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); |
|
332 |
|
333 // Update the stored EXIF data. |
|
334 if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { |
|
335 $image_meta['image_meta']['orientation'] = 1; |
|
336 } |
|
337 } else { |
|
338 // TODO: Log errors. |
|
339 } |
|
340 } |
|
341 } |
|
342 } |
|
343 |
|
344 /* |
|
345 * Initial save of the new metadata. |
|
346 * At this point the file was uploaded and moved to the uploads directory |
|
347 * but the image sub-sizes haven't been created yet and the `sizes` array is empty. |
|
348 */ |
|
349 wp_update_attachment_metadata( $attachment_id, $image_meta ); |
|
350 |
|
351 $new_sizes = wp_get_registered_image_subsizes(); |
|
352 |
|
353 /** |
|
354 * Filters the image sizes automatically generated when uploading an image. |
|
355 * |
|
356 * @since 2.9.0 |
|
357 * @since 4.4.0 Added the `$image_meta` argument. |
|
358 * @since 5.3.0 Added the `$attachment_id` argument. |
|
359 * |
|
360 * @param array $new_sizes Associative array of image sizes to be created. |
|
361 * @param array $image_meta The image meta data: width, height, file, sizes, etc. |
|
362 * @param int $attachment_id The attachment post ID for the image. |
|
363 */ |
|
364 $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id ); |
|
365 |
|
366 return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ); |
|
367 } |
|
368 |
|
369 /** |
|
370 * Low-level function to create image sub-sizes. |
|
371 * |
|
372 * Updates the image meta after each sub-size is created. |
|
373 * Errors are stored in the returned image metadata array. |
|
374 * |
|
375 * @since 5.3.0 |
|
376 * @access private |
|
377 * |
|
378 * @param array $new_sizes Array defining what sizes to create. |
|
379 * @param string $file Full path to the image file. |
|
380 * @param array $image_meta The attachment meta data array. |
|
381 * @param int $attachment_id Attachment Id to process. |
|
382 * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. |
|
383 */ |
|
384 function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { |
|
385 if ( empty( $image_meta ) || ! is_array( $image_meta ) ) { |
|
386 // Not an image attachment. |
|
387 return array(); |
|
388 } |
|
389 |
|
390 // Check if any of the new sizes already exist. |
|
391 if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { |
|
392 foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { |
|
393 /* |
|
394 * Only checks "size name" so we don't override existing images even if the dimensions |
|
395 * don't match the currently defined size with the same name. |
|
396 * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. |
|
397 */ |
|
398 if ( array_key_exists( $size_name, $new_sizes ) ) { |
|
399 unset( $new_sizes[ $size_name ] ); |
|
400 } |
|
401 } |
|
402 } else { |
|
403 $image_meta['sizes'] = array(); |
|
404 } |
|
405 |
|
406 if ( empty( $new_sizes ) ) { |
|
407 // Nothing to do... |
|
408 return $image_meta; |
|
409 } |
|
410 |
|
411 /* |
|
412 * Sort the image sub-sizes in order of priority when creating them. |
|
413 * This ensures there is an appropriate sub-size the user can access immediately |
|
414 * even when there was an error and not all sub-sizes were created. |
|
415 */ |
|
416 $priority = array( |
|
417 'medium' => null, |
|
418 'large' => null, |
|
419 'thumbnail' => null, |
|
420 'medium_large' => null, |
|
421 ); |
|
422 |
|
423 $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); |
|
424 |
|
425 $editor = wp_get_image_editor( $file ); |
|
426 |
|
427 if ( is_wp_error( $editor ) ) { |
|
428 // The image cannot be edited. |
|
429 return $image_meta; |
|
430 } |
|
431 |
|
432 // If stored EXIF data exists, rotate the source image before creating sub-sizes. |
|
433 if ( ! empty( $image_meta['image_meta'] ) ) { |
|
434 $rotated = $editor->maybe_exif_rotate(); |
|
435 |
|
436 if ( is_wp_error( $rotated ) ) { |
|
437 // TODO: Log errors. |
|
438 } |
|
439 } |
|
440 |
|
441 if ( method_exists( $editor, 'make_subsize' ) ) { |
|
442 foreach ( $new_sizes as $new_size_name => $new_size_data ) { |
|
443 $new_size_meta = $editor->make_subsize( $new_size_data ); |
|
444 |
|
445 if ( is_wp_error( $new_size_meta ) ) { |
|
446 // TODO: Log errors. |
|
447 } else { |
|
448 // Save the size meta value. |
|
449 $image_meta['sizes'][ $new_size_name ] = $new_size_meta; |
|
450 wp_update_attachment_metadata( $attachment_id, $image_meta ); |
|
451 } |
|
452 } |
|
453 } else { |
|
454 // Fall back to `$editor->multi_resize()`. |
|
455 $created_sizes = $editor->multi_resize( $new_sizes ); |
|
456 |
|
457 if ( ! empty( $created_sizes ) ) { |
|
458 $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); |
|
459 wp_update_attachment_metadata( $attachment_id, $image_meta ); |
|
460 } |
|
461 } |
|
462 |
|
463 return $image_meta; |
|
464 } |
|
465 |
|
466 /** |
|
467 * Generate attachment meta data and create image sub-sizes for images. |
|
71 * |
468 * |
72 * @since 2.1.0 |
469 * @since 2.1.0 |
73 * |
470 * |
74 * @param int $attachment_id Attachment Id to process. |
471 * @param int $attachment_id Attachment Id to process. |
75 * @param string $file Filepath of the Attached image. |
472 * @param string $file Filepath of the Attached image. |
76 * @return mixed Metadata for attachment. |
473 * @return mixed Metadata for attachment. |
77 */ |
474 */ |
78 function wp_generate_attachment_metadata( $attachment_id, $file ) { |
475 function wp_generate_attachment_metadata( $attachment_id, $file ) { |
79 $attachment = get_post( $attachment_id ); |
476 $attachment = get_post( $attachment_id ); |
80 |
477 |
81 $metadata = array(); |
478 $metadata = array(); |
82 $support = false; |
479 $support = false; |
83 $mime_type = get_post_mime_type( $attachment ); |
480 $mime_type = get_post_mime_type( $attachment ); |
84 |
481 |
85 if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) { |
482 if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) { |
86 $imagesize = getimagesize( $file ); |
|
87 $metadata['width'] = $imagesize[0]; |
|
88 $metadata['height'] = $imagesize[1]; |
|
89 |
|
90 // Make the file path relative to the upload dir. |
|
91 $metadata['file'] = _wp_relative_upload_path( $file ); |
|
92 |
|
93 // Make thumbnails and other intermediate sizes. |
483 // Make thumbnails and other intermediate sizes. |
94 $_wp_additional_image_sizes = wp_get_additional_image_sizes(); |
484 $metadata = wp_create_image_subsizes( $file, $attachment_id ); |
95 |
|
96 $sizes = array(); |
|
97 foreach ( get_intermediate_image_sizes() as $s ) { |
|
98 $sizes[ $s ] = array( |
|
99 'width' => '', |
|
100 'height' => '', |
|
101 'crop' => false, |
|
102 ); |
|
103 if ( isset( $_wp_additional_image_sizes[ $s ]['width'] ) ) { |
|
104 // For theme-added sizes |
|
105 $sizes[ $s ]['width'] = intval( $_wp_additional_image_sizes[ $s ]['width'] ); |
|
106 } else { |
|
107 // For default sizes set in options |
|
108 $sizes[ $s ]['width'] = get_option( "{$s}_size_w" ); |
|
109 } |
|
110 |
|
111 if ( isset( $_wp_additional_image_sizes[ $s ]['height'] ) ) { |
|
112 // For theme-added sizes |
|
113 $sizes[ $s ]['height'] = intval( $_wp_additional_image_sizes[ $s ]['height'] ); |
|
114 } else { |
|
115 // For default sizes set in options |
|
116 $sizes[ $s ]['height'] = get_option( "{$s}_size_h" ); |
|
117 } |
|
118 |
|
119 if ( isset( $_wp_additional_image_sizes[ $s ]['crop'] ) ) { |
|
120 // For theme-added sizes |
|
121 $sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop']; |
|
122 } else { |
|
123 // For default sizes set in options |
|
124 $sizes[ $s ]['crop'] = get_option( "{$s}_crop" ); |
|
125 } |
|
126 } |
|
127 |
|
128 /** |
|
129 * Filters the image sizes automatically generated when uploading an image. |
|
130 * |
|
131 * @since 2.9.0 |
|
132 * @since 4.4.0 Added the `$metadata` argument. |
|
133 * @since 5.1.0 Added the `$attachment_id` argument. |
|
134 * |
|
135 * @param array $sizes An associative array of image sizes. |
|
136 * @param array $metadata An associative array of image metadata: width, height, file. |
|
137 * @param int $attachment_id Current attachment ID. |
|
138 */ |
|
139 $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes, $metadata, $attachment_id ); |
|
140 |
|
141 if ( $sizes ) { |
|
142 $editor = wp_get_image_editor( $file ); |
|
143 |
|
144 if ( ! is_wp_error( $editor ) ) { |
|
145 $metadata['sizes'] = $editor->multi_resize( $sizes ); |
|
146 } |
|
147 } else { |
|
148 $metadata['sizes'] = array(); |
|
149 } |
|
150 |
|
151 // Fetch additional metadata from EXIF/IPTC. |
|
152 $image_meta = wp_read_image_metadata( $file ); |
|
153 if ( $image_meta ) { |
|
154 $metadata['image_meta'] = $image_meta; |
|
155 } |
|
156 } elseif ( wp_attachment_is( 'video', $attachment ) ) { |
485 } elseif ( wp_attachment_is( 'video', $attachment ) ) { |
157 $metadata = wp_read_video_metadata( $file ); |
486 $metadata = wp_read_video_metadata( $file ); |
158 $support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' ); |
487 $support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' ); |
159 } elseif ( wp_attachment_is( 'audio', $attachment ) ) { |
488 } elseif ( wp_attachment_is( 'audio', $attachment ) ) { |
160 $metadata = wp_read_audio_metadata( $file ); |
489 $metadata = wp_read_audio_metadata( $file ); |
202 * |
531 * |
203 * @since 3.9.0 |
532 * @since 3.9.0 |
204 * |
533 * |
205 * @param array $image_attachment An array of parameters to create the thumbnail. |
534 * @param array $image_attachment An array of parameters to create the thumbnail. |
206 * @param array $metadata Current attachment metadata. |
535 * @param array $metadata Current attachment metadata. |
207 * @param array $uploaded An array containing the thumbnail path and url. |
536 * @param array $uploaded { |
537 * Information about the newly-uploaded file. |
|
538 * |
|
539 * @type string $file Filename of the newly-uploaded file. |
|
540 * @type string $url URL of the uploaded file. |
|
541 * @type string $type File type. |
|
542 * } |
|
208 */ |
543 */ |
209 $image_attachment = apply_filters( 'attachment_thumbnail_args', $image_attachment, $metadata, $uploaded ); |
544 $image_attachment = apply_filters( 'attachment_thumbnail_args', $image_attachment, $metadata, $uploaded ); |
210 |
545 |
211 $sub_attachment_id = wp_insert_attachment( $image_attachment, $uploaded['file'] ); |
546 $sub_attachment_id = wp_insert_attachment( $image_attachment, $uploaded['file'] ); |
212 add_post_meta( $sub_attachment_id, '_cover_hash', $hash ); |
547 add_post_meta( $sub_attachment_id, '_cover_hash', $hash ); |
227 /** |
562 /** |
228 * Filters the image sizes generated for non-image mime types. |
563 * Filters the image sizes generated for non-image mime types. |
229 * |
564 * |
230 * @since 4.7.0 |
565 * @since 4.7.0 |
231 * |
566 * |
232 * @param array $fallback_sizes An array of image size names. |
567 * @param string[] $fallback_sizes An array of image size names. |
233 * @param array $metadata Current attachment metadata. |
568 * @param array $metadata Current attachment metadata. |
234 */ |
569 */ |
235 $fallback_sizes = apply_filters( 'fallback_intermediate_image_sizes', $fallback_sizes, $metadata ); |
570 $fallback_sizes = apply_filters( 'fallback_intermediate_image_sizes', $fallback_sizes, $metadata ); |
236 |
571 |
237 $sizes = array(); |
572 $registered_sizes = wp_get_registered_image_subsizes(); |
238 $_wp_additional_image_sizes = wp_get_additional_image_sizes(); |
573 $merged_sizes = array_intersect_key( $registered_sizes, array_flip( $fallback_sizes ) ); |
239 |
574 |
240 foreach ( $fallback_sizes as $s ) { |
575 // Force thumbnails to be soft crops. |
241 if ( isset( $_wp_additional_image_sizes[ $s ]['width'] ) ) { |
576 if ( isset( $merged_sizes['thumbnail'] ) && is_array( $merged_sizes['thumbnail'] ) ) { |
242 $sizes[ $s ]['width'] = intval( $_wp_additional_image_sizes[ $s ]['width'] ); |
577 $merged_sizes['thumbnail']['crop'] = false; |
243 } else { |
|
244 $sizes[ $s ]['width'] = get_option( "{$s}_size_w" ); |
|
245 } |
|
246 |
|
247 if ( isset( $_wp_additional_image_sizes[ $s ]['height'] ) ) { |
|
248 $sizes[ $s ]['height'] = intval( $_wp_additional_image_sizes[ $s ]['height'] ); |
|
249 } else { |
|
250 $sizes[ $s ]['height'] = get_option( "{$s}_size_h" ); |
|
251 } |
|
252 |
|
253 if ( isset( $_wp_additional_image_sizes[ $s ]['crop'] ) ) { |
|
254 $sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop']; |
|
255 } else { |
|
256 // Force thumbnails to be soft crops. |
|
257 if ( 'thumbnail' !== $s ) { |
|
258 $sizes[ $s ]['crop'] = get_option( "{$s}_crop" ); |
|
259 } |
|
260 } |
|
261 } |
578 } |
262 |
579 |
263 // Only load PDFs in an image editor if we're processing sizes. |
580 // Only load PDFs in an image editor if we're processing sizes. |
264 if ( ! empty( $sizes ) ) { |
581 if ( ! empty( $merged_sizes ) ) { |
265 $editor = wp_get_image_editor( $file ); |
582 $editor = wp_get_image_editor( $file ); |
266 |
583 |
267 if ( ! is_wp_error( $editor ) ) { // No support for this type of file |
584 if ( ! is_wp_error( $editor ) ) { // No support for this type of file. |
268 /* |
585 /* |
269 * PDFs may have the same file filename as JPEGs. |
586 * PDFs may have the same file filename as JPEGs. |
270 * Ensure the PDF preview image does not overwrite any JPEG images that already exist. |
587 * Ensure the PDF preview image does not overwrite any JPEG images that already exist. |
271 */ |
588 */ |
272 $dirname = dirname( $file ) . '/'; |
589 $dirname = dirname( $file ) . '/'; |
276 $uploaded = $editor->save( $preview_file, 'image/jpeg' ); |
593 $uploaded = $editor->save( $preview_file, 'image/jpeg' ); |
277 unset( $editor ); |
594 unset( $editor ); |
278 |
595 |
279 // Resize based on the full size image, rather than the source. |
596 // Resize based on the full size image, rather than the source. |
280 if ( ! is_wp_error( $uploaded ) ) { |
597 if ( ! is_wp_error( $uploaded ) ) { |
281 $editor = wp_get_image_editor( $uploaded['path'] ); |
598 $image_file = $uploaded['path']; |
282 unset( $uploaded['path'] ); |
599 unset( $uploaded['path'] ); |
283 |
600 |
284 if ( ! is_wp_error( $editor ) ) { |
601 $metadata['sizes'] = array( |
285 $metadata['sizes'] = $editor->multi_resize( $sizes ); |
602 'full' => $uploaded, |
286 $metadata['sizes']['full'] = $uploaded; |
603 ); |
287 } |
604 |
605 // Save the meta data before any image post-processing errors could happen. |
|
606 wp_update_attachment_metadata( $attachment_id, $metadata ); |
|
607 |
|
608 // Create sub-sizes saving the image meta after each. |
|
609 $metadata = _wp_make_subsizes( $merged_sizes, $image_file, $metadata, $attachment_id ); |
|
288 } |
610 } |
289 } |
611 } |
290 } |
612 } |
291 } |
613 } |
292 |
614 |
297 |
619 |
298 /** |
620 /** |
299 * Filters the generated attachment meta data. |
621 * Filters the generated attachment meta data. |
300 * |
622 * |
301 * @since 2.1.0 |
623 * @since 2.1.0 |
302 * |
624 * @since 5.3.0 The `$context` parameter was added. |
303 * @param array $metadata An array of attachment meta data. |
625 * |
304 * @param int $attachment_id Current attachment ID. |
626 * @param array $metadata An array of attachment meta data. |
305 */ |
627 * @param int $attachment_id Current attachment ID. |
306 return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id ); |
628 * @param string $context Additional context. Can be 'create' when metadata was initially created for new attachment |
629 * or 'update' when the metadata was updated. |
|
630 */ |
|
631 return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' ); |
|
307 } |
632 } |
308 |
633 |
309 /** |
634 /** |
310 * Convert a fraction string to a decimal. |
635 * Convert a fraction string to a decimal. |
311 * |
636 * |
313 * |
638 * |
314 * @param string $str |
639 * @param string $str |
315 * @return int|float |
640 * @return int|float |
316 */ |
641 */ |
317 function wp_exif_frac2dec( $str ) { |
642 function wp_exif_frac2dec( $str ) { |
318 @list( $n, $d ) = explode( '/', $str ); |
643 if ( false === strpos( $str, '/' ) ) { |
319 if ( ! empty( $d ) ) { |
644 return $str; |
320 return $n / $d; |
645 } |
646 |
|
647 list( $numerator, $denominator ) = explode( '/', $str ); |
|
648 if ( ! empty( $denominator ) ) { |
|
649 return $numerator / $denominator; |
|
321 } |
650 } |
322 return $str; |
651 return $str; |
323 } |
652 } |
324 |
653 |
325 /** |
654 /** |
329 * |
658 * |
330 * @param string $str |
659 * @param string $str |
331 * @return int |
660 * @return int |
332 */ |
661 */ |
333 function wp_exif_date2ts( $str ) { |
662 function wp_exif_date2ts( $str ) { |
334 @list( $date, $time ) = explode( ' ', trim( $str ) ); |
663 list( $date, $time ) = explode( ' ', trim( $str ) ); |
335 @list( $y, $m, $d ) = explode( ':', $date ); |
664 list( $y, $m, $d ) = explode( ':', $date ); |
336 |
665 |
337 return strtotime( "{$y}-{$m}-{$d} {$time}" ); |
666 return strtotime( "{$y}-{$m}-{$d} {$time}" ); |
338 } |
667 } |
339 |
668 |
340 /** |
669 /** |
390 @getimagesize( $file, $info ); |
719 @getimagesize( $file, $info ); |
391 |
720 |
392 if ( ! empty( $info['APP13'] ) ) { |
721 if ( ! empty( $info['APP13'] ) ) { |
393 $iptc = @iptcparse( $info['APP13'] ); |
722 $iptc = @iptcparse( $info['APP13'] ); |
394 |
723 |
395 // Headline, "A brief synopsis of the caption." |
724 // Headline, "A brief synopsis of the caption". |
396 if ( ! empty( $iptc['2#105'][0] ) ) { |
725 if ( ! empty( $iptc['2#105'][0] ) ) { |
397 $meta['title'] = trim( $iptc['2#105'][0] ); |
726 $meta['title'] = trim( $iptc['2#105'][0] ); |
398 /* |
727 /* |
399 * Title, "Many use the Title field to store the filename of the image, |
728 * Title, "Many use the Title field to store the filename of the image, |
400 * though the field may be used in many ways." |
729 * though the field may be used in many ways". |
401 */ |
730 */ |
402 } elseif ( ! empty( $iptc['2#005'][0] ) ) { |
731 } elseif ( ! empty( $iptc['2#005'][0] ) ) { |
403 $meta['title'] = trim( $iptc['2#005'][0] ); |
732 $meta['title'] = trim( $iptc['2#005'][0] ); |
404 } |
733 } |
405 |
734 |
406 if ( ! empty( $iptc['2#120'][0] ) ) { // description / legacy caption |
735 if ( ! empty( $iptc['2#120'][0] ) ) { // Description / legacy caption. |
407 $caption = trim( $iptc['2#120'][0] ); |
736 $caption = trim( $iptc['2#120'][0] ); |
408 |
737 |
409 mbstring_binary_safe_encoding(); |
738 mbstring_binary_safe_encoding(); |
410 $caption_length = strlen( $caption ); |
739 $caption_length = strlen( $caption ); |
411 reset_mbstring_encoding(); |
740 reset_mbstring_encoding(); |
416 } |
745 } |
417 |
746 |
418 $meta['caption'] = $caption; |
747 $meta['caption'] = $caption; |
419 } |
748 } |
420 |
749 |
421 if ( ! empty( $iptc['2#110'][0] ) ) { // credit |
750 if ( ! empty( $iptc['2#110'][0] ) ) { // Credit. |
422 $meta['credit'] = trim( $iptc['2#110'][0] ); |
751 $meta['credit'] = trim( $iptc['2#110'][0] ); |
423 } elseif ( ! empty( $iptc['2#080'][0] ) ) { // creator / legacy byline |
752 } elseif ( ! empty( $iptc['2#080'][0] ) ) { // Creator / legacy byline. |
424 $meta['credit'] = trim( $iptc['2#080'][0] ); |
753 $meta['credit'] = trim( $iptc['2#080'][0] ); |
425 } |
754 } |
426 |
755 |
427 if ( ! empty( $iptc['2#055'][0] ) && ! empty( $iptc['2#060'][0] ) ) { // created date and time |
756 if ( ! empty( $iptc['2#055'][0] ) && ! empty( $iptc['2#060'][0] ) ) { // Created date and time. |
428 $meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] ); |
757 $meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] ); |
429 } |
758 } |
430 |
759 |
431 if ( ! empty( $iptc['2#116'][0] ) ) { // copyright |
760 if ( ! empty( $iptc['2#116'][0] ) ) { // Copyright. |
432 $meta['copyright'] = trim( $iptc['2#116'][0] ); |
761 $meta['copyright'] = trim( $iptc['2#116'][0] ); |
433 } |
762 } |
434 |
763 |
435 if ( ! empty( $iptc['2#025'][0] ) ) { // keywords array |
764 if ( ! empty( $iptc['2#025'][0] ) ) { // Keywords array. |
436 $meta['keywords'] = array_values( $iptc['2#025'] ); |
765 $meta['keywords'] = array_values( $iptc['2#025'] ); |
437 } |
766 } |
438 } |
767 } |
439 } |
768 } |
440 |
769 |
447 * |
776 * |
448 * @param array $image_types Image types to check for exif data. |
777 * @param array $image_types Image types to check for exif data. |
449 */ |
778 */ |
450 $exif_image_types = apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) ); |
779 $exif_image_types = apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) ); |
451 |
780 |
452 if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types ) ) { |
781 if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types, true ) ) { |
453 $exif = @exif_read_data( $file ); |
782 $exif = @exif_read_data( $file ); |
454 |
783 |
455 if ( ! empty( $exif['ImageDescription'] ) ) { |
784 if ( ! empty( $exif['ImageDescription'] ) ) { |
456 mbstring_binary_safe_encoding(); |
785 mbstring_binary_safe_encoding(); |
457 $description_length = strlen( $exif['ImageDescription'] ); |
786 $description_length = strlen( $exif['ImageDescription'] ); |
458 reset_mbstring_encoding(); |
787 reset_mbstring_encoding(); |
459 |
788 |
460 if ( empty( $meta['title'] ) && $description_length < 80 ) { |
789 if ( empty( $meta['title'] ) && $description_length < 80 ) { |
461 // Assume the title is stored in ImageDescription |
790 // Assume the title is stored in ImageDescription. |
462 $meta['title'] = trim( $exif['ImageDescription'] ); |
791 $meta['title'] = trim( $exif['ImageDescription'] ); |
463 } |
792 } |
464 |
793 |
465 if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) ) { |
794 if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) ) { |
466 $meta['caption'] = trim( $exif['COMPUTED']['UserComment'] ); |
795 $meta['caption'] = trim( $exif['COMPUTED']['UserComment'] ); |
559 * |
888 * |
560 * @param string $path File path to test. |
889 * @param string $path File path to test. |
561 * @return bool True if suitable, false if not suitable. |
890 * @return bool True if suitable, false if not suitable. |
562 */ |
891 */ |
563 function file_is_displayable_image( $path ) { |
892 function file_is_displayable_image( $path ) { |
564 $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP ); |
893 $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO ); |
565 |
|
566 // IMAGETYPE_ICO is only defined in PHP 5.3+. |
|
567 if ( defined( 'IMAGETYPE_ICO' ) ) { |
|
568 $displayable_image_types[] = IMAGETYPE_ICO; |
|
569 } |
|
570 |
894 |
571 $info = @getimagesize( $path ); |
895 $info = @getimagesize( $path ); |
572 if ( empty( $info ) ) { |
896 if ( empty( $info ) ) { |
573 $result = false; |
897 $result = false; |
574 } elseif ( ! in_array( $info[2], $displayable_image_types ) ) { |
898 } elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) { |
575 $result = false; |
899 $result = false; |
576 } else { |
900 } else { |
577 $result = true; |
901 $result = true; |
578 } |
902 } |
579 |
903 |
652 */ |
976 */ |
653 function _load_image_to_edit_path( $attachment_id, $size = 'full' ) { |
977 function _load_image_to_edit_path( $attachment_id, $size = 'full' ) { |
654 $filepath = get_attached_file( $attachment_id ); |
978 $filepath = get_attached_file( $attachment_id ); |
655 |
979 |
656 if ( $filepath && file_exists( $filepath ) ) { |
980 if ( $filepath && file_exists( $filepath ) ) { |
657 if ( 'full' != $size && ( $data = image_get_intermediate_size( $attachment_id, $size ) ) ) { |
981 if ( 'full' !== $size ) { |
658 /** |
982 $data = image_get_intermediate_size( $attachment_id, $size ); |
659 * Filters the path to the current image. |
983 |
660 * |
984 if ( $data ) { |
661 * The filter is evaluated for all image sizes except 'full'. |
985 $filepath = path_join( dirname( $filepath ), $data['file'] ); |
662 * |
986 |
663 * @since 3.1.0 |
987 /** |
664 * |
988 * Filters the path to the current image. |
665 * @param string $path Path to the current image. |
989 * |
666 * @param string $attachment_id Attachment ID. |
990 * The filter is evaluated for all image sizes except 'full'. |
667 * @param string $size Size of the image. |
991 * |
668 */ |
992 * @since 3.1.0 |
669 $filepath = apply_filters( 'load_image_to_edit_filesystempath', path_join( dirname( $filepath ), $data['file'] ), $attachment_id, $size ); |
993 * |
670 } |
994 * @param string $path Path to the current image. |
671 } elseif ( function_exists( 'fopen' ) && true == ini_get( 'allow_url_fopen' ) ) { |
995 * @param string $attachment_id Attachment ID. |
996 * @param string $size Size of the image. |
|
997 */ |
|
998 $filepath = apply_filters( 'load_image_to_edit_filesystempath', $filepath, $attachment_id, $size ); |
|
999 } |
|
1000 } |
|
1001 } elseif ( function_exists( 'fopen' ) && ini_get( 'allow_url_fopen' ) ) { |
|
672 /** |
1002 /** |
673 * Filters the image URL if not in the local filesystem. |
1003 * Filters the image URL if not in the local filesystem. |
674 * |
1004 * |
675 * The filter is only evaluated if fopen is enabled on the server. |
1005 * The filter is only evaluated if fopen is enabled on the server. |
676 * |
1006 * |
703 * |
1033 * |
704 * @param string $attachment_id Attachment ID. |
1034 * @param string $attachment_id Attachment ID. |
705 * @return string|false New file path on success, false on failure. |
1035 * @return string|false New file path on success, false on failure. |
706 */ |
1036 */ |
707 function _copy_image_file( $attachment_id ) { |
1037 function _copy_image_file( $attachment_id ) { |
708 $dst_file = $src_file = get_attached_file( $attachment_id ); |
1038 $dst_file = get_attached_file( $attachment_id ); |
1039 $src_file = $dst_file; |
|
1040 |
|
709 if ( ! file_exists( $src_file ) ) { |
1041 if ( ! file_exists( $src_file ) ) { |
710 $src_file = _load_image_to_edit_path( $attachment_id ); |
1042 $src_file = _load_image_to_edit_path( $attachment_id ); |
711 } |
1043 } |
712 |
1044 |
713 if ( $src_file ) { |
1045 if ( $src_file ) { |
718 * The directory containing the original file may no longer |
1050 * The directory containing the original file may no longer |
719 * exist when using a replication plugin. |
1051 * exist when using a replication plugin. |
720 */ |
1052 */ |
721 wp_mkdir_p( dirname( $dst_file ) ); |
1053 wp_mkdir_p( dirname( $dst_file ) ); |
722 |
1054 |
723 if ( ! @copy( $src_file, $dst_file ) ) { |
1055 if ( ! copy( $src_file, $dst_file ) ) { |
724 $dst_file = false; |
1056 $dst_file = false; |
725 } |
1057 } |
726 } else { |
1058 } else { |
727 $dst_file = false; |
1059 $dst_file = false; |
728 } |
1060 } |