font_discovery
Mojo module đźˇ
font_discovery
Font discovery: resolves a family/slant/weight request to a font file path on disk, with no linked library. One of the three parts of text rendering, alongside glyph resolution and metrics (ttf.mojo) and rasterization (fill_path_aa, see path.mojo).
Four steps, matching what libfontconfig does for a drawing library:
- Enumerate. Walk each platform’s font directories
(
_font_directories), collect everysfntcontainer (.ttf/.ttc/.otf/.otc), and read each one’s identity from itsname/OS/2/head/posttables (_parse_face). No XML; the font files are the database. The result is written to a cache file (see below), since it is the same on every run until a font is installed or removed. - Expand generic families. “sans-serif”/“serif”/“monospace” and the
metric aliases (Helvetica, Arial, Times, Courier) are ordered
preference lists, not real families (
_family_candidates) – the job fontconfig’s/etc/fonts/conf.d/*.confrules do. - Score, don’t filter. Every installed face is ranked and the best
wins, so a request resolves as long as one font is installed
(
_score). Terms run in fontconfig’s priority order (FcCompare*): family, spacing, slant, weight, width. - Fall back per character.
resolve_font_file_for_charranks a font that maps the codepoint above every other term, reading candidatecmaptables in score order (_face_covers_codepoint) – fontconfig’sFC_CHARSETconstraint.
Not covered: fontconfig’s XML rule engine, per-language coverage matching, and named-instance expansion of variable fonts (a variable font matches as its default instance).
Where it looks: on Linux ~/.local/share/fonts and ~/.fonts plus
/usr/share/fonts, /usr/local/share/fonts and /usr/share/X11/fonts;
on macOS ~/Library/Fonts, /Library/Fonts, /System/Library/Fonts
(and its Supplemental) plus Homebrew’s font prefixes.
CANVAS_MOJO_FONT_PATH (colon-separated directories) is searched
ahead of those. Fonts have to be installed for text to render; this
package bundles none.
A scan costs a few milliseconds, most of it the directory walk, and
its result is a pure function of which font files are installed – the
same table on every run of every program. FontDatabase() therefore
reads it from a cache file when one is valid and writes one when it
scans (_cache_path, _read_cache, _write_cache), which takes a
first text call from tens of milliseconds to about one. The cache is
an accelerator and never a source of truth: unreadable, stale, corrupt
or unwritable, the scan runs exactly as it would have.
Invalidation. The cache records _CACHE_FORMAT, the font
directories searched, and the modification time of every directory the
walk visited. Installing or removing a font changes its directory’s
mtime, so the next call rebuilds rather than silently missing the new
font – the failure mode a version-keyed-only cache (matplotlib’s
fontlist-vXXX.json) is famous for.
CANVAS_MOJO_FONT_CACHE overrides where the file lives, and the
value off disables the cache so every call scans – what the tests
and anything debugging discovery want. Otherwise it is
$XDG_CACHE_HOME/canvas_mojo/fonts.txt, or ~/.cache/canvas_mojo/
when that is unset.
Matching against an already-built FontDatabase is arithmetic over a
list, so build one FontDatabase (or FontCache) and reuse it rather
than resolving per call.
This module imports nothing from canvas.text, which is why
FontSlant/FontWeight and the small binary readers below live here:
Mojo resolves a struct’s method surface, and whatever it imports,
eagerly. It is also why _face_covers_codepoint walks a cmap here –
it answers “is this codepoint mapped” from a byte range read off disk,
where TTFFace needs the whole parsed file.
This module resolves a font file and nothing more: no outline parsing, measuring, hinting or rasterizing.
Structs
FontSlant: A font’s upright/italic/oblique style. Defined here to keep this module independent ofcanvas.text;render.mojore-exports it.FontWeight: A font’s normal/bold weight, defined here for the same reason FontSlant is.FontFace: One installed face: where its file is, what it calls itself, and the handful of style axes matching compares. Everything here comes out of the font’s own tables; nothing is inferred from the filename.FontDatabase: Every installed face on this machine, scanned once.