Matterhorn 31-014 · PDF/UA-1 UA1:7.21.4.2-3
CIDSet that leaves out glyphs the CID font contains
CIDSet must accurately list the CIDs in the embedded subset.
The condition, as the protocol states it. “The FontDescriptor dictionary of an embedded CID font contains a CIDSet string, but at least one of the glyphs present in the font program is not listed in the CIDSet string.”
Matterhorn Protocol 1.1, checkpoint 31, index 31-014, section UA1:7.21.4.2-3. Software can decide this one on its own.
What a screen reader actually does
Processors may mis-handle the subset.
What CIDSet is
A CID font — the kind most producers use for Unicode text, including Hebrew, Arabic and CJK — refers to its glyphs by number, the CID. Its font descriptor may carry a /CIDSet stream saying which CIDs the embedded subset contains. It is a bitmap: one bit per CID, high-order bit first, so the first byte covers CIDs 0 to 7, the second 8 to 15, and so on.
PDF/UA-1 does not require it. If it is present, it must identify every CID present in the font program, used or not. This condition is a bit left clear for a CID that is there; a bit set for a CID that is not is 31-015.
In the file Taggart’s test suite uses for this page, the embedded TrueType program has four glyphs and an /Identity CIDToGIDMap, so CIDs 0 to 3 are present. The CIDSet is one byte:
/CIDSet <E0> % 1110 0000 → CIDs 0, 1, 2
CID 0 1 2 3 4 5 6 7
bit 1 1 1 0 0 0 0 0
program ✓ ✓ ✓ ✓
CID 3 is in the program and on the page, and its bit is clear.
What a screen-reader user hears
Nothing different. The page draws its three glyphs and pdftotext reads them; CIDSet plays no part in either. That needs saying, because the condition is reported as a blocker and a reader is not losing anything. The file is non-conformant because it describes its own subset wrongly, and the cost falls on software that believes the description — a processor deciding from CIDSet which glyphs it can reuse will think CID 3 is not available.
Where it comes from
A bitmap built from the text instead of the program. A producer that sets a bit for every CID its text uses has written the set of used CIDs, which is not what the field means. Anything the subset holds that the text does not use goes missing from it. The most predictable casualty is CID 0: it is .notdef, every TrueType program has it, and no text sets it on purpose. A CIDSet whose first bit is clear fails on that alone.
Glyphs that travel with other glyphs. A TrueType accented letter can be a composite that draws its base letter and its accent from other glyphs, so a subsetter keeps those too. They are in the program, and a bitmap built from the text leaves them out.
A program replaced without its descriptor. Re-subsetting or swapping the embedded program and copying the old CIDSet across leaves a bitmap for a different subset.
How to check it free
veraPDF tests it under clause 7.21.4.2, for fonts whose name carries a subset tag (ABCDEF+…). A full font with an inaccurate CIDSet is not checked by that rule.
Read the bits yourself. The bitmap is a few bytes and a short script lays it beside the program. This uses the free Python libraries pikepdf and fontTools, for TrueType-based CID fonts:
import io, sys
import pikepdf
from fontTools.ttLib import TTFont
with pikepdf.open(sys.argv[1]) as pdf:
for fd in pdf.objects:
if not (isinstance(fd, pikepdf.Dictionary) and "/CIDSet" in fd and "/FontFile2" in fd):
continue
bits = bytes(fd.CIDSet.read_bytes())
listed = [cid for cid in range(len(bits) * 8) if bits[cid // 8] & (0x80 >> cid % 8)]
glyphs = TTFont(io.BytesIO(bytes(fd.FontFile2.read_bytes())))["maxp"].numGlyphs
print(fd.FontName, "CIDSet lists", listed, "- program has glyphs 0 to", glyphs - 1)
On the test file it prints CIDSet lists [0, 1, 2] - program has glyphs 0 to 3. With an /Identity CIDToGIDMap, CIDs and glyph numbers are the same; with a CIDToGIDMap stream, map each CID through it first.
Acrobat Pro shows the structure, though not the bits in this form. Following Adobe’s guide to advanced preflight inspections: select Use print production → Preflight from the All tools menu, then Options → Browse Internal PDF Structure to find the descriptor’s CIDSet, and Options → Create Inventory for the glyphs each font contains — Adobe’s page on Preflight reports describes what the inventory lists.
How to fix it free
Remove CIDSet. It is optional and the condition only exists when it is present, so deleting it resolves this without changing anything that is drawn or read:
import pikepdf
with pikepdf.open("in.pdf") as pdf:
for obj in pdf.objects:
if isinstance(obj, pikepdf.Dictionary) and "/CIDSet" in obj:
del obj["/CIDSet"]
pdf.save("out.pdf")
Acrobat cannot do this: Adobe’s page on the structure browser says you can navigate the PDF and its fonts there but not edit them, and Adobe documents no Preflight fixup that rewrites a CIDSet.
If the file also claims PDF/A-1, keep the entry and correct it instead, because PDF/A-1 requires CIDSet on CID font subsets. The 31-015 page has a script that rebuilds the bitmap from the program for the common /Identity case.
What Taggart does
Taggart does not detect this condition. It does not open embedded font programs, so it cannot tell which CIDs a program holds. On the test file it reports nothing about the font; the condition sits instead on the list of those Taggart asks a person to confirm before it will claim PDF/UA conformance.
What it does report under 31-014 is narrower: a descriptor with a CIDSet and no embedded program for it to describe.
/JBATCH+CIDSetShort declares a /CIDSet but has no embedded font program to describe.
That font also gets 31-009, which is the finding to act on — a CIDSet can only be right or wrong about a program that is there. For the condition on this page, use veraPDF or the script above before you confirm it.
How Taggart handles it
Software could decide this one, and Taggart does not check it yet. A clean report from Taggart says nothing about it either way. It is on the list of conditions Taggart asks you to confirm before it writes the PDF/UA claim into the file; check it by hand or with another validator, as described above.
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
Does a wrong CIDSet affect what a screen reader reads?
No. A viewer draws from the font program and a reader takes characters from the ToUnicode CMap; neither consults CIDSet. The condition is about the file describing its own subset accurately, and its cost falls on software that trusts the description.
Is it safe to remove CIDSet?
For PDF/UA, yes: it is optional, and the requirement only applies when it is present. PDF 2.0 deprecates it. The exception is a file that also claims PDF/A-1, which requires CIDSet on every CID font subset; rebuild it there instead.
Why would CID 0 be missing?
Because it is never on the page. CID 0 is the .notdef glyph, which every TrueType program has and no text deliberately uses. A producer that sets bits only for the CIDs its text uses leaves bit 0 clear, and the font fails on that alone.
What is a CID, as opposed to a glyph index?
A CID is the number the PDF uses for a glyph in a CID font; the glyph index is the number the font program uses. For a TrueType-based CID font the /CIDToGIDMap connects them, and with the common /Identity map they are the same number.
Where this sits in the standards
| Standard | Reference |
|---|---|
| Matterhorn Protocol 1.1 | Checkpoint 31 (Fonts), index 31-014 |
| PDF/UA-1 (ISO 14289-1) | Clause 7.21.4.2-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-015 A glyph listed in CIDSet is not present in the CID font programCIDSet promises CIDs the font does not contain.
- 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-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-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.
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-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-019 The font dictionary for a non-symbolic TrueType font does not contain an Encoding entryNon-symbolic TrueType fonts must declare an /Encoding.
- 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 does not check this condition yet, and lists it for you to confirm. Its validator is free and checks 75 of the 87 Matterhorn conditions software can decide — no watermark, no expiry, and no account needed up to 60 pages.
Taggart does not check this condition yet. A generated file that fails it is in the repository, with a test that fails the day Taggart starts catching it. Written on . The Acrobat steps follow Adobe’s documentation and were not walked through by hand. Last updated .