[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * File contains all the administration image manipulation functions. 4 * 5 * @package WordPress 6 * @subpackage Administration 7 */ 8 9 /** 10 * Crops an image to a given size. 11 * 12 * @since 2.1.0 13 * 14 * @param string|int $src The source file or Attachment ID. 15 * @param int $src_x The start x 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. 18 * @param int $src_h The height to crop. 19 * @param int $dst_w The destination width. 20 * @param int $dst_h The destination height. 21 * @param bool|false $src_abs Optional. If the source crop points are absolute. 22 * @param string|false $dst_file Optional. The destination file to write to. 23 * @return string|WP_Error New filepath on success, WP_Error on failure. 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 ) { 26 $src_file = $src; 27 if ( is_numeric( $src ) ) { // Handle int as attachment ID. 28 $src_file = get_attached_file( $src ); 29 30 if ( ! file_exists( $src_file ) ) { 31 // If the file doesn't exist, attempt a URL fopen on the src link. 32 // This can occur with certain file replication plugins. 33 $src = _load_image_to_edit_path( $src, 'full' ); 34 } else { 35 $src = $src_file; 36 } 37 } 38 39 $editor = wp_get_image_editor( $src ); 40 if ( is_wp_error( $editor ) ) { 41 return $editor; 42 } 43 44 $src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs ); 45 if ( is_wp_error( $src ) ) { 46 return $src; 47 } 48 49 if ( ! $dst_file ) { 50 $dst_file = str_replace( wp_basename( $src_file ), 'cropped-' . wp_basename( $src_file ), $src_file ); 51 } 52 53 /* 54 * The directory containing the original file may no longer exist when 55 * using a replication plugin. 56 */ 57 wp_mkdir_p( dirname( $dst_file ) ); 58 59 $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), wp_basename( $dst_file ) ); 60 61 $result = $editor->save( $dst_file ); 62 if ( is_wp_error( $result ) ) { 63 return $result; 64 } 65 66 return $dst_file; 67 } 68 69 /** 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. 468 * 469 * @since 2.1.0 470 * 471 * @param int $attachment_id Attachment Id to process. 472 * @param string $file Filepath of the Attached image. 473 * @return mixed Metadata for attachment. 474 */ 475 function wp_generate_attachment_metadata( $attachment_id, $file ) { 476 $attachment = get_post( $attachment_id ); 477 478 $metadata = array(); 479 $support = false; 480 $mime_type = get_post_mime_type( $attachment ); 481 482 if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) { 483 // Make thumbnails and other intermediate sizes. 484 $metadata = wp_create_image_subsizes( $file, $attachment_id ); 485 } elseif ( wp_attachment_is( 'video', $attachment ) ) { 486 $metadata = wp_read_video_metadata( $file ); 487 $support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' ); 488 } elseif ( wp_attachment_is( 'audio', $attachment ) ) { 489 $metadata = wp_read_audio_metadata( $file ); 490 $support = current_theme_supports( 'post-thumbnails', 'attachment:audio' ) || post_type_supports( 'attachment:audio', 'thumbnail' ); 491 } 492 493 if ( $support && ! empty( $metadata['image']['data'] ) ) { 494 // Check for existing cover. 495 $hash = md5( $metadata['image']['data'] ); 496 $posts = get_posts( 497 array( 498 'fields' => 'ids', 499 'post_type' => 'attachment', 500 'post_mime_type' => $metadata['image']['mime'], 501 'post_status' => 'inherit', 502 'posts_per_page' => 1, 503 'meta_key' => '_cover_hash', 504 'meta_value' => $hash, 505 ) 506 ); 507 $exists = reset( $posts ); 508 509 if ( ! empty( $exists ) ) { 510 update_post_meta( $attachment_id, '_thumbnail_id', $exists ); 511 } else { 512 $ext = '.jpg'; 513 switch ( $metadata['image']['mime'] ) { 514 case 'image/gif': 515 $ext = '.gif'; 516 break; 517 case 'image/png': 518 $ext = '.png'; 519 break; 520 } 521 $basename = str_replace( '.', '-', wp_basename( $file ) ) . '-image' . $ext; 522 $uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] ); 523 if ( false === $uploaded['error'] ) { 524 $image_attachment = array( 525 'post_mime_type' => $metadata['image']['mime'], 526 'post_type' => 'attachment', 527 'post_content' => '', 528 ); 529 /** 530 * Filters the parameters for the attachment thumbnail creation. 531 * 532 * @since 3.9.0 533 * 534 * @param array $image_attachment An array of parameters to create the thumbnail. 535 * @param array $metadata Current attachment metadata. 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 * } 543 */ 544 $image_attachment = apply_filters( 'attachment_thumbnail_args', $image_attachment, $metadata, $uploaded ); 545 546 $sub_attachment_id = wp_insert_attachment( $image_attachment, $uploaded['file'] ); 547 add_post_meta( $sub_attachment_id, '_cover_hash', $hash ); 548 $attach_data = wp_generate_attachment_metadata( $sub_attachment_id, $uploaded['file'] ); 549 wp_update_attachment_metadata( $sub_attachment_id, $attach_data ); 550 update_post_meta( $attachment_id, '_thumbnail_id', $sub_attachment_id ); 551 } 552 } 553 } elseif ( 'application/pdf' === $mime_type ) { 554 // Try to create image thumbnails for PDFs. 555 556 $fallback_sizes = array( 557 'thumbnail', 558 'medium', 559 'large', 560 ); 561 562 /** 563 * Filters the image sizes generated for non-image mime types. 564 * 565 * @since 4.7.0 566 * 567 * @param string[] $fallback_sizes An array of image size names. 568 * @param array $metadata Current attachment metadata. 569 */ 570 $fallback_sizes = apply_filters( 'fallback_intermediate_image_sizes', $fallback_sizes, $metadata ); 571 572 $registered_sizes = wp_get_registered_image_subsizes(); 573 $merged_sizes = array_intersect_key( $registered_sizes, array_flip( $fallback_sizes ) ); 574 575 // Force thumbnails to be soft crops. 576 if ( isset( $merged_sizes['thumbnail'] ) && is_array( $merged_sizes['thumbnail'] ) ) { 577 $merged_sizes['thumbnail']['crop'] = false; 578 } 579 580 // Only load PDFs in an image editor if we're processing sizes. 581 if ( ! empty( $merged_sizes ) ) { 582 $editor = wp_get_image_editor( $file ); 583 584 if ( ! is_wp_error( $editor ) ) { // No support for this type of file. 585 /* 586 * PDFs may have the same file filename as JPEGs. 587 * Ensure the PDF preview image does not overwrite any JPEG images that already exist. 588 */ 589 $dirname = dirname( $file ) . '/'; 590 $ext = '.' . pathinfo( $file, PATHINFO_EXTENSION ); 591 $preview_file = $dirname . wp_unique_filename( $dirname, wp_basename( $file, $ext ) . '-pdf.jpg' ); 592 593 $uploaded = $editor->save( $preview_file, 'image/jpeg' ); 594 unset( $editor ); 595 596 // Resize based on the full size image, rather than the source. 597 if ( ! is_wp_error( $uploaded ) ) { 598 $image_file = $uploaded['path']; 599 unset( $uploaded['path'] ); 600 601 $metadata['sizes'] = array( 602 'full' => $uploaded, 603 ); 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 ); 610 } 611 } 612 } 613 } 614 615 // Remove the blob of binary data from the array. 616 if ( $metadata ) { 617 unset( $metadata['image']['data'] ); 618 } 619 620 /** 621 * Filters the generated attachment meta data. 622 * 623 * @since 2.1.0 624 * @since 5.3.0 The `$context` parameter was added. 625 * 626 * @param array $metadata An array of attachment meta data. 627 * @param int $attachment_id Current 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' ); 632 } 633 634 /** 635 * Convert a fraction string to a decimal. 636 * 637 * @since 2.5.0 638 * 639 * @param string $str 640 * @return int|float 641 */ 642 function wp_exif_frac2dec( $str ) { 643 if ( false === strpos( $str, '/' ) ) { 644 return $str; 645 } 646 647 list( $numerator, $denominator ) = explode( '/', $str ); 648 if ( ! empty( $denominator ) ) { 649 return $numerator / $denominator; 650 } 651 return $str; 652 } 653 654 /** 655 * Convert the exif date format to a unix timestamp. 656 * 657 * @since 2.5.0 658 * 659 * @param string $str 660 * @return int 661 */ 662 function wp_exif_date2ts( $str ) { 663 list( $date, $time ) = explode( ' ', trim( $str ) ); 664 list( $y, $m, $d ) = explode( ':', $date ); 665 666 return strtotime( "{$y}-{$m}-{$d} {$time}" ); 667 } 668 669 /** 670 * Get extended image metadata, exif or iptc as available. 671 * 672 * Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso 673 * created_timestamp, focal_length, shutter_speed, and title. 674 * 675 * The IPTC metadata that is retrieved is APP13, credit, byline, created date 676 * and time, caption, copyright, and title. Also includes FNumber, Model, 677 * DateTimeDigitized, FocalLength, ISOSpeedRatings, and ExposureTime. 678 * 679 * @todo Try other exif libraries if available. 680 * @since 2.5.0 681 * 682 * @param string $file 683 * @return array|false Image metadata array on success, false on failure. 684 */ 685 function wp_read_image_metadata( $file ) { 686 if ( ! file_exists( $file ) ) { 687 return false; 688 } 689 690 list( , , $image_type ) = @getimagesize( $file ); 691 692 /* 693 * EXIF contains a bunch of data we'll probably never need formatted in ways 694 * that are difficult to use. We'll normalize it and just extract the fields 695 * that are likely to be useful. Fractions and numbers are converted to 696 * floats, dates to unix timestamps, and everything else to strings. 697 */ 698 $meta = array( 699 'aperture' => 0, 700 'credit' => '', 701 'camera' => '', 702 'caption' => '', 703 'created_timestamp' => 0, 704 'copyright' => '', 705 'focal_length' => 0, 706 'iso' => 0, 707 'shutter_speed' => 0, 708 'title' => '', 709 'orientation' => 0, 710 'keywords' => array(), 711 ); 712 713 $iptc = array(); 714 /* 715 * Read IPTC first, since it might contain data not available in exif such 716 * as caption, description etc. 717 */ 718 if ( is_callable( 'iptcparse' ) ) { 719 @getimagesize( $file, $info ); 720 721 if ( ! empty( $info['APP13'] ) ) { 722 $iptc = @iptcparse( $info['APP13'] ); 723 724 // Headline, "A brief synopsis of the caption". 725 if ( ! empty( $iptc['2#105'][0] ) ) { 726 $meta['title'] = trim( $iptc['2#105'][0] ); 727 /* 728 * Title, "Many use the Title field to store the filename of the image, 729 * though the field may be used in many ways". 730 */ 731 } elseif ( ! empty( $iptc['2#005'][0] ) ) { 732 $meta['title'] = trim( $iptc['2#005'][0] ); 733 } 734 735 if ( ! empty( $iptc['2#120'][0] ) ) { // Description / legacy caption. 736 $caption = trim( $iptc['2#120'][0] ); 737 738 mbstring_binary_safe_encoding(); 739 $caption_length = strlen( $caption ); 740 reset_mbstring_encoding(); 741 742 if ( empty( $meta['title'] ) && $caption_length < 80 ) { 743 // Assume the title is stored in 2:120 if it's short. 744 $meta['title'] = $caption; 745 } 746 747 $meta['caption'] = $caption; 748 } 749 750 if ( ! empty( $iptc['2#110'][0] ) ) { // Credit. 751 $meta['credit'] = trim( $iptc['2#110'][0] ); 752 } elseif ( ! empty( $iptc['2#080'][0] ) ) { // Creator / legacy byline. 753 $meta['credit'] = trim( $iptc['2#080'][0] ); 754 } 755 756 if ( ! empty( $iptc['2#055'][0] ) && ! empty( $iptc['2#060'][0] ) ) { // Created date and time. 757 $meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] ); 758 } 759 760 if ( ! empty( $iptc['2#116'][0] ) ) { // Copyright. 761 $meta['copyright'] = trim( $iptc['2#116'][0] ); 762 } 763 764 if ( ! empty( $iptc['2#025'][0] ) ) { // Keywords array. 765 $meta['keywords'] = array_values( $iptc['2#025'] ); 766 } 767 } 768 } 769 770 $exif = array(); 771 772 /** 773 * Filters the image types to check for exif data. 774 * 775 * @since 2.5.0 776 * 777 * @param array $image_types Image types to check for exif data. 778 */ 779 $exif_image_types = apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) ); 780 781 if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types, true ) ) { 782 $exif = @exif_read_data( $file ); 783 784 if ( ! empty( $exif['ImageDescription'] ) ) { 785 mbstring_binary_safe_encoding(); 786 $description_length = strlen( $exif['ImageDescription'] ); 787 reset_mbstring_encoding(); 788 789 if ( empty( $meta['title'] ) && $description_length < 80 ) { 790 // Assume the title is stored in ImageDescription. 791 $meta['title'] = trim( $exif['ImageDescription'] ); 792 } 793 794 if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) ) { 795 $meta['caption'] = trim( $exif['COMPUTED']['UserComment'] ); 796 } 797 798 if ( empty( $meta['caption'] ) ) { 799 $meta['caption'] = trim( $exif['ImageDescription'] ); 800 } 801 } elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) ) { 802 $meta['caption'] = trim( $exif['Comments'] ); 803 } 804 805 if ( empty( $meta['credit'] ) ) { 806 if ( ! empty( $exif['Artist'] ) ) { 807 $meta['credit'] = trim( $exif['Artist'] ); 808 } elseif ( ! empty( $exif['Author'] ) ) { 809 $meta['credit'] = trim( $exif['Author'] ); 810 } 811 } 812 813 if ( empty( $meta['copyright'] ) && ! empty( $exif['Copyright'] ) ) { 814 $meta['copyright'] = trim( $exif['Copyright'] ); 815 } 816 if ( ! empty( $exif['FNumber'] ) ) { 817 $meta['aperture'] = round( wp_exif_frac2dec( $exif['FNumber'] ), 2 ); 818 } 819 if ( ! empty( $exif['Model'] ) ) { 820 $meta['camera'] = trim( $exif['Model'] ); 821 } 822 if ( empty( $meta['created_timestamp'] ) && ! empty( $exif['DateTimeDigitized'] ) ) { 823 $meta['created_timestamp'] = wp_exif_date2ts( $exif['DateTimeDigitized'] ); 824 } 825 if ( ! empty( $exif['FocalLength'] ) ) { 826 $meta['focal_length'] = (string) wp_exif_frac2dec( $exif['FocalLength'] ); 827 } 828 if ( ! empty( $exif['ISOSpeedRatings'] ) ) { 829 $meta['iso'] = is_array( $exif['ISOSpeedRatings'] ) ? reset( $exif['ISOSpeedRatings'] ) : $exif['ISOSpeedRatings']; 830 $meta['iso'] = trim( $meta['iso'] ); 831 } 832 if ( ! empty( $exif['ExposureTime'] ) ) { 833 $meta['shutter_speed'] = (string) wp_exif_frac2dec( $exif['ExposureTime'] ); 834 } 835 if ( ! empty( $exif['Orientation'] ) ) { 836 $meta['orientation'] = $exif['Orientation']; 837 } 838 } 839 840 foreach ( array( 'title', 'caption', 'credit', 'copyright', 'camera', 'iso' ) as $key ) { 841 if ( $meta[ $key ] && ! seems_utf8( $meta[ $key ] ) ) { 842 $meta[ $key ] = utf8_encode( $meta[ $key ] ); 843 } 844 } 845 846 foreach ( $meta['keywords'] as $key => $keyword ) { 847 if ( ! seems_utf8( $keyword ) ) { 848 $meta['keywords'][ $key ] = utf8_encode( $keyword ); 849 } 850 } 851 852 $meta = wp_kses_post_deep( $meta ); 853 854 /** 855 * Filters the array of meta data read from an image's exif data. 856 * 857 * @since 2.5.0 858 * @since 4.4.0 The `$iptc` parameter was added. 859 * @since 5.0.0 The `$exif` parameter was added. 860 * 861 * @param array $meta Image meta data. 862 * @param string $file Path to image file. 863 * @param int $image_type Type of image, one of the `IMAGETYPE_XXX` constants. 864 * @param array $iptc IPTC data. 865 * @param array $exif EXIF data. 866 */ 867 return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif ); 868 869 } 870 871 /** 872 * Validate that file is an image. 873 * 874 * @since 2.5.0 875 * 876 * @param string $path File path to test if valid image. 877 * @return bool True if valid image, false if not valid image. 878 */ 879 function file_is_valid_image( $path ) { 880 $size = @getimagesize( $path ); 881 return ! empty( $size ); 882 } 883 884 /** 885 * Validate that file is suitable for displaying within a web page. 886 * 887 * @since 2.5.0 888 * 889 * @param string $path File path to test. 890 * @return bool True if suitable, false if not suitable. 891 */ 892 function file_is_displayable_image( $path ) { 893 $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO ); 894 895 $info = @getimagesize( $path ); 896 if ( empty( $info ) ) { 897 $result = false; 898 } elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) { 899 $result = false; 900 } else { 901 $result = true; 902 } 903 904 /** 905 * Filters whether the current image is displayable in the browser. 906 * 907 * @since 2.5.0 908 * 909 * @param bool $result Whether the image can be displayed. Default true. 910 * @param string $path Path to the image. 911 */ 912 return apply_filters( 'file_is_displayable_image', $result, $path ); 913 } 914 915 /** 916 * Load an image resource for editing. 917 * 918 * @since 2.9.0 919 * 920 * @param int $attachment_id Attachment ID. 921 * @param string $mime_type Image mime type. 922 * @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array 923 * of width and height values in pixels (in that order). Default 'full'. 924 * @return resource|GdImage|false The resulting image resource or GdImage instance on success, 925 * false on failure. 926 */ 927 function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) { 928 $filepath = _load_image_to_edit_path( $attachment_id, $size ); 929 if ( empty( $filepath ) ) { 930 return false; 931 } 932 933 switch ( $mime_type ) { 934 case 'image/jpeg': 935 $image = imagecreatefromjpeg( $filepath ); 936 break; 937 case 'image/png': 938 $image = imagecreatefrompng( $filepath ); 939 break; 940 case 'image/gif': 941 $image = imagecreatefromgif( $filepath ); 942 break; 943 default: 944 $image = false; 945 break; 946 } 947 948 if ( is_gd_image( $image ) ) { 949 /** 950 * Filters the current image being loaded for editing. 951 * 952 * @since 2.9.0 953 * 954 * @param resource|GdImage $image Current image. 955 * @param int $attachment_id Attachment ID. 956 * @param string|int[] $size Requested image size. Can be any registered image size name, or 957 * an array of width and height values in pixels (in that order). 958 */ 959 $image = apply_filters( 'load_image_to_edit', $image, $attachment_id, $size ); 960 961 if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) { 962 imagealphablending( $image, false ); 963 imagesavealpha( $image, true ); 964 } 965 } 966 967 return $image; 968 } 969 970 /** 971 * Retrieve the path or URL of an attachment's attached file. 972 * 973 * If the attached file is not present on the local filesystem (usually due to replication plugins), 974 * then the URL of the file is returned if `allow_url_fopen` is supported. 975 * 976 * @since 3.4.0 977 * @access private 978 * 979 * @param int $attachment_id Attachment ID. 980 * @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array 981 * of width and height values in pixels (in that order). Default 'full'. 982 * @return string|false File path or URL on success, false on failure. 983 */ 984 function _load_image_to_edit_path( $attachment_id, $size = 'full' ) { 985 $filepath = get_attached_file( $attachment_id ); 986 987 if ( $filepath && file_exists( $filepath ) ) { 988 if ( 'full' !== $size ) { 989 $data = image_get_intermediate_size( $attachment_id, $size ); 990 991 if ( $data ) { 992 $filepath = path_join( dirname( $filepath ), $data['file'] ); 993 994 /** 995 * Filters the path to an attachment's file when editing the image. 996 * 997 * The filter is evaluated for all image sizes except 'full'. 998 * 999 * @since 3.1.0 1000 * 1001 * @param string $path Path to the current image. 1002 * @param int $attachment_id Attachment ID. 1003 * @param string|int[] $size Requested image size. Can be any registered image size name, or 1004 * an array of width and height values in pixels (in that order). 1005 */ 1006 $filepath = apply_filters( 'load_image_to_edit_filesystempath', $filepath, $attachment_id, $size ); 1007 } 1008 } 1009 } elseif ( function_exists( 'fopen' ) && ini_get( 'allow_url_fopen' ) ) { 1010 /** 1011 * Filters the path to an attachment's URL when editing the image. 1012 * 1013 * The filter is only evaluated if the file isn't stored locally and `allow_url_fopen` is enabled on the server. 1014 * 1015 * @since 3.1.0 1016 * 1017 * @param string|false $image_url Current image URL. 1018 * @param int $attachment_id Attachment ID. 1019 * @param string|int[] $size Requested image size. Can be any registered image size name, or 1020 * an array of width and height values in pixels (in that order). 1021 */ 1022 $filepath = apply_filters( 'load_image_to_edit_attachmenturl', wp_get_attachment_url( $attachment_id ), $attachment_id, $size ); 1023 } 1024 1025 /** 1026 * Filters the returned path or URL of the current image. 1027 * 1028 * @since 2.9.0 1029 * 1030 * @param string|false $filepath File path or URL to current image, or false. 1031 * @param int $attachment_id Attachment ID. 1032 * @param string|int[] $size Requested image size. Can be any registered image size name, or 1033 * an array of width and height values in pixels (in that order). 1034 */ 1035 return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size ); 1036 } 1037 1038 /** 1039 * Copy an existing image file. 1040 * 1041 * @since 3.4.0 1042 * @access private 1043 * 1044 * @param int $attachment_id Attachment ID. 1045 * @return string|false New file path on success, false on failure. 1046 */ 1047 function _copy_image_file( $attachment_id ) { 1048 $dst_file = get_attached_file( $attachment_id ); 1049 $src_file = $dst_file; 1050 1051 if ( ! file_exists( $src_file ) ) { 1052 $src_file = _load_image_to_edit_path( $attachment_id ); 1053 } 1054 1055 if ( $src_file ) { 1056 $dst_file = str_replace( wp_basename( $dst_file ), 'copy-' . wp_basename( $dst_file ), $dst_file ); 1057 $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), wp_basename( $dst_file ) ); 1058 1059 /* 1060 * The directory containing the original file may no longer 1061 * exist when using a replication plugin. 1062 */ 1063 wp_mkdir_p( dirname( $dst_file ) ); 1064 1065 if ( ! copy( $src_file, $dst_file ) ) { 1066 $dst_file = false; 1067 } 1068 } else { 1069 $dst_file = false; 1070 } 1071 1072 return $dst_file; 1073 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Mon Jan 25 08:20:01 2021 | Cross-referenced by PHPXref |