Home  ›  Guides  ›  Accessibility

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, /ActualText and metadata that assistive technology needs — then check the result with the read-only validatePdfUA().

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:

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:

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.

{ 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.warn by default, a custom onDiagnostic sink, or a thrown error under strict: true. Base-14 dictionaries under tagged mode also gain full /ToUnicode coverage, 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:

Further reading#