Skip to content
getnextpdf.com

Pro edition

Compliance — Deep Reference

The Compliance module bundles three independent surfaces under NextPDF\Pro\Compliance:

  • Language-tag reporting — a strict PDF/UA-2 /Lang policy facade plus a structured, PSR-3-shaped compliance-event reporter.
  • E-invoice handling — Factur-X 1.08 / ZUGFeRD 2.4 validation against the EN 16931 semantic model, and hybrid PDF/A-3 emission.
  • Provenance — embed and extract caller-supplied C2PA manifest stores through an adversarial-hardened JUMBF parser; claim synthesis stays preview-gated.

The module reports what it checks. It does not perform cryptographic signing.

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 per-feature license flag exists. This is a Pro-edition capability. The experimental C2PA claim builder additionally requires an explicit environment opt-in (see Edge cases & failure modes).

Terminal window
composer require nextpdf/pro:^3
SymbolParametersDefault behaviorReturnsThrows or fails withNotes
LangComplianceReporter::warn() / ::error()string $tag, string $reason, ?string $clauseReference = nullEmits one structured JSON record per language-tag event through the PSR-3 loggervoidJsonException if the record fails JSON encodingwarn = lax-mode reject; error = strict-mode reject
LangComplianceReporter::reportException()InvalidBcp47TagException $exception, string $severity = 'error'Extracts tag and reason from the exception; delegates to warn() or error()voidAs aboveConvenience path
LangComplianceReporter::buildRecord()string $severity, string $tag, string $reason, ?string $clauseReference = nullBuilds the record array without loggingarrayDoes not throwFor custom sinks such as per-file JSON summaries
ConformancePolicy::default()?LoggerInterface $logger = nullStrict UA-2 policy: malformed or unregistered /Lang tags are rejectedselfDoes not throwThe v5.0 default is strict
ConformancePolicy::fromCore()CoreConformancePolicy $core, ?LoggerInterface $logger = nullWraps an existing Core policy as-is; no axes flippedselfDoes not throwPrefer default() for the strict posture
ConformancePolicy::withStrictUa2()bool $enabledReturns a copy with the strict axis set; disabling emits a PSR-3 noticeselfDoes not throwDeprecated opt-out; removal target 6.0.0
ConformancePolicy::isStrictUa2() / ::mode()Reads the underlying Core policybool / ConformanceModeDoes not throw
EInvoiceValidator::validate()string $pdfPathFull pipeline: PDF/A-3 wrapper check, attachment extraction, profile detection, EN 16931 rules, SchematronEInvoiceValidationResultEInvoiceException subclass on I/O failure, malformed PDF structure, or tooling crashFrozen SPI interface; a well-formed non-e-invoice PDF returns a result, never throws
EInvoiceXmlValidator::validate()string $xmlPayload, ValidatorContext $contextStructural pre-flight plus the EN 16931 deep-semantic rule corpus over a CII payloadcontract ValidationResultDoes not throw for invalid input; rejection surfaces as a failed result with findingsConcrete cross-tier validator; input gated through XmlGuard
EInvoiceValidationResult::isValid()True only when wrapper, attachment spec, profile, syntax hold and no FATAL violation existsboolDoes not throwEmpty violation list alone is not validity
EInvoiceValidationResult::notAnEInvoice()Deterministic all-null, all-false resultselfDoes not throwFactory for the “not a hybrid invoice” case
EInvoiceProfilestring-backed enumCases MINIMUM, BASIC_WL, BASIC, EN16931, EXTENDED, backed by BT-24 URNsisEn16931Conformant() is false for MINIMUM and BASIC_WL
EInvoiceSyntaxstring-backed enumCases UN_CEFACT_CII, UBL_INVOICE, UBL_CREDIT_NOTEOnly CII is isFacturXEligible(); UBL is validator-only
BusinessRuleViolationstring $ruleId, BusinessRuleSeverity $severity, string $message, ?string $xpath = null, ?string $ramPath = nullImmutable violation DTORule-id families BR-, BR-CO-, BR-CL-, BR-DEC-, BR-FXEXT-
BusinessRuleSeveritystring-backed enumFATAL invalidates the invoice; WARNING flags a quality concernMirrors the EN 16931 Schematron levels
FacturXEmbedder::embed()see signature fenceAppends embedded-file stream, filespec, and XMP to a PDF/A source; rewrites the xrefvoidEInvoiceException on malformed XML, unreadable source, missing catalog, object-stream or xref-stream source, or output write failureSource file is left intact
FacturXEmbedderOptions::default()/AFRelationship /Alternative, filename factur-x.xml, type INVOICE, version 1.0selfDoes not throwDefaults satisfy the German mandate and remain accepted in France
FacturXEmbedderOptions::withRelationship() / ::withFilename()stringReturns a copy with the override appliedselfInvalidArgumentException outside the acceptance setsRelationships: Source, Data, Alternative; filenames include zugferd-invoice.xml and xrechnung.xml
FacturXEmbedderOptions::withDocumentType()string $documentTypeReturns a copy with the XMP document-type overrideselfDoes not throwValues are not enumerated defensively
FacturXContractEmbedder::embed()string $pdfBytes, string $xmlPayload, EmbedderOptions $optionsByte-in, byte-out adapter over FacturXEmbedder via short-lived temp filesstringEInvoiceException; the XRECHNUNG profile is rejected as Enterprise-onlyCross-tier EmbedderInterface implementation
C2paManifestEmbedder::embed()string $pdfBytes, ManifestStore $storeEmbeds the store’s byte serialisation at the profile locationstringC2paException on any embed failureFrozen SPI interface; bytes-only, no I/O
C2paManifestEmbedder::extract()string $pdfBytesParses an embedded store through the hardened JUMBF parserManifestStore|nullC2paException subclass when a store is present but breaches a hardening capNull signals absence; absence never throws
ManifestStore::fromBoxes() / ::empty()list<JumbfBox> / —Builds the immutable store value objectselfDoes not throwBox order is load-bearing for round-trip equality
ManifestStore::toBytes() / ::isEmpty() / ::size()Serialises the root boxes; empty store serialises to an empty stringstring / bool / intDoes not throw
JumbfBoxParser::parse()string $bytesParses root-level JUMBF boxes under hard capslist<JumbfBox>MalformedJumbfException, JumbfBombException, JumbfCycleDetectedException, JumbfDepthExceededExceptionCaps: depth 8, 64 MiB per box, 128 MiB total, MAX_CHILDREN_PER_SUPERBOX 4096
JumbfBox::superbox() / ::leaf()string $tbox, …Builds a validated box; toBytes() round-trips through the parserselfMalformedJumbfException when the TBox is not exactly 4 bytes
C2paCapabilityStatus::current() / ::summary()Reports the C2PA capability maturity, currently preview-draftself / stringDoes not throwMachine-checkable preview marker
Feature::PREVIEW_C2PA_DRAFT->isEnabled()Reads the process environment on every call; only the literal '1' enablesboolDoes not throwEnv var NEXTPDF_FEATURE_PREVIEW_C2PA_DRAFT
ExperimentalC2paEmbedder::buildManifestStore()string $sourceBytes, string $producerBuilds a draft-pinned manifest store with one SHA-256 hash-binding claim assertionManifestStoreConstructor throws LogicException when the preview flag is offPreview; wire format pinned to a draft snapshot; no claim signature emitted

Entry-point signatures, verbatim:

public static function default(?LoggerInterface $logger = null): self
public function withStrictUa2(bool $enabled): self
public function isStrictUa2(): bool
public function validate(string $pdfPath): EInvoiceValidationResult
public function embed(
string $sourcePdfPath,
string $xml,
EInvoiceProfile $profile,
string $outputPdfPath,
?FacturXEmbedderOptions $options = null,
): void
public function embed(string $pdfBytes, ManifestStore $store): string
public function extract(string $pdfBytes): ?ManifestStore

Language-tag reporting. LangComplianceReporter emits one structured JSON record per PDF/UA-2 language-tag event. Each record carries the fixed event discriminator, a severity (warn for a lax-mode reject, error for a strict-mode reject), the offending tag verbatim, a machine-readable reason, the parsed tag components (or null when the tag fails the RFC 5646 shape grammar), an ISO 14289-2 §8.4.4 clause reference, and a UTC timestamp with microseconds. The JSON travels as the PSR-3 message body; downstream sinks parse the message field directly. ConformancePolicy is the Premium facade over the Core conformance policy. Its default applies strict UA-2 language handling and rejects a malformed or unregistered tag reaching /Lang. The opt-out helper withStrictUa2(false) reverts to the legacy lax behavior and logs a PSR-3 notice when the effective value actually changes. NextPDF marks that helper deprecated since v5.0 with removal target 6.0.0. To migrate: audit the corpus for malformed /Lang values with composer pdfua2:audit-lang-tags <pdf-or-dir>, correct them, then drop the opt-out call.

E-invoice handling. EInvoiceValidator is the frozen SPI contract for hybrid-PDF validation: PDF/A-3 wrapper check, /AF attachment extraction, profile detection from the BT-24 specification identifier, the EN 16931 business-rule engine, and a Schematron pass. A well-formed non-Factur-X PDF returns EInvoiceValidationResult::notAnEInvoice() rather than throwing; only I/O failures, malformed PDF structure, or tooling crashes raise an EInvoiceException subclass. EInvoiceXmlValidator is the concrete cross-tier XML validator: it gates input through the Core XmlGuard, runs structural pre-flight and the deep EN 16931 semantic rule corpus, and fails closed — engine errors surface as error findings, never as silent passes. FacturXEmbedder amends a PDF/A source into a hybrid PDF/A-3: it appends an embedded-file stream, a filespec with a configurable /AFRelationship, and a Factur-X XMP extension packet, then rewrites the classical cross-reference table. Both the catalog /AF array and the /Names /EmbeddedFiles name tree reference the attachment, so legacy ZUGFeRD readers resolve it.

Provenance. C2paManifestEmbedder embeds a caller-supplied C2PA manifest store into a PDF byte string, or extracts one. ManifestStore is the immutable value object that crosses the boundary. The seam is bytes-only and vendor-neutral: it does not synthesize claims, ingest URI references, or resolve hash bindings, and it performs no network or filesystem I/O. extract() returns null on a miss and is cheap on PDFs without a store. Every non-null extraction has already passed the JumbfBoxParser hardening caps.

This module reports what it checks. The e-invoice validator is not a tax-authority validator and excludes national extensions (for example Italian SDI, French Chorus Pro, German XRechnung). As EN 16931-1 states, the invoice issuer remains responsible for meeting the rules of relevant legislation.

  • A well-formed non-Factur-X PDF returns a “not an e-invoice” result; it does not throw.
  • An empty business-rule violation list does not by itself mean the document is valid; wrapper and attachment checks also apply.
  • FacturXEmbedder fails closed on sources using compressed object streams (/Type /ObjStm) or cross-reference streams (/Type /XRef, hybrid /XRefStm). Re-save such sources with a classical cross-reference table first.
  • XML payloads are gated through the Core XmlGuard: DOCTYPE or entity declarations, oversized input, and invalid UTF-8 are rejected with an EInvoiceException on the embed path, or a failed result on the validator path.
  • FacturXContractEmbedder rejects the XRECHNUNG profile loudly instead of silently downgrading it; XRechnung emission is an Enterprise capability.
  • C2paManifestEmbedder::extract() distinguishes absence (null) from malformation (C2paException subclass naming the breached invariant: malformed structure, size or count bomb, offset cycle, nesting depth).
  • ExperimentalC2paEmbedder construction throws a LogicException unless the preview environment flag equals '1'. Its wire format is pinned to a C2PA draft snapshot and can change without notice; it emits no claim signature. This capability stays preview until the C2PA PDF profile freezes.
  • The strict UA-2 lax opt-out is deprecated; migrate to the strict default (see Behavior contract).
  • This module does not perform cryptographic signing. C2PA claim signing and key custody are out of scope; see the Security module for FIPS-mode signing behavior.
BehaviorReferenceStatus
Natural-language declaration (/Lang)ISO 14289-2:2024 §8.4.4Checked / reported
Core invoice semantic modelEN 16931-1:2026Checked (issuer remains responsible)
Associated files / embedded file streamsISO 32000-2:2020 §14.13.2Emitted (/AF, /EF, /Params)
Attachment relationship and container rulesFactur-X 1.08 §3.1, §6.2Emitted / checked (default /AFRelationship /Alternative)
C2PA manifest store / JUMBFC2PA 2.1 §11.1Embed / extract supported; claim synthesis preview

This records the specifications the module is built against and what it checks or emits.

  • The reporter’s record shape is a stable contract; downstream alerting rules may pin against the fixed event discriminator.
  • Disabling strict UA-2 emits a telemetry-visible deprecation notice only when the effective value changes; re-asserting the current value is silent.
  • The Factur-X embedder preserves source bytes verbatim and appends new objects; it aims to preserve PDF/A-3 conformance but does not re-validate. Pipe the output through an external PDF/A validator for hard attestation.
  • The C2PA seam freezes five invariants: no third-party imports, bytes-only contract, no I/O, null-on-miss extraction, and no claim synthesis in the stable layer.
  • JumbfBoxParser caps are public constants; size the inputs you accept against them rather than re-deriving limits.

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.