Skip to content

openpdf-renderer: honor /SMask soft masks on Image XObjects - #1604

Open
andreasrosdalw wants to merge 2 commits into
LibrePDF:masterfrom
andreasrosdalw:feature/renderer-smask-alpha
Open

openpdf-renderer: honor /SMask soft masks on Image XObjects#1604
andreasrosdalw wants to merge 2 commits into
LibrePDF:masterfrom
andreasrosdalw:feature/renderer-smask-alpha

Conversation

@andreasrosdalw

@andreasrosdalw andreasrosdalw commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • OpenPdfCorePageRenderer.decodeImage()/renderImage() never looked at an Image XObject's /SMask entry (PDF §11.6.5.3 soft mask), so images were always painted fully opaque.
  • /SMask is exactly how a transparent-background PNG (a logo, a product photo, a watermark, ...) ends up embedded in a PDF, so this was a real, common visual bug: such images rendered as a solid opaque rectangle instead of blending into the page.
  • Adds soft-mask decoding: applySoftMask/compositeSoftMask resolve the (possibly indirect) /SMask stream, decode it as an 8-bit DeviceGray alpha channel via readSoftMaskGray (supporting JPEG/JPX via ImageIO, Flate-decoded, and uncompressed encodings — the same set already supported for base image rasters), and composite it onto the decoded base image, producing a TYPE_INT_ARGB result. The soft mask's own /Width//Height need not match the base image (legal per spec) — handled with nearest-neighbor resampling.
  • Purely additive to the image-decoding path; doesn't touch text, color, path, or clipping state.
  • Out of scope (by design, to keep this PR small): /Mask (stencil / color-key masking) is not yet handled.
  • Updated openpdf-renderer/README.md's XObject-coverage section to document the new capability.

Image XObjects with a /SMask entry (PDF §11.6.5.3) -- the mechanism behind
transparent-background PNGs (logos, product photos, ...) embedded in a PDF
-- previously rendered fully opaque, since decodeImage()/renderImage() never
looked at /SMask. Decode the soft mask as an 8-bit DeviceGray alpha channel
(supporting the same JPEG/JPX/Flate/uncompressed encodings as the base image
raster path, with nearest-neighbor resampling when the mask's own dimensions
differ from the base image's) and composite it onto the decoded image before
drawing. Update the README to document the new capability.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 7 complexity · 0 duplication

Metric Results
Complexity 7
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

andreasrosdalw added a commit to andreasrosdalw/OpenPDF that referenced this pull request Jul 28, 2026
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1604, LibrePDF#1605,
and LibrePDF#1606, so these four independent PRs (all based on master) don't each
carry their own overlapping README.md diff and risk merge conflicts with
each other depending on merge order.
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1603, LibrePDF#1605,
and LibrePDF#1606, so these four independent PRs (all based on master) don't each
carry their own overlapping README.md diff and risk merge conflicts with
each other depending on merge order.
andreasrosdalw added a commit to andreasrosdalw/OpenPDF that referenced this pull request Jul 28, 2026
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1603, LibrePDF#1604,
and LibrePDF#1606, so these four independent PRs (all based on master) don't each
carry their own overlapping README.md diff and risk merge conflicts with
each other depending on merge order.
andreasrosdalw added a commit to andreasrosdalw/OpenPDF that referenced this pull request Jul 28, 2026
Consolidated into LibrePDF#1607 along with the README updates for LibrePDF#1603, LibrePDF#1604,
and LibrePDF#1605, so these four independent PRs (all based on master) don't each
carry their own overlapping README.md diff and risk merge conflicts with
each other depending on merge order.
@sonarqubecloud

Copy link
Copy Markdown

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