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 0x01 when 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.

Guide source at the documented commit ↗