[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-admin/includes/ -> image.php (source)

   1  <?php
   2  /**
   3   * File contains all the administration image manipulation functions.
   4   *
   5   * @package WordPress
   6   * @subpackage Administration
   7   */
   8  
   9  /**
  10   * Crop 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 int $src_abs Optional. If the source crop points are absolute.
  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.
  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      // Remove sizes that already exist. Only checks for matching "size names".
 121      // It is possible that the dimensions for a particular size name have changed.
 122      // For example the user has changed the values on the Settings -> Media screen.
 123      // However we keep the old sub-sizes with the previous dimensions
 124      // as the image may have been used in an older post.
 125      $missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] );
 126  
 127      /**
 128       * Filters the array of missing image sub-sizes for an uploaded image.
 129       *
 130       * @since 5.3.0
 131       *
 132       * @param array $missing_sizes Array with the missing image sub-sizes.
 133       * @param array $image_meta    The image meta data.
 134       * @param int   $attachment_id The image attachment post ID.
 135       */
 136      return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id );
 137  }
 138  
 139  /**
 140   * If any of the currently registered image sub-sizes are missing,
 141   * create them and update the image meta data.
 142   *
 143   * @since 5.3.0
 144   *
 145   * @param int $attachment_id The image attachment post ID.
 146   * @return array|WP_Error The updated image meta data array or WP_Error object
 147   *                        if both the image meta and the attached file are missing.
 148   */
 149  function wp_update_image_subsizes( $attachment_id ) {
 150      $image_meta = wp_get_attachment_metadata( $attachment_id );
 151      $image_file = wp_get_original_image_path( $attachment_id );
 152  
 153      if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
 154          // Previously failed upload?
 155          // If there is an uploaded file, make all sub-sizes and generate all of the attachment meta.
 156          if ( ! empty( $image_file ) ) {
 157              $image_meta = wp_create_image_subsizes( $image_file, $attachment_id );
 158          } else {
 159              return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) );
 160          }
 161      } else {
 162          $missing_sizes = wp_get_missing_image_subsizes( $attachment_id );
 163  
 164          if ( empty( $missing_sizes ) ) {
 165              return $image_meta;
 166          }
 167  
 168          // This also updates the image meta.
 169          $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id );
 170      }
 171  
 172      /** This filter is documented in wp-admin/includes/image.php */
 173      $image_meta = apply_filters( 'wp_generate_attachment_metadata', $image_meta, $attachment_id, 'update' );
 174  
 175      // Save the updated metadata.
 176      wp_update_attachment_metadata( $attachment_id, $image_meta );
 177  
 178      return $image_meta;
 179  }
 180  
 181  /**
 182   * Updates the attached file and image meta data when the original image was edited.
 183   *
 184   * @since 5.3.0
 185   * @access private
 186   *
 187   * @param array  $saved_data    The data returned from WP_Image_Editor after successfully saving an image.
 188   * @param string $original_file Path to the original file.
 189   * @param array  $image_meta    The image meta data.
 190   * @param int    $attachment_id The attachment post ID.
 191   * @return array The updated image meta data.
 192   */
 193  function _wp_image_meta_replace_original( $saved_data, $original_file, $image_meta, $attachment_id ) {
 194      $new_file = $saved_data['path'];
 195  
 196      // Update the attached file meta.
 197      update_attached_file( $attachment_id, $new_file );
 198  
 199      // Width and height of the new image.
 200      $image_meta['width']  = $saved_data['width'];
 201      $image_meta['height'] = $saved_data['height'];
 202  
 203      // Make the file path relative to the upload dir.
 204      $image_meta['file'] = _wp_relative_upload_path( $new_file );
 205  
 206      // Store the original image file name in image_meta.
 207      $image_meta['original_image'] = wp_basename( $original_file );
 208  
 209      return $image_meta;
 210  }
 211  
 212  /**
 213   * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata.
 214   *
 215   * Intended for use after an image is uploaded. Saves/updates the image metadata after each
 216   * sub-size is created. If there was an error, it is added to the returned image metadata array.
 217   *
 218   * @since 5.3.0
 219   *
 220   * @param string $file          Full path to the image file.
 221   * @param int    $attachment_id Attachment Id to process.
 222   * @return array The image attachment meta data.
 223   */
 224  function wp_create_image_subsizes( $file, $attachment_id ) {
 225      $imagesize = @getimagesize( $file );
 226  
 227      if ( empty( $imagesize ) ) {
 228          // File is not an image.
 229          return array();
 230      }
 231  
 232      // Default image meta
 233      $image_meta = array(
 234          'width'  => $imagesize[0],
 235          'height' => $imagesize[1],
 236          'file'   => _wp_relative_upload_path( $file ),
 237          'sizes'  => array(),
 238      );
 239  
 240      // Fetch additional metadata from EXIF/IPTC.
 241      $exif_meta = wp_read_image_metadata( $file );
 242  
 243      if ( $exif_meta ) {
 244          $image_meta['image_meta'] = $exif_meta;
 245      }
 246  
 247      /**
 248       * Filters the "BIG image" threshold value.
 249       *
 250       * If the original image width or height is above the threshold, it will be scaled down. The threshold is
 251       * used as max width and max height. The scaled down image will be used as the largest available size, including
 252       * the `_wp_attached_file` post meta value.
 253       *
 254       * Returning `false` from the filter callback will disable the scaling.
 255       *
 256       * @since 5.3.0
 257       *
 258       * @param int    $threshold     The threshold value in pixels. Default 2560.
 259       * @param array  $imagesize     {
 260       *     Indexed array of the image width and height in pixels.
 261       *
 262       *     @type int $0 The image width.
 263       *     @type int $1 The image height.
 264       * }
 265       * @param string $file          Full path to the uploaded image file.
 266       * @param int    $attachment_id Attachment post ID.
 267       */
 268      $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
 269  
 270      // If the original image's dimensions are over the threshold, scale the image
 271      // and use it as the "full" size.
 272      if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
 273          $editor = wp_get_image_editor( $file );
 274  
 275          if ( is_wp_error( $editor ) ) {
 276              // This image cannot be edited.
 277              return $image_meta;
 278          }
 279  
 280          // Resize the image
 281          $resized = $editor->resize( $threshold, $threshold );
 282          $rotated = null;
 283  
 284          // If there is EXIF data, rotate according to EXIF Orientation.
 285          if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) {
 286              $resized = $editor->maybe_exif_rotate();
 287              $rotated = $resized;
 288          }
 289  
 290          if ( ! is_wp_error( $resized ) ) {
 291              // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
 292              // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
 293              $saved = $editor->save( $editor->generate_filename( 'scaled' ) );
 294  
 295              if ( ! is_wp_error( $saved ) ) {
 296                  $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
 297  
 298                  // If the image was rotated update the stored EXIF data.
 299                  if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) {
 300                      $image_meta['image_meta']['orientation'] = 1;
 301                  }
 302              } else {
 303                  // TODO: log errors.
 304              }
 305          } else {
 306              // TODO: log errors.
 307          }
 308      } elseif ( ! empty( $exif_meta['orientation'] ) && (int) $exif_meta['orientation'] !== 1 ) {
 309          // Rotate the whole original image if there is EXIF data and "orientation" is not 1.
 310  
 311          $editor = wp_get_image_editor( $file );
 312  
 313          if ( is_wp_error( $editor ) ) {
 314              // This image cannot be edited.
 315              return $image_meta;
 316          }
 317  
 318          // Rotate the image
 319          $rotated = $editor->maybe_exif_rotate();
 320  
 321          if ( true === $rotated ) {
 322              // Append `-rotated` to the image file name.
 323              $saved = $editor->save( $editor->generate_filename( 'rotated' ) );
 324  
 325              if ( ! is_wp_error( $saved ) ) {
 326                  $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
 327  
 328                  // Update the stored EXIF data.
 329                  if ( ! empty( $image_meta['image_meta']['orientation'] ) ) {
 330                      $image_meta['image_meta']['orientation'] = 1;
 331                  }
 332              } else {
 333                  // TODO: log errors.
 334              }
 335          }
 336      }
 337  
 338      // Initial save of the new metadata.
 339      // At this point the file was uploaded and moved to the uploads directory
 340      // but the image sub-sizes haven't been created yet and the `sizes` array is empty.
 341      wp_update_attachment_metadata( $attachment_id, $image_meta );
 342  
 343      $new_sizes = wp_get_registered_image_subsizes();
 344  
 345      /**
 346       * Filters the image sizes automatically generated when uploading an image.
 347       *
 348       * @since 2.9.0
 349       * @since 4.4.0 Added the `$image_meta` argument.
 350       * @since 5.3.0 Added the `$attachment_id` argument.
 351       *
 352       * @param array $new_sizes     Associative array of image sizes to be created.
 353       * @param array $image_meta    The image meta data: width, height, file, sizes, etc.
 354       * @param int   $attachment_id The attachment post ID for the image.
 355       */
 356      $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id );
 357  
 358      return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id );
 359  }
 360  
 361  /**
 362   * Low-level function to create image sub-sizes.
 363   *
 364   * Updates the image meta after each sub-size is created.
 365   * Errors are stored in the returned image metadata array.
 366   *
 367   * @since 5.3.0
 368   * @access private
 369   *
 370   * @param array  $new_sizes     Array defining what sizes to create.
 371   * @param string $file          Full path to the image file.
 372   * @param array  $image_meta    The attachment meta data array.
 373   * @param int    $attachment_id Attachment Id to process.
 374   * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing.
 375   */
 376  function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) {
 377      if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
 378          // Not an image attachment.
 379          return array();
 380      }
 381  
 382      // Check if any of the new sizes already exist.
 383      if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) {
 384          foreach ( $image_meta['sizes'] as $size_name => $size_meta ) {
 385              // Only checks "size name" so we don't override existing images even if the dimensions
 386              // don't match the currently defined size with the same name.
 387              // To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta.
 388              if ( array_key_exists( $size_name, $new_sizes ) ) {
 389                  unset( $new_sizes[ $size_name ] );
 390              }
 391          }
 392      } else {
 393          $image_meta['sizes'] = array();
 394      }
 395  
 396      if ( empty( $new_sizes ) ) {
 397          // Nothing to do...
 398          return $image_meta;
 399      }
 400  
 401      // Sort the image sub-sizes in order of priority when creating them.
 402      // This ensures there is an appropriate sub-size the user can access immediately
 403      // even when there was an error and not all sub-sizes were created.
 404      $priority = array(
 405          'medium'       => null,
 406          'large'        => null,
 407          'thumbnail'    => null,
 408          'medium_large' => null,
 409      );
 410  
 411      $new_sizes = array_filter( array_merge( $priority, $new_sizes ) );
 412  
 413      $editor = wp_get_image_editor( $file );
 414  
 415      if ( is_wp_error( $editor ) ) {
 416          // The image cannot be edited.
 417          return $image_meta;
 418      }
 419  
 420      // If stored EXIF data exists, rotate the source image before creating sub-sizes.
 421      if ( ! empty( $image_meta['image_meta'] ) ) {
 422          $rotated = $editor->maybe_exif_rotate();
 423  
 424          if ( is_wp_error( $rotated ) ) {
 425              // TODO: log errors.
 426          }
 427      }
 428  
 429      if ( method_exists( $editor, 'make_subsize' ) ) {
 430          foreach ( $new_sizes as $new_size_name => $new_size_data ) {
 431              $new_size_meta = $editor->make_subsize( $new_size_data );
 432  
 433              if ( is_wp_error( $new_size_meta ) ) {
 434                  // TODO: log errors.
 435              } else {
 436                  // Save the size meta value.
 437                  $image_meta['sizes'][ $new_size_name ] = $new_size_meta;
 438                  wp_update_attachment_metadata( $attachment_id, $image_meta );
 439              }
 440          }
 441      } else {
 442          // Fall back to `$editor->multi_resize()`.
 443          $created_sizes = $editor->multi_resize( $new_sizes );
 444  
 445          if ( ! empty( $created_sizes ) ) {
 446              $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes );
 447              wp_update_attachment_metadata( $attachment_id, $image_meta );
 448          }
 449      }
 450  
 451      return $image_meta;
 452  }
 453  
 454  /**
 455   * Generate attachment meta data and create image sub-sizes for images.
 456   *
 457   * @since 2.1.0
 458   *
 459   * @param int $attachment_id Attachment Id to process.
 460   * @param string $file Filepath of the Attached image.
 461   * @return mixed Metadata for attachment.
 462   */
 463  function wp_generate_attachment_metadata( $attachment_id, $file ) {
 464      $attachment = get_post( $attachment_id );
 465  
 466      $metadata  = array();
 467      $support   = false;
 468      $mime_type = get_post_mime_type( $attachment );
 469  
 470      if ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) {
 471          // Make thumbnails and other intermediate sizes.
 472          $metadata = wp_create_image_subsizes( $file, $attachment_id );
 473      } elseif ( wp_attachment_is( 'video', $attachment ) ) {
 474          $metadata = wp_read_video_metadata( $file );
 475          $support  = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' );
 476      } elseif ( wp_attachment_is( 'audio', $attachment ) ) {
 477          $metadata = wp_read_audio_metadata( $file );
 478          $support  = current_theme_supports( 'post-thumbnails', 'attachment:audio' ) || post_type_supports( 'attachment:audio', 'thumbnail' );
 479      }
 480  
 481      if ( $support && ! empty( $metadata['image']['data'] ) ) {
 482          // Check for existing cover.
 483          $hash   = md5( $metadata['image']['data'] );
 484          $posts  = get_posts(
 485              array(
 486                  'fields'         => 'ids',
 487                  'post_type'      => 'attachment',
 488                  'post_mime_type' => $metadata['image']['mime'],
 489                  'post_status'    => 'inherit',
 490                  'posts_per_page' => 1,
 491                  'meta_key'       => '_cover_hash',
 492                  'meta_value'     => $hash,
 493              )
 494          );
 495          $exists = reset( $posts );
 496  
 497          if ( ! empty( $exists ) ) {
 498              update_post_meta( $attachment_id, '_thumbnail_id', $exists );
 499          } else {
 500              $ext = '.jpg';
 501              switch ( $metadata['image']['mime'] ) {
 502                  case 'image/gif':
 503                      $ext = '.gif';
 504                      break;
 505                  case 'image/png':
 506                      $ext = '.png';
 507                      break;
 508              }
 509              $basename = str_replace( '.', '-', wp_basename( $file ) ) . '-image' . $ext;
 510              $uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] );
 511              if ( false === $uploaded['error'] ) {
 512                  $image_attachment = array(
 513                      'post_mime_type' => $metadata['image']['mime'],
 514                      'post_type'      => 'attachment',
 515                      'post_content'   => '',
 516                  );
 517                  /**
 518                   * Filters the parameters for the attachment thumbnail creation.
 519                   *
 520                   * @since 3.9.0
 521                   *
 522                   * @param array $image_attachment An array of parameters to create the thumbnail.
 523                   * @param array $metadata         Current attachment metadata.
 524                   * @param array $uploaded         An array containing the thumbnail path and url.
 525                   */
 526                  $image_attachment = apply_filters( 'attachment_thumbnail_args', $image_attachment, $metadata, $uploaded );
 527  
 528                  $sub_attachment_id = wp_insert_attachment( $image_attachment, $uploaded['file'] );
 529                  add_post_meta( $sub_attachment_id, '_cover_hash', $hash );
 530                  $attach_data = wp_generate_attachment_metadata( $sub_attachment_id, $uploaded['file'] );
 531                  wp_update_attachment_metadata( $sub_attachment_id, $attach_data );
 532                  update_post_meta( $attachment_id, '_thumbnail_id', $sub_attachment_id );
 533              }
 534          }
 535      } elseif ( 'application/pdf' === $mime_type ) {
 536          // Try to create image thumbnails for PDFs.
 537  
 538          $fallback_sizes = array(
 539              'thumbnail',
 540              'medium',
 541              'large',
 542          );
 543  
 544          /**
 545           * Filters the image sizes generated for non-image mime types.
 546           *
 547           * @since 4.7.0
 548           *
 549           * @param string[] $fallback_sizes An array of image size names.
 550           * @param array    $metadata       Current attachment metadata.
 551           */
 552          $fallback_sizes = apply_filters( 'fallback_intermediate_image_sizes', $fallback_sizes, $metadata );
 553  
 554          $registered_sizes = wp_get_registered_image_subsizes();
 555          $merged_sizes     = array_intersect_key( $registered_sizes, array_flip( $fallback_sizes ) );
 556  
 557          // Force thumbnails to be soft crops.
 558          if ( isset( $merged_sizes['thumbnail'] ) && is_array( $merged_sizes['thumbnail'] ) ) {
 559              $merged_sizes['thumbnail']['crop'] = false;
 560          }
 561  
 562          // Only load PDFs in an image editor if we're processing sizes.
 563          if ( ! empty( $merged_sizes ) ) {
 564              $editor = wp_get_image_editor( $file );
 565  
 566              if ( ! is_wp_error( $editor ) ) { // No support for this type of file
 567                  /*
 568                   * PDFs may have the same file filename as JPEGs.
 569                   * Ensure the PDF preview image does not overwrite any JPEG images that already exist.
 570                   */
 571                  $dirname      = dirname( $file ) . '/';
 572                  $ext          = '.' . pathinfo( $file, PATHINFO_EXTENSION );
 573                  $preview_file = $dirname . wp_unique_filename( $dirname, wp_basename( $file, $ext ) . '-pdf.jpg' );
 574  
 575                  $uploaded = $editor->save( $preview_file, 'image/jpeg' );
 576                  unset( $editor );
 577  
 578                  // Resize based on the full size image, rather than the source.
 579                  if ( ! is_wp_error( $uploaded ) ) {
 580                      $image_file = $uploaded['path'];
 581                      unset( $uploaded['path'] );
 582  
 583                      $metadata['sizes'] = array(
 584                          'full' => $uploaded,
 585                      );
 586  
 587                      // Save the meta data before any image post-processing errors could happen.
 588                      wp_update_attachment_metadata( $attachment_id, $metadata );
 589  
 590                      // Create sub-sizes saving the image meta after each.
 591                      $metadata = _wp_make_subsizes( $merged_sizes, $image_file, $metadata, $attachment_id );
 592                  }
 593              }
 594          }
 595      }
 596  
 597      // Remove the blob of binary data from the array.
 598      if ( $metadata ) {
 599          unset( $metadata['image']['data'] );
 600      }
 601  
 602      /**
 603       * Filters the generated attachment meta data.
 604       *
 605       * @since 2.1.0
 606       * @since 5.3.0 The `$context` parameter was added.
 607       *
 608       * @param array  $metadata      An array of attachment meta data.
 609       * @param int    $attachment_id Current attachment ID.
 610       * @param string $context       Additional context. Can be 'create' when metadata was initially created for new attachment
 611       *                              or 'update' when the metadata was updated.
 612       */
 613      return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' );
 614  }
 615  
 616  /**
 617   * Convert a fraction string to a decimal.
 618   *
 619   * @since 2.5.0
 620   *
 621   * @param string $str
 622   * @return int|float
 623   */
 624  function wp_exif_frac2dec( $str ) {
 625      if ( false === strpos( $str, '/' ) ) {
 626          return $str;
 627      }
 628  
 629      list( $n, $d ) = explode( '/', $str );
 630      if ( ! empty( $d ) ) {
 631          return $n / $d;
 632      }
 633      return $str;
 634  }
 635  
 636  /**
 637   * Convert the exif date format to a unix timestamp.
 638   *
 639   * @since 2.5.0
 640   *
 641   * @param string $str
 642   * @return int
 643   */
 644  function wp_exif_date2ts( $str ) {
 645      list( $date, $time ) = explode( ' ', trim( $str ) );
 646      list( $y, $m, $d )   = explode( ':', $date );
 647  
 648      return strtotime( "{$y}-{$m}-{$d} {$time}" );
 649  }
 650  
 651  /**
 652   * Get extended image metadata, exif or iptc as available.
 653   *
 654   * Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso
 655   * created_timestamp, focal_length, shutter_speed, and title.
 656   *
 657   * The IPTC metadata that is retrieved is APP13, credit, byline, created date
 658   * and time, caption, copyright, and title. Also includes FNumber, Model,
 659   * DateTimeDigitized, FocalLength, ISOSpeedRatings, and ExposureTime.
 660   *
 661   * @todo Try other exif libraries if available.
 662   * @since 2.5.0
 663   *
 664   * @param string $file
 665   * @return bool|array False on failure. Image metadata array on success.
 666   */
 667  function wp_read_image_metadata( $file ) {
 668      if ( ! file_exists( $file ) ) {
 669          return false;
 670      }
 671  
 672      list( , , $image_type ) = @getimagesize( $file );
 673  
 674      /*
 675       * EXIF contains a bunch of data we'll probably never need formatted in ways
 676       * that are difficult to use. We'll normalize it and just extract the fields
 677       * that are likely to be useful. Fractions and numbers are converted to
 678       * floats, dates to unix timestamps, and everything else to strings.
 679       */
 680      $meta = array(
 681          'aperture'          => 0,
 682          'credit'            => '',
 683          'camera'            => '',
 684          'caption'           => '',
 685          'created_timestamp' => 0,
 686          'copyright'         => '',
 687          'focal_length'      => 0,
 688          'iso'               => 0,
 689          'shutter_speed'     => 0,
 690          'title'             => '',
 691          'orientation'       => 0,
 692          'keywords'          => array(),
 693      );
 694  
 695      $iptc = array();
 696      /*
 697       * Read IPTC first, since it might contain data not available in exif such
 698       * as caption, description etc.
 699       */
 700      if ( is_callable( 'iptcparse' ) ) {
 701          @getimagesize( $file, $info );
 702  
 703          if ( ! empty( $info['APP13'] ) ) {
 704              $iptc = @iptcparse( $info['APP13'] );
 705  
 706              // Headline, "A brief synopsis of the caption."
 707              if ( ! empty( $iptc['2#105'][0] ) ) {
 708                  $meta['title'] = trim( $iptc['2#105'][0] );
 709                  /*
 710                  * Title, "Many use the Title field to store the filename of the image,
 711                  * though the field may be used in many ways."
 712                  */
 713              } elseif ( ! empty( $iptc['2#005'][0] ) ) {
 714                  $meta['title'] = trim( $iptc['2#005'][0] );
 715              }
 716  
 717              if ( ! empty( $iptc['2#120'][0] ) ) { // description / legacy caption
 718                  $caption = trim( $iptc['2#120'][0] );
 719  
 720                  mbstring_binary_safe_encoding();
 721                  $caption_length = strlen( $caption );
 722                  reset_mbstring_encoding();
 723  
 724                  if ( empty( $meta['title'] ) && $caption_length < 80 ) {
 725                      // Assume the title is stored in 2:120 if it's short.
 726                      $meta['title'] = $caption;
 727                  }
 728  
 729                  $meta['caption'] = $caption;
 730              }
 731  
 732              if ( ! empty( $iptc['2#110'][0] ) ) { // credit
 733                  $meta['credit'] = trim( $iptc['2#110'][0] );
 734              } elseif ( ! empty( $iptc['2#080'][0] ) ) { // creator / legacy byline
 735                  $meta['credit'] = trim( $iptc['2#080'][0] );
 736              }
 737  
 738              if ( ! empty( $iptc['2#055'][0] ) && ! empty( $iptc['2#060'][0] ) ) { // created date and time
 739                  $meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] );
 740              }
 741  
 742              if ( ! empty( $iptc['2#116'][0] ) ) { // copyright
 743                  $meta['copyright'] = trim( $iptc['2#116'][0] );
 744              }
 745  
 746              if ( ! empty( $iptc['2#025'][0] ) ) { // keywords array
 747                  $meta['keywords'] = array_values( $iptc['2#025'] );
 748              }
 749          }
 750      }
 751  
 752      $exif = array();
 753  
 754      /**
 755       * Filters the image types to check for exif data.
 756       *
 757       * @since 2.5.0
 758       *
 759       * @param array $image_types Image types to check for exif data.
 760       */
 761      $exif_image_types = apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) );
 762  
 763      if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types, true ) ) {
 764          $exif = @exif_read_data( $file );
 765  
 766          if ( ! empty( $exif['ImageDescription'] ) ) {
 767              mbstring_binary_safe_encoding();
 768              $description_length = strlen( $exif['ImageDescription'] );
 769              reset_mbstring_encoding();
 770  
 771              if ( empty( $meta['title'] ) && $description_length < 80 ) {
 772                  // Assume the title is stored in ImageDescription
 773                  $meta['title'] = trim( $exif['ImageDescription'] );
 774              }
 775  
 776              if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) ) {
 777                  $meta['caption'] = trim( $exif['COMPUTED']['UserComment'] );
 778              }
 779  
 780              if ( empty( $meta['caption'] ) ) {
 781                  $meta['caption'] = trim( $exif['ImageDescription'] );
 782              }
 783          } elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) ) {
 784              $meta['caption'] = trim( $exif['Comments'] );
 785          }
 786  
 787          if ( empty( $meta['credit'] ) ) {
 788              if ( ! empty( $exif['Artist'] ) ) {
 789                  $meta['credit'] = trim( $exif['Artist'] );
 790              } elseif ( ! empty( $exif['Author'] ) ) {
 791                  $meta['credit'] = trim( $exif['Author'] );
 792              }
 793          }
 794  
 795          if ( empty( $meta['copyright'] ) && ! empty( $exif['Copyright'] ) ) {
 796              $meta['copyright'] = trim( $exif['Copyright'] );
 797          }
 798          if ( ! empty( $exif['FNumber'] ) ) {
 799              $meta['aperture'] = round( wp_exif_frac2dec( $exif['FNumber'] ), 2 );
 800          }
 801          if ( ! empty( $exif['Model'] ) ) {
 802              $meta['camera'] = trim( $exif['Model'] );
 803          }
 804          if ( empty( $meta['created_timestamp'] ) && ! empty( $exif['DateTimeDigitized'] ) ) {
 805              $meta['created_timestamp'] = wp_exif_date2ts( $exif['DateTimeDigitized'] );
 806          }
 807          if ( ! empty( $exif['FocalLength'] ) ) {
 808              $meta['focal_length'] = (string) wp_exif_frac2dec( $exif['FocalLength'] );
 809          }
 810          if ( ! empty( $exif['ISOSpeedRatings'] ) ) {
 811              $meta['iso'] = is_array( $exif['ISOSpeedRatings'] ) ? reset( $exif['ISOSpeedRatings'] ) : $exif['ISOSpeedRatings'];
 812              $meta['iso'] = trim( $meta['iso'] );
 813          }
 814          if ( ! empty( $exif['ExposureTime'] ) ) {
 815              $meta['shutter_speed'] = (string) wp_exif_frac2dec( $exif['ExposureTime'] );
 816          }
 817          if ( ! empty( $exif['Orientation'] ) ) {
 818              $meta['orientation'] = $exif['Orientation'];
 819          }
 820      }
 821  
 822      foreach ( array( 'title', 'caption', 'credit', 'copyright', 'camera', 'iso' ) as $key ) {
 823          if ( $meta[ $key ] && ! seems_utf8( $meta[ $key ] ) ) {
 824              $meta[ $key ] = utf8_encode( $meta[ $key ] );
 825          }
 826      }
 827  
 828      foreach ( $meta['keywords'] as $key => $keyword ) {
 829          if ( ! seems_utf8( $keyword ) ) {
 830              $meta['keywords'][ $key ] = utf8_encode( $keyword );
 831          }
 832      }
 833  
 834      $meta = wp_kses_post_deep( $meta );
 835  
 836      /**
 837       * Filters the array of meta data read from an image's exif data.
 838       *
 839       * @since 2.5.0
 840       * @since 4.4.0 The `$iptc` parameter was added.
 841       * @since 5.0.0 The `$exif` parameter was added.
 842       *
 843       * @param array  $meta       Image meta data.
 844       * @param string $file       Path to image file.
 845       * @param int    $image_type Type of image, one of the `IMAGETYPE_XXX` constants.
 846       * @param array  $iptc       IPTC data.
 847       * @param array  $exif       EXIF data.
 848       */
 849      return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif );
 850  
 851  }
 852  
 853  /**
 854   * Validate that file is an image.
 855   *
 856   * @since 2.5.0
 857   *
 858   * @param string $path File path to test if valid image.
 859   * @return bool True if valid image, false if not valid image.
 860   */
 861  function file_is_valid_image( $path ) {
 862      $size = @getimagesize( $path );
 863      return ! empty( $size );
 864  }
 865  
 866  /**
 867   * Validate that file is suitable for displaying within a web page.
 868   *
 869   * @since 2.5.0
 870   *
 871   * @param string $path File path to test.
 872   * @return bool True if suitable, false if not suitable.
 873   */
 874  function file_is_displayable_image( $path ) {
 875      $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO );
 876  
 877      $info = @getimagesize( $path );
 878      if ( empty( $info ) ) {
 879          $result = false;
 880      } elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) {
 881          $result = false;
 882      } else {
 883          $result = true;
 884      }
 885  
 886      /**
 887       * Filters whether the current image is displayable in the browser.
 888       *
 889       * @since 2.5.0
 890       *
 891       * @param bool   $result Whether the image can be displayed. Default true.
 892       * @param string $path   Path to the image.
 893       */
 894      return apply_filters( 'file_is_displayable_image', $result, $path );
 895  }
 896  
 897  /**
 898   * Load an image resource for editing.
 899   *
 900   * @since 2.9.0
 901   *
 902   * @param string $attachment_id Attachment ID.
 903   * @param string $mime_type Image mime type.
 904   * @param string $size Optional. Image size, defaults to 'full'.
 905   * @return resource|false The resulting image resource on success, false on failure.
 906   */
 907  function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
 908      $filepath = _load_image_to_edit_path( $attachment_id, $size );
 909      if ( empty( $filepath ) ) {
 910          return false;
 911      }
 912  
 913      switch ( $mime_type ) {
 914          case 'image/jpeg':
 915              $image = imagecreatefromjpeg( $filepath );
 916              break;
 917          case 'image/png':
 918              $image = imagecreatefrompng( $filepath );
 919              break;
 920          case 'image/gif':
 921              $image = imagecreatefromgif( $filepath );
 922              break;
 923          default:
 924              $image = false;
 925              break;
 926      }
 927      if ( is_resource( $image ) ) {
 928          /**
 929           * Filters the current image being loaded for editing.
 930           *
 931           * @since 2.9.0
 932           *
 933           * @param resource $image         Current image.
 934           * @param string   $attachment_id Attachment ID.
 935           * @param string   $size          Image size.
 936           */
 937          $image = apply_filters( 'load_image_to_edit', $image, $attachment_id, $size );
 938          if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) {
 939              imagealphablending( $image, false );
 940              imagesavealpha( $image, true );
 941          }
 942      }
 943      return $image;
 944  }
 945  
 946  /**
 947   * Retrieve the path or url of an attachment's attached file.
 948   *
 949   * If the attached file is not present on the local filesystem (usually due to replication plugins),
 950   * then the url of the file is returned if url fopen is supported.
 951   *
 952   * @since 3.4.0
 953   * @access private
 954   *
 955   * @param string $attachment_id Attachment ID.
 956   * @param string $size Optional. Image size, defaults to 'full'.
 957   * @return string|false File path or url on success, false on failure.
 958   */
 959  function _load_image_to_edit_path( $attachment_id, $size = 'full' ) {
 960      $filepath = get_attached_file( $attachment_id );
 961  
 962      if ( $filepath && file_exists( $filepath ) ) {
 963          if ( 'full' !== $size ) {
 964              $data = image_get_intermediate_size( $attachment_id, $size );
 965  
 966              if ( $data ) {
 967                  $filepath = path_join( dirname( $filepath ), $data['file'] );
 968  
 969                  /**
 970                   * Filters the path to the current image.
 971                   *
 972                   * The filter is evaluated for all image sizes except 'full'.
 973                   *
 974                   * @since 3.1.0
 975                   *
 976                   * @param string $path          Path to the current image.
 977                   * @param string $attachment_id Attachment ID.
 978                   * @param string $size          Size of the image.
 979                   */
 980                  $filepath = apply_filters( 'load_image_to_edit_filesystempath', $filepath, $attachment_id, $size );
 981              }
 982          }
 983      } elseif ( function_exists( 'fopen' ) && ini_get( 'allow_url_fopen' ) ) {
 984          /**
 985           * Filters the image URL if not in the local filesystem.
 986           *
 987           * The filter is only evaluated if fopen is enabled on the server.
 988           *
 989           * @since 3.1.0
 990           *
 991           * @param string $image_url     Current image URL.
 992           * @param string $attachment_id Attachment ID.
 993           * @param string $size          Size of the image.
 994           */
 995          $filepath = apply_filters( 'load_image_to_edit_attachmenturl', wp_get_attachment_url( $attachment_id ), $attachment_id, $size );
 996      }
 997  
 998      /**
 999       * Filters the returned path or URL of the current image.
1000       *
1001       * @since 2.9.0
1002       *
1003       * @param string|bool $filepath      File path or URL to current image, or false.
1004       * @param string      $attachment_id Attachment ID.
1005       * @param string      $size          Size of the image.
1006       */
1007      return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size );
1008  }
1009  
1010  /**
1011   * Copy an existing image file.
1012   *
1013   * @since 3.4.0
1014   * @access private
1015   *
1016   * @param string $attachment_id Attachment ID.
1017   * @return string|false New file path on success, false on failure.
1018   */
1019  function _copy_image_file( $attachment_id ) {
1020      $dst_file = get_attached_file( $attachment_id );
1021      $src_file = $dst_file;
1022  
1023      if ( ! file_exists( $src_file ) ) {
1024          $src_file = _load_image_to_edit_path( $attachment_id );
1025      }
1026  
1027      if ( $src_file ) {
1028          $dst_file = str_replace( wp_basename( $dst_file ), 'copy-' . wp_basename( $dst_file ), $dst_file );
1029          $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), wp_basename( $dst_file ) );
1030  
1031          /*
1032           * The directory containing the original file may no longer
1033           * exist when using a replication plugin.
1034           */
1035          wp_mkdir_p( dirname( $dst_file ) );
1036  
1037          if ( ! copy( $src_file, $dst_file ) ) {
1038              $dst_file = false;
1039          }
1040      } else {
1041          $dst_file = false;
1042      }
1043  
1044      return $dst_file;
1045  }


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7