Skip to content

Add support for WebP images - #6311

Merged
laurmaedje merged 1 commit into
typst:mainfrom
LinusU:webp-no-feature-flag
Jun 4, 2025
Merged

laurmaedje merged 1 commit into
typst:mainfrom
LinusU:webp-no-feature-flag

Conversation

@LinusU

@LinusU LinusU commented May 21, 2025

Copy link
Copy Markdown
Contributor

This adds support for loading WebP images, the same way that any other images is loaded.

I'd be happy to add a test for this, but I think that we first need to add a WebP image to the assets package, similar to how it's done with "rhino.png" or "tiger.jpg". Also, I couldn't find any test for GIF files, so maybe the same should be done for a GIF file as well?

As can be seen in the diff over Cargo.lock, this doesn't add any new dependencies! image-webp is already included in the build (via krilla).

Some background on why I'm submitting this can be found here: #2925 (comment)

I have also made a version that gates this behind a feature flag, it can be found here: main...LinusU:typst:webp

@LinusU LinusU mentioned this pull request May 21, 2025
@LinusU
LinusU force-pushed the webp-no-feature-flag branch from 1916e43 to 0915660 Compare May 21, 2025 12:55
@LinusU

LinusU commented May 21, 2025

Copy link
Copy Markdown
Contributor Author

(force push to fix formatting using cargo fmt --all)

@LinusU

LinusU commented May 21, 2025

Copy link
Copy Markdown
Contributor Author

I've tested this in our app, and it works as expected 🎉

#set page(width: 384pt, height: auto)

#text(size: 64pt)[Hello, World!]

#image("https://locousercontent.com/fOlMF5zs1R0FiO7YSnP-Uc-LEUtGLKTs3CnjCwulN7YWF8UUrnplFOJUeHgsgIpS/original.webp")

test

Comment thread docs/tutorial/1-writing.md Outdated
@LinusU
LinusU force-pushed the webp-no-feature-flag branch from 0915660 to 7f8db8d Compare May 21, 2025 13:02
@LinusU

LinusU commented May 21, 2025

Copy link
Copy Markdown
Contributor Author

(force push to fix casing of WebP in docs)

@Enivex

Enivex commented May 21, 2025

Copy link
Copy Markdown
Collaborator

Does this increase binary size?

Webp is kind of a dead end that's being replaced by other formats. In particular jxl and avif.

It can also not be natively embedded in any of typst's targets, with the exception of the experimental HTML

@LinusU

LinusU commented May 21, 2025

Copy link
Copy Markdown
Contributor Author

Does this increase binary size?

Before: 35M (34,640,352 bytes)
After: 35M (34,756,432 bytes)

Not noticeably, just by 0.3%.

Webp is kind of a dead end that's being replaced by other formats. In particular jxl and avif.

I would love to use any image format that gives us better compression, but at the moment we use these images in a number of places, including the web & in React Native. Currently, support for JPEG XL is only in Safari. AVIF could potentially be something we could start adopting soon, but it would require us shipping native code in our apps since React Native currently doesn't support it.

Now, this is just my use case, and is of course something that you can say is outside the scope of this project. I'm just trying to describe why WebP matters to us.

Also, we have a lot of files uploaded in lossy WebP source format from our clients. Switching our current files would probably cause inflation in file size, or drop in quality.

It can also not be natively embedded in any of typst's targets, with the exception of the experimental HTML

I'm not exactly sure how the output works today, but creating two documents using typst compile, one with a WebP and the other with a PNG file, produces almost identical outputs. I couldn't find a PNG header inside the PDF when looking with a Hex Editor, but maybe it's embedded as a PNG nonetheless...

In any case, this approach doesn't seem to be any worse than converting the images to PNG before loading them into Typst.

Again, speaking for my own use case, we are using the PNG rasterizer to render the output as an image.

We do want PDF export as well, but as long as the same pixels shows up inside the PDF, which seems to be the case, this is good enough for us. I'm guessing that the potential issue is that the file size might be bigger inside the PDF?

@laurmaedje

Copy link
Copy Markdown
Member

The PDF output will be the same as with a PNG as it is reencoded. The main reason against WebP support in Typst was compile time and dependency count, which is a bit of a moot point given our current dependencies.
(It's unlikely to change but possible). The other reason originally was to prevent a slippery slope where more and more image formats are requested if we give in once. Though that wouldn't necessarily apply here since the situation is different w.r.t. dependencies.

@reknih What do you think?

@Enivex

Enivex commented May 22, 2025

Copy link
Copy Markdown
Collaborator

I'm guessing that the potential issue is that the file size might be bigger inside the PDF?

Yes, they will be converted to png.

Edit: Or potentially jpg, depending how this was implemented.

@reknih

reknih commented Jun 2, 2025

Copy link
Copy Markdown
Member

@laurmaedje asked me (the resident file format enjoyer at Typst) to comment on this:

We have been somewhat apprehensive about adding WebP in the past. Let me lay out our rationale here:

Reasons to add WebP

  • Users may have WebP files that they may want to use
  • When using HTML export, outputting WebP images might be desirable

Reasons against WebP

  • Binary size increase of including WebP encoder/decoder. Since a dependency has already covertly added a decoder, this is somewhat of a moot point, at least on the decoding side.
  • Attack / maintenance surface, same as above
  • Including a WebP in PDF means that it needs to be recoded into a format PDF supports. Currently, Typst chooses either JPEG or PNG. This means that Typst has to choose between lossily recoding the file to JPEG or potentially producing a way bigger output file by creating a PNG. In my opinion, a smart implementation would divine the intent of the user by reading the WebP save quality parameter and choosing JPEG if it falls below about 95. Worth noting that PDF also supports JPEG 2000 which is more similar to WebP in that it supports both lossy and lossless compression at better compression rates than PNG and JPEG, but since no pure Rust encoder is available for that format, we cannot write it.
  • This would be an image format that we'd include that does not feature a matching encoder. That means that for HTML export, we'd need to add additional dependencies to support scaling images down or cropping them. This also applies to GIF files, but for them, recoding as a PNG is always a win, for WebP not necessarily. The true win for WebP & HTML export would be to have an encoder so that we could convert JPEGs to smaller WebPs instead.
  • WebP is not particularly more prolific than other image formats and somewhat infamously unpopular. TIFF is widely used in professional workflows, if you have an iPhone camera, you deal with a lot of HEIC files, ... Adding WebP support could encourage image format proliferation.
  • Subjectivity Alert In our opinion, WebP is a bad file format for assets in a design workflow. Its compression artifacts are more visible than JPEG's, starting at quality level 80 and below in many images. For example, compare the quality 50 JPEG and WebP file on this page: Even if the image differences seem to be similar or even less on the black comparison chart, the edges of the shadow in the top segment of the image are perceptively more affected by blocking artifacts in the WebP vs the JPEG, for just about ~15% less in file size. We want to help our users to deliver great-looking documents, and for this, standard settings of most JPEG encoders are better suited. If a reduction in file size is desired, we should instead do it in the export process using carefully chosen parameters.

Despite these negatives, I cautiously approve of this PR since including the encoder for SVGs only seems dumb. We're incurring the binary size, security, and maintenance burden anyways and including WebPs is feasible for determined users, it's just harder than it needs to be.

With that being said, I am not opposed to completely removing this dependency instead in the upcoming or a future release.

@LinusU

LinusU commented Jun 3, 2025

Copy link
Copy Markdown
Contributor Author

Attack / maintenance surface, same as above

Not saying that this isn't a concern, but something that I think makes this easier to swallow is that the WebP encoder/decoder is maintained by the same organization (image-rs) that maintains the image crate together with the png, jpeg, gif encoders/decoders that we already use. It's also 100% Rust, has fuzz testing, and uses #![forbid(unsafe_code)].

Including a WebP in PDF means that it needs to be recoded into a format PDF supports.

This is true, but if you already have a WebP file, that is something that needs to happen if you want to put it in a PDF, whichever tool you use. E.g. for my use case the workaround would be to convert the images to PNG myself, which would have the exact same end result.

WebP is not particularly more prolific than other image formats and somewhat infamously unpopular.

Today I learned 😁 But it seems like the complaint was that when people tried to save images from websites they got a WebP file, which then their programs couldn't open? I guess this isn't as much of a problem today when tools support WebP, and even then, I think that the tradeoff less bandwidth for every visitor vs. some people have to jump through hoops to save images is an easy choice for most websites...

This would be an image format that we'd include that does not feature a matching encoder. That means that for HTML export, we'd need to add additional dependencies to support scaling images down or cropping them. This also applies to GIF files, but for them, recoding as a PNG is always a win, for WebP not necessarily. The true win for WebP & HTML export would be to have an encoder so that we could convert JPEGs to smaller WebPs instead.

This could be something to include though. For the HTML export, it seems like it would be better to convert both GIF and PNG to WebP, instead of PNG. Since "WebP lossless images are 26% smaller in size compared to PNGs", and all browsers supports it.

WebP is a bad file format for assets in a design workflow

I think that I agree with this. In a design workflow I imagine that you always want to work with the raw data and only perform lossless transformations until the absolute last step, where you compress it in order to deliver the final product.

I have no expertise to make me try and comment on the perceptual visual differences vs file size but at least the WebP team has a study linked that backs up their claim that WebP has smaller sizes when the end result looks the same: https://developers.google.com/speed/webp/docs/webp_study

Despite these negatives, I cautiously approve of this PR since [...]

This makes me very happy to hear! 🙌

Let me know if there is anything else I can do to help move this forward! 🚀

Comment thread crates/typst-library/src/visualize/image/raster.rs Outdated
Comment thread crates/typst-library/src/visualize/image/mod.rs Outdated
@LinusU
LinusU force-pushed the webp-no-feature-flag branch from 7f8db8d to 3ecc692 Compare June 3, 2025 15:45
@LinusU

LinusU commented Jun 3, 2025

Copy link
Copy Markdown
Contributor Author

Adressed comments and rebased on latest main

@laurmaedje
laurmaedje added this pull request to the merge queue Jun 4, 2025
@laurmaedje

Copy link
Copy Markdown
Member

Thanks!

Merged via the queue into typst:main with commit 1de2095 Jun 4, 2025
Dikluwe pushed a commit to Dikluwe/typst-crystalline that referenced this pull request Sep 12, 2026
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.

5 participants