A JBIG2 encoder and decoder in Rust for bilevel (black-and-white) images — scanned documents, PDF-embedded streams, and standalone JBIG2 files.
The crate is split into an encode half and a decode half over a shared
protocol core. Both halves are independent Cargo features; a consumer can build
either alone. Both halves are production ready.
Three operating points:
generic— pages encoded as generic JBIG2 regions, no symbol dictionary. Fastest, lossless with respect to the input bitmap, largest output of the three.symbol— connected components are extracted, repeated glyphs are grouped into a symbol dictionary, and page instances reference dictionary entries. Lossy (symbol substitution), much smaller thangenericon text-heavy scans.sym_unify— the symbol pipeline plus a family-unification pass that merges symbol variants across pages (scanning noise, border variation, page-to-page drift) using exact dictionary-entry byte accounting and planner-side anchor remapping. Smallest output; slower than plainsymbol.
Mode selection is by config preset:
use jbig2enc_rust::Jbig2Config;
let generic = Jbig2Config::lossless();
let symbol = Jbig2Config::text();
let sym_unify = Jbig2Config::text_symbol_unify();In-process 350-page comparison against the original C jbig2enc (same
preloaded PBM page set, release builds both sides, page preparation outside
the timed region), corpus sahib2/350p:
c generic: 5228.2 KB in 1.15 srust generic: 5232.2 KB in 2.47 sc symbol: 2566.5 KB in 24.58 srust symbol: 2227.1 KB in 6.05 srust sym_unify: 2025.9 KB in 16.93 s
Relative: Rust generic is size-parity with the C encoder; Rust symbol is
13.2% smaller and about 4.1× faster than C symbol mode; sym_unify is 21.1%
smaller than C symbol mode. Within the Rust encoder, symbol is 57.4% smaller
than generic, and sym_unify is 61.3% smaller than generic (9.0% smaller
than symbol).
A confed/10p release benchmark after the 0.5.3 portable-SIMD comparator
change (wide-based u32x8 kernels in symbol matching, scalar early-exit
preserved):
symbol: 175.3 KB in 0.57 ssym_unify: 145.0 KB in 2.47 s (17.3% smaller thansymbol)
Known encoder limits: the symbol modes leave more material in generic residual regions than an ideal long-book encoder would, and refinement-assisted coding is not implemented. These are compression-efficiency limits; no substitution errors have been observed on the development corpora.
A full-featured JBIG2 decoder covering both embedded (PDF /JBIG2Decode,
globals stream + page stream) and standalone file organisation:
- Generic regions: arithmetic templates 0–3, TPGDON, and MMR.
- Symbol dictionaries and text regions: arithmetic and Huffman variants, including refinement/aggregate coding and custom Huffman tables.
- Refinement regions, pattern dictionaries, and halftone regions.
- Striped pages, intermediate regions, and segment retention handling.
DecodeStrictness::Compatibleaccepts the spec deviations real-world encoders produce and records each recovery as a typedRecoveryEvent;Strictrejects them. Inputs outside the supported surface return typedUnsupportederrors — the decoder does not panic on malformed input.decode_embedded_intodecodes into caller-owned buffers through a pooledDecoderContext(per-worker context, shared immutableDecodedGlobals), for zero-allocation steady state in multi-page workloads. Output is packed 1-bit rows.
Decode benchmarks (release, single thread): A4 generic page ~31 ms (parity
with jbig2dec 0.20), symbol page ~4.4 ms, halftone page ~20 ms. jbig2dec
is used as the differential oracle in the test suite; some validation tools
expect it in PATH.
[dependencies]
jbig2enc-rust = "0.5.3" # encoder + decoder + symboldict (defaults)Decoder only:
[dependencies.jbig2enc-rust]
version = "0.5.3"
default-features = false
features = ["decode"]Encoding a single page:
use jbig2enc_rust::{Jbig2Config, encode_single_image};
let input = vec![0, 1, 0, 1, 1, 0, 1, 0];
let (width, height) = (2, 4);
let result = encode_single_image(&input, width, height, false)?;PDF split output (global dictionary and page-local data separated for embedding):
use jbig2enc_rust::{Jbig2Context, encode_single_image};
let _ctx = Jbig2Context::with_pdf_mode(true);
let result = encode_single_image(&input, width, height, true)?;
// result.global_data — global dictionary segments, if any
// result.page_data — page-local segmentsencode— the encoder half.decode— the decoder half. Either half builds without the other.symboldict— symbol-dictionary encoding (impliesencode; on by default).parallel— Rayon-based parallel helpers where available.tracing,trace_encoder,trace_arith— logging/tracing hooks.line_verify— line verification helpers.refine— refinement-related configuration paths.profiling— profiling support.halftone_bin— image support for the halftone tooling binaries.
src/
encode/ encoder pipeline: api, cc (connected components), classify,
comparator, simd, cost, unify planning, halftone, document assembly
decode/ decoder: segment/page parsing, generic/text/halftone regions,
symbol dictionaries, huffman, mmr, refinement, arith, context pooling
shared/ protocol core shared by both halves
cargo test # full suite
cargo test --no-default-features --features decode # decoder half alone
cargo test --test comparator # symbol comparator
cargo test --test perf_smoke -- --ignored --nocaptureThe encoder's byte-identical output guard lives in
tests/encode_byte_identical.rs (regenerate hashes with
UPDATE_ENCODE_HASHES=1).
MIT OR Apache-2.0.
When built with symboldict, the symbol matching path includes code adapted
from djvulibre; binaries built with that functionality may carry GPL
implications. Review your build configuration before distributing.
- ISO/IEC 14492:2001 (JBIG2)
- Original
jbig2enc: https://github.com/agl/jbig2enc djvulibre: https://github.com/djvuzone/djvulibre- Leptonica's JBIG2 classifier notes
- M. Valliappan, B. L. Evans, D. A. D. Tompkins, F. Kossentini, "Lossy Compression of Stochastic Halftones with JBIG2" (informed the halftone path)
- Symbol-dictionary design literature consulted for the
sym_unifycost model: Ye/Schilling/Cosman/Koy, "Symbol Dictionary Design for the JBIG2 Standard"; Ye/Cosman, "Fast and Memory Efficient JBIG2 Encoder"; Figuera/Yi/Bouman, "A New Approach to JBIG2 Binary Image Compression"; Guo/Depalov/Bauer/Bradburn/Allebach/Bouman, "Binary Image Compression Using Conditional Entropy-Based Dictionary Design and Indexing"