Repository navigation
fix(render): draw images in /CalGray and /CalRGB colour spaces - #411
Merged
cau-git merged 4 commits intoOct 29, 2026
Merged
Conversation
Contributor
|
✅ DCO Check Passed Thanks @wittjeff, all your commits are properly signed off. 🎉 |
Contributor
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
7 tasks
Member
|
Thanks for this contribution. This PR is on hold until it has been consolidated as described in #428. Please resubmit it as part of the topic PR listed there, with the evidence the issue asks for. |
An image XObject whose /ColorSpace is [/CalGray dict] or [/CalRGB dict] was left as the array's JSON dump by the image resource, which only unwrapped the ICCBased, DeviceN, Separation and Indexed arrays. The bitmap state then reported the space as unsupported and the renderer drew its yellow placeholder over the whole image. PDF/A image-to-PDF converters write scanned bilevel pages as /CalGray, so such a scan rendered as an empty yellow page, and OCR run on that page found nothing. The image resource now hands any other array space to pdf_resource<PAGE_COLORSPACE>, which already classifies CalGray and CalRGB for fill and stroke colours, and reads the samples as the device space of that family. An /Indexed base written the same way is mapped alike. /Lab still needs a real conversion and is left as is. The Group 4 encoder of the CCITT unit test moves to tests/image_fixtures.py so the new tests build their fixtures with it. Refs docling-project/docling#4861 Signed-off-by: Jeff Witt <1848307+wittjeff@users.noreply.github.com>
…on regression tests/test_regression_pypdfium_render.py only reports how the corpus compares with pypdfium2. Add an enforced comparison for the pages that a rendering fix cites, each with its own tolerance, starting with the /CalRGB JPEG on dln_23cb379b... page 1 (fails on 7.24.1 with mean_abs_error 5.10 and changed_pixels_ratio 0.131; passes with the fix at 0.26 and 0.0045). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jeff Witt <1848307+wittjeff@users.noreply.github.com>
wittjeff
force-pushed
the
fix/cal-colorspace-images
branch
from
October 28, 2026 18:40
bb2baa5 to
e282c5f
Compare
The only array spaces that reached the generic PAGE_COLORSPACE branch and came back with a device family were /CalGray and /CalRGB; Lab, Pattern and the rest stayed unsupported, and the CMYK case could not be reached. Map the two names directly at both sites (top-level /ColorSpace and /Indexed base) with one small helper, which keeps the behaviour and drops the per-image colour-space resource and the /Separation exclusion. Add the docling#4861 sample (APT002, a /CalGray CCITT G4 scan) to the cited pages: mean_abs_error 38.1 / changed_pixels_ratio 0.111 before the fix, 3.30 / 0.051 after; the rest is anti-aliasing of the downsampled 1-bit scan. Signed-off-by: Christoph Auer <cau@zurich.ibm.com>
cau-git
previously approved these changes
Oct 29, 2026
… truth Dataset PR docling-project#11 adds APT002 (docling#4861) with its ground truth and updates the render ground truth of dln_23cb379b... page 1, whose /CalRGB image is now decoded. Signed-off-by: Christoph Auer <cau@zurich.ibm.com>
cau-git
approved these changes
Oct 29, 2026
PeterStaar-IBM
approved these changes
Oct 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Image XObjects in the CIE-based
/CalGrayand/CalRGBcolour spaces were never drawn. The renderer painted its yellow "missing image" placeholder over them instead, which is what docling-project/docling#4861 reports as an "empty yellow page". PDF/A image-to-PDF converters write/CalGrayfor scanned bilevel pages, and with the default docling pipeline the OCR stage runs on that rendered page, so such a document converted to nothing at all.Standalone PR under #428 (no other open PR touches
page_xobject_image.h). Rebased onmain(7.24.1), no merge commits.Evidence from the regression dataset
dln_23cb379bc6159a37e5488b65bb08ad295ce3ff21d9ddf682064330594ad59db3.pdf, page 1, carries a/CalRGBJPEG (/Im1, 2080x200). Left to right: pypdfium2,main, this PR.Against pypdfium2, over the whole regression corpus (940 sampled pages, scale 2.0, the page set and metric of
test_regression_pypdfium_render.py; before = the 7.24.1 release, which ismain):dln_23cb379b…p1PYPDFIUM_IMAGE_TOLERANCE, and no page moved from within it to above it.The sample from docling#4861 (APT002.pdf, not in the dataset) renders as the scanned invoice, and with this extension the default docling pipeline converts it to 22 text items and 2 tables, the content pdfium yields, where it produced an empty document before.
Root cause
/CalGrayand/CalRGBare always written as an array,[/CalGray << /WhitePoint ... /Gamma ... >>]. The image resource (page_xobject_image.h) only recognised the/ICCBased,/DeviceN,/Separationand/Indexedarrays; anything else kept the array's JSON dump as its colour-space name, the bitmap state reported it as unsupported and no pixels reached the renderer:The vector-colour path already handled both spaces (
page_colorspace.hmaps/CalGrayto gray and/CalRGBto RGB), so only the image path was missing them.Fix
pdf_resource<PAGE_COLORSPACE>and reads the samples as the device space of the family it reports (detail::device_space_for_family). The calibration dictionary only refines how the samples look, which the renderer does not model, so this matches how fill and stroke colours in these spaces are already treated./Indexedimage whose base is[/CalRGB ...]or[/CalGray ...]gets the same mapping forindexed_base_cs. A/Separationbase is skipped there because the existing code parses it as a tint space right below./Labis left as it was: it needs a real L*a*b* to RGB conversion and stays reported as unsupported.Tests
tests/test_regression_pypdfium_cited_pages.py(new, second commit): an enforced comparison against pypdfium2 on the cited page, with its own tolerance (mean_abs_error1.0,changed_pixels_ratio0.02). It fails on 7.24.1 (5.10 / 0.131) and passes with the fix (0.26 / 0.0045). The file holds a list of cited pages, so later rendering fixes can add theirs.tests/test_unit_cal_colorspace_images.pyrenders four synthetic pages and checks the painted block and the white margin: a/CalGrayGroup 4 fax image with the default/Decodeand with/BlackIs1 trueplus/Decode [1 0], a/CalRGB8-bit image, and an/Indexedimage over a/CalRGBbase. All four fail with the placeholder colour(255, 255, 153)before the fix. The Group 4 encoder thattest_unit_ccitt_images.pyused moves totests/image_fixtures.py.test_regression_parse.pyand the new test: pass.test_regression_threaded_render.py: fails only on the ground-truth change below.Ground truth changes
One page:
dln_23cb379bc6159a37e5488b65bb08ad295ce3ff21d9ddf682064330594ad59db3.pdfpage 1. Its image is now decoded, so its render ground truth changes:groundtruth/render/bitmaps/…page_no_1.bitmaps.json.gz:bitmaps[0]getspixel_formatrgb(wasunknown), its decoded shape (was empty),extension.jpg(was.bin), and the hashes andretained_filenameof the decoded image.groundtruth/render/instructions/…page_no_1.instructions.json.gz: the image instruction getspixel_formatrgband its shape.groundtruth/render/bitmap_data/…page_no_1.bitmap_1.jpg: new retained image.…page_no_1.full_page.pngdoes not change: it is pixel-identical to the new render.How I checked that nothing else moves: I regenerated the render ground truth with this branch and compared the decoded contents with the pinned revision. Six other pages also differ on my machine (image hashes on
11315887125936234472_013p3/p13/p14,17068186561688296387-1p1 and5ad06a71-…-2p1; font ascent and descent on07f5395c8b3e7d1c_0001p1 and11564548392525182445_007). They differ the same way with the 7.24.1 release wheel and with a local build ofmainplus unrelated changes, so they come from the local environment, not from this PR.Refs docling-project/docling#4861
Checklist (#428):
main, no merge commits🤖 Generated with Claude Code