The lessons on tagging, tables and annotations all deal with structure that is missing. This one deals with structure that is present but wrong — a tag tree that exists, passes a glance, and still misleads. These are the failures that survive an automated tagging pass, because auto-tagging produces something for every piece of content; it just often produces the wrong thing.
A generic H where a level was needed, a jump from H2 to H4, a list item floating outside any list, a tag invented by an exporter that no reader understands. None of these change how the page looks, and all of them change what a screen reader announces. This lesson covers the four clusters where the wrong tag does the most damage.
What you’ll learn
Why PDF/UA-2 wants numbered H1–H6 rather than the generic H, and why levels must not skip; how a list needs L ▸ LI ▸ LBody and what breaks when a level is missing; how to role-map a custom tag onto a standard one so readers know what it means; why PDF 2.0 replaced Note with FENote; and how to describe a code block so it isn’t read character by character.
Standards this lesson maps to
Standard
Criterion
Level
What it requires
PDF/UA-2
ISO 14289-2 § 8.2.5 Structure types
—
Content must use standard structure types with correct semantics; non-standard types must be role-mapped.
WCAG 2.2
1.3.1 Info and Relationships
A
Headings, lists and other structure conveyed visually must be available programmatically.
WCAG 2.2
2.4.6 Headings and Labels
AA
Headings describe topic or purpose — which requires them to be real, levelled headings.
WCAG 2.2
2.4.10 Section Headings
AAA
Section headings organise the content.
EN 301 549
Clause 10 (non-web documents)
—
Applies the WCAG criteria above to documents rather than web pages.
The four problems we’ll fix
Each card isolates one “right structure, wrong tag” defect. The Bad and Good examples show the document’s underlying tag tree — written as escaped, non-running text — and the Code panel shows how to produce the good version from the source or in Acrobat.
Generic H tags, skipped levels, and no H1
PDF/UA-2WCAG 2.2 · 1.3.1AEN 301 549Section 508
Headings are how anyone using a screen reader navigates a long document — they pull up a list of headings and jump, exactly as a sighted reader skims for bold text. That only works if the headings carry levels. PDF allows a generic H tag with no number, and auto-tagging emits it freely; the result is a flat list of headings with no hierarchy, so the reader can see there are twenty sections but not which are subsections of which. Skipped levels cause the mirror-image problem: jumping H2 → H4 implies a missing section and leaves the reader hunting for content that was never there. And a document whose first heading is an H3 gives no anchor for what the document as a whole is about.
Generic H tags, a level skipped from 2 to 4, and the document opening at H3.
headings-bad.txt
<Document>
<H3>Quarterly Report</H3> <!-- document starts at level 3 -->
<H>Overview</H> <!-- generic H: no level at all -->
<H2>Regional performance</H2>
<H4>North</H4> <!-- skips level 3 -->
<H4>South</H4>
</Document>
Heading list heard by the reader:
"Quarterly Report, heading level 3"
"Overview, heading" ← no level, no place in the outline
"Regional performance, level 2"
"North, level 4" ← where is level 3?
Good
One H1 naming the document, then levels that descend one at a time.
headings-good.txt
<Document>
<H1>Quarterly Report</H1>
<H2>Overview</H2>
<H2>Regional performance</H2>
<H3>North</H3>
<H3>South</H3>
<H4>Coastal district</H4> <!-- 4 is fine: it follows a 3 -->
</Document>
Heading list heard by the reader:
"Quarterly Report, level 1"
"Overview, level 2"
"Regional performance, level 2"
"North, level 3" "South, level 3"
"Coastal district, level 4"
Code
Headings come from paragraph styles in the source. Fixing them in the PDF is repair work; fixing the styles fixes every future export.
authoring-headings.txt
Word
Apply real styles: Heading 1, Heading 2, Heading 3 …
Never fake a heading with bold + larger font — it exports as <P>.
View ▸ Navigation Pane to see the outline you are producing.
Export ▸ ☑ "Create bookmarks using: Headings"
InDesign
Paragraph Styles ▸ style options ▸ Export Tagging
Map each style to H1, H2, H3 … (not to "Automatic")
Acrobat (repairing)
Accessibility tags panel ▸ select the tag
Right-click ▸ Properties ▸ Type ▸ choose "Heading Level 2"
For a generic <H>, change its Type to the correct H1–H6.
Work top to bottom so you can see the outline forming.
How to fix
Apply real heading styles in the source rather than manual bold and size changes — only styles export as heading tags.
Give the document exactly one H1 that names what the document is.
Walk the outline top to bottom and make each heading either the same level as the last, one deeper, or any level shallower — never more than one deeper.
Replace every generic H with a numbered H1–H6; PDF/UA-2 expects the numbered form.
Re-run the scan and check the heading list in a screen reader — the outline you hear should match the outline you see.
List items outside a list, or with no LBody
PDF/UA-2WCAG 2.2 · 1.3.1AEN 301 549
A PDF list has three nested levels: an L for the list, an LI for each item, and inside that an LBody holding the item’s text — plus an optional Lbl for the bullet or number. Readers use that nesting to announce “list of six items” and to let the user skip past it. Two things break it. An LI that sits outside any L is an item belonging to no list, so nothing is announced and the skip-list command does nothing. And an LI whose text sits directly inside it, with no LBody, loses the separation between the bullet and the content — which matters most in numbered lists, where the number and the text run together.
One item has escaped its list; the others hold their text directly, with no LBody.
lists-bad.txt
<LI>
<Lbl>•</Lbl>
North region: up 12% <!-- LI outside any L -->
</LI>
<L>
<LI>
<Lbl>•</Lbl>
South region: flat <!-- text directly in LI, no LBody -->
</LI>
<LI>
<Lbl>•</Lbl>
East region: down 3%
</LI>
</L>
Screen reader: "North region up 12%" (no list announced)
"list of 2 items, bullet South region flat…"
Good
Every item sits inside the list, and every item wraps its text in an LBody.
lists-good.txt
<L>
<LI>
<Lbl>•</Lbl>
<LBody>North region: up 12%</LBody>
</LI>
<LI>
<Lbl>•</Lbl>
<LBody>South region: flat</LBody>
</LI>
<LI>
<Lbl>•</Lbl>
<LBody>East region: down 3%</LBody>
</LI>
</L>
Screen reader: "list of 3 items,
bullet, North region: up 12%, 1 of 3 …"
Nested lists go INSIDE the parent's LBody:
<LI><Lbl>1.</Lbl><LBody>Regions
<L><LI><Lbl>a.</Lbl><LBody>North</LBody></LI></L>
</LBody></LI>
Code
Broken lists nearly always mean the source used manual bullets — a typed “•” or a dash followed by a tab — instead of a list style.
authoring-lists.txt
Word
Use the real List Bullet / List Number styles, or the
Bullets and Numbering buttons. Do NOT type "•" or "-" and Tab.
A manually bulleted paragraph exports as <P>, never as <L>.
InDesign
Paragraph Styles ▸ Bullets and Numbering ▸ List Type: Bullets
Export Tagging ▸ map the style so it exports as a list.
Acrobat (repairing)
Accessibility tags panel:
Select the stray <LI> ▸ drag it inside the <L>
Select an <LI> whose text is loose ▸ right-click ▸ New Tag ▸ LBody
and drag the text container into it
Or re-tag the whole region:
All tools ▸ Prepare for accessibility ▸ Fix reading order
marquee the list ▸ click "Text", then rebuild it as a list
in the tags panel.
How to fix
Use real list styles in the source; a typed bullet character followed by a tab produces paragraphs, not a list.
In the tag tree confirm the shape is L ▸ LI ▸ LBody, with Lbl holding the bullet or number where present.
Drag any LI that sits outside an L into its list — an item with no list is announced as loose text.
Wrap loose item text in an LBody so the label and the content stay distinct.
Nest sublists inside the parent item’s LBody, not as siblings of the LI.
Invented tags that nothing can interpret
PDF/UA-2WCAG 2.2 · 4.1.2AEN 301 549
PDF lets a producer invent structure types. Exporters use this constantly — you will find tags named Pullquote, Sidebar, TableTitle, or whatever the source template called its styles. That is legal, but only if the document also declares what each invented tag means, by role-mapping it onto a standard type in the structure tree’s /RoleMap. Without that mapping the reader meets a tag it has never heard of and has no basis for announcing it — the content is usually read as undifferentiated text, and the semantics the author intended are lost. A specific case of the same problem: PDF 2.0 deprecated the Note tag in favour of FENote, so a file still using Note is using a type that PDF/UA-2 no longer recognises.
Three invented tags with no /RoleMap entry, and a deprecated Note.
custom-tags-bad.txt
/StructTreeRoot
<< /Type /StructTreeRoot
/RoleMap << >> <!-- empty: nothing is mapped -->
>>
<Document>
<Pullquote>"Growth exceeded every forecast."</Pullquote>
<Sidebar>About our methodology…</Sidebar>
<TableTitle>Revenue by region</TableTitle>
<Note>Figures exclude intra-group sales.</Note> <!-- deprecated -->
</Document>
Reader: unknown types → announced as plain text, if at all.
Good
Every custom tag is mapped onto a standard type, and Note becomes FENote.
custom-tags-good.txt
/StructTreeRoot
<< /Type /StructTreeRoot
/RoleMap << /Pullquote /BlockQuote
/Sidebar /Aside
/TableTitle /Caption >>
>>
<Document>
<Pullquote>"Growth exceeded every forecast."</Pullquote>
→ interpreted as BlockQuote
<Sidebar>About our methodology…</Sidebar>
→ interpreted as Aside
<TableTitle>Revenue by region</TableTitle>
→ interpreted as Caption
<FENote>Figures exclude intra-group sales.</FENote>
</Document>
Simplest alternative: don't invent tags. Use BlockQuote,
Aside and Caption directly and the RoleMap stays empty.
Code
The cheapest fix is usually to stop producing custom tags at all — map the source styles onto standard types at export time.
role-mapping.txt
InDesign (prevent the problem)
Paragraph Styles ▸ style options ▸ Export Tagging ▸ PDF
Set each style's tag to a STANDARD type
(BlockQuote, Aside, Caption, P, H2 …) rather than
leaving the style name to become the tag.
Acrobat (map what already exists)
Accessibility tags panel ▸ Options ▸ Edit Role Map
(also called "Edit Tag Map" / "Role Mapping")
For each custom tag listed, choose the standard type
that best describes it:
Pullquote → BlockQuote
Sidebar → Aside
TableTitle → Caption
BodyText → P
Note → FENote
PDF 2.0 replaced Note with FENote.
Change the tag's Type in the tags panel, or role-map
Note → FENote if the tag name cannot be changed.
How to fix
Prefer standard structure types at export time — set each source style to export as P, H2, BlockQuote, Aside or Caption rather than as its own name.
For custom tags already in the file, open the Accessibility Tags panel ▸ Options ▸ Edit Role Map and map each one onto the standard type that best describes it.
Map to meaning, not to appearance — a “Sidebar” is an Aside, not a Figure, even if it is drawn in a box.
Replace Note tags with FENote, which is the PDF 2.0 type for footnotes and endnotes.
Re-run the scan; CUSTOM_TAG_MAPPING passes once every non-standard type has a role map entry.
Code samples read character by character
PDF/UA-2WCAG 2.2 · 1.1.1AEN 301 549
A Code tag marks content that is not prose — a command, a snippet, a configuration block. Screen readers treat it differently from surrounding text, often reading punctuation aloud and disabling the pronunciation rules that make ordinary sentences intelligible. That is correct behaviour for a short inline command, and punishing for a twenty-line listing, which becomes a stream of “slash, angle bracket, underscore” with no way to tell what it does. A description on the Code element gives the reader the summary first, so they can decide whether to listen to the characters at all. Separately, PDF/UA forbids reference XObjects — content imported by pointing at an external file rather than embedding it. If the referenced file is unavailable, the content simply is not there, so a conforming document cannot rely on one.
A long listing with no description, and page content pulled in from an external file.
code-and-xobjects-bad.txt
<Code>
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/site.crt;
ssl_certificate_key /etc/ssl/site.key;
location /api/ { proxy_pass http://127.0.0.1:3000; }
}
</Code>
Read aloud: "server open brace listen four four three
s s l semicolon server underscore name …"
<< /Type /XObject /Subtype /Form
/Ref << /F (shared-header.pdf) /Page 1 >> <!-- reference XObject -->
>>
If shared-header.pdf is missing, this content does not exist.
Good
The Code element carries a plain-language description; the external content is embedded rather than referenced.
code-and-xobjects-good.txt
<Code Alt="Nginx server block: listens on port 443 with TLS,
serves example.com, and proxies /api/ requests to a
local service on port 3000.">
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/site.crt;
ssl_certificate_key /etc/ssl/site.key;
location /api/ { proxy_pass http://127.0.0.1:3000; }
}
</Code>
Read aloud: "Nginx server block: listens on port 443 with TLS…"
then the listing itself, if the reader wants it.
<< /Type /XObject /Subtype /Form >> <!-- content embedded, no /Ref -->
Code
Describe what the code does, not what it says — the reader can already hear what it says.
describing-code.txt
Acrobat
Accessibility tags panel ▸ select the <Code> tag
Right-click ▸ Properties ▸ Alternate Text
What to write:
✓ "Nginx server block that terminates TLS on 443 and
proxies /api/ to localhost:3000."
✗ "Code sample." (says nothing)
✗ "server listen 443 ssl server_name" (repeats the listing)
Inline commands don't need this — a description is for
blocks long enough that hearing every character is a cost.
Reference XObjects
Re-generate the PDF with content EMBEDDED rather than
referenced. In the producing tool, avoid "link to file" /
"external page" placement; place the artwork or page so it
is written into this document.
There is no Acrobat repair for this — it must be re-exported.
How to fix
Add alt text to Code elements that are long enough to be tedious read aloud — one sentence saying what the code accomplishes.
Describe the effect, not the syntax; the characters are already available to anyone who wants them.
Leave short inline commands undescribed — a redundant description is noise.
Replace reference XObjects by re-exporting with the content embedded; there is no way to repair this in Acrobat, because the content is not in the file to begin with.
Re-run the scan; both checks clear once descriptions are present and no /Ref entry remains.
Recap
Headings — numbered H1–H6, one H1 per document, and never skip a level going down (1.3.1, 2.4.6).
Lists — L ▸ LI ▸ LBody, with sublists inside the parent item’s LBody.
Custom tags — anything non-standard needs a /RoleMap entry pointing at a standard type; better still, export standard types in the first place.
Notes — PDF 2.0 replaced Note with FENote.
Code — describe long listings so nobody has to hear every bracket; embed content rather than referencing external files.
These are the defects an automated tagging pass leaves behind, because auto-tagging always produces a tag. Reviewing the tag tree after autotagging — rather than trusting it — is what separates a document that passes a checker from one that reads correctly.