Skip to content

Fix #1534: TIFF/1-bit indexed images rendered inverted#1597

Open
andreasrosdalw wants to merge 1 commit into
LibrePDF:masterfrom
andreasrosdalw:fix-1534-indexed-1bit-inverted
Open

Fix #1534: TIFF/1-bit indexed images rendered inverted#1597
andreasrosdalw wants to merge 1 commit into
LibrePDF:masterfrom
andreasrosdalw:fix-1534-indexed-1bit-inverted

Conversation

@andreasrosdalw

@andreasrosdalw andreasrosdalw commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #1534.

Root cause

The indexed-color fast path added in #1499 (3.0.1) and reworked in #1524 (3.0.2) builds the PDF image via Image.getInstance(width, height, 1, bitsPerPixel, pixelData). For 1-bit data that factory has a special case that CCITT-G4-compresses the pixels and returns a CCITT image with CCITT_BLACKIS1 semantics:

if (components == 1 && bpc == 1) {
    byte[] g4 = CCITTG4Encoder.compress(data, width, height);
    return Image.getInstance(width, height, false, Image.CCITTG4, Image.CCITT_BLACKIS1, g4, transparency);
}

This breaks 1-bit indexed images twice over:

  • PdfImage never applies the additional /Indexed colorspace dictionary to CCITT images, so the palette is silently dropped and the data is rendered as DeviceGray.
  • With BlackIs1, palette index 1 renders as black. A bilevel TIFF with PhotometricInterpretation = WhiteIsZero decodes to a TYPE_BYTE_BINARY BufferedImage with palette {0 = white, 1 = black}, so black and white swap — exactly the inversion reported in the issue. 3.0.0 was unaffected because this code path didn't exist yet (images went through the generic RGB path).

Fix

Create the ImgRaw directly in the indexed path so the pixel data stays palette indices and the /Indexed colorspace is preserved. The image is still flate-compressed by PdfImage as before.

Verification

  • Two new regression tests: a 1-bit TYPE_BYTE_BINARY image with a {white, black} palette, and the issue's exact flow (binary image → PNG bytes → Image.getInstance(byte[])). Both resolve every pixel of the resulting image through the embedded palette and compare with the source BufferedImage; both fail before the fix and pass after.
  • End-to-end check with the sample.tif attached to TIFF images are rendered inverted starting with 3.0.1 #1534 (decoded with TwelveMonkeys, converted with the reporter's reproduction code): the image embedded in the produced PDF now matches all 8,748,480 pixels of the original TIFF exactly (previously fully inverted).
  • Full openpdf-core test suite passes (2082 tests, 0 failures).

…f CCITT-encoding them

The indexed-color path introduced in LibrePDF#1499/LibrePDF#1524 created the image via
Image.getInstance(width, height, 1, bpc, pixelData), which for 1-bit data
compresses the pixels with CCITT G4 and BlackIs1 semantics. That discards
the palette (PdfImage never applies the additional /Indexed colorspace to
CCITT images) and renders palette index 1 as black. Bilevel TIFFs decoded
with PhotometricInterpretation WhiteIsZero (palette {white, black}) were
therefore rendered inverted since 3.0.1.

Create the ImgRaw directly so the pixel data stays palette indices and the
/Indexed colorspace is preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@andreasrosdalw andreasrosdalw changed the title Fix #1534: TIFF/1-bit indexed images rendered inverted since 3.0.1 Fix #1534: TIFF/1-bit indexed images rendered inverted Jul 21, 2026
@sonarqubecloud

Copy link
Copy Markdown

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 6 complexity · 2 duplication

Metric Results
Complexity 6
Duplication 2

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.

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.

TIFF images are rendered inverted starting with 3.0.1

1 participant