@@ -2413,14 +2413,23 @@ private function validate_image_dimensions( int $width, int $height, string $ima
24132413 );
24142414 }
24152415
2416- // 'original' size: should match original attachment dimensions.
2416+ /*
2417+ * 'original' size: the full-size image that replaces the main file (see
2418+ * sideload_item()/finalize_item()). The endpoint expects any EXIF
2419+ * orientation to be applied to the image already, which can swap width
2420+ * and height, so the dimensions must match the stored dimensions or be
2421+ * their transpose.
2422+ */
24172423 if ( 'original ' === $ image_size ) {
24182424 $ metadata = wp_get_attachment_metadata ( $ attachment_id , true );
24192425 if ( is_array ( $ metadata ) && isset ( $ metadata ['width ' ], $ metadata ['height ' ] ) ) {
24202426 $ expected_width = (int ) $ metadata ['width ' ];
24212427 $ expected_height = (int ) $ metadata ['height ' ];
24222428
2423- if ( $ width !== $ expected_width || $ height !== $ expected_height ) {
2429+ $ matches_dimensions = $ width === $ expected_width && $ height === $ expected_height ;
2430+ $ transposes_dimensions = $ width === $ expected_height && $ height === $ expected_width ;
2431+
2432+ if ( ! $ matches_dimensions && ! $ transposes_dimensions ) {
24242433 return new WP_Error (
24252434 'rest_upload_dimension_mismatch ' ,
24262435 sprintf (
@@ -2665,8 +2674,6 @@ public function sideload_item( WP_REST_Request $request ) {
26652674 $ sub_size_data ['file ' ] = wp_basename ( $ path );
26662675 $ sub_size_data ['mime_type ' ] = $ type ;
26672676 $ sub_size_data ['filesize ' ] = wp_filesize ( $ path );
2668- } elseif ( 'original ' === $ image_size ) {
2669- $ sub_size_data ['file ' ] = wp_basename ( $ path );
26702677 } elseif ( self ::IMAGE_SIZE_SOURCE_ORIGINAL === $ image_size ) {
26712678 /*
26722679 * Source-format original (e.g. the HEIC kept next to its JPEG
@@ -2681,8 +2688,16 @@ public function sideload_item( WP_REST_Request $request ) {
26812688 * finalize_item can store it under its dedicated meta key.
26822689 */
26832690 $ sub_size_data ['file ' ] = wp_basename ( $ path );
2684- } elseif ( 'scaled ' === $ image_size ) {
2685- // Record the current attached file as the original.
2691+ } elseif ( 'scaled ' === $ image_size || 'original ' === $ image_size ) {
2692+ /*
2693+ * 'scaled' and 'original' both replace the attachment's main file
2694+ * with the supplied image and keep the file being replaced as
2695+ * `original_image`, which is the untouched upload. A 'scaled'
2696+ * image is downsized and an 'original' image has any EXIF
2697+ * orientation already applied. This is the same swap WordPress
2698+ * makes when it scales or rotates an image on upload; see
2699+ * _wp_image_meta_replace_original().
2700+ */
26862701 $ current_file = get_attached_file ( $ attachment_id , true );
26872702
26882703 if ( ! $ current_file ) {
@@ -2695,19 +2710,19 @@ public function sideload_item( WP_REST_Request $request ) {
26952710
26962711 $ sub_size_data ['original_image ' ] = wp_basename ( $ current_file );
26972712
2698- // Validate the scaled image before updating the attached file.
2713+ // Validate the supplied image before updating the attached file.
26992714 $ size = wp_getimagesize ( $ path );
27002715 $ filesize = wp_filesize ( $ path );
27012716
27022717 if ( ! $ size || ! $ filesize ) {
27032718 return new WP_Error (
27042719 'rest_sideload_invalid_image ' ,
2705- __ ( 'Unable to read the scaled image file. ' ),
2720+ __ ( 'Unable to read the sideloaded image file. ' ),
27062721 array ( 'status ' => 500 )
27072722 );
27082723 }
27092724
2710- // Update the attached file to point to the scaled version .
2725+ // Update the attached file to point to the supplied image .
27112726 // This writes to _wp_attached_file meta, not _wp_attachment_metadata.
27122727 if (
27132728 get_attached_file ( $ attachment_id , true ) !== $ path &&
@@ -2834,8 +2849,39 @@ public function finalize_item( WP_REST_Request $request ) {
28342849 continue ;
28352850 }
28362851
2837- if ( 'original ' === $ image_size ) {
2838- $ metadata ['original_image ' ] = $ sub_size ['file ' ];
2852+ if ( 'original ' === $ image_size || 'scaled ' === $ image_size ) {
2853+ // Skip malformed entries so a bad payload cannot blank out the
2854+ // main file metadata.
2855+ if ( empty ( $ sub_size ['file ' ] ) ) {
2856+ continue ;
2857+ }
2858+
2859+ /*
2860+ * Record the supplied full-size image (from sideload_item()) as
2861+ * the main file, keeping the current attached file as
2862+ * `original_image`. A 'scaled' image is downsized and an
2863+ * 'original' image is rotated; both have any EXIF orientation
2864+ * already applied by the client.
2865+ */
2866+ if ( ! empty ( $ sub_size ['original_image ' ] ) ) {
2867+ $ metadata ['original_image ' ] = $ sub_size ['original_image ' ];
2868+ }
2869+ $ metadata ['width ' ] = $ sub_size ['width ' ] ?? 0 ;
2870+ $ metadata ['height ' ] = $ sub_size ['height ' ] ?? 0 ;
2871+ $ metadata ['filesize ' ] = $ sub_size ['filesize ' ] ?? 0 ;
2872+ $ metadata ['file ' ] = $ sub_size ['file ' ];
2873+
2874+ /*
2875+ * The supplied image has its orientation applied already, so
2876+ * reset the stored value (from the upload) to 1, as
2877+ * wp_create_image_subsizes() does for both its scale and rotate
2878+ * paths. Otherwise exif_orientation would still report the
2879+ * pre-rotation value and the client would rotate the image
2880+ * again on a re-fetch.
2881+ */
2882+ if ( ! empty ( $ metadata ['image_meta ' ]['orientation ' ] ) ) {
2883+ $ metadata ['image_meta ' ]['orientation ' ] = 1 ;
2884+ }
28392885 } elseif ( self ::IMAGE_SIZE_SOURCE_ORIGINAL === $ image_size ) {
28402886 /*
28412887 * Source-format original: stored under its own meta key so the
@@ -2855,14 +2901,6 @@ public function finalize_item( WP_REST_Request $request ) {
28552901 } elseif ( 'animated_video_poster ' === $ image_size ) {
28562902 // Static first-frame poster for the converted video.
28572903 $ metadata ['animated_video_poster ' ] = $ sub_size ['file ' ];
2858- } elseif ( 'scaled ' === $ image_size ) {
2859- if ( ! empty ( $ sub_size ['original_image ' ] ) ) {
2860- $ metadata ['original_image ' ] = $ sub_size ['original_image ' ];
2861- }
2862- $ metadata ['width ' ] = $ sub_size ['width ' ] ?? 0 ;
2863- $ metadata ['height ' ] = $ sub_size ['height ' ] ?? 0 ;
2864- $ metadata ['filesize ' ] = $ sub_size ['filesize ' ] ?? 0 ;
2865- $ metadata ['file ' ] = $ sub_size ['file ' ] ?? '' ;
28662904 } else {
28672905 $ metadata ['sizes ' ] = $ metadata ['sizes ' ] ?? array ();
28682906
0 commit comments