Optional module · pdftoolkit/pdf-codecs 0.1.0
English | Français
PDFToolkit\Codecs\Jbig2\Jbig2Decoder implements Decoder for a single bilevel image. It accepts PDF embedded segment streams, optional already-decoded global segments, and single-page sequential/random-access T.88 files. It returns one unsigned, one-bit Gray component, MSB first with byte-aligned rows: 0 is black, 1 is white. Unused low bits in the last byte of each row are zero. The internal T.88 bitmap convention is reversed only at the public output boundary.
use PDFToolkit\Codecs\Codec;
use PDFToolkit\Codecs\DecodeLimits;
use PDFToolkit\Codecs\DecodeRequest;
use PDFToolkit\Codecs\Jbig2\Jbig2Decoder;
$image = (new Jbig2Decoder(new DecodeLimits(maxPixels: 4_000_000)))
->decode(new DecodeRequest(Codec::Jbig2, $encoded, globals: $decodedGlobals));
The caller supplies bytes. This package does not resolve PDF references, decrypt streams, apply preceding PDF filters, open files, execute processes or infer a certificate/trust policy. The Reader adapter owns those tasks. A page number greater than one can identify the single extracted page of a standalone source; accepting it is not a claim that its stream meets the stricter PDF 2.0 embedded-page association rule.
Implementation and conformance matrix
References below are to the official ITU-T T.88 (08/2018), ISO/IEC 14492:2019. “Physical” counts PDF pages from one, including front matter; printed pages are 12 lower. Tests are in tests/Jbig2; the fixture manifest records every source hash, expected sample hash, mechanism and required proof. This matrix describes the implemented mechanisms, not a universal certification of arbitrary input.
| Mechanism | Clauses / physical pages | Objects | Evidence |
|---|---|---|---|
| Sequential, random-access and embedded organization; global/page references, retention, dependency depth, short/long reference counts | 7.2–7.3, 60–67; Annex D | SegmentReader, Segment, Jbig2Session |
Annex H split pages/globals; organization, references and malformed framing regressions |
| Arithmetic generic bitmaps, templates 0–3, adaptive coordinates and typical-row prediction | 6.2.5, 27–31 | GenericBitmapDecoder, ArithmeticContext |
All four templates with/without prediction, displaced/overlapping AT; independent jbig2enc → libjbig2dec comparisons |
| Extended template with 12 adaptive pixels | Figure 3b, 28; 7.4.6.2–3, 85–86 | GenericBitmapDecoder, Jbig2Session |
Independently MQ-encoded normative vector plus cross-review of fixed/nominal context positions; external-decoder limitation below |
| MMR generic, collective and pattern bitmaps; grayscale bitplane delimiters | 6.2.6, 32; Annex C | MmrBitmapDecoder, MmrRowReader, MmrCodes |
Annex H MMR page/pattern/halftone bitmaps, known/unknown length and EOFB regressions |
| Generic refinement, templates 0/1, reference offsets, typical-pixel prediction, intermediate/page-buffer references | 6.3, 33–36; 7.4.7, 87–89 | GenericBitmapDecoder, RegionBitmap |
Independent MQ encoder; libjbig2dec page-buffer and Poppler intermediate comparisons; normative transport/prediction vectors |
| Text: arithmetic/Huffman, strips, four reference corners, transposition, OR/AND/XOR/XNOR, refinement | 6.4, 37–45; 7.4.3, 71–80 | TextRegionDecoder, TextParameters |
32 corner/transposition/operator fixtures, Annex H, Huffman refinement checked with Poppler |
| Symbols: arithmetic direct coding, Huffman raw/MMR collective bitmap, single refinement and multiple-instance aggregation | 6.5, 45–52; 7.4.2, 67–71 | SymbolDictionaryDecoder |
Annex H, raw collective/custom table, arithmetic/Huffman aggregation and refinement fixtures |
| Retained GB/GR bitmap contexts with fresh integer contexts | 7.4.2.2, 70; E.3.7–8 | ArithmeticStates, ArithmeticContext, SymbolDictionary |
Independent encoder restarted with retained statistics; two branches copy one prior dictionary; normative cross-review |
| Halftones/patterns: arithmetic/MMR, templates 0–3, Gray-to-binary planes, skip masks, signed grid origin, five combination operators and both default pixels | 6.6–6.7, 53–59; 7.4.4–5, 81–84; Annex C | HalftoneDecoder, PatternDictionary, Bitmap |
80 template/skip/operator/default fixtures plus Annex H; libjbig2dec on default 0, explicit normative default-1 comparisons |
| Standard Huffman tables B.1–B.15, custom canonical tables, ID run tables, OOB compatibility | 7.4.13, 92; Annex B | StandardTables, HuffmanTable, HuffmanSelection |
Custom height table, raw/Huffman aggregate paths, duplicate-prefix/OOB/unused-table rejection |
| Page defaults/combination, stripes and unknown heights, unknown-length generic regions, optional metadata/extensions | 7.4.8–14, 89–93 | Jbig2Session, SegmentReader |
Striped/unknown-length arithmetic and MMR tests, cumulative metadata and necessary-extension rejection |
In 7.4.6.3 the prose says “32-byte”, but Figure 50 and the explicit fields enumerate bytes 0 through 23, representing twelve coordinate pairs. The implementation uses those 24 bytes. Context bit identity is fixed by the nominal template, even when adaptive pixels move or overlap; 6.2.5.7 allows a bit order independent of the adaptive positions.
T.88 6.2.6 expressly prohibits the T.6 extension codes, including uncompressed mode, in its MMR profile. The separate Reader CCITT filter can support that mode; the JBIG2 decoder rejects it. MMR alignment padding is skipped as T.88 prescribes. EOFB is mandatory for halftone bitplanes and the unknown-length MMR path, optional where a compressed byte count provides the boundary.
Explicit scope and bounds
Coloured T.88 extensions/palettes, multiple-page image requests and unknown necessary extensions produce UnsupportedFeature. They are not converted to bilevel or silently discarded. Optional extensions and profile declarations do not change image pixels; their bytes share a cumulative metadata budget. Unknown/reserved or inconsistent coding fields, invalid references, incompatible retained contexts, malformed runs/tables and truncated framing produce MalformedImage. A request that exceeds configured resources produces ResourceLimit.
A fresh DecodeBudget covers encoded bytes including globals, pixels/dimensions, output bytes, cumulative allocated buffers, segment/reference counts, dependency depth and work. Global dictionaries, page bitmaps, intermediate regions, refinements and copied context arrays share that budget. Bitmap storage is packed; intermediate glyphs may be empty, but the returned image has positive dimensions. This accounting is not a PHP process memory limit; applications can additionally bound their process memory and time.
The decoder reconstructs the image encoded in the source. It cannot detect whether a lossy JBIG2 encoder substituted one glyph for another before producing those bytes.
Validation and independent-oracle limits
Run:
php vendor/bin/phpunit tests/Jbig2
python tools/validate_jbig2.py --php php --report _build/jbig2-validation.json
The oracle needs Python with pypdf and Pillow, libjbig2dec.so.0 version 0.20, and Poppler pdfimages (--pdfimages accepts an explicit executable). Tested tool versions include Poppler 22.12 and 26.10. The report path is arbitrary; adjacent PDF/PNG artifacts retain the encoded region bytes used for Poppler. End-page/end-file transport controls alone are removed when those streams are embedded in a PDF image.
The current manifest contains 145 vectors: 92 require exact libjbig2dec output, 8 require exact Poppler output, and 45 are explicitly labelled normative vectors, not independent-decoder confirmations. PHP output must match every expected dimension and sample hash. Both external outcomes remain visible in the report, even when a known external limitation is not the required proof. independentCoverageComplete therefore remains false.
Observed external limits:
- libjbig2dec 0.20 rejects intermediate generic regions and retained contexts, misdecodes Huffman refinement/aggregation cases that Poppler reproduces exactly, and ignores the 2018 extended-template bit 4.
- libjbig2dec 0.20 fills the halftone default bitmap with byte
0x01when HDEFPIXEL is 1 (jbig2_halftone.c), whereas the standard requires every pixel to be 1 (0xff). All corresponding default-0 cases compare independently; default-1 vectors also have a normative cross-review of filling, clipping and the five operators. - Poppler's typical-refinement and retained-context results differ on these vectors. Equivalent page-buffer typical-refinement vectors agree with libjbig2dec. These limitations are not hidden by widening a pixel tolerance.
- The official 2018 ZIP's small TT2–TT7 files contain missing symbol references, inconsistent export runs or damaged MMR data that libjbig2dec independently reports. They are not installed as valid golden fixtures. Its two large TT9/TT10 samples matched the official BMPs and libjbig2dec exactly during development; the durable compact normative sequence comes directly from Annex H.
Rebuild synthetic fixtures with the pinned independent Apache-2.0 jbig2enc arithmetic encoder. Its source is test tooling only and is not linked into the PHP package:
# jbig2enc revision 4553595aa094cad7d55c4efc9657ea31db23c742
c++ -O2 -I /path/to/jbig2enc/src tools/jbig2_encoder.cpp -o /path/to/jbig2-encoder
python tools/generate_jbig2.py --encoder /path/to/jbig2-encoder
The generator defines image/segment layouts and expected pixels independently of the PHP implementation. The MQ encoder supplies compression; separate libjbig2dec/Poppler processes supply decoding comparisons. Fixtures and generator metadata preserve their exact provenance in tests/Jbig2/Fixtures/README.md and provenance.json.