mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-31 13:56:54 -06:00
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