AI · accessibility

Making 1,000-page PDFs accessible with AI: what worked and what did not

I built DokAudit, a service that makes PDFs accessible and proves it. The most useful lesson was not about which model to use. It was about where not to trust one.

The task

An accessible PDF has a structure a screen reader can follow: tagged headings, lists and tables in reading order, a document language and title, alternative text for images, fonts whose characters map to real text, form fields with labels. The standards are PDF/UA, WCAG and, for public procurement in Europe, EN 301 549. Two tools decide in practice whether a document passes: veraPDF, the open-source PDF/UA validator, and PAC, the PDF Accessibility Checker. My goal for every document was simple to state: both green.

The European Accessibility Act has applied since 28 June 2025 to products and services placed on the EU market, such as e-commerce, banking services and e-books. That can matter for companies outside the EU that sell there too. See AccessibleEU for the official overview; this article is about the engineering, not the law.

What worked: rules first

Most repairs do not need AI at all. They are deterministic and can be measured: set the title and show it instead of the file name, set the document language, couple the tab order to the structure, embed fonts and fix their ToUnicode mapping and widths, add tooltips to form fields, mark decoration as artifacts, fix links. DokAudit does these automatically and re-checks after every step. Colour contrast is an optional repair, because it changes how the document looks.

Checking works the same way. The official PDF/UA check is reproducible on the command line:

# PDF/UA-1 validation with the veraPDF command-line tool
verapdf --flavour ua1 --format text budget-2026.pdf
# Scanned pages need a text layer before anything else
ocrmypdf -l deu+eng --skip-text scanned.pdf with-text.pdf

Rules are cheap, fast and explainable. Every euro spent on AI should go into the part rules cannot do.

What worked: a local layout model for structure

The hard part is structure: which text is a heading, which lines form a list, where a table starts and which cell a number belongs to. A PDF often only knows "draw these glyphs at these coordinates".

For this I use a layout model that runs locally: Docling with the Heron layout model. It looks at the rendered page and recognises headings, paragraphs, lists, tables and figures. Measured as correct structure per character, the result rose from 63.1% without it to 95.5% with it. That one change did more for quality than anything an LLM contributed.

Running it locally also means documents do not leave the infrastructure for the bulk of the work, and the cost per page is predictable.

What did not work: trusting LLM tables

Before the layout model, I let a large language model reconstruct tables. The output looked convincing: correct headers, the right number of columns, plausible values. On closer inspection, some numbers were in the wrong cell. In a budget document, a figure that moves one row up is not a formatting issue. It is a wrong statement in an official document, and it would have passed every automated accessibility check, because the structure was valid.

The lesson: an LLM can produce structure that is formally perfect and wrong in content. Accessibility checkers test the form, not the meaning. Anything an LLM produces about a table has to be checked against what is actually on the page.

So the LLM (Claude) now has a narrow job. It only steps in on pages where the layout model is unsure, and its result is compared with the rendered page image before it is used. If they disagree, the page is flagged instead of "fixed".

What worked: a budget per page

A 1,000-page document with an LLM call per page is expensive and slow. Two measures keep it predictable:

  • A cost limit per page. The LLM is only called where the layout model is unsure, and never beyond the limit.
  • An own worker fleet. Two server containers and a PC with an RTX 3070 Ti process the layout work, at about 1.8 seconds per page. A dispatcher works on the pull principle: workers fetch jobs when they are free, nothing has to reach them from outside.

What did not work: unbounded learning runs

DokAudit learns rules from documents, for example how a particular font maps to Unicode. One day a large learning run coincided with one very large PDF that needed 8.7 GB of RAM, and the platform hung. Since then there is a RAM guard, and learning runs only start with a budget and a lock. A second rule came from the same direction: a learned rule only becomes global after it has passed a cross-check. A rule that helps one document can break another.

The result on a real document

A German city's budget plan, 1,181 pages, anonymised here. In one run, the veraPDF errors fell from 241,880 to 31. The remaining errors were font problems; after adding new font rules, the document was fully green in veraPDF and PAC.

What I would tell anyone building something similar

  • Measure first. Without a number like "correct structure per character", every model change is an opinion.
  • Use deterministic repairs for everything that can be deterministic.
  • Use a local, specialised model for perception tasks such as layout.
  • Use the LLM for the uncertain rest, with a budget, and check its output against the source.
  • Let learned rules earn their way into production.
  • Guard the platform against the one huge input that will arrive eventually.

Checklist

  • Target defined: veraPDF and PAC both green
  • Deterministic repairs first: title, language, tab order, fonts, forms, links, artifacts
  • OCR for scanned pages before structure work
  • Structure from a local layout model, with a measured quality figure
  • LLM only for uncertain pages, within a cost limit per page
  • Every LLM result cross-checked against the page image; disagreements flagged, not fixed
  • Learned rules global only after a cross-check
  • RAM guard and budgets for large documents and learning runs
  • Every document re-checked and documented in a report

Hiring for Microsoft 365, identity or email security in Singapore?

Permanent role, contract or a first conversation: write to me. I answer personally.