Skip to content

fix(docreader): apply PDF image SMask so embedded figures are not all-black - #2775

Open
649111698 wants to merge 1 commit into
Tencent:mainfrom
649111698:fix/pdf-embedded-image-smask
Open

fix(docreader): apply PDF image SMask so embedded figures are not all-black#2775
649111698 wants to merge 1 commit into
Tencent:mainfrom
649111698:fix/pdf-embedded-image-smask

Conversation

@649111698

Copy link
Copy Markdown

Problem

PDFs that embed figures with a soft transparency mask (/\SMask) — a common export format from plotting tools — get their embedded figures extracted as all-black JPEGs. The figure content becomes unreadable in the document chunks, and downstream VLM captions / OCR run on a black rectangle (e.g. captions come back as "black background with three white triangular shapes").

Root cause

_extract_embedded_images decoded each embedded image with PdfObject.get_bitmap(), which returns the raw base image plane and ignores the /\SMask. For masked figures the visible content lives in the mask while the base RGB plane is black, so the raw plane decodes to a solid black rectangle, which is then saved as JPEG as-is.

Verified on a production PDF: 7 of 23 embedded images carried an /\SMask; each decoded to ~100% dark pixels via get_bitmap() while the same object rendered normally in any PDF viewer.

Fix

New helper _decode_embedded_image_pil():

  1. Decode with get_bitmap(render=True) (FPDFImageObj_GetRenderedBitmap), which renders through pdfium's imaging pipeline and carries the mask as an alpha channel.
  2. If the result has real transparency, composite over white — JPEG cannot keep alpha and PDF pages render on white, so this matches what a viewer displays.
  3. Fall back to the previous raw decode if rendering raises.

Opaque images are byte-for-byte unaffected.

Testing

  • New docreader/tests/test_pdf_embedded_images.py with a hand-built PDF that reproduces the exact pattern (black RGB base plane + /\SMask circle): asserts corners become white, the opaque circle keeps its color, and the extracted JPEG is no longer all-black. Also asserts a plain opaque image is unchanged.
  • Full docreader suite: 161 tests pass (13 skipped), no behavior change for the other 158.
  • End-to-end on the production PDF that triggered the bug: all 7 masked figures' dark-pixel ratio drops from ~100% to 0.7%–4.5% and are fully readable.

…-black

get_bitmap() decodes only the base image plane and ignores any /SMask
(soft transparency mask). Figures exported from plotting tools often
store their visible content in the mask while the base RGB plane is
black, so extraction produced all-black JPEGs for such images.

Decode embedded figures via get_bitmap(render=True), which renders
through pdfium's imaging pipeline and carries the mask as an alpha
channel, then composite over white (JPEG has no alpha; PDF pages render
on white). Falls back to the raw decode when rendering is unavailable.

On a production PDF with 7 SMask figures, dark-pixel ratio drops from
~100% to 0.7%-4.5%. Opaque images are unchanged.
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