What makes a PDF accessible

An accessible PDF lets a screen reader deliver the same content, in the same order, with the same meaning as the page a sighted reader sees. Selectable text is not enough: the file also has to carry the structure behind the page. This guide covers what that structure is, the standards that define it, and how to check a PDF you already have, then fix it, or produce an accessible one from the start.

Structure: tags, the structure tree and reading order

A PDF page is a canvas. Text is painted at coordinates, in the order the producer emitted it, and nothing on the page says which words are a heading, which block is a table, or where one paragraph ends. An accessible PDF adds a second layer beside the page: the structure tree, a nested list of structural elements - Document, H1, P, Table, TR, TH, TD, Figure, Link, L - that says what each piece of content is and the order a reader should meet it in. Assistive technology navigates that tree, not the coordinates.

Structure tags
Every run of text, every table cell and every image is tagged with its role, so the document has headings, paragraphs, lists and tables instead of one undifferentiated stream of words.
Logical reading order
The tree's order is the reading order. A two-column layout assembled from independently positioned text boxes reads across the columns, in whatever order the producer happened to paint them, unless the tree says otherwise.
Alt text on figures
Images, charts and diagrams become a Figure element carrying an /Alt entry: the sentence a screen reader announces in place of the picture. A figure with no alt text is an accessibility failure, not a courtesy.
Title and language
The document has a title (/Title) announced when a reader opens it, and a language (/Lang) that selects pronunciation rules. Both are required by the accessibility standard, and both are metadata a producer can forget to write.
Real text, with Unicode mapping
Characters are drawn from a font and mapped back to Unicode through a /ToUnicode table. A scanned page, or a page exported as a picture of text, has no characters to read and no structure to tag, so it needs OCR first.
Table header cells
Header cells are tagged TH with a column scope attribute, and body cells TD, so a screen reader can say “Revenue, second data column” instead of reading cell values with no context.
Tagged links
A link is a Link structure element with its annotation attached, so a reader can list the document's links and activate them. A coloured underline on the page is not the same thing.
Artifacts
Decoration, such as rules, watermarks, page numbers and background graphics, is marked as an artifact, which tells the reader to skip it. Untagged decoration gets announced as content and interrupts the document.

This is the tree the validator reads, and the one /tags displays. Here is a real one, from assets/samples/markdown/graph-diagrams.md rendered by the markdown pipeline:

tag tree
Document
  H1
  P
  H2
  Figure  /Alt (CI pipeline from ingest through validation to publication)
  H2
  Figure  /Alt (Expense approval flow from submission through finance approval to payment)
  P

Two headings, two body paragraphs, two figures, each figure carrying the description its source supplied. A longer document has the same shape with more nodes: the invoice sample in the same directory renders to 99 elements, most of them table rows and cells.

The standards: PDF/UA-1 and PDF/A-2A

PDF/UA-1 (ISO 14289-1) is the accessibility standard for PDF. It sets out the requirements in the list above - tagged content in logical order, alt text on figures, a document title and language, machine-readable metadata - and it is what veraPDF tests when you ask for PDF/UA-1. A conforming file gives a screen reader what it needs to announce and navigate it; one that does not can look perfect and still be unusable.

PDF/A-2A (ISO 19005-2, conformance level A) is the archival profile. PDF/A asks that a file render identically years from now: fonts embedded, colour defined by an output intent, nothing fetched from outside. Level B guarantees only that visual fidelity; level A adds the tagged structure, so A is the level to ask for when accessibility matters. The two standards overlap but neither implies the other: a PDF/A-2A file can still have a wrong reading order or missing alt text, and a PDF/UA-1 file can still fail the archival checks. makesPDF writes both in the same pass, and /validate reports each independently.

WCAG is the W3C's Web Content Accessibility Guidelines, written for web pages. The two overlap for PDF content: text alternatives, structure, meaningful sequence, document language and title. They are still separate standards, so PDF/UA-1 conformance is not by itself a WCAG conformance claim. The page around the PDF, and how it is delivered, are judged separately.

Laws that reference these standards

Two US requirements are cited most often. This is a summary for orientation, not legal advice. Each paragraph links the official source, which is what to check before relying on it.

Section 508

Section 508 of the Rehabilitation Act requires US federal agencies to make their electronic and information technology accessible to people with disabilities, which covers the documents they publish. The Section 508 programme and the U.S. Access Board's ICT standards are the primary sources; the Revised 508 Standards incorporate WCAG 2.0 Level AA.

ADA Title II

Title II of the Americans with Disabilities Act covers state and local governments. The Department of Justice's web and mobile app accessibility rule adopts WCAG 2.1 Level AA as the technical standard for their web content and mobile apps. Its compliance dates were extended by interim final rule in April 2026: 26 April 2027 for public entities with a total population of 50,000 or more, and 26 April 2028 for smaller entities and special district governments. The /civic page covers what that means for council agendas, minutes and budget documents, with templates derived from real published PDFs.

How to check a PDF

/validate runs veraPDF, the ISO reference validator, over any PDF you drop in. It reports PDF/UA-1 and PDF/A-2A separately and lists failed rules by clause and test number, which is what tells you what to fix. It is free and needs no sign-in: the upload is held in memory for one request and discarded when the report renders.

Two things an automated check cannot judge. It cannot tell whether the alt text is any good, since “image”, “chart” and “logo” all pass and none of them help. And it cannot tell whether the reading order makes sense: elements can be tagged and still be in the wrong sequence, or split a sentence across a page break in a way a person has to hear to notice. Run the document through a screen reader, such as NVDA or JAWS on Windows or VoiceOver on macOS, and listen to it before you sign off. The validator catches missing alt text; only a listener catches bad alt text.

How to see a PDF's tags

/tags opens the structure tree itself: every element, its role, the alt text or actual text it carries, and the marked-content IDs that tie it back to the page. Where the validator answers “is this compliant?”, this answers “what is the structure?”, which is useful when a failure names a clause and a page but you cannot find the node it means, or when you want to confirm a remediated file kept its headings. The report comes back as JSON or as an indented outline PDF, and the check is free for signed-in callers.

Enhancing a PDF in place also writes a tag report as an artifact, so the structure record of a remediated document is downloadable without uploading it a second time.

How to fix an existing PDF

/enhance takes a PDF you already publish and produces accessible output in two modes. In-place keeps the original page (same pixels, same fonts) and writes the structure tree, alt text and metadata on top, which is the right choice for a signed letter or a document whose appearance carries meaning. Regenerate rebuilds the document from a clean source it hands back as DSL (and markdown where the layout is expressible), so the next version is an edit rather than another remediation job. The default does both in one job, billed once.

Vision supplies alt text on in-place runs, and the job reports every figure it asked about and what it applied. The first page of any upload is free. It works on born-digital PDFs with extractable text and single-column layouts, up to roughly 50 pages; scanned or image-only PDFs need OCR and are not supported yet, and tagging a digitally signed PDF invalidates the signature, so use regenerate for those.

How to create an accessible PDF from the start

The cheaper direction is to never produce an untagged file. /markdown converts Markdown to a PDF that is tagged, PDF/UA-1 and PDF/A-2A, in the same pipeline that lays out the page rather than as a repair pass after it.

The structure comes from the source. Headings become H1 to H6, lists become L and LI, tables become Table, TR, TH and TD, links become Link elements with their annotations attached, and an image keeps its alt text as /Alt on a Figure. Set the document title and language in frontmatter, since both are required for conformance, and a diagram can carry its own description:

markdown
---
title: Publication accessibility statement
lang: en
---

# What we changed

![Council chamber, empty before a hearing](chamber.jpg)

```mermaid
accTitle: Publication workflow
accDescr: Drafts are tagged, validated, then published.
flowchart LR
  draft --> tag --> validate --> publish
```

A Mermaid block's accTitle and accDescr become the figure's alt text, so a diagram is never announced as an unlabelled figure. The same writer backs the template DSL, for documents that need real layout control: the DSL's blocks carry the same roles and metadata, and /docs/dsl documents the DSL itself.

Before rendering, the source can be checked without producing a PDF: POST /api/v1/md/validate flags heading level skips, images with no alt text and diagrams with no description, and POST /api/v1/preview/validate does the same for a template.

Have a PDF in hand? Check it with /validate, then read its tree on /tags. Starting from scratch? Render a tagged PDF from Markdown.