PDF documents have a troublesome history with accessibility. For a long time they were print snapshots, opaque to screen readers. Things are different now. With Prince, it is easy to produce accessible, tagged PDF files from semantic HTML, CSS and SVG.
In 2012, the PDF profile PDF/UA (for Universal Accessibility
) was standardised. It is the U.S. Library of Congress' preferred format for page-oriented content, and the International Standard for accessible PDF technology, ISO 14289.
This guide borrows from Bruce Lawson's introduction to tagged PDFs with Prince. To run the examples yourself, please use the latest build.
We start with a trivial HTML file:
htmlh1 { color: red } p { color: green } <title>My lovely PDF</title> <h1>Lovely heading</h1> <p>Marvellous paragraph!
From the command line, type:
prince sample-1.html
Prince has produced a PDF file in the same folder. If you inspect the tag structure in Adobe Acrobat Pro, Acrobat reports No Tags available
. On Linux, the free pdfinfo command (from poppler-utils) says the same thing:
pdfinfo sample-1.pdf
Look for the line Tagged: no. That is perfectly legitimate: documents intended only for printing do not need tags.
So let's tell Prince to make a tagged PDF:
prince sample-1.html --tagged-pdf
You can also enable tagged PDF from CSS:
html@prince-pdf { -prince-pdf-tagged: enable; } <h1>Lovely heading</h1> <p>Marvellous paragraph!
Inspecting this file in Acrobat Pro shows the tag structure. On Linux, pdfinfo now reports Tagged: yes, and you can print the structure tree with:
pdfinfo -struct sample-1.pdf
Under the <Document> tag (PDF's equivalent of a <body> element), we have an <H1> and a <P>. PDF tags often, but not always, have the same name as their HTML counterparts. As Adobe says:
PDF tags are similar to tags used in HTML to make Web pages more accessible. The World Wide Web Consortium (W3C) did pioneering work with HTML tags to incorporate the document structure that was needed for accessibility as the HTML standard evolved.
The fact that the PDF now has structural tags does not mean it is accessible.
Let's try making a PDF with the PDF/UA profile:
prince sample-1.html --pdf-profile="PDF/UA-1"
Prince aborts, giving the error:
prince: error: PDF/UA-1 requires language specification
The HTML is missing the lang attribute on the html element, which tells assistive technologies which language the text is written in. This is very important to screen reader users: the pronunciation of the word six
is very different in English and French.
This is a very common error on the web. WebAIM regularly finds that a missing language specification is among the most frequent accessibility failures.
Fix the document by amending the HTML element:
html@prince-pdf { -prince-pdf-profile: "PDF/UA-1"; } <html lang=en> <title>My lovely PDF</title> <h1>Lovely heading</h1> <p>Marvellous paragraph!
Now it princifies without errors. Inspecting it in Acrobat Pro, a new <Annot> tag has appeared. That is the small Prince logo that free licenses generate, with alternate text This document was created with Prince, a great way of getting web content onto paper
.
This generation of the <Annot> with alternate text, and checking that the document's language is specified, is why we generally advise using the --pdf-profile switch rather than --tagged-pdf.
If you cannot set the lang attribute on the markup, the -prince-lang property can specify the language for tagged PDF:
html { -prince-lang: en }
Prince cannot always map HTML directly to PDF tags. This could be because there is no direct counterpart in HTML, or because the source markup has conflicting markup and styling.
HTML has a <main> element, which does not have a one-to-one correspondence with a single PDF tag. On many sites there is one article per document, wrapped by a <main> element, or some other element serving to wrap the main content.
Take a Wikipedia article. The content is wrapped with <div id="bodyContent">. We can tell Prince to map this to the PDF <Art> tag, defined as a self-contained body of text considered to be a single narrative
:
html#bodyContent { -prince-pdf-tag-type: Art } <div id=bodyContent> <h1>Stegosaurus</h1> <p>Stegosaurus is a genus of herbivorous dinosaurs. </div>
On another site, we might want to map the <main> element to <Art>. The same method applies:
main { -prince-pdf-tag-type: Art }
Different authors' conventions over the years is one reason why Prince cannot map everything automatically. By default, HTML <article> is already mapped to <Art>.
Much of the mapping of HTML elements to PDF tags lives in the default style sheet html.css, in the style sub-folder of the Prince installation. This makes it clearer how Prince maps HTML to PDF tags, and allows you to override or customise it.
htmlarticle { -prince-pdf-tag-type: Art } h1 { -prince-pdf-tag-type: H1 } p { -prince-pdf-tag-type: P } img { -prince-pdf-tag-type: Figure; -prince-alt-text: attr(alt); } abbr { -prince-expansion-text: attr(title) }
Two properties are especially useful: -prince-alt-text and -prince-expansion-text. They can be overridden to support ARIA attributes:
img { -prince-alt-text: attr(aria-label) }
The prince-* extensions were invented before CSS vendor prefixes were standardised. They are aliased to -prince-*, which is the form used in this guide. Both work.
Adobe maintains a list of standard PDF tags, most of which Prince can map to HTML counterparts.
Taking our lead from Wikipedia again, we might want to produce a PDF table of contents from the Contents
box. That box is wrapped in a list inside a <div id="toc">. To make this into a PDF Table of Contents (<TOC>):
html#toc ul { -prince-pdf-tag-type: TOC } #toc li { -prince-pdf-tag-type: TOCI } <div id=toc> <ul> <li><a href="#diet">Diet</a> <li><a href="#habitat">Habitat</a> </ul> </div>
HTML hyperlinks are converted into PDF links by Prince automatically.
If you use HTML <nav> as the wrapper for internal navigation, use these declarations instead:
nav ul { -prince-pdf-tag-type: TOC }
nav li { -prince-pdf-tag-type: TOCI }
Only internal links are appropriate for a PDF Table of Contents, which is why Prince cannot automatically map <nav> to <TOC>. You can add the mapping in your own style sheet, or by pulling in a supplementary style sheet.
For HTML pages that do not include a hyperlinked table of contents, Prince can generate one from the heading structure. See the ToC guide.
There are a number of tricky questions when markup and style conflict. For example, consider this markup which is used to fake
a bulleted list visually:
htmldiv div { display: list-item; list-style-type: disc; list-style-position: inside; } <div> <div>One</div> <div>Two</div> <div>Three</div> </div>
Browsers render it as a bulleted list. But this merely looks like a list. Structurally it is three meaningless <div> elements. If you need this to be tagged in the output PDF as a list (so a screen reader user can jump from list to list), you can use these lines of CSS:
body > div { -prince-pdf-tag-type: L }
div div { -prince-pdf-tag-type: LI }
Prince role-maps list tags to PDF's list structure tag <L>. Prince also sets the ListNumbering attribute when it can infer it.
Better, of course, is to use <ul> and <li> in the HTML.
Often, developers supplement their HTML with ARIA roles. This can be useful when retrofitting legacy markup, especially when that markup contains few semantic elements. The usual example is adding role=button to a set of nested <div>s that are styled to look like a button.
Prince does not do anything special with ARIA roles, partly because, as WebAIM reports, they are often used to override correct HTML semantics and thus present incorrect information to screen reader users.
By supplementing Prince's mappings, an author can map elements with specific ARIA roles to PDF tags. For example, if your page has many <div role="article"> you can map these to PDF <Art> tags:
htmldiv[role="article"] { -prince-pdf-tag-type: Art } <div role="article"> <h1>Lovely heading</h1> <p>Marvellous paragraph! </div>
As with HTML, the more structured and semantic the markup is, the better the output will be. Prince cannot verify that alternate text is an accurate description of the function of an image. Claiming that a document meets the PDF/UA-1 profile actually requires some human review, so Prince has to trust that the author has done their part in making the input intelligible.
Using Prince, it is very easy to turn long documents, even whole books, into accessible and attractive PDFs.
Default mappings from HTML to PDF tags, as set in Prince's html.css:
| HTML | PDF tag | Notes |
|---|---|---|
<article> |
Art |
A self-contained narrative |
<section> |
Sect |
|
<blockquote> |
BlockQuote |
|
<h1> … <h6> |
H1 … H6 |
|
<ol>, <ul> |
L |
|
<li> |
LI |
|
<dl> |
DL |
|
dl > div |
DL-Div |
|
<dt>, <dd> |
DT, DD |
|
<figure> |
Div |
Figure grouper |
<figcaption> |
Caption |
|
<p> |
P |
|
<q> |
Quote |
|
<code> |
Code |
|
<img> |
Figure |
Alt text from alt |
<abbr>, <acronym> |
Span |
Expansion from title |
Useful command-line switches:
| Switch | Purpose |
|---|---|
--tagged-pdf |
Enable tagged PDF |
--pdf-profile="PDF/UA-1" |
Accessible PDF profile (preferred) |