Skip to content

Decode palette PNGs instead of dropping them - #19

Merged
mantissaman merged 1 commit into
mainfrom
fix/palette-png
Jul 29, 2026
Merged

Decode palette PNGs instead of dropping them#19
mantissaman merged 1 commit into
mainfrom
fix/palette-png

Conversation

@mantissaman

Copy link
Copy Markdown
Contributor

Closes #17.

The bug

decode_image mapped PNG colour types to a channel count and sent anything unrecognised to a catch-all:

let channels: usize = match color_type {
    0 => 1, 2 => 3, 4 => 2, 6 => 4,
    _ => return None,
};

Colour type 3, indexed colour, landed there. Every caller treats None as nothing to draw, so an indexed PNG was silently omitted from both PDF and PNG output.

These are not unusual. Most tools default to a palette when an image has few colours, and Word embeds whatever it is handed. The file attached to #3 carries a 640x480 8-bit colormap PNG, which is why that document looked like it had no image even after #10 corrected the anchor position.

The fix

PLTE supplies the palette and tRNS supplies per-entry alpha when present. Each index is expanded to RGB or RGBA after unfiltering, which is where the existing code already has a clean one-byte-per-pixel scanline to work from.

Three decisions worth calling out:

  • An index past the end of the palette is malformed input. It draws black rather than discarding the whole image, so one bad byte does not cost you the picture.
  • A missing PLTE is still rejected, because there is nothing to expand against.
  • Fully opaque images carry no alpha channel, matching what the RGBA path already does, so PDF viewers do not get an SMask they do not need.

Bit depths below 8 are still rejected by the existing bit_depth != 8 check. Sub-byte indexed images stay unsupported rather than silently wrong, and that is now a deliberate limit rather than an accident of the catch-all.

Verified

Four tests build indexed PNGs from scratch and cover palette expansion, tRNS alpha including entries it does not mention, the missing palette case, and the out-of-range index case.

Rendering the file from #3 with this and #18 applied together, the image finally appears, in the right place, with its transparency intact. The page went from 19 KB to 70 KB of PNG output.

Checks

cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings and the full suite all pass. 347 tests, up from 343, with four added here.

python3 scripts/hash_harness.py --check also passes, 28 entries matching. Worth stating plainly why: none of the harness fixtures contains an indexed PNG, so the baselines are genuinely unaffected. This change does alter output for any document that does contain one, which is the entire point of it.

decode_image mapped PNG colour types to a channel count and sent
anything unrecognised to a catch-all that returned None. Colour type 3,
indexed colour, landed there. Every caller reads None as nothing to
draw, so an indexed PNG was silently omitted from both PDF and PNG
output with no warning anywhere.

These are not unusual. Most tools default to a palette when an image
has few colours, and Word embeds whatever it is given. The file
attached to #3 carries a 640x480 8-bit colormap PNG, which is why that
document appeared to have no image even after the anchor position was
corrected in #10.

Colour type 3 is now handled: PLTE supplies the palette, tRNS supplies
per-entry alpha when present, and each index is expanded to RGB or RGBA
after unfiltering. An index past the end of the palette is malformed
input, so it draws black rather than throwing the whole image away, and
a missing PLTE is still rejected. Fully opaque images carry no alpha
channel, matching what the RGBA path already does so PDF viewers do not
get an unnecessary SMask.

Bit depths below 8 are still rejected by the existing bit_depth check,
so sub-byte indexed images remain unsupported rather than wrong.

Closes #17.
@mantissaman
mantissaman merged commit 760f45d into main Jul 29, 2026
7 checks passed
@mantissaman
mantissaman deleted the fix/palette-png branch July 29, 2026 22:49
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.

Palette PNGs (colour type 3) are silently dropped from all output

1 participant