BUG: Avoid flattening single-colorant /DeviceN images to grayscale#3916
Open
pablopupo wants to merge 1 commit into
Open
BUG: Avoid flattening single-colorant /DeviceN images to grayscale#3916pablopupo wants to merge 1 commit into
pablopupo wants to merge 1 commit into
Conversation
A /DeviceN color space with one colorant and a /DeviceCMYK alternate was always treated as grayscale, even for colorants other than /Black, logging a "converted to Gray" warning. /Separation already resolves this by deriving the mode from the alternate color space instead, so /DeviceN now falls through to that same recursive lookup for any colorant besides /Black, which is the one case that maps onto grayscale exactly. Closes py-pdf#3302
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3916 +/- ##
==========================================
+ Coverage 97.86% 97.88% +0.01%
==========================================
Files 57 57
Lines 10738 10736 -2
Branches 2006 2005 -1
==========================================
Hits 10509 10509
+ Misses 127 126 -1
+ Partials 102 101 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Fixes #3302.
_get_image_mode has a /Separation branch that resolves its Pillow mode by recursing into the color space's alternate space, and a /DeviceN branch a few lines below that does mostly the same thing except for one special case. Any single colorant paired with a /DeviceCMYK alternate went straight to grayscale, warning "Color %s converted to Gray" for every colorant besides /Black. That shortcut was added in #2322 for K-only scanner images, where grayscale is exact, but it swallowed every other single-colorant DeviceN image the same way, which is what the reporter's /Cyan image was hitting.
The fix narrows that shortcut to require the colorant literally be /Black before returning grayscale directly. Everything else now falls through to the same recursive alternate-space lookup /Separation already uses (that recursion resolves a /DeviceCMYK alternate straight to CMYK on its own, no new codepath needed). I also checked whether _apply_decode's Separation-only decode-inversion override should be extended to DeviceN too, and it should not. _image_from_bytes's existing byte-replication fallback already gets the light-to-dark direction right on its own, and stacking Separation's decode reversal on top would double-invert it. Left it alone since it is a separate, pre-existing issue outside the scope of #3302.
I did not have a working copy of the reporter's PDF (an issuu.com viewer link with time-limited AWS download links, 50 MB), so I built a synthetic single-colorant /Cyan DeviceN CMYK image to verify. Before the fix, the extracted pixels for tints 0/128/255 come out (255,255,255), (127,127,127), (0,0,0), identical byte for byte to what a /Black-colorant image produces (mode L, plus the warning). After the fix, they come out (255,255,255), (63,63,63), (0,0,0) in mode CMYK with no warning. The /Black case is unchanged, still mode L, byte for byte.
Added two regression tests in tests/generic/test_image_xobject.py, one at the _get_image_mode level and one round-tripping through _xobj_to_image. Ran the full test_filters.py and tests/generic/ suites (162 passed, 5 pre-existing skips) plus ruff, mypy, and pyupgrade, all clean.