From a96ea8aa4cff7d6891f4fbdeb1f468b2557ad6c6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 4 Mar 2022 16:34:34 -0700 Subject: [PATCH 001/102] Core patch, first pass --- src/wp-admin/includes/image.php | 162 ++++++++++++------ src/wp-includes/class-wp-image-editor-gd.php | 15 +- .../class-wp-image-editor-imagick.php | 11 +- 3 files changed, 125 insertions(+), 63 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 627fdfb4b5ac2..55043c46f2f66 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -373,6 +373,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { * * Updates the image meta after each sub-size is created. * Errors are stored in the returned image metadata array. + * Since 6.0.0 this function is capable of generating multiple mime type sub-sizes. * * @since 5.3.0 * @access private @@ -384,84 +385,105 @@ function wp_create_image_subsizes( $file, $attachment_id ) { * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. */ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { + $mime_type = get_post_mime_type( $attachment_id ); if ( empty( $image_meta ) || ! is_array( $image_meta ) ) { // Not an image attachment. return array(); } + // Assemble the output mime types + $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); + $output_mime_types = array( $mime_type ); + error_log( json_encode( $valid_mime_transforms, JSON_PRETTY_PRINT)); - // Check if any of the new sizes already exist. - if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { - foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - /* - * Only checks "size name" so we don't override existing images even if the dimensions - * don't match the currently defined size with the same name. - * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. - */ - if ( array_key_exists( $size_name, $new_sizes ) ) { - unset( $new_sizes[ $size_name ] ); - } - } - } else { - $image_meta['sizes'] = array(); + if ( isset( $valid_mime_transforms[ $mime_type ] ) ) { + $output_mime_types = $valid_mime_transforms[ $mime_type ]; } - if ( empty( $new_sizes ) ) { - // Nothing to do... - return $image_meta; - } + // Generate off of the output types. + foreach( $output_mime_types as $mime_type ) { - /* - * Sort the image sub-sizes in order of priority when creating them. - * This ensures there is an appropriate sub-size the user can access immediately - * even when there was an error and not all sub-sizes were created. - */ - $priority = array( - 'medium' => null, - 'large' => null, - 'thumbnail' => null, - 'medium_large' => null, - ); + // Check if any of the new sizes already exist. + if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { + foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { + if ( ! isset ( $size_meta['sources'] ) ) { + $size_meta['sources'] = array(); + } + + if ( ! isset( $size_meta['sources'][ $mime_type ] ) ) { + $size_meta['sources'][ $mime_type ] = array(); + continue; + } + + /* + * Only checks "size name" so we don't override existing images even if the dimensions + * don't match the currently defined size with the same name. + * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. + */ + if ( array_key_exists( $size_name, $new_sizes ) ) { + unset( $new_sizes[ $size_name ] ); + } + } + } else { + $image_meta['sizes'] = array(); + } - $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); + if ( empty( $new_sizes ) ) { + // Nothing to do... + return $image_meta; + } - $editor = wp_get_image_editor( $file ); + /* + * Sort the image sub-sizes in order of priority when creating them. + * This ensures there is an appropriate sub-size the user can access immediately + * even when there was an error and not all sub-sizes were created. + */ + $priority = array( + 'medium' => null, + 'large' => null, + 'thumbnail' => null, + 'medium_large' => null, + ); - if ( is_wp_error( $editor ) ) { - // The image cannot be edited. - return $image_meta; - } + $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - // If stored EXIF data exists, rotate the source image before creating sub-sizes. - if ( ! empty( $image_meta['image_meta'] ) ) { - $rotated = $editor->maybe_exif_rotate(); + $editor = wp_get_image_editor( $file ); - if ( is_wp_error( $rotated ) ) { - // TODO: Log errors. + if ( is_wp_error( $editor ) ) { + // The image cannot be edited. + return $image_meta; } - } - if ( method_exists( $editor, 'make_subsize' ) ) { - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data ); + // If stored EXIF data exists, rotate the source image before creating sub-sizes. + if ( ! empty( $image_meta['image_meta'] ) ) { + $rotated = $editor->maybe_exif_rotate(); - if ( is_wp_error( $new_size_meta ) ) { + if ( is_wp_error( $rotated ) ) { // TODO: Log errors. - } else { - // Save the size meta value. - $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - wp_update_attachment_metadata( $attachment_id, $image_meta ); } } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( method_exists( $editor, 'make_subsize' ) ) { + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + $new_size_meta = $editor->make_subsize( $new_size_data, $mime_type ); + + if ( is_wp_error( $new_size_meta ) ) { + // TODO: Log errors. + } else { + // Save the size meta value. + $image_meta['sizes'][ $new_size_name ] = $new_size_meta; + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes, $mime_type ); + + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } } } - return $image_meta; } @@ -642,7 +664,6 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) { */ return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' ); } - /** * Convert a fraction string to a decimal. * @@ -1134,3 +1155,30 @@ function _copy_image_file( $attachment_id ) { return $dst_file; } + +/** + * Returns an array with the list of valid mime types that a specific mime type can be converted into it, + * for example an image/jpeg can be converted into an image/webp. + * + * @since 6.0.0 + * + * @param $attachment_id int The attachment ID. + * @return array> An array of valid mime types, where the key is the mime type and the value is the extension type. + */ +function wp_get_image_mime_transforms( $attachment_id) { + $image_mime_transforms = array( + 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), + 'image/webp' => array( 'image/webp', 'image/jpeg' ), + ); + + /** + * Filter to allow the definition of a custom mime types, in which a defined mime type + * can be transformed and provide a wide range of mime types. + * + * @since 6.0.0 + * + * @param array $image_mime_transforms A map with the valid mime transforms. + * @param int $attachment_id The ID of the attachment where the hook was dispatched. + */ + return (array) apply_filters( 'wp_image_mime_transforms', $image_mime_transforms, $attachment_id ); +} \ No newline at end of file diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index f32e2b42029db..183bcf8a356e9 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -261,18 +261,23 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 + * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } + * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data ) { + public function make_subsize( $size_data, $mime_type ) { + error_log( json_encode( $size_data, JSON_PRETTY_PRINT)); + error_log( json_encode( $mime_type, JSON_PRETTY_PRINT)); + if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -296,7 +301,11 @@ public function make_subsize( $size_data ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $resized ); + if ( $mime_type ) { + $saved = $this->_save( $resized, null, $mime_type ); + } else { + $saved = $this->_save( $resized ); + } imagedestroy( $resized ); } diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index d898db553a71f..2614d6e4f4ee9 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -473,17 +473,18 @@ public function multi_resize( $sizes ) { * * @since 5.3.0 * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } + * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data ) { + public function make_subsize( $size_data, $mime_type ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -508,7 +509,11 @@ public function make_subsize( $size_data ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $this->image ); + if ( $mime_type ) { + $saved = $this->_save( $this->image, null, $mime_type ); + } else { + $saved = $this->_save( $this->image ); + } $this->image->clear(); $this->image->destroy(); From fa8b9ef790221a4b4fefe92af998d6dcdf8f830b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 6 Mar 2022 13:24:57 -0700 Subject: [PATCH 002/102] Store the sources meta --- src/wp-admin/includes/image.php | 29 ++++++++++++++------ src/wp-includes/class-wp-image-editor-gd.php | 3 -- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 55043c46f2f66..aa2ec9e8cabcc 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -393,24 +393,24 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Assemble the output mime types $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); $output_mime_types = array( $mime_type ); - error_log( json_encode( $valid_mime_transforms, JSON_PRETTY_PRINT)); if ( isset( $valid_mime_transforms[ $mime_type ] ) ) { $output_mime_types = $valid_mime_transforms[ $mime_type ]; } + $dirname = pathinfo( $file, PATHINFO_DIRNAME ); // Generate off of the output types. - foreach( $output_mime_types as $mime_type ) { + foreach( $output_mime_types as $mime_index => $mime_type ) { - // Check if any of the new sizes already exist. + // Check if any of the new sizes already exist for this mime type. if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { if ( ! isset ( $size_meta['sources'] ) ) { $size_meta['sources'] = array(); } - if ( ! isset( $size_meta['sources'][ $mime_type ] ) ) { - $size_meta['sources'][ $mime_type ] = array(); + if ( ! isset( $new_sizes['sources'][ $mime_type ] ) ) { + $new_sizes['sources'][ $mime_type ] = array(); continue; } @@ -419,7 +419,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { * don't match the currently defined size with the same name. * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. */ - if ( array_key_exists( $size_name, $new_sizes ) ) { + if ( array_key_exists( $size_name, $new_sizes['sources'][ $mime_type ] ) ) { unset( $new_sizes[ $size_name ] ); } } @@ -469,8 +469,21 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { - // Save the size meta value. - $image_meta['sizes'][ $new_size_name ] = $new_size_meta; + // Save the default (first) mime size in the 'sizes'meta value. + if ( 0 === $mime_index ) { + $image_meta['sizes'][ $new_size_name ] = $new_size_meta; + } + + // Store the mime type specific sub-size in the 'sources' attribute. + $source_meta = array( + 'file' => $new_size_meta['file'], + 'filesize' => 0 + ); + $file_location = path_join( $dirname, $new_size_meta['file'] ); + if ( file_exists( $file_location ) ) { + $source_meta['filesize'] = filesize( $file_location ); + } + $image_meta['sizes'][ $new_size_name ]['sources'][ $mime_type ] = $source_meta; wp_update_attachment_metadata( $attachment_id, $image_meta ); } } diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index 183bcf8a356e9..5acc29b62cc40 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -275,9 +275,6 @@ public function multi_resize( $sizes ) { * WP_Error object on error. */ public function make_subsize( $size_data, $mime_type ) { - error_log( json_encode( $size_data, JSON_PRETTY_PRINT)); - error_log( json_encode( $mime_type, JSON_PRETTY_PRINT)); - if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } From 32807da3b44a62be0b9f7af4669947ac08f0d2a0 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 6 Mar 2022 13:47:43 -0700 Subject: [PATCH 003/102] Delete additional mime types in wp_delete_attachment_files --- src/wp-includes/post.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 10dddf859b9ce..0363ced34e016 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6480,6 +6480,27 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $deleted = false; } } + if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { + + $original_size_mime = empty( $sizeinfo['mime-type'] ) ? '' : $sizeinfo['mime-type']; + foreach ( $sizeinfo['sources'] as $mime => $properties ) { + + if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + continue; + } + + // Delete alternate mime types. + if ( $original_size_mime === $mime ) { + continue; + } + + $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; + } + } + } + } } From c930645cba69013906279c7b7a960262e4ca32e1 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 6 Mar 2022 13:49:43 -0700 Subject: [PATCH 004/102] Cleanup, remove unintended changes --- src/wp-admin/includes/image.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index aa2ec9e8cabcc..6b5417b2068d0 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -489,7 +489,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } } else { // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes, $mime_type ); + $created_sizes = $editor->multi_resize( $new_sizes ); if ( ! empty( $created_sizes ) ) { $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); @@ -1194,4 +1194,4 @@ function wp_get_image_mime_transforms( $attachment_id) { * @param int $attachment_id The ID of the attachment where the hook was dispatched. */ return (array) apply_filters( 'wp_image_mime_transforms', $image_mime_transforms, $attachment_id ); -} \ No newline at end of file +} From 3db70bd7b4c251a5a69038bbe8f96bbd603bea08 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 6 Mar 2022 14:58:27 -0700 Subject: [PATCH 005/102] Add full size and root level meta --- src/wp-admin/includes/image.php | 60 +++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 6b5417b2068d0..0fa4b6b177577 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -247,9 +247,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( $exif_meta ) { $image_meta['image_meta'] = $exif_meta; } - + $mime_type = $imagesize['mime']; // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. - if ( 'image/png' !== $imagesize['mime'] ) { + if ( 'image/png' !== $mime_type ) { /** * Filters the "BIG image" threshold value. @@ -297,21 +297,45 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); + $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); - // If the image was rotated update the stored EXIF data. - if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; + // Generate all off of the output types. + foreach( $output_mime_types as $mime_index => $mime_type ) { + if ( 0 === $mime_index ) { + $saved = $editor->save( $editor->generate_filename( 'scaled' ), $mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + + // If the image was rotated update the stored EXIF data. + if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } + } else { + // TODO: Log errors. + } + } else { + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $mime_type ) ), $mime_type ); + } + + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $mime_type ] = array( + 'file' => $saved['file'], + 'filesize' => filesize( $saved['path'] ) + ); + // If the image was rotated update the stored EXIF data. + if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } else { + // TODO: Log errors. + } } - } else { - // TODO: Log errors. } } else { // TODO: Log errors. } + } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { // Rotate the whole original image if there is EXIF data and "orientation" is not 1. @@ -390,16 +414,18 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Not an image attachment. return array(); } - // Assemble the output mime types - $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); - $output_mime_types = array( $mime_type ); - if ( isset( $valid_mime_transforms[ $mime_type ] ) ) { - $output_mime_types = $valid_mime_transforms[ $mime_type ]; + // Set up a top level sources array for the full size images. + if ( ! isset( $metadata['sources'] ) || ! is_array( $metadata['sources'] ) ) { + $metadata['sources'] = array(); } - $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - // Generate off of the output types. + // Assemble the output mime types + $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); + $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); + $dirname = pathinfo( $file, PATHINFO_DIRNAME ); + + // Generate all off of the output types. foreach( $output_mime_types as $mime_index => $mime_type ) { // Check if any of the new sizes already exist for this mime type. From 62855be6d17e58fae35e1bc5c3cff7aa7534cd72 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 7 Mar 2022 14:41:42 -0700 Subject: [PATCH 006/102] Add alternate mime full sizes to wp_delete_attachment_files --- src/wp-includes/post.php | 55 +++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 0363ced34e016..8a1afb2016f01 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6481,24 +6481,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { - - $original_size_mime = empty( $sizeinfo['mime-type'] ) ? '' : $sizeinfo['mime-type']; - foreach ( $sizeinfo['sources'] as $mime => $properties ) { - - if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { - continue; - } - - // Delete alternate mime types. - if ( $original_size_mime === $mime ) { - continue; - } - - $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } - } + $deleted = $deleted && _wp_delete_alternate_mime_sizes( $sizeinfo['sources'], $intermediate_dir, $file ); } } @@ -6520,6 +6503,11 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } + // Remove any additional mime type full sized images. + if ( isset( $meta['sources' ] ) && is_array( $meta['sources'] ) ) { + $deleted = $deleted && _wp_delete_alternate_mime_sizes( $meta['sources'], $intermediate_dir, $file ); + } + if ( is_array( $backup_sizes ) ) { $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) ); @@ -6543,6 +6531,37 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { return $deleted; } +/** + * Delete sources files from a directory, skipping default mime type. + * + * @since 6.0.0. + * + * @param array $sources The sources array to delete files from. + * @param string $intermediate_dir The directory to delete files from. + * @param string $file Absolute path to the attachment's file. + * @return bool Whether deletion succeeded. + */ +function _wp_delete_alternate_mime_sizes( $sources, $intermediate_dir, $file ) { + $deleted = true; + $index = 0; + foreach ( $sources as $mime => $properties ) { + // Skip the default mime type.] + if ( 0 === $index++ ) { + continue; + } + + if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + continue; + } + + $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; + } + } + return $deleted; +} + /** * Retrieves attachment metadata for attachment ID. * From 54d434656a89eae31840caa768d95c10bf3cb64b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 7 Mar 2022 15:28:02 -0700 Subject: [PATCH 007/102] Update image tag output to use specified mime type. --- src/wp-includes/media.php | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 5ab3cd1913f93..0b5d3f310d0f5 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1843,6 +1843,9 @@ function wp_filter_content_tags( $content, $context = null ) { $filtered_image = wp_img_tag_add_loading_attr( $filtered_image, $context ); } + // Use alternate mime types when specified and available. + $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $attachment_id ); + if ( $filtered_image !== $match[0] ) { $content = str_replace( $match[0], $filtered_image, $content ); } @@ -1866,6 +1869,52 @@ function wp_filter_content_tags( $content, $context = null ) { return $content; } +/** + * Potentially add alternate mime type images to the content output. + * + * @since 6.0.0 + * + * @param string $image The HTML `img` tag where the attribute should be added. + * @param int $attachment_id The attachment ID. + * @return string Converted `img` tag with `loading` attribute added. + */ +function wp_image_use_alternate_mime_types( $image, $attachment_id ) { + $metadata = wp_get_attachment_metadata( $attachment_id ); + if ( empty( $metadata['file'] ) ) { + return $image; + } + + // TODO: Add a filterable option to determine image extensions. + $target_image_extensions = array( + 'jpg', + 'jpeg', + ); + + // TODO: Add a filterable option to change the selected mime type. + $target_mime = 'image/webp'; + + // Find the appropriate size for the provided URL. + foreach ( $metadata['sizes'] as $name => $size_data ) { + // Not the size we are looking for. + if ( empty( $size_data['file'] ) ) { + continue; + } + + if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) { + continue; + } + $src_filename = wp_basename( $size_data['file'] ); + + // This is the same as the file we want to replace nothing to do here. + if ( $size_data['sources'][ $target_mime ]['file'] === $src_filename ) { + continue; + } + + $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); + } + return $image; +} + /** * Adds `loading` attribute to an `img` HTML tag. * From daaacba88f3424c8cfa40bf3fdb1830581b44f1c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 7 Mar 2022 15:52:14 -0700 Subject: [PATCH 008/102] Ensure full size image generated, sources populated --- src/wp-admin/includes/image.php | 50 ++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 0fa4b6b177577..299e1963656e2 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -235,10 +235,11 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Default image meta. $image_meta = array( - 'width' => $imagesize[0], - 'height' => $imagesize[1], - 'file' => _wp_relative_upload_path( $file ), - 'sizes' => array(), + 'width' => $imagesize[0], + 'height' => $imagesize[1], + 'file' => _wp_relative_upload_path( $file ), + 'sizes' => array(), + 'sources' => array(), ); // Fetch additional metadata from EXIF/IPTC. @@ -250,6 +251,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $mime_type = $imagesize['mime']; // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $mime_type ) { + $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); + $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); /** * Filters the "BIG image" threshold value. @@ -273,10 +276,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { * @param int $attachment_id Attachment post ID. */ $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - + $over_threshold = $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ); // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. - if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { + if ( $over_threshold ) { $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { @@ -297,10 +300,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); - - $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); - // Generate all off of the output types. foreach( $output_mime_types as $mime_index => $mime_type ) { if ( 0 === $mime_index ) { @@ -365,6 +364,37 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } } + + // When not over threshold, still generate alternate mime type full size + // images and populate the root level sources array. + if ( ! $over_threshold ) { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + + foreach( $output_mime_types as $mime_index => $mime_type ) { + // Only store data for first mime type. + if ( 0 === $mime_index ) { + $image_meta['sources'][ $mime_type ] = array( + 'file' => wp_basename( $image_meta['file'] ), + 'filesize' => filesize( $file ), + ); + continue; + } + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $mime_type ) ), $mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $mime_type ] = array( + 'file' => $saved['file'], + 'filesize' => filesize( $saved['path'] ) + ); + } else { + // @TODO Log errors + } + } + } } /* From 958e752bab006a4bf151dd5969f6e4d128f6f6e1 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 7 Mar 2022 17:18:42 -0700 Subject: [PATCH 009/102] Handle the full size image in image tags --- src/wp-includes/media.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 0b5d3f310d0f5..d7f6bd467eb4b 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1912,6 +1912,22 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); } + + // Handle the full size image. + if ( isset( $metadata['sources'] ) && isset( $metadata['sources'][ $target_mime ] ) ) { + if ( empty( $metadata['sources'][ $target_mime ]['file'] ) ) { + return $image; + } + $src_filename = wp_basename( $metadata['file'] ); + + // This is the same as the file we want to replace nothing to do here. + if ( $metadata['sources'][ $target_mime ]['file'] === $src_filename ) { + return $image; + } + + $image = str_replace( $src_filename, $metadata['sources'][ $target_mime ]['file'], $image ); + } + return $image; } From 80a78d14cac1dc6b21fb756339263afec4e3214f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 10:43:32 -0700 Subject: [PATCH 010/102] Cleanup, documentation --- src/wp-admin/includes/image.php | 100 ++++++++++++++++---------------- src/wp-includes/media.php | 2 +- src/wp-includes/post.php | 3 +- 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 299e1963656e2..a24301421e265 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -214,12 +214,13 @@ function _wp_image_meta_replace_original( $saved_data, $original_file, $image_me } /** - * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata. + * Creates image mime variations and sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata. * * Intended for use after an image is uploaded. Saves/updates the image metadata after each * sub-size is created. If there was an error, it is added to the returned image metadata array. * * @since 5.3.0 + * @since 6.0.0 Generates sub-sizes in alternate mime types based on the `wp_image_mime_transforms` filter. * * @param string $file Full path to the image file. * @param int $attachment_id Attachment ID to process. @@ -251,7 +252,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $mime_type = $imagesize['mime']; // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $mime_type ) { - $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); + $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id ); $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); /** @@ -276,10 +277,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { * @param int $attachment_id Attachment post ID. */ $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - $over_threshold = $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ); - // If the original image's dimensions are over the threshold, - // scale the image and use it as the "full" size. - if ( $over_threshold ) { + + // If the original image's dimensions are over the threshold, scale the image and use it as the "full" size. + if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { @@ -300,8 +300,14 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - // Generate all off of the output types. - foreach( $output_mime_types as $mime_index => $mime_type ) { + foreach ( $output_mime_types as $mime_index => $mime_type ) { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + // The first mime type is the default and crates a file named "my_image-scaled". if ( 0 === $mime_index ) { $saved = $editor->save( $editor->generate_filename( 'scaled' ), $mime_type ); if ( ! is_wp_error( $saved ) ) { @@ -315,67 +321,63 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // TODO: Log errors. } } else { - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $mime_type ) ), $mime_type ); + // Additional mime types are generated from the original image and named "my_image-{mime-extension}-scaled" + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $mime_type ) . '-scaled' ), $mime_type ); } if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $mime_type ] = array( - 'file' => $saved['file'], - 'filesize' => filesize( $saved['path'] ) + 'file' => $saved['file'], + 'filesize' => filesize( $saved['path'] ), ); // If the image was rotated update the stored EXIF data. if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { $image_meta['image_meta']['orientation'] = 1; } else { - // TODO: Log errors. + // TODO: Log errors. } } } } else { // TODO: Log errors. } + } else { + // When not over threshold, still generate alternate mime type full size + // images and populate the root level sources array. + + if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + $editor = wp_get_image_editor( $file ); + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. + $rotated = $editor->maybe_exif_rotate(); - } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - - // Rotate the image. - $rotated = $editor->maybe_exif_rotate(); - - if ( true === $rotated ) { - // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); + if ( true === $rotated ) { + // Append `-rotated` to the image file name. + $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - // Update the stored EXIF data. - if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; + // Update the stored EXIF data. + if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } + } else { + // TODO: Log errors. } - } else { - // TODO: Log errors. } } - } - // When not over threshold, still generate alternate mime type full size - // images and populate the root level sources array. - if ( ! $over_threshold ) { $editor = wp_get_image_editor( $file ); - if ( is_wp_error( $editor ) ) { // This image cannot be edited. return $image_meta; } - foreach( $output_mime_types as $mime_index => $mime_type ) { + foreach ( $output_mime_types as $mime_index => $mime_type ) { // Only store data for first mime type. if ( 0 === $mime_index ) { $image_meta['sources'][ $mime_type ] = array( @@ -388,7 +390,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $mime_type ] = array( 'file' => $saved['file'], - 'filesize' => filesize( $saved['path'] ) + 'filesize' => filesize( $saved['path'] ), ); } else { // @TODO Log errors @@ -451,17 +453,17 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } // Assemble the output mime types - $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id); + $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id ); $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); $dirname = pathinfo( $file, PATHINFO_DIRNAME ); // Generate all off of the output types. - foreach( $output_mime_types as $mime_index => $mime_type ) { + foreach ( $output_mime_types as $mime_index => $mime_type ) { // Check if any of the new sizes already exist for this mime type. if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - if ( ! isset ( $size_meta['sources'] ) ) { + if ( ! isset( $size_meta['sources'] ) ) { $size_meta['sources'] = array(); } @@ -531,9 +533,9 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } // Store the mime type specific sub-size in the 'sources' attribute. - $source_meta = array( - 'file' => $new_size_meta['file'], - 'filesize' => 0 + $source_meta = array( + 'file' => $new_size_meta['file'], + 'filesize' => 0, ); $file_location = path_join( $dirname, $new_size_meta['file'] ); if ( file_exists( $file_location ) ) { @@ -1234,7 +1236,7 @@ function _copy_image_file( $attachment_id ) { * @param $attachment_id int The attachment ID. * @return array> An array of valid mime types, where the key is the mime type and the value is the extension type. */ -function wp_get_image_mime_transforms( $attachment_id) { +function wp_get_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), 'image/webp' => array( 'image/webp', 'image/jpeg' ), diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index d7f6bd467eb4b..417570b42cdc7 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1870,7 +1870,7 @@ function wp_filter_content_tags( $content, $context = null ) { } /** - * Potentially add alternate mime type images to the content output. + * Potentially use alternate mime type images in the content output. * * @since 6.0.0 * diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 8a1afb2016f01..2cde12aaabac6 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6483,7 +6483,6 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { $deleted = $deleted && _wp_delete_alternate_mime_sizes( $sizeinfo['sources'], $intermediate_dir, $file ); } - } } @@ -6504,7 +6503,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } // Remove any additional mime type full sized images. - if ( isset( $meta['sources' ] ) && is_array( $meta['sources'] ) ) { + if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { $deleted = $deleted && _wp_delete_alternate_mime_sizes( $meta['sources'], $intermediate_dir, $file ); } From f2d0893a7e9c725468cab55f19f00af35c4b7f68 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 14:25:13 -0700 Subject: [PATCH 011/102] Unroll full sized image generation to single loop, dry out code with _wp_get_sources_from_meta --- src/wp-admin/includes/image.php | 166 ++++++++++++++++---------------- 1 file changed, 82 insertions(+), 84 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index a24301421e265..6c3a802454817 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -278,40 +278,38 @@ function wp_create_image_subsizes( $file, $attachment_id ) { */ $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - // If the original image's dimensions are over the threshold, scale the image and use it as the "full" size. - if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { - $editor = wp_get_image_editor( $file ); + // Generate full sized images for all mime types. + foreach ( $output_mime_types as $mime_index => $output_mime_type ) { + // If the original image's dimensions are over the threshold, + // scale the image and use it as the "full" size. + if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { + $editor = wp_get_image_editor( $file ); - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } - // Resize the image. - $resized = $editor->resize( $threshold, $threshold ); - $rotated = null; + // Resize the image. + $resized = $editor->resize( $threshold, $threshold ); + $rotated = null; - // If there is EXIF data, rotate according to EXIF Orientation. - if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { - $resized = $editor->maybe_exif_rotate(); - $rotated = $resized; - } + // If there is EXIF data, rotate according to EXIF Orientation. + if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { + $resized = $editor->maybe_exif_rotate(); + $rotated = $resized; + } - if ( ! is_wp_error( $resized ) ) { - // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". - // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - foreach ( $output_mime_types as $mime_index => $mime_type ) { - $editor = wp_get_image_editor( $file ); + if ( ! is_wp_error( $resized ) ) { + // For the default mime type scaled image is named "my_image-scaled.jpg". + if ( $mime_type === $output_mime_type ) { + // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". + // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). + $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - // The first mime type is the default and crates a file named "my_image-scaled". - if ( 0 === $mime_index ) { - $saved = $editor->save( $editor->generate_filename( 'scaled' ), $mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); // If the image was rotated update the stored EXIF data. if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { @@ -321,46 +319,42 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // TODO: Log errors. } } else { - // Additional mime types are generated from the original image and named "my_image-{mime-extension}-scaled" - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $mime_type ) . '-scaled' ), $mime_type ); - } - - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $mime_type ] = array( - 'file' => $saved['file'], - 'filesize' => filesize( $saved['path'] ), - ); - // If the image was rotated update the stored EXIF data. - if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } else { - // TODO: Log errors. + // Additional mime types are named "my_image-{mime-extension}-scaled" + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) . '-scaled' ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); } } + + } else { + // TODO: Log errors. } } else { - // TODO: Log errors. - } - } else { - // When not over threshold, still generate alternate mime type full size - // images and populate the root level sources array. + if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - $editor = wp_get_image_editor( $file ); - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - $rotated = $editor->maybe_exif_rotate(); + $editor = wp_get_image_editor( $file ); - if ( true === $rotated ) { - // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + + // Rotate the image. + $rotated = $editor->maybe_exif_rotate(); + + // For the default mime type rotated image is named "my_image-rotated.jpg". + if ( true === $rotated && $mime_type === $output_mime_type ) { + // Append `-rotated` to the image file name. + $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); + } else { + // For alternate mimes, append -{mime-extension}-rotated to the image file name. + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) . '-rotated' ), $output_mime_type ); + } if ( ! is_wp_error( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); // Update the stored EXIF data. if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { $image_meta['image_meta']['orientation'] = 1; @@ -368,37 +362,28 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } else { // TODO: Log errors. } - } - } - - $editor = wp_get_image_editor( $file ); - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - - foreach ( $output_mime_types as $mime_index => $mime_type ) { - // Only store data for first mime type. - if ( 0 === $mime_index ) { - $image_meta['sources'][ $mime_type ] = array( - 'file' => wp_basename( $image_meta['file'] ), - 'filesize' => filesize( $file ), - ); - continue; - } - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $mime_type ) ), $mime_type ); - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $mime_type ] = array( - 'file' => $saved['file'], - 'filesize' => filesize( $saved['path'] ), - ); } else { - // @TODO Log errors + // For the default mime type, only add the file data to the 'sources' array. + if ( $mime_type === $output_mime_type ) { + $image_meta['sources'][ $output_mime_type ] = array( + 'file' => wp_basename( $image_meta['file'] ), + 'filesize' => filesize( $file ), + ); + } else { + // For alternate mime types, generate a full size image and add it to the 'sources' array. + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + } else { + // @TODO Log errors + } + } } } } } + /* * Initial save of the new metadata. * At this point the file was uploaded and moved to the uploads directory @@ -424,6 +409,19 @@ function wp_create_image_subsizes( $file, $attachment_id ) { return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ); } +/** + * Get a sources array element from a meta. + * + * @param array $meta The meta to get the source from. + * @return array The source array element. + */ +function _wp_get_sources_from_meta( $meta ) { + return array( + 'file' => wp_basename( $meta['file'] ), + 'filesize' => filesize( $meta['path'] ), + ); +} + /** * Low-level function to create image sub-sizes. * @@ -528,7 +526,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // TODO: Log errors. } else { // Save the default (first) mime size in the 'sizes'meta value. - if ( 0 === $mime_index ) { + if ( $mime_type === $mime_type ) { $image_meta['sizes'][ $new_size_name ] = $new_size_meta; } From 08228509f03dbb233e78f74af1fb21c2ac343c66 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 14:38:52 -0700 Subject: [PATCH 012/102] remove duplicate editor construction --- src/wp-admin/includes/image.php | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 6c3a802454817..e8f5e4c8036bb 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -280,16 +280,16 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Generate full sized images for all mime types. foreach ( $output_mime_types as $mime_index => $output_mime_type ) { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - // Resize the image. $resized = $editor->resize( $threshold, $threshold ); $rotated = null; @@ -333,12 +333,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - $editor = wp_get_image_editor( $file ); - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } // Rotate the image. $rotated = $editor->maybe_exif_rotate(); @@ -456,7 +451,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $dirname = pathinfo( $file, PATHINFO_DIRNAME ); // Generate all off of the output types. - foreach ( $output_mime_types as $mime_index => $mime_type ) { + foreach ( $output_mime_types as $mime_index => $output_mime_type ) { // Check if any of the new sizes already exist for this mime type. if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { @@ -465,8 +460,8 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $size_meta['sources'] = array(); } - if ( ! isset( $new_sizes['sources'][ $mime_type ] ) ) { - $new_sizes['sources'][ $mime_type ] = array(); + if ( ! isset( $new_sizes['sources'][ $output_mime_type ] ) ) { + $new_sizes['sources'][ $output_mime_type ] = array(); continue; } @@ -475,7 +470,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { * don't match the currently defined size with the same name. * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. */ - if ( array_key_exists( $size_name, $new_sizes['sources'][ $mime_type ] ) ) { + if ( array_key_exists( $size_name, $new_sizes['sources'][ $output_mime_type ] ) ) { unset( $new_sizes[ $size_name ] ); } } @@ -520,13 +515,13 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( method_exists( $editor, 'make_subsize' ) ) { foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $mime_type ); + $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { // Save the default (first) mime size in the 'sizes'meta value. - if ( $mime_type === $mime_type ) { + if ( $mime_type === $output_mime_type ) { $image_meta['sizes'][ $new_size_name ] = $new_size_meta; } @@ -539,7 +534,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( file_exists( $file_location ) ) { $source_meta['filesize'] = filesize( $file_location ); } - $image_meta['sizes'][ $new_size_name ]['sources'][ $mime_type ] = $source_meta; + $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = $source_meta; wp_update_attachment_metadata( $attachment_id, $image_meta ); } } From bd9a90c8bd8ae4df252160f697e5cf973439e0d9 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 15:01:46 -0700 Subject: [PATCH 013/102] Add mime type support check in loops, skip trying unsupported mime types. * Revert to 0 (first element) as default mime type ofr sub sizes --- src/wp-admin/includes/image.php | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index e8f5e4c8036bb..cbe3de28d6acf 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -287,6 +287,12 @@ function wp_create_image_subsizes( $file, $attachment_id ) { return $image_meta; } + // This mime type is not supported, skip it unless it is the only mime type, in which case + // core will fall back to a supported type. + if ( ! $editor->supports_mime_type( $output_mime_type ) && 1 < sizeof( $output_mime_types ) ) { + continue; + } + // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { @@ -302,7 +308,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // For the default mime type scaled image is named "my_image-scaled.jpg". - if ( $mime_type === $output_mime_type ) { + if ( 0 === $mime_index ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); @@ -339,7 +345,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $rotated = $editor->maybe_exif_rotate(); // For the default mime type rotated image is named "my_image-rotated.jpg". - if ( true === $rotated && $mime_type === $output_mime_type ) { + if ( true === $rotated && 0 === $mime_index ) { // Append `-rotated` to the image file name. $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); } else { @@ -359,7 +365,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } else { // For the default mime type, only add the file data to the 'sources' array. - if ( $mime_type === $output_mime_type ) { + if ( 0 === $mime_index ) { $image_meta['sources'][ $output_mime_type ] = array( 'file' => wp_basename( $image_meta['file'] ), 'filesize' => filesize( $file ), @@ -504,6 +510,12 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { return $image_meta; } + // This mime type is not supported, skip it unless it is the only mime type, in which case + // core will fall back to a supported type. + if ( ! $editor->supports_mime_type( $output_mime_type ) && 1 < sizeof( $output_mime_types ) ) { + return $image_meta; + } + // If stored EXIF data exists, rotate the source image before creating sub-sizes. if ( ! empty( $image_meta['image_meta'] ) ) { $rotated = $editor->maybe_exif_rotate(); @@ -521,7 +533,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // TODO: Log errors. } else { // Save the default (first) mime size in the 'sizes'meta value. - if ( $mime_type === $output_mime_type ) { + if ( 0 === $mime_index ) { $image_meta['sizes'][ $new_size_name ] = $new_size_meta; } From f8634315525072d66de6de618b85d7148170f5dd Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 15:10:50 -0700 Subject: [PATCH 014/102] Docblock improvements & cleanup --- src/wp-admin/includes/image.php | 8 ++------ src/wp-includes/class-wp-image-editor-imagick.php | 1 + src/wp-includes/media.php | 2 +- src/wp-includes/post.php | 3 ++- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index cbe3de28d6acf..db5393b8c8af1 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -314,7 +314,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); // If the image was rotated update the stored EXIF data. @@ -331,7 +331,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); } } - } else { // TODO: Log errors. } @@ -339,8 +338,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - // Rotate the image. $rotated = $editor->maybe_exif_rotate(); @@ -354,7 +351,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); // Update the stored EXIF data. if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { @@ -384,7 +381,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } - /* * Initial save of the new metadata. * At this point the file was uploaded and moved to the uploads directory diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 2614d6e4f4ee9..b4112c6dd3455 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -472,6 +472,7 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 + * @since 6.0.0 Added the $mime_type parameter. * * @param array $size_data { * Array of size data. diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 417570b42cdc7..47fbb8d4d246e 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1913,7 +1913,7 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); } - // Handle the full size image. + // Ensure the full size image is also replaced. if ( isset( $metadata['sources'] ) && isset( $metadata['sources'][ $target_mime ] ) ) { if ( empty( $metadata['sources'][ $target_mime ]['file'] ) ) { return $image; diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 2cde12aaabac6..296372d121d85 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6480,6 +6480,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $deleted = false; } } + // Check for alternate mime types in the sizeinfo['sources'] array to delete. if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { $deleted = $deleted && _wp_delete_alternate_mime_sizes( $sizeinfo['sources'], $intermediate_dir, $file ); } @@ -6502,7 +6503,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } - // Remove any additional mime type full sized images. + // Check for alternate full size mime types in the root sources array to delete. if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { $deleted = $deleted && _wp_delete_alternate_mime_sizes( $meta['sources'], $intermediate_dir, $file ); } From cbd4ce74a1bc05fed5e4a2eb86c90a64def0f3c1 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 16:01:13 -0700 Subject: [PATCH 015/102] Cleanup and match filter name to issue --- src/wp-admin/includes/image.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index db5393b8c8af1..ec33bed585c7b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -252,7 +252,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $mime_type = $imagesize['mime']; // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $mime_type ) { - $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id ); + $valid_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); /** @@ -448,11 +448,11 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } // Assemble the output mime types - $valid_mime_transforms = wp_get_image_mime_transforms( $attachment_id ); + $valid_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - // Generate all off of the output types. + // Generate all off of the output mime types. foreach ( $output_mime_types as $mime_index => $output_mime_type ) { // Check if any of the new sizes already exist for this mime type. @@ -1237,7 +1237,7 @@ function _copy_image_file( $attachment_id ) { * @param $attachment_id int The attachment ID. * @return array> An array of valid mime types, where the key is the mime type and the value is the extension type. */ -function wp_get_image_mime_transforms( $attachment_id ) { +function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), 'image/webp' => array( 'image/webp', 'image/jpeg' ), @@ -1252,5 +1252,5 @@ function wp_get_image_mime_transforms( $attachment_id ) { * @param array $image_mime_transforms A map with the valid mime transforms. * @param int $attachment_id The ID of the attachment where the hook was dispatched. */ - return (array) apply_filters( 'wp_image_mime_transforms', $image_mime_transforms, $attachment_id ); + return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id ); } From 810818a3cf9bcb629326ba268325ab2ddadb3ec1 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 16:01:35 -0700 Subject: [PATCH 016/102] =?UTF-8?q?Add=20a=20new=20=E2=80=98wp=5Fcontent?= =?UTF-8?q?=5Fimage=5Fmimes=E2=80=99=20filter=20for=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wp-includes/media.php | 40 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 47fbb8d4d246e..a0450e99f36ca 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1884,14 +1884,20 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { return $image; } - // TODO: Add a filterable option to determine image extensions. - $target_image_extensions = array( - 'jpg', - 'jpeg', - ); + $target_mimes = array( 'image/webp', 'image/jpeg' ); - // TODO: Add a filterable option to change the selected mime type. - $target_mime = 'image/webp'; + /** + * Filter the content image mime type output selection and order. + * + * When outputting images in the content, the first mime type available will be used. + * + * @since 6.0.0 + * + * @param array $target_mimes The image output mime type and order. Default is ( 'image/webp', 'image/jpeg' ). + * @return array The filtered output mime type and order. + * + */ + $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes ); // Find the appropriate size for the provided URL. foreach ( $metadata['sizes'] as $name => $size_data ) { @@ -1899,18 +1905,20 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { if ( empty( $size_data['file'] ) ) { continue; } + foreach ( $target_mimes as $target_mime ) { + if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) { + continue; + } + $src_filename = wp_basename( $size_data['file'] ); - if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) { - continue; - } - $src_filename = wp_basename( $size_data['file'] ); + // This is the same as the file we want to replace nothing to do here. + if ( $size_data['sources'][ $target_mime ]['file'] === $src_filename ) { + continue; + } - // This is the same as the file we want to replace nothing to do here. - if ( $size_data['sources'][ $target_mime ]['file'] === $src_filename ) { - continue; + $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); + break; } - - $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); } // Ensure the full size image is also replaced. From 892b34cb0b42e72b0b8db5975829dbd656dfb4f6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 8 Mar 2022 16:31:28 -0700 Subject: [PATCH 017/102] Clean up and document image replacement approach. --- src/wp-admin/includes/image.php | 17 ++++++++--------- src/wp-includes/media.php | 27 +++++++++++++-------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index ec33bed585c7b..ec14a6a3e5ef8 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -436,7 +436,6 @@ function _wp_get_sources_from_meta( $meta ) { * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. */ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { - $mime_type = get_post_mime_type( $attachment_id ); if ( empty( $image_meta ) || ! is_array( $image_meta ) ) { // Not an image attachment. return array(); @@ -446,6 +445,13 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( ! isset( $metadata['sources'] ) || ! is_array( $metadata['sources'] ) ) { $metadata['sources'] = array(); } + $mime_type = get_post_mime_type( $attachment_id ); + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // The image cannot be edited. + return $image_meta; + } // Assemble the output mime types $valid_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); @@ -499,17 +505,10 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // The image cannot be edited. - return $image_meta; - } - // This mime type is not supported, skip it unless it is the only mime type, in which case // core will fall back to a supported type. if ( ! $editor->supports_mime_type( $output_mime_type ) && 1 < sizeof( $output_mime_types ) ) { - return $image_meta; + continue; } // If stored EXIF data exists, rotate the source image before creating sub-sizes. diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index a0450e99f36ca..502550a93580a 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1899,13 +1899,18 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { */ $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes ); - // Find the appropriate size for the provided URL. - foreach ( $metadata['sizes'] as $name => $size_data ) { - // Not the size we are looking for. - if ( empty( $size_data['file'] ) ) { + // Find the appropriate size for the provided URL in the first available mime type. + foreach ( $target_mimes as $target_mime ) { + if ( ! isset( $metadata['sources'][ $target_mime ] ) || empty( $metadata['sources'][ $target_mime ]['file'] ) ) { continue; } - foreach ( $target_mimes as $target_mime ) { + + // Handle sub-sized image replacement. + foreach ( $metadata['sizes'] as $name => $size_data ) { + // Not the size we are looking for. + if ( empty( $size_data['file'] ) ) { + continue; + } if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) { continue; } @@ -1916,16 +1921,11 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { continue; } - $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); - break; + // Found a match, replace with the new filename and stop searching. + $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); } - } - // Ensure the full size image is also replaced. - if ( isset( $metadata['sources'] ) && isset( $metadata['sources'][ $target_mime ] ) ) { - if ( empty( $metadata['sources'][ $target_mime ]['file'] ) ) { - return $image; - } + // Handle full size image replacement $src_filename = wp_basename( $metadata['file'] ); // This is the same as the file we want to replace nothing to do here. @@ -1935,7 +1935,6 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { $image = str_replace( $src_filename, $metadata['sources'][ $target_mime ]['file'], $image ); } - return $image; } From 0d38d2ebccad919f39a5c2efc6ff0ce6d81dcf89 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 9 Mar 2022 08:55:48 -0700 Subject: [PATCH 018/102] Add default value to make_subsize functions --- src/wp-includes/class-wp-image-editor-gd.php | 2 +- src/wp-includes/class-wp-image-editor-imagick.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index 5acc29b62cc40..b7e48c5904c65 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -274,7 +274,7 @@ public function multi_resize( $sizes ) { * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data, $mime_type ) { + public function make_subsize( $size_data, $mime_type = null ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index b4112c6dd3455..0f11840df7176 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -485,7 +485,7 @@ public function multi_resize( $sizes ) { * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data, $mime_type ) { + public function make_subsize( $size_data, $mime_type = null ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } From 775f4ffe4840d79280fdc7933d32f5430ba81151 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 9 Mar 2022 09:24:39 -0700 Subject: [PATCH 019/102] Add and improve tests --- src/wp-admin/includes/image.php | 10 +- src/wp-includes/class-wp-image-editor-gd.php | 2 +- .../class-wp-image-editor-imagick.php | 2 +- src/wp-includes/media.php | 2 +- tests/phpunit/data/images/test-image.jpeg | Bin 0 -> 2028 bytes tests/phpunit/tests/image/editor.php | 406 ++++++++++++++++++ 6 files changed, 414 insertions(+), 8 deletions(-) create mode 100644 tests/phpunit/data/images/test-image.jpeg diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index ec14a6a3e5ef8..98180e5b9e9a1 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -313,7 +313,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); - if ( ! is_wp_error( $saved ) ) { + if ( ! is_wp_error( $saved ) && ! empty( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); @@ -327,7 +327,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } else { // Additional mime types are named "my_image-{mime-extension}-scaled" $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) . '-scaled' ), $output_mime_type ); - if ( ! is_wp_error( $saved ) ) { + if ( ! is_wp_error( $saved && ! empty( $saved ) ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); } } @@ -350,7 +350,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) . '-rotated' ), $output_mime_type ); } - if ( ! is_wp_error( $saved ) ) { + if ( ! is_wp_error( $saved && ! empty( $saved ) ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); // Update the stored EXIF data. @@ -370,7 +370,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } else { // For alternate mime types, generate a full size image and add it to the 'sources' array. $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) ), $output_mime_type ); - if ( ! is_wp_error( $saved ) ) { + if ( ! is_wp_error( $saved ) && ! empty( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); } else { // @TODO Log errors @@ -446,7 +446,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $metadata['sources'] = array(); } $mime_type = get_post_mime_type( $attachment_id ); - $editor = wp_get_image_editor( $file ); + $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { // The image cannot be edited. diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index b7e48c5904c65..b393464a90288 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -247,7 +247,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data ); + $meta = $this->make_subsize( $size_data, null ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 0f11840df7176..ac8fc716f07c2 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -458,7 +458,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data ); + $meta = $this->make_subsize( $size_data, null ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 502550a93580a..247cd10f5b784 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1922,7 +1922,7 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { } // Found a match, replace with the new filename and stop searching. - $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); + $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); } // Handle full size image replacement diff --git a/tests/phpunit/data/images/test-image.jpeg b/tests/phpunit/data/images/test-image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..534aac1d6bf494f7e83c96b32cfe29271e8aac5a GIT binary patch literal 2028 zcmbu9dr(t%7RP@#A%s9M1_M#3O~Dt1TBd+Rgp##vMX-pVB9KCg6dQ;vBM%J{Kmmyu zlp-oER04)V-4!WI0zQD41VL?4UdqEok{}O7I7=&|K(rj7AT%0fhQ?sb zjDvu(jPHRN5o6={$yW0fp|Mz}LnQa(CDj&dK5LZNhV{a0J@%*M;wP>w&H zn4Fq^_FVOP?#;Y<0r_pw#03HL(#ZZRmvIOX%FGOHhBa|Ps3RtDq8Y~V6LXubq1f0% zE1cYqTaZ30scw|u)_8=$w)<0hE$!BND%Pn?Xm82>H?Z9Qi);z(53Uh_M?=Qtp^1P7 z^m=Ddg*xUEF*fx4@ZHSXCnJe=DZag3N8(bgM}=qUA?rlawXkoAha?sMXwEsMH-uEO zIg)q|(2ZG^z?e+9Pe5D)UY2EQaM_Za)qdVc_^NaoZ{g@BzVyNHxXkPD*ob}0DKXQH zM{Y#cD8kEXrWG;KDOuExEy8Tqfizw^m0XT3m_NZ=R&|Up`B;xv7(nhZ7K7-woReh? z*_`M{^WN?2IB1oZNF`95ZYjL_aYYA}v8**md4Q`Fb0-RP?ITTW2c^2E-SJ`No2=$& zf92E0yCtz76rnZDrB8)2X{ii@)iwYtZ=RNH(-m>hvRgA<0#vbJ(l9 z>bHJ)hd_T%Z}G4vgJ<3bYov1wZCuZW$Mzvf84U>w_^(fSRuLxtp|=dJouR>78OUY> zP)xmSkk@-@cIqyQnaQ>dUBRp``Zi8z%zuBdcC{Sr<=O3SCqLq z-P&zW|0~5e1<$*7HD1eo)u&v~oXU-u*yeKUTkFKPOCjq7*}iOoIvT;Miq$-%I!nig z4M5J96_YtZsy%m9^#hBHd?CFpCfFl5GBSKB@=<&(f^!$qO zv_g*yyleoHL+vE33sSEMMW}k57{-p13yt*z4BG};5`m-*LrOJYKBHK-#{PsHQD*b% z5*hebQYF4rcJ%yIftDiA!Hq%+YpY#1sO9m^ZJe|8p(~eT6QS1&+V`k%oZfO)lcK-f ztM%8_h~*W0lpkT5-}pqJq?|70gd@?KaHiN+%c|CXTJe`GsxtdzCEft?lfSq&;JYa! zfu$H*iJZxF5Z>&+1Y;@%&Ga3tJMlmlV*q&};$#CrSPzi(Ps-p8om&Rg#kUP$h!@tM zZT-A)^vjHq3j#QGx2Sa>+nm++Vcf*#+3xCWm|qe*F3mWo`SuMK9*R{fhLC zH2G6E#Pz`oo{Dvw*2LA?3`%M5YpGYXA0v61-CAb`;@s7d1I^g5+E*iVc(;T?O0&u# za=Zu9X9-r1>9Z>$XY3ZaMcVipW*IrEEB^qJRLfla0he?2mpFh`2f_hLuEec~hWB+H z=MN7m39^vFG3?bBJI~9<>^L8&0t&RTu%1OB1TDUIBm6`?J7{57PxCa{&SttLD!u+z zmeZ@wB=<=x7BBLvBD9Y>AK_|fS`Wm3JUUZdty+*i;8M`@8=YpPF+M76XEK@1k}wE= z6E=_~Zrn-u)%AL$DYWm+e&4{=+Hz0(gDv~B!s(51EYjSsB|l?-+nytFpvkMnIOFz9 zDR2a3)r>u@!>8CLTUP0fss2j>~b=h(03{cN-c_1$N%Z5La;vo0Y! zI;s8swf|z}Ny>|h(z@NdXGN(=&e+9YI4(#NEFQ0u&EmBYh?|Na;oG*aMDE|uGyoi8 zykHB3o_sRfzr*W7%V1RWiyJ$|)uT_IwbaV?{w(ejt*pM5Ak>9RHNkCXtY&<^a{_Pd PO?Tnlf9gLS!7%(gr&MST literal 0 HcmV?d00001 diff --git a/tests/phpunit/tests/image/editor.php b/tests/phpunit/tests/image/editor.php index 487dad066489d..06d31e83f1160 100644 --- a/tests/phpunit/tests/image/editor.php +++ b/tests/phpunit/tests/image/editor.php @@ -361,5 +361,411 @@ public function _test_wp_get_webp_info() { ), ); } + /** + * Create the original image mime type when the image is uploaded + * + * @dataProvider provider_image_with_default_behaviors_during_upload + * + * @since 6.0.0 + */ + public function it_should_create_the_original_image_mime_type_when_the_image_is_uploaded( $file_location, $expected_mime, $targeted_mime ) { + $attachment_id = $this->factory->attachment->create_upload_object( $file_location ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertIsArray( $metadata ); + foreach ( $metadata['sizes'] as $size_name => $properties ) { + $this->assertArrayHasKey( 'sources', $properties ); + $this->assertIsArray( $properties['sources'] ); + $this->assertArrayHasKey( $expected_mime, $properties['sources'] ); + $this->assertArrayHasKey( 'filesize', $properties['sources'][ $expected_mime ] ); + $this->assertArrayHasKey( 'file', $properties['sources'][ $expected_mime ] ); + $this->assertArrayHasKey( $targeted_mime, $properties['sources'] ); + $this->assertArrayHasKey( 'filesize', $properties['sources'][ $targeted_mime ] ); + $this->assertArrayHasKey( 'file', $properties['sources'][ $targeted_mime ] ); + } + } + + public function provider_image_with_default_behaviors_during_upload() { + yield 'JPEG image' => array( + DIR_TESTDATA . '/images/test-image.jpg', + 'image/jpeg', + 'image/webp', + ); + + yield 'WebP image' => array( + DIR_TESTDATA . '/images/webp-lossy.webp', + 'image/webp', + 'image/jpeg', + ); + } + + /** + * Not create the sources property if no transform is provided + * + * @since 6.0.0 + */ + public function it_should_not_create_the_sources_property_if_no_transform_is_provided() { + add_filter( 'webp_uploads_supported_image_mime_transforms', '__return_empty_array' ); + + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertIsArray( $metadata ); + foreach ( $metadata['sizes'] as $size_name => $properties ) { + $this->assertArrayNotHasKey( 'sources', $properties ); + } + } + + /** + * Create the sources property when no transform is available + * + * @since 6.0.0 + */ + public function it_should_create_the_sources_property_when_no_transform_is_available() { + add_filter( + 'webp_uploads_supported_image_mime_transforms', + function () { + return array( 'image/jpeg' => array() ); + } + ); + + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertIsArray( $metadata ); + foreach ( $metadata['sizes'] as $size_name => $properties ) { + $this->assertArrayHasKey( 'sources', $properties ); + $this->assertIsArray( $properties['sources'] ); + $this->assertArrayHasKey( 'image/jpeg', $properties['sources'] ); + $this->assertArrayHasKey( 'filesize', $properties['sources']['image/jpeg'] ); + $this->assertArrayHasKey( 'file', $properties['sources']['image/jpeg'] ); + $this->assertArrayNotHasKey( 'image/webp', $properties['sources'] ); + } + } + + /** + * Not create the sources property if the mime is not specified on the transforms images + * + * @since 6.0.0 + */ + public function it_should_not_create_the_sources_property_if_the_mime_is_not_specified_on_the_transforms_images() { + add_filter( + 'webp_uploads_supported_image_mime_transforms', + function () { + return array( 'image/jpeg' => array() ); + } + ); + + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/webp-lossy.webp' + ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertIsArray( $metadata ); + foreach ( $metadata['sizes'] as $size_name => $properties ) { + $this->assertArrayNotHasKey( 'sources', $properties ); + } + } + + /** + * Create a WebP version with all the required properties + * + * @since 6.0.0 + */ + public function it_should_create_a_webp_version_with_all_the_required_properties() { + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + $this->assertArrayHasKey( 'sources', $metadata['sizes']['thumbnail'] ); + $this->assertArrayHasKey( 'image/jpeg', $metadata['sizes']['thumbnail']['sources'] ); + $this->assertArrayHasKey( 'filesize', $metadata['sizes']['thumbnail']['sources']['image/jpeg'] ); + $this->assertArrayHasKey( 'file', $metadata['sizes']['thumbnail']['sources']['image/jpeg'] ); + $this->assertArrayHasKey( 'image/webp', $metadata['sizes']['thumbnail']['sources'] ); + $this->assertArrayHasKey( 'filesize', $metadata['sizes']['thumbnail']['sources']['image/webp'] ); + $this->assertArrayHasKey( 'file', $metadata['sizes']['thumbnail']['sources']['image/webp'] ); + $this->assertStringEndsNotWith( '.jpeg', $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ); + $this->assertStringEndsWith( '.webp', $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ); + } + + /** + * Remove `scaled` suffix from the generated filename + * + * @since 6.0.0 + */ + public function it_should_remove_scaled_suffix_from_the_generated_filename() { + // The leafs image is 1080 pixels wide with this filter we ensure a -scaled version is created. + add_filter( + 'big_image_size_threshold', + function () { + return 850; + } + ); + + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + $metadata = wp_get_attachment_metadata( $attachment_id ); + $this->assertStringEndsWith( '-scaled.jpg', get_attached_file( $attachment_id ) ); + $this->assertArrayHasKey( 'image/webp', $metadata['sizes']['medium']['sources'] ); + $this->assertStringEndsNotWith( '-scaled.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); + $this->assertStringEndsWith( '-300x200.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); + } + + /** + * Remove the generated webp images when the attachment is deleted + * + * @since 6.0.0 + */ + public function it_should_remove_the_generated_webp_images_when_the_attachment_is_deleted() { + // Make sure no editor is available. + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $file = get_attached_file( $attachment_id, true ); + $dirname = pathinfo( $file, PATHINFO_DIRNAME ); + + $this->assertIsString( $file ); + $this->assertFileExists( $file ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + $sizes = array( 'thumbnail', 'medium' ); + + foreach ( $sizes as $size_name ) { + $this->assertArrayHasKey( 'image/webp', $metadata['sizes'][ $size_name ]['sources'] ); + $this->assertArrayHasKey( 'file', $metadata['sizes'][ $size_name ]['sources']['image/webp'] ); + $this->assertFileExists( + path_join( $dirname, $metadata['sizes'][ $size_name ]['sources']['image/webp']['file'] ) + ); + } + + wp_delete_attachment( $attachment_id ); + + foreach ( $sizes as $size_name ) { + $this->assertFileDoesNotExist( + path_join( $dirname, $metadata['sizes'][ $size_name ]['sources']['image/webp']['file'] ) + ); + } + } + + /** + * Remove the attached WebP version if the attachment is force deleted but empty trash day is not defined + * + * @since 6.0.0 + */ + public function it_should_remove_the_attached_webp_version_if_the_attachment_is_force_deleted_but_empty_trash_day_is_not_defined() { + // Make sure no editor is available. + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $file = get_attached_file( $attachment_id, true ); + $dirname = pathinfo( $file, PATHINFO_DIRNAME ); + + $this->assertIsString( $file ); + $this->assertFileExists( $file ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertFileExists( + path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) + ); + + wp_delete_attachment( $attachment_id, true ); + + $this->assertFileDoesNotExist( + path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) + ); + } + + /** + * Remove the WebP version of the image if the image is force deleted and empty trash days is set to zero + * + * @since 6.0.0 + */ + public function it_should_remove_the_webp_version_of_the_image_if_the_image_is_force_deleted_and_empty_trash_days_is_set_to_zero() { + // Make sure no editor is available. + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $file = get_attached_file( $attachment_id, true ); + $dirname = pathinfo( $file, PATHINFO_DIRNAME ); + + $this->assertIsString( $file ); + $this->assertFileExists( $file ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + $this->assertFileExists( + path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) + ); + + define( 'EMPTY_TRASH_DAYS', 0 ); + + wp_delete_attachment( $attachment_id, true ); + + $this->assertFileDoesNotExist( + path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) + ); + } + + /** + * Avoid the change of URLs of images that are not part of the media library + * + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_avoid_the_change_of_urls_of_images_that_are_not_part_of_the_media_library() { + $paragraph = '

Donec accumsan, sapien et , id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.

'; + + $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) ); + } + + /** + * Avoid replacing not existing attachment IDs + * + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_avoid_replacing_not_existing_attachment_i_ds() { + $paragraph = '

Donec accumsan, sapien et , id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.

'; + + $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) ); + } + + /** + * Prevent replacing a WebP image + * + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_prevent_replacing_a_webp_image() { + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/webp-lossy.webp' + ); + + $tag = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) ); + + $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); + } + + /** + * Prevent replacing a jpg image if the image does not have the target class name + * + * @since 6.0.0 + */ + public function it_should_prevent_replacing_a_jpg_image_if_the_image_does_not_have_the_target_class_name() { + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $tag = wp_get_attachment_image( $attachment_id, 'medium' ); + + $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) ); + } + + /** + * Replace the references to a JPG image to a WebP version + * + * @dataProvider provider_replace_images_with_different_extensions + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_replace_the_references_to_a_jpg_image_to_a_webp_version( $image_path ) { + $attachment_id = $this->factory->attachment->create_upload_object( $image_path ); + + $tag = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) ); + $expected_tag = $tag; + $metadata = wp_get_attachment_metadata( $attachment_id ); + foreach ( $metadata['sizes'] as $size => $properties ) { + $expected_tag = str_replace( $properties['sources']['image/jpeg']['file'], $properties['sources']['image/webp']['file'], $expected_tag ); + } + + $this->assertNotEmpty( $expected_tag ); + $this->assertNotSame( $tag, $expected_tag ); + $this->assertSame( $expected_tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); + } + + public function provider_replace_images_with_different_extensions() { + yield 'An image with a .jpg extension' => array( DIR_TESTDATA . '/images/test-image.jpg' ); + yield 'An image with a .jpeg extension' => array( DIR_TESTDATA . '/images/test-image.jpeg' ); + } + + /** + * Contain the full image size from the original mime + * + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_contain_the_full_image_size_from_the_original_mime() { + $attachment_id = $this->factory->attachment->create_upload_object( + DIR_TESTDATA . '/images/test-image.jpg' + ); + + $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); + + $expected = array( + 'ext' => 'jpg', + 'type' => 'image/jpeg', + ); + $this->assertSame( $expected, wp_check_filetype( get_attached_file( $attachment_id ) ) ); + $this->assertContains( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); + } + + /** + * Prevent replacing an image with no available sources + * + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_prevent_replacing_an_image_with_no_available_sources() { + add_filter( 'webp_uploads_supported_image_mime_transforms', '__return_empty_array' ); + + $attachment_id = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg' ); + + $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); + $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); + } + + /** + * Prevent update not supported images with no available sources + * + * @dataProvider data_provider_not_supported_webp_images + * @group webp_uploads_update_image_references + * + * @since 6.0.0 + */ + public function it_should_prevent_update_not_supported_images_with_no_available_sources( $image_path ) { + $attachment_id = $this->factory->attachment->create_upload_object( $image_path ); + + $this->assertIsNumeric( $attachment_id ); + $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); + + $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); + } + + public function data_provider_not_supported_webp_images() { + yield 'PNG image' => array( DIR_TESTDATA . '/images/test-image.png' ); + yield 'GIFT image' => array( DIR_TESTDATA . '/images/test-image.gif' ); + } + + } From e61ec6b46a0440e025d824f04c43a6f9c1f7d598 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 9 Mar 2022 12:57:20 -0700 Subject: [PATCH 020/102] Add mapping for PDF images to export as jpeg --- src/wp-admin/includes/image.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 98180e5b9e9a1..518401d812f6b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1238,8 +1238,9 @@ function _copy_image_file( $attachment_id ) { */ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( - 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), - 'image/webp' => array( 'image/webp', 'image/jpeg' ), + 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), + 'image/webp' => array( 'image/webp', 'image/jpeg' ), + 'application/pdf' => array( 'image/jpeg' ), ); /** From 913c157b22c32f599c3dd73ba9dec096944603e7 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 9 Mar 2022 13:46:12 -0700 Subject: [PATCH 021/102] =?UTF-8?q?Add=20=E2=80=98sources=E2=80=99=20to=20?= =?UTF-8?q?test=20data,=20clean=20up=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wp-includes/media.php | 14 ++++--- tests/phpunit/tests/image/functions.php | 56 +++++++++++++++++++++++++ tests/phpunit/tests/media.php | 23 ++++++++++ 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 247cd10f5b784..e0a6455bf97fa 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1870,7 +1870,7 @@ function wp_filter_content_tags( $content, $context = null ) { } /** - * Potentially use alternate mime type images in the content output. + * Use alternate mime type images in the content output when available. * * @since 6.0.0 * @@ -1893,12 +1893,14 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { * * @since 6.0.0 * - * @param array $target_mimes The image output mime type and order. Default is ( 'image/webp', 'image/jpeg' ). - * @return array The filtered output mime type and order. - * + * @param array $target_mimes The image output mime type and order. Default is ( 'image/webp', 'image/jpeg' ). + * @param int $attachment_id The attachment ID. + * @return array The filtered output mime type and order. Return an empty array to skip mime type substitution. */ - $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes ); - + $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes, $attachment_id ); + if ( false === $target_mimes ) { + return $image; + } // Find the appropriate size for the provided URL in the first available mime type. foreach ( $target_mimes as $target_mime ) { if ( ! isset( $metadata['sources'][ $target_mime ] ) || empty( $metadata['sources'][ $target_mime ]['file'] ) ) { diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index 8cd42a13b5529..9ee77fdc26412 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -492,23 +492,39 @@ public function test_wp_generate_attachment_metadata_pdf() { 'width' => 232, 'height' => 300, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-232x300.jpg', + ), + ), ), 'large' => array( 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', 'width' => 791, 'height' => 1024, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', + ), + ), ), 'thumbnail' => array( 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', 'width' => 116, 'height' => 150, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', + ), + ), ), ), ); $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); + $metadata = $this->_unset_source_filesize_data( $metadata ); $this->assertSame( $expected, $metadata ); unlink( $test_file ); @@ -562,23 +578,40 @@ public function test_crop_setting_for_pdf() { 'width' => 300, 'height' => 300, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-300x300.jpg', + ), + ), ), 'large' => array( 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', 'width' => 791, 'height' => 1024, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', + ), + ), + ), 'thumbnail' => array( 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', 'width' => 116, 'height' => 150, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', + ), + ), ), ), ); $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); + $metadata = $this->_unset_source_filesize_data( $metadata ); $this->assertSame( $expected, $metadata ); unlink( $test_file ); @@ -587,6 +620,23 @@ public function test_crop_setting_for_pdf() { } } + /** + * Unset the sources filesize for each image size for tests since the filesize + * value will vary depending on the compression characteristics of the + * underlying image library. + * + * @param array $metadata The metadata to clear sources/filesize from. + * @param array The cleaned up meta data. + */ + function _unset_source_filesize_data( $metadata ) { + foreach ( $metadata['sizes'] as $size => $data ) { + if ( isset( $metadata['sizes'][ $size ]['sources']['image/jpeg']['filesize'] ) ) { + unset( $metadata['sizes'][ $size ]['sources']['image/jpeg']['filesize'] ); + } + } + return $metadata; + } + /** * @ticket 39231 */ @@ -622,10 +672,16 @@ public function test_fallback_intermediate_image_sizes() { 'width' => 77, 'height' => 100, 'mime-type' => 'image/jpeg', + 'sources' => array( + 'image/jpeg' => array( + 'file' => 'wordpress-gsoc-flyer-pdf-77x100.jpg', + ), + ), ); $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); $this->assertArrayHasKey( 'test-size', $metadata['sizes'], 'The `test-size` was not added to the metadata.' ); + $metadata = $this->_unset_source_filesize_data( $metadata ); $this->assertSame( $expected, $metadata['sizes']['test-size'] ); remove_image_size( 'test-size' ); diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 5d92d9718bed2..e4ce4c7eb0940 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -2252,11 +2252,14 @@ public function test_wp_filter_content_tags_srcset_sizes() { // Do not add width, height, and loading. add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); + } /** @@ -2285,9 +2288,11 @@ public function test_wp_filter_content_tags_srcset_sizes_wrong() { */ public function test_wp_filter_content_tags_srcset_sizes_with_preexisting_srcset() { // Generate HTML and add a dummy srcset attribute. + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); $img = wp_img_tag_add_loading_attr( $img, 'test' ); $img = preg_replace( '|]+) />|', '', $img ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); // The content filter should return the image unchanged. $this->assertSame( $img, wp_filter_content_tags( $img ) ); @@ -2343,6 +2348,7 @@ public function test_wp_calculate_image_srcset_animated_gifs() { * @requires function imagejpeg */ public function test_wp_filter_content_tags_schemes() { + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $image_meta = wp_get_attachment_metadata( self::$large_id ); $size_array = $this->get_image_size_array_from_meta( $image_meta, 'medium' ); @@ -2387,6 +2393,7 @@ public function test_wp_filter_content_tags_schemes() { $actual = wp_filter_content_tags( $unfiltered ); $this->assertSame( $expected, $actual ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -2804,6 +2811,8 @@ public function test_media_handle_upload_ignores_page_parent_for_directory_date( * @requires function imagejpeg */ public function test_wp_filter_content_tags_width_height() { + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); + $image_meta = wp_get_attachment_metadata( self::$large_id ); $size_array = $this->get_image_size_array_from_meta( $image_meta, 'medium' ); @@ -2837,11 +2846,13 @@ public function test_wp_filter_content_tags_width_height() { // Do not add loading, srcset, and sizes. add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -2895,11 +2906,13 @@ public function test_wp_filter_content_tags_loading_lazy() { // Do not add width, height, srcset, and sizes. add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -2927,9 +2940,13 @@ public function test_wp_filter_content_tags_loading_lazy_opted_in() { // Enable globally for all tags. add_filter( 'wp_lazy_loading_enabled', '__return_true' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); + $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_lazy_loading_enabled', '__return_true' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); + } /** @@ -2953,9 +2970,12 @@ public function test_wp_filter_content_tags_loading_lazy_opted_out() { // Disable globally for all tags. add_filter( 'wp_lazy_loading_enabled', '__return_false' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); + $this->assertSame( $content, wp_filter_content_tags( $content ) ); remove_filter( 'wp_lazy_loading_enabled', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -3381,6 +3401,7 @@ function() { */ function test_wp_filter_content_tags_with_wp_get_loading_attr_default() { global $wp_query, $wp_the_query; + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $img1 = get_image_tag( self::$large_id, '', '', '', 'large' ); $iframe1 = ''; @@ -3391,6 +3412,7 @@ function test_wp_filter_content_tags_with_wp_get_loading_attr_default() { $lazy_img3 = wp_img_tag_add_loading_attr( $img3, 'the_content' ); $lazy_iframe2 = wp_iframe_tag_add_loading_attr( $iframe2, 'the_content' ); + // Use a threshold of 2. add_filter( 'wp_omit_loading_attr_threshold', @@ -3415,6 +3437,7 @@ function() { $content_filtered = wp_filter_content_tags( $content_unfiltered, 'the_content' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); } + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); // After filtering, the first image should not be lazy-loaded while the other ones should be. $this->assertSame( $content_expected, $content_filtered ); From f2cacab93130c076bf7619ed3fd243d505e5ed00 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 10 Mar 2022 07:38:13 -0700 Subject: [PATCH 022/102] Test cleanup --- tests/phpunit/tests/image/functions.php | 2 +- tests/phpunit/tests/media.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index f8e68ba38ae6c..664af75b809b1 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -592,7 +592,7 @@ public function test_crop_setting_for_pdf() { 'height' => 300, 'mime-type' => 'image/jpeg', 'sources' => array( - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), 'image/jpeg' => array( 'file' => 'wordpress-gsoc-flyer-pdf-300x300.jpg', ), diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index e4ce4c7eb0940..85b7f8d8e5cd3 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -3412,7 +3412,6 @@ function test_wp_filter_content_tags_with_wp_get_loading_attr_default() { $lazy_img3 = wp_img_tag_add_loading_attr( $img3, 'the_content' ); $lazy_iframe2 = wp_iframe_tag_add_loading_attr( $iframe2, 'the_content' ); - // Use a threshold of 2. add_filter( 'wp_omit_loading_attr_threshold', From 69607047ef896a594ae5d0f0e75bc8cccff01cfc Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 10 Mar 2022 07:45:07 -0700 Subject: [PATCH 023/102] Switch all filesize calls to wp_filesize --- src/wp-admin/includes/image.php | 6 ++-- tests/phpunit/tests/image/functions.php | 47 +++++++++---------------- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index a8b10bfba21cb..2fe5fb1e6c87e 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -369,7 +369,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( 0 === $mime_index ) { $image_meta['sources'][ $output_mime_type ] = array( 'file' => wp_basename( $image_meta['file'] ), - 'filesize' => filesize( $file ), + 'filesize' => wp_filesize( $file ), ); } else { // For alternate mime types, generate a full size image and add it to the 'sources' array. @@ -419,7 +419,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { function _wp_get_sources_from_meta( $meta ) { return array( 'file' => wp_basename( $meta['file'] ), - 'filesize' => filesize( $meta['path'] ), + 'filesize' => wp_filesize( $meta['path'] ), ); } @@ -543,7 +543,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { ); $file_location = path_join( $dirname, $new_size_meta['file'] ); if ( file_exists( $file_location ) ) { - $source_meta['filesize'] = filesize( $file_location ); + $source_meta['filesize'] = wp_filesize( $file_location ); } $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = $source_meta; wp_update_attachment_metadata( $attachment_id, $image_meta ); diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index 664af75b809b1..e74db6647382d 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -480,7 +480,6 @@ public function test_wp_generate_attachment_metadata_pdf() { $this->assertNotEmpty( $attachment_id ); $temp_dir = get_temp_dir(); - $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); $expected = array( @@ -500,7 +499,9 @@ public function test_wp_generate_attachment_metadata_pdf() { 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ), 'sources' => array( 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-232x300.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-232x300.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ), + ), ), ), @@ -512,7 +513,8 @@ public function test_wp_generate_attachment_metadata_pdf() { 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), 'sources' => array( 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), ), ), ), @@ -524,7 +526,8 @@ public function test_wp_generate_attachment_metadata_pdf() { 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), 'sources' => array( 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), ), ), ), @@ -532,8 +535,6 @@ public function test_wp_generate_attachment_metadata_pdf() { 'filesize' => wp_filesize( $test_file ), ); - $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); - $metadata = $this->_unset_source_filesize_data( $metadata ); $this->assertSame( $expected, $metadata ); unlink( $test_file ); @@ -574,7 +575,6 @@ public function test_crop_setting_for_pdf() { $this->assertNotEmpty( $attachment_id ); $temp_dir = get_temp_dir(); - $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); $expected = array( @@ -594,7 +594,8 @@ public function test_crop_setting_for_pdf() { 'sources' => array( 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-300x300.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-300x300.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), ), ), ), @@ -606,7 +607,8 @@ public function test_crop_setting_for_pdf() { 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), 'sources' => array( 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), ), ), @@ -619,7 +621,8 @@ public function test_crop_setting_for_pdf() { 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), 'sources' => array( 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), ), ), ), @@ -627,8 +630,6 @@ public function test_crop_setting_for_pdf() { 'filesize' => wp_filesize( $test_file ), ); - $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); - $metadata = $this->_unset_source_filesize_data( $metadata ); $this->assertSame( $expected, $metadata ); unlink( $test_file ); @@ -637,23 +638,6 @@ public function test_crop_setting_for_pdf() { } } - /** - * Unset the sources filesize for each image size for tests since the filesize - * value will vary depending on the compression characteristics of the - * underlying image library. - * - * @param array $metadata The metadata to clear sources/filesize from. - * @param array The cleaned up meta data. - */ - function _unset_source_filesize_data( $metadata ) { - foreach ( $metadata['sizes'] as $size => $data ) { - if ( isset( $metadata['sizes'][ $size ]['sources']['image/jpeg']['filesize'] ) ) { - unset( $metadata['sizes'][ $size ]['sources']['image/jpeg']['filesize'] ); - } - } - return $metadata; - } - /** * @ticket 39231 */ @@ -694,9 +678,11 @@ public function test_fallback_intermediate_image_sizes() { 'height' => 100, 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), 'mime-type' => 'image/jpeg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), 'sources' => array( 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-77x100.jpg', + 'file' => 'wordpress-gsoc-flyer-pdf-77x100.jpg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), ), ), ); @@ -705,7 +691,6 @@ public function test_fallback_intermediate_image_sizes() { $this->assertSame( $metadata['sizes']['test-size'], $expected ); $this->assertArrayHasKey( 'test-size', $metadata['sizes'], 'The `test-size` was not added to the metadata.' ); - $metadata = $this->_unset_source_filesize_data( $metadata ); $this->assertSame( $expected, $metadata['sizes']['test-size'] ); remove_image_size( 'test-size' ); From 3c85bc110fa5da3bdcaa0a3a228b15fa2f974318 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 10 Mar 2022 13:00:11 -0700 Subject: [PATCH 024/102] Use root meta filesize when available vs. recalculating --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 2fe5fb1e6c87e..3316899b8d8b9 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -369,7 +369,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( 0 === $mime_index ) { $image_meta['sources'][ $output_mime_type ] = array( 'file' => wp_basename( $image_meta['file'] ), - 'filesize' => wp_filesize( $file ), + 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), ); } else { // For alternate mime types, generate a full size image and add it to the 'sources' array. From f38374961500a12784ece13ff68fc6969e9a4f32 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 10 Mar 2022 13:23:43 -0700 Subject: [PATCH 025/102] Fix test data --- tests/phpunit/tests/image/functions.php | 5 ++--- tests/phpunit/tests/media.php | 9 +++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index e74db6647382d..39bf09cf0cf6d 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -591,8 +591,8 @@ public function test_crop_setting_for_pdf() { 'width' => 300, 'height' => 300, 'mime-type' => 'image/jpeg', + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), 'sources' => array( - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), 'image/jpeg' => array( 'file' => 'wordpress-gsoc-flyer-pdf-300x300.jpg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), @@ -676,9 +676,8 @@ public function test_fallback_intermediate_image_sizes() { 'file' => 'wordpress-gsoc-flyer-pdf-77x100.jpg', 'width' => 77, 'height' => 100, - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), 'mime-type' => 'image/jpeg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), + 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), 'sources' => array( 'image/jpeg' => array( 'file' => 'wordpress-gsoc-flyer-pdf-77x100.jpg', diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 85b7f8d8e5cd3..a1fcd90a3dab9 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -2288,14 +2288,15 @@ public function test_wp_filter_content_tags_srcset_sizes_wrong() { */ public function test_wp_filter_content_tags_srcset_sizes_with_preexisting_srcset() { // Generate HTML and add a dummy srcset attribute. - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $img = get_image_tag( self::$large_id, '', '', '', 'medium' ); $img = wp_img_tag_add_loading_attr( $img, 'test' ); $img = preg_replace( '|]+) />|', '', $img ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); // The content filter should return the image unchanged. $this->assertSame( $img, wp_filter_content_tags( $img ) ); + + remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -2536,7 +2537,11 @@ public function test_wp_get_attachment_image_should_use_wp_get_attachment_metada 'srcset="' . $uploads_url . 'test-image-testsize-999x999.jpg 999w, ' . $uploads_url . $basename . '-150x150.jpg 150w" ' . 'sizes="(max-width: 999px) 100vw, 999px" />'; + error_log( 'self::$large_id --- ' . json_encode( self::$large_id, JSON_PRETTY_PRINT ) ); + error_log( 'wp_get_attachment_metadata() --- ' . json_encode( wp_get_attachment_metadata( self::$large_id ), JSON_PRETTY_PRINT ) ); + $actual = wp_get_attachment_image( self::$large_id, 'testsize' ); + error_log( 'actual --- ' . json_encode( $actual, JSON_PRETTY_PRINT ) ); remove_filter( 'wp_get_attachment_metadata', array( $this, 'filter_36246' ) ); From cba3b43cf27c786ee1846eda64681068563156a4 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 10 Mar 2022 15:27:17 -0700 Subject: [PATCH 026/102] Cleanup --- src/wp-includes/media.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 814faf726fa1a..17553bb47a3b3 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1898,6 +1898,7 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { * @return array The filtered output mime type and order. Return an empty array to skip mime type substitution. */ $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes, $attachment_id ); + if ( false === $target_mimes ) { return $image; } From 3486a08919f7a3cbd1788a3e25475d82d3149d0e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 14 Mar 2022 16:32:43 -0600 Subject: [PATCH 027/102] Remove mime support checks, handled downstream already --- src/wp-admin/includes/image.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 3316899b8d8b9..c3655b44ec893 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -291,12 +291,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { return $image_meta; } - // This mime type is not supported, skip it unless it is the only mime type, in which case - // core will fall back to a supported type. - if ( ! $editor->supports_mime_type( $output_mime_type ) && 1 < sizeof( $output_mime_types ) ) { - continue; - } - // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { @@ -509,12 +503,6 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - // This mime type is not supported, skip it unless it is the only mime type, in which case - // core will fall back to a supported type. - if ( ! $editor->supports_mime_type( $output_mime_type ) && 1 < sizeof( $output_mime_types ) ) { - continue; - } - // If stored EXIF data exists, rotate the source image before creating sub-sizes. if ( ! empty( $image_meta['image_meta'] ) ) { $rotated = $editor->maybe_exif_rotate(); From 0afad05a69d7babec060333fb0c10ce0481f1e6c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 14 Mar 2022 16:33:00 -0600 Subject: [PATCH 028/102] Improve doc blocks, tests, cleanup --- src/wp-admin/includes/image.php | 12 ++++++------ src/wp-includes/media.php | 7 ++++--- tests/phpunit/tests/media.php | 4 ---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index c3655b44ec893..540853887d740 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -451,7 +451,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { return $image_meta; } - // Assemble the output mime types + // Determine which mime types to output. $valid_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); $dirname = pathinfo( $file, PATHINFO_DIRNAME ); @@ -519,7 +519,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { - // Save the default (first) mime size in the 'sizes'meta value. + // Save the default mime size in the 'sizes' meta value. if ( 0 === $mime_index ) { $image_meta['sizes'][ $new_size_name ] = $new_size_meta; } @@ -1225,8 +1225,9 @@ function _copy_image_file( $attachment_id ) { } /** - * Returns an array with the list of valid mime types that a specific mime type can be converted into it, - * for example an image/jpeg can be converted into an image/webp. + * Returns an array with the list of valid mime types that a specific mime type should be converted into. + * For example an `image/jpeg` should be converted into an `image/jpeg` and `image/webp`. The first type + * is considered the primary output type for this image. * * @since 6.0.0 * @@ -1241,8 +1242,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { ); /** - * Filter to allow the definition of a custom mime types, in which a defined mime type - * can be transformed and provide a wide range of mime types. + * Filter to the output mime types for a given input mime type. * * @since 6.0.0 * diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 17553bb47a3b3..a78cb2f4a3d3d 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1893,7 +1893,7 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { * * @since 6.0.0 * - * @param array $target_mimes The image output mime type and order. Default is ( 'image/webp', 'image/jpeg' ). + * @param array $target_mimes The image output mime type and order. Default is array( 'image/webp', 'image/jpeg' ). * @param int $attachment_id The attachment ID. * @return array The filtered output mime type and order. Return an empty array to skip mime type substitution. */ @@ -1926,12 +1926,13 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { // Found a match, replace with the new filename and stop searching. $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); + continue; } - // Handle full size image replacement + // Handle full size image replacement. $src_filename = wp_basename( $metadata['file'] ); - // This is the same as the file we want to replace nothing to do here. + // This is the same as the file we want to replace nothing else to do here. if ( $metadata['sources'][ $target_mime ]['file'] === $src_filename ) { return $image; } diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index a1fcd90a3dab9..2e9e2173bc88e 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -2537,11 +2537,7 @@ public function test_wp_get_attachment_image_should_use_wp_get_attachment_metada 'srcset="' . $uploads_url . 'test-image-testsize-999x999.jpg 999w, ' . $uploads_url . $basename . '-150x150.jpg 150w" ' . 'sizes="(max-width: 999px) 100vw, 999px" />'; - error_log( 'self::$large_id --- ' . json_encode( self::$large_id, JSON_PRETTY_PRINT ) ); - error_log( 'wp_get_attachment_metadata() --- ' . json_encode( wp_get_attachment_metadata( self::$large_id ), JSON_PRETTY_PRINT ) ); - $actual = wp_get_attachment_image( self::$large_id, 'testsize' ); - error_log( 'actual --- ' . json_encode( $actual, JSON_PRETTY_PRINT ) ); remove_filter( 'wp_get_attachment_metadata', array( $this, 'filter_36246' ) ); From 39c17eede06af4f00166704285269048dc031734 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 15 Mar 2022 16:22:02 -0600 Subject: [PATCH 029/102] Increase the maximum retry count from 4 to 8 * Should this be filterable? * Note: also need to increate retry count in Gutenberg --- src/js/_enqueues/vendor/plupload/wp-plupload.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/_enqueues/vendor/plupload/wp-plupload.js b/src/js/_enqueues/vendor/plupload/wp-plupload.js index 0fdebf77d1858..217b3c09e2f54 100644 --- a/src/js/_enqueues/vendor/plupload/wp-plupload.js +++ b/src/js/_enqueues/vendor/plupload/wp-plupload.js @@ -138,7 +138,7 @@ window.wp = window.wp || {}; times = tryAgainCount[ file.id ]; - if ( times && times > 4 ) { + if ( times && times > 8 ) { /* * The file may have been uploaded and attachment post created, * but post-processing and resizing failed... From 26ba1ee678779ff38b2e9fca26b7e967da347ff5 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 16 Mar 2022 16:46:07 -0600 Subject: [PATCH 030/102] Update src/wp-admin/includes/image.php Co-authored-by: Felix Arntz --- src/wp-admin/includes/image.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 540853887d740..60dabe5e8386a 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -405,7 +405,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } /** - * Get a sources array element from a meta. + * Gets a sources array element from a meta. + * + * @since 6.0.0 + * @access private * * @param array $meta The meta to get the source from. * @return array The source array element. From 92a26bb815480ec9355d9f57333cfcef440d7ddc Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 16 Mar 2022 16:48:48 -0600 Subject: [PATCH 031/102] Update src/wp-admin/includes/image.php Co-authored-by: Felix Arntz --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 60dabe5e8386a..af74e099bf609 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -425,9 +425,9 @@ function _wp_get_sources_from_meta( $meta ) { * * Updates the image meta after each sub-size is created. * Errors are stored in the returned image metadata array. - * Since 6.0.0 this function is capable of generating multiple mime type sub-sizes. * * @since 5.3.0 + * @since 6.0.0 Support for generating multiple mime type sub-sizes was added. * @access private * * @param array $new_sizes Array defining what sizes to create. From 9d7f8c8c55fa9ec4a44fcdf055b20f43b31fb29b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 18 Mar 2022 10:09:02 -0600 Subject: [PATCH 032/102] Double retry count in plupload/handlers.js --- src/js/_enqueues/vendor/plupload/handlers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/_enqueues/vendor/plupload/handlers.js b/src/js/_enqueues/vendor/plupload/handlers.js index fa602daf434ab..c7e3c253d3599 100644 --- a/src/js/_enqueues/vendor/plupload/handlers.js +++ b/src/js/_enqueues/vendor/plupload/handlers.js @@ -486,7 +486,7 @@ jQuery( document ).ready( function( $ ) { times = tryAgainCount[ file.id ]; - if ( times && times > 4 ) { + if ( times && times > 8 ) { /* * The file may have been uploaded and attachment post created, * but post-processing and resizing failed... From 018b3fbd682ff042b18f5478b38412811d53c1da Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 18 Mar 2022 10:44:18 -0600 Subject: [PATCH 033/102] Restore most of _wp_make_subsizes, add _wp_make_additional_mime_subsizes for additional sizes --- src/wp-admin/includes/image.php | 223 ++++++++++++++++++++++++-------- 1 file changed, 168 insertions(+), 55 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index af74e099bf609..f206e14185a6b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -416,7 +416,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { function _wp_get_sources_from_meta( $meta ) { return array( 'file' => wp_basename( $meta['file'] ), - 'filesize' => wp_filesize( $meta['path'] ), + 'filesize' => isset( $meta['filesize'] ) ? $meta['filesize'] : wp_filesize( $meta['path'] ), ); } @@ -442,25 +442,139 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { return array(); } - // Set up a top level sources array for the full size images. - if ( ! isset( $metadata['sources'] ) || ! is_array( $metadata['sources'] ) ) { - $metadata['sources'] = array(); + // Check if any of the new sizes already exist. + if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { + foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { + /* + * Only checks "size name" so we don't override existing images even if the dimensions + * don't match the currently defined size with the same name. + * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. + */ + if ( array_key_exists( $size_name, $new_sizes ) ) { + unset( $new_sizes[ $size_name ] ); + } + } + } else { + $image_meta['sizes'] = array(); } - $mime_type = get_post_mime_type( $attachment_id ); - $editor = wp_get_image_editor( $file ); + + if ( empty( $new_sizes ) ) { + // Nothing to do... + return $image_meta; + } + + // Calculate the primary and additional mime types to generate. + $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); + $mime_type = get_post_mime_type( $attachment_id ); + + extract( _wp_extract_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ) ); + + /* + * Sort the image sub-sizes in order of priority when creating them. + * This ensures there is an appropriate sub-size the user can access immediately + * even when there was an error and not all sub-sizes were created. + */ + $priority = array( + 'medium' => null, + 'large' => null, + 'thumbnail' => null, 'medium_large' => null, + ); + + $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); + + $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { // The image cannot be edited. return $image_meta; } - // Determine which mime types to output. - $valid_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); - $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); - $dirname = pathinfo( $file, PATHINFO_DIRNAME ); + // If stored EXIF data exists, rotate the source image before creating sub-sizes. + if ( ! empty( $image_meta['image_meta'] ) ) { + $rotated = $editor->maybe_exif_rotate(); + + if ( is_wp_error( $rotated ) ) { + // TODO: Log errors. + } + } + + if ( method_exists( $editor, 'make_subsize' ) ) { + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); + + if ( is_wp_error( $new_size_meta ) ) { + // TODO: Log errors. + } else { + // Save the size meta value. + $image_meta['sizes'][ $new_size_name ] = $new_size_meta; + error_log( 'new_size_meta --- ' . json_encode( $new_size_meta, JSON_PRETTY_PRINT)); + + $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } + // Set up a top level sources array for the full size images. + if ( ! isset( $metadata['sources'] ) || ! is_array( $metadata['sources'] ) ) { + $metadata['sources'] = array(); + } + + // Populate the primary mime type data. + $metadata['sources'][ $mime_type ] = array( + 'file' => wp_basename( $file ), + 'filesize' => isset( $metadata['filesize'] ) ? $metadata['filesize'] : wp_filesize( $file ), + ); + wp_update_attachment_metadata( $attachment_id, $metadata ); + + // Generate any additional mime types. + $image_meta = _wp_make_additional_mime_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_types ); + + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); + + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } + + + return $image_meta; +} + +/** + * Make additional mime type sub-sizes. + * + * @since 6.0.0 + * + * @access private + * + * @param array $new_sizes Array defining what sizes to create. + * @param string $file Full path to the image file. + * @param array $image_meta The attachment meta data array. + * @param int $attachment_id Attachment ID to process. + * @param array $additional_mime_types Additional mime types to generate. + * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. + * @return void + */ +function _wp_make_additional_mime_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_types ) { + if ( empty( $additional_mime_types ) ) { + // Nothing to do... + return $image_meta; + } + + // Start with the original image. + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) || ! method_exists( $editor, 'make_subsize' ) ) { + // The image cannot be edited. + return $image_meta; + } + + $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - // Generate all off of the output mime types. - foreach ( $output_mime_types as $mime_index => $output_mime_type ) { + // Generate all off of the additional (non-primary) output mime types. + foreach ( $additional_mime_types as $output_mime_type ) { // Check if any of the new sizes already exist for this mime type. if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { @@ -492,20 +606,6 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { return $image_meta; } - /* - * Sort the image sub-sizes in order of priority when creating them. - * This ensures there is an appropriate sub-size the user can access immediately - * even when there was an error and not all sub-sizes were created. - */ - $priority = array( - 'medium' => null, - 'large' => null, - 'thumbnail' => null, - 'medium_large' => null, - ); - - $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - // If stored EXIF data exists, rotate the source image before creating sub-sizes. if ( ! empty( $image_meta['image_meta'] ) ) { $rotated = $editor->maybe_exif_rotate(); @@ -515,40 +615,26 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } } - if ( method_exists( $editor, 'make_subsize' ) ) { - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); - if ( is_wp_error( $new_size_meta ) ) { - // TODO: Log errors. - } else { - // Save the default mime size in the 'sizes' meta value. - if ( 0 === $mime_index ) { - $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - } - - // Store the mime type specific sub-size in the 'sources' attribute. - $source_meta = array( - 'file' => $new_size_meta['file'], - 'filesize' => 0, - ); - $file_location = path_join( $dirname, $new_size_meta['file'] ); - if ( file_exists( $file_location ) ) { - $source_meta['filesize'] = wp_filesize( $file_location ); - } - $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = $source_meta; - wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( is_wp_error( $new_size_meta ) ) { + // TODO: Log errors. + } else { + // Store the mime type specific sub-size in the 'sources' attribute. + $source_meta = array( + 'file' => $new_size_meta['file'], + 'filesize' => 0, + ); + $file_location = path_join( $dirname, $new_size_meta['file'] ); + if ( file_exists( $file_location ) ) { + $source_meta['filesize'] = wp_filesize( $file_location ); } - } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); - - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = $source_meta; wp_update_attachment_metadata( $attachment_id, $image_meta ); } } + } return $image_meta; } @@ -1254,3 +1340,30 @@ function wp_upload_image_mime_transforms( $attachment_id ) { */ return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id ); } + +/** + * Extract the primary and additional mime output types for an image from the $image_mime_transforms. + * + * @since 6.0.0 + * + * @param $image_mime_transforms array> An array of valid mime types, where the key is the mime type and the value is the extension type. + * @param $attachment_id int The attachment ID. + * @param $mime_type string The mime type of the image. + */ +function _wp_extract_primary_and_additional_mime_types( $image_mime_transforms, $attachment_id, $mime_type ) { + $mime_type = get_post_mime_type( $attachment_id ); + $output_mime_types = isset( $image_mime_transforms[ $mime_type ] ) ? $image_mime_transforms[ $mime_type ] : array( $mime_type ); + + // Use original mime type as primary mime type, or alternatively the first one. + $primary_mime_type_key = array_search( $mime_type, $output_mime_types, true ); + if ( false === $primary_mime_type_key ) { + $primary_mime_type_key = 0; + } + // Split output mime types into primary mime type and additional mime types. + $additional_mime_types = $output_mime_types; + list( $primary_mime_type ) = array_splice( $additional_mime_types, $primary_mime_type_key, 1 ); + return array( + 'primary_mime_type' => $primary_mime_type, + 'additional_mime_types' => $additional_mime_types, + ); +} \ No newline at end of file From 9803604ec2b5ee4a7bf59665affa5d297021ea53 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 18 Mar 2022 13:51:11 -0600 Subject: [PATCH 034/102] Better integrate mime type loops into image creation --- src/wp-admin/includes/image.php | 298 ++++++++++++-------------------- 1 file changed, 109 insertions(+), 189 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index f206e14185a6b..8e7583be0a48b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -244,7 +244,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { 'file' => _wp_relative_upload_path( $file ), 'filesize' => wp_filesize( $file ), 'sizes' => array(), - 'sources' => array(), ); // Fetch additional metadata from EXIF/IPTC. @@ -253,11 +252,16 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( $exif_meta ) { $image_meta['image_meta'] = $exif_meta; } - $mime_type = $imagesize['mime']; + + // Calculate the primary and additional mime types to generate. + $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); + $mime_type = wp_get_image_mime( $file ); + $mime_extension_map = array_flip( wp_get_mime_types() ); + + extract( _wp_extract_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ) ); + // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. - if ( 'image/png' !== $mime_type ) { - $valid_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); - $output_mime_types = isset( $valid_mime_transforms[ $mime_type ] ) ? $valid_mime_transforms[ $mime_type ] : array( $mime_type ); + if ( 'image/png' !== $imagesize['mime'] ) { /** * Filters the "BIG image" threshold value. @@ -282,8 +286,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { */ $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - // Generate full sized images for all mime types. - foreach ( $output_mime_types as $mime_index => $output_mime_type ) { + // If the original image's dimensions are over the threshold, + // scale the image and use it as the "full" size. + if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { @@ -291,88 +296,92 @@ function wp_create_image_subsizes( $file, $attachment_id ) { return $image_meta; } - // If the original image's dimensions are over the threshold, - // scale the image and use it as the "full" size. - if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { - // Resize the image. - $resized = $editor->resize( $threshold, $threshold ); - $rotated = null; - - // If there is EXIF data, rotate according to EXIF Orientation. - if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { - $resized = $editor->maybe_exif_rotate(); - $rotated = $resized; - } + // Resize the image. + $resized = $editor->resize( $threshold, $threshold ); + $rotated = null; - if ( ! is_wp_error( $resized ) ) { - // For the default mime type scaled image is named "my_image-scaled.jpg". - if ( 0 === $mime_index ) { - // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". - // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); - - if ( ! is_wp_error( $saved ) && ! empty( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - - // If the image was rotated update the stored EXIF data. - if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - } else { - // TODO: Log errors. - } - } else { - // Additional mime types are named "my_image-{mime-extension}-scaled" - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) . '-scaled' ), $output_mime_type ); - if ( ! is_wp_error( $saved && ! empty( $saved ) ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - } + // If there is EXIF data, rotate according to EXIF Orientation. + if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { + $resized = $editor->maybe_exif_rotate(); + $rotated = $resized; + } + + if ( ! is_wp_error( $resized ) ) { + // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". + // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). + $saved = $editor->save( $editor->generate_filename( 'scaled' ), $primary_mime_type ); + + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + + $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $saved ); + + // If the image was rotated update the stored EXIF data. + if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; } } else { // TODO: Log errors. } - } else { - if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - // Rotate the image. - $rotated = $editor->maybe_exif_rotate(); - - // For the default mime type rotated image is named "my_image-rotated.jpg". - if ( true === $rotated && 0 === $mime_index ) { - // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); - } else { - // For alternate mimes, append -{mime-extension}-rotated to the image file name. - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) . '-rotated' ), $output_mime_type ); + + // Save additional mime types. + foreach ( $additional_mime_types as $output_mime_type ) { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + continue; } - if ( ! is_wp_error( $saved && ! empty( $saved ) ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + $saved = $editor->save( $editor->generate_filename( 'scaled', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - // Update the stored EXIF data. - if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - } else { - // TODO: Log errors. + } + } + } else { + // TODO: Log errors. + } + } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. + + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + + // Rotate the image. + $rotated = $editor->maybe_exif_rotate(); + + if ( true === $rotated ) { + // Append `-rotated` to the image file name. + $saved = $editor->save( $editor->generate_filename( 'rotated' ), $primary_mime_type ); + + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + + $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $saved ); + + // Update the stored EXIF data. + if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; } } else { - // For the default mime type, only add the file data to the 'sources' array. - if ( 0 === $mime_index ) { - $image_meta['sources'][ $output_mime_type ] = array( - 'file' => wp_basename( $image_meta['file'] ), - 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), - ); - } else { - // For alternate mime types, generate a full size image and add it to the 'sources' array. - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) ), $output_mime_type ); - if ( ! is_wp_error( $saved ) && ! empty( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - } else { - // @TODO Log errors - } + // TODO: Log errors. + } + // Save additional mime types. + foreach ( $additional_mime_types as $output_mime_type ) { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + continue; + } + + $saved = $editor->save( $editor->generate_filename( 'rotated', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); } } } @@ -465,7 +474,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Calculate the primary and additional mime types to generate. $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); - $mime_type = get_post_mime_type( $attachment_id ); + $mime_type = wp_get_image_mime( $file ); extract( _wp_extract_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ) ); @@ -477,7 +486,8 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $priority = array( 'medium' => null, 'large' => null, - 'thumbnail' => null, 'medium_large' => null, + 'thumbnail' => null, + 'medium_large' => null, ); $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); @@ -501,33 +511,41 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( method_exists( $editor, 'make_subsize' ) ) { foreach ( $new_sizes as $new_size_name => $new_size_data ) { $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); - + if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { + $image_meta['sizes'][ $new_size_name ]['sources'] = array(); + } if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { // Save the size meta value. $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - error_log( 'new_size_meta --- ' . json_encode( $new_size_meta, JSON_PRETTY_PRINT)); $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } + + // Save additional mime types for this size. + foreach ( $additional_mime_types as $output_mime_type ) { + $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); + + if ( ! is_wp_error( $new_size_meta ) ) { + $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } } // Set up a top level sources array for the full size images. - if ( ! isset( $metadata['sources'] ) || ! is_array( $metadata['sources'] ) ) { - $metadata['sources'] = array(); + if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { + $image_meta['sources'] = array(); } // Populate the primary mime type data. - $metadata['sources'][ $mime_type ] = array( + $image_meta['sources'][ $mime_type ] = array( 'file' => wp_basename( $file ), - 'filesize' => isset( $metadata['filesize'] ) ? $metadata['filesize'] : wp_filesize( $file ), + 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), ); wp_update_attachment_metadata( $attachment_id, $metadata ); - // Generate any additional mime types. - $image_meta = _wp_make_additional_mime_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_types ); - } else { // Fall back to `$editor->multi_resize()`. $created_sizes = $editor->multi_resize( $new_sizes ); @@ -538,104 +556,6 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } } - - return $image_meta; -} - -/** - * Make additional mime type sub-sizes. - * - * @since 6.0.0 - * - * @access private - * - * @param array $new_sizes Array defining what sizes to create. - * @param string $file Full path to the image file. - * @param array $image_meta The attachment meta data array. - * @param int $attachment_id Attachment ID to process. - * @param array $additional_mime_types Additional mime types to generate. - * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. - * @return void - */ -function _wp_make_additional_mime_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_types ) { - if ( empty( $additional_mime_types ) ) { - // Nothing to do... - return $image_meta; - } - - // Start with the original image. - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) || ! method_exists( $editor, 'make_subsize' ) ) { - // The image cannot be edited. - return $image_meta; - } - - $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - - // Generate all off of the additional (non-primary) output mime types. - foreach ( $additional_mime_types as $output_mime_type ) { - - // Check if any of the new sizes already exist for this mime type. - if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { - foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - if ( ! isset( $size_meta['sources'] ) ) { - $size_meta['sources'] = array(); - } - - if ( ! isset( $new_sizes['sources'][ $output_mime_type ] ) ) { - $new_sizes['sources'][ $output_mime_type ] = array(); - continue; - } - - /* - * Only checks "size name" so we don't override existing images even if the dimensions - * don't match the currently defined size with the same name. - * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. - */ - if ( array_key_exists( $size_name, $new_sizes['sources'][ $output_mime_type ] ) ) { - unset( $new_sizes[ $size_name ] ); - } - } - } else { - $image_meta['sizes'] = array(); - } - - if ( empty( $new_sizes ) ) { - // Nothing to do... - return $image_meta; - } - - // If stored EXIF data exists, rotate the source image before creating sub-sizes. - if ( ! empty( $image_meta['image_meta'] ) ) { - $rotated = $editor->maybe_exif_rotate(); - - if ( is_wp_error( $rotated ) ) { - // TODO: Log errors. - } - } - - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); - - if ( is_wp_error( $new_size_meta ) ) { - // TODO: Log errors. - } else { - // Store the mime type specific sub-size in the 'sources' attribute. - $source_meta = array( - 'file' => $new_size_meta['file'], - 'filesize' => 0, - ); - $file_location = path_join( $dirname, $new_size_meta['file'] ); - if ( file_exists( $file_location ) ) { - $source_meta['filesize'] = wp_filesize( $file_location ); - } - $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = $source_meta; - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - } - - } return $image_meta; } @@ -1366,4 +1286,4 @@ function _wp_extract_primary_and_additional_mime_types( $image_mime_transforms, 'primary_mime_type' => $primary_mime_type, 'additional_mime_types' => $additional_mime_types, ); -} \ No newline at end of file +} From 2f7f383acc9266fbcdb48a502718870b1505813f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sat, 19 Mar 2022 18:04:41 -0600 Subject: [PATCH 035/102] Save meta data during intiial large -scaled/-rotated image creation --- src/wp-admin/includes/image.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 8e7583be0a48b..cc135863479df 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -320,6 +320,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { $image_meta['image_meta']['orientation'] = 1; } + wp_update_attachment_metadata( $attachment_id, $image_meta ); } else { // TODO: Log errors. } @@ -336,6 +337,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $saved = $editor->save( $editor->generate_filename( 'scaled', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); } } } else { @@ -367,6 +369,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { $image_meta['image_meta']['orientation'] = 1; } + wp_update_attachment_metadata( $attachment_id, $image_meta ); } else { // TODO: Log errors. } @@ -382,6 +385,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $saved = $editor->save( $editor->generate_filename( 'rotated', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); } } } From 1b11a8008788127da5b2f1f8c69e8322dd2dfd85 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sat, 19 Mar 2022 18:20:18 -0600 Subject: [PATCH 036/102] Remove PDF mapping --- src/wp-admin/includes/image.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index cc135863479df..92413110b8e51 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1251,7 +1251,6 @@ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), 'image/webp' => array( 'image/webp', 'image/jpeg' ), - 'application/pdf' => array( 'image/jpeg' ), ); /** From 3a0f707689cab264de02adb553b5322717015af4 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sat, 19 Mar 2022 18:20:40 -0600 Subject: [PATCH 037/102] refactor deletion code into main loop, exclude main mime type --- src/wp-admin/includes/image.php | 16 ++++++++------ src/wp-includes/post.php | 39 +++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 92413110b8e51..17f56f567131a 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -257,8 +257,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); $mime_extension_map = array_flip( wp_get_mime_types() ); - - extract( _wp_extract_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ) ); + $mimes_to_generate = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); + $primary_mime_type = isset( $mimes_to_generate['primary_mime_type'] ) ? $mimes_to_generate['primary_mime_type'] : array(); + $additional_mime_types = isset( $mimes_to_generate['additional_mime_types'] ) ? $mimes_to_generate['additional_mime_types'] : array(); // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $imagesize['mime'] ) { @@ -479,8 +480,9 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Calculate the primary and additional mime types to generate. $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); - - extract( _wp_extract_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ) ); + $mimes_to_generate = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); + $primary_mime_type = isset( $mimes_to_generate['primary_mime_type'] ) ? $mimes_to_generate['primary_mime_type'] : array(); + $additional_mime_types = isset( $mimes_to_generate['additional_mime_types'] ) ? $mimes_to_generate['additional_mime_types'] : array(); /* * Sort the image sub-sizes in order of priority when creating them. @@ -1249,8 +1251,8 @@ function _copy_image_file( $attachment_id ) { */ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( - 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), - 'image/webp' => array( 'image/webp', 'image/jpeg' ), + 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), + 'image/webp' => array( 'image/webp', 'image/jpeg' ), ); /** @@ -1273,7 +1275,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * @param $attachment_id int The attachment ID. * @param $mime_type string The mime type of the image. */ -function _wp_extract_primary_and_additional_mime_types( $image_mime_transforms, $attachment_id, $mime_type ) { +function _wp_get_primary_and_additional_mime_types( $image_mime_transforms, $attachment_id, $mime_type ) { $mime_type = get_post_mime_type( $attachment_id ); $output_mime_types = isset( $image_mime_transforms[ $mime_type ] ) ? $image_mime_transforms[ $mime_type ] : array( $mime_type ); diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index a9293cf6b06da..95f5050a9afb2 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6449,6 +6449,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $uploadpath = wp_get_upload_dir(); $deleted = true; + $mime_type = wp_get_image_mime( $file ); if ( ! empty( $meta['thumb'] ) ) { // Don't delete the thumb if another attachment uses it. @@ -6480,9 +6481,23 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $deleted = false; } } - // Check for alternate mime types in the sizeinfo['sources'] array to delete. + // Check for alternate size mime types in the sizeinfo['sources'] array to delete. if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { - $deleted = $deleted && _wp_delete_alternate_mime_sizes( $sizeinfo['sources'], $intermediate_dir, $file ); + foreach ( $sizeinfo['sources'] as $mime => $properties ) { + // Skip the default mime type. + if ( $mime_type === $mime ) { + continue; + } + + if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + continue; + } + + $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; + } + } } } } @@ -6505,7 +6520,23 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { // Check for alternate full size mime types in the root sources array to delete. if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { - $deleted = $deleted && _wp_delete_alternate_mime_sizes( $meta['sources'], $intermediate_dir, $file ); + if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { + foreach ( $meta['sources'] as $mime => $properties ) { + // Skip the default mime type. + if ( $mime_type === $mime ) { + continue; + } + + if ( empty( $properties['file'] ) ) { + continue; + } + + $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; + } + } + } } if ( is_array( $backup_sizes ) ) { @@ -6550,7 +6581,7 @@ function _wp_delete_alternate_mime_sizes( $sources, $intermediate_dir, $file ) { continue; } - if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + if ( empty( $properties['file'] ) ) { continue; } From 7c0a98f0954b16293d4fa1f098c920955796906e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 20 Mar 2022 09:05:23 -0600 Subject: [PATCH 038/102] Skip attachment id 0 --- src/wp-includes/media.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index a78cb2f4a3d3d..df5f4314a4672 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1844,7 +1844,9 @@ function wp_filter_content_tags( $content, $context = null ) { } // Use alternate mime types when specified and available. - $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $attachment_id ); + if ( $attachment_id > 0 ) { + $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $attachment_id ); + } if ( $filtered_image !== $match[0] ) { $content = str_replace( $match[0], $filtered_image, $content ); From 73bfa1203c2317378ae982ee42939a6bec1910c9 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 20 Mar 2022 09:32:14 -0600 Subject: [PATCH 039/102] Only transform jpeg images --- src/wp-includes/media.php | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index df5f4314a4672..2573c3937af78 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1886,6 +1886,20 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { return $image; } + // Only alter images with a `sources` attribute + if ( empty( $metadata['sources'] ) ) { + return $image; + }; + + // Only transform jpeg images. + $source_mimes = array( 'image/jpeg' ); + + // If the image primary mime isn't in the source mimes skip this image. + $primary_mime = wp_get_image_mime( $metadata['sources'][0]['file'] ); + if ( ! in_array( $primary_mime, $source_mimes, true ) ) { + return $image; + } + $target_mimes = array( 'image/webp', 'image/jpeg' ); /** @@ -1904,25 +1918,31 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { if ( false === $target_mimes ) { return $image; } + // Find the appropriate size for the provided URL in the first available mime type. foreach ( $target_mimes as $target_mime ) { if ( ! isset( $metadata['sources'][ $target_mime ] ) || empty( $metadata['sources'][ $target_mime ]['file'] ) ) { continue; } - // Handle sub-sized image replacement. + // Go through each image and replace with the first available mime type version. foreach ( $metadata['sizes'] as $name => $size_data ) { - // Not the size we are looking for. + // Check if size has a file. if ( empty( $size_data['file'] ) ) { continue; } + + // Check if size has a source in the desired mime type. if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) { continue; } + $target_file = $size_data['sources'][ $target_mime ]['file']; + + // Replace the existing output image for this size. $src_filename = wp_basename( $size_data['file'] ); // This is the same as the file we want to replace nothing to do here. - if ( $size_data['sources'][ $target_mime ]['file'] === $src_filename ) { + if ( $target_file === $src_filename ) { continue; } From c30d709764de524941178f896e3d1f314b047590 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Sun, 20 Mar 2022 09:39:11 -0600 Subject: [PATCH 040/102] Skip mime types that are not supported. --- src/wp-admin/includes/image.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 17f56f567131a..2018aaf9db339 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -335,6 +335,11 @@ function wp_create_image_subsizes( $file, $attachment_id ) { continue; } + if ( empty( $mime_extension_map[ $output_mime_type ] ) ) { + // Skip mime types that are not supported. + continue; + } + $saved = $editor->save( $editor->generate_filename( 'scaled', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); @@ -383,6 +388,11 @@ function wp_create_image_subsizes( $file, $attachment_id ) { continue; } + if ( empty( $mime_extension_map[ $output_mime_type ] ) ) { + // Skip mime types that are not supported. + continue; + } + $saved = $editor->save( $editor->generate_filename( 'rotated', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); @@ -550,7 +560,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { 'file' => wp_basename( $file ), 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), ); - wp_update_attachment_metadata( $attachment_id, $metadata ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); } else { // Fall back to `$editor->multi_resize()`. From 1cc2d7c6e050a82587b418ece0cde1d8f2c371b9 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 21 Mar 2022 08:12:57 -0600 Subject: [PATCH 041/102] Fix mime data contruction --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 2018aaf9db339..6cbe0ef6fd88e 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -545,7 +545,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); if ( ! is_wp_error( $new_size_meta ) ) { - $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); + $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } } From ec9c4f9bdde1845ce3ddf841ca2286f6fafa8ddc Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 21 Mar 2022 08:47:49 -0600 Subject: [PATCH 042/102] Ensure presence a top level sources array for the full size images --- src/wp-admin/includes/image.php | 45 +++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 6cbe0ef6fd88e..493dc993aec33 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -401,6 +401,37 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } } + // Ensure presence a top level sources array for the full size images. + if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { + $image_meta['sources'] = array(); + } + + // Populate the top level primary mime type data. + if ( empty( $image_meta['sources'][ $primary_mime_type ] ) ) { + $image_meta['sources'][ $primary_mime_type ] = array( + 'file' => wp_basename( $file ), + 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), + ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + + // Populate the top level additional mime type data. + foreach ( $additional_mime_types as $output_mime_type ) { + if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { + $saved = $editor->save( $editor->generate_filename( '', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } + } } /* @@ -525,6 +556,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } if ( method_exists( $editor, 'make_subsize' ) ) { + // Make the primarey mime type sub sized images foreach ( $new_sizes as $new_size_name => $new_size_data ) { $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { @@ -535,7 +567,6 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } else { // Save the size meta value. $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } @@ -550,18 +581,6 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } } } - // Set up a top level sources array for the full size images. - if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { - $image_meta['sources'] = array(); - } - - // Populate the primary mime type data. - $image_meta['sources'][ $mime_type ] = array( - 'file' => wp_basename( $file ), - 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), - ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } else { // Fall back to `$editor->multi_resize()`. $created_sizes = $editor->multi_resize( $new_sizes ); From de69a6cd4d5855684ff23abe1b060b3c21b345cd Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 21 Mar 2022 10:23:57 -0600 Subject: [PATCH 043/102] Clean up full size generation --- src/wp-admin/includes/image.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 493dc993aec33..1a403b393d61a 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -425,7 +425,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Populate the top level additional mime type data. foreach ( $additional_mime_types as $output_mime_type ) { if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { - $saved = $editor->save( $editor->generate_filename( '', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -559,14 +559,14 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Make the primarey mime type sub sized images foreach ( $new_sizes as $new_size_name => $new_size_data ) { $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); - if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { - $image_meta['sizes'][ $new_size_name ]['sources'] = array(); - } if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { // Save the size meta value. $image_meta['sizes'][ $new_size_name ] = $new_size_meta; + if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { + $image_meta['sizes'][ $new_size_name ]['sources'] = array(); + } $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } From 84d1e4ce871209ee6aa9947c4036d58ecfa0bbd2 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 21 Mar 2022 12:21:26 -0600 Subject: [PATCH 044/102] Remove sources check for now --- src/wp-includes/media.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 2573c3937af78..5a350bb858427 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1891,15 +1891,6 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { return $image; }; - // Only transform jpeg images. - $source_mimes = array( 'image/jpeg' ); - - // If the image primary mime isn't in the source mimes skip this image. - $primary_mime = wp_get_image_mime( $metadata['sources'][0]['file'] ); - if ( ! in_array( $primary_mime, $source_mimes, true ) ) { - return $image; - } - $target_mimes = array( 'image/webp', 'image/jpeg' ); /** From 1e9fdc8b2d397b1d5989536ed822a6df2b97caec Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 21 Mar 2022 12:58:28 -0600 Subject: [PATCH 045/102] =?UTF-8?q?Restore=20default=20=E2=80=98applicatio?= =?UTF-8?q?n/pdf=E2=80=99=20mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wp-admin/includes/image.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 1a403b393d61a..073bea9644882 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -379,6 +379,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } else { // TODO: Log errors. } + // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { $editor = wp_get_image_editor( $file ); @@ -1280,8 +1281,9 @@ function _copy_image_file( $attachment_id ) { */ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( - 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), - 'image/webp' => array( 'image/webp', 'image/jpeg' ), + 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), + 'image/webp' => array( 'image/webp', 'image/jpeg' ), + 'application/pdf' => array( 'image/jpeg' ), ); /** From a22dc141297b79d2c407e6745c89df7de9cbe1d8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 21 Mar 2022 13:18:49 -0600 Subject: [PATCH 046/102] When deleting, the first sources element is primary --- src/wp-includes/post.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 95f5050a9afb2..62b44577afc0a 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6449,7 +6449,6 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $uploadpath = wp_get_upload_dir(); $deleted = true; - $mime_type = wp_get_image_mime( $file ); if ( ! empty( $meta['thumb'] ) ) { // Don't delete the thumb if another attachment uses it. @@ -6482,10 +6481,11 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } // Check for alternate size mime types in the sizeinfo['sources'] array to delete. - if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { + if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && 1 < sizeof( $sizeinfo['sources'] ) ) { + $index = 0; foreach ( $sizeinfo['sources'] as $mime => $properties ) { - // Skip the default mime type. - if ( $mime_type === $mime ) { + // Skip the first mime type which was already deleted. + if ( 0 === $index++ ) { continue; } @@ -6521,9 +6521,10 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { // Check for alternate full size mime types in the root sources array to delete. if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { + $index = 0; foreach ( $meta['sources'] as $mime => $properties ) { - // Skip the default mime type. - if ( $mime_type === $mime ) { + // Skip the first (primary) large image which was already deleted. + if ( 0 === $index++ ) { continue; } @@ -6576,7 +6577,7 @@ function _wp_delete_alternate_mime_sizes( $sources, $intermediate_dir, $file ) { $deleted = true; $index = 0; foreach ( $sources as $mime => $properties ) { - // Skip the default mime type.] + // Skip the first (primary) large image which was already deleted. if ( 0 === $index++ ) { continue; } From d7168f680d234908827dfb3158f8d0e6ef0f7624 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 10:03:54 -0600 Subject: [PATCH 047/102] Update src/wp-admin/includes/image.php Co-authored-by: Felix Arntz --- src/wp-admin/includes/image.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 073bea9644882..a49115da3fd3d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -257,9 +257,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); $mime_extension_map = array_flip( wp_get_mime_types() ); - $mimes_to_generate = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); - $primary_mime_type = isset( $mimes_to_generate['primary_mime_type'] ) ? $mimes_to_generate['primary_mime_type'] : array(); - $additional_mime_types = isset( $mimes_to_generate['additional_mime_types'] ) ? $mimes_to_generate['additional_mime_types'] : array(); + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $imagesize['mime'] ) { From 2192ee2b2558f263ef275093b1b0776b44abad8b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 10:17:29 -0600 Subject: [PATCH 048/102] Update src/wp-admin/includes/image.php Co-authored-by: Felix Arntz --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index a49115da3fd3d..5ac61d51e7cac 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -555,7 +555,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } if ( method_exists( $editor, 'make_subsize' ) ) { - // Make the primarey mime type sub sized images + // Make the primary mime type sub sized images foreach ( $new_sizes as $new_size_name => $new_size_data ) { $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); if ( is_wp_error( $new_size_meta ) ) { From 7c0de80666651b57ebb7215b6d6a75654e1c60ca Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 10:24:13 -0600 Subject: [PATCH 049/102] Update src/wp-admin/includes/image.php Co-authored-by: Felix Arntz --- src/wp-admin/includes/image.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 5ac61d51e7cac..0cc107d146ccd 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1299,6 +1299,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * Extract the primary and additional mime output types for an image from the $image_mime_transforms. * * @since 6.0.0 + * @access private * * @param $image_mime_transforms array> An array of valid mime types, where the key is the mime type and the value is the extension type. * @param $attachment_id int The attachment ID. From e781a0a99e220da0fc561f2baf59c1894f9672a8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 10:27:26 -0600 Subject: [PATCH 050/102] Update src/wp-includes/class-wp-image-editor-gd.php Co-authored-by: Felix Arntz --- src/wp-includes/class-wp-image-editor-gd.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index 56f80cf70ec86..eb878760be409 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -298,11 +298,7 @@ public function make_subsize( $size_data, $mime_type = null ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - if ( $mime_type ) { - $saved = $this->_save( $resized, null, $mime_type ); - } else { - $saved = $this->_save( $resized ); - } + $saved = $this->_save( $resized, null, $mime_type ); imagedestroy( $resized ); } From 06151d3b911a7fd1d13f8c2b25df19e355917453 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 10:28:19 -0600 Subject: [PATCH 051/102] Update src/wp-includes/class-wp-image-editor-imagick.php Co-authored-by: Felix Arntz --- src/wp-includes/class-wp-image-editor-imagick.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index e788e707af45c..4df33e2cb95dc 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -510,11 +510,7 @@ public function make_subsize( $size_data, $mime_type = null ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - if ( $mime_type ) { - $saved = $this->_save( $this->image, null, $mime_type ); - } else { - $saved = $this->_save( $this->image ); - } + $saved = $this->_save( $this->image, null, $mime_type ); $this->image->clear(); $this->image->destroy(); From 8c2412d44d989581a0c8c63185901ab4eb78ba5b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 12:49:05 -0600 Subject: [PATCH 052/102] Remove remnant _wp_delete_alternate_mime_sizes --- src/wp-includes/post.php | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 62b44577afc0a..2f422a2dfd303 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6563,37 +6563,6 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { return $deleted; } -/** - * Delete sources files from a directory, skipping default mime type. - * - * @since 6.0.0. - * - * @param array $sources The sources array to delete files from. - * @param string $intermediate_dir The directory to delete files from. - * @param string $file Absolute path to the attachment's file. - * @return bool Whether deletion succeeded. - */ -function _wp_delete_alternate_mime_sizes( $sources, $intermediate_dir, $file ) { - $deleted = true; - $index = 0; - foreach ( $sources as $mime => $properties ) { - // Skip the first (primary) large image which was already deleted. - if ( 0 === $index++ ) { - continue; - } - - if ( empty( $properties['file'] ) ) { - continue; - } - - $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } - } - return $deleted; -} - /** * Retrieves attachment metadata for attachment ID. * From 9855e8308aaa3215971406a9b6cafabc569d9e45 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 12:55:40 -0600 Subject: [PATCH 053/102] Remove editor reset in `wp_create_image_subsizes` --- src/wp-admin/includes/image.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 0cc107d146ccd..538038f3704fd 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -326,13 +326,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - continue; - } - if ( empty( $mime_extension_map[ $output_mime_type ] ) ) { // Skip mime types that are not supported. continue; From a8a0e562ada0f68c2aef5135941cc739a0ae1f41 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 12:58:41 -0600 Subject: [PATCH 054/102] Update src/wp-admin/includes/image.php Co-authored-by: Felix Arntz --- src/wp-admin/includes/image.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 538038f3704fd..9d8516e16e997 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -331,7 +331,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { continue; } - $saved = $editor->save( $editor->generate_filename( 'scaled', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); + $extension = explode( '|', $mime_extension_map[ $output_mime_type ] )[0]; + $saved = $editor->save( $editor->generate_filename( 'scaled', null, $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); From 6ef7092473a7959d18a9c5809eeb0ddb927ced27 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 13:39:33 -0600 Subject: [PATCH 055/102] Remove additional editor reset in `wp_create_image_subsizes` --- src/wp-admin/includes/image.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 9d8516e16e997..76117f0e491e4 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -374,13 +374,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - continue; - } - if ( empty( $mime_extension_map[ $output_mime_type ] ) ) { // Skip mime types that are not supported. continue; From f9e795418840531c4c058843bdee95665661adbb Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 14:00:06 -0600 Subject: [PATCH 056/102] Ensure primary mime falls back to image mime type --- src/wp-admin/includes/image.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 76117f0e491e4..5087cba57ff3d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -258,6 +258,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $mime_type = wp_get_image_mime( $file ); $mime_extension_map = array_flip( wp_get_mime_types() ); list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); + if ( empty( $primary_mime_type ) ) { + $primary_mime_type = $mime_type; + } // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $imagesize['mime'] ) { @@ -508,9 +511,10 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); $mimes_to_generate = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); - $primary_mime_type = isset( $mimes_to_generate['primary_mime_type'] ) ? $mimes_to_generate['primary_mime_type'] : array(); - $additional_mime_types = isset( $mimes_to_generate['additional_mime_types'] ) ? $mimes_to_generate['additional_mime_types'] : array(); - + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); + if ( empty( $primary_mime_type ) ) { + $primary_mime_type = $mime_type; + } /* * Sort the image sub-sizes in order of priority when creating them. * This ensures there is an appropriate sub-size the user can access immediately From 0337b61e738af66b593b7098546e5ab2a45a8b7e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 15:18:59 -0600 Subject: [PATCH 057/102] Refactor extension usage --- src/wp-admin/includes/image.php | 77 ++++++++++++++------------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 5087cba57ff3d..4f4867a88bbc1 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -256,12 +256,25 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Calculate the primary and additional mime types to generate. $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); - $mime_extension_map = array_flip( wp_get_mime_types() ); list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); if ( empty( $primary_mime_type ) ) { $primary_mime_type = $mime_type; } + // Add a top level sources array for the full size images. + if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { + $image_meta['sources'] = array(); + } + + // Populate the top level primary mime type data. + if ( empty( $image_meta['sources'][ $primary_mime_type ] ) ) { + $image_meta['sources'][ $primary_mime_type ] = array( + 'file' => wp_basename( $file ), + 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), + ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $imagesize['mime'] ) { @@ -329,13 +342,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { - if ( empty( $mime_extension_map[ $output_mime_type ] ) ) { - // Skip mime types that are not supported. - continue; - } - - $extension = explode( '|', $mime_extension_map[ $output_mime_type ] )[0]; - $saved = $editor->save( $editor->generate_filename( 'scaled', null, $extension ), $output_mime_type ); + $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); + // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. + $saved = $editor->save( $editor->generate_filename( 'scaled', null, $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -377,47 +386,25 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { - if ( empty( $mime_extension_map[ $output_mime_type ] ) ) { - // Skip mime types that are not supported. - continue; - } - - $saved = $editor->save( $editor->generate_filename( 'rotated', null, $mime_extension_map[ $output_mime_type ] ), $output_mime_type ); + $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); + // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. + $saved = $editor->save( $editor->generate_filename( 'rotated', null, $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } } } - } - // Ensure presence a top level sources array for the full size images. - if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { - $image_meta['sources'] = array(); - } - - // Populate the top level primary mime type data. - if ( empty( $image_meta['sources'][ $primary_mime_type ] ) ) { - $image_meta['sources'][ $primary_mime_type ] = array( - 'file' => wp_basename( $file ), - 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), - ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - - // Populate the top level additional mime type data. - foreach ( $additional_mime_types as $output_mime_type ) { - if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { - $saved = $editor->save( $editor->generate_filename( str_replace( 'image/', '', $output_mime_type ) ), $output_mime_type ); - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + } else { + // Populate the top level additional mime type data. + foreach ( $additional_mime_types as $output_mime_type ) { + if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { + $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } } } } @@ -1309,7 +1296,7 @@ function _wp_get_primary_and_additional_mime_types( $image_mime_transforms, $att $additional_mime_types = $output_mime_types; list( $primary_mime_type ) = array_splice( $additional_mime_types, $primary_mime_type_key, 1 ); return array( - 'primary_mime_type' => $primary_mime_type, - 'additional_mime_types' => $additional_mime_types, + $primary_mime_type, + $additional_mime_types, ); } From 2d582d80bf64bcd1cbe63e11d20c89af1502f527 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 15:24:40 -0600 Subject: [PATCH 058/102] Use _wp_get_sources_from_meta for top level meta construction --- src/wp-admin/includes/image.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 4f4867a88bbc1..436d2d25ff39a 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -268,10 +268,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Populate the top level primary mime type data. if ( empty( $image_meta['sources'][ $primary_mime_type ] ) ) { - $image_meta['sources'][ $primary_mime_type ] = array( - 'file' => wp_basename( $file ), - 'filesize' => isset( $image_meta['filesize'] ) ? $image_meta['filesize'] : wp_filesize( $file ), - ); + $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $image_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } From d133ef14a5a072bbfe2efb8f74075d637bcaac3b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 15:46:50 -0600 Subject: [PATCH 059/102] Move empty check into _wp_get_primary_and_additional_mime_types and clean up mime type usage --- src/wp-admin/includes/image.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 436d2d25ff39a..7be2244771197 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -256,10 +256,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Calculate the primary and additional mime types to generate. $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); - if ( empty( $primary_mime_type ) ) { - $primary_mime_type = $mime_type; - } + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $mime_type ); // Add a top level sources array for the full size images. if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { @@ -494,11 +491,8 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Calculate the primary and additional mime types to generate. $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); $mime_type = wp_get_image_mime( $file ); - $mimes_to_generate = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $attachment_id, $mime_type ); - if ( empty( $primary_mime_type ) ) { - $primary_mime_type = $mime_type; - } + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $mime_type ); + /* * Sort the image sub-sizes in order of priority when creating them. * This ensures there is an appropriate sub-size the user can access immediately @@ -1277,21 +1271,25 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * @access private * * @param $image_mime_transforms array> An array of valid mime types, where the key is the mime type and the value is the extension type. - * @param $attachment_id int The attachment ID. - * @param $mime_type string The mime type of the image. + * @param $original_mime_type string The mime type of the image. + * @return array> An array with the primary mime type and the additional mime types. */ -function _wp_get_primary_and_additional_mime_types( $image_mime_transforms, $attachment_id, $mime_type ) { - $mime_type = get_post_mime_type( $attachment_id ); - $output_mime_types = isset( $image_mime_transforms[ $mime_type ] ) ? $image_mime_transforms[ $mime_type ] : array( $mime_type ); +function _wp_get_primary_and_additional_mime_types( $image_mime_transforms, $original_mime_type ) { + $output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type ); // Use original mime type as primary mime type, or alternatively the first one. - $primary_mime_type_key = array_search( $mime_type, $output_mime_types, true ); + $primary_mime_type_key = array_search( $original_mime_type, $output_mime_types, true ); if ( false === $primary_mime_type_key ) { $primary_mime_type_key = 0; } // Split output mime types into primary mime type and additional mime types. $additional_mime_types = $output_mime_types; list( $primary_mime_type ) = array_splice( $additional_mime_types, $primary_mime_type_key, 1 ); + + // Ensure $primary_mime_type is set. + if ( empty( $primary_mime_type ) ) { + $primary_mime_type = $original_mime_type; + } return array( $primary_mime_type, $additional_mime_types, From fccdca8e2df9542e1db84fd3b65e1e33b9359a13 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 15:47:04 -0600 Subject: [PATCH 060/102] Remove extraneous PDF mapping --- src/wp-admin/includes/image.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 7be2244771197..7c3e9748f2064 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1248,9 +1248,8 @@ function _copy_image_file( $attachment_id ) { */ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( - 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), - 'image/webp' => array( 'image/webp', 'image/jpeg' ), - 'application/pdf' => array( 'image/jpeg' ), + 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), + 'image/webp' => array( 'image/webp', 'image/jpeg' ), ); /** From e4580c0de72abfd1b96befb53e03665e993030dc Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 15:56:13 -0600 Subject: [PATCH 061/102] Add context to function call and filter (wp_content_image_mimes) --- src/wp-includes/media.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 5a350bb858427..cf9254dbdbc18 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1845,7 +1845,7 @@ function wp_filter_content_tags( $content, $context = null ) { // Use alternate mime types when specified and available. if ( $attachment_id > 0 ) { - $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $attachment_id ); + $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $context, $attachment_id ); } if ( $filtered_image !== $match[0] ) { @@ -1877,10 +1877,11 @@ function wp_filter_content_tags( $content, $context = null ) { * @since 6.0.0 * * @param string $image The HTML `img` tag where the attribute should be added. + * @param string $context Additional context to pass to the filters. * @param int $attachment_id The attachment ID. * @return string Converted `img` tag with `loading` attribute added. */ -function wp_image_use_alternate_mime_types( $image, $attachment_id ) { +function wp_image_use_alternate_mime_types( $image, $context, $attachment_id ) { $metadata = wp_get_attachment_metadata( $attachment_id ); if ( empty( $metadata['file'] ) ) { return $image; @@ -1900,11 +1901,12 @@ function wp_image_use_alternate_mime_types( $image, $attachment_id ) { * * @since 6.0.0 * - * @param array $target_mimes The image output mime type and order. Default is array( 'image/webp', 'image/jpeg' ). - * @param int $attachment_id The attachment ID. + * @param array $target_mimes The image output mime type and order. Default is array( 'image/webp', 'image/jpeg' ). + * @param int $attachment_id The attachment ID. + * @param string $context Additional context to pass to the filters. * @return array The filtered output mime type and order. Return an empty array to skip mime type substitution. */ - $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes, $attachment_id ); + $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes, $attachment_id, $context ); if ( false === $target_mimes ) { return $image; From f27e922046b280b78adc93c4c6426e042fa6694d Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 15:58:03 -0600 Subject: [PATCH 062/102] Use count & reverse conditional --- src/wp-includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 2f422a2dfd303..a19ed78d2799f 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6481,7 +6481,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } // Check for alternate size mime types in the sizeinfo['sources'] array to delete. - if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && 1 < sizeof( $sizeinfo['sources'] ) ) { + if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && count( $sizeinfo['sources'] ) > 1 ) { $index = 0; foreach ( $sizeinfo['sources'] as $mime => $properties ) { // Skip the first mime type which was already deleted. From a55164ae0d765c00d62ebb6029e43ef8b18087a9 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 16:16:24 -0600 Subject: [PATCH 063/102] Clean up deletion loops --- src/wp-includes/post.php | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index a19ed78d2799f..afcabf852dcb4 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6482,13 +6482,9 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } // Check for alternate size mime types in the sizeinfo['sources'] array to delete. if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && count( $sizeinfo['sources'] ) > 1 ) { - $index = 0; - foreach ( $sizeinfo['sources'] as $mime => $properties ) { - // Skip the first mime type which was already deleted. - if ( 0 === $index++ ) { - continue; - } - + $sources = $sizeinfo['sources']; + array_shift( $sources ); + foreach ( $sources as $mime => $properties ) { if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { continue; } @@ -6520,22 +6516,16 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { // Check for alternate full size mime types in the root sources array to delete. if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { - if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { - $index = 0; - foreach ( $meta['sources'] as $mime => $properties ) { - // Skip the first (primary) large image which was already deleted. - if ( 0 === $index++ ) { - continue; - } - - if ( empty( $properties['file'] ) ) { - continue; - } + $sources = $meta['sources']; + array_shift( $sources ); + foreach ( $sources as $mime => $properties ) { + if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + continue; + } - $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } + $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; } } } From b757e5a035544bfc1e35af8f9fe2dd985b238509 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 16:16:41 -0600 Subject: [PATCH 064/102] Always generate either -scaled or non scaled alternate mime FULL sized images --- src/wp-admin/includes/image.php | 84 +++++++++++++++++---------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 7c3e9748f2064..3cfef9bb3ff1f 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -347,57 +347,59 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } else { // TODO: Log errors. } - } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; + } else { + // Populate the top level additional mime type data. + foreach ( $additional_mime_types as $output_mime_type ) { + if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { + $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } } - // Rotate the image. - $rotated = $editor->maybe_exif_rotate(); + if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - if ( true === $rotated ) { - // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ), $primary_mime_type ); + $editor = wp_get_image_editor( $file ); - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } - $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $saved ); + // Rotate the image. + $rotated = $editor->maybe_exif_rotate(); - // Update the stored EXIF data. - if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } else { - // TODO: Log errors. - } + if ( true === $rotated ) { + // Append `-rotated` to the image file name. + $saved = $editor->save( $editor->generate_filename( 'rotated' ), $primary_mime_type ); - // Save additional mime types. - foreach ( $additional_mime_types as $output_mime_type ) { - $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. - $saved = $editor->save( $editor->generate_filename( 'rotated', null, $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + + $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $saved ); + + // Update the stored EXIF data. + if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } wp_update_attachment_metadata( $attachment_id, $image_meta ); + } else { + // TODO: Log errors. } - } - } - } else { - // Populate the top level additional mime type data. - foreach ( $additional_mime_types as $output_mime_type ) { - if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { - $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + + // Save additional mime types. + foreach ( $additional_mime_types as $output_mime_type ) { + $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); + // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. + $saved = $editor->save( $editor->generate_filename( 'rotated', null, $extension ), $output_mime_type ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } } } } From 3ba92547e2cf8a08ef24c3f6f8077dc833dfc111 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 22 Mar 2022 16:23:46 -0600 Subject: [PATCH 065/102] Use extension naming format for now --- src/wp-admin/includes/image.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 3cfef9bb3ff1f..ba9ef42712127 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -338,7 +338,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { foreach ( $additional_mime_types as $output_mime_type ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. - $saved = $editor->save( $editor->generate_filename( 'scaled', null, $extension ), $output_mime_type ); + // adding extension to name for now. + $saved = $editor->save( $editor->generate_filename( $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -395,7 +396,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { foreach ( $additional_mime_types as $output_mime_type ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. - $saved = $editor->save( $editor->generate_filename( 'rotated', null, $extension ), $output_mime_type ); + // adding extension to name for now. + $saved = $editor->save( $editor->generate_filename( $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); From 9c0937c8d578ab6ac5505e8dd88afc7e5c9d050e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2022 13:17:15 -0600 Subject: [PATCH 066/102] Extract mime type and upload mime transforms in _wp_get_primary_and_additional_mime_types drying out code --- src/wp-admin/includes/image.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index ba9ef42712127..b19c552ec7b8d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -254,9 +254,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } // Calculate the primary and additional mime types to generate. - $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); - $mime_type = wp_get_image_mime( $file ); - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $mime_type ); + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $file, $attachment_id ); // Add a top level sources array for the full size images. if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { @@ -493,9 +491,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } // Calculate the primary and additional mime types to generate. - $upload_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); - $mime_type = wp_get_image_mime( $file ); - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $upload_mime_transforms, $mime_type ); + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); /* * Sort the image sub-sizes in order of priority when creating them. @@ -1273,12 +1269,14 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * @since 6.0.0 * @access private * - * @param $image_mime_transforms array> An array of valid mime types, where the key is the mime type and the value is the extension type. - * @param $original_mime_type string The mime type of the image. + * @param string $file Full path to the image file. + * @param int $attachment_id Attachment ID to process. * @return array> An array with the primary mime type and the additional mime types. */ -function _wp_get_primary_and_additional_mime_types( $image_mime_transforms, $original_mime_type ) { - $output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type ); +function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) { + $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); + $original_mime_type = wp_get_image_mime( $file ); + $output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type ); // Use original mime type as primary mime type, or alternatively the first one. $primary_mime_type_key = array_search( $original_mime_type, $output_mime_types, true ); From 00dbbf9626dafe581940697bf6d75046ca569d10 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2022 13:41:08 -0600 Subject: [PATCH 067/102] Improve image editor integration * Image classes: add `set_mime_type` to base class and revert other changes * image processing set mime type appropriately before saving images --- src/wp-admin/includes/image.php | 66 +++++++++++-------- src/wp-includes/class-wp-image-editor-gd.php | 10 ++- .../class-wp-image-editor-imagick.php | 10 ++- src/wp-includes/class-wp-image-editor.php | 11 ++++ 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index b19c552ec7b8d..0968f0a02f737 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -316,7 +316,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ), $primary_mime_type ); + $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); if ( ! is_wp_error( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); @@ -334,6 +334,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. // adding extension to name for now. @@ -347,8 +348,16 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // TODO: Log errors. } } else { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + // Populate the top level additional mime type data. foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); @@ -358,16 +367,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } } + $editor->set_mime_type( $primary_mime_type ); + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } // Rotate the image. $rotated = $editor->maybe_exif_rotate(); @@ -392,10 +395,11 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. // adding extension to name for now. - $saved = $editor->save( $editor->generate_filename( $extension ), $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( $extension ) ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -522,11 +526,10 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // TODO: Log errors. } } - - if ( method_exists( $editor, 'make_subsize' ) ) { - // Make the primary mime type sub sized images - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + // Make the primary mime type sub sized images. + if ( method_exists( $editor, 'make_subsize' ) ) { + $new_size_meta = $editor->make_subsize( $new_size_data ); if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { @@ -538,24 +541,35 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); + + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } - // Save additional mime types for this size. - foreach ( $additional_mime_types as $output_mime_type ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); + // Save additional mime types sub sizes. + foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); + if ( method_exists( $editor, 'make_subsize' ) ) { + $new_size_meta = $editor->make_subsize( $new_size_data ); if ( ! is_wp_error( $new_size_meta ) ) { $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } - } - } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } } } diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index 4d7cc73f06bae..80e58f77cf996 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -247,7 +247,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data, null ); + $meta = $this->make_subsize( $size_data ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; @@ -261,20 +261,18 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 - * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } - * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data, $mime_type = null ) { + public function make_subsize( $size_data ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -298,7 +296,7 @@ public function make_subsize( $size_data, $mime_type = null ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $resized, null, $mime_type ); + $saved = $this->_save( $resized ); imagedestroy( $resized ); } diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 8ba376e192618..7301c9b710436 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -458,7 +458,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data, null ); + $meta = $this->make_subsize( $size_data ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; @@ -472,20 +472,18 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 - * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } - * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data, $mime_type = null ) { + public function make_subsize( $size_data ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -510,7 +508,7 @@ public function make_subsize( $size_data, $mime_type = null ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $this->image, null, $mime_type ); + $saved = $this->_save( $this->image ); $this->image->clear(); $this->image->destroy(); diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 823501eb46e8a..4e3480aa8300c 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -627,5 +627,16 @@ protected static function get_extension( $mime_type = null ) { return wp_get_default_extension_for_mime_type( $mime_type ); } + + /** + * Set the editor mime type, useful when outputting alternate mime types. + * + * @since 6.0.0 + * + * @param string $mime_type The mime type to set. + */ + public function set_mime_type( $mime_type ) { + $this->mime_type = $mime_type; + } } From c5fddab15065a97218f6ac2b697786b05babcdb9 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2022 14:07:54 -0600 Subject: [PATCH 068/102] Revert "Improve image editor integration" This reverts commit 00dbbf9626dafe581940697bf6d75046ca569d10. --- src/wp-admin/includes/image.php | 66 ++++++++----------- src/wp-includes/class-wp-image-editor-gd.php | 10 +-- .../class-wp-image-editor-imagick.php | 10 +-- src/wp-includes/class-wp-image-editor.php | 11 ---- 4 files changed, 38 insertions(+), 59 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 0968f0a02f737..b19c552ec7b8d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -316,7 +316,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); + $saved = $editor->save( $editor->generate_filename( 'scaled' ), $primary_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); @@ -334,7 +334,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. // adding extension to name for now. @@ -348,16 +347,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // TODO: Log errors. } } else { - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - // Populate the top level additional mime type data. foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); @@ -367,10 +358,16 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } } - $editor->set_mime_type( $primary_mime_type ); - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. + + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } // Rotate the image. $rotated = $editor->maybe_exif_rotate(); @@ -395,11 +392,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. // adding extension to name for now. - $saved = $editor->save( $editor->generate_filename( $extension ) ); + $saved = $editor->save( $editor->generate_filename( $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -526,10 +522,11 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // TODO: Log errors. } } - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - // Make the primary mime type sub sized images. - if ( method_exists( $editor, 'make_subsize' ) ) { - $new_size_meta = $editor->make_subsize( $new_size_data ); + + if ( method_exists( $editor, 'make_subsize' ) ) { + // Make the primary mime type sub sized images + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { @@ -541,36 +538,25 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); - - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - } - // Save additional mime types sub sizes. - foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); - if ( method_exists( $editor, 'make_subsize' ) ) { - $new_size_meta = $editor->make_subsize( $new_size_data ); + // Save additional mime types for this size. + foreach ( $additional_mime_types as $output_mime_type ) { + $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); if ( ! is_wp_error( $new_size_meta ) ) { $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); - - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } } } + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); + + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } } return $image_meta; diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index 80e58f77cf996..4d7cc73f06bae 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -247,7 +247,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data ); + $meta = $this->make_subsize( $size_data, null ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; @@ -261,18 +261,20 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 + * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } + * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data ) { + public function make_subsize( $size_data, $mime_type = null ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -296,7 +298,7 @@ public function make_subsize( $size_data ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $resized ); + $saved = $this->_save( $resized, null, $mime_type ); imagedestroy( $resized ); } diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 7301c9b710436..8ba376e192618 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -458,7 +458,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data ); + $meta = $this->make_subsize( $size_data, null ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; @@ -472,18 +472,20 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 + * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } + * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data ) { + public function make_subsize( $size_data, $mime_type = null ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -508,7 +510,7 @@ public function make_subsize( $size_data ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $this->image ); + $saved = $this->_save( $this->image, null, $mime_type ); $this->image->clear(); $this->image->destroy(); diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 4e3480aa8300c..823501eb46e8a 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -627,16 +627,5 @@ protected static function get_extension( $mime_type = null ) { return wp_get_default_extension_for_mime_type( $mime_type ); } - - /** - * Set the editor mime type, useful when outputting alternate mime types. - * - * @since 6.0.0 - * - * @param string $mime_type The mime type to set. - */ - public function set_mime_type( $mime_type ) { - $this->mime_type = $mime_type; - } } From 822a49bbf6be66b181fe76b51ad10efa95d1ddfb Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2022 15:41:02 -0600 Subject: [PATCH 069/102] Comment alignment --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index b19c552ec7b8d..335f0a859e75b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1258,7 +1258,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * @since 6.0.0 * * @param array $image_mime_transforms A map with the valid mime transforms. - * @param int $attachment_id The ID of the attachment where the hook was dispatched. + * @param int $attachment_id The ID of the attachment where the hook was dispatched. */ return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id ); } From cf481a7d0be3b69f07ad87de71685eef0c7f2361 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2022 22:17:31 -0600 Subject: [PATCH 070/102] Revert "Revert "Improve image editor integration"" This reverts commit c5fddab15065a97218f6ac2b697786b05babcdb9. --- src/wp-admin/includes/image.php | 66 +++++++++++-------- src/wp-includes/class-wp-image-editor-gd.php | 10 ++- .../class-wp-image-editor-imagick.php | 10 ++- src/wp-includes/class-wp-image-editor.php | 11 ++++ 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 335f0a859e75b..d3911082bcc5e 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -316,7 +316,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $resized ) ) { // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ), $primary_mime_type ); + $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); if ( ! is_wp_error( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); @@ -334,6 +334,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. // adding extension to name for now. @@ -347,8 +348,16 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // TODO: Log errors. } } else { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + // Populate the top level additional mime type data. foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); @@ -358,16 +367,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } } } + $editor->set_mime_type( $primary_mime_type ); + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } // Rotate the image. $rotated = $editor->maybe_exif_rotate(); @@ -392,10 +395,11 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Save additional mime types. foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. // adding extension to name for now. - $saved = $editor->save( $editor->generate_filename( $extension ), $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( $extension ) ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -522,11 +526,10 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // TODO: Log errors. } } - - if ( method_exists( $editor, 'make_subsize' ) ) { - // Make the primary mime type sub sized images - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $primary_mime_type ); + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + // Make the primary mime type sub sized images. + if ( method_exists( $editor, 'make_subsize' ) ) { + $new_size_meta = $editor->make_subsize( $new_size_data ); if ( is_wp_error( $new_size_meta ) ) { // TODO: Log errors. } else { @@ -538,24 +541,35 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); + + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } - // Save additional mime types for this size. - foreach ( $additional_mime_types as $output_mime_type ) { - $new_size_meta = $editor->make_subsize( $new_size_data, $output_mime_type ); + // Save additional mime types sub sizes. + foreach ( $additional_mime_types as $output_mime_type ) { + $editor->set_mime_type( $output_mime_type ); + if ( method_exists( $editor, 'make_subsize' ) ) { + $new_size_meta = $editor->make_subsize( $new_size_data ); if ( ! is_wp_error( $new_size_meta ) ) { $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } - } - } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( ! empty( $created_sizes ) ) { + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } } } diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index 4d7cc73f06bae..80e58f77cf996 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -247,7 +247,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data, null ); + $meta = $this->make_subsize( $size_data ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; @@ -261,20 +261,18 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 - * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } - * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data, $mime_type = null ) { + public function make_subsize( $size_data ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -298,7 +296,7 @@ public function make_subsize( $size_data, $mime_type = null ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $resized, null, $mime_type ); + $saved = $this->_save( $resized ); imagedestroy( $resized ); } diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 8ba376e192618..7301c9b710436 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -458,7 +458,7 @@ public function multi_resize( $sizes ) { $metadata = array(); foreach ( $sizes as $size => $size_data ) { - $meta = $this->make_subsize( $size_data, null ); + $meta = $this->make_subsize( $size_data ); if ( ! is_wp_error( $meta ) ) { $metadata[ $size ] = $meta; @@ -472,20 +472,18 @@ public function multi_resize( $sizes ) { * Create an image sub-size and return the image meta data value for it. * * @since 5.3.0 - * @since 6.0.0 Added the $mime_type parameter. * - * @param array $size_data { + * @param array $size_data { * Array of size data. * * @type int $width The maximum width in pixels. * @type int $height The maximum height in pixels. * @type bool $crop Whether to crop the image to exact dimensions. * } - * @param string $mime_type Optional. The mime type of the image. * @return array|WP_Error The image data array for inclusion in the `sizes` array in the image meta, * WP_Error object on error. */ - public function make_subsize( $size_data, $mime_type = null ) { + public function make_subsize( $size_data ) { if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { return new WP_Error( 'image_subsize_create_error', __( 'Cannot resize the image. Both width and height are not set.' ) ); } @@ -510,7 +508,7 @@ public function make_subsize( $size_data, $mime_type = null ) { if ( is_wp_error( $resized ) ) { $saved = $resized; } else { - $saved = $this->_save( $this->image, null, $mime_type ); + $saved = $this->_save( $this->image ); $this->image->clear(); $this->image->destroy(); diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 823501eb46e8a..4e3480aa8300c 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -627,5 +627,16 @@ protected static function get_extension( $mime_type = null ) { return wp_get_default_extension_for_mime_type( $mime_type ); } + + /** + * Set the editor mime type, useful when outputting alternate mime types. + * + * @since 6.0.0 + * + * @param string $mime_type The mime type to set. + */ + public function set_mime_type( $mime_type ) { + $this->mime_type = $mime_type; + } } From 98ab98017e0814b6259e367ae3dec02513957a5b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2022 23:03:20 -0600 Subject: [PATCH 071/102] Introduce reset_mime_type and mime_type_set to base image class --- src/wp-admin/includes/image.php | 8 +++++--- src/wp-includes/class-wp-image-editor.php | 25 ++++++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index d3911082bcc5e..efa2f17024585 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -314,6 +314,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } if ( ! is_wp_error( $resized ) ) { + $editor->set_mime_type( $primary_mime_type ); // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); @@ -338,7 +339,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. // adding extension to name for now. - $saved = $editor->save( $editor->generate_filename( $extension ), $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( $extension ) ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -360,7 +361,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $editor->set_mime_type( $output_mime_type ); if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - $saved = $editor->save( $editor->generate_filename( null, null, $extension ), $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( null, null, $extension ) ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); @@ -377,7 +378,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( true === $rotated ) { // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ), $primary_mime_type ); + $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); if ( ! is_wp_error( $saved ) ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); @@ -527,6 +528,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { } } foreach ( $new_sizes as $new_size_name => $new_size_data ) { + $editor->set_mime_type( $primary_mime_type ); // Make the primary mime type sub sized images. if ( method_exists( $editor, 'make_subsize' ) ) { $new_size_meta = $editor->make_subsize( $new_size_data ); diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 4e3480aa8300c..bcfe8626b066e 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -15,6 +15,7 @@ abstract class WP_Image_Editor { protected $file = null; protected $size = null; protected $mime_type = null; + protected $mime_type_set = false; protected $output_mime_type = null; protected $default_mime_type = 'image/jpeg'; protected $quality = false; @@ -336,11 +337,17 @@ protected function get_output_format( $filename = null, $mime_type = null ) { // If no file specified, grab editor's current extension and mime-type. $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); $file_mime = $this->mime_type; + // Favor the file mime extension. + $ext = $this->get_extension( $file_mime ); + if ( $this->mime_type_set && $ext !== $file_ext ) { + $new_ext = $ext; + $mime_type = $file_mime; + } } // Check to see if specified mime-type is the same as type implied by // file extension. If so, prefer extension from file. - if ( ! $mime_type || ( $file_mime == $mime_type ) ) { + if ( ! $this->mime_type_set && ( ! $mime_type || ( $file_mime == $mime_type ) ) ) { $mime_type = $file_mime; $new_ext = $file_ext; } @@ -631,12 +638,24 @@ protected static function get_extension( $mime_type = null ) { /** * Set the editor mime type, useful when outputting alternate mime types. * + * Track that the mime type is set with the mime type set flag. + * * @since 6.0.0 * * @param string $mime_type The mime type to set. */ public function set_mime_type( $mime_type ) { - $this->mime_type = $mime_type; + $this->mime_type = $mime_type; + $this->mime_type_set = true; } -} + /** + * Reset the mime type to the original file mime type. + * + * Reset the mime type set flag. + */ + public function reset_mime_type() { + $this->mime_type = wp_get_image_mime( $this->file ); + $this->mime_type_set = false; + } +} From de0500d9cc7ed0f441897024436ff93c1524fe7e Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 24 Mar 2022 20:28:05 -0600 Subject: [PATCH 072/102] Restructure loops putting mime types as outer --- src/wp-admin/includes/image.php | 387 +++++++++++++++----------------- 1 file changed, 175 insertions(+), 212 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index efa2f17024585..bd90aae65b899 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -244,6 +244,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { 'file' => _wp_relative_upload_path( $file ), 'filesize' => wp_filesize( $file ), 'sizes' => array(), + 'sources' => array(), ); // Fetch additional metadata from EXIF/IPTC. @@ -253,157 +254,121 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $image_meta['image_meta'] = $exif_meta; } - // Calculate the primary and additional mime types to generate. - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $file, $attachment_id ); - - // Add a top level sources array for the full size images. - if ( ! isset( $image_meta['sources'] ) || ! is_array( $image_meta['sources'] ) ) { - $image_meta['sources'] = array(); - } - - // Populate the top level primary mime type data. - if ( empty( $image_meta['sources'][ $primary_mime_type ] ) ) { - $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $image_meta ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - - // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. - if ( 'image/png' !== $imagesize['mime'] ) { - - /** - * Filters the "BIG image" threshold value. - * - * If the original image width or height is above the threshold, it will be scaled down. The threshold is - * used as max width and max height. The scaled down image will be used as the largest available size, including - * the `_wp_attached_file` post meta value. - * - * Returning `false` from the filter callback will disable the scaling. - * - * @since 5.3.0 - * - * @param int $threshold The threshold value in pixels. Default 2560. - * @param array $imagesize { - * Indexed array of the image width and height in pixels. - * - * @type int $0 The image width. - * @type int $1 The image height. - * } - * @param string $file Full path to the uploaded image file. - * @param int $attachment_id Attachment post ID. - */ - $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - - // If the original image's dimensions are over the threshold, - // scale the image and use it as the "full" size. - if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { - $editor = wp_get_image_editor( $file ); + // Calculate the primary (first) and additional mime types to generate. + $mime_types_to_generate = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); + foreach ( $mime_types_to_generate as $output_index => $output_mime_type ) { + // Populate the top level primary mime type data. + if ( 0 === $output_index ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $image_meta ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } + // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. + if ( 'image/png' !== $imagesize['mime'] ) { + /** + * Filters the "BIG image" threshold value. + * + * If the original image width or height is above the threshold, it will be scaled down. The threshold is + * used as max width and max height. The scaled down image will be used as the largest available size, including + * the `_wp_attached_file` post meta value. + * + * Returning `false` from the filter callback will disable the scaling. + * + * @since 5.3.0 + * + * @param int $threshold The threshold value in pixels. Default 2560. + * @param array $imagesize { + * Indexed array of the image width and height in pixels. + * + * @type int $0 The image width. + * @type int $1 The image height. + * } + * @param string $file Full path to the uploaded image file. + * @param int $attachment_id Attachment post ID. + */ + $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - // Resize the image. - $resized = $editor->resize( $threshold, $threshold ); - $rotated = null; + // If the original image's dimensions are over the threshold, + // scale the image and use it as the "full" size. + if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { + $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); - // If there is EXIF data, rotate according to EXIF Orientation. - if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { - $resized = $editor->maybe_exif_rotate(); - $rotated = $resized; - } + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + $editor->set_mime_type( $output_mime_type ); - if ( ! is_wp_error( $resized ) ) { - $editor->set_mime_type( $primary_mime_type ); - // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". - // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); + // Resize the image. + $resized = $editor->resize( $threshold, $threshold ); + $rotated = null; - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + // If there is EXIF data, rotate according to EXIF Orientation. + if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { + $resized = $editor->maybe_exif_rotate(); + $rotated = $resized; + } - $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $saved ); + if ( ! is_wp_error( $resized ) ) { + // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". + // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). + $saved = $editor->save( $editor->generate_filename( 'scaled' ), $output_mime_type ); - // If the image was rotated update the stored EXIF data. - if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; + if ( ! is_wp_error( $saved ) ) { + if ( 0 === $output_index ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + } + // If the image was rotated update the stored EXIF data. + if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } + } else { + // TODO: Log errors. } - wp_update_attachment_metadata( $attachment_id, $image_meta ); } else { // TODO: Log errors. } - - // Save additional mime types. - foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); - $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - // @todo Correct issue where filename winds up with duplicate "-scaled" suffix added unless the editor is reset. - // adding extension to name for now. - $saved = $editor->save( $editor->generate_filename( $extension ) ); - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - } } else { - // TODO: Log errors. - } - } else { - $editor = wp_get_image_editor( $file ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - - // Populate the top level additional mime type data. - foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); - if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { - $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - $saved = $editor->save( $editor->generate_filename( null, null, $extension ) ); - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + // Generate additional full sized images. + if ( $output_index > 0 ) { + if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { + $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); + $saved = $editor->save( $editor->generate_filename( null, null, $extension ) ); + if ( ! is_wp_error( $saved ) ) { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } } } - } - $editor->set_mime_type( $primary_mime_type ); - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - // Rotate the image. - $rotated = $editor->maybe_exif_rotate(); + $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); - if ( true === $rotated ) { - // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + $editor->set_mime_type( $output_mime_type ); - $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $saved ); + // Rotate the image. + $rotated = $editor->maybe_exif_rotate(); - // Update the stored EXIF data. - if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } else { - // TODO: Log errors. - } + if ( true === $rotated ) { + // Append `-rotated` to the image file name. + $saved = $editor->save( $editor->generate_filename( 'rotated' ), $output_mime_type ); - // Save additional mime types. - foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); - $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - // @todo Correct issue where filename winds up with duplicate "-rotated" suffix added unless the editor is reset. - // adding extension to name for now. - $saved = $editor->save( $editor->generate_filename( $extension ) ); if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + + // Update the stored EXIF data. + if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } + } else { + // TODO: Log errors. } } } @@ -412,10 +377,10 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } /* - * Initial save of the new metadata. - * At this point the file was uploaded and moved to the uploads directory - * but the image sub-sizes haven't been created yet and the `sizes` array is empty. - */ + * Initial save of the new metadata. + * At this point the file was uploaded and moved to the uploads directory + * but the image sub-sizes haven't been created yet and the `sizes` array is empty. + */ wp_update_attachment_metadata( $attachment_id, $image_meta ); $new_sizes = wp_get_registered_image_subsizes(); @@ -473,104 +438,90 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { // Not an image attachment. return array(); } - - // Check if any of the new sizes already exist. - if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { - foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - /* - * Only checks "size name" so we don't override existing images even if the dimensions - * don't match the currently defined size with the same name. - * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. - */ - if ( array_key_exists( $size_name, $new_sizes ) ) { - unset( $new_sizes[ $size_name ] ); + // Calculate the primary (first) and additional mime types to generate. + $mime_types_to_generate = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); + foreach ( $mime_types_to_generate as $output_index => $output_mime_type ) { + // For the primary image, check if any of the new sizes already exist. + if ( 0 === $output_index && isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { + foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { + /* + * Only checks "size name" so we don't override existing images even if the dimensions + * don't match the currently defined size with the same name. + * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. + */ + if ( array_key_exists( $size_name, $new_sizes ) ) { + unset( $new_sizes[ $size_name ] ); + } } + } else { + $image_meta['sizes'] = array(); } - } else { - $image_meta['sizes'] = array(); - } - if ( empty( $new_sizes ) ) { - // Nothing to do... - return $image_meta; - } + if ( empty( $new_sizes ) ) { + // Nothing to do... + return $image_meta; + } - // Calculate the primary and additional mime types to generate. - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); + /* + * Sort the image sub-sizes in order of priority when creating them. + * This ensures there is an appropriate sub-size the user can access immediately + * even when there was an error and not all sub-sizes were created. + */ + $priority = array( + 'medium' => null, + 'large' => null, + 'thumbnail' => null, + 'medium_large' => null, + ); - /* - * Sort the image sub-sizes in order of priority when creating them. - * This ensures there is an appropriate sub-size the user can access immediately - * even when there was an error and not all sub-sizes were created. - */ - $priority = array( - 'medium' => null, - 'large' => null, - 'thumbnail' => null, - 'medium_large' => null, - ); + $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); + $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); - $editor = wp_get_image_editor( $file ); + if ( is_wp_error( $editor ) ) { + // The image cannot be edited. + return $image_meta; + } - if ( is_wp_error( $editor ) ) { - // The image cannot be edited. - return $image_meta; - } + $editor->set_mime_type( $output_mime_type ); - // If stored EXIF data exists, rotate the source image before creating sub-sizes. - if ( ! empty( $image_meta['image_meta'] ) ) { - $rotated = $editor->maybe_exif_rotate(); + // If stored EXIF data exists, rotate the source image before creating sub-sizes. + if ( ! empty( $image_meta['image_meta'] ) ) { + $rotated = $editor->maybe_exif_rotate(); - if ( is_wp_error( $rotated ) ) { - // TODO: Log errors. - } - } - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $editor->set_mime_type( $primary_mime_type ); - // Make the primary mime type sub sized images. - if ( method_exists( $editor, 'make_subsize' ) ) { - $new_size_meta = $editor->make_subsize( $new_size_data ); - if ( is_wp_error( $new_size_meta ) ) { + if ( is_wp_error( $rotated ) ) { // TODO: Log errors. - } else { - // Save the size meta value. - $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { - $image_meta['sizes'][ $new_size_name ]['sources'] = array(); - } - $image_meta['sizes'][ $new_size_name ]['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); - - if ( ! empty( $created_sizes ) ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); } } - // Save additional mime types sub sizes. - foreach ( $additional_mime_types as $output_mime_type ) { - $editor->set_mime_type( $output_mime_type ); - if ( method_exists( $editor, 'make_subsize' ) ) { + if ( method_exists( $editor, 'make_subsize' ) ) { + foreach ( $new_sizes as $new_size_name => $new_size_data ) { $new_size_meta = $editor->make_subsize( $new_size_data ); - if ( ! is_wp_error( $new_size_meta ) ) { + if ( is_wp_error( $new_size_meta ) ) { + // TODO: Log errors. + } else { + // Save the primary mime type sizes meta value. + if ( 0 === $output_index ) { + $image_meta['sizes'][ $new_size_name ] = $new_size_meta; + } + // Update the sources array with the new sub-size. $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); + } + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); - if ( ! empty( $created_sizes ) ) { + if ( ! empty( $created_sizes ) ) { + // Save the primary mime type sizes meta value. + if ( 0 === $output_index ) { $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); } + // Update the sources array with the new sub-size. + $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); } } } @@ -1294,6 +1245,18 @@ function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) { $original_mime_type = wp_get_image_mime( $file ); $output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type ); + // Exclude any output mime types that the system doesn't support. + $output_mime_types = array_filter( + $output_mime_types, + function( $mime_type ) { + return wp_image_editor_supports( + array( + 'mime_type' => $mime_type, + ) + ); + } + ); + // Use original mime type as primary mime type, or alternatively the first one. $primary_mime_type_key = array_search( $original_mime_type, $output_mime_types, true ); if ( false === $primary_mime_type_key ) { @@ -1307,8 +1270,8 @@ function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) { if ( empty( $primary_mime_type ) ) { $primary_mime_type = $original_mime_type; } - return array( - $primary_mime_type, - $additional_mime_types, + return array_merge( + array( $primary_mime_type ), + $additional_mime_types ); } From bcf4d0aaaefd50ee0d20ef8d8ea618e56dbf59bd Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 08:45:31 -0600 Subject: [PATCH 073/102] Update src/wp-admin/includes/image.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Crisoforo Gaspar Hernández --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index bd90aae65b899..f05d2fa12b6a4 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -333,7 +333,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( $output_index > 0 ) { if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - $saved = $editor->save( $editor->generate_filename( null, null, $extension ) ); + $saved = $editor->save( $editor->generate_filename( '', null, $extension ) ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); From d9e9d1c365c47f225389f683dc39386671777b03 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 08:48:18 -0600 Subject: [PATCH 074/102] Improve generate_filename suffix handling --- src/wp-includes/class-wp-image-editor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index bcfe8626b066e..b85f558ec5581 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -433,7 +433,7 @@ protected function get_output_format( $filename = null, $mime_type = null ) { */ public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) { // $suffix will be appended to the destination filename, just before the extension. - if ( ! $suffix ) { + if ( null === $suffix ) { $suffix = $this->get_suffix(); } From 43afd05511ccf3f12ae958d6f10b8ca82a0f0a8c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 08:50:45 -0600 Subject: [PATCH 075/102] Update doc blocks for wp_upload_image_mime_transforms --- src/wp-admin/includes/image.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index f05d2fa12b6a4..854ba08ce15fd 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1211,7 +1211,8 @@ function _copy_image_file( $attachment_id ) { * @since 6.0.0 * * @param $attachment_id int The attachment ID. - * @return array> An array of valid mime types, where the key is the mime type and the value is the extension type. + * @return array> An array of valid mime types, where the key is the source file mime type and the + * value is one or more mime file types to generate. */ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( @@ -1224,7 +1225,8 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * * @since 6.0.0 * - * @param array $image_mime_transforms A map with the valid mime transforms. + * @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type + * and the value is one or more mime file types to generate. * @param int $attachment_id The ID of the attachment where the hook was dispatched. */ return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id ); From d9e864489d9b1473ad81ca614f2a3189cc61955c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 11:56:42 -0600 Subject: [PATCH 076/102] Update generate_filename and doc block to enable no suffix --- src/wp-includes/class-wp-image-editor.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index b85f558ec5581..b0a14a4800266 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -425,8 +425,11 @@ protected function get_output_format( $filename = null, $mime_type = null ) { * Builds an output filename based on current file, and adding proper suffix * * @since 3.5.0 + * @since 6.0.0 Skips adding a suffix when set to an empty string. * - * @param string $suffix + * @param string $suffix Optional. Suffix to add to the filename. Passing null + * will result in a 'widthxheight' suffix. Passing + * an empty string will result in no suffix. * @param string $dest_path * @param string $extension * @return string filename @@ -454,6 +457,10 @@ public function generate_filename( $suffix = null, $dest_path = null, $extension } } + if ( '' === $suffix ) { + return trailingslashit( $dir ) . "{$name}.{$new_ext}"; + } + return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; } From af9db6725d4ae1379416dcc1b7c225dcfd505d8f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 12:00:56 -0600 Subject: [PATCH 077/102] Improve logic for sub size loop --- src/wp-admin/includes/image.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 854ba08ce15fd..acbf5e98a5ab6 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -442,19 +442,21 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { $mime_types_to_generate = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); foreach ( $mime_types_to_generate as $output_index => $output_mime_type ) { // For the primary image, check if any of the new sizes already exist. - if ( 0 === $output_index && isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { - foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - /* - * Only checks "size name" so we don't override existing images even if the dimensions - * don't match the currently defined size with the same name. - * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. - */ - if ( array_key_exists( $size_name, $new_sizes ) ) { - unset( $new_sizes[ $size_name ] ); + if ( 0 === $output_index ) { + if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { + foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { + /* + * Only checks "size name" so we don't override existing images even if the dimensions + * don't match the currently defined size with the same name. + * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. + */ + if ( array_key_exists( $size_name, $new_sizes ) ) { + unset( $new_sizes[ $size_name ] ); + } } + } else { + $image_meta['sizes'] = array(); } - } else { - $image_meta['sizes'] = array(); } if ( empty( $new_sizes ) ) { From 4e36c6c83405413433693ff37cc8df0d1778f795 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 12:01:18 -0600 Subject: [PATCH 078/102] Ensure $editor available --- src/wp-admin/includes/image.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index acbf5e98a5ab6..1a0bf1151f739 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -265,6 +265,14 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. if ( 'image/png' !== $imagesize['mime'] ) { + $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + continue; + } + $editor->set_mime_type( $output_mime_type ); + /** * Filters the "BIG image" threshold value. * @@ -291,13 +299,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { - $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - $editor->set_mime_type( $output_mime_type ); // Resize the image. $resized = $editor->resize( $threshold, $threshold ); From eaffc7028b0cf22c617b9659460c13a01a7ac27c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 12:02:38 -0600 Subject: [PATCH 079/102] generate additional full sizes without extension --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 1a0bf1151f739..7b430908989a1 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -334,7 +334,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( $output_index > 0 ) { if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - $saved = $editor->save( $editor->generate_filename( '', null, $extension ) ); + $saved = $editor->save( $editor->generate_filename( '', null, $extension ), $output_mime_type ); if ( ! is_wp_error( $saved ) ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); wp_update_attachment_metadata( $attachment_id, $image_meta ); From 75315ceb639e20961f07459104f714d7a36398b9 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 12:15:52 -0600 Subject: [PATCH 080/102] Use legacy JPEG output for PDF tests --- tests/phpunit/tests/image/functions.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index 39bf09cf0cf6d..b9c96c8721b16 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -460,6 +460,9 @@ public function test_wp_generate_attachment_metadata_pdf() { $this->markTestSkipped( 'Rendering PDFs is not supported on this system.' ); } + // Use legacy JPEG output. + add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); + $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf'; $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf'; copy( $orig_file, $test_file ); @@ -541,6 +544,7 @@ public function test_wp_generate_attachment_metadata_pdf() { foreach ( $metadata['sizes'] as $size ) { unlink( $temp_dir . $size['file'] ); } + remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); } /** @@ -555,6 +559,9 @@ public function test_crop_setting_for_pdf() { update_option( 'medium_crop', 1 ); + // Use legacy JPEG output. + add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); + $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf'; $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf'; copy( $orig_file, $test_file ); @@ -636,6 +643,8 @@ public function test_crop_setting_for_pdf() { foreach ( $metadata['sizes'] as $size ) { unlink( $temp_dir . $size['file'] ); } + remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); + } /** @@ -646,6 +655,9 @@ public function test_fallback_intermediate_image_sizes() { $this->markTestSkipped( 'Rendering PDFs is not supported on this system.' ); } + // Use legacy JPEG output. + add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); + $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf'; $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf'; copy( $orig_file, $test_file ); @@ -699,6 +711,7 @@ public function test_fallback_intermediate_image_sizes() { foreach ( $metadata['sizes'] as $size ) { unlink( $temp_dir . $size['file'] ); } + remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); } public function filter_fallback_intermediate_image_sizes( $fallback_sizes, $metadata ) { From 7f78dacb120a660ab7657cbbc8568af17e3f4a94 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 12:38:07 -0600 Subject: [PATCH 081/102] =?UTF-8?q?Calculate=20over=20threshold=20only=20o?= =?UTF-8?q?nce=20(=E2=80=9Cbig=5Fimage=5Fsize=5Fthreshold=E2=80=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wp-admin/includes/image.php | 50 +++++++++++++++++---------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 7b430908989a1..b20a509ccc9a3 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -254,6 +254,31 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $image_meta['image_meta'] = $exif_meta; } + /** + * Filters the "BIG image" threshold value. + * + * If the original image width or height is above the threshold, it will be scaled down. The threshold is + * used as max width and max height. The scaled down image will be used as the largest available size, including + * the `_wp_attached_file` post meta value. + * + * Returning `false` from the filter callback will disable the scaling. + * + * @since 5.3.0 + * + * @param int $threshold The threshold value in pixels. Default 2560. + * @param array $imagesize { + * Indexed array of the image width and height in pixels. + * + * @type int $0 The image width. + * @type int $1 The image height. + * } + * @param string $file Full path to the uploaded image file. + * @param int $attachment_id Attachment post ID. + */ + $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); + + $over_threshold = $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ); + // Calculate the primary (first) and additional mime types to generate. $mime_types_to_generate = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); foreach ( $mime_types_to_generate as $output_index => $output_mime_type ) { @@ -273,32 +298,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { } $editor->set_mime_type( $output_mime_type ); - /** - * Filters the "BIG image" threshold value. - * - * If the original image width or height is above the threshold, it will be scaled down. The threshold is - * used as max width and max height. The scaled down image will be used as the largest available size, including - * the `_wp_attached_file` post meta value. - * - * Returning `false` from the filter callback will disable the scaling. - * - * @since 5.3.0 - * - * @param int $threshold The threshold value in pixels. Default 2560. - * @param array $imagesize { - * Indexed array of the image width and height in pixels. - * - * @type int $0 The image width. - * @type int $1 The image height. - * } - * @param string $file Full path to the uploaded image file. - * @param int $attachment_id Attachment post ID. - */ - $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. - if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { + if ( $over_threshold ) { // Resize the image. $resized = $editor->resize( $threshold, $threshold ); From 253a0dff3c5da6d1b613daa237f8e258c3bdb8f0 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 15:26:02 -0600 Subject: [PATCH 082/102] =?UTF-8?q?Add=20a=20root=20level=20(primary)=20?= =?UTF-8?q?=E2=80=98meta=5Ftype=E2=80=99=20attribute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/wp-admin/includes/image.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index b20a509ccc9a3..f7e4b64a5802f 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -285,6 +285,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { // Populate the top level primary mime type data. if ( 0 === $output_index ) { $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $image_meta ); + $image_meta['mime_type'] = $output_mime_type; wp_update_attachment_metadata( $attachment_id, $image_meta ); } From 4acc7c5a8234e4c864937f721643d3a5573ba101 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 15:37:40 -0600 Subject: [PATCH 083/102] Ensure alternate mime -scaled images are included in sources meta --- src/wp-admin/includes/image.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index f7e4b64a5802f..806889a39aa2d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -321,6 +321,8 @@ function wp_create_image_subsizes( $file, $attachment_id ) { if ( ! is_wp_error( $saved ) ) { if ( 0 === $output_index ) { $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + } else { + $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); } // If the image was rotated update the stored EXIF data. if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { From efa33f29bbd87c9d99d62a81e9799b6dddfef271 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 28 Mar 2022 15:39:30 -0600 Subject: [PATCH 084/102] Use primary mime when deleting additional sources --- src/wp-includes/post.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 4c1aa0327b206..d108fb196e987 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6466,6 +6466,8 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } + $primary_mime = isset( $meta['mime_type'] ) ? $meta['mime_type'] : false; + // Remove intermediate and backup images if there are any. if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) { $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) ); @@ -6481,11 +6483,9 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } // Check for alternate size mime types in the sizeinfo['sources'] array to delete. - if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && count( $sizeinfo['sources'] ) > 1 ) { - $sources = $sizeinfo['sources']; - array_shift( $sources ); - foreach ( $sources as $mime => $properties ) { - if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && $primary_mime ) { + foreach ( $sizeinfo['sources'] as $mime => $properties ) { + if ( $mime === $primary_mime || ! is_array( $properties ) || empty( $properties['file'] ) ) { continue; } @@ -6519,7 +6519,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $sources = $meta['sources']; array_shift( $sources ); foreach ( $sources as $mime => $properties ) { - if ( ! is_array( $properties ) || empty( $properties['file'] ) ) { + if ( $mime === $primary_mime || ! is_array( $properties ) || empty( $properties['file'] ) ) { continue; } From 133d9a6cc45a292d9eefe6c581066aa4f14e4f1f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 29 Mar 2022 08:54:03 -0600 Subject: [PATCH 085/102] Update src/wp-includes/post.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Crisoforo Gaspar Hernández --- src/wp-includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index d108fb196e987..48fc663992599 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6484,7 +6484,7 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } // Check for alternate size mime types in the sizeinfo['sources'] array to delete. if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && $primary_mime ) { - foreach ( $sizeinfo['sources'] as $mime => $properties ) { + foreach ( $sizeinfo['sources'] as $mime => $properties ) { if ( $mime === $primary_mime || ! is_array( $properties ) || empty( $properties['file'] ) ) { continue; } From bf1c483d639a1628ef28534d2b937be215a7857a Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 29 Mar 2022 08:54:11 -0600 Subject: [PATCH 086/102] Update tests/phpunit/tests/image/functions.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Crisoforo Gaspar Hernández --- tests/phpunit/tests/image/functions.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index b9c96c8721b16..95f6505d7dd12 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -504,7 +504,6 @@ public function test_wp_generate_attachment_metadata_pdf() { 'image/jpeg' => array( 'file' => 'wordpress-gsoc-flyer-pdf-232x300.jpg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ), - ), ), ), From 4bffe64ca8f930e8ac400e5c5db66f9862debeed Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 29 Mar 2022 15:30:58 -0600 Subject: [PATCH 087/102] Update src/wp-includes/class-wp-image-editor.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Crisoforo Gaspar Hernández --- src/wp-includes/class-wp-image-editor.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index b0a14a4800266..ae8d5e331a7a1 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -457,11 +457,13 @@ public function generate_filename( $suffix = null, $dest_path = null, $extension } } - if ( '' === $suffix ) { - return trailingslashit( $dir ) . "{$name}.{$new_ext}"; + if ( empty( $suffix ) ) { + $suffix = ""; + } else { + $suffix = "-{$suffix}"; } - return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; + return trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}"; } /** From abcab3a2c9c731456568b0e7ba8ac2a98ce17ed3 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 29 Mar 2022 15:54:46 -0600 Subject: [PATCH 088/102] phpcbf --- src/wp-includes/class-wp-image-editor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index ae8d5e331a7a1..accffb700878c 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -340,7 +340,7 @@ protected function get_output_format( $filename = null, $mime_type = null ) { // Favor the file mime extension. $ext = $this->get_extension( $file_mime ); if ( $this->mime_type_set && $ext !== $file_ext ) { - $new_ext = $ext; + $new_ext = $ext; $mime_type = $file_mime; } } @@ -458,7 +458,7 @@ public function generate_filename( $suffix = null, $dest_path = null, $extension } if ( empty( $suffix ) ) { - $suffix = ""; + $suffix = ''; } else { $suffix = "-{$suffix}"; } From 790f0119816c1857a88e1fcd9b6414fead4d6f1c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 3 May 2022 15:40:20 -0600 Subject: [PATCH 089/102] Ensure $meta['file'] set before using --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index df187698f6b5c..30d797081ad7d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -422,7 +422,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { */ function _wp_get_sources_from_meta( $meta ) { return array( - 'file' => wp_basename( $meta['file'] ), + 'file' => isset( $meta['file'] ) ? wp_basename( $meta['file'] ) : '', 'filesize' => isset( $meta['filesize'] ) ? $meta['filesize'] : wp_filesize( $meta['path'] ), ); } From 416f08866588bc4dbb096c4169e88b813b68cd23 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 3 May 2022 15:42:41 -0600 Subject: [PATCH 090/102] restore whitespace --- tests/phpunit/tests/image/functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index 5dfbecd6b13cc..332b12108fd0d 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -601,6 +601,7 @@ public function test_crop_setting_for_pdf() { $this->assertNotEmpty( $attachment_id ); $temp_dir = get_temp_dir(); + $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); $expected = array( From 6a894f54fc71de2751cf97517948853bf38356fe Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 10 May 2022 07:50:42 -0600 Subject: [PATCH 091/102] Ensure image filter skipped for test --- tests/phpunit/tests/media.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 693ece460f726..917a25c2c270f 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -2365,6 +2365,7 @@ public function test_wp_filter_content_tags_filter_with_identical_image_tags_dis add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); + add_filter( 'wp_content_image_mimes', '__return_empty_array' ); add_filter( 'wp_content_img_tag', @@ -2890,8 +2891,6 @@ public function test_media_handle_upload_ignores_page_parent_for_directory_date( * @requires function imagejpeg */ public function test_wp_filter_content_tags_width_height() { - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); - $image_meta = wp_get_attachment_metadata( self::$large_id ); $size_array = $this->get_image_size_array_from_meta( $image_meta, 'medium' ); From d19186144ad988eae8fa73af414ac0a32d92d89b Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Tue, 7 Jun 2022 18:28:26 +0100 Subject: [PATCH 092/102] Enhance separation of concerns in image generation logic, introduce mime_type parameter and various private low-level functions to avoid duplicate and complex code. --- src/wp-admin/includes/image.php | 498 +++++++++++++++++++------------- 1 file changed, 293 insertions(+), 205 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 30d797081ad7d..e60f440c585a2 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -77,16 +77,22 @@ function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $s * Registered sub-sizes that are larger than the image are skipped. * * @since 5.3.0 + * @since 6.0.0 The $mime_type parameter was added. * - * @param int $attachment_id The image attachment post ID. + * @param int $attachment_id The image attachment post ID. + * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the primary image mime. * @return array[] Associative array of arrays of image sub-size information for * missing image sizes, keyed by image size name. */ -function wp_get_missing_image_subsizes( $attachment_id ) { +function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { if ( ! wp_attachment_is_image( $attachment_id ) ) { return array(); } + if ( ! $mime_type ) { + $mime_type = get_post_mime_type( get_post( $attachment_id ) ); + } + $registered_sizes = wp_get_registered_image_subsizes(); $image_meta = wp_get_attachment_metadata( $attachment_id ); @@ -129,19 +135,33 @@ function wp_get_missing_image_subsizes( $attachment_id ) { * However we keep the old sub-sizes with the previous dimensions * as the image may have been used in an older post. */ - $missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] ); + $missing_sizes = array(); + foreach ( $possible_sizes as $size_name => $size_data ) { + if ( ! isset( $image_meta['sizes'][ $size_name ] ) ) { + $missing_sizes[ $size_name ] = $size_data; + continue; + } + + if ( $size_meta['mime-type'] === $mime_type || isset( $size_meta['sources'][ $mime_type ] ) ) { + continue; + } + + $missing_sizes[ $size_name ] = $size_data; + } /** * Filters the array of missing image sub-sizes for an uploaded image. * * @since 5.3.0 + * @since 6.0.0 The $mime_type filter parameter was added. * * @param array[] $missing_sizes Associative array of arrays of image sub-size information for * missing image sizes, keyed by image size name. * @param array $image_meta The image meta data. * @param int $attachment_id The image attachment post ID. + * @param string $mime_type The image mime type to get missing sizes for. */ - return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id ); + return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id, $mime_type ); } /** @@ -258,141 +278,45 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $image_meta['image_meta'] = $exif_meta; } - /** - * Filters the "BIG image" threshold value. - * - * If the original image width or height is above the threshold, it will be scaled down. The threshold is - * used as max width and max height. The scaled down image will be used as the largest available size, including - * the `_wp_attached_file` post meta value. - * - * Returning `false` from the filter callback will disable the scaling. - * - * @since 5.3.0 - * - * @param int $threshold The threshold value in pixels. Default 2560. - * @param array $imagesize { - * Indexed array of the image width and height in pixels. - * - * @type int $0 The image width. - * @type int $1 The image height. - * } - * @param string $file Full path to the uploaded image file. - * @param int $attachment_id Attachment post ID. - */ - $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); - - $over_threshold = $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ); - // Calculate the primary (first) and additional mime types to generate. - $mime_types_to_generate = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); - foreach ( $mime_types_to_generate as $output_index => $output_mime_type ) { - // Populate the top level primary mime type data. - if ( 0 === $output_index ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $image_meta ); - $image_meta['mime_type'] = $output_mime_type; - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - - // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. - if ( 'image/png' !== $imagesize['mime'] ) { - $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - continue; - } - $editor->set_mime_type( $output_mime_type ); - - // If the original image's dimensions are over the threshold, - // scale the image and use it as the "full" size. - if ( $over_threshold ) { + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); - // Resize the image. - $resized = $editor->resize( $threshold, $threshold ); - $rotated = null; - - // If there is EXIF data, rotate according to EXIF Orientation. - if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { - $resized = $editor->maybe_exif_rotate(); - $rotated = $resized; - } - - if ( ! is_wp_error( $resized ) ) { - // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". - // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - $saved = $editor->save( $editor->generate_filename( 'scaled' ), $output_mime_type ); - - if ( ! is_wp_error( $saved ) ) { - if ( 0 === $output_index ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - } else { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - } - // If the image was rotated update the stored EXIF data. - if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - } else { - // TODO: Log errors. - } - } else { - // TODO: Log errors. - } - } else { - // Generate additional full sized images. - if ( $output_index > 0 ) { - if ( empty( $image_meta['sources'][ $output_mime_type ] ) ) { - $extension = wp_get_default_extension_for_mime_type( $output_mime_type ); - $saved = $editor->save( $editor->generate_filename( '', null, $extension ), $output_mime_type ); - if ( ! is_wp_error( $saved ) ) { - $image_meta['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $saved ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - } - } - - if ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { - // Rotate the whole original image if there is EXIF data and "orientation" is not 1. - - $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); - - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return $image_meta; - } - - $editor->set_mime_type( $output_mime_type ); - - // Rotate the image. - $rotated = $editor->maybe_exif_rotate(); + list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $primary_mime_type ); + if ( is_wp_error( $editor ) ) { + return $image_meta; + } + $suffix = _wp_get_image_suffix( $resized, $rotated ); - if ( true === $rotated ) { - // Append `-rotated` to the image file name. - $saved = $editor->save( $editor->generate_filename( 'rotated' ), $output_mime_type ); + // Save image only if either it was modified or if the primary mime type is different from the original. + if ( ! empty( $suffix ) || $primary_mime_type !== $imagesize['mime'] ) { + $saved = $editor->save( $editor->generate_filename( $suffix ) ); - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - // Update the stored EXIF data. - if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - } else { - // TODO: Log errors. - } - } - } + // If the image was rotated update the stored EXIF data. + if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; } + } else { + // TODO: Log errors. } } + // Set 'sources' for the primary mime type. + $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $image_meta ); + /* - * Initial save of the new metadata. - * At this point the file was uploaded and moved to the uploads directory - * but the image sub-sizes haven't been created yet and the `sizes` array is empty. - */ + * Initial save of the new metadata. + * At this point the file was uploaded and moved to the uploads directory + * but the image sub-sizes haven't been created yet and the `sizes` array is empty. + */ wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( ! empty( $additional_mime_types ) ) { + $image_meta = _wp_make_additional_mime_types( $additional_mime_types, $file, $image_meta, $attachment_id ); + } + $new_sizes = wp_get_registered_image_subsizes(); /** @@ -408,7 +332,118 @@ function wp_create_image_subsizes( $file, $attachment_id ) { */ $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id ); - return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ); + $image_meta = _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $primary_mime_type ); + foreach ( $additional_mime_types as $additional_mime_type ) { + $image_meta = _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_type ); + } + + return $image_meta; +} + +/** + * Returns a WP_Image_Editor instance where the image file has been scaled and rotated as necessary. + * + * @since 6.0.0 + * @access private + * + * @param string $file Full path to the image file. + * @param int $attachment_id Attachment ID. + * @param array $imagesize { + * Indexed array of the image width and height in pixels. + * + * @type int $0 The image width. + * @type int $1 The image height. + * } + * @param array|null $exif_meta EXIF metadata if extracted from the image file. + * @param string $mime_type Output mime type. + * @return array Array with three entries: The WP_Image_Editor instance, whether the image was resized, and whether the + * image was rotated. Each entry can alternatively be a WP_Error in case something went wrong. + */ +function _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type ) { + $resized = null; + $rotated = null; + + $editor = wp_get_image_editor( $file, array( 'mime_type' => $mime_type ) ); + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return array( $editor, $resized, $rotated ); + } + + if ( ! empty( $mime_type ) ) { + $editor->set_mime_type( $mime_type ); + } + + // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. + if ( 'image/png' !== $mime_type ) { + /** + * Filters the "BIG image" threshold value. + * + * If the original image width or height is above the threshold, it will be scaled down. The threshold is + * used as max width and max height. The scaled down image will be used as the largest available size, including + * the `_wp_attached_file` post meta value. + * + * Returning `false` from the filter callback will disable the scaling. + * + * @since 5.3.0 + * + * @param int $threshold The threshold value in pixels. Default 2560. + * @param array $imagesize { + * Indexed array of the image width and height in pixels. + * + * @type int $0 The image width. + * @type int $1 The image height. + * } + * @param string $file Full path to the uploaded image file. + * @param int $attachment_id Attachment post ID. + */ + $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id ); + + // If the original image's dimensions are over the threshold, + // scale the image and use it as the "full" size. + if ( $threshold && ( $imagesize[0] > $threshold || $imagesize[1] > $threshold ) ) { + // Resize the image. + $resized = $editor->resize( $threshold, $threshold ); + + // If there is EXIF data, rotate according to EXIF Orientation. + if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { + $resized = $editor->maybe_exif_rotate(); + $rotated = $resized; + } + } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { + // Rotate the whole original image if there is EXIF data and "orientation" is not 1. + $rotated = $editor->maybe_exif_rotate(); + } + } + + return array( $editor, $resized, $rotated ); +} + +/** + * Gets the suffix to use for image files based on resizing and rotating. + * + * @since 6.0.0 + * @access private + * + * @param bool|WP_Error Whether the image was resized, or an error if resizing failed. + * @param bool|WP_Error Whether the image was rotated, or an error if rotating failed. + * @return string The suffix to use for the file name, or empty string if none. + */ +function _wp_get_image_suffix( $resized, $rotated ) { + if ( $resized && ! is_wp_error( $resized ) ) { + // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". + // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). + return 'scaled'; + } + + if ( true === $rotated ) { + // Append `-rotated` to the image file name. + return 'rotated'; + } + + if ( is_wp_error( $resized ) || is_wp_error( $rotated ) ) { + // TODO: Log errors. + } + return ''; } /** @@ -434,107 +469,159 @@ function _wp_get_sources_from_meta( $meta ) { * Errors are stored in the returned image metadata array. * * @since 5.3.0 - * @since 6.0.0 Support for generating multiple mime type sub-sizes was added. + * @since 6.0.0 The $mime_type parameter was added. * @access private * * @param array $new_sizes Array defining what sizes to create. * @param string $file Full path to the image file. * @param array $image_meta The attachment meta data array. * @param int $attachment_id Attachment ID to process. + * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the image mime of $file. * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. */ -function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { +function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mime_type = '' ) { if ( empty( $image_meta ) || ! is_array( $image_meta ) ) { // Not an image attachment. return array(); } - // Calculate the primary (first) and additional mime types to generate. - $mime_types_to_generate = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); - foreach ( $mime_types_to_generate as $output_index => $output_mime_type ) { - // For the primary image, check if any of the new sizes already exist. - if ( 0 === $output_index ) { - if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { - foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - /* - * Only checks "size name" so we don't override existing images even if the dimensions - * don't match the currently defined size with the same name. - * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. - */ - if ( array_key_exists( $size_name, $new_sizes ) ) { - unset( $new_sizes[ $size_name ] ); - } + + if ( ! $mime_type ) { + $mime_type = wp_get_image_mime( $file ); + } + + // Check if any of the new sizes already exist. + if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { + foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { + /* + * Only checks "size name" so we don't override existing images even if the dimensions + * don't match the currently defined size with the same name. + * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. + */ + if ( array_key_exists( $size_name, $new_sizes ) ) { + // Unset the size if it is either the required mime type already exists either as main mime type or + // within sources. + if ( $size_meta['mime-type'] === $mime_type || isset( $size_meta['sources'][ $mime_type ] ) ) { + unset( $new_sizes[ $size_name ] ); } - } else { - $image_meta['sizes'] = array(); } } + } else { + $image_meta['sizes'] = array(); + } - if ( empty( $new_sizes ) ) { - // Nothing to do... - return $image_meta; - } + if ( empty( $new_sizes ) ) { + // Nothing to do... + return $image_meta; + } - /* - * Sort the image sub-sizes in order of priority when creating them. - * This ensures there is an appropriate sub-size the user can access immediately - * even when there was an error and not all sub-sizes were created. - */ - $priority = array( - 'medium' => null, - 'large' => null, - 'thumbnail' => null, - 'medium_large' => null, - ); + /* + * Sort the image sub-sizes in order of priority when creating them. + * This ensures there is an appropriate sub-size the user can access immediately + * even when there was an error and not all sub-sizes were created. + */ + $priority = array( + 'medium' => null, + 'large' => null, + 'thumbnail' => null, + 'medium_large' => null, + ); - $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); + $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - $editor = wp_get_image_editor( $file, array( 'mime_type' => $output_mime_type ) ); + $editor = wp_get_image_editor( $file, array( 'mime_type' => $mime_type ) ); - if ( is_wp_error( $editor ) ) { - // The image cannot be edited. - return $image_meta; - } + if ( is_wp_error( $editor ) ) { + // The image cannot be edited. + return $image_meta; + } - $editor->set_mime_type( $output_mime_type ); + $editor->set_mime_type( $mime_type ); - // If stored EXIF data exists, rotate the source image before creating sub-sizes. - if ( ! empty( $image_meta['image_meta'] ) ) { - $rotated = $editor->maybe_exif_rotate(); + // If stored EXIF data exists, rotate the source image before creating sub-sizes. + if ( ! empty( $image_meta['image_meta'] ) ) { + $rotated = $editor->maybe_exif_rotate(); - if ( is_wp_error( $rotated ) ) { - // TODO: Log errors. - } + if ( is_wp_error( $rotated ) ) { + // TODO: Log errors. } + } - if ( method_exists( $editor, 'make_subsize' ) ) { - foreach ( $new_sizes as $new_size_name => $new_size_data ) { - $new_size_meta = $editor->make_subsize( $new_size_data ); - - if ( is_wp_error( $new_size_meta ) ) { - // TODO: Log errors. - } else { - // Save the primary mime type sizes meta value. - if ( 0 === $output_index ) { - $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - } - // Update the sources array with the new sub-size. - $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( method_exists( $editor, 'make_subsize' ) ) { + foreach ( $new_sizes as $new_size_name => $new_size_data ) { + $new_size_meta = $editor->make_subsize( $new_size_data ); + + if ( is_wp_error( $new_size_meta ) ) { + // TODO: Log errors. + } else { + // Save the size meta value. + if ( ! isset( $image_meta['sizes'][ $new_size_name ] ) ) { + $image_meta['sizes'][ $new_size_name ] = $new_size_meta; } + if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { + $image_meta['sizes'][ $new_size_name ]['sources'] = array(); + } + $image_meta['sizes'][ $new_size_name ]['sources'][ $mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); } - } else { - // Fall back to `$editor->multi_resize()`. - $created_sizes = $editor->multi_resize( $new_sizes ); + } + } else { + // Fall back to `$editor->multi_resize()`. + $created_sizes = $editor->multi_resize( $new_sizes ); - if ( ! empty( $created_sizes ) ) { - // Save the primary mime type sizes meta value. - if ( 0 === $output_index ) { - $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); + if ( ! empty( $created_sizes ) ) { + foreach ( $created_sizes as $created_size_name => $created_size_meta ) { + if ( ! isset( $image_meta['sizes'][ $created_size_name ] ) ) { + $image_meta['sizes'][ $created_size_name ] = $created_size_meta; } - // Update the sources array with the new sub-size. - $image_meta['sizes'][ $new_size_name ]['sources'][ $output_mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); - wp_update_attachment_metadata( $attachment_id, $image_meta ); + if ( ! isset( $image_meta['sizes'][ $created_size_name ]['sources'] ) ) { + $image_meta['sizes'][ $created_size_name ]['sources'] = array(); + } + $image_meta['sizes'][ $created_size_name ]['sources'][ $mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); } + wp_update_attachment_metadata( $attachment_id, $image_meta ); + } + } + + return $image_meta; +} + +/** + * Low-level function to create full-size images in additional mime types. + * + * Updates the image meta after each mime type image is created. + * + * @since 6.0.0 + * @access private + * + * @param array $new_mime_types Array defining what mime types to create. + * @param string $file Full path to the image file. + * @param array $image_meta The attachment meta data array. + * @param int $attachment_id Attachment ID to process. + * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. + */ +function _wp_make_additional_mime_types( $new_mime_types, $file, $image_meta, $attachment_id ) { + $imagesize = array( + $image_meta['width'], + $image_meta['height'], + ); + $exif_meta = isset( $image_meta['image_meta'] ) ? $image_meta['image_meta'] : null; + + foreach ( $new_mime_types as $mime_type ) { + list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type ); + if ( is_wp_error( $editor ) ) { + // The image cannot be edited. + continue; + } + + $suffix = _wp_get_image_suffix( $resized, $rotated ); + + $saved = $editor->save( $editor->generate_filename( $suffix ) ); + + if ( is_wp_error( $saved ) ) { + // TODO: Log errors. + } else { + $image_meta['sources'][ $mime_type ] = _wp_get_sources_from_meta( $saved ); + wp_update_attachment_metadata( $attachment_id, $image_meta ); } } @@ -723,6 +810,7 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) { */ return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' ); } + /** * Convert a fraction string to a decimal. * @@ -1231,8 +1319,8 @@ function _copy_image_file( $attachment_id ) { * @since 6.0.0 * * @param $attachment_id int The attachment ID. - * @return array> An array of valid mime types, where the key is the source file mime type and the - * value is one or more mime file types to generate. + * @return array An array of valid mime types, where the key is the source file mime type and the list of mime types to + * generate. */ function wp_upload_image_mime_transforms( $attachment_id ) { $image_mime_transforms = array( @@ -1260,7 +1348,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * * @param string $file Full path to the image file. * @param int $attachment_id Attachment ID to process. - * @return array> An array with the primary mime type and the additional mime types. + * @return array An array with two entries, the primary mime type and the list of additional mime types. */ function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) { $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); @@ -1292,8 +1380,8 @@ function( $mime_type ) { if ( empty( $primary_mime_type ) ) { $primary_mime_type = $original_mime_type; } - return array_merge( - array( $primary_mime_type ), - $additional_mime_types + return array( + $primary_mime_type, + $additional_mime_types, ); } From 688d4f9aa6b52555f8f4afd06731ccd04f85dcdf Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Tue, 7 Jun 2022 18:28:56 +0100 Subject: [PATCH 093/102] Treat the manually set mime_type class property like a fallback for the mime_type method parameter. --- src/wp-includes/class-wp-image-editor.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 9427ac714480c..eedc77810a028 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -325,6 +325,10 @@ protected function get_default_quality( $mime_type ) { protected function get_output_format( $filename = null, $mime_type = null ) { $new_ext = null; + if ( ! $mime_type && $this->mime_type_set ) { + $mime_type = $this->mime_type; + } + // By default, assume specified type takes priority. if ( $mime_type ) { $new_ext = $this->get_extension( $mime_type ); @@ -336,18 +340,12 @@ protected function get_output_format( $filename = null, $mime_type = null ) { } else { // If no file specified, grab editor's current extension and mime-type. $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); - $file_mime = $this->mime_type; - // Favor the file mime extension. - $ext = $this->get_extension( $file_mime ); - if ( $this->mime_type_set && $ext !== $file_ext ) { - $new_ext = $ext; - $mime_type = $file_mime; - } + $file_mime = ! $this->mime_type_set ? $this->mime_type : $this->get_mime_type( $file_ext ); } // Check to see if specified mime-type is the same as type implied by // file extension. If so, prefer extension from file. - if ( ! $this->mime_type_set && ( ! $mime_type || ( $file_mime == $mime_type ) ) ) { + if ( ! $mime_type || ( $file_mime == $mime_type ) ) { $mime_type = $file_mime; $new_ext = $file_ext; } From 503134e10e69463ed7c0e3b970d963e4e9fda0fe Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Tue, 7 Jun 2022 18:31:02 +0100 Subject: [PATCH 094/102] Update version numbers to 6.1.0. --- src/wp-admin/includes/image.php | 22 +++++++++++----------- src/wp-includes/class-wp-image-editor.php | 6 ++++-- src/wp-includes/media.php | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index e60f440c585a2..95476395f2050 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -77,7 +77,7 @@ function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $s * Registered sub-sizes that are larger than the image are skipped. * * @since 5.3.0 - * @since 6.0.0 The $mime_type parameter was added. + * @since 6.1.0 The $mime_type parameter was added. * * @param int $attachment_id The image attachment post ID. * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the primary image mime. @@ -153,7 +153,7 @@ function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { * Filters the array of missing image sub-sizes for an uploaded image. * * @since 5.3.0 - * @since 6.0.0 The $mime_type filter parameter was added. + * @since 6.1.0 The $mime_type filter parameter was added. * * @param array[] $missing_sizes Associative array of arrays of image sub-size information for * missing image sizes, keyed by image size name. @@ -247,7 +247,7 @@ function _wp_image_meta_replace_original( $saved_data, $original_file, $image_me * sub-size is created. If there was an error, it is added to the returned image metadata array. * * @since 5.3.0 - * @since 6.0.0 Generates sub-sizes in alternate mime types based on the `wp_image_mime_transforms` filter. + * @since 6.1.0 Generates sub-sizes in alternate mime types based on the `wp_image_mime_transforms` filter. * * @param string $file Full path to the image file. * @param int $attachment_id Attachment ID to process. @@ -343,7 +343,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { /** * Returns a WP_Image_Editor instance where the image file has been scaled and rotated as necessary. * - * @since 6.0.0 + * @since 6.1.0 * @access private * * @param string $file Full path to the image file. @@ -421,7 +421,7 @@ function _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $e /** * Gets the suffix to use for image files based on resizing and rotating. * - * @since 6.0.0 + * @since 6.1.0 * @access private * * @param bool|WP_Error Whether the image was resized, or an error if resizing failed. @@ -449,7 +449,7 @@ function _wp_get_image_suffix( $resized, $rotated ) { /** * Gets a sources array element from a meta. * - * @since 6.0.0 + * @since 6.1.0 * @access private * * @param array $meta The meta to get the source from. @@ -469,7 +469,7 @@ function _wp_get_sources_from_meta( $meta ) { * Errors are stored in the returned image metadata array. * * @since 5.3.0 - * @since 6.0.0 The $mime_type parameter was added. + * @since 6.1.0 The $mime_type parameter was added. * @access private * * @param array $new_sizes Array defining what sizes to create. @@ -590,7 +590,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim * * Updates the image meta after each mime type image is created. * - * @since 6.0.0 + * @since 6.1.0 * @access private * * @param array $new_mime_types Array defining what mime types to create. @@ -1316,7 +1316,7 @@ function _copy_image_file( $attachment_id ) { * For example an `image/jpeg` should be converted into an `image/jpeg` and `image/webp`. The first type * is considered the primary output type for this image. * - * @since 6.0.0 + * @since 6.1.0 * * @param $attachment_id int The attachment ID. * @return array An array of valid mime types, where the key is the source file mime type and the list of mime types to @@ -1331,7 +1331,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { /** * Filter to the output mime types for a given input mime type. * - * @since 6.0.0 + * @since 6.1.0 * * @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type * and the value is one or more mime file types to generate. @@ -1343,7 +1343,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { /** * Extract the primary and additional mime output types for an image from the $image_mime_transforms. * - * @since 6.0.0 + * @since 6.1.0 * @access private * * @param string $file Full path to the image file. diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index eedc77810a028..da8e6d3363378 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -423,7 +423,7 @@ protected function get_output_format( $filename = null, $mime_type = null ) { * Builds an output filename based on current file, and adding proper suffix * * @since 3.5.0 - * @since 6.0.0 Skips adding a suffix when set to an empty string. + * @since 6.1.0 Skips adding a suffix when set to an empty string. * * @param string $suffix Optional. Suffix to add to the filename. Passing null * will result in a 'widthxheight' suffix. Passing @@ -647,7 +647,7 @@ protected static function get_extension( $mime_type = null ) { * * Track that the mime type is set with the mime type set flag. * - * @since 6.0.0 + * @since 6.1.0 * * @param string $mime_type The mime type to set. */ @@ -660,6 +660,8 @@ public function set_mime_type( $mime_type ) { * Reset the mime type to the original file mime type. * * Reset the mime type set flag. + * + * @since 6.1.0 */ public function reset_mime_type() { $this->mime_type = wp_get_image_mime( $this->file ); diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index dfaf31f6a3b3b..5c183c6fae8cb 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1897,7 +1897,7 @@ function wp_filter_content_tags( $content, $context = null ) { /** * Use alternate mime type images in the content output when available. * - * @since 6.0.0 + * @since 6.1.0 * * @param string $image The HTML `img` tag where the attribute should be added. * @param string $context Additional context to pass to the filters. @@ -1922,7 +1922,7 @@ function wp_image_use_alternate_mime_types( $image, $context, $attachment_id ) { * * When outputting images in the content, the first mime type available will be used. * - * @since 6.0.0 + * @since 6.1.0 * * @param array $target_mimes The image output mime type and order. Default is array( 'image/webp', 'image/jpeg' ). * @param int $attachment_id The attachment ID. From 52836e86f99de1ef0ba237287cda885427600a47 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Tue, 7 Jun 2022 18:44:08 +0100 Subject: [PATCH 095/102] Ensure retry mechanism works correctly and also generates full sized images for additional mime types. --- src/wp-admin/includes/image.php | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 95476395f2050..ef4e0208ad29b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -169,6 +169,7 @@ function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { * create them and update the image meta data. * * @since 5.3.0 + * @since 6.1.0 Now supports additional mime types, creating the additional sub-sizes and 'full' sized images. * * @param int $attachment_id The image attachment post ID. * @return array|WP_Error The updated image meta data array or WP_Error object @@ -187,14 +188,33 @@ function wp_update_image_subsizes( $attachment_id ) { return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) ); } } else { - $missing_sizes = wp_get_missing_image_subsizes( $attachment_id ); + // Get the primary and additional mime types to generate. + list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $image_file, $attachment_id ); - if ( empty( $missing_sizes ) ) { - return $image_meta; + // Generate missing 'full' image files for additional mime types. + if ( ! empty( $additional_mime_types ) ) { + if ( isset( $image_meta['sources'] ) ) { + $missing_mime_types = array_diff( $additional_mime_types, array_keys( $image_meta['sources'] ) ); + } else { + $missing_mime_types = $additional_mime_types; + } + if ( ! empty( $missing_mime_types ) ) { + $image_meta = _wp_make_additional_mime_types( $missing_mime_types, $image_file, $image_meta, $attachment_id ); + } } - // This also updates the image meta. - $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id ); + // Generate missing image sub-sizes for each mime type. + $all_mime_types = array_merge( array( $primary_mime_type ), $additional_mime_types ); + foreach ( $all_mime_types as $mime_type ) { + $missing_sizes = wp_get_missing_image_subsizes( $attachment_id, $mime_type ); + + if ( empty( $missing_sizes ) ) { + continue; + } + + // This also updates the image meta. + $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id, $mime_type ); + } } /** This filter is documented in wp-admin/includes/image.php */ @@ -278,7 +298,7 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $image_meta['image_meta'] = $exif_meta; } - // Calculate the primary (first) and additional mime types to generate. + // Get the primary and additional mime types to generate. list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $primary_mime_type ); From d75e58a2a792a1c55d3cd1969fb8053a58746b34 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 14 Jun 2022 09:56:28 -0600 Subject: [PATCH 096/102] fix after merge --- src/wp-includes/media.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index fbec85f138d4e..74bae684fcdaa 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1850,8 +1850,9 @@ function wp_filter_content_tags( $content, $context = null ) { // Add 'decoding=async' attribute unless a 'decoding' attribute is already present. if ( ! str_contains( $filtered_image, ' decoding=' ) ) { $filtered_image = wp_img_tag_add_decoding_attr( $filtered_image, $context ); - // Use alternate mime types when specified and available. + } + // Use alternate mime types when specified and available. if ( $attachment_id > 0 ) { $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $context, $attachment_id ); } From 1589bfdc78b67f574a72f730aa1f29184597c182 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 14 Jun 2022 15:41:14 -0600 Subject: [PATCH 097/102] Fixes for tests --- src/wp-admin/includes/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index ef4e0208ad29b..4a7988e1ed45b 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -142,7 +142,7 @@ function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { continue; } - if ( $size_meta['mime-type'] === $mime_type || isset( $size_meta['sources'][ $mime_type ] ) ) { + if ( ( isset( $size_data['mime-type'] ) && $size_data['mime-type'] === $mime_type ) || isset( $size_data['sources'][ $mime_type ] ) ) { continue; } From 4dbb3e77b797772a225894c615a43572e9fd26c8 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 16 Jun 2022 15:06:50 -0600 Subject: [PATCH 098/102] Handle an empty value for $output_mime_types: only output the original type. --- src/wp-admin/includes/image.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 4a7988e1ed45b..c911d1e7a074c 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -1387,6 +1387,11 @@ function( $mime_type ) { } ); + // Handle an empty value for $output_mime_types: only output the original type. + if ( empty( $output_mime_types ) ) { + return array( $original_mime_type, array() ); + } + // Use original mime type as primary mime type, or alternatively the first one. $primary_mime_type_key = array_search( $original_mime_type, $output_mime_types, true ); if ( false === $primary_mime_type_key ) { @@ -1396,10 +1401,6 @@ function( $mime_type ) { $additional_mime_types = $output_mime_types; list( $primary_mime_type ) = array_splice( $additional_mime_types, $primary_mime_type_key, 1 ); - // Ensure $primary_mime_type is set. - if ( empty( $primary_mime_type ) ) { - $primary_mime_type = $original_mime_type; - } return array( $primary_mime_type, $additional_mime_types, From f24edd16279bfab013df43552fb29afc148f0e33 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 16 Jun 2022 15:10:38 -0600 Subject: [PATCH 099/102] Improve deletion to use sources data when available, falling back to sizeinfo data --- src/wp-admin/includes/image.php | 4 ++-- src/wp-includes/post.php | 25 +++++++++++-------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index c911d1e7a074c..2ad8b43180c2c 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -90,7 +90,7 @@ function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { } if ( ! $mime_type ) { - $mime_type = get_post_mime_type( get_post( $attachment_id ) ); + $mime_type = get_post_mime_type( get_post( $attachment_id ) ); } $registered_sizes = wp_get_registered_image_subsizes(); @@ -506,7 +506,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim } if ( ! $mime_type ) { - $mime_type = wp_get_image_mime( $file ); + $mime_type = wp_get_image_mime( $file ); } // Check if any of the new sizes already exist. diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index f7cdebedc46cb..7e84a9eccd1ba 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6446,30 +6446,27 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } - $primary_mime = isset( $meta['mime_type'] ) ? $meta['mime_type'] : false; - // Remove intermediate and backup images if there are any. if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) { $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) ); foreach ( $meta['sizes'] as $size => $sizeinfo ) { - $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file ); - - if ( ! empty( $intermediate_file ) ) { - $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } - } // Check for alternate size mime types in the sizeinfo['sources'] array to delete. - if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) && $primary_mime ) { + if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { foreach ( $sizeinfo['sources'] as $mime => $properties ) { - if ( $mime === $primary_mime || ! is_array( $properties ) || empty( $properties['file'] ) ) { - continue; + $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; } + } + } else { + // Otherwise, delete files from the sizeinfo data. + $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file ); + + if ( ! empty( $intermediate_file ) ) { + $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file ); - $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { $deleted = false; } From 90c5196add166cd30d31b68c98840e2f3f25e73a Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 17 Jun 2022 10:15:59 -0600 Subject: [PATCH 100/102] Output WebP by default for core sizes --- src/wp-admin/includes/image.php | 92 +++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 17 deletions(-) diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 2ad8b43180c2c..3f9ca23d9f5a1 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -492,11 +492,11 @@ function _wp_get_sources_from_meta( $meta ) { * @since 6.1.0 The $mime_type parameter was added. * @access private * - * @param array $new_sizes Array defining what sizes to create. - * @param string $file Full path to the image file. - * @param array $image_meta The attachment meta data array. - * @param int $attachment_id Attachment ID to process. - * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the image mime of $file. + * @param array $new_sizes Array defining what sizes to create. + * @param string $file Full path to the image file. + * @param array $image_meta The attachment meta data array. + * @param int $attachment_id Attachment ID to process. + * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the image mime of $file. * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. */ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mime_type = '' ) { @@ -505,13 +505,21 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim return array(); } + $original_mime_type = wp_get_image_mime( $file ); + if ( ! $mime_type ) { - $mime_type = wp_get_image_mime( $file ); + $mime_type = $original_mime_type; } + // Check if any of the new sizes already exist. if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { + // Check if the output mime is enabled for this image size. + if ( ! _wp_mime_type_available_for_image_size( $attachment_id, $size_name, $original_mime_type, $mime_type ) ) { + continue; + } + /* * Only checks "size name" so we don't override existing images even if the dimensions * don't match the currently defined size with the same name. @@ -605,6 +613,25 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim return $image_meta; } +/** + * Check whether secondary mime output is available for a given size and mime type. + * + * @since 6.1.0 + * + * @param int $attachment_id Attachment ID. + * @param string $size_name Size name to check. + * @return bool Whether the size is available for the given mime type. + */ +function _wp_mime_type_available_for_image_size( $attachment_id, $size_name, $source_mime, $destination_mime ) { + if ( $source_mime === $destination_mime ) { + return true; + } + $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id, $size_name ); + + return isset( $image_mime_transforms[ $source_mime ] [ $destination_mime ] ); + +} + /** * Low-level function to create full-size images in additional mime types. * @@ -1336,28 +1363,59 @@ function _copy_image_file( $attachment_id ) { * For example an `image/jpeg` should be converted into an `image/jpeg` and `image/webp`. The first type * is considered the primary output type for this image. * + * Called for each uploaded image to determine the list of mime types that should be converted into. Then, + * called again for each image size as they are generated to see if the image should be converted into the mime type + * for that size. + * * @since 6.1.0 * - * @param $attachment_id int The attachment ID. + * @param int $attachment_id The attachment ID. + * @param string $image_size The image size name. Optional. * @return array An array of valid mime types, where the key is the source file mime type and the list of mime types to * generate. */ -function wp_upload_image_mime_transforms( $attachment_id ) { - $image_mime_transforms = array( - 'image/jpeg' => array( 'image/jpeg', 'image/webp' ), - 'image/webp' => array( 'image/webp', 'image/jpeg' ), +function wp_upload_image_mime_transforms( $attachment_id, $image_size ) { + + // For WordPress 6.1, only output WebP by default for core/core theme sizes. + $default_core_sizes = array ( + 'thumbnail', + 'medium', + 'medium_large', + 'large', + // 2x medium_large size. + '1536x1536', + // 2x large size. + '2048x2048', + // Twentyeleven theme. + 'large-feature', + 'small-feature', + // Twentyfourteen theme. + 'twentyfourteen-full-width', + // Twentyseventeen theme. + 'twentyseventeen-featured-image', + 'twentyseventeen-thumbnail-avatar', + // Twentytwenty theme. + 'twentytwenty-fullscreen', ); + $image_mime_transforms = array(); + if ( empty( $image_size ) || in_array( $image_size, $default_core_sizes ) ) { + $image_mime_transforms = array( + 'image/jpeg' => array( 'image/webp' ), + ); + } + /** - * Filter to the output mime types for a given input mime type. + * Filter to the output mime types for a given input mime type and image size. * * @since 6.1.0 * - * @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type - * and the value is one or more mime file types to generate. - * @param int $attachment_id The ID of the attachment where the hook was dispatched. + * @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type + * and the value is one or more mime file types to generate. + * @param int $attachment_id The ID of the attachment where the hook was dispatched. + * @param string $image_size The image size name. Optional. */ - return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id ); + return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id, $image_size ); } /** @@ -1371,7 +1429,7 @@ function wp_upload_image_mime_transforms( $attachment_id ) { * @return array An array with two entries, the primary mime type and the list of additional mime types. */ function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) { - $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id ); + $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id, false ); $original_mime_type = wp_get_image_mime( $file ); $output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type ); From e9a51a8d93480cf7522075c74f9b8563fd80228c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 20 Jun 2023 12:06:08 -0600 Subject: [PATCH 101/102] Change default mapping to WebP, restore trunk --- src/js/_enqueues/vendor/plupload/handlers.js | 2 +- .../_enqueues/vendor/plupload/wp-plupload.js | 2 +- src/wp-admin/includes/image.php | 497 ++++-------------- src/wp-includes/class-wp-image-editor.php | 51 +- src/wp-includes/functions.php | 11 +- src/wp-includes/media.php | 93 +--- src/wp-includes/post.php | 38 +- tests/phpunit/tests/image/editor.php | 406 -------------- tests/phpunit/tests/image/functions.php | 57 +- tests/phpunit/tests/media.php | 24 - 10 files changed, 119 insertions(+), 1062 deletions(-) diff --git a/src/js/_enqueues/vendor/plupload/handlers.js b/src/js/_enqueues/vendor/plupload/handlers.js index 2c35d8cfa4380..e4ff962c9762e 100644 --- a/src/js/_enqueues/vendor/plupload/handlers.js +++ b/src/js/_enqueues/vendor/plupload/handlers.js @@ -486,7 +486,7 @@ jQuery( document ).ready( function( $ ) { times = tryAgainCount[ file.id ]; - if ( times && times > 8 ) { + if ( times && times > 4 ) { /* * The file may have been uploaded and attachment post created, * but post-processing and resizing failed... diff --git a/src/js/_enqueues/vendor/plupload/wp-plupload.js b/src/js/_enqueues/vendor/plupload/wp-plupload.js index 217b3c09e2f54..0fdebf77d1858 100644 --- a/src/js/_enqueues/vendor/plupload/wp-plupload.js +++ b/src/js/_enqueues/vendor/plupload/wp-plupload.js @@ -138,7 +138,7 @@ window.wp = window.wp || {}; times = tryAgainCount[ file.id ]; - if ( times && times > 8 ) { + if ( times && times > 4 ) { /* * The file may have been uploaded and attachment post created, * but post-processing and resizing failed... diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 9f93000ed376e..f937bdc2ff53d 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -77,22 +77,16 @@ function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $s * Registered sub-sizes that are larger than the image are skipped. * * @since 5.3.0 - * @since 6.1.0 The $mime_type parameter was added. * - * @param int $attachment_id The image attachment post ID. - * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the primary image mime. + * @param int $attachment_id The image attachment post ID. * @return array[] Associative array of arrays of image sub-size information for * missing image sizes, keyed by image size name. */ -function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { +function wp_get_missing_image_subsizes( $attachment_id ) { if ( ! wp_attachment_is_image( $attachment_id ) ) { return array(); } - if ( ! $mime_type ) { - $mime_type = get_post_mime_type( get_post( $attachment_id ) ); - } - $registered_sizes = wp_get_registered_image_subsizes(); $image_meta = wp_get_attachment_metadata( $attachment_id ); @@ -135,33 +129,19 @@ function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { * However we keep the old sub-sizes with the previous dimensions * as the image may have been used in an older post. */ - $missing_sizes = array(); - foreach ( $possible_sizes as $size_name => $size_data ) { - if ( ! isset( $image_meta['sizes'][ $size_name ] ) ) { - $missing_sizes[ $size_name ] = $size_data; - continue; - } - - if ( ( isset( $size_data['mime-type'] ) && $size_data['mime-type'] === $mime_type ) || isset( $size_data['sources'][ $mime_type ] ) ) { - continue; - } - - $missing_sizes[ $size_name ] = $size_data; - } + $missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] ); /** * Filters the array of missing image sub-sizes for an uploaded image. * * @since 5.3.0 - * @since 6.1.0 The $mime_type filter parameter was added. * * @param array[] $missing_sizes Associative array of arrays of image sub-size information for * missing image sizes, keyed by image size name. * @param array $image_meta The image meta data. * @param int $attachment_id The image attachment post ID. - * @param string $mime_type The image mime type to get missing sizes for. */ - return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id, $mime_type ); + return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id ); } /** @@ -169,7 +149,6 @@ function wp_get_missing_image_subsizes( $attachment_id, $mime_type = '' ) { * create them and update the image meta data. * * @since 5.3.0 - * @since 6.1.0 Now supports additional mime types, creating the additional sub-sizes and 'full' sized images. * * @param int $attachment_id The image attachment post ID. * @return array|WP_Error The updated image meta data array or WP_Error object @@ -188,33 +167,14 @@ function wp_update_image_subsizes( $attachment_id ) { return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) ); } } else { - // Get the primary and additional mime types to generate. - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $image_file, $attachment_id ); + $missing_sizes = wp_get_missing_image_subsizes( $attachment_id ); - // Generate missing 'full' image files for additional mime types. - if ( ! empty( $additional_mime_types ) ) { - if ( isset( $image_meta['sources'] ) ) { - $missing_mime_types = array_diff( $additional_mime_types, array_keys( $image_meta['sources'] ) ); - } else { - $missing_mime_types = $additional_mime_types; - } - if ( ! empty( $missing_mime_types ) ) { - $image_meta = _wp_make_additional_mime_types( $missing_mime_types, $image_file, $image_meta, $attachment_id ); - } + if ( empty( $missing_sizes ) ) { + return $image_meta; } - // Generate missing image sub-sizes for each mime type. - $all_mime_types = array_merge( array( $primary_mime_type ), $additional_mime_types ); - foreach ( $all_mime_types as $mime_type ) { - $missing_sizes = wp_get_missing_image_subsizes( $attachment_id, $mime_type ); - - if ( empty( $missing_sizes ) ) { - continue; - } - - // This also updates the image meta. - $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id, $mime_type ); - } + // This also updates the image meta. + $image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id ); } /** This filter is documented in wp-admin/includes/image.php */ @@ -262,13 +222,12 @@ function _wp_image_meta_replace_original( $saved_data, $original_file, $image_me } /** - * Creates image mime variations and sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata. + * Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata. * * Intended for use after an image is uploaded. Saves/updates the image metadata after each * sub-size is created. If there was an error, it is added to the returned image metadata array. * * @since 5.3.0 - * @since 6.1.0 Generates sub-sizes in alternate mime types based on the `wp_image_mime_transforms` filter. * * @param string $file Full path to the image file. * @param int $attachment_id Attachment ID to process. @@ -289,7 +248,6 @@ function wp_create_image_subsizes( $file, $attachment_id ) { 'file' => _wp_relative_upload_path( $file ), 'filesize' => wp_filesize( $file ), 'sizes' => array(), - 'sources' => array(), ); // Fetch additional metadata from EXIF/IPTC. @@ -299,103 +257,9 @@ function wp_create_image_subsizes( $file, $attachment_id ) { $image_meta['image_meta'] = $exif_meta; } - // Get the primary and additional mime types to generate. - list( $primary_mime_type, $additional_mime_types ) = _wp_get_primary_and_additional_mime_types( $file, $attachment_id ); - - list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $primary_mime_type ); - if ( is_wp_error( $editor ) ) { - return $image_meta; - } - $suffix = _wp_get_image_suffix( $resized, $rotated ); - - // Save image only if either it was modified or if the primary mime type is different from the original. - if ( ! empty( $suffix ) || $primary_mime_type !== $imagesize['mime'] ) { - $saved = $editor->save( $editor->generate_filename( $suffix ) ); - - if ( ! is_wp_error( $saved ) ) { - $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - - // If the image was rotated update the stored EXIF data. - if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { - $image_meta['image_meta']['orientation'] = 1; - } - } else { - // TODO: Log errors. - } - } - - // Set 'sources' for the primary mime type. - $image_meta['sources'][ $primary_mime_type ] = _wp_get_sources_from_meta( $image_meta ); - - /* - * Initial save of the new metadata. - * At this point the file was uploaded and moved to the uploads directory - * but the image sub-sizes haven't been created yet and the `sizes` array is empty. - */ - wp_update_attachment_metadata( $attachment_id, $image_meta ); - - if ( ! empty( $additional_mime_types ) ) { - $image_meta = _wp_make_additional_mime_types( $additional_mime_types, $file, $image_meta, $attachment_id ); - } - - $new_sizes = wp_get_registered_image_subsizes(); - - /** - * Filters the image sizes automatically generated when uploading an image. - * - * @since 2.9.0 - * @since 4.4.0 Added the `$image_meta` argument. - * @since 5.3.0 Added the `$attachment_id` argument. - * - * @param array $new_sizes Associative array of image sizes to be created. - * @param array $image_meta The image meta data: width, height, file, sizes, etc. - * @param int $attachment_id The attachment post ID for the image. - */ - $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id ); - - $image_meta = _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $primary_mime_type ); - foreach ( $additional_mime_types as $additional_mime_type ) { - $image_meta = _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $additional_mime_type ); - } - - return $image_meta; -} - -/** - * Returns a WP_Image_Editor instance where the image file has been scaled and rotated as necessary. - * - * @since 6.1.0 - * @access private - * - * @param string $file Full path to the image file. - * @param int $attachment_id Attachment ID. - * @param array $imagesize { - * Indexed array of the image width and height in pixels. - * - * @type int $0 The image width. - * @type int $1 The image height. - * } - * @param array|null $exif_meta EXIF metadata if extracted from the image file. - * @param string $mime_type Output mime type. - * @return array Array with three entries: The WP_Image_Editor instance, whether the image was resized, and whether the - * image was rotated. Each entry can alternatively be a WP_Error in case something went wrong. - */ -function _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type ) { - $resized = null; - $rotated = null; - - $editor = wp_get_image_editor( $file, array( 'mime_type' => $mime_type ) ); - if ( is_wp_error( $editor ) ) { - // This image cannot be edited. - return array( $editor, $resized, $rotated ); - } - - if ( ! empty( $mime_type ) ) { - $editor->set_mime_type( $mime_type ); - } - // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736. - if ( 'image/png' !== $mime_type ) { + if ( 'image/png' !== $imagesize['mime'] ) { + /** * Filters the "BIG image" threshold value. * @@ -421,66 +285,96 @@ function _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $e // If the original image's dimensions are over the threshold, // scale the image and use it as the "full" size. - if ( $threshold && ( $imagesize[0] > $threshold || $imagesize[1] > $threshold ) ) { + if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) { + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + // Resize the image. $resized = $editor->resize( $threshold, $threshold ); + $rotated = null; // If there is EXIF data, rotate according to EXIF Orientation. if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) { $resized = $editor->maybe_exif_rotate(); $rotated = $resized; } + + if ( ! is_wp_error( $resized ) ) { + // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". + // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). + $saved = $editor->save( $editor->generate_filename( 'scaled' ) ); + + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); + + // If the image was rotated update the stored EXIF data. + if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } + } else { + // TODO: Log errors. + } + } else { + // TODO: Log errors. + } } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) { // Rotate the whole original image if there is EXIF data and "orientation" is not 1. + + $editor = wp_get_image_editor( $file ); + + if ( is_wp_error( $editor ) ) { + // This image cannot be edited. + return $image_meta; + } + + // Rotate the image. $rotated = $editor->maybe_exif_rotate(); - } - } - return array( $editor, $resized, $rotated ); -} + if ( true === $rotated ) { + // Append `-rotated` to the image file name. + $saved = $editor->save( $editor->generate_filename( 'rotated' ) ); -/** - * Gets the suffix to use for image files based on resizing and rotating. - * - * @since 6.1.0 - * @access private - * - * @param bool|WP_Error Whether the image was resized, or an error if resizing failed. - * @param bool|WP_Error Whether the image was rotated, or an error if rotating failed. - * @return string The suffix to use for the file name, or empty string if none. - */ -function _wp_get_image_suffix( $resized, $rotated ) { - if ( $resized && ! is_wp_error( $resized ) ) { - // Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg". - // This doesn't affect the sub-sizes names as they are generated from the original image (for best quality). - return 'scaled'; - } + if ( ! is_wp_error( $saved ) ) { + $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id ); - if ( true === $rotated ) { - // Append `-rotated` to the image file name. - return 'rotated'; + // Update the stored EXIF data. + if ( ! empty( $image_meta['image_meta']['orientation'] ) ) { + $image_meta['image_meta']['orientation'] = 1; + } + } else { + // TODO: Log errors. + } + } + } } - if ( is_wp_error( $resized ) || is_wp_error( $rotated ) ) { - // TODO: Log errors. - } - return ''; -} + /* + * Initial save of the new metadata. + * At this point the file was uploaded and moved to the uploads directory + * but the image sub-sizes haven't been created yet and the `sizes` array is empty. + */ + wp_update_attachment_metadata( $attachment_id, $image_meta ); -/** - * Gets a sources array element from a meta. - * - * @since 6.1.0 - * @access private - * - * @param array $meta The meta to get the source from. - * @return array The source array element. - */ -function _wp_get_sources_from_meta( $meta ) { - return array( - 'file' => isset( $meta['file'] ) ? wp_basename( $meta['file'] ) : '', - 'filesize' => isset( $meta['filesize'] ) ? $meta['filesize'] : wp_filesize( $meta['path'] ), - ); + $new_sizes = wp_get_registered_image_subsizes(); + + /** + * Filters the image sizes automatically generated when uploading an image. + * + * @since 2.9.0 + * @since 4.4.0 Added the `$image_meta` argument. + * @since 5.3.0 Added the `$attachment_id` argument. + * + * @param array $new_sizes Associative array of image sizes to be created. + * @param array $image_meta The image meta data: width, height, file, sizes, etc. + * @param int $attachment_id The attachment post ID for the image. + */ + $new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id ); + + return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ); } /** @@ -490,48 +384,30 @@ function _wp_get_sources_from_meta( $meta ) { * Errors are stored in the returned image metadata array. * * @since 5.3.0 - * @since 6.1.0 The $mime_type parameter was added. * @access private * - * @param array $new_sizes Array defining what sizes to create. - * @param string $file Full path to the image file. - * @param array $image_meta The attachment meta data array. - * @param int $attachment_id Attachment ID to process. - * @param string $mime_type Optional. The mime type to check for missing sizes. Default is the image mime of $file. + * @param array $new_sizes Array defining what sizes to create. + * @param string $file Full path to the image file. + * @param array $image_meta The attachment meta data array. + * @param int $attachment_id Attachment ID to process. * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. */ -function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mime_type = '' ) { +function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { if ( empty( $image_meta ) || ! is_array( $image_meta ) ) { // Not an image attachment. return array(); } - $original_mime_type = wp_get_image_mime( $file ); - - if ( ! $mime_type ) { - $mime_type = $original_mime_type; - } - - // Check if any of the new sizes already exist. if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) { foreach ( $image_meta['sizes'] as $size_name => $size_meta ) { - // Check if the output mime is enabled for this image size. - if ( ! _wp_mime_type_available_for_image_size( $attachment_id, $size_name, $original_mime_type, $mime_type ) ) { - continue; - } - /* * Only checks "size name" so we don't override existing images even if the dimensions * don't match the currently defined size with the same name. * To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta. */ if ( array_key_exists( $size_name, $new_sizes ) ) { - // Unset the size if it is either the required mime type already exists either as main mime type or - // within sources. - if ( $size_meta['mime-type'] === $mime_type || isset( $size_meta['sources'][ $mime_type ] ) ) { - unset( $new_sizes[ $size_name ] ); - } + unset( $new_sizes[ $size_name ] ); } } } else { @@ -557,15 +433,13 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim $new_sizes = array_filter( array_merge( $priority, $new_sizes ) ); - $editor = wp_get_image_editor( $file, array( 'mime_type' => $mime_type ) ); + $editor = wp_get_image_editor( $file ); if ( is_wp_error( $editor ) ) { // The image cannot be edited. return $image_meta; } - $editor->set_mime_type( $mime_type ); - // If stored EXIF data exists, rotate the source image before creating sub-sizes. if ( ! empty( $image_meta['image_meta'] ) ) { $rotated = $editor->maybe_exif_rotate(); @@ -583,13 +457,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim // TODO: Log errors. } else { // Save the size meta value. - if ( ! isset( $image_meta['sizes'][ $new_size_name ] ) ) { - $image_meta['sizes'][ $new_size_name ] = $new_size_meta; - } - if ( ! isset( $image_meta['sizes'][ $new_size_name ]['sources'] ) ) { - $image_meta['sizes'][ $new_size_name ]['sources'] = array(); - } - $image_meta['sizes'][ $new_size_name ]['sources'][ $mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); + $image_meta['sizes'][ $new_size_name ] = $new_size_meta; wp_update_attachment_metadata( $attachment_id, $image_meta ); } } @@ -598,77 +466,7 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id, $mim $created_sizes = $editor->multi_resize( $new_sizes ); if ( ! empty( $created_sizes ) ) { - foreach ( $created_sizes as $created_size_name => $created_size_meta ) { - if ( ! isset( $image_meta['sizes'][ $created_size_name ] ) ) { - $image_meta['sizes'][ $created_size_name ] = $created_size_meta; - } - if ( ! isset( $image_meta['sizes'][ $created_size_name ]['sources'] ) ) { - $image_meta['sizes'][ $created_size_name ]['sources'] = array(); - } - $image_meta['sizes'][ $created_size_name ]['sources'][ $mime_type ] = _wp_get_sources_from_meta( $new_size_meta ); - } - wp_update_attachment_metadata( $attachment_id, $image_meta ); - } - } - - return $image_meta; -} - -/** - * Check whether secondary mime output is available for a given size and mime type. - * - * @since 6.1.0 - * - * @param int $attachment_id Attachment ID. - * @param string $size_name Size name to check. - * @return bool Whether the size is available for the given mime type. - */ -function _wp_mime_type_available_for_image_size( $attachment_id, $size_name, $source_mime, $destination_mime ) { - if ( $source_mime === $destination_mime ) { - return true; - } - $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id, $size_name ); - - return isset( $image_mime_transforms[ $source_mime ] [ $destination_mime ] ); - -} - -/** - * Low-level function to create full-size images in additional mime types. - * - * Updates the image meta after each mime type image is created. - * - * @since 6.1.0 - * @access private - * - * @param array $new_mime_types Array defining what mime types to create. - * @param string $file Full path to the image file. - * @param array $image_meta The attachment meta data array. - * @param int $attachment_id Attachment ID to process. - * @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing. - */ -function _wp_make_additional_mime_types( $new_mime_types, $file, $image_meta, $attachment_id ) { - $imagesize = array( - $image_meta['width'], - $image_meta['height'], - ); - $exif_meta = isset( $image_meta['image_meta'] ) ? $image_meta['image_meta'] : null; - - foreach ( $new_mime_types as $mime_type ) { - list( $editor, $resized, $rotated ) = _wp_maybe_scale_and_rotate_image( $file, $attachment_id, $imagesize, $exif_meta, $mime_type ); - if ( is_wp_error( $editor ) ) { - // The image cannot be edited. - continue; - } - - $suffix = _wp_get_image_suffix( $resized, $rotated ); - - $saved = $editor->save( $editor->generate_filename( $suffix ) ); - - if ( is_wp_error( $saved ) ) { - // TODO: Log errors. - } else { - $image_meta['sources'][ $mime_type ] = _wp_get_sources_from_meta( $saved ); + $image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes ); wp_update_attachment_metadata( $attachment_id, $image_meta ); } } @@ -1359,110 +1157,3 @@ function _copy_image_file( $attachment_id ) { return $dst_file; } - -/** - * Returns an array with the list of valid mime types that a specific mime type should be converted into. - * For example an `image/jpeg` should be converted into an `image/jpeg` and `image/webp`. The first type - * is considered the primary output type for this image. - * - * Called for each uploaded image to determine the list of mime types that should be converted into. Then, - * called again for each image size as they are generated to see if the image should be converted into the mime type - * for that size. - * - * @since 6.1.0 - * - * @param int $attachment_id The attachment ID. - * @param string $image_size The image size name. Optional. - * @return array An array of valid mime types, where the key is the source file mime type and the list of mime types to - * generate. - */ -function wp_upload_image_mime_transforms( $attachment_id, $image_size ) { - - // For WordPress 6.1, only output WebP by default for core/core theme sizes. - $default_core_sizes = array ( - 'thumbnail', - 'medium', - 'medium_large', - 'large', - // 2x medium_large size. - '1536x1536', - // 2x large size. - '2048x2048', - // Twentyeleven theme. - 'large-feature', - 'small-feature', - // Twentyfourteen theme. - 'twentyfourteen-full-width', - // Twentyseventeen theme. - 'twentyseventeen-featured-image', - 'twentyseventeen-thumbnail-avatar', - // Twentytwenty theme. - 'twentytwenty-fullscreen', - ); - $image_mime_transforms = array(); - if ( empty( $image_size ) || in_array( $image_size, $default_core_sizes ) ) { - $image_mime_transforms = array( - 'image/jpeg' => array( 'image/webp' ), - ); - } - - - /** - * Filter to the output mime types for a given input mime type and image size. - * - * @since 6.1.0 - * - * @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type - * and the value is one or more mime file types to generate. - * @param int $attachment_id The ID of the attachment where the hook was dispatched. - * @param string $image_size The image size name. Optional. - */ - return (array) apply_filters( 'wp_upload_image_mime_transforms', $image_mime_transforms, $attachment_id, $image_size ); -} - -/** - * Extract the primary and additional mime output types for an image from the $image_mime_transforms. - * - * @since 6.1.0 - * @access private - * - * @param string $file Full path to the image file. - * @param int $attachment_id Attachment ID to process. - * @return array An array with two entries, the primary mime type and the list of additional mime types. - */ -function _wp_get_primary_and_additional_mime_types( $file, $attachment_id ) { - $image_mime_transforms = wp_upload_image_mime_transforms( $attachment_id, false ); - $original_mime_type = wp_get_image_mime( $file ); - $output_mime_types = isset( $image_mime_transforms[ $original_mime_type ] ) ? $image_mime_transforms[ $original_mime_type ] : array( $original_mime_type ); - - // Exclude any output mime types that the system doesn't support. - $output_mime_types = array_filter( - $output_mime_types, - function( $mime_type ) { - return wp_image_editor_supports( - array( - 'mime_type' => $mime_type, - ) - ); - } - ); - - // Handle an empty value for $output_mime_types: only output the original type. - if ( empty( $output_mime_types ) ) { - return array( $original_mime_type, array() ); - } - - // Use original mime type as primary mime type, or alternatively the first one. - $primary_mime_type_key = array_search( $original_mime_type, $output_mime_types, true ); - if ( false === $primary_mime_type_key ) { - $primary_mime_type_key = 0; - } - // Split output mime types into primary mime type and additional mime types. - $additional_mime_types = $output_mime_types; - list( $primary_mime_type ) = array_splice( $additional_mime_types, $primary_mime_type_key, 1 ); - - return array( - $primary_mime_type, - $additional_mime_types, - ); -} diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index da85265587fd4..53bdae40a3db6 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -16,7 +16,6 @@ abstract class WP_Image_Editor { protected $file = null; protected $size = null; protected $mime_type = null; - protected $mime_type_set = false; protected $output_mime_type = null; protected $default_mime_type = 'image/jpeg'; protected $quality = false; @@ -336,10 +335,6 @@ protected function get_default_quality( $mime_type ) { protected function get_output_format( $filename = null, $mime_type = null ) { $new_ext = null; - if ( ! $mime_type && $this->mime_type_set ) { - $mime_type = $this->mime_type; - } - // By default, assume specified type takes priority. if ( $mime_type ) { $new_ext = $this->get_extension( $mime_type ); @@ -351,7 +346,7 @@ protected function get_output_format( $filename = null, $mime_type = null ) { } else { // If no file specified, grab editor's current extension and mime-type. $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); - $file_mime = ! $this->mime_type_set ? $this->mime_type : $this->get_mime_type( $file_ext ); + $file_mime = $this->mime_type; } // Check to see if specified mime-type is the same as type implied by @@ -380,7 +375,7 @@ protected function get_output_format( $filename = null, $mime_type = null ) { * @param string $filename Path to the image. * @param string $mime_type The source image mime type. */ - $output_format = apply_filters( 'image_editor_output_format', array(), $filename, $mime_type ); + $output_format = apply_filters( 'image_editor_output_format', get_default_image_editor_output_format(), $filename, $mime_type ); if ( isset( $output_format[ $mime_type ] ) && $this->supports_mime_type( $output_format[ $mime_type ] ) @@ -434,18 +429,15 @@ protected function get_output_format( $filename = null, $mime_type = null ) { * Builds an output filename based on current file, and adding proper suffix * * @since 3.5.0 - * @since 6.1.0 Skips adding a suffix when set to an empty string. * - * @param string $suffix Optional. Suffix to add to the filename. Passing null - * will result in a 'widthxheight' suffix. Passing - * an empty string will result in no suffix. + * @param string $suffix * @param string $dest_path * @param string $extension * @return string filename */ public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) { // $suffix will be appended to the destination filename, just before the extension. - if ( null === $suffix ) { + if ( ! $suffix ) { $suffix = $this->get_suffix(); } @@ -466,13 +458,7 @@ public function generate_filename( $suffix = null, $dest_path = null, $extension } } - if ( empty( $suffix ) ) { - $suffix = ''; - } else { - $suffix = "-{$suffix}"; - } - - return trailingslashit( $dir ) . "{$name}{$suffix}.{$new_ext}"; + return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; } /** @@ -652,30 +638,5 @@ protected static function get_extension( $mime_type = null ) { return wp_get_default_extension_for_mime_type( $mime_type ); } - - /** - * Set the editor mime type, useful when outputting alternate mime types. - * - * Track that the mime type is set with the mime type set flag. - * - * @since 6.1.0 - * - * @param string $mime_type The mime type to set. - */ - public function set_mime_type( $mime_type ) { - $this->mime_type = $mime_type; - $this->mime_type_set = true; - } - - /** - * Reset the mime type to the original file mime type. - * - * Reset the mime type set flag. - * - * @since 6.1.0 - */ - public function reset_mime_type() { - $this->mime_type = wp_get_image_mime( $this->file ); - $this->mime_type_set = false; - } } + diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 10ad8e8b5e589..bf2c7202aed75 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -2692,7 +2692,7 @@ function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) */ if ( $is_image ) { /** This filter is documented in wp-includes/class-wp-image-editor.php */ - $output_formats = apply_filters( 'image_editor_output_format', array(), $_dir . $filename, $mime_type ); + $output_formats = apply_filters( 'image_editor_output_format', get_default_image_editor_output_format(), $_dir . $filename, $mime_type ); $alt_types = array(); if ( ! empty( $output_formats[ $mime_type ] ) ) { @@ -2760,6 +2760,15 @@ function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) } } + /** + * Get the default output format. + */ + function get_default_image_editor_output_format() { + return array ( + 'image/jpeg' => 'image/webp', + ); + } + /** * Filters the result when generating a unique file name. * diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index ce87ed58fa88c..96668c156c7c8 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -1867,11 +1867,6 @@ function wp_filter_content_tags( $content, $context = null ) { $filtered_image = wp_img_tag_add_decoding_attr( $filtered_image, $context ); } - // Use alternate mime types when specified and available. - if ( $attachment_id > 0 ) { - $filtered_image = wp_image_use_alternate_mime_types( $filtered_image, $context, $attachment_id ); - } - /** * Filters an img tag within the content for a given context. * @@ -1918,92 +1913,6 @@ function wp_filter_content_tags( $content, $context = null ) { return $content; } -/** - * Use alternate mime type images in the content output when available. - * - * @since 6.1.0 - * - * @param string $image The HTML `img` tag where the attribute should be added. - * @param string $context Additional context to pass to the filters. - * @param int $attachment_id The attachment ID. - * @return string Converted `img` tag with `loading` attribute added. - */ -function wp_image_use_alternate_mime_types( $image, $context, $attachment_id ) { - $metadata = wp_get_attachment_metadata( $attachment_id ); - if ( empty( $metadata['file'] ) ) { - return $image; - } - - // Only alter images with a `sources` attribute - if ( empty( $metadata['sources'] ) ) { - return $image; - }; - - $target_mimes = array( 'image/webp', 'image/jpeg' ); - - /** - * Filter the content image mime type output selection and order. - * - * When outputting images in the content, the first mime type available will be used. - * - * @since 6.1.0 - * - * @param array $target_mimes The image output mime type and order. Default is array( 'image/webp', 'image/jpeg' ). - * @param int $attachment_id The attachment ID. - * @param string $context Additional context to pass to the filters. - * @return array The filtered output mime type and order. Return an empty array to skip mime type substitution. - */ - $target_mimes = apply_filters( 'wp_content_image_mimes', $target_mimes, $attachment_id, $context ); - - if ( false === $target_mimes ) { - return $image; - } - - // Find the appropriate size for the provided URL in the first available mime type. - foreach ( $target_mimes as $target_mime ) { - if ( ! isset( $metadata['sources'][ $target_mime ] ) || empty( $metadata['sources'][ $target_mime ]['file'] ) ) { - continue; - } - - // Go through each image and replace with the first available mime type version. - foreach ( $metadata['sizes'] as $name => $size_data ) { - // Check if size has a file. - if ( empty( $size_data['file'] ) ) { - continue; - } - - // Check if size has a source in the desired mime type. - if ( empty( $size_data['sources'][ $target_mime ]['file'] ) ) { - continue; - } - $target_file = $size_data['sources'][ $target_mime ]['file']; - - // Replace the existing output image for this size. - $src_filename = wp_basename( $size_data['file'] ); - - // This is the same as the file we want to replace nothing to do here. - if ( $target_file === $src_filename ) { - continue; - } - - // Found a match, replace with the new filename and stop searching. - $image = str_replace( $src_filename, $size_data['sources'][ $target_mime ]['file'], $image ); - continue; - } - - // Handle full size image replacement. - $src_filename = wp_basename( $metadata['file'] ); - - // This is the same as the file we want to replace nothing else to do here. - if ( $metadata['sources'][ $target_mime ]['file'] === $src_filename ) { - return $image; - } - - $image = str_replace( $src_filename, $metadata['sources'][ $target_mime ]['file'], $image ); - } - return $image; -} - /** * Adds `loading` attribute to an `img` HTML tag. * @@ -4011,7 +3920,7 @@ function wp_get_image_editor( $path, $args = array() ) { // Check and set the output mime type mapped to the input type. if ( isset( $args['mime_type'] ) ) { /** This filter is documented in wp-includes/class-wp-image-editor.php */ - $output_format = apply_filters( 'image_editor_output_format', array(), $path, $args['mime_type'] ); + $output_format = apply_filters( 'image_editor_output_format', get_default_image_editor_output_format(), $path, $args['mime_type'] ); if ( isset( $output_format[ $args['mime_type'] ] ) ) { $args['output_mime_type'] = $output_format[ $args['mime_type'] ]; } diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index fc9f5698eb6f2..1d543fe9860e5 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6335,25 +6335,13 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) ); foreach ( $meta['sizes'] as $size => $sizeinfo ) { + $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file ); - // Check for alternate size mime types in the sizeinfo['sources'] array to delete. - if ( isset( $sizeinfo['sources'] ) && is_array( $sizeinfo['sources'] ) ) { - foreach ( $sizeinfo['sources'] as $mime => $properties ) { - $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } - } - } else { - // Otherwise, delete files from the sizeinfo data. - $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file ); - - if ( ! empty( $intermediate_file ) ) { - $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file ); + if ( ! empty( $intermediate_file ) ) { + $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } + if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { + $deleted = false; } } } @@ -6375,22 +6363,6 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { } } - // Check for alternate full size mime types in the root sources array to delete. - if ( isset( $meta['sources'] ) && is_array( $meta['sources'] ) ) { - $sources = $meta['sources']; - array_shift( $sources ); - foreach ( $sources as $mime => $properties ) { - if ( $mime === $primary_mime || ! is_array( $properties ) || empty( $properties['file'] ) ) { - continue; - } - - $intermediate_file = str_replace( wp_basename( $file ), $properties['file'], $file ); - if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) { - $deleted = false; - } - } - } - if ( is_array( $backup_sizes ) ) { $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) ); diff --git a/tests/phpunit/tests/image/editor.php b/tests/phpunit/tests/image/editor.php index 1029bc9f5c62a..6e800deb7af12 100644 --- a/tests/phpunit/tests/image/editor.php +++ b/tests/phpunit/tests/image/editor.php @@ -363,411 +363,5 @@ public function data_wp_get_webp_info() { ), ); } - /** - * Create the original image mime type when the image is uploaded - * - * @dataProvider provider_image_with_default_behaviors_during_upload - * - * @since 6.0.0 - */ - public function it_should_create_the_original_image_mime_type_when_the_image_is_uploaded( $file_location, $expected_mime, $targeted_mime ) { - $attachment_id = $this->factory->attachment->create_upload_object( $file_location ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - - $this->assertIsArray( $metadata ); - foreach ( $metadata['sizes'] as $size_name => $properties ) { - $this->assertArrayHasKey( 'sources', $properties ); - $this->assertIsArray( $properties['sources'] ); - $this->assertArrayHasKey( $expected_mime, $properties['sources'] ); - $this->assertArrayHasKey( 'filesize', $properties['sources'][ $expected_mime ] ); - $this->assertArrayHasKey( 'file', $properties['sources'][ $expected_mime ] ); - $this->assertArrayHasKey( $targeted_mime, $properties['sources'] ); - $this->assertArrayHasKey( 'filesize', $properties['sources'][ $targeted_mime ] ); - $this->assertArrayHasKey( 'file', $properties['sources'][ $targeted_mime ] ); - } - } - - public function provider_image_with_default_behaviors_during_upload() { - yield 'JPEG image' => array( - DIR_TESTDATA . '/images/test-image.jpg', - 'image/jpeg', - 'image/webp', - ); - - yield 'WebP image' => array( - DIR_TESTDATA . '/images/webp-lossy.webp', - 'image/webp', - 'image/jpeg', - ); - } - - /** - * Not create the sources property if no transform is provided - * - * @since 6.0.0 - */ - public function it_should_not_create_the_sources_property_if_no_transform_is_provided() { - add_filter( 'webp_uploads_supported_image_mime_transforms', '__return_empty_array' ); - - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - - $this->assertIsArray( $metadata ); - foreach ( $metadata['sizes'] as $size_name => $properties ) { - $this->assertArrayNotHasKey( 'sources', $properties ); - } - } - - /** - * Create the sources property when no transform is available - * - * @since 6.0.0 - */ - public function it_should_create_the_sources_property_when_no_transform_is_available() { - add_filter( - 'webp_uploads_supported_image_mime_transforms', - function () { - return array( 'image/jpeg' => array() ); - } - ); - - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - - $this->assertIsArray( $metadata ); - foreach ( $metadata['sizes'] as $size_name => $properties ) { - $this->assertArrayHasKey( 'sources', $properties ); - $this->assertIsArray( $properties['sources'] ); - $this->assertArrayHasKey( 'image/jpeg', $properties['sources'] ); - $this->assertArrayHasKey( 'filesize', $properties['sources']['image/jpeg'] ); - $this->assertArrayHasKey( 'file', $properties['sources']['image/jpeg'] ); - $this->assertArrayNotHasKey( 'image/webp', $properties['sources'] ); - } - } - - /** - * Not create the sources property if the mime is not specified on the transforms images - * - * @since 6.0.0 - */ - public function it_should_not_create_the_sources_property_if_the_mime_is_not_specified_on_the_transforms_images() { - add_filter( - 'webp_uploads_supported_image_mime_transforms', - function () { - return array( 'image/jpeg' => array() ); - } - ); - - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/webp-lossy.webp' - ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - - $this->assertIsArray( $metadata ); - foreach ( $metadata['sizes'] as $size_name => $properties ) { - $this->assertArrayNotHasKey( 'sources', $properties ); - } - } - - /** - * Create a WebP version with all the required properties - * - * @since 6.0.0 - */ - public function it_should_create_a_webp_version_with_all_the_required_properties() { - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - $this->assertArrayHasKey( 'sources', $metadata['sizes']['thumbnail'] ); - $this->assertArrayHasKey( 'image/jpeg', $metadata['sizes']['thumbnail']['sources'] ); - $this->assertArrayHasKey( 'filesize', $metadata['sizes']['thumbnail']['sources']['image/jpeg'] ); - $this->assertArrayHasKey( 'file', $metadata['sizes']['thumbnail']['sources']['image/jpeg'] ); - $this->assertArrayHasKey( 'image/webp', $metadata['sizes']['thumbnail']['sources'] ); - $this->assertArrayHasKey( 'filesize', $metadata['sizes']['thumbnail']['sources']['image/webp'] ); - $this->assertArrayHasKey( 'file', $metadata['sizes']['thumbnail']['sources']['image/webp'] ); - $this->assertStringEndsNotWith( '.jpeg', $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ); - $this->assertStringEndsWith( '.webp', $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ); - } - - /** - * Remove `scaled` suffix from the generated filename - * - * @since 6.0.0 - */ - public function it_should_remove_scaled_suffix_from_the_generated_filename() { - // The leafs image is 1080 pixels wide with this filter we ensure a -scaled version is created. - add_filter( - 'big_image_size_threshold', - function () { - return 850; - } - ); - - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - $metadata = wp_get_attachment_metadata( $attachment_id ); - $this->assertStringEndsWith( '-scaled.jpg', get_attached_file( $attachment_id ) ); - $this->assertArrayHasKey( 'image/webp', $metadata['sizes']['medium']['sources'] ); - $this->assertStringEndsNotWith( '-scaled.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); - $this->assertStringEndsWith( '-300x200.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); - } - - /** - * Remove the generated webp images when the attachment is deleted - * - * @since 6.0.0 - */ - public function it_should_remove_the_generated_webp_images_when_the_attachment_is_deleted() { - // Make sure no editor is available. - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $file = get_attached_file( $attachment_id, true ); - $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - - $this->assertIsString( $file ); - $this->assertFileExists( $file ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - $sizes = array( 'thumbnail', 'medium' ); - - foreach ( $sizes as $size_name ) { - $this->assertArrayHasKey( 'image/webp', $metadata['sizes'][ $size_name ]['sources'] ); - $this->assertArrayHasKey( 'file', $metadata['sizes'][ $size_name ]['sources']['image/webp'] ); - $this->assertFileExists( - path_join( $dirname, $metadata['sizes'][ $size_name ]['sources']['image/webp']['file'] ) - ); - } - - wp_delete_attachment( $attachment_id ); - - foreach ( $sizes as $size_name ) { - $this->assertFileDoesNotExist( - path_join( $dirname, $metadata['sizes'][ $size_name ]['sources']['image/webp']['file'] ) - ); - } - } - - /** - * Remove the attached WebP version if the attachment is force deleted but empty trash day is not defined - * - * @since 6.0.0 - */ - public function it_should_remove_the_attached_webp_version_if_the_attachment_is_force_deleted_but_empty_trash_day_is_not_defined() { - // Make sure no editor is available. - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $file = get_attached_file( $attachment_id, true ); - $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - - $this->assertIsString( $file ); - $this->assertFileExists( $file ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - - $this->assertFileExists( - path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) - ); - - wp_delete_attachment( $attachment_id, true ); - - $this->assertFileDoesNotExist( - path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) - ); - } - - /** - * Remove the WebP version of the image if the image is force deleted and empty trash days is set to zero - * - * @since 6.0.0 - */ - public function it_should_remove_the_webp_version_of_the_image_if_the_image_is_force_deleted_and_empty_trash_days_is_set_to_zero() { - // Make sure no editor is available. - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $file = get_attached_file( $attachment_id, true ); - $dirname = pathinfo( $file, PATHINFO_DIRNAME ); - - $this->assertIsString( $file ); - $this->assertFileExists( $file ); - - $metadata = wp_get_attachment_metadata( $attachment_id ); - - $this->assertFileExists( - path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) - ); - - define( 'EMPTY_TRASH_DAYS', 0 ); - - wp_delete_attachment( $attachment_id, true ); - - $this->assertFileDoesNotExist( - path_join( $dirname, $metadata['sizes']['thumbnail']['sources']['image/webp']['file'] ) - ); - } - - /** - * Avoid the change of URLs of images that are not part of the media library - * - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_avoid_the_change_of_urls_of_images_that_are_not_part_of_the_media_library() { - $paragraph = '

Donec accumsan, sapien et , id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.

'; - - $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) ); - } - - /** - * Avoid replacing not existing attachment IDs - * - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_avoid_replacing_not_existing_attachment_i_ds() { - $paragraph = '

Donec accumsan, sapien et , id commodo nisi sapien et est. Mauris nisl odio, iaculis vitae pellentesque nec.

'; - - $this->assertSame( $paragraph, webp_uploads_update_image_references( $paragraph ) ); - } - - /** - * Prevent replacing a WebP image - * - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_prevent_replacing_a_webp_image() { - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/webp-lossy.webp' - ); - - $tag = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) ); - - $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - } - - /** - * Prevent replacing a jpg image if the image does not have the target class name - * - * @since 6.0.0 - */ - public function it_should_prevent_replacing_a_jpg_image_if_the_image_does_not_have_the_target_class_name() { - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $tag = wp_get_attachment_image( $attachment_id, 'medium' ); - - $this->assertSame( $tag, webp_uploads_update_image_references( $tag ) ); - } - - /** - * Replace the references to a JPG image to a WebP version - * - * @dataProvider provider_replace_images_with_different_extensions - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_replace_the_references_to_a_jpg_image_to_a_webp_version( $image_path ) { - $attachment_id = $this->factory->attachment->create_upload_object( $image_path ); - - $tag = wp_get_attachment_image( $attachment_id, 'medium', false, array( 'class' => "wp-image-{$attachment_id}" ) ); - $expected_tag = $tag; - $metadata = wp_get_attachment_metadata( $attachment_id ); - foreach ( $metadata['sizes'] as $size => $properties ) { - $expected_tag = str_replace( $properties['sources']['image/jpeg']['file'], $properties['sources']['image/webp']['file'], $expected_tag ); - } - - $this->assertNotEmpty( $expected_tag ); - $this->assertNotSame( $tag, $expected_tag ); - $this->assertSame( $expected_tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - } - - public function provider_replace_images_with_different_extensions() { - yield 'An image with a .jpg extension' => array( DIR_TESTDATA . '/images/test-image.jpg' ); - yield 'An image with a .jpeg extension' => array( DIR_TESTDATA . '/images/test-image.jpeg' ); - } - - /** - * Contain the full image size from the original mime - * - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_contain_the_full_image_size_from_the_original_mime() { - $attachment_id = $this->factory->attachment->create_upload_object( - DIR_TESTDATA . '/images/test-image.jpg' - ); - - $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); - - $expected = array( - 'ext' => 'jpg', - 'type' => 'image/jpeg', - ); - $this->assertSame( $expected, wp_check_filetype( get_attached_file( $attachment_id ) ) ); - $this->assertContains( wp_basename( get_attached_file( $attachment_id ) ), webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - } - - /** - * Prevent replacing an image with no available sources - * - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_prevent_replacing_an_image_with_no_available_sources() { - add_filter( 'webp_uploads_supported_image_mime_transforms', '__return_empty_array' ); - - $attachment_id = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/test-image.jpg' ); - - $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); - $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - } - - /** - * Prevent update not supported images with no available sources - * - * @dataProvider data_provider_not_supported_webp_images - * @group webp_uploads_update_image_references - * - * @since 6.0.0 - */ - public function it_should_prevent_update_not_supported_images_with_no_available_sources( $image_path ) { - $attachment_id = $this->factory->attachment->create_upload_object( $image_path ); - - $this->assertIsNumeric( $attachment_id ); - $tag = wp_get_attachment_image( $attachment_id, 'full', false, array( 'class' => "wp-image-{$attachment_id}" ) ); - - $this->assertSame( $tag, webp_uploads_img_tag_update_mime_type( $tag, 'the_content', $attachment_id ) ); - } - - public function data_provider_not_supported_webp_images() { - yield 'PNG image' => array( DIR_TESTDATA . '/images/test-image.png' ); - yield 'GIFT image' => array( DIR_TESTDATA . '/images/test-image.gif' ); - } - - } diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php index 73a6ac9b1a239..356b2dceacd8c 100644 --- a/tests/phpunit/tests/image/functions.php +++ b/tests/phpunit/tests/image/functions.php @@ -747,9 +747,6 @@ public function test_wp_generate_attachment_metadata_pdf() { $this->markTestSkipped( 'Rendering PDFs is not supported on this system.' ); } - // Use legacy JPEG output. - add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); - $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf'; $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf'; copy( $orig_file, $test_file ); @@ -771,6 +768,7 @@ public function test_wp_generate_attachment_metadata_pdf() { $this->assertIsInt( $attachment_id, 'Could not create attachment - non-integer response returned.' ); $temp_dir = get_temp_dir(); + $metadata = wp_generate_attachment_metadata( $attachment_id, $test_file ); $expected = array( @@ -788,12 +786,6 @@ public function test_wp_generate_attachment_metadata_pdf() { 'height' => 300, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-232x300.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-232x300.jpg' ), - ), - ), ), 'large' => array( 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', @@ -801,12 +793,6 @@ public function test_wp_generate_attachment_metadata_pdf() { 'height' => 1024, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), - ), - ), ), 'thumbnail' => array( 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', @@ -814,12 +800,6 @@ public function test_wp_generate_attachment_metadata_pdf() { 'height' => 150, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), - ), - ), ), ), 'filesize' => wp_filesize( $test_file ), @@ -831,7 +811,6 @@ public function test_wp_generate_attachment_metadata_pdf() { foreach ( $metadata['sizes'] as $size ) { unlink( $temp_dir . $size['file'] ); } - remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); } /** @@ -846,9 +825,6 @@ public function test_crop_setting_for_pdf() { update_option( 'medium_crop', 1 ); - // Use legacy JPEG output. - add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); - $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf'; $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf'; copy( $orig_file, $test_file ); @@ -888,12 +864,6 @@ public function test_crop_setting_for_pdf() { 'height' => 300, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-300x300.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-300x300.jpg' ), - ), - ), ), 'large' => array( 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', @@ -901,13 +871,6 @@ public function test_crop_setting_for_pdf() { 'height' => 1024, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-791x1024.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-791x1024.jpg' ), - ), - ), - ), 'thumbnail' => array( 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', @@ -915,12 +878,6 @@ public function test_crop_setting_for_pdf() { 'height' => 150, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-116x150.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-116x150.jpg' ), - ), - ), ), ), 'filesize' => wp_filesize( $test_file ), @@ -932,8 +889,6 @@ public function test_crop_setting_for_pdf() { foreach ( $metadata['sizes'] as $size ) { unlink( $temp_dir . $size['file'] ); } - remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); - } /** @@ -944,9 +899,6 @@ public function test_fallback_intermediate_image_sizes() { $this->markTestSkipped( 'Rendering PDFs is not supported on this system.' ); } - // Use legacy JPEG output. - add_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); - $orig_file = DIR_TESTDATA . '/images/wordpress-gsoc-flyer.pdf'; $test_file = get_temp_dir() . 'wordpress-gsoc-flyer.pdf'; copy( $orig_file, $test_file ); @@ -980,12 +932,6 @@ public function test_fallback_intermediate_image_sizes() { 'height' => 100, 'mime-type' => 'image/jpeg', 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), - 'sources' => array( - 'image/jpeg' => array( - 'file' => 'wordpress-gsoc-flyer-pdf-77x100.jpg', - 'filesize' => wp_filesize( $temp_dir . 'wordpress-gsoc-flyer-pdf-77x100.jpg' ), - ), - ), ); // Different environments produce slightly different filesize results. @@ -1001,7 +947,6 @@ public function test_fallback_intermediate_image_sizes() { foreach ( $metadata['sizes'] as $size ) { unlink( $temp_dir . $size['file'] ); } - remove_filter( 'wp_upload_image_mime_transforms', '__return_empty_array' ); } public function filter_fallback_intermediate_image_sizes( $fallback_sizes, $metadata ) { diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 26387f09a1b36..9ea0cc53b087b 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -2269,14 +2269,11 @@ public function test_wp_filter_content_tags_srcset_sizes() { // Do not add width, height, and loading. add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); - } /** @@ -2310,12 +2307,9 @@ public function test_wp_filter_content_tags_srcset_sizes_with_preexisting_srcset $img = wp_img_tag_add_loading_attr( $img, 'test' ); $img = wp_img_tag_add_decoding_attr( $img, 'the_content' ); $img = preg_replace( '|]+) />|', '', $img ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); // The content filter should return the image unchanged. $this->assertSame( $img, wp_filter_content_tags( $img ) ); - - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -2385,7 +2379,6 @@ public function test_wp_filter_content_tags_filter_with_identical_image_tags_dis add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); add_filter( 'wp_img_tag_add_decoding_attr', '__return_false' ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); add_filter( 'wp_content_img_tag', @@ -2448,7 +2441,6 @@ public function test_wp_calculate_image_srcset_animated_gifs() { * @requires function imagejpeg */ public function test_wp_filter_content_tags_schemes() { - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $image_meta = wp_get_attachment_metadata( self::$large_id ); $size_array = $this->get_image_size_array_from_meta( $image_meta, 'medium' ); @@ -2494,7 +2486,6 @@ public function test_wp_filter_content_tags_schemes() { $actual = wp_filter_content_tags( $unfiltered ); $this->assertSame( $expected, $actual ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -2988,13 +2979,11 @@ public function test_wp_filter_content_tags_width_height() { // Do not add loading, srcset, and sizes. add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -3070,13 +3059,11 @@ public function test_wp_filter_content_tags_loading_lazy() { // Do not add width, height, srcset, and sizes. add_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); add_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_img_tag_add_width_and_height_attr', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -3105,13 +3092,9 @@ public function test_wp_filter_content_tags_loading_lazy_opted_in() { // Enable globally for all tags. add_filter( 'wp_lazy_loading_enabled', '__return_true' ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); - $this->assertSame( $content_filtered, wp_filter_content_tags( $content_unfiltered ) ); remove_filter( 'wp_lazy_loading_enabled', '__return_true' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); - } /** @@ -3136,12 +3119,9 @@ public function test_wp_filter_content_tags_loading_lazy_opted_out() { // Disable globally for all tags. add_filter( 'wp_lazy_loading_enabled', '__return_false' ); - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); - $this->assertSame( $content, wp_filter_content_tags( $content ) ); remove_filter( 'wp_lazy_loading_enabled', '__return_false' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); } /** @@ -3662,9 +3642,6 @@ public function test_wp_omit_loading_attr_threshold_filter() { * @ticket 53675 */ public function test_wp_filter_content_tags_with_wp_get_loading_attr_default() { - global $wp_query, $wp_the_query; - add_filter( 'wp_content_image_mimes', '__return_empty_array' ); - $img1 = get_image_tag( self::$large_id, '', '', '', 'large' ); $iframe1 = ''; $img2 = get_image_tag( self::$large_id, '', '', '', 'medium' ); @@ -3692,7 +3669,6 @@ public function test_wp_filter_content_tags_with_wp_get_loading_attr_default() { $content_filtered = wp_filter_content_tags( $content_unfiltered, 'the_content' ); remove_filter( 'wp_img_tag_add_srcset_and_sizes_attr', '__return_false' ); } - remove_filter( 'wp_content_image_mimes', '__return_empty_array' ); // After filtering, the first image should not be lazy-loaded while the other ones should be. $this->assertSame( $content_expected, $content_filtered ); From 6232ffa5ccafa050e00904aef9c3b2a08f7acc6c Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Tue, 20 Jun 2023 12:15:52 -0600 Subject: [PATCH 102/102] Consolidate filtering into helper to reduce duplication of code --- src/wp-includes/class-wp-image-editor.php | 21 +----------- src/wp-includes/functions.php | 40 ++++++++++++++++++++--- src/wp-includes/media.php | 3 +- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 53bdae40a3db6..f48eab8fbbe87 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -356,26 +356,7 @@ protected function get_output_format( $filename = null, $mime_type = null ) { $new_ext = $file_ext; } - /** - * Filters the image editor output format mapping. - * - * Enables filtering the mime type used to save images. By default, - * the mapping array is empty, so the mime type matches the source image. - * - * @see WP_Image_Editor::get_output_format() - * - * @since 5.8.0 - * - * @param string[] $output_format { - * An array of mime type mappings. Maps a source mime type to a new - * destination mime type. Default empty array. - * - * @type string ...$0 The new mime type. - * } - * @param string $filename Path to the image. - * @param string $mime_type The source image mime type. - */ - $output_format = apply_filters( 'image_editor_output_format', get_default_image_editor_output_format(), $filename, $mime_type ); + get_default_image_editor_output_format( $filename, $mime_type ); if ( isset( $output_format[ $mime_type ] ) && $this->supports_mime_type( $output_format[ $mime_type ] ) diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index bf2c7202aed75..f4b15286a08e8 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -2692,7 +2692,7 @@ function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) */ if ( $is_image ) { /** This filter is documented in wp-includes/class-wp-image-editor.php */ - $output_formats = apply_filters( 'image_editor_output_format', get_default_image_editor_output_format(), $_dir . $filename, $mime_type ); + $output_formats = get_default_image_editor_output_format( $_dir . $filename, $mime_type ); $alt_types = array(); if ( ! empty( $output_formats[ $mime_type ] ) ) { @@ -2761,12 +2761,44 @@ function wp_unique_filename( $dir, $filename, $unique_filename_callback = null ) } /** - * Get the default output format. + * Get the default output format. Return is filtered by `image_editor_output_format`. + * + * @since 6.4.0 + * + * @param string $filename Path to the image. + * @param string $mime_type The source image mime type. + * + * @return string[] $output_format { + * An array of mime type mappings. Maps a source mime type to a new + * destination mime type. Default maps jpeg to webp. + * + * @type string ...$0 The new mime type. + * } */ - function get_default_image_editor_output_format() { - return array ( + function get_default_image_editor_output_format( $filename, $mime_type ) { + $default = array ( 'image/jpeg' => 'image/webp', ); + /** + * Filters the image editor output format mapping. + * + * Enables filtering the mime type used to save images. By default, + * the mapping array is empty, so the mime type matches the source image. + * + * @see WP_Image_Editor::get_output_format() + * + * @since 5.8.0 + * + * @param string[] $output_format { + * An array of mime type mappings. Maps a source mime type to a new + * destination mime type. Default maps jpeg to webp. + * + * @type string ...$0 The new mime type. + * } + * @param string $filename Path to the image. + * @param string $mime_type The source image mime type. + */ + return apply_filters( 'image_editor_output_format', $default(), $filename, $mime_type ); } /** diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 96668c156c7c8..ee995b8e65781 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -3919,8 +3919,7 @@ function wp_get_image_editor( $path, $args = array() ) { // Check and set the output mime type mapped to the input type. if ( isset( $args['mime_type'] ) ) { - /** This filter is documented in wp-includes/class-wp-image-editor.php */ - $output_format = apply_filters( 'image_editor_output_format', get_default_image_editor_output_format(), $path, $args['mime_type'] ); + $output_format = get_default_image_editor_output_format( $path, $args['mime_type'] ); if ( isset( $output_format[ $args['mime_type'] ] ) ) { $args['output_mime_type'] = $output_format[ $args['mime_type'] ]; }