Matterhorn 31-027 · PDF/UA-1 UA1:7.21.7-1
Missing ToUnicode CMap: the PDF looks fine and extracts nothing
A ToUnicode CMap is required unless the font uses MacRoman/MacExpert/WinAnsi encoding, or is a Type 1/Type 3 whose glyph names are all in the Adobe Glyph List or the Symbol set, or is a Type 0 whose descendant uses a standard Adobe character collection, or is a non-symbolic TrueType.
The condition, as the protocol states it. “A font dictionary does not contain the ToUnicode entry and none of the exemptions in 7.21.7-1 apply.”
Matterhorn Protocol 1.1, checkpoint 31, index 31-027, section UA1:7.21.7-1. Software can decide this one on its own.
What a screen reader actually does
Text renders but extracts as nothing. This is the number-one cause of "the PDF looks fine but the screen reader is silent".
Why a PDF can be text and not text
A PDF content stream does not contain characters. It contains instructions to draw glyphs:
BT /F1 11 Tf 1 0 0 1 72 700 Tm <0037 0048 0055 0050 0056> Tj ET
Those are glyph indices in a subsetted font. Whether they mean Terms or something else depends entirely on what the font dictionary says. PDF/UA-1 clause 7.21.7 requires a /ToUnicode CMap unless one of a short list of exemptions applies:
- the font uses
MacRomanEncoding,MacExpertEncodingorWinAnsiEncoding; - it is a Type 1 or Type 3 font whose referenced glyph names are all in the Adobe Glyph List or the Symbol set;
- it is a Type 0 font whose descendant CIDFont uses
Adobe-GB1,Adobe-CNS1,Adobe-Japan1orAdobe-Korea1; - it is a non-symbolic TrueType font.
If none applies and there is no /ToUnicode, the characters cannot be recovered. Condition 31-027 fires.
A ToUnicode CMap is an ordinary PostScript-style CMap stream:
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
5 beginbfchar
<0037> <0054> % glyph 0x37 is "T"
<0048> <0065> % "e"
<0055> <0072> % "r"
<0050> <006D> % "m"
<0056> <0073> % "s"
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end
How to check a file free
Copy and paste. Select a paragraph in any viewer, paste it into a plain text editor. If you get boxes, question marks or the wrong letters, the mapping is broken. This catches most of it in five seconds.
Search inside the PDF. Ctrl/Cmd + F and type a word you can see on the page. No match means no extractable text.
pdffonts (Poppler, free). pdffonts file.pdf lists every font with an uni column. no in that column is a font with no ToUnicode:
name type emb sub uni
----------------------- ------------ --- --- ---
ABCDEF+HelveticaNeue Type 1C yes yes no
veraPDF or PAC. Both report 7.21.7 violations by clause. Both are free.
How to fix it, and when you cannot
If the source document exists, re-export. This is the real fix and it is free. In Word: File → Save as Adobe PDF, or Export → Create PDF/XPS with Document structure tags for accessibility ticked. In InDesign: export as Adobe PDF (Interactive) or (Print) with Create Tagged PDF on. Both write correct ToUnicode data. In LaTeX, add \usepackage{cmap} before fontenc, or switch to LuaLaTeX or XeLaTeX with fontspec, which produce correct mappings by default.
If the font is standard, nothing is wrong. A font using WinAnsi encoding is exempt and extracts correctly without a ToUnicode CMap at all. Check the encoding before you go looking for a problem.
If it is a subsetted font with an Identity encoding and no ToUnicode, the information is not in the file. It cannot be recovered by any tool, because there is nothing to recover it from. Your options are the source document, or OCR over the rendered page — accepting that OCR introduces its own errors, and that those errors are invisible to a sighted reviewer, which is condition 08-001.
Acrobat’s Preflight (Print Production → Preflight) has fixups for some font problems, but none of them invent a Unicode mapping, and that is correct behaviour.
Where Taggart differs
Taggart evaluates the exemptions rather than just looking for the entry, so it does not report a WinAnsi font as broken. For each font it reports what it found:
ABCDEF+HelveticaNeuehas no ToUnicode CMap and none of the PDF/UA exemptions apply. Text set in this font renders but does not extract.
It also parses the CMaps that are present, and reports the two ways they fail quietly: codes mapped to U+0000 (31-028) and codes mapped to U+FEFF or U+FFFE (31-029). Both pass a presence check and both destroy extraction.
On the repair, Taggart is deliberately conservative. Where the encoding is recoverable it says so and explains that no CMap is needed. Where it is not, it refuses to write one and tells you why:
Cannot recover a Unicode mapping for: ABCDEF+HelveticaNeue. Re-export the source document with the fonts embedded rather than letting Taggart guess.
A wrong ToUnicode is worse than a missing one. A missing one is detectable; a wrong one silently corrupts every extraction, every search index and every screen reader from that point on, and looks conformant while doing it.
How Taggart handles it
Taggart detects this condition automatically. It is reported as a blocker, with the page, the structure path and the object id of every occurrence.
The fix is Repair ToUnicode CMaps. It changes what the document means, so Taggart proposes it and waits for your approval rather than applying it unattended.
Whatever Taggart changes, the page still looks identical. Fixes edit the PDF object model, not the content streams, and every remediation is re-rendered and compared against the original at structural similarity ≥ 0.999 before you get the file back. A tool that quietly reflows your document is worse than no tool.
Frequently asked questions
The text is visible. How can it not be text?
Because a PDF paints glyphs by index, not by character. The content stream says 'draw glyph 47 of this font'. Which character glyph 47 represents is only recoverable if the font declares it — through a ToUnicode CMap, a standard encoding, or glyph names in the Adobe Glyph List. Without any of those, glyph 47 is a shape with no meaning attached.
Why does Acrobat's checker sometimes pass a file that extracts as gibberish?
Acrobat checks for the presence of a mapping, not its correctness. A ToUnicode CMap that maps every code to U+0020, or to zero, satisfies a presence check and destroys the text. Those are separate conditions — 31-028 covers zero targets and 31-029 covers byte-order marks — and they are worth checking for explicitly.
Can this be fixed automatically?
Only sometimes, and a tool that claims otherwise is guessing. If the font uses WinAnsi or MacRoman encoding, or its glyph names resolve through the Adobe Glyph List, the mapping is recoverable and no ToUnicode is even required. If it is a subsetted font with meaningless glyph names and no encoding, the information is simply not in the file, and inventing it silently corrupts every extraction from that point on.
Is this an OCR problem?
No, and the distinction matters. A scanned page has no text at all and needs OCR. A page with a broken ToUnicode has real text that cannot be read. Running OCR over it would discard perfectly good glyph positions and replace them with a guess. The fix is to repair or replace the font mapping, or to re-export from the source.
Where this sits in the standards
| Standard | Reference |
|---|---|
| Matterhorn Protocol 1.1 | Checkpoint 31 (Fonts), index 31-027 |
| PDF/UA-1 (ISO 14289-1) | Clause 7.21.7-1 |
| WCAG 2.1 | 1.3.1 Info and Relationships — Level A |
| EN 301 549 / Section 508 / ADA Title II | All three point at WCAG 2.1 Level AA for non-web documents, so a Level A or AA criterion here is in scope for each of them. |
Related conditions
- 10-001 Character code cannot be mapped to Unicode A glyph is painted whose character code has no Unicode equivalent reachable through the font's ToUnicode CMap, encoding, or glyph names. The text renders correctly but cannot be extracted.
- 31-028 Unicode values in the ToUnicode CMap are zero U+0000 is not a valid mapping target; it usually means the producer could not determine the character and wrote a placeholder.
- 31-029 Unicode values in the ToUnicode CMap are U+FEFF or U+FFFE Byte-order marks are not characters and must not appear as mapping targets.
- 31-009 For a font used by text intended to be rendered the font program is not embedded PDF/UA-1 requires every font used for visible text to be embedded, so the document renders and extracts identically everywhere. A glyph counts as rendered if the text render mode is not 3.
Other conditions in checkpoint 31
Checkpoint 31 covers fonts. These are its other failure conditions; the ones without a link are in the rule set but do not have a written page yet.
- 31-001 CIDSystemInfo Registry values are not identical The CMap and the descendant CID font must agree on the character collection registry.
- 31-002 CIDSystemInfo Ordering values are not identical The CMap and the descendant CID font must agree on the character collection ordering.
- 31-003 CIDSystemInfo Supplement of the CID font is lower than that of the CMap The font's character collection supplement must be at least as new as the CMap's, or the CMap may reference CIDs the font does not define.
- 31-004 A Type 2 CID font contains neither a stream nor the name Identity as CIDToGIDMap CIDToGIDMap must be either the name /Identity or a stream mapping CIDs to glyph indices.
- 31-005 A Type 2 CID font does not contain a CIDToGIDMap entry The entry is absent entirely.
- 31-006 A CMap is neither listed in ISO 32000-1 Table 118 nor embedded A predefined CMap must be one of the standard names; anything else must be embedded in the file.
- 31-007 The WMode entry in a CMap dictionary is not identical to the WMode value in the CMap stream Writing mode (horizontal or vertical) must be declared consistently in the dictionary and the stream.
- 31-008 A CMap references another CMap which is not listed in ISO 32000-1 Table 118 A UseCMap chain must terminate at a predefined CMap.
- 31-009 For a font used by text intended to be rendered the font program is not embedded PDF/UA-1 requires every font used for visible text to be embedded, so the document renders and extracts identically everywhere. A glyph counts as rendered if the text render mode is not 3.
- 31-010 A font program is embedded that is not legally embeddable for unlimited, universal rendering The font's embedding permission bits forbid the kind of embedding used. Machines can read the flags but the flags may not reflect the actual licence.
- 31-011 The embedded font program does not contain glyphs for all referenced glyphs The subset embedded in the file is missing glyphs the content stream asks for — very common when a document is edited after subsetting.
- 31-012 A glyph present in an embedded Type 1 font program is not listed in CharSet CharSet must accurately list the glyphs in the embedded subset.
- 31-013 A glyph listed in CharSet is not present in the Type 1 font program CharSet promises glyphs the font does not contain.
- 31-014 A glyph present in an embedded CID font program is not listed in CIDSet CIDSet must accurately list the CIDs in the embedded subset.
- 31-015 A glyph listed in CIDSet is not present in the CID font program CIDSet promises CIDs the font does not contain.
- 31-016 Glyph widths in the font dictionary and the font program differ The /Widths array must agree with the embedded font program.
- 31-017 A non-symbolic TrueType font has no non-symbolic cmap A font declared non-symbolic must actually carry a (3,1) or (1,0) cmap subtable.
- 31-018 A glyph cannot be looked up by any non-symbolic cmap entry Some rendered glyph is unreachable through the declared cmap tables.
- 31-019 The font dictionary for a non-symbolic TrueType font does not contain an Encoding entry Non-symbolic TrueType fonts must declare an /Encoding.
- 31-020 The Encoding dictionary for a non-symbolic TrueType font does not contain BaseEncoding If /Encoding is a dictionary it must name a /BaseEncoding.
- 31-021 BaseEncoding is neither MacRomanEncoding nor WinAnsiEncoding Only MacRomanEncoding and WinAnsiEncoding are permitted for non-symbolic TrueType fonts.
- 31-022 Differences contains glyph names not listed in the Adobe Glyph List Glyph names in /Differences must be resolvable through the Adobe Glyph List so a processor can derive Unicode.
- 31-023 Differences present but the font has no (3,1) Microsoft Unicode cmap The Differences array is resolved through the (3,1) cmap; without it the mapping is undefined.
- 31-024 The Encoding entry is present in the font dictionary for a symbolic TrueType font Symbolic TrueType fonts must not carry an /Encoding; the font's own cmap governs.
- 31-025 The embedded font program for a symbolic TrueType font contains no cmap A symbolic TrueType font must contain at least one cmap subtable.
- 31-026 A symbolic TrueType font has multiple cmaps but none is a (3,0) Microsoft Symbol cmap With multiple cmaps present, a (3,0) symbol cmap is required to disambiguate.
- 31-028 Unicode values in the ToUnicode CMap are zero U+0000 is not a valid mapping target; it usually means the producer could not determine the character and wrote a placeholder.
- 31-029 Unicode values in the ToUnicode CMap are U+FEFF or U+FFFE Byte-order marks are not characters and must not appear as mapping targets.
- 31-030 Characters used in text showing operators reference the .notdef glyph The content stream paints glyph index 0, which is by definition "no glyph" — typically a hollow box or nothing at all.
Check your own file. Taggart’s validator is free and unlimited — every machine-checkable Matterhorn condition, no page cap, no watermark, no account needed.
Verified against a real document on . Last updated .