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, TD structure elements; TH cells should carry a Scope attribute (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.
From data view to tagged PDF Chain: semantic HTML report view, a tagging-capable renderer, a tagged PDF with structure tree, and validation with a PDF/UA checker and a screen reader. From data view to tagged PDFSemantic HTMLheadings, caption,th scopeTagging rendererChromium withtagging, WeasyPrint,PrinceTagged PDFstructure tree,title, languagePDF/UA checkPAC or veraPDF, thenNVDA or JAWSrenderoutputvalidate
Tags are only as good as the HTML they come from.

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.

Screenshot PDF versus tagged PDF Comparison: a screenshot-based PDF contains only an image; a tagged PDF contains text, headings, table structure, title and language. Screenshot PDF versus tagged PDF✗ Screenshot PDF (html2canvas + jsPDF)Image of the tableNo headings or bookmarksNot searchable, not selectableFails PDF/UA entirely✓ Tagged PDF from HTMLReal text and table tagsHeadings become bookmarksSearchable and copyablePasses PDF/UA checks when HTML is semantic

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 &amp; 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
Generators and the tags they produce Matrix of generation approaches — screenshot to PDF, Chromium print with tagging, Prince, WeasyPrint — against text, table tags, header scope and PDF/UA validity. Generators and the tags they produceReal textTable tagsTH scopePDF/UAScreenshot to PDFNoNoNoFailsChromium taggedYesYesPartialClosePrince (PDF/UA)YesYesYesPassesWeasyPrint (PDF/UA)YesYesBasicClose
Start from semantic HTML; prefer engines with a PDF/UA mode for formal reports.

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.

Permalink to "Related"

← Back to Data Export & Print