Accessibility
Tagged PDF, PDF/UA and screen-reader-friendly output. Pass
layout: { tagged: true }(or a PDF/A level) and pdfnative emits the structure tree,/ActualTextand metadata that assistive technology needs — then check the result with the read-onlyvalidatePdfUA().
pdfnative produces tagged, accessible PDFs out of the box. This guide covers PDF/UA, PDF/A, the structure tree, and best practices for screen-reader-friendly output.
Why tagged PDF matters#
A tagged PDF carries a parallel structure tree alongside the visual content. Assistive technologies (screen readers, refreshable braille displays, reflow tools) walk the structure tree to present the document in reading order — without it, they fall back to heuristic guessing of what's a heading, a paragraph, or a table cell.
pdfnative's tagged mode targets two related standards:
- PDF/UA (ISO 14289-1) — accessibility conformance, structure tree required.
- PDF/A (ISO 19005) — long-term archival, with PDF/A-2u additionally requiring Unicode mapping for every glyph.
Enabling tagged mode#
const pdf = buildDocumentPDFBytes(params, { tagged: true });
tagged: true is shorthand for tagged: 'pdfa2b' — the modern default.
| Option | PDF version | Standard | Use case |
|---|---|---|---|
tagged: false (default) |
1.4 | ISO 32000-1 | Simple PDFs, no accessibility guarantees |
tagged: true |
1.7 | PDF/A-2b | Recommended — accessible + archival |
tagged: 'pdfa1b' |
1.4 | PDF/A-1b | Legacy archives requiring PDF 1.4 |
tagged: 'pdfa2u' |
1.7 | PDF/A-2u | Archive with mandatory text extraction |
tagged: 'pdfa3b' |
1.7 | PDF/A-3b | PDF/A-2b + embedded files (e.g. ZUGFeRD invoices) |
What pdfnative emits#
When tagged is enabled, the document gets:
- Structure tree (
/StructTreeRoot) with semantic roles:/Document(root)/H1,/H2,/H3for headings/Pfor paragraphs/L→/LIfor lists/Table→/TR→/TH//TDfor tables/Figurefor images, barcodes, SVG (with/Alttext)/Linkfor hyperlinks (with/Contentsdescription)/Formfor AcroForm fields/TOC→/TOCIfor tables of contents
- Marked content (
/Span << /MCID n /ActualText <hex> >> BDC) wrapping every text run, so screen readers receive the original Unicode even when the visible glyph is a shaped Arabic ligature or Devanagari conjunct. - Per-page parent trees (
/StructParents+/ParentTree) — required by ISO 14289-1 §7.10.3. - Document metadata in XMP (PDF/A-required).
- sRGB ICC OutputIntent so colors remain stable across viewers.
Block-level accessibility hints#
Images and SVG → require alt#
{ type: 'image', data: pngBytes, width: 300, alt: 'Q1 revenue chart, $1.2M peak in March' }
{ type: 'svg', data: logoSvg, width: 200, alt: 'Company logo' } // note: property is `data`
Only image and svg blocks accept an alt property (and chart accepts altText, auto-generated when omitted); a barcode block has no alt field — its accessible description cannot currently be customised. Every /Figure in the structure tree gets an /Alt entry from the alt property. Always provide alt text for non-decorative images — empty strings are treated as decorative and may be skipped by screen readers.
Links → meaningful link text#
{ type: 'link', text: 'Read the API reference', url: 'https://github.com/Nizoka/pdfnative#api' }
Avoid text: 'click here' — screen reader users who navigate by link list lose context.
Tables → header rows are detected automatically#
When you pass a headers array, those cells are tagged /TH and the data rows are tagged /TD. For complex tables with span-cells or merged headers, pdfnative currently emits flat /Table → /TR → /TH|TD — explicit cell scopes (/Scope = Row|Column) are not yet exposed.
Forms → label every field#
{ type: 'formField', fieldType: 'text', name: 'email', label: 'Email address', width: 400 }
The label is drawn as a visible text label next to the widget. pdfnative does not emit a /TU (tooltip) entry, so the visible label is the field's only accessible name — keep it present and descriptive.
Verifying conformance#
veraPDF (PDF/A)#
# veraPDF is a Java application, not an npm package — install it from
# https://verapdf.org/software/ (installer) or run it via Docker:
docker run --rm -v "$PWD:/data" verapdf/cli --format text /data/my-document.pdf
veraPDF is the reference PDF/A validator. The pdfnative test suite generates 242 sample PDFs and the PDF/A samples are validated against veraPDF on every release.
PAC 2024 (PDF/UA)#
PAC (PDF Accessibility Checker) is a free Windows tool that walks the structure tree and reports PDF/UA issues. Run it on samples in test-output/tagged/.
Screen reader spot-check#
For a real-world test, open your PDF with NVDA (Windows, free), VoiceOver (macOS, built in), or TalkBack (Android). Listen to the reading order — it should match the visual order.
Limitations and known caveats#
| Area | Status |
|---|---|
Heading roles /H1–/H3 |
Supported |
/H4–/H6 |
Mapped to /H3 (PDF/UA permits this) |
Cell scope /Scope |
Not yet exposed — flat table tags |
| Reading order overrides | Implicit (block insertion order) — no explicit /Order array |
| Language tags per text run | Document-level only (/Lang in catalog) |
| Artifact tagging (decorative content) | Not yet exposed — header/footer/watermark are tagged as content |
Contributions to address these are welcome — see the Roadmap.
PDF/A declaration guards (v1.7.0). Tagged documents that also claim PDF/A are now checked at build time: configurations that would break the declared level (base-14 text without embedded fonts, form fields under a claim, DeviceCMYK images against the sRGB OutputIntent) surface a diagnostic —
console.warnby default, a customonDiagnosticsink, or a thrown error understrict: true. Base-14 dictionaries under tagged mode also gain full/ToUnicodecoverage, which changes output bytes versus 1.6.0. Details in the PDF/A guide.
Testing your PDFs are accessible#
A pragmatic checklist for solo developers:
-
tagged: trueis set on every user-facing PDF. - Every
image/svg/barcodeblock has a meaningfulalt. - Every
linkhas descriptive text (no "click here"). - Every
formFieldhas alabel. - Sample passes veraPDF for the chosen PDF/A variant.
- Sample reads in the correct order in NVDA/VoiceOver.
- If the document is multi-language, the dominant language is set in the document title.
Further reading#
- ISO 14289-1 (PDF/UA-1) — full PDF/UA specification.
- Matterhorn Protocol 1.1 — ~136 verifiable PDF/UA failure conditions.
- veraPDF documentation — running validation in CI.
- WebAIM PDF accessibility — accessibility primer.