Table of Contents

JBIG2

Egami reads JBIG2 (ITU-T T.88 | ISO/IEC 14492), the bi-level format of scanned documents and of the JBIG2Decode images in PDF, written from the 2018 edition of T.88 and the PDF specification. Jbig2Format.Info is registered in EgamiContext.Default. JBIG2 is never written. Every page of Egami's corpus (made with jbig2enc in each of its modes), of PDFBox's JBIG2 test streams and of the T.88 Annex H datastream decodes bit for bit as PDFium and jbig2dec decode it.

Reading files

A standalone file starts with the T.88 file header, so Raster.Load recognizes it. Each page loads as Gray8: black 0, white 255, as 1-bit TIFF does. Load reads the first page, or the one Jbig2ReaderOptions.Page names; LoadAll reads every page at its own size, and RasterInfo.ImageCount says how many there are without decoding any.

// A standalone .jb2 file is detected like any other format; each page loads as Gray8, black 0 and white 255.
using Raster first = Raster.Load("book.jb2");
using RasterList pages = Raster.LoadAll("book.jb2");
using Raster third = Raster.Load("book.jb2", options: new Jbig2ReaderOptions { Page = 2 });

Images from a PDF

A PDF holds JBIG2 in T.88's embedded form: segments without a file header, and often a separate JBIG2Globals stream with the symbol dictionaries several images share. The embedded form has no signature, so load it with the format named. Jbig2Globals.Parse decodes the globals once; the object is immutable and can be shared by every image of the document, on any thread.

// A PDF's JBIG2Decode image has no file header, so name the format. Parse the JBIG2Globals stream once and share it:
// every image of the document that refers to it reuses its decoded symbols.
Jbig2Globals globals = Jbig2Globals.Parse(globalsStream);
using Raster image = Raster.Load(imageStream, Jbig2Format.Info, options: new Jbig2ReaderOptions { Globals = globals });

A PDF image's /Decode array and its color space stay the PDF library's business: Egami returns the page as T.88 defines it.

What is decoded

Segment Read
Generic regions Templates 0 to 3 and the extended template, adaptive pixels anywhere, typical prediction, skipped pixels; MMR through Egami's CCITT Group 4 decoder
Refinement regions Templates 0 and 1, typical prediction, against the page or an intermediate region
Symbol dictionaries, text regions Arithmetic and Huffman, refinement and aggregation, every reference corner and transposition, standard tables B.1 to B.15 and code table segments
Pattern dictionaries, halftone regions Arithmetic and MMR gray-scale planes, any grid, skipping
Pages Known and unknown height, striped, every combination operator, intermediate regions; end of file and random-access files
Extensions Comments into Jbig2Details.Comments; others skipped unless marked necessary, which throws NotSupportedException
Colour palettes (T.88 Amendment 3) Skipped with a warning; the page decodes as bilevel

Jbig2Details reports, per page, the stream's Jbig2Organization, whether the page used the globals, its striping, default pixel and Jbig2CombinationOperator, the Jbig2RegionKinds it is made of and whether it is eventually lossless. The page information's resolution goes to RasterMetadata.Resolution.

using Raster page = Raster.Load("book.jb2");
var details = (Jbig2Details)page.Metadata.FormatDetails!;
Console.WriteLine($"Page {details.PageIndex}, {details.Organization}, regions {details.Regions}");
Console.WriteLine($"Default pixel {details.DefaultPixel}, combined by {details.CombinationOperator}, lossless {details.Lossless}");

Damaged and hostile data

JBIG2 decoders are a long-standing attack surface (FORCEDENTRY, in 2021, was an integer overflow in one), so every size is checked arithmetic and counted against SafetyLimits before memory is taken: page and region sizes against MaxWidth, MaxHeight and MaxPixels; symbols, patterns, tables and every bitmap together against MaxTotalBytes. A segment that refers to a segment that is not before it, in the stream or in the globals, always throws. In lenient mode a damaged region is skipped with a warning and the rest of the page decodes; strict mode throws CorruptDataException.

The ITU's own conformance streams for T.88 mostly break the Recommendation (their text regions refer to no symbol dictionary), and PDFium and jbig2dec reject them as Egami does; the two that follow it, and the Annex H datastream, decode exactly.