Skip to content

fix(pdf): keep the soft mask of a JPEG image - #1201

Merged
andiwand merged 1 commit into
mainfrom
fix/pdf-jpeg-soft-mask
Oct 10, 2026
Merged

andiwand merged 1 commit into
mainfrom
fix/pdf-jpeg-soft-mask

Conversation

@andiwand

@andiwand andiwand commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Refs #1188, refs opendocument-app/OpenDocument.droid#703

A user reports that a transparent image in a PDF shows with a black background. The report has no sample file, but one cause reproduces on current main: encode_image passes a DCTDecode JPEG through to the browser undecoded and drops its /SMask or stencil /Mask. Producers often store a transparent image as a JPEG with a separate soft mask, and the JPEG holds black under the transparent pixels.

The common case already works: LibreOffice writes a transparent PNG as Flate with an /SMask, and odr renders that correctly.

Before and after

A probe PDF draws a red rectangle, then a JPEG of a blue circle on black with a Flate /SMask that is opaque only inside the circle.

  • Ghostscript: the blue circle on the red rectangle.
  • main: a black square with the circle in it.
  • this PR: the blue circle on the red rectangle, as in Ghostscript.

Change

  • encode_image keeps the JPEG as it is, and writes its alpha plane as an RGBA png (EncodedImage::alpha_png). XObject::image_alpha and ImageElement::alpha_mask carry it to the renderer.
  • The pdf view writes the png as a <mask mask-type="alpha"> in the image's own unit square, one for each mask on a page (MaskRegistry::register_image_mask), and the <image> references it. The browser composites. No JPEG decoder is needed. The alpha soft masks of the graphics state already use mask-type="alpha", so this needs no browser support that the pdf view did not need before.

Limits

  • A colour-key /Mask on a JPEG still needs the decoded samples, so it stays unsupported.
  • An image inside a tiling pattern is not masked, because the pattern tiles are written without the page's mask registry.

Verification

  • PdfFile.a_jpeg_keeps_its_soft_mask: a JPEG with an /SMask gets one mask, and a JPEG without one gets none, in both text modes. Without the change, the test fails.
  • The PDF and html tests pass (471 tests).

The report has no sample, and OpenDocument.droid 4.21.0 still uses core 7.4.0. So I suggest closing #1188 only when the reporter confirms the fix, or sends a file.

Reference output

PDF pages with JPEG images that have soft masks will change. I will update the reference output after this PR and #1200 have landed.

A JPEG passes through to the browser undecoded, so its /SMask or stencil
/Mask was dropped, and the transparent pixels showed the colour the JPEG
holds there, often black. Keep the alpha apart as an RGBA png and mask the
image with it in the SVG, so the browser composites what a JPEG cannot hold.

Refs #1188

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5yx1nDeVcNX3BxqK1WpJz
@andiwand
andiwand force-pushed the fix/pdf-jpeg-soft-mask branch from 879a59f to 9a017e9 Compare October 10, 2026 09:45
@andiwand
andiwand merged commit 1516a83 into main Oct 10, 2026
23 checks passed
@andiwand
andiwand deleted the fix/pdf-jpeg-soft-mask branch October 10, 2026 09:47
andiwand added a commit that referenced this pull request Oct 10, 2026
The output now masks a JPEG image with its soft mask (#1201), and extracts
Adobe small capitals as letters (#1200).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5yx1nDeVcNX3BxqK1WpJz
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