Matterhorn 31-015 · PDF/UA-1 UA1:7.21.4.2-4
CIDSet marks glyphs the CID font does not contain
CIDSet promises CIDs the font does not contain.
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 listed in the CIDSet string is not present in the font program.”
Matterhorn Protocol 1.1, checkpoint 31, index 31-015, section UA1:7.21.4.2-4. Software can decide this one on its own.
What a screen reader actually does
Processors may mis-handle the subset.
What the condition is
A CID font’s descriptor may carry /CIDSet, a bitmap with one bit per CID — high-order bit first, the first byte covering CIDs 0 to 7 — saying which glyphs the embedded subset contains. When it is present, PDF/UA-1 requires it to match the program. Condition 31-014 is a bit left clear for a CID that is there. This one is a bit set for a CID that is not.
The file Taggart’s test suite uses for this page has a TrueType-based CID font with four glyphs and an /Identity CIDToGIDMap, so CIDs 0 to 3 exist. Its CIDSet claims six:
/CIDSet <FC> % 1111 1100 → CIDs 0 to 5
CID 0 1 2 3 4 5 6 7
bit 1 1 1 1 1 1 0 0
program ✓ ✓ ✓ ✓ ✗ ✗
CIDs 4 and 5 are promised and absent.
Why a promise is the more dangerous error
A bitmap that leaves CIDs out undersells the font; the worst a trusting processor can do is decline to use a glyph it could have used. A bitmap that sets bits for missing CIDs oversells it. A processor that decided from CIDSet whether this subset could draw some new text — a form value, a correction — would conclude it could use CID 4. The viewer would then go to the program for glyph 4, not find it, and draw .notdef. That is 31-011: text that reads as a character and shows as a blank.
What a screen-reader user hears
In the file as it stands, nothing different. The page draws CIDs 1 to 3, the ToUnicode CMap maps them, and pdftotext returns ABC. CIDSet is not consulted by either path. The condition is a false statement in the file, not yet a wrong word in anybody’s ear.
Where it comes from
A subset that shrank after the bitmap was written. File-size optimisers and tools that merge documents sometimes re-subset fonts, dropping glyphs no text uses. One that copies the old descriptor across, CIDSet included, leaves bits set for glyphs it just removed.
A bitmap written as a range. A producer that sets a solid run of bits — every CID from 0 up to some limit — rather than the CIDs it actually embedded claims whatever falls inside the run, present or not.
A descriptor shared between subsets. Two subsets of the same font pointing at one descriptor cannot both match its bitmap.
How to check it free
Read the bits. The 31-014 page has a short pikepdf and fontTools script that prints the CIDs a CIDSet lists beside the glyphs the program contains. On the test file it prints CIDSet lists [0, 1, 2, 3, 4, 5] - program has glyphs 0 to 3. Any listed CID beyond the program’s last glyph — or, with a CIDToGIDMap stream, any listed CID that maps to no glyph — is this condition.
Acrobat Pro shows where the pieces are. 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 and its CIDSet; Options → Create Inventory lists the glyphs each embedded font contains, as Adobe’s page on Preflight reports describes.
veraPDF checks CIDSet under clause 7.21.4.2 for subset-tagged fonts; see the FAQ below on whether it covers this direction.
How to fix it free
Remove CIDSet, unless the file claims PDF/A-1. It is optional, and the condition only exists when it is present. The deletion script is on the 31-014 page. Acrobat cannot edit the entry: Adobe’s structure browser is for looking at fonts, not changing them.
Or rebuild it from the program. For a TrueType-based CID font with an /Identity CIDToGIDMap — the common case — every glyph number in the program is a CID that is present, so the correct bitmap is simply a bit for each one. This is also the fix for a file that claims PDF/A-1, which requires the entry:
import io
import pikepdf
from fontTools.ttLib import TTFont
with pikepdf.open("in.pdf") as pdf:
for cidfont in pdf.objects:
if not (isinstance(cidfont, pikepdf.Dictionary)
and cidfont.get("/Subtype") == "/CIDFontType2"
and cidfont.get("/CIDToGIDMap") == "/Identity"):
continue
fd = cidfont.FontDescriptor
if "/CIDSet" in fd and "/FontFile2" in fd:
n = TTFont(io.BytesIO(bytes(fd.FontFile2.read_bytes())))["maxp"].numGlyphs
bits = bytearray((n + 7) // 8)
for cid in range(n):
bits[cid // 8] |= 0x80 >> cid % 8
fd.CIDSet = pdf.make_stream(bytes(bits))
pdf.save("out.pdf")
On the test file the bitmap becomes <F0>: CIDs 0 to 3. It fixes 31-014 in the same pass. For a CID font with a CIDToGIDMap stream, or a CFF-based one, the mapping from CID to glyph has to be read first, and re-exporting is usually less work.
What Taggart does
Taggart does not detect this condition. It does not open embedded font programs, so it has no glyph count to hold a CIDSet against. On the test file it reports nothing about the font. A clean report is therefore not evidence that a CIDSet is accurate, which is why 31-015 is on the list of conditions Taggart asks a person to confirm before it claims conformance.
The one CIDSet problem it does report is a CIDSet on a font whose program is not embedded at all, which it files under 31-014 alongside 31-009. For an over-full bitmap, read the bits as 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
If no text uses the extra CIDs, why does it matter?
Because the bitmap exists to be believed. Software that decides from CIDSet what a subset can draw, rather than opening the program, will think those glyphs are available. Anything it then sets in them is drawn as .notdef — blanks or boxes where the text says there are characters, which is condition 31-011.
Can a CIDSet be too long without listing extra CIDs?
Yes, harmlessly. Trailing bytes whose bits are all clear claim nothing. The condition is about bits that are set, not about the length of the stream.
Does veraPDF catch this direction?
veraPDF's rule for clause 7.21.4.2 is worded around CIDs present in the program that the bitmap fails to identify, and only applies to fonts with a subset tag. Its documentation does not say whether a set bit with no glyph behind it fails as well, so compare the bitmap with the program directly rather than relying on a pass.
Where this sits in the standards
| Standard | Reference |
|---|---|
| Matterhorn Protocol 1.1 | Checkpoint 31 (Fonts), index 31-015 |
| PDF/UA-1 (ISO 14289-1) | Clause 7.21.4.2-4 |
| 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-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-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-013 A glyph listed in CharSet is not present in the Type 1 font programCharSet promises glyphs the font does not contain.
- 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.
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-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 .