Invoices, statements, certificates, and reports are increasingly rendered from HTML templates. Whether the PDF that comes out is accessible depends on two things: whether the engine writes a tag structure at all, and whether the HTML gave it real semantics to write. This guide covers the engines developers actually use, with the flags that control tagging, and ends with validation, because a tagged file and a conformant file are not the same thing.
Chrome, headless Chrome, and Puppeteer
In July 2020 the Chromium team announced that starting with Chrome 85, choosing Save as PDF from the print dialog generates a tagged PDF carrying headings, lists, tables, paragraphs, and image descriptions from the page's structure. Headless generation caught up: Puppeteer's PDFOptions now include tagged, described as "Generate tagged (accessible) PDF" and defaulting to true, and outline, which generates a document outline (bookmarks) and defaults to false. Both are marked experimental in the API reference. Earlier Puppeteer versions did not carry tags across, which is why older issue threads recommend launching Chrome with an --export-tagged-pdf flag; on a current Puppeteer, check the option rather than the flag.
await page.pdf({
path: "statement.pdf",
format: "A4",
printBackground: true,
tagged: true,
outline: true,
});Two Chrome-specific cautions. The tag tree mirrors the accessibility tree of the rendered page, so anything hidden from assistive technology in HTML is hidden in the PDF, and anything semantically weak in HTML (a div styled as a heading, a table used for layout) is weak in the PDF. And the document title in the PDF comes from the page's <title>, so set it deliberately rather than leaving a template default.
Firefox
Firefox's source documentation describes tagged PDF output in its print pipeline, controlled by the preference accessibility.tagged_pdf_output.enabled. It maps the accessibility tree to PDF structure types (headings to H1 to H6, paragraphs to P, tables to Table with row and column spans and header associations), carries alt text for figures, and derives a bookmark outline from headings. Because the behaviour is preference-controlled, verify the output of the Firefox version you automate rather than assuming it.
Prince
Prince supports tagged PDF and a set of PDF profiles selected with --pdf-profile on the command line, setProfile in server wrappers, or PDF.profile in JavaScript. Its documentation lists PDF/UA-1 (ISO 14289-1, PDF 1.7) alongside PDF/A-1a to PDF/A-3b and the PDF/X family, and supports combined profiles PDF/A-1a+PDF/UA-1, PDF/A-2a+PDF/UA-1, and PDF/A-3a+PDF/UA-1. The PDF/A-"a" profiles and PDF/UA-1 automatically enable tagged output; --tagged-pdf enables it without a profile. The option --fail-pdf-profile-error aborts generation when a profile's requirements are not met, which is the right setting for a build pipeline. Prince's documentation does not list PDF/UA-2.
prince statement.html \
--pdf-profile="PDF/A-2a+PDF/UA-1" \
--fail-pdf-profile-error \
-o statement.pdfWeasyPrint
WeasyPrint's --pdf-variant option accepts pdf/ua-1 and pdf/ua-2 as well as the PDF/A and PDF/X variants, and its Python API exposes pdf_variant and a pdf_tags option for tagged output. The documentation is explicit that "the generation of PDF/A and PDF/UA documents is supported. However, the generated documents are not guaranteed to be valid, and users have the responsibility to check that they follow the rules listed by the related specifications." Treat WeasyPrint's PDF/UA output as a candidate to validate, not a conformance claim.
wkhtmltopdf and other legacy engines
wkhtmltopdf's issue tracker has open, years-old requests for accessible tagged output, and users report that it ignores alternate text and language information required for WCAG. Engines in that generation render pages without structure. If a pipeline depends on one and accessibility is now a requirement, the practical answer is to move the rendering step to Chrome, Prince, or another engine that writes tags, rather than to post-process untagged files.
Java and .NET libraries
iText's pdfHTML add-on documents HTML-to-PDF generation for documents that must conform to PDF/A and PDF/UA, which suits back-office systems already on iText. Lower-level libraries such as Apache PDFBox can write structure elements but leave the entire tag tree to your code, which is a substantial undertaking; they are better suited to fixing or inspecting files than to authoring them from scratch.
The HTML decides the tags
- Use
<h1>to<h6>in order; do not style<div>or<p>to look like headings. - Give every meaningful
<img>analtattribute, and usealt=""for decorative images so they become artifacts. - Mark table headers with
<th>andscope, add<caption>, and never use tables for layout. - Set
<html lang="...">andlangon passages in other languages; set a real<title>. - Use
<ul>,<ol>, and<li>for lists,<a href>with descriptive text for links, and landmarks such as<main>and<nav>sparingly, since the PDF has no equivalent. - Avoid conveying meaning only through CSS such as colour or generated content, which the PDF's structure will not carry.
Validate before you ship
Add a validation step to the pipeline. The open-source veraPDF validator checks PDF/A and PDF/UA syntax and publishes profiles for PDF/UA-1, PDF/UA-2, and WTPDF; it has a command-line interface suited to continuous integration. PAC (PDF Accessibility Checker) is free, checks PDF/UA and WCAG requirements, and includes a screen reader preview for manual review. For a quick look at what an engine actually wrote, the free PDF tag viewer shows the tree, and the PDF accessibility checker reports machine-detectable failures. None of these certify conformance; the human checks in the PDF accessibility checklist still apply to a representative sample of generated documents.
Frequently asked questions
Does Puppeteer generate accessible PDFs?
Current Puppeteer exposes a tagged option on page.pdf() that defaults to true and produces a tagged PDF using Chrome's tagging, which Chrome has supported since version 85. An outline option, off by default, adds bookmarks. The tags mirror the page's HTML semantics, so headings, alt attributes, table headers, and lang must be present in the HTML, and the output should still be validated.
Which HTML-to-PDF engine supports PDF/UA?
Prince documents a PDF/UA-1 profile and combined PDF/A plus PDF/UA-1 profiles. WeasyPrint accepts pdf/ua-1 and pdf/ua-2 variants but states its output is not guaranteed valid. Chrome and Puppeteer write tagged PDF without targeting a PDF/UA profile. iText's pdfHTML documents PDF/UA output for Java and .NET. Validate any of them with veraPDF or PAC.
Can I make wkhtmltopdf output accessible?
Not in practice. wkhtmltopdf has open requests for tagged output that have not been implemented, and its files carry no structure, alt text, or language. Move the rendering step to an engine that writes tags, such as Chrome, Prince, or WeasyPrint, rather than trying to add tags afterwards.
Sources
The statements above rest on the primary sources below. Where a source is a law or standard, the version and date named in the text are the ones checked on the review date. This guide is general information, not legal advice.
- Chromium Blog, July 29, 2020: Using Chrome to generate more accessible PDFs
- Puppeteer API reference: PDFOptions . The tagged (default true) and outline (default false) options.
- Puppeteer issue #7509: Export tagged PDFs for Accessibility . History of the earlier gap between Chrome's print dialog and Puppeteer output.
- Firefox Source Docs: Tagged PDF Output
- Prince documentation: PDF output and profiles . PDF/UA-1 profile, combined PDF/A plus PDF/UA-1 profiles, --tagged-pdf, and --fail-pdf-profile-error.
- WeasyPrint documentation: API reference . The pdf/ua-1 and pdf/ua-2 variants, pdf_tags, and the statement that output is not guaranteed valid.
- wkhtmltopdf issue #1616: not generating accessible PDFs
- iText: Making PDF/A document creation easier with iText and pdfHTML
- veraPDF documentation: CLI validation
- PAC: PDF Accessibility Checker
Keep reading
Accessible statements and notices at scale
How to make high-volume transactional documents (statements, bills, explanations of benefits, notices) accessible by fixing the template and composition pipeline, what the law expects, and how to validate by sample.
Read the guide →Acrobat accessibility checker vs PAC, explained
Why a PDF can pass Adobe Acrobat's accessibility checker and fail PAC, what each tool actually tests, how to read their reports, and what neither can decide.
Read the guide →PDF/UA-2 explained: what ISO 14289-2 changes
PDF/UA-2 (ISO 14289-2:2024) is the accessibility standard for PDF 2.0 files. What it adds over PDF/UA-1, its relationship to WTPDF, and which tools produce or validate it today.
Read the guide →