Generating Accessible PDF Reports from Data Views
Permalink to "Generating Accessible PDF Reports from Data Views"Many organisations need PDFs: monthly statements, audit reports, regulatory filings, “download as PDF” buttons on dashboards. A PDF generated naively — a screenshot of the page, or an untagged print-to-PDF — is a picture of text to a screen reader: no headings, no table structure, sometimes no text at all. An accessible PDF is tagged: it carries a structure tree, similar to HTML’s, with headings, tables, header cells and reading order. Producing one from a data view is very achievable if you start from good HTML and choose the right tool.
This guide belongs to data export & print.
Spec reference
Permalink to "Spec reference"- PDF/UA (ISO 14289) — the standard for universally accessible PDF: everything tagged, logical reading order, tables with header cells, document title displayed, language set.
- WCAG 2.2 applied to documents — via techniques PDF6 (table elements), PDF9 (headings), PDF16 (language), PDF18 (title), and EN 301 549 clause 10 for non-web documents.
- Tables in PDF —
Table,TR,TH,TDstructure elements; TH cells should carry aScopeattribute (Column, Row) or IDs and Headers for complex tables. - SC 1.3.2 Meaningful Sequence (A) — the tag order must match the reading order, which matters for multi-column report layouts.
When to use — and when not to
Permalink to "When to use — and when not to"Use PDF when the format is mandated, when the document must be fixed (a signed statement), or when it will be archived. Keep reports short and structured: a summary, a few tables, charts with text alternatives.
Do not use PDF for large datasets. A 4,000-row table in a PDF is hard to navigate for everyone, and slow in screen readers; offer CSV and Excel exports alongside, and keep the PDF to the summary. Do not use PDF as the only format for interactive dashboards.
The misapplication: generating the PDF from a canvas or screenshot (html2canvas + jsPDF). The result contains an image of the table and no text — inaccessible and unsearchable.
Annotated code example
Permalink to "Annotated code example"<!-- Report view: semantic HTML is the source of the tags -->
<html lang="en"> <!-- PDF language (PDF16) -->
<head><title>Overdue invoices — October 2026</title></head> <!-- PDF title (PDF18) -->
<body>
<h1>Overdue invoices — October 2026</h1>
<p>240 invoices, total 182,400.00 EUR. Filters: Status is Overdue.</p>
<h2>By customer</h2>
<table>
<caption>Overdue invoices by customer</caption>
<thead><tr><th scope="col">Customer</th><th scope="col">Invoices</th><th scope="col">Amount (EUR)</th></tr></thead>
<tbody><tr><th scope="row">Lund & Co</th><td>12</td><td>14,880.00</td></tr></tbody>
</table>
<figure>
<img src="aging.svg" alt="Bar chart: most overdue amounts are 30 to 60 days late; 12 percent are over 90 days.">
<figcaption>Ageing of overdue amounts</figcaption>
</figure>
</body></html>
// Puppeteer / Chromium: request a tagged PDF with document outline
const pdf = await page.pdf({
format: 'A4', printBackground: true,
tagged: true, // generate structure tags from the DOM (PDF/UA groundwork)
outline: true, // bookmarks from headings
displayHeaderFooter: false,
});
Chromium’s tagged output has improved substantially but is not fully PDF/UA-conformant on its own; dedicated engines such as Prince or WeasyPrint (with PDF/UA mode) produce stricter results. Whatever you use, validate.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Reader + viewer | Table navigation | Notes |
|---|---|---|
| NVDA + Adobe Acrobat Reader | Ctrl+Alt+arrows with headers announced | Requires TH with scope |
| JAWS + Acrobat Reader | Table commands, headers announced | Best overall PDF support |
| VoiceOver + Preview (macOS) | Limited; reads text in order | Preview ignores much tagging |
| NVDA + browser PDF viewer (Chrome, Edge) | Basic structure | Edge’s viewer reads tags reasonably |
Integration context
Permalink to "Integration context"The HTML report view should follow semantic HTML table construction, including writing table captions and summaries and correct usage of scope and headers in complex tables. Charts in the report need text alternatives, as in generating accessible text alternatives for D3 charts.
Gotchas
Permalink to "Gotchas"Page headers and footers. Repeated page furniture (logos, page numbers) should be tagged as artifacts so readers skip them. Chromium’s displayHeaderFooter content is not tagged as content — check how your engine handles it.
Tables split across pages. Tags keep the table as one structure even if it spans pages; that is correct. Repeat the visual header with thead { display: table-header-group }.
Charts as images. SVG charts become images or vector paths; give them alt text in the HTML so the tag carries it.
Framework notes
Permalink to "Framework notes"Server-side rendering frameworks make report views easy to build as routes: a Next.js or Nuxt page at /reports/overdue?print=1 renders the semantic report, and a worker renders it to PDF with Puppeteer. In Angular, use a dedicated report component with print-specific styles rather than printing the live dashboard. Avoid client-side PDF libraries that draw text manually (pdfmake, jsPDF) for long reports — their tagging support is limited or manual.
// Worker: render the semantic report route to a tagged PDF
await page.goto(`${origin}/reports/overdue?print=1`, { waitUntil: 'networkidle0' });
const buf = await page.pdf({ format: 'A4', tagged: true, outline: true });
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Create a report layout in the design system — print-safe typography, a header block with title, scope and date, table and figure components with captions — and render PDFs only from that layout. Teams then do not reinvent report markup per feature, and every report inherits tagging-friendly structure.
FAQ
Permalink to "FAQ"Is a PDF generated with "Save as PDF" in Chrome accessible?
Recent Chrome versions tag PDFs generated from print, which is a big improvement, but results depend on the page’s HTML and are not guaranteed PDF/UA. Validate before relying on it.
Can I fix an untagged PDF afterwards?
Acrobat Pro can add tags, but manual remediation is slow and error-prone, especially for tables. Fix the source HTML and regenerate instead.
Should every dashboard have a PDF export?
Only if users need it. Many users are better served by an accessible HTML page and an Excel export.
How long can an accessible PDF table be?
There is no limit, but beyond a few pages navigation gets tedious. Summarise in the PDF and attach the full data as a spreadsheet.
Which validator should I use?
PAC (PDF Accessibility Checker) is free and widely used for PDF/UA checks on Windows; veraPDF is open source, scriptable and suits CI. Use one automatically and a screen reader manually — validators confirm structure, not whether the reading experience makes sense.
Related
Permalink to "Related"- CSV and Excel exports — for large data
- Print stylesheets — the same layout for paper
- Writing table captions and summaries — captions that carry into the PDF