Skip to content

ci(test): run every embedded font through OTS - #806

Merged
andiwand merged 1 commit into
mainfrom
ci/ots-sanitize-fonts
Sep 2, 2026
Merged

ci(test): run every embedded font through OTS#806
andiwand merged 1 commit into
mainfrom
ci/ots-sanitize-fonts

Conversation

@andiwand

@andiwand andiwand commented Sep 2, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Part 1 of #765 — the cheap, deterministic half.

OTS is the sanitizer every browser puts in front of an @font-face, and it is far stricter than FreeType or fontTools: a font it rejects is dropped whole and every glyph renders as tofu. font_is_usable (html/pdf_file.cpp) only probes that our own encode path does not throw; whether OTS accepts the bytes we then emit has never been checked anywhere. #613, two thirds of #660 and #702 all shipped that way and were all found by eye.

What lands

test/scripts/check_fonts.py <dir> walks the html, pulls out every data:font/...;base64 payload, sanitizes each distinct one once (the same font is embedded in every page of a document) and, on a rejection, names the html file, the font-family, and OTS's own reason.

The test job runs it over build/test/output right after odr_test, next to the existing html-tidy / compare-html steps. opentype-sanitizer==9.2.0 joins the pip install; the script falls back to ots-sanitize on PATH.

The point of doing it this way: it needs no browser and no reference output, so it cannot be papered over by regenerating that — which is how #660 came to sit in the reference unnoticed.

Verification

Over today's corpus output:

765 distinct font(s), 8492 embedding(s), 0 rejected by OTS

in 8 seconds.

And with one font's first table tag clobbered, to prove it is not vacuous:

FAIL .../broken.html: odr-f1
     Failed to sanitize file!
     WARNING: Table directory is not correctly ordered
     ERROR: no supported glyph data table(s) present
     and 1 more embeddings
3 distinct font(s), 6 embedding(s), 1 rejected by OTS
exit=1

Not in this PR

Part 2 of the issue — the glyph-level oracle comparing gs -sDEVICE=txtwrite against the html's text — and the stronger "assert it inside odr_test over test/src/internal/font/" variant. Both are worth doing and neither blocks this.

CI-only, so no CHANGELOG.md entry.

OTS is the sanitizer every browser puts in front of an `@font-face`, and it is
far stricter than FreeType or fontTools: a font it rejects is dropped whole and
the text renders as tofu. #613, two thirds of #660 and #702 all shipped that
way and were all found by eye.

`test/scripts/check_fonts.py` pulls every `data:font/...;base64` payload out of
the generated html, sanitizes each distinct one once and names the html file
and the `font-family` of any OTS refuses. The `test` job runs it over
`build/test/output` right after `odr_test`.

No browser, no reference output — which is the point, since a regression
already in the reference output is invisible to the comparison and #660 in fact
sat there.

Over today's corpus: 765 distinct fonts, 8492 embeddings, none rejected, in
eight seconds.

Part 1 of #765.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018cMYRXLJdiCkH65Jm2W9B5
@andiwand
andiwand merged commit b372ea0 into main Sep 2, 2026
36 checks passed
@andiwand
andiwand deleted the ci/ots-sanitize-fonts branch September 2, 2026 18:38
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