Conformance: ConformanceMode routing and the validation boundary
At a glance
Section titled “At a glance”NextPDF\Conformance is the single discriminator that tells the writer which
International Organization for Standardization (ISO) contract a Portable
Document Format (PDF) file targets. The library emits the structures that
contract defines. It does not, and cannot, certify the resulting file as
conformant. Only an external validator can certify conformance.
Install
Section titled “Install”composer require nextpdf/core:^3Conceptual overview
Section titled “Conceptual overview”The Conformance module exposes two public types. ConformanceMode is a backed
enum that names the target contract (Plain, PdfUa1, PdfUa2, PdfA2,
PdfA3, PdfA3b, PdfA3u, PdfA4, PdfA4e, PdfA4f). ConformancePolicy
is an immutable value object that combines a mode with independent strictness
toggles.
The mode is the single source of truth for downstream writer gates. Before
this enum existed, the engine inferred whether a document was spec-tagged from
scattered flags. ConformanceMode::isTagged(), isAccessibility(),
isArchival(), pdfaPart(), pdfaConformanceLetter(), and requiresPdf17()
each return a typed answer that the writer reads directly. The catalog,
/MarkInfo, file-header lineage, and Extensible Metadata Platform (XMP)
pdfaid markers stay aligned with the declared intent.
Read the support boundary literally. NextPDF Core emits structures these standards define. ISO 19005-4:2020 §6.7.3 specifies the identification schema that records which PDF/A variant a file claims. ISO 19005-4:2020 states that conformance is determined as specified in its Clause 5: against the normative requirements, by a checking tool, not by the producing library. ISO 14289-2:2024 §6 frames conformity as a property a file satisfies. Setting a NextPDF mode is necessary input for a conforming file. By itself, it is not a conformance result.
This follows the same Verified-versus-Claimed discipline as the Cascading Style Sheets (CSS) support matrix. A capability is Verified only when it has both a passing test or oracle run and a cited clause. Everything else is something the library emits: useful, but not a conformance guarantee. Conformance belongs to the final file and a validator, not to a library promise. Validate output with veraPDF (see “Conformance” below).
A second boundary matters for archival work. PDF/A-4 authoring, including
the OutputIntent dictionary, the embedded International Color Consortium (ICC)
profile, and the XMP extension schema, ships in the nextpdf/pro extension,
not in Core. In a Core-only installation, Document::enablePdfA() raises
InvalidConfigException because the security.pdfa capability is not
registered. Core still owns the ConformanceMode discriminator, so
introspection and the PDF/Universal Accessibility (PDF/UA-2) tagged path work,
but it does not author a PDF/A-4 file on its own.
API surface
Section titled “API surface”| Type | Kind | Key members |
|---|---|---|
NextPDF\Conformance\ConformanceMode | enum: string | Plain, PdfUa1, PdfUa2, PdfA2, PdfA3, PdfA3b, PdfA3u, PdfA4, PdfA4e, PdfA4f; isTagged(), isAccessibility(), isArchival(), requiresPdfUa2PageTabs(), pdfaPart(): ?int, pdfaConformanceLetter(): string, requiresPdf17(): bool |
NextPDF\Conformance\ConformancePolicy | final readonly class | __construct(ConformanceMode $mode = PdfUa2, bool $strictUa2 = false, bool $rejectUnvalidatedLang = false, …); lax(), strictUa2(), withUax9IsolateSupport(), withoutAstShadowMode(), withBlackPointCompensation(), withStrictOcspProducedAtTolerance(), withAllowStaleOcsp() |
ConformancePolicy enforces one invariant in its constructor: the strict
PDF/UA-2 toggles apply only when the mode is PdfUa2, and strictUa2 = true
forces rejectUnvalidatedLang = true. Incoherent combinations throw
InvalidConfigException instead of degrading silently.
Code sample — Quick start
Section titled “Code sample — Quick start”<?php
declare(strict_types=1);
use NextPDF\Conformance\ConformanceMode;
$mode = ConformanceMode::PdfA4f;
// Introspect the declared contract — these drive writer-side gates.$mode->pdfaPart(); // 4$mode->pdfaConformanceLetter(); // 'F'$mode->requiresPdf17(); // false (PDF/A-4 is PDF 2.0 lineage)$mode->isArchival(); // trueCode sample — Production
Section titled “Code sample — Production”The path that ships with Core and exercises a conformance contract end to end
is the PDF/UA-2 tagged route. This runnable example is
examples/31-pdfua2-tagged.php. It is also the oracle target for the strict
PDF/UA-2 profile.
<?php
declare(strict_types=1);
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// Set the tagged-PDF contract BEFORE writing content so the HTML pipeline// wires the TaggedContentEmitter at parser-construction time.$doc->enableTaggedPdf(lang: 'en');
$doc->setTitle('Accessible report');// … write content …
$doc->save(__DIR__ . '/out/report-ua2.pdf');
// The library has now emitted PDF/UA-2 structures. It has NOT asserted// conformance. Verify the file with the pinned veraPDF oracle://// php oracle/run.php pdfua.strict//// (Requires the veraPDF Docker image — opt-in; see "Conformance" below.)Edge cases & gotchas
Section titled “Edge cases & gotchas”- PDF/A-4 authoring is Premium. In a Core-only install,
Document::enablePdfA()throwsInvalidConfigException(security.pdfaunavailable). Core owns the discriminator, not the OutputIntent/ICC/XMP emission. See/specifications/pdfa4/. - Umbrella vs variant cases.
PdfA4is the umbrella case and reports an emptypdfaConformanceLetter(). ISO 19005-4:2020 §6.7.3 directs a file conforming to neither PDF/A-4e nor PDF/A-4f to provide nopdfa:conformance. UsePdfA4e/PdfA4fonly for the Annex B / Annex A variants. - Strict PDF/UA-2 is opt-in.
ConformancePolicy::lax()is the backward-compatible default.strictUa2()forces/MarkInfo /Marked trueand Best Current Practice (BCP) 47 rejection. Enabling it on a non-PdfUa2mode throws. isTagged()is variant-aware.Plain,PdfA2,PdfA3b, andPdfA4ereport untagged. The writer must not infer a structure tree from archival mode alone.- A passing mode is not a passing file. Setting
PdfA4does not make the output a valid PDF/A-4 file. Run a validator.
Performance
Section titled “Performance”ConformanceMode and ConformancePolicy are pure value types. Enum case
resolution and match dispatch are O(1), with no allocation beyond the
immutable policy object. They add no measurable cost to the write path. The
writer, not the discriminator, dominates the module budget (wall_ms: 1500).
The veraPDF oracle is an out-of-band continuous integration (CI) step, not
part of document generation.
Security notes
Section titled “Security notes”The strict toggles on ConformancePolicy gate fail-closed security
behaviour: securityPkiRfc5280Strict (Request for Comments (RFC) 5280 §6 path
validation), strictOcspProducedAtTolerance (RFC 6960 §4.2.2.1 skew window),
and allowStaleOcsp (default false; reject Open Certification Status
Protocol (OCSP) responses missing nextUpdate). These defaults are
conservative and flip to strict in a future major per the documented
backward-compatibility strategy. See the
security module and the project threat model
for signature-path detail.
Conformance
Section titled “Conformance”NextPDF does not certify conformance. It emits structures defined by the standards below. A separate validator decides whether a file conforms.
| Standard | Clause | What NextPDF Core does | Status |
|---|---|---|---|
| ISO 14289-2:2024 (PDF/UA-2) | §6 | Emits structure tree, /MarkInfo /Marked true (strict), /Lang, pdfuaid XMP via the Core tagged path | Verified profile: pdfua.strict validated by the veraPDF oracle (php oracle/run.php pdfua.strict) when the veraPDF binary is present (opt-in gate) |
| ISO 19005-4:2020 (PDF/A-4) | §6.7.3 | Emits the pdfaid:part / pdfa:conformance discriminator via ConformanceMode | Claimed (discriminator emission, unit-tested); PDF/A-4 file authoring is Premium-only |
| ISO 32000-2:2020 (PDF 2.0) | §7.5.2 | Base catalog/document structure | Claimed (base engine behaviour) |
Support is not conformance. NextPDF Core emits PDF/UA-2 and PDF/A-4
identification structures, with implementation evidence in
tests/Unit/Conformance/. Only a validator running the matching profile can
assert that a file conforms to PDF/A-4 or PDF/UA-2. Per ISO 19005-4:2020
Clause 5, conformance determination is the validator’s job, not the producing
library’s.
veraPDF gating is honest about its requirement. The oracle
(oracle/run.php, oracle/lib/OracleRunner.php) shells out to a pinned
veraPDF Docker image. When Docker or the image is absent, the runner exits
with code 2 (infrastructure failure) and verifies nothing. The
pdfua.strict profile is therefore an opt-in conformance gate. It proves
conformance only on machines where the veraPDF binary is present. NextPDF
ships no embedded validator and makes no conformance claim in its absence.
Citations are paraphrased from the NextPDF compliance corpus. The full
64-character reference_id digests are recorded in the page frontmatter and
in _normative-evidence-conf.md.
See also
Section titled “See also”- Compliance module — PDF/R-1 validator, Arlington grammar, and lifecycle tools
- Accessibility module — PDF/UA-2 tagged-content emitter
- PDF/A-4 specification mapping — ISO 19005-4 feature coverage and explicit non-coverage
- PDF/UA-2 specification mapping — ISO 14289-2 accessibility mapping
- Security module — signature-path strict toggles