Tables in PDF

A table is the single hardest thing to get right in a PDF, and the easiest to get wrong without noticing. On screen it is a grid of ruled lines and aligned text, and a sighted reader gets the relationships for free — they glance up a column to the heading, or left along a row. A screen reader has none of that. It can only announce “Region: North, Revenue: 1.2M” if the tag tree records which cell is a header, which direction that header governs, and which data cells belong to it.

When a table exports as a plain grid of untyped cells — or worse, as loose text that merely looks aligned — every value becomes a naked number read in isolation. This lesson works through the four failures behind almost every unreadable PDF table: no header cells at all, headers that exist but govern nothing, a malformed cell structure that breaks the grid, and complex tables with no summary to orient the reader.

What you’ll learn

How to tag header cells as TH rather than TD; how /Scope tells assistive technology whether a header governs its row or its column; how to tie awkward cells to their headers explicitly with /Headers and /ID when scope isn’t enough; why TR, TH and TD must nest in exactly that order; and when a table is complex enough to need a summary.

Standards this lesson maps to
Standard Criterion Level What it requires
PDF/UA-2 ISO 14289-2 § 8.2.5.9 Table structure elements must form a legal, regular grid, with header cells identified and associated with the data they describe.
WCAG 2.2 1.3.1 Info and Relationships A Relationships conveyed visually — a cell belonging to a column heading — must also be available programmatically.
WCAG 2.2 1.3.2 Meaningful Sequence A Cells must be read in an order that preserves meaning.
EN 301 549 Clause 10 (non-web documents) Applies the WCAG criteria above to documents rather than web pages.
Section 508 § 1194.22(g)–(h) / Revised 508 Row and column headers must be identified for data tables, and complex tables must associate data cells with headers.

The four problems we’ll fix

Each card isolates one table defect. Because a PDF isn’t HTML, the Bad and Good examples show the document’s underlying tag tree — written as escaped, non-running text so it cannot affect this page — and the Code panel shows how to produce the good version from the source or in Acrobat.

A table with no header cells

PDF/UA-2 WCAG 2.2 · 1.3.1 A EN 301 549 Section 508

The most common table failure is that every cell exported as a TD. The top row looks like a header — bold, shaded, ruled off — but that styling is visual only, so nothing in the tag tree says it is a header. A screen reader then reads the table as an undifferentiated grid: the user hears “North, 1.2M, 8%” with no idea which number is revenue and which is growth. The related failure is subtler: cells are tagged TH, but carry no /Scope, so the reader knows a cell is a header without knowing whether it governs the column beneath it or the row beside it.

Checks this covers: TABLE_HEADERS_EXIST, TABLE_HEADER_SCOPE — open any one for the full step-by-step fix.

Bad

Every cell is a TD. The first row is bold and shaded in the visual layer, which conveys nothing to assistive technology.

table-no-headers.txt
<Table>
  <TR>
    <TD>Region</TD>      <!-- looks like a header: bold + grey fill -->
    <TD>Revenue</TD>     <!-- but it is tagged TD, so it is just data -->
    <TD>Growth</TD>
  </TR>
  <TR>
    <TD>North</TD><TD>1.2M</TD><TD>8%</TD>
  </TR>
</Table>

Screen reader: "North … 1.2M … 8%"   (no header announced)

Good

The heading row is tagged TH, and each one declares /Scope /Column so it governs the cells beneath it. Row labels get /Scope /Row.

table-with-headers.txt
<Table>
  <TR>
    <TH Scope="Column">Region</TH>
    <TH Scope="Column">Revenue</TH>
    <TH Scope="Column">Growth</TH>
  </TR>
  <TR>
    <TH Scope="Row">North</TH>
    <TD>1.2M</TD>
    <TD>8%</TD>
  </TR>
</Table>

Screen reader: "North, Revenue 1.2M, Growth 8%"

Code

Author the source so the export tags headers for you. Only reach for the Acrobat table editor to correct what the export got wrong.

authoring-table-headers.txt
Word
  Select the top row → Table Layout ▸ Repeat Header Rows
  Table Properties ▸ Row ▸ ☑ "Repeat as header row at the top of each page"
  (this is what exports the row as TH, not the bold styling)

InDesign
  Select the header row → Table ▸ Convert Rows ▸ To Header

Acrobat (fixing an existing PDF)
  All tools ▸ Prepare for accessibility ▸ Fix reading order
  Select the table → "Table" button → Table Editor
  Right-click the top row → Table Cell Properties
    Type: Header Cell     Scope: Column
  Repeat for row labels with Scope: Row

How to fix

  1. Mark the heading row as a header row in the source — in Word, “Repeat as header row”; in InDesign, Table ▸ Convert Rows ▸ To Header. Bold text and shading are not headers.
  2. Re-export the tagged PDF and confirm the top row now carries TH tags.
  3. Give every TH a scope: /Column for a heading above its data, /Row for a label at the start of a row.
  4. Where a table has both, tag the corner cell carefully — it is usually a TH that labels the row headers beneath it.
  5. Re-run the scan; TABLE_HEADERS_EXIST and TABLE_HEADER_SCOPE should both pass, and a read-aloud pass should announce a header before each value.

Data cells that are not linked to their headers

PDF/UA-2 WCAG 2.2 · 1.3.1 A EN 301 549 ADA Title II

Scope works for a simple grid, where one header sits directly above or beside its data. It breaks down as soon as the table has merged cells, stacked heading rows, or a header that spans several columns — exactly the tables that carry the most information. In those cases scope is ambiguous: a header spanning three columns does not say which of the cells below it belongs to which sub-heading. PDF/UA then requires an explicit association: each header cell gets an /ID, and each data cell lists the ids of every header that governs it in a /Headers array. A cell governed by both a column and a row header lists both.

Checks this covers: TABLE_DATA_ASSOCIATION — open any one for the full step-by-step fix.

Bad

Two heading rows and a spanning header. Scope alone cannot express that “Q1” belongs under “2025” — the association is guesswork.

table-ambiguous-headers.txt
<Table>
  <TR>
    <TH Scope="Column" ColSpan="2">2025</TH>   <!-- spans two columns -->
    <TH Scope="Column" ColSpan="2">2026</TH>
  </TR>
  <TR>
    <TH Scope="Column">Q1</TH><TH Scope="Column">Q2</TH>
    <TH Scope="Column">Q1</TH><TH Scope="Column">Q2</TH>
  </TR>
  <TR>
    <TD>1.2M</TD><TD>1.4M</TD><TD>1.5M</TD><TD>1.9M</TD>
  </TR>
</Table>

Which "Q1" is 1.5M under? Nothing records it.

Good

Every header carries an /ID, and every data cell names the headers that govern it. The association is now explicit and unambiguous.

table-headers-ids.txt
<Table>
  <TR>
    <TH ID="y25" Scope="Column" ColSpan="2">2025</TH>
    <TH ID="y26" Scope="Column" ColSpan="2">2026</TH>
  </TR>
  <TR>
    <TH ID="q1a" Scope="Column">Q1</TH>
    <TH ID="q2a" Scope="Column">Q2</TH>
    <TH ID="q1b" Scope="Column">Q1</TH>
    <TH ID="q2b" Scope="Column">Q2</TH>
  </TR>
  <TR>
    <TD Headers="[y25 q1a]">1.2M</TD>
    <TD Headers="[y25 q2a]">1.4M</TD>
    <TD Headers="[y26 q1b]">1.5M</TD>
    <TD Headers="[y26 q2b]">1.9M</TD>
  </TR>
</Table>

Screen reader: "2026, Q1, 1.5M"

Code

No authoring tool emits /Headers automatically — this is remediation work in Acrobat or a dedicated tool. The cheaper fix is usually to simplify the table.

associating-headers.txt
Acrobat
  All tools ▸ Prepare for accessibility ▸ Fix reading order
  Select the table → "Table" → Table Editor
  Right-click a HEADER cell → Table Cell Properties
    Type: Header Cell    ID: y25        (give every header a unique id)
  Right-click a DATA cell → Table Cell Properties
    "Associated Header Cell IDs" → add y25 and q1a

Better, where you control the source: avoid the need entirely
  Split one two-tier table into two single-tier tables
  ("2025 results" and "2026 results"), each with one header row.
  A table simple enough for Scope is easier for everyone to read.

How to fix

  1. First ask whether the table needs to be complex at all — splitting a two-tier table into two simple ones removes the problem instead of documenting it.
  2. If it must stay complex, give every header cell a unique /ID in the Acrobat table editor.
  3. For each data cell, list every header that governs it in /Headers — typically one column header and one row header, plus any spanning header above them.
  4. Do not mix approaches within a table: if you use /Headers, use it for every data cell, or the ones you missed fall back to ambiguous scope.
  5. Verify by reading a middle cell with a screen reader — it should announce its full header path, not just the number.

Rows and cells nested in the wrong place

PDF/UA-2 WCAG 2.2 · 1.3.1 A EN 301 549

A PDF table has a strict shape: Table contains TR rows (either directly or inside a THead, TBody or TFoot row group), and each TR contains only TH or TD cells. Exports and hand edits break this constantly — a TD parented straight to the Table, a stray P between rows, a TR floating outside any table. The result is a grid the reader cannot navigate: cell-by-cell movement stops working, because the coordinates it depends on no longer exist. A related defect is irregularity — rows with different numbers of columns once spans are accounted for, which leaves holes in the grid.

Checks this covers: TABLE_LEGALITY, TABLE_ROW_STRUCTURE, TABLE_HEADER_STRUCTURE, TABLE_DATA_STRUCTURE, TABLE_REGULARITY — open any one for the full step-by-step fix.

Bad

A cell hangs directly off the table, a paragraph has drifted between rows, and the last row is one column short.

table-malformed.txt
<Table>
  <TD>Orphaned cell</TD>          <!-- TD must sit inside a TR -->
  <TR>
    <TH Scope="Column">Region</TH>
    <TH Scope="Column">Revenue</TH>
    <TH Scope="Column">Growth</TH>
  </TR>
  <P>Figures are provisional.</P>  <!-- not allowed between rows -->
  <TR>
    <TD>North</TD><TD>1.2M</TD>    <!-- 2 cells where the grid needs 3 -->
  </TR>
</Table>

Good

Rows sit in row groups, every cell sits in a row, prose moved outside the table, and each row accounts for all three columns.

table-legal.txt
<P>Figures are provisional.</P>

<Table>
  <THead>
    <TR>
      <TH Scope="Column">Region</TH>
      <TH Scope="Column">Revenue</TH>
      <TH Scope="Column">Growth</TH>
    </TR>
  </THead>
  <TBody>
    <TR>
      <TH Scope="Row">North</TH>
      <TD>1.2M</TD>
      <TD>8%</TD>
    </TR>
    <TR>
      <TH Scope="Row">South</TH>
      <TD>0.9M</TD>
      <TD ColSpan="1">—</TD>       <!-- empty, but present: grid stays regular -->
    </TR>
  </TBody>
</Table>

Code

Malformed structure almost always comes from the source — merged cells, a table split across pages, or text boxes floating over a grid. Fix it there and re-export.

repairing-table-structure.txt
In the source
  Remove merged cells that span rows AND columns at once
  Keep one table per logical grid — don't nest tables for layout
  Move captions and notes OUT of the table, above or below it
  Give every row the same number of columns; use an empty cell,
    not a missing one, where there is no value

Acrobat (repairing tags)
  View ▸ Show/Hide ▸ Side panels ▸ Accessibility tags
  Drag a stray TD into its TR; drag a stray TR into the Table
  Delete tags that don't belong (a P between rows) and re-tag
    that content outside the table
  Table Editor ▸ right-click ▸ "Table Cell Properties" to set
    Row Span / Column Span so every row totals the same width

How to fix

  1. Open the Accessibility Tags panel and expand the table — the shape should read Table ▸ TR ▸ TH|TD, optionally with THead/TBody in between, and nothing else.
  2. Move any cell that hangs directly off the Table into a TR.
  3. Move prose, captions and notes out of the table entirely — a caption belongs in a Caption tag or a paragraph beside it.
  4. Count the columns in every row, allowing for spans; add an empty cell where a value is missing rather than leaving the row short.
  5. Re-run the scan and confirm the four structure checks and TABLE_REGULARITY pass together — they usually fail and clear as a group.

A complex table with no summary

PDF/UA-2 WCAG 2.2 · 1.3.1 A EN 301 549

A sighted reader takes in a table’s shape at a glance — how many columns, how it is organised, where the totals sit. Someone listening to it gets no such overview; they enter at the top-left and move cell by cell. For a simple three-column table that is fine. For a large or multi-tier one it is disorienting, because there is no way to know what the structure is before committing to reading it. A summary gives that overview in a sentence: what the table contains and how it is arranged. It is the lowest-severity check in this lesson and the only one that is advisory rather than a hard failure — but on a genuinely complex table it is the difference between orientation and guesswork.

Checks this covers: TABLE_SUMMARY — open any one for the full step-by-step fix.

Bad

A six-column, two-tier table drops the reader straight into the data with no orientation.

table-no-summary.txt
<Table>
  <THead>
    <TR><TH ColSpan="3">2025</TH><TH ColSpan="3">2026</TH></TR>
    <TR><TH>Q1</TH><TH>Q2</TH><TH>Q3</TH>
        <TH>Q1</TH><TH>Q2</TH><TH>Q3</TH></TR>
  </THead>
  <TBody>
    … 40 rows of regional figures …
  </TBody>
</Table>

No /Summary — the listener discovers the shape by walking it.

Good

A /Summary on the table, plus a visible Caption, tell the reader what they are about to enter.

table-with-summary.txt
<Table Summary="Quarterly revenue by region. Rows are the twelve
                sales regions; columns are grouped by year, then by
                quarter within each year. The final row is the total.">
  <Caption>Quarterly revenue by region, 2025–2026</Caption>
  <THead>
    <TR><TH ColSpan="3">2025</TH><TH ColSpan="3">2026</TH></TR>
    <TR><TH>Q1</TH><TH>Q2</TH><TH>Q3</TH>
        <TH>Q1</TH><TH>Q2</TH><TH>Q3</TH></TR>
  </THead>
  <TBody>
    … 40 rows of regional figures …
  </TBody>
</Table>

Code

Write the summary as prose a person would say out loud, not as a restatement of the headings — the reader is about to hear those anyway.

writing-a-summary.txt
Acrobat
  Accessibility tags panel → select the <Table> tag
  Right-click ▸ Properties ▸ Object Properties
  Enter the text in the "Summary" field

What to write — describe the SHAPE, not the numbers:
  ✓ "Rows are sales regions; columns are grouped by year then
     quarter. The last row is the total."
  ✗ "Table showing Q1, Q2, Q3 for 2025 and 2026."   (just the headers)
  ✗ "Revenue table."                                 (says nothing)

A visible <Caption> and a /Summary do different jobs:
  Caption = the table's title, shown to everyone
  Summary = its structure, for someone who cannot see the grid

How to fix

  1. Decide whether the table is genuinely complex — more than one header tier, spanning cells, or more rows than fit on a screen. A simple grid does not need a summary and is better without one.
  2. Select the Table tag in the Accessibility Tags panel and add the text under Properties ▸ Object Properties ▸ Summary.
  3. Describe the arrangement — what the rows are, what the columns are, and where any totals sit.
  4. Add a visible Caption as well if the table has no heading above it; the two serve different readers.
  5. Re-run the scan; TABLE_SUMMARY clears once the summary is present.

Recap

  • Headers — the heading row must be tagged TH, not styled to look like one, and every TH needs a /Scope (1.3.1).
  • Association — where a header spans columns or the table has two tiers, tie each data cell to its headers with /ID and /Headers (1.3.1).
  • StructureTable ▸ TR ▸ TH|TD, nothing else in between, and every row the same width once spans are counted.
  • Summary — for genuinely complex tables, describe the arrangement so a listener knows the shape before walking the grid.
  • Simplify first — most association and regularity failures disappear when a two-tier table becomes two plain ones.

Tables are where PDF remediation is most expensive, and where authoring the source correctly saves the most work: a table built with real header rows and no merged cells exports clean and needs no repair at all.