[ Index ]

PHP Cross Reference of WordPress

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


Generated: Wed Sep 18 01:00:03 2019 Cross-referenced by PHPXref 0.7.1