feat(pdf): decode jbig2 images - #703
Merged
Merged
Conversation
There was a problem hiding this comment.
💡 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".
andiwand
force-pushed
the
fix/pdf-word-spacing-rotation-glyph-names
branch
from
August 18, 2026 18:09
f03169c to
b7426e2
Compare
andiwand
force-pushed
the
feat/pdf-jbig2
branch
from
August 18, 2026 18:09
b64b1de to
81191cc
Compare
Base automatically changed from
fix/pdf-word-spacing-rotation-glyph-names
to
main
August 18, 2026 18:10
andiwand
force-pushed
the
feat/pdf-jbig2
branch
2 times, most recently
from
August 18, 2026 18:19
0653899 to
da4b56a
Compare
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
force-pushed
the
feat/pdf-jbig2
branch
from
August 18, 2026 18:23
da4b56a to
02e476f
Compare
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.
🤖 Generated with Claude Code
Stacked on #702.
Three scanned certificate pages of
Fahrradtraeger_Uebler_Primavelo_P2_P3.pdfcame out blank. Their page body is a
/JBIG2Decodeimage mask: the filterframework stopped at the codec and
Doskipped the image. There is no libraryto defer to the way
pdf_jpxdefers to openjpeg, sopdf_jbig2decodes it inhouse — ~900 lines, no new dependency.
What it covers
The coding a scanner emits, and nothing beyond it:
TPGDON)/JBIG2GlobalsincludedMMR/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
ATpixels, which is what theTPGDONcontext constantsassume.
/JBIG2Globalsnames its shared symbol dictionary by reference. Only theparser can follow one, so the bytes reach the filter through a new
DecodeOptionsrather than the filter growing document knowledge.Test
page association), the page and packing path, and every refusal.
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.
is no encoder to round-trip against and I did not want to invent a test
vector.