Font libraries
Status: complete — 34 subjects surveyed across C, C++, Zig, Rust, C#, Go, JavaScript and Python. The synthesis in comparison.md closes each research question with a verdict; the shared vocabulary is concepts.md. Last reviewed: October 3, 2026
sparkles is about to replace its font stack. Today a terminal, a code viewer and their Android builds draw text through sparkles:raylib-text: a raylib (stb_truetype) atlas for single codepoints beside a FreeType and HarfBuzz C shim for clusters and fallbacks, added as a spike for notcurses features. A new programmer-font explorer app needs everything that stack lacks: variable fonts, feature control, every metric set, outlines, inspection and real font discovery. The decision is to write sparkles:font from scratch, in D wherever D is the better place for the code. This survey is the evidence base for that library's API.
The questions this survey answers
Every subject was read for the same six questions, under a fixed seven-part analysis spine, so the comparison can set like against like.
| # | Question | Verdict |
|---|---|---|
| RQ1 | What layering do mature libraries converge on, and where do they put allocation, lifetime and thread-safety? | Five objects, caches beside them |
| RQ2 | Is a from-scratch rasterizer feasible on the CPU or in GPU compute, and what does quality cost? | Go for a D CPU rasterizer; no-go for D shaping; defer GPU |
| RQ3 | How do variation coordinates flow through shaping and rasterization? | One normalized vector per instance, everywhere |
| RQ4 | How are discovery, matching and fallback done? | Cached index, coverage-pruned fallback list |
| RQ5 | What must a font inspector expose? | Reflectable tables plus enumerations |
| RQ6 | How are glyph outlines exposed, and in what units? | Sink and segment array over one decoder, font units |
How this survey was scoped
The app this library serves was scoped first, in a structured interview whose product research covered the font managers, inspectors and comparison tools designers use today. Their features fell into three groups, and that grouping decided which libraries to read:
- Inspection — tables, metadata,
OpenTypefeatures with toggles, glyph maps, variation axes, language coverage, metrics reports, CSS export. Every browser inspector does this client-side on a JavaScript parser, which is why opentype.js, fontkit and lib-font are here. - Preview and comparison — custom text at any size, waterfalls, synced multi-font cards, metric overlays. This needs shaping, variation and rasterization: the C cores, the Rust stacks and the platform stacks.
- Management — folders, tags, activation, cloud catalogs. Discovery and matching are in scope (fontconfig, fontdb, font-kit); activation and catalogs are not.
The explorer's own identity is programming and terminal fonts, which is why the two terminal stacks, Ghostty and crossfont, are read as closely as the general-purpose libraries.
Master catalog
| Subject | Ecosystem | Category | What it teaches |
|---|---|---|---|
| FreeType | C | C core | Face, size and slot as layers; a shared mutable slot is why a face is single-threaded. |
| HarfBuzz | C++ / C ABI | C core, shaper | The face as a tag → bytes function; variation on the sized font; callback sinks for outlines and paint. |
| Fontconfig | C | font database | The fallback list as the primary API, pruned by coverage; lexicographic scoring over a priority table. |
| Pango | C / GLib | layout engine | A fontset is the unit of fallback; scale the shaper so its output is the layout unit. |
| stb_truetype | C | parser, rasterizer | Offsets over a borrowed buffer, ~700-line rasterizer, and the source of every raylib text limit in sparkles. |
| msdfgen | C++ | rasterizer | Owning bitmap versus borrowed section; a path object for random access; units as an explicit enum. |
| Slug | C++ / GPU | rasterizer | Exact per-pixel outline evaluation on the GPU, and why it costs too much at terminal sizes. |
| Skia | C++ | platform text stack | One canonical glyph-cache key; the rasterizer as a private seam; metric validity flags. |
| Core Text | C / Apple | platform text stack | One descriptor type for query and result; per-string fallback from the OS. |
| DirectWrite | C++ COM | platform text stack | A variation instance is a new face; fallback returns a run length and a scale. |
| Ghostty | Zig | terminal font stack | Storage, resolution policy and cache as three owners; deferred faces; box drawing as a synthetic face. |
| crossfont | Rust | terminal font stack | A terminal's metric set is seven numbers; charset-pruned, lazily opened fallback. |
| Fontations | Rust | parser | Generated zero-copy table readers; coordinates passed, never stored; differential testing against FreeType. |
| ttf-parser | Rust | parser | A zero-allocation face on the stack, and the regret of mutable variation state. |
| rustybuzz | Rust | shaper | A HarfBuzz port is ~17 KLOC, and tracking upstream is what ended it. |
| Allsorts | Rust | parser, shaper | An independent shaper lands at the same ~17 KLOC; partial results with the first error. |
| swash | Rust | shaper, rasterizer | Cluster-shaped shaping output and a raster source order for colour versus outline glyphs. |
| fontdb | Rust | font database | A CSS-matching in-memory database with no per-codepoint fallback. |
| cosmic-text | Rust | layout engine | One &mut context is simple and thread-confining; the raster cache key to copy. |
| Parley + Fontique | Rust | layout engine, font database | Two contexts, long-lived and scratch; Synthesis as data; fallback probed by script sample. |
| fontdue | Rust | rasterizer | The fill core is ~200 lines; cached geometry is where the speed is. |
| ab_glyph | Rust | rasterizer | A by-value scaled-font wrapper; outlines as data; px-per-em as the only honest size unit. |
| Vello | Rust / WGSL | rasterizer (GPU compute) | The renderer contract is (blob, index, size, coords, hint) + [glyph, x, y]; 5.4 KLOC of shaders. |
| glyphon | Rust / wgpu | rasterizer (GPU atlas) | Mask and colour atlases, generation-stamped eviction, one instanced draw. |
| font-kit | Rust | platform adapter, database | A serializable {path or bytes, index} handle between discovery and loading; CSS §5.2 in ~130 lines. |
| SixLabors.Fonts | C# | layout engine | Two-level loading for listing versus rendering; an outline sink that can refuse. |
| Typography | C# | parser, shaper | A managed TrueType interpreter is ~2,100 lines and incomplete. |
| SharpFont | C# | managed binding | Hand-mirrored structs are a silent-corruption contract; ImportC removes that class of bug. |
| go-text/typesetting | Go | layout engine | The most complete from-scratch managed stack; Footprint is the explorer's list row. |
| x/image/font | Go | parser, rasterizer | Caller-owned scratch per call; outlines as a flat segment array. |
| opentype.js | JavaScript | parser | Reflective table objects make an inspector free; enumerable GSUB records. |
| fontkit | JavaScript | parser, shaper | Declarative table schemas; WOFF2 behind the same font interface. |
| lib-font | JavaScript | parser | The enumeration API an inspector wants: scripts, language systems, features. |
| fontTools | Python | parser, toolkit | The table schema as data; the canonical pen protocol for outlines. |
Taxonomy: layers covered
Which of the six layers each subject implements itself. A blank cell means the subject delegates the layer or does without it; the deep-dive's spine says which.
| Subject | Parse | Discover | Match / fallback | Shape | Outline | Raster |
|---|---|---|---|---|---|---|
| FreeType | ✓ | ✓ | ✓ | |||
| HarfBuzz | ✓ | ✓ | ✓ | |||
| Fontconfig | ✓ | ✓ | ||||
| Pango | ✓ | ✓ | ||||
| stb_truetype | ✓ | ✓ | ✓ | |||
| msdfgen | ✓ | ✓ | ||||
| Slug | ✓ | ✓ | ||||
| Skia | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Core Text | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| DirectWrite | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Ghostty | part | ✓ | ✓ | ✓ | ✓ | |
| crossfont | ✓ | ✓ | ✓ | |||
| Fontations | ✓ | ✓ | ||||
| ttf-parser | ✓ | ✓ | ||||
| rustybuzz | ✓ | |||||
| Allsorts | ✓ | ✓ | ✓ | |||
| swash | ✓ | ✓ | ✓ | ✓ | ||
| fontdb | ✓ | match | ||||
| cosmic-text | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Parley + Fontique | ✓ | ✓ | ✓ | |||
| fontdue | ✓ | |||||
| ab_glyph | ✓ | ✓ | ||||
| Vello | ✓ | ✓ | ||||
| glyphon | atlas | |||||
| font-kit | ✓ | ✓ | ✓ | ✓ | ||
| SixLabors.Fonts | ✓ | ✓ | ✓ | ✓ | ✓ | |
| Typography | ✓ | ✓ | ✓ | ✓ | ✓ | |
| SharpFont | bind | bind | bind | |||
| go-text/typesetting | ✓ | ✓ | ✓ | ✓ | ✓ | |
| x/image/font | ✓ | ✓ | ✓ | |||
| opentype.js | ✓ | part | ✓ | |||
| fontkit | ✓ | ✓ | ✓ | |||
| lib-font | ✓ | |||||
| fontTools | ✓ | ✓ |
Only the three platform stacks cover all six. Every from-scratch stack outside them stops before one layer, and the layer most often skipped is rasterization (managed layout engines) or shaping (rasterizers and parsers).
Taxonomy: where variation coordinates live
The decision RQ3 turns on, re-cut across the subjects that support variation.
| Placement | Subjects |
|---|---|
| mutable state on the face | ttf-parser, ab_glyph, FreeType (FT_Set_Var_Design_Coordinates) |
| on the sized font / instance object | HarfBuzz, Core Text, Pango, Skia (SkFontArguments), go-text |
| instance is a new face | DirectWrite, SixLabors.Fonts (CreateVariationInstance) |
| passed per call as a borrowed location | Fontations, Vello, Parley |
| applied by rewriting tables | fontTools (varLib.instancer) |
| parsed but not applied, or not supported | Typography, fontdue, stb_truetype, crossfont |
Milestones
Dates as given in the deep-dives and their cited sources; the two OpenType specification releases are from the OpenType version history.
| Year | Milestone |
|---|---|
| 2005 | Loop and Blinn publish resolution-independent curve rendering on the GPU, the ancestor of Slug. |
| 2007 | Valve's single-channel signed distance fields for text. |
| 2014 | msdfgen begins as Viktor Chlumský's master's thesis: multi-channel fields keep corners. |
| 2016 | Variable fonts arrive in OpenType 1.8 (September); crossfont starts as Alacritty's font stack. |
| 2017 | Lengyel's Slug paper in JCGT. |
| 2019 | Allsorts ships inside the Prince typesetter; SharpFont's last commit. |
| 2021 | COLR version 1 colour fonts are specified in OpenType 1.9 (December). |
| 2023 | Typography's last commit. |
| 2026 | At this survey's pins, rustybuzz is archived in favour of HarfRust, and Fontations backs Skia's font path in Chrome. |
Runnable examples
Five programs back measured claims: the first two the comparison's, the last three the font specification's Stage 0 spikes. Those that use HarfBuzz or FreeType reach it through hand-declared extern(C) prototypes with no C shim, and all skip cleanly when no font can be found.
harfbuzz-shape-features.d— HarfBuzz's object layers, the feature inventory an inspector lists,fvaraxes, and the same text shaped withcaltandligaon and off.outline-sink-raster.d— the outline sink, a variation reaching both advances and outlines, and a 71-line signed-area rasterizer rendering the result.ligature-cells.d— 160 programming ligatures shaped on and off: whether glyph count or advances leave the cell grid, and how far ligature ink reaches past its own cell.font-scan-timing.d— what a font catalog reads per face (table directory, family, weight,cmapcoverage), timed over every installed font, serially and on a worker pool.raster-oracle-diff.d— the accumulation rasterizer over every glyph of a face at four sizes, compared with FreeType (under FreeType's overlap rule, then also its flattening), with a finer flattening of its own, and with a 4×4 render that finds overlaps the font does not flag.
dub run --single docs/research/font-libraries/examples/outline-sink-raster.d -- /path/to/font.ttf gSuggested reading paths
- Designing
sparkles:font.comparison.mdtop to bottom, then HarfBuzz, Fontations, Ghostty and fontconfig in that order. - The terminal path. Ghostty, crossfont, glyphon, then
concepts.md§ units. - Writing a rasterizer. stb_truetype, fontdue, ab_glyph, the outline example, then Vello and Slug for the GPU side.
- Building the inspector. lib-font, opentype.js, fontTools, then comparison § RQ5.
- Deciding whether to own shaping. rustybuzz, Allsorts, go-text/typesetting, SixLabors.Fonts.
Sources
Every deep-dive cites its subject at a pinned commit, or for the three closed-source subjects (Core Text, DirectWrite, Slug) the vendor documentation and paper with retrieval dates. Shared references:
- OpenType specification.
- CSS Fonts Module Level 4.
- OpenType version history — release dates of 1.8 and 1.9.