Make WordPress Core

Opened 6 years ago

Closed 4 years ago

Last modified 4 months ago

#52788 closed feature request (maybelater)

Add JPEG XL support.

Reported by: atjn Owned by: adamsilverstein
Priority: normal Milestone:
Component: Media Version:
Severity: normal Keywords: has-patch has-unit-tests
Cc: Focuses: performance

Description

JPEG XL is a new image format that beats JPEG, PNG, WebP and AVIF in pretty much all categories. I believe it will be widely used on the internet in a few years.

Cloudinary has made ​an awesome overview that shows why JPEG XL is better than anything else. Please note that they helped make the format, so they could be biased, but I haven't found any issues with it.

First and foremost it would be great to support uploading it to media, but down the line I think it could be used for much more. Thumbnails of any other image type could be generated in JPEG XL, greatly improving performance. It might even make sense to support delivering any source image as a JPEG XL, no matter the original encoding.

Here are a few points I think are especially nice for WordPress:

  • Encoding speed is better than WebP and AVIF, so there's no risk that users would have to upgrade their server to use it.
  • It supports ​progressive decode which is huge for perceived performance, and could theoretically allow you to only produce one large thumbnail and then just not load the entire image when you need a smaller preview.
  • It is expected to be widely supported, meaning users can use it as their main local editing format, they can download and directly view the images, and it will work in all browsers. This is not the case for WebP and AVIF, which has caused a lot of frustration among developers and end-users.

JPEG XL is, as of writing, not supported in any browsers, but will almost certainly be soon. Chromium has marked it as their highest priority. You can see the current status here: ​caniuse.com/jpegxl

The official JPEG XL page is here: ​jpeg.org/jpegxl
A free and open source encoder/decoder is in the works: ​gitlab.com/wg1/jpeg-xl

Change History (11)

#1 @adamsilverstein
6 years ago

  • Milestone Awaiting Review → Future Release
  • Owner set to adamsilverstein
  • Status new → assigned

#2 @atjn
6 years ago

If you want to play around with it, I can recommend ​Squoosh. Just remember that the encoder is in early beta, so it will be slower and produce larger files than it should do.

#3 follow-up: @spacedmonkey
6 years ago

Having a quick look into this, it seems like neither of the imagick or gd support this codex. So until they support it, feels like it unlikely core can.

#4 in reply to: ↑ 3 @atjn
6 years ago

Replying to spacedmonkey

Good point! As far as I can tell, it is already supported in imagick: ​https://imagemagick.org/script/formats.php#supported

If JPEG XL becomes widely supported in browsers, I would expect GD to add support, the same way they have done with WebP and AVIF.

#5 @atjn
5 years ago

You can now enable experimental JPEG XL support in Chrome, Edge, and Firefox: ​https://caniuse.com/jpegxl

#6 @adamsilverstein
5 years ago

Noting that JPEGXL support is being proposed for libgd in ​https://github.com/libgd/libgd/issues/699.

#7 @mukesh27
4 years ago

  • Milestone Future Release
  • Resolution → maybelater
  • Status assigned → closed

We're probably years away from widespread JPEG XL support in browsers as the majority of browsers do not support it natively. Once the browser and gd added support for JPEG XL then we can check if WordPress core allows it to not.

For now, mark it as maybelater. Feel free to open it if you think you have any solutions or feedback.

#8 @sabernhardt
4 years ago

#56742 was marked as a duplicate.

#9 @gregbenz
4 years ago

Chrome, Firefox, Edge, Brave support JXL via developer flags / development builds. Native browser support is on the near horizon.
​https://jpegxl.io/articles/browser-support/

This ticket was mentioned in ​PR #12006 on ​WordPress/wordpress-develop by ​@adamsilverstein.


4 months ago
#10

  • Keywords has-patch has-unit-tests added

## Summary

  • Register image/jxl as an allowed upload MIME type.
  • Add wp_is_jxl_file(), a magic-bytes check that recognizes both JXL flavors (naked codestream + ISOBMFF container).
  • Add wp_filter_jxl_filetype_and_ext() on wp_check_filetype_and_ext to restore the canonical image/jxl MIME when PHP's fileinfo reports the non-canonical image/x-jxl form.
  • Accept original-jxl as a sideload image_size. The HEIC and JXL branches in sideload_item() are collapsed since both write to $metadata['original'] and are mutually exclusive.
  • Rename wp_delete_attachment_heic_companion_file() → wp_delete_attachment_preserved_original_companion_file() since the same delete_attachment hook now covers both HEIC and JXL companions. Implementation unchanged; only the name and docblock are generalized. The test file is renamed alongside, and a JXL-companion deletion test is added.

## Why

JPEG XL is a modern image format that can losslessly transcode existing JPEGs at significantly smaller size. WordPress core does not recognize .jxl uploads today:

  • image/jxl is not in the default upload_mimes list, so the upload is rejected before any conversion can happen.
  • getimagesize() cannot identify JXL files. PHP's fileinfo extension reports them as image/x-jxl (not the registered image/jxl), so wp_check_filetype_and_ext() flags the upload as MIME-mismatched and rejects it.

When client-side media processing is enabled, the browser decodes the uploaded JXL to a JPEG using a lazy-loaded VIPS/WASM module and uploads the JPEG as the primary attachment. The original .jxl is preserved as a companion file under $metadata[https://github.com/WordPress/wordpress-develop/pull/11323 'original'], exactly like the HEIC companion-original pattern in #11323.

This PR is the server-only backport of ​WordPress/gutenberg#77584. The lazy WASM loader, VIPS worker, store actions, and editor UI ship through the normal Gutenberg → Core package sync.

## Base

Stacked on ​#11323 (HEIC canvas fallback) — this PR generalizes the HEIC cleanup function so it can serve both formats. Merge #11323 first, then rebase this branch onto trunk.

## Backport scope

### src/wp-includes/media.php

  • New wp_is_jxl_file( $file ) — 12-byte magic-bytes check; recognizes both JXL flavors.
  • New wp_add_jxl_upload_mimes( $mimes ) — adds 'jxl' => 'image/jxl'.
  • New wp_filter_jxl_filetype_and_ext( $data, $file, $filename ) — restores image/jxl when fileinfo returned image/x-jxl (or nothing) and the magic bytes confirm it.
  • wp_delete_attachment_heic_companion_file() → wp_delete_attachment_preserved_original_companion_file(). Implementation identical; docblock generalized.

### src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php

  • register_routes() — adds original-jxl to the sideload image_size enum.
  • sideload_item() — collapses the original-heic branch with original-jxl (both write to $metadata['original']).

### src/wp-includes/default-filters.php

  • add_filter( 'upload_mimes', 'wp_add_jxl_upload_mimes' );
  • add_filter( 'wp_check_filetype_and_ext', 'wp_filter_jxl_filetype_and_ext', 10, 3 );
  • delete_attachment action updated to call the renamed function.

## Security model

The companion-file cleanup carries the existing HEIC hardening unchanged:

  1. wp_basename() always strips path components from the recorded metadata before joining.
  2. wp_delete_file_from_directory() requires the resolved path to be a regular file strictly inside wp_get_upload_dir()['basedir'].

The new magic-bytes check reads only the first 12 bytes via fopen( 'rb' ) / fread() and closes the handle immediately. It runs on the uploaded temp file before the file is moved into the uploads directory, so it never touches a user-controlled path.

The wp_filter_jxl_filetype_and_ext() filter is conservative: it only acts when core has already failed to identify the file (empty( $data['type'] )) AND the extension is .jxl AND the magic bytes match. A JPEG renamed to .jxl is not rescued.

## Test plan

  • [ ] vendor/bin/phpunit tests/phpunit/tests/media/wpJxlUpload.php — 10 new tests pass (magic-bytes for naked + ISOBMFF + JPEG + PNG + short file + missing file; upload_mimes filter; filetype filter happy path, already-recognized passthrough, and JXL-extension-with-wrong-magic rejection).
  • [ ] vendor/bin/phpunit tests/phpunit/tests/media/wpDeleteAttachmentPreservedOriginalCompanionFile.php — existing HEIC tests still pass + new JXL companion deletion test passes.
  • [ ] Manual: with client-side media processing enabled in Chromium 137+, upload a real .jxl file. Confirm the JPEG derivative is the primary attachment and the .jxl is preserved on disk and recorded in _wp_attachment_metadata['original'].
  • [ ] Manual: rename a JPEG to evil.jxl and attempt to upload. Confirm the upload is rejected (magic bytes fail).
  • [ ] Manual: delete a JXL-derived attachment. Confirm both the JPEG and the .jxl are removed.
  • [ ] Manual: delete a HEIC-derived attachment (regression check). Confirm the HEIC companion is still removed via the renamed hook.

## Related

  • Upstream: ​WordPress/gutenberg#77584 (OPEN, currently CONFLICTING with trunk). Keep this PR in draft until the GB PR merges.
  • Stacked on: ​#11323 — HEIC canvas fallback. This PR renames the function added there.
  • Part of the 7.1 client-side media reintroduction: ​#11324.

​@adamsilverstein commented on ​PR #12006:


4 months ago
#11

Marking as draft since we are punting JXL support for client side media for now, see ​https://github.com/WordPress/gutenberg/pull/77584

Note: See TracTickets for help on using tickets.