Skip to content

feat(pdf): decode jbig2 images - #703

Merged
andiwand merged 1 commit into
mainfrom
feat/pdf-jbig2
Aug 18, 2026
Merged

feat(pdf): decode jbig2 images#703
andiwand merged 1 commit into
mainfrom
feat/pdf-jbig2

Conversation

@andiwand

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Stacked on #702.

Three scanned certificate pages of Fahrradtraeger_Uebler_Primavelo_P2_P3.pdf
came out blank. Their page body is a /JBIG2Decode image mask: the filter
framework stopped at the codec and Do skipped the image. There is no library
to defer to the way pdf_jpx defers to openjpeg, so pdf_jbig2 decodes it in
house — ~900 lines, no new dependency.

What it covers

The coding a scanner emits, and nothing beyond it:

  • the MQ arithmetic decoder (T.88 Annex E, software conventions)
  • the arithmetic integer and symbol-id decoders (Annex A)
  • generic regions, all four templates, with typical prediction (TPGDON)
  • symbol dictionaries and text regions, every reference corner and transposed
  • page information and composition through the external combination operators
  • the PDF embedded-stream organization, /JBIG2Globals included

MMR/Huffman coding, refinement and halftone regions are refused. Refusing skips
the image exactly as today rather than painting a page missing its content.

Notes

The generic-region template is sorted by row and column rather than laid out in
the spec's bit order. That is a relabeling of the arithmetic contexts, and the
decoder adapts per context, so any bijection over the same pixel set decodes
identically — the same trick pdf.js uses, and the ordering it produces is the
spec's for nominal AT pixels, which is what the TPGDON context constants
assume.

/JBIG2Globals names its shared symbol dictionary by reference. Only the
parser can follow one, so the bytes reach the filter through a new
DecodeOptions rather than the filter growing document knowledge.

Test

  • 10 unit tests over the segment-header walk (long referred-to form, four-byte
    page association), the page and packing path, and every refusal.
  • Full suite green. Exactly one reference-output file changes — the pdf that
    prompted this — and its three scanned pages now match a ghostscript render of
    the same file, at the mask's own 2352×3328 rather than the raster's.
  • The correctness of the arithmetic decoding is guarded by that snapshot; there
    is no encoder to round-trip against and I did not want to invent a test
    vector.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b64b1de9dd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/odr/internal/pdf/pdf_jbig2.cpp
Comment thread src/odr/internal/pdf/pdf_jbig2.cpp
Comment thread src/odr/internal/pdf/pdf_jbig2.cpp
@andiwand
andiwand force-pushed the fix/pdf-word-spacing-rotation-glyph-names branch from f03169c to b7426e2 Compare August 18, 2026 18:09
Base automatically changed from fix/pdf-word-spacing-rotation-glyph-names to main August 18, 2026 18:10
@andiwand
andiwand force-pushed the feat/pdf-jbig2 branch 2 times, most recently from 0653899 to da4b56a Compare August 18, 2026 18:19
A scanned page whose body is a `/JBIG2Decode` image mask came out blank: the
filter framework stopped at the codec and the image was skipped. There is no
library to defer to for JBIG2 the way `pdf_jpx` defers to openjpeg, so
`pdf_jbig2` decodes it in house.

Covered is the coding a scanner emits: the MQ arithmetic decoder (T.88 Annex
E), the arithmetic integer and symbol-id decoders (Annex A), generic regions
for all four templates including typical prediction, symbol dictionaries and
text regions, and page composition through the external combination operators.
MMR/Huffman coding, refinement and halftone regions are refused, which skips
the image exactly as before rather than painting a page missing its content.

`/JBIG2Globals` names its symbol dictionary by reference and only the parser
can follow one, so the bytes reach the filter through a new `DecodeOptions`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014VsAxi7RXU1TNQVz8EM7VJ
@andiwand
andiwand merged commit 28d24a8 into main Aug 18, 2026
18 of 19 checks passed
@andiwand
andiwand deleted the feat/pdf-jbig2 branch August 18, 2026 18:24
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