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