PDF/A Compliant PDF Generation API: Closing the Chrome Gap
September 24, 2026


Why 'Print to PDF' Isn't the Same as PDF/A
Every team automating document generation eventually hits the same wall: a PDF rendered from HTML looks perfect, opens fine, prints correctly — then gets bounced by a regulator, court e-filing system, or records-retention audit for not being PDF/A. Headless Chrome's Page.printToPDF (via the Chrome DevTools Protocol) produces a completely valid PDF. It just isn't an archival one.
PDF/A is defined by ISO 19005, built around one goal: guaranteeing a document renders identically decades from now, without depending on external fonts, live network resources, or software that may not exist anymore. Everything the file needs — fonts, color definitions, structure — must be self-contained inside the PDF. Chrome's PDF output was designed to reproduce a browser's rendered page faithfully, once, right now — not to archive it.
This is the core distinction developers building a PDF/A compliant PDF generation API need to internalize: rendering and archiving are different jobs. A headless browser PDF/A workflow has to treat printToPDF as step one of a pipeline, not the finish line.
PDF/A Conformance Levels, Decoded for Developers
Not all PDF/A is the same, and picking the wrong flavor either under-delivers on a compliance mandate or adds engineering effort you didn't need. Three main parts of the standard are in active use, each with conformance sub-levels:
- PDF/A-1 (based on PDF 1.4) — the oldest and strictest, no support for transparency or layers. Good baseline for simple invoicing or records that must survive on very conservative readers.
- PDF/A-2 — based on PDF 1.7, adds transparency, JPEG2000, and PDF/A file attachments. The most commonly requested level today.
- PDF/A-3 — identical to PDF/A-2 but allows embedding arbitrary non-PDF/A files (like source XML or XBRL). This matters for e-invoicing formats like ZUGFeRD/Factur-X.
Then there's the conformance letter: b (basic, visual reproducibility only), u (basic plus mandatory Unicode text mapping), and a (basic plus full tagging for accessibility, aligned with PDF/UA principles). Comparing PDF/A-1b vs PDF/A-2u: 1b is often enough for a static invoice archive, while 2u is safer when documents must be searchable and text-extractable, which most regulators now expect by default. Reserve the a levels — PDF/A-2a or PDF/A-3a — for cases with an explicit accessibility mandate, since they require proper document structure tagging, not just visual and text fidelity. Don't reach for a full PDF/A-3a pipeline for a simple invoicing use case; match the level to the actual legal or accessibility requirement.
What's Missing From Headless Browser PDF Output
A raw PDF from a headless browser and a valid PDF/A file can look byte-for-byte similar on screen and still be worlds apart structurally. The gaps that matter:
- Fonts: PDF/A requires all fonts fully embedded, not referenced. Chrome embeds subsets in most cases, but this isn't guaranteed for every font-loading path, and subsetting choices can trip conformance checks around glyph coverage.
- Color and output intent: PDF/A mandates a declared, embedded ICC output intent so color reproduces consistently on any device. Headless-rendered PDFs typically have none.
- XMP metadata: PDF/A requires XML-based XMP metadata declaring the exact conformance level (e.g.,
pdfaid:partandpdfaid:conformance) alongside standard document info. Chrome's CDP output doesn't generate this — as Browserless's own documentation confirms, the printToPDF path doesn't expose document metadata controls at all. - Document ID and structure: PDF/A wants a stable file identifier and a well-formed object structure; ad hoc browser output often lacks both.
- Disallowed features: encryption, embedded JavaScript, and certain transparency constructs are forbidden or restricted under PDF/A and need to be stripped.
- Tagging: for PDF/A-2a and PDF/A-3a, the file needs a full tag tree describing reading order and semantic structure — something rendering engines don't produce natively, and which is separate from PDF/UA certification.
None of this means printToPDF is broken. It solves a different, narrower problem than archival compliance.
Closing the Gap: A Practical Generation-to-Compliance Pipeline
The fix is a thin, deterministic post-processing layer sitting after rendering, not a replacement for it. A workable recipe to convert HTML to PDF/A programmatically:
- Render the HTML to PDF using your headless browser API, with print backgrounds, tagged output (if supported), and consistent fonts specified in CSS to avoid fallback substitution.
- Post-process with a PDF library capable of PDF/A conversion (open-source options include
pikepdf/veraPDF-adjacent tooling, or commercial SDKs) to: embed any non-embedded font subsets, inject an ICC output intent, write the XMP metadata block with the correctpdfaidconformance declaration, set a document ID, and strip JavaScript/encryption/disallowed transparency. - Tag, only if targeting an
a-level conformance, by generating or preserving a structure tree during rendering or post-processing. - Output the finished file with a PDF/A identifier that actually matches what was applied — not just asserted.
This keeps the architecture simple: one call to generate PDF/A from HTML, one deterministic transform step, both scriptable and stateless enough to run at API volume.
Verifying Compliance: Don't Trust the Metadata
A file can declare pdfaid:conformance="B" in its XMP metadata and still fail actual conformance rules — wrong color space, an untagged structure claiming level A, a stray JavaScript action. As Apryse's developer guidance points out, metadata is a claim, not a guarantee. The only way to know a file is genuinely compliant is to validate it against the ISO 19005 rules directly.
That's the role of veraPDF, the open-source validator built by the PDF Association and Open Preservation Foundation and treated as the industry reference implementation, including for the underlying Matterhorn Protocol rules that define exactly what "compliant" means machine-checkably. Unlike a heavyweight proprietary SDK, veraPDF runs as a CLI or Docker container, fitting naturally into an API pipeline: after post-processing, shell out to (or hit) veraPDF, parse the pass/fail report, and gate archiving or e-filing submission on that result. ConvertAPI's rundown of programmatic validation methods is useful if you're comparing CLI, REST, or library-based ways to wire this in — the key point is that PDF/A validation belongs in the pipeline itself, not as a manual spot-check before a deadline.
For regulated workflows — think PDF/A for regulatory e-filing automation — this validation gate is what actually prevents rejected submissions, since courts and agencies run their own veraPDF-equivalent checks on intake.
Putting It Together in a Rendering API Pipeline
The full flow: HTML in → headless rendering → PDF/A post-processing → veraPDF validation → archive or submit. Each stage is replaceable independently, which is exactly what you want in production — you can swap validators or post-processors without touching how documents get rendered.
Browsevra's rendering API is built to be the dependable first stage of that flow: consistent Chromium-based HTML rendering, control over fonts and print options, and a stable archival PDF API foundation to build the compliance layer on top of. Check the Docs for the PDF endpoint and rendering options, and see Pricing when you're ready to move a compliance pipeline into production. Start with reliable rendering at browsevra, then bolt on the PDF/A and validation steps described above.
Frequently Asked Questions
Does headless Chrome's Page.printToPDF produce a PDF/A file by default?
No. It produces a standard, valid PDF optimized for visual fidelity, but it lacks embedded ICC output intents, XMP metadata declaring a PDF/A conformance level, a guaranteed document ID, and (for accessibility levels) structure tagging. Post-processing is required to make the output archival-compliant.
What is the difference between PDF/A-1, PDF/A-2, and PDF/A-3, and which one should I use?
PDF/A-1 is the strictest, oldest baseline with no transparency support; PDF/A-2 adds transparency, JPEG2000, and PDF/A attachments and is the most commonly required today; PDF/A-3 additionally allows embedding non-PDF files like XML for e-invoicing formats. Use the b/u/a suffix to match your needs: b for basic visual archiving, u for searchable text, a when accessibility tagging is legally mandated.
What technical elements does a headless browser PDF need before it qualifies as PDF/A?
It needs fully embedded fonts, a declared ICC output intent for color, XMP metadata with the correct pdfaid conformance fields, a stable document ID, and removal of disallowed features like JavaScript and encryption. For a-level conformance, it also needs a complete accessibility tag tree.
How do you programmatically verify that a generated PDF actually meets PDF/A conformance?
Run it through veraPDF, the open-source validator built on the Matterhorn Protocol rules and treated as the industry standard for automated PDF/A checking. It runs via CLI or Docker inside a pipeline, returning a machine-readable pass/fail report rather than relying on the file's self-declared metadata.
How should this fit into an automated API pipeline that renders HTML at scale?
Structure it as four stages: render HTML to PDF via a headless browser API, post-process to add PDF/A-required elements, validate with veraPDF, then gate archiving or submission on that validation result. Keeping rendering, conversion, and validation as separate steps lets each be swapped or scaled independently.