Matterhorn 31-019 · PDF/UA-1 UA1:7.21.6-3
TrueType font with no /Encoding: every reader guesses
Non-symbolic TrueType fonts must declare an /Encoding.
The condition, as the protocol states it. “The font dictionary for a non-symbolic TrueType font does not contain an Encoding entry.”
Matterhorn Protocol 1.1, checkpoint 31, index 31-019, section UA1:7.21.6-3. Software can decide this one on its own.
What a screen reader actually does
Character codes cannot be reliably mapped to characters.
What the entry is for
A TrueType font in a PDF is a simple font: each byte in a text string is one character code, 0 to 255. Two things need a route from that code. The viewer needs a glyph to draw, and anything reading the text — a screen reader, search, copy and paste — needs a character.
For a font whose descriptor says it is non-symbolic (the Nonsymbolic flag, value 32, in /Flags), PDF/UA-1 fixes both routes by making the font name its encoding: /WinAnsiEncoding or /MacRomanEncoding, either as the value of /Encoding or as the /BaseEncoding of an encoding dictionary. Those two are separate conditions, 31-020 and 31-021. This one is the step before them: no /Encoding at all.
12 0 obj
<< /Type /Font
/Subtype /TrueType
/BaseFont /BatchJNoEncoding
/FirstChar 65 /LastChar 67
/Widths [ 600 600 600 ]
/FontDescriptor 13 0 R % /Flags 32 — non-symbolic
>> % … and no /Encoding
endobj
That is the font in the file Taggart’s test suite uses for this page. Its program is embedded and its (3,1) Unicode cmap is correct. The only thing missing is the line that says what its codes mean.
Why naming it matters
With /WinAnsiEncoding the chain is defined end to end. Code 0x92 is the glyph name quoteright, which is U+2019, which the viewer looks up in the font’s Unicode cmap and which an extractor hands to the screen reader as an apostrophe.
Without it, ISO 32000-1 (9.6.6.4) treats the font the way it treats a symbolic one: the code goes straight into the font’s (3,0) or (1,0) cmap subtable. A font built for ordinary text often has neither — it has (3,1) — and then the standard gives the viewer nothing to go on. Every viewer falls back on something of its own choosing, and so does every text extractor.
They mostly choose the same thing for ASCII, which is why this survives. Above 127 the encodings disagree:
| Code | WinAnsi | MacRoman |
|---|---|---|
0x92 | ’ | í |
0x96 | – | ñ |
0xE9 | é | È |
0xD5 | Õ | ’ |
What a screen-reader user hears
Whatever the PDF library under the screen reader guessed. On plain ASCII text that is the right thing. On a word like don’t, set by a producer that used WinAnsi codes, a reader that guesses MacRoman gets donít — and says it. An en dash becomes ñ. Nothing on the page looks wrong, so nobody checking it by eye finds out.
The guesses are visible if you know where to look. Poppler’s pdffonts lists the encoding of the test file’s font as WinAnsi. The file contains no such entry; that column is Poppler’s assumption, printed as if it were a fact. Acrobat’s Fonts tab (File → Properties) is no better a witness: Adobe describes it as listing the encoding used to display the fonts, which is the viewer’s choice rather than the file’s declaration.
Where it comes from
A producer that writes every embedded TrueType font the way a symbolic font is written — no /Encoding, codes handed straight to the font — and then sets the Nonsymbolic flag in the descriptor. Either half is legitimate on its own. Together they contradict each other, because the flag decides which rules apply: a symbolic font must not carry /Encoding (31-024), a non-symbolic one must.
The same mismatch appears when a tool rewrites descriptor flags after the fact, or when a font dictionary is assembled by hand in a script that copies the flags from one font and the dictionary layout from another.
How to check it free
veraPDF fails it under clause 7.21.6. Its own rule number is 7.21.6-2, and the message talks about the font’s encoding not defining a correct mapping to the Adobe Glyph List — not obviously the same thing, but the rule’s test fails any non-symbolic TrueType font whose /Encoding is not MacRomanEncoding or WinAnsiEncoding, and a missing one is not either of those.
Acrobat Pro can show you the font dictionary, though not change it. Following Adobe’s guide to advanced preflight inspections: select Use print production → Preflight from the All tools menu, then in the Preflight dialog box select Options → Browse Internal PDF Structure. Expand the tree down to the page’s font resources and open the TrueType font. If there is no Encoding key beside FontDescriptor, that is this condition.
Copy a word with a character above 127 and paste it into a plain text editor. A wrong character is the symptom; a right one tells you only that this reader guessed well.
How to fix it free
Re-export from the source document, and check the new file’s font dictionaries the same way. A producer that wrote this once will write it again with the same settings, so try a different export route if the first does not change.
Acrobat cannot add the entry. Adobe’s page for the structure browser says you can navigate the PDF and fonts in those windows but cannot edit them, and no Preflight fixup Adobe documents sets a font’s encoding.
A short script can, once you have decided which encoding is true. With the free Python library pikepdf:
import pikepdf
with pikepdf.open("in.pdf") as pdf:
for obj in pdf.objects:
if not isinstance(obj, pikepdf.Dictionary):
continue
fd = obj.get("/FontDescriptor")
if (obj.get("/Subtype") == "/TrueType" and "/Encoding" not in obj
and fd is not None and not int(fd.get("/Flags", 0)) & 4):
obj.Encoding = pikepdf.Name.WinAnsiEncoding
pdf.save("out.pdf")
It adds /WinAnsiEncoding to every TrueType font that is not flagged symbolic and has no encoding. Before you trust the result, copy a word containing a character above 127 from the new file, and compare the page with the old one: an encoding changes which glyph a viewer looks up as well as what the text reads as.
What Taggart does
Taggart reports every non-symbolic TrueType font that some text is set in and that has no /Encoding, with the page it first appears on and its object number. On the test file:
/BatchJNoEncoding is a non-symbolic TrueType font whose font dictionary has no /Encoding entry.
It is a blocker and there is no automatic repair. Adding the entry, as the script above does, clears it; the test suite checks that too.
Three details of how it decides:
- Non-symbolic is read from the descriptor’s Flags. A font counts as non-symbolic unless its Symbolic flag is set and its Nonsymbolic flag is not. A font with no Flags at all, or Flags of 0, is not classified, and this condition is not reported for it.
- It stands in for the rest. Once there is no encoding there is nothing to check a base encoding or a Differences array against, so 31-020 to 31-022 are skipped for that font. And because PDF/UA exempts non-symbolic TrueType fonts from needing a ToUnicode CMap, so does Taggart: the test file’s font, with no encoding and no ToUnicode, gets this finding and no other — under an exemption that only works if the encoding this condition asks for is there.
- Taggart guesses too. The text reader behind its screen-reader preview decodes a single-byte font with no encoding and no ToUnicode as Latin-1. For ASCII that is right. For
0x92it produces a control character, not an apostrophe — one more reader making a choice the file should have made for it.
How Taggart handles it
Taggart checks for this condition automatically and reports it as a blocker, with where in the file it found it.
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 copies and pastes correctly. Is this a real problem?
For plain ASCII it usually is not, yet. Codes 32 to 126 mean the same letters in WinAnsi and MacRoman, so every guess lands on the same character. The guesses part company above 127 — curly quotes, dashes, accented letters — and the file fails PDF/UA either way, because the standard asks the font to say which encoding it uses rather than leave it to the reader.
Which encoding should I add, WinAnsi or MacRoman?
The one the producer used when it chose the codes. Find a character above 127 in the document — a curly apostrophe, an en dash, an accented letter — add an encoding, and copy that word. If it pastes correctly, you picked the right one. If the document is pure ASCII, either is correct and WinAnsiEncoding is the usual choice.
Why must a symbolic TrueType font not have /Encoding when this one must?
Because the two kinds of font are looked up differently. A symbolic font's codes go straight into its own (3,0) cmap, so PDF/UA forbids an /Encoding that would contradict it (31-024). A non-symbolic font is looked up by character, and the encoding is what says which character a code is. The descriptor's Flags decide which rule applies, so a font with the wrong flag fails the other condition.
Why doesn't Taggart just add WinAnsiEncoding?
Because it is a statement about what every code in the font means, and for codes above 127 it can be wrong. A wrong encoding extracts cleanly and says the wrong thing, which is harder to notice than a missing one. Taggart reports the font and leaves the choice to someone who can check the text.
Where this sits in the standards
| Standard | Reference |
|---|---|
| Matterhorn Protocol 1.1 | Checkpoint 31 (Fonts), index 31-019 |
| PDF/UA-1 (ISO 14289-1) | Clause 7.21.6-3 |
| 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
- 31-020 The Encoding dictionary for a non-symbolic TrueType font does not contain BaseEncodingIf /Encoding is a dictionary it must name a /BaseEncoding.
- 31-021 BaseEncoding is neither MacRomanEncoding nor WinAnsiEncodingOnly MacRomanEncoding and WinAnsiEncoding are permitted for non-symbolic TrueType fonts.
- 31-024 The Encoding entry is present in the font dictionary for a symbolic TrueType fontSymbolic TrueType fonts must not carry an /Encoding; the font's own cmap governs.
- 31-017 A non-symbolic TrueType font has no non-symbolic cmapA font declared non-symbolic must actually carry a (3,1) or (1,0) cmap subtable.
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 identicalThe CMap and the descendant CID font must agree on the character collection registry.
- 31-002 CIDSystemInfo Ordering values are not identicalThe 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 CMapThe 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 CIDToGIDMapCIDToGIDMap 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 entryThe entry is absent entirely.
- 31-006 A CMap is neither listed in ISO 32000-1 Table 118 nor embeddedA 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 streamWriting 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 118A 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 embeddedPDF/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 renderingThe 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 glyphsThe 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 CharSetCharSet must accurately list the glyphs in the embedded subset.
- 31-013 A glyph listed in CharSet is not present in the Type 1 font programCharSet promises glyphs the font does not contain.
- 31-014 A glyph present in an embedded CID font program is not listed in CIDSetCIDSet must accurately list the CIDs in the embedded subset.
- 31-015 A glyph listed in CIDSet is not present in the CID font programCIDSet promises CIDs the font does not contain.
- 31-016 Glyph widths in the font dictionary and the font program differThe /Widths array must agree with the embedded font program.
- 31-017 A non-symbolic TrueType font has no non-symbolic cmapA 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 entrySome rendered glyph is unreachable through the declared cmap tables.
- 31-020 The Encoding dictionary for a non-symbolic TrueType font does not contain BaseEncodingIf /Encoding is a dictionary it must name a /BaseEncoding.
- 31-021 BaseEncoding is neither MacRomanEncoding nor WinAnsiEncodingOnly MacRomanEncoding and WinAnsiEncoding are permitted for non-symbolic TrueType fonts.
- 31-022 Differences contains glyph names not listed in the Adobe Glyph ListGlyph 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 cmapThe 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 fontSymbolic 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 cmapA 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 cmapWith multiple cmaps present, a (3,0) symbol cmap is required to disambiguate.
- 31-027 A font dictionary does not contain the ToUnicode entry and no exemption appliesA 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.
- 31-028 Unicode values in the ToUnicode CMap are zeroU+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+FFFEByte-order marks are not characters and must not appear as mapping targets.
- 31-030 Characters used in text showing operators reference the .notdef glyphThe 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 — 75 of the 87 Matterhorn conditions software can decide, with the other 12 listed for you to confirm; no watermark, no expiry, and no account needed up to 60 pages.
Checked with Taggart on a generated file that fails this condition on . The Acrobat steps follow Adobe’s documentation and were not walked through by hand. Last updated .