Files
figcraft/crates
Wiktor KlucznyandClaude Opus 5.5 6acbd91347 M8: text reads only the fallback fonts it draws with
The first text laid out asked the font database for its fallback faces
(the fonts a run's missing characters are drawn with), and each of the 29
fallback families went through `find`, which reads every face of a family.
On Linux with Noto Sans CJK that is all seven 16–20 MB files, twice (SC and
KR name the faces of the same collections, and the bytes were shared only
within one family); Hiragino Sans is ten files on macOS. Each text laid out
afterwards repeated the 29 lookups under the font database's write lock.

- `fallbacks_for` reads, per family, the face nearest the run's style and
  only when it is nearer than the faces read already (`fallback_face`,
  `nearer_pending`); a face that can't be read gives way to the next one.
  Used by name, a family still reads all its faces (`find`).
- Faces of one file share its bytes across families (`file_bytes`, kept
  while the file keeps its length).
- The fallback faces are worked out once per style until fonts change
  (`FallbackLists`, by generation), not for every text laid out.
- Choosing a face by name before it is read counts a slant mismatch, italic
  or oblique, as `best` does: DejaVu Sans names its slanted faces Oblique,
  and a regular run's fallback stays Book.

Measured on Linux with Noto Sans CJK, DejaVu Sans and Inter installed: the
first text laid out read 41 font files (265 MiB, kept in memory) and parsed
41 faces; now 3 files (20 MiB) and 4 faces, 6 and 8 with a bold run too,
150–190 ms less file reading. The fallback lookups per text laid out took
about 45 µs and now 0.2 µs (lookup logic timed alone). The faces drawn are
the same except between two equally near the run's style.

Tests: fontdb_fallback_tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GmygZSTYRL3cVw7cJWMG3B
2026-10-30 12:24:57 +01:00
..
…