Optional module · pdftoolkit/pdf-codecs 0.1.0
English | Français
PDFToolkit\Codecs\Jpeg\JpegDecoder decodes the eight-bit Huffman DCT processes used by PDF: baseline sequential (SOF0), extended sequential (SOF1) and progressive (SOF2). It performs no file access or process execution. Each call owns its parser, scan history, tables, coefficients and budgets.
use PDFToolkit\Codecs\Codec;
use PDFToolkit\Codecs\DecodeLimits;
use PDFToolkit\Codecs\DecodeRequest;
use PDFToolkit\Codecs\Jpeg\JpegDecoder;
$decoder = new JpegDecoder(new DecodeLimits(maxPixels: 4_000_000));
$image = $decoder->decode(new DecodeRequest(Codec::Jpeg, $jpegBytes, colorTransform: null));
$firstComponent = $image->components[0];
$sample = $firstComponent->sample(0, 0);
The decoder accepts one to four components, interleaved and separate scans, sampling factors 1–4 (including fractional expansion ratios), 8/16-bit quantization tables where the process permits them, Huffman table redefinitions, byte stuffing and ordered restart markers. Progressive spectral selection, successive approximation, EOB runs and coefficient refinement are decoded with per-component scan-order validation. An initial DC-only progressive image can be decoded with untransmitted AC coefficients set to zero. Quantization tables are retained for a component when its first scan starts, as required when table destinations are reused for later components.
The inverse DCT follows ITU T.81 Annex A's separable mathematical definition. Subsampled components are expanded by nearest-neighbour replication to the full image dimensions. Every returned ComponentPlane therefore has precision 8, unsigned samples, separation 1 and exactly width × height bytes, in original component order. This is not a resizing or colour-management API. Other decoders' interpolation and integer inverse transforms can produce slightly different samples.
Colour rules
The Adobe APP14 transform takes precedence over the request's colorTransform. Without APP14, an explicit 0 or 1 applies; when both are absent, three components default to transform 1 and other component counts to 0. JFIF and component identifiers such as R/G/B do not override this PDF rule. A parameter of 1 is ignored for one or two components.
Transform 0 preserves decoded component values. Transform 1 converts YCbCr to RGB for three components, or YCCK to CMYK for four components. For YCCK, the first three transformed components are 255-R, 255-G, 255-B; K is passed through unchanged. Adobe marker value 2 explicitly identifies YCCK and also overrides ColorTransform. There is no automatic inversion of all CMYK components and no CMYK-to-RGB conversion. PDF /Decode arrays, colour spaces and colour management remain the caller's responsibility.
ColorInformation identifies Gray for one component, RGB/CMYK after the corresponding transform, and Unknown for untransformed multicomponent samples: the count alone does not establish their PDF colour space. ICC APP2 chunks are reassembled in sequence and retained as opaque bytes in iccProfile; the decoder does not apply the profile. Missing, duplicate or inconsistent chunks are rejected.
Bounds and failures
DecodeLimits controls encoded input, output, dimensions, pixels, components, cumulative memory allocations, work, segment count and metadata bytes. Restart markers consume the same segment budget as outer markers. Metadata includes ignored APP/COM payloads. Packed signed coefficient storage prevents a PHP hash-array allocation for every coefficient. Allocation is checked before constructing sample/coefficient buffers; inverse transforms and entropy reads consume work. Limits are reset for each decode call, including after a failure. Allocation accounting is conservative and cumulative, not a guarantee about the PHP process's total memory use. JPEG uses no recursive tree or tile structure; maxDepth and maxTiles do not impose additional JPEG limits.
MalformedImage identifies invalid lengths, tables, references, scan ordering, entropy padding or restarts. ResourceLimit identifies an exhausted configured budget. UnsupportedFeature identifies a process outside this PDF DCT profile: non-eight-bit samples, arithmetic coding, lossless/hierarchical JPEG, DNL-dependent height or unsupported extension markers. The decoder does not recover missing scans, invent a height, search past damaged entropy data or silently lower precision. It requires SOI, a frame, initial DC data for every component and EOI; bytes after EOI are outside the decoded image.
Reproducible validation and provenance
php vendor/bin/phpunit tests/Jpeg
python -m pip install --require-hashes -r tools/jpeg-validation-requirements.txt
python tools/validate_jpeg.py --php php --report _build/jpeg-validation.json
The oracle requires pdfimages from Poppler and Python 3.13 or later. Its 25 independently encoded fixtures cover sequential/progressive data, restarts, odd dimensions, grey/RGB/CMYK/YCCK, RGB-coded Adobe images, separate scans and horizontal/vertical subsampling. Every quantized coefficient is compared exactly with IJG libjpeg through jpeglib. Pixels use an explicit maximum tolerance of 1 level for grey/raw CMYK and 3 levels for transformed RGB/YCCK, plus mean error ≤0.15, with fancy upsampling disabled for a comparable replication policy. Fourteen full-resolution cases also compare JPEG and decoded-component PDF images extracted by Poppler, with a maximum tolerance of 3. Subsampled Poppler output is not treated as a pixel oracle because its interpolation differs.
Unit tests additionally exercise known coefficients, two components, SOF1 and 16-bit quantizers, fractional 3:2 sampling, table reuse, APP14 precedence, unchanged K, ICC assembly, malformed scans and limits. tools/jpeg_generate.py regenerates the committed fixtures using independent Pillow/libjpeg and jpeglib encoders; fixture hashes and encoder versions are recorded in their manifest. These tests establish the stated cases, not universal JPEG conformance.
Progressive entropy algorithms were reviewed and adapted from Mozilla PDF.js at revision 24a63a4d488d4fe26c2a5ca32216ac9698d0de59 (Apache-2.0). The exact source hashes, notices, licence and adaptation boundaries are retained in resources/jpeg. Its rendering API, error recovery and IDCT implementation were not ported. Normative anchors are ISO 32000-2 §7.4.8, ITU T.81 Annexes A/B/C/F/G and Adobe Technical Note 5116 §§6, 13, 15 and 18.