Skip to content

REST API: Expose per-attachment output format and progressive flags on attachment responses#12007

Open
adamsilverstein wants to merge 2 commits into
WordPress:trunkfrom
adamsilverstein:backport/75793-image-output-format
Open

REST API: Expose per-attachment output format and progressive flags on attachment responses#12007
adamsilverstein wants to merge 2 commits into
WordPress:trunkfrom
adamsilverstein:backport/75793-image-output-format

Conversation

@adamsilverstein
Copy link
Copy Markdown
Member

Summary

Adds two readonly fields to WP_REST_Attachments_Controller, in the edit context, alongside the existing exif_orientation field:

  • image_output_format — returns the resolved output MIME type when image_editor_output_format maps the source MIME to a different one (e.g. JPEG → WebP), or null when no conversion is needed. The filter is invoked with the real attached filename and MIME type so plugins can make per-file decisions, mirroring the way WP_Image_Editor::set_quality() resolves the output format.
  • image_save_progressive — returns the boolean result of the image_save_progressive filter for the attachment's MIME type.

Both fields are evaluated only for image attachments (wp_attachment_is_image()).

Why

Client-side media processing encodes sub-sizes (and the threshold-scaled "scaled" copy) in the browser. To match server-side output, the browser needs to know the output MIME type and whether to use progressive encoding for the specific file being saved. Those decisions are filename- and MIME-aware (image_editor_output_format signature is ( array $formats, string $filename, string $mime_type )), so values computed at the REST API root index — without a real filename — are incorrect for any non-trivial site.

Putting the resolved values on the per-attachment response gives the client real context to act on, mirroring the existing exif_orientation pattern.

Trac ticket

https://core.trac.wordpress.org/ticket/65367

Backport from Gutenberg

Tests

New coverage in tests/phpunit/tests/rest-api/rest-attachments-controller.php:

  • test_image_output_format_and_progressive_schema — both fields are present, typed correctly, readonly, edit-context only.
  • test_image_output_format_and_progressive_defaults_in_create_response — JPEG default response has image_output_format = null and image_save_progressive = false.
  • test_image_output_format_with_custom_filter — a JPEG→WebP image_editor_output_format filter is reflected in the response, and the filter sees the real attached filename and MIME type.
  • test_image_save_progressive_with_custom_filter — a MIME-aware image_save_progressive filter is honored.
  • test_image_output_format_skipped_for_non_image — non-image attachments do not surface the fields.
  • Schema property count updated from 32 to 34.

Local results: WP_Test_REST_Attachments_Controller runs green (134 tests, 809 assertions, 2 skipped). PHPCS clean.

Follow-up

A follow-up could remove the now-redundant index-level image_output_formats, jpeg_interlaced, png_interlaced, gif_interlaced fields from WP_REST_Server::get_index(), but that needs coordinated updates to JS preloads in site-editor.php and edit-form-blocks.php and is left for a separate change.

…n attachment responses.

Add two readonly fields to `WP_REST_Attachments_Controller`, in the `edit`
context, alongside the existing `exif_orientation` field:

* `image_output_format` returns the resolved output MIME type when
  `image_editor_output_format` maps the source MIME to a different one
  (e.g. JPEG -> WebP), or `null` when no conversion is needed. The filter
  is invoked with the real attached filename and MIME type so plugins can
  make per-file decisions, mirroring the way `WP_Image_Editor::set_quality()`
  resolves the output format.
* `image_save_progressive` returns the boolean result of the
  `image_save_progressive` filter for the attachment's MIME type.

Both fields are evaluated only for image attachments. Client-side media
processing now has the per-file context it needs to encode sub-sizes
consistently with server-side behavior, instead of relying on the
file-less generic values previously surfaced on the REST API root index.

Adds focused PHPUnit coverage for the schema, the default response,
custom filter behavior, and that the fields are skipped for non-image
attachments.

See #65367.
Backport of Gutenberg PR #75793 (WordPress/gutenberg#75793).
@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.

…rom the index.

The image_output_formats, jpeg_interlaced, png_interlaced, and gif_interlaced
settings were exposed on the REST API root index without any per-file context,
so the image_editor_output_format filter ran with an empty filename and could
not make per-file decisions. These values are now provided per attachment via
the image_output_format and image_save_progressive response fields added in the
attachments controller, making the file-less index values redundant.

Keep image_sizes and image_size_threshold on the index, which remain useful
without file context.

Update the generated REST API qunit fixture to match.
adamsilverstein added a commit to WordPress/gutenberg that referenced this pull request May 28, 2026
Core's re-introduced client-side media processing (changeset 62428) restored a
pre-#75793 REST API root index that still exposes the file-less
`image_output_formats`, `jpeg_interlaced`, `png_interlaced`, and `gif_interlaced`
settings. When the plugin runs against Core trunk, these keys are present, so the
`assertArrayNotHasKey` assertions in the media processing test fail.

The proper fix lives in Core, where wordpress-develop#12007 removes these
redundant index keys in favor of the per-attachment `image_output_format` and
`image_save_progressive` response fields. Until that PR merges, disable the
affected assertions and link to it so they can be reactivated.

See WordPress/wordpress-develop#12007
@adamsilverstein
Copy link
Copy Markdown
Member Author

Added a follow-up commit that completes the #75793 migration in Core: it removes the now-redundant, file-less image_output_formats, jpeg_interlaced, png_interlaced, and gif_interlaced keys from WP_REST_Server::get_index() (and updates the generated qunit fixture).

Those values were exposed on the REST root index without per-file context, so image_editor_output_format ran with an empty filename and couldn't make per-file decisions. They're now superseded by the per-attachment image_output_format / image_save_progressive response fields added in this PR. image_sizes and image_size_threshold remain on the index since they don't need file context.

This also resolves the failing Gutenberg media processing test (the plugin asserts those keys are absent from the index). A temporary stopgap that disables those assertions until this merges is in WordPress/gutenberg#78788.

@adamsilverstein adamsilverstein marked this pull request as ready for review May 28, 2026 19:23
@github-actions
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.

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

adamsilverstein added a commit to WordPress/gutenberg that referenced this pull request May 28, 2026
Core's re-introduced client-side media processing (changeset 62428) restored a
pre-#75793 REST API root index that still exposes the file-less
`image_output_formats`, `jpeg_interlaced`, `png_interlaced`, and `gif_interlaced`
settings. When the plugin runs against Core trunk, these keys are present, so the
`assertArrayNotHasKey` assertions in the media processing test fail.

The proper fix lives in Core, where wordpress-develop#12007 removes these
redundant index keys in favor of the per-attachment `image_output_format` and
`image_save_progressive` response fields. Until that PR merges, disable the
affected assertions and link to it so they can be reactivated.

See WordPress/wordpress-develop#12007
peterwilsoncc pushed a commit to peterwilsoncc/gutenberg-build that referenced this pull request May 28, 2026
Core's re-introduced client-side media processing (changeset 62428) restored a
pre-#75793 REST API root index that still exposes the file-less
`image_output_formats`, `jpeg_interlaced`, `png_interlaced`, and `gif_interlaced`
settings. When the plugin runs against Core trunk, these keys are present, so the
`assertArrayNotHasKey` assertions in the media processing test fail.

The proper fix lives in Core, where wordpress-develop#12007 removes these
redundant index keys in favor of the per-attachment `image_output_format` and
`image_save_progressive` response fields. Until that PR merges, disable the
affected assertions and link to it so they can be reactivated.

See WordPress/wordpress-develop#12007

Source: WordPress/gutenberg@db0cf2f
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.

1 participant