Skip to content

Let a composite font's identifiers reach its glyphs through the charset - #15

Merged
tannevaled merged 1 commit into
mainfrom
cid-through-charset
Aug 27, 2026
Merged

Let a composite font's identifiers reach its glyphs through the charset#15
tannevaled merged 1 commit into
mainfrom
cid-through-charset

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

How an identifier reaches a glyph depends on which sort of program carries them.

  • TrueType-based: a map of its own, and where it says nothing the identifier is the glyph number.
  • CFF addressed by identifier: the mapping lives in the font program's own charset. The map a document may supply is defined only for the other sort and means nothing here.

This took the identifier for the glyph number in both cases — wrong in the worse of the two ways. Past the end of the font it draws nothing; inside it, which is the usual case, it draws a real glyph and the wrong one.

Measured over 5 999 corpus files

CFF fonts addressed by identifier 85
whose charset is not the identity 38
glyphs that came out wrong 11 148
right 467
blank 17

96 in every hundred, silently. The blank pages that first drew attention to this are the small half of the problem.

What it looks like

21 of 3 040 sample pages change. The worst goes from a page of scattered fragments to a page of text, and its distance from macOS's own rendering falls 0.0312 to 0.0196. Two of three spot-checked pages moved closer; the third by 0.0002 the wrong way, within the sparse-page blind spot of that metric.

An identifier the font does not name now draws nothing, rather than whichever glyph happens to carry that number.

The test

There was no scaffolding for this: the existing composite-font tests build TrueType-based fonts, where the defect is invisible. So the test builds a CID-keyed CFF whose one drawable glyph answers to identifier 100 and is glyph 1 — a reader taking one for the other looks for glyph 100 in a font of two and draws nothing.

It fails without the change and passes with it.

Needs go-opentype/opentype v0.10.0, which exposes the charset (opentype#32, merged).

Found by a licence-aware reading of the mature implementations — pdf.js, PDFBox, pdfcpu, qpdf, pypdf, all Apache/BSD, no code copied — which this fleet had never done.

Stacked on bounded-clip. 100% statement coverage, go vet and gofmt clean, nine cross-compile targets.

How an identifier reaches a glyph depends on which sort of program carries
them. A TrueType-based one says so in a map of its own, and where it says
nothing the identifier is the glyph number. A CFF one addressed by identifier
says nothing of the kind: the mapping lives in the font program's own charset,
and the map a document may supply is defined only for the other sort.

This took the identifier for the glyph number in both cases. That is wrong in
the worse of the two ways: past the end of the font it draws nothing, but
inside it — which is the usual case — it draws a real glyph and the wrong one.

Measured over 5 999 corpus files: of 85 CFF fonts addressed by identifier, 38
have a charset that is not the identity. Of the glyphs those fonts were asked
for by the pages that use them, 11 148 came out the wrong glyph, 467 right and
17 blank. Ninety-six in every hundred, silently.

Twenty-one of 3 040 sample pages change. Put beside macOS's own renderer, the
worst of them goes from a page of scattered fragments to a page of text, and
its distance from the reference falls from 0.0312 to 0.0196.

An identifier the font does not name now draws nothing, rather than whichever
glyph happens to carry that number.

The test builds a font addressed by identifier whose one drawable glyph answers
to identifier 100 and is glyph 1, so that a reader taking one for the other
looks for glyph 100 in a font of two and draws nothing. It fails without the
change and passes with it.

Found by a licence-aware reading of the mature implementations, which this
fleet had never done.
@tannevaled
tannevaled changed the base branch from bounded-clip to main August 27, 2026 08:59
@tannevaled
tannevaled force-pushed the cid-through-charset branch from 2be4b38 to 5694778 Compare August 27, 2026 08:59
@tannevaled
tannevaled merged commit ab0032d into main Aug 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant