Pro edition
Filter — Deep Reference
At a glance
Section titled “At a glance”This page is the contract-level reference for the NextPDF Pro Filter module, namespace NextPDF\Pro\Filter. The surface consists of two classes. DecodeParms parses a PDF /DecodeParms dictionary fragment into an immutable, bounds-checked value object. PngPredictor reverses the PNG predictor family (tags 10-15) on FlateDecoded stream bytes. The module serves the Pro Diff and Classifier extractors. It is not a general stream-filter framework. This page states the public API, the observable behavior contract, and the typed failure modes. Usage guidance and code samples live on the Filter capability page.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Pro (nextpdf/pro) and activates with a Pro-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
No runtime capability flag gates this module. The Filter classes are available whenever nextpdf/pro is installed.
Public API surface
Section titled “Public API surface”| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
DecodeParms::__construct | int $predictor = 1, int $columns = 1, int $colors = 1, int $bitsPerComponent = 8 | Defaults encode “no predictor” | — | — | final readonly; all four properties are public and immutable |
DecodeParms::fromDictionary() | string $raw — raw dictionary text, surrounding object body tolerated | Absent keys keep their defaults; matching is whitespace-tolerant | self | InvalidArgumentException | Parse-time chokepoint; bounds listed in the behavior contract |
DecodeParms::isPngPredictor() | none | Pure predicate; no I/O | bool — true for predictor 10-15 | — | Branch on this before calling the reverse filter |
PngPredictor | — | Stateless | — | — | final; the single entry point is the static inverse() |
PngPredictor::inverse() | string $raw, int $columns, int $colors, int $bitsPerComponent, int $predictor | Reverse-filters row by row on the per-row tag; empty input returns an empty string | string — reconstructed payload with filter tags stripped | InvalidArgumentException | Accepts predictor 10-15 only; the TIFF predictor is out of scope |
Entry-point signatures
Section titled “Entry-point signatures”public function __construct( public int $predictor = 1, public int $columns = 1, public int $colors = 1, public int $bitsPerComponent = 8,) {}
public static function fromDictionary(string $raw): self
public function isPngPredictor(): boolpublic static function inverse( string $raw, int $columns, int $colors, int $bitsPerComponent, int $predictor,): stringBehavior contract
Section titled “Behavior contract”/DecodeParms parsing
Section titled “/DecodeParms parsing”DecodeParms::fromDictionary() matches four recognized keys as integers in raw dictionary text: /Predictor, /Columns, /Colors, and /BitsPerComponent. These are the predictor parameters ISO 32000-2:2020 §7.4.4.4 defines for the LZWDecode and FlateDecode filters. Matching is whitespace-tolerant and survives surrounding PDF tokens. Absent keys keep their defaults: predictor 1, columns 1, colors 1, bits-per-component 8. Present values are validated fail-closed at parse time, before any geometry can reach the reverse filter’s row allocation:
- A present negative value for any recognized key is rejected.
/Columnsabove 1,000,000 is rejected./Colorsabove 32 is rejected./BitsPerComponentoutside {1, 2, 4, 8, 16} is rejected.- A derived row stride above 64,000,000 bytes is rejected.
isPngPredictor() returns true when the parsed predictor is 10 through 15. Predictor 1 (no prediction) and predictor 2 (the TIFF group) return false.
Row geometry
Section titled “Row geometry”PngPredictor::inverse() consumes a FlateDecoded byte stream in which each row is preceded by a one-byte filter tag. It emits the reconstructed payload with tags stripped. Row payload width is ceil(columns * colors * bitsPerComponent / 8) bytes; the row stride adds one tag byte. The left-neighbor offset (bytes per pixel) is max(1, floor(colors * bitsPerComponent / 8)), so sub-byte packings floor to one byte. Filtering operates on whole bytes regardless of bit depth, matching PNG filter semantics.
Per-row reconstruction
Section titled “Per-row reconstruction”| Tag | Filter | Reconstruction |
|---|---|---|
| 0 | None | passthrough |
| 1 | Sub | recon[x] = filt[x] + recon[x-bpp] |
| 2 | Up | recon[x] = filt[x] + prior[x] |
| 3 | Average | recon[x] = filt[x] + floor((recon[x-bpp] + prior[x]) / 2) |
| 4 | Paeth | recon[x] = filt[x] + Paeth(left, up, up-left) |
All sums are taken modulo 256. For the first row, and for bytes left of the first pixel, the missing neighbor reads as zero, per W3C PNG §9.2. The reverse operation is driven entirely by the per-row tag. That is the conforming behavior for both fixed predictors (10-14) and Optimum (15) under ISO 32000-2:2020 §7.4.4.4, so writer tag variance is tolerated.
Validation layering
Section titled “Validation layering”Parameter validation runs in two layers by design. DecodeParms is the parse-time chokepoint and rejects hostile magnitudes first. PngPredictor::inverse() retains its own checks as a second layer: range checks on all four parameters, overflow guards that compare single factors against PHP_INT_MAX before forming the stride product, the same 64,000,000-byte per-row ceiling, and an input-proportional bound that rejects a declared stride larger than the entire input before any row buffer is allocated.
Determinism
Section titled “Determinism”Both entry points are pure static functions of their inputs. There is no I/O, no logging, and no global state. Runtime is linear in input length with a small per-byte constant. /DecodeParms parsing is a few bounded regular-expression matches. Budgets are stated in the frontmatter performance_budget.
Edge cases & failure modes
Section titled “Edge cases & failure modes”Every failure in this module raises InvalidArgumentException with the offending value named in the message.
fromDictionary()rejects a present negative value for any recognized key.fromDictionary()rejects/Columnsabove 1,000,000 and/Colorsabove 32.fromDictionary()rejects/BitsPerComponentoutside {1, 2, 4, 8, 16} and a derived row stride above 64,000,000 bytes.inverse()rejects a predictor outside 10-15. The TIFF predictor (2) is never reverse-filtered here; branch onisPngPredictor()first.inverse()rejectscolumnsorcolorsbelow 1 andbitsPerComponentoutside the legal set.inverse()rejects geometry whose stride product would overflow the platform integer, before any allocation.inverse()rejects a row stride above the 64,000,000-byte per-row ceiling, independent of actual input length.inverse()returns an empty string for empty input; that is not an error.inverse()fails a declared row stride larger than the whole input as a truncated row at offset 0.inverse()fails a trailing partial row as a truncated row, naming the offset and byte counts.inverse()fails an unknown per-row filter tag (not 0-4) with the tag value and row offset.- A mismatch between declared
/DecodeParmsgeometry and the actual stream layout surfaces as a parameter or truncation error, never as silently corrupt output. - The Average filter uses integer division, matching the PNG specification’s floor semantics.
- No cryptographic operation occurs in this module. Behavior is identical in FIPS-constrained deployments.
Conformance
Section titled “Conformance”| Claim | Standard | Clause |
|---|---|---|
The /Predictor filter parameter selects the predictor algorithm; permitted values come from the predictor-values table. | ISO 32000-2:2020 | §7.4.4.4 |
| PDF defines two predictor groups: the TIFF group is the single Predictor 2 function; the PNG group is tags 10-15. | ISO 32000-2:2020 | §7.4.4.4 |
/BitsPerComponent valid values are 1, 2, 4, 8, and 16 with default 8; /Colors is 1 or greater with default 1; /Columns defaults to 1. | ISO 32000-2:2020 | §7.4.4.4 |
| Reconstruction functions for filter types 0-4 operate byte-wise modulo 256; absent left and prior-row bytes read as zero. | W3C PNG (Third Edition) | §9.2 |
The Paeth filter type computes the PaethPredictor of the left, above, and upper-left neighbors and chooses the closest. | W3C PNG (Third Edition) | §9.4 |
All clauses are paraphrased; NextPDF does not reproduce normative text. Conformance of the reconstruction math and the parameter defaults is exercised by the unit suite. A full PDF stream-filter framework, and reversal of the TIFF predictor, are out of scope for this module.
Development notes
Section titled “Development notes”- Both classes ship since
nextpdf/pro3.0.0 and are current in 3.1.0. - The module is consumed by the Pro Diff and Classifier extractors when their inputs carry a predictor.
- Branch on
isPngPredictor()before callinginverse(); predictor 1 and the TIFF predictor need no PNG reversal. - The module bounds its own per-row allocation. Callers reversing predictors on untrusted streams should still bound the decompressed input size upstream, as the Pro extractors do.
- Fixed predictors (10-14) and Optimum (15) share one code path; the per-row tag drives reconstruction in both cases.
- Internal mechanism detail stays in the source repository’s internal documentation and is out of scope for this manual.
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.
See also
Section titled “See also”- Filter (capability) — install, quick start, and production usage samples.
- Diff — Deep Reference — a consumer of the reverse filter.
- Classifier — Deep Reference — a consumer of the reverse filter.