Skip to content

Media REST API: Fix sideload and finalize for EXIF rotated images#12550

Open
adamsilverstein wants to merge 2 commits into
WordPress:trunkfrom
adamsilverstein:fix/rest-sideload-original-exif-rotation
Open

Media REST API: Fix sideload and finalize for EXIF rotated images#12550
adamsilverstein wants to merge 2 commits into
WordPress:trunkfrom
adamsilverstein:fix/rest-sideload-original-exif-rotation

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jul 16, 2026

Copy link
Copy Markdown
Member

Backport of WordPress/gutenberg#80295 (part of WordPress/gutenberg#77582).

Problem

In the client-side media upload flow, the client uploads the original file unmodified, applies the EXIF rotation itself, and sideloads the rotated file with image_size=original. For quarter-turn orientations (EXIF 5-8) the rotated file's width and height are swapped relative to the stored metadata, so validate_image_dimensions() rejected the sideload with a 400 (rest_upload_dimension_mismatch) and the upload failed.

The original sideload also stored the rotated file as original_image while the un-rotated upload stayed the attachment's main file, which is backwards relative to what core does when it rotates on upload.

Solution

  • Treat an image_size=original sideload like a scaled sideload: the supplied (rotated) file replaces the attachment's main file, and the untouched upload is kept as original_image. This mirrors the swap _wp_image_meta_replace_original() performs in wp_create_image_subsizes().
  • Accept transposed dimensions when validating an original sideload.
  • Reset the stored EXIF orientation to 1 in finalize for both original and scaled sub-sizes, as wp_create_image_subsizes() does for its scale and rotate paths, so exif_orientation no longer reports the pre-rotation value once rotation has been applied.
  • Skip original/scaled finalize entries missing the file name so a malformed payload cannot blank out the main file metadata.

Includes a parity test asserting the client-side path produces the same attachment metadata (dimensions, orientation, original_image) as a classic server-side upload of the same EXIF-rotated fixture.

Trac ticket: https://core.trac.wordpress.org/ticket/65643

Commit message

Media REST API: Fix sideload and finalize for EXIF rotated images.

Fix an issue where client-side media uploads of JPEGs with a quarter-turn EXIF orientation (values 5-8) failed with a 400 `rest_upload_dimension_mismatch` error, because the rotated file's swapped width and height did not match the stored metadata.

An `image_size=original` sideload is now handled like a `scaled` one: the rotated file becomes the attachment's main file and the untouched upload is kept as `original_image`, matching what core does when it rotates on upload. Finalize also resets the stored EXIF orientation once rotation has been applied.

Follow-up to [61982], [62619].

Props adamsilverstein, andrewserong, ramonjd.
Fixes #65643.

Treat an image_size=original sideload like a scaled sideload: the
supplied file (the client-rotated original) replaces the attachment's
main file and the untouched upload is kept as original_image. This is
the same swap _wp_image_meta_replace_original() performs when the
server rotates an image on upload.

Accept transposed dimensions when validating an original sideload,
since quarter-turn EXIF orientations (5-8) swap width and height;
previously these uploads failed with rest_upload_dimension_mismatch.

Reset the stored EXIF orientation to 1 in finalize for both original
and scaled sub-sizes, as wp_create_image_subsizes() does, so
exif_orientation no longer reports the pre-rotation value after the
rotation has been applied. Skip original/scaled finalize entries
missing the file name so a malformed payload cannot blank out the
main file metadata.
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props adamsilverstein, andrewserong.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@andrewserong andrewserong left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening this! It's a faithful backport of WordPress/gutenberg#80295 and I manually tested that this works as expected with the rotated file saved as the main source_url / media_details.file, with the original file as original_image.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants