Skip to content

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.

#QuestionVerdict
RQ1What layering do mature libraries converge on, and where do they put allocation, lifetime and thread-safety?Five objects, caches beside them
RQ2Is 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
RQ3How do variation coordinates flow through shaping and rasterization?One normalized vector per instance, everywhere
RQ4How are discovery, matching and fallback done?Cached index, coverage-pruned fallback list
RQ5What must a font inspector expose?Reflectable tables plus enumerations
RQ6How 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, OpenType features 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 ​

SubjectEcosystemCategoryWhat it teaches
FreeTypeCC coreFace, size and slot as layers; a shared mutable slot is why a face is single-threaded.
HarfBuzzC++ / C ABIC core, shaperThe face as a tag → bytes function; variation on the sized font; callback sinks for outlines and paint.
FontconfigCfont databaseThe fallback list as the primary API, pruned by coverage; lexicographic scoring over a priority table.
PangoC / GLiblayout engineA fontset is the unit of fallback; scale the shaper so its output is the layout unit.
stb_truetypeCparser, rasterizerOffsets over a borrowed buffer, ~700-line rasterizer, and the source of every raylib text limit in sparkles.
msdfgenC++rasterizerOwning bitmap versus borrowed section; a path object for random access; units as an explicit enum.
SlugC++ / GPUrasterizerExact per-pixel outline evaluation on the GPU, and why it costs too much at terminal sizes.
SkiaC++platform text stackOne canonical glyph-cache key; the rasterizer as a private seam; metric validity flags.
Core TextC / Appleplatform text stackOne descriptor type for query and result; per-string fallback from the OS.
DirectWriteC++ COMplatform text stackA variation instance is a new face; fallback returns a run length and a scale.
GhosttyZigterminal font stackStorage, resolution policy and cache as three owners; deferred faces; box drawing as a synthetic face.
crossfontRustterminal font stackA terminal's metric set is seven numbers; charset-pruned, lazily opened fallback.
FontationsRustparserGenerated zero-copy table readers; coordinates passed, never stored; differential testing against FreeType.
ttf-parserRustparserA zero-allocation face on the stack, and the regret of mutable variation state.
rustybuzzRustshaperA HarfBuzz port is ~17 KLOC, and tracking upstream is what ended it.
AllsortsRustparser, shaperAn independent shaper lands at the same ~17 KLOC; partial results with the first error.
swashRustshaper, rasterizerCluster-shaped shaping output and a raster source order for colour versus outline glyphs.
fontdbRustfont databaseA CSS-matching in-memory database with no per-codepoint fallback.
cosmic-textRustlayout engineOne &mut context is simple and thread-confining; the raster cache key to copy.
Parley + FontiqueRustlayout engine, font databaseTwo contexts, long-lived and scratch; Synthesis as data; fallback probed by script sample.
fontdueRustrasterizerThe fill core is ~200 lines; cached geometry is where the speed is.
ab_glyphRustrasterizerA by-value scaled-font wrapper; outlines as data; px-per-em as the only honest size unit.
VelloRust / WGSLrasterizer (GPU compute)The renderer contract is (blob, index, size, coords, hint) + [glyph, x, y]; 5.4 KLOC of shaders.
glyphonRust / wgpurasterizer (GPU atlas)Mask and colour atlases, generation-stamped eviction, one instanced draw.
font-kitRustplatform adapter, databaseA serializable {path or bytes, index} handle between discovery and loading; CSS §5.2 in ~130 lines.
SixLabors.FontsC#layout engineTwo-level loading for listing versus rendering; an outline sink that can refuse.
TypographyC#parser, shaperA managed TrueType interpreter is ~2,100 lines and incomplete.
SharpFontC#managed bindingHand-mirrored structs are a silent-corruption contract; ImportC removes that class of bug.
go-text/typesettingGolayout engineThe most complete from-scratch managed stack; Footprint is the explorer's list row.
x/image/fontGoparser, rasterizerCaller-owned scratch per call; outlines as a flat segment array.
opentype.jsJavaScriptparserReflective table objects make an inspector free; enumerable GSUB records.
fontkitJavaScriptparser, shaperDeclarative table schemas; WOFF2 behind the same font interface.
lib-fontJavaScriptparserThe enumeration API an inspector wants: scripts, language systems, features.
fontToolsPythonparser, toolkitThe 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.

SubjectParseDiscoverMatch / fallbackShapeOutlineRaster
FreeType✓✓✓
HarfBuzz✓✓✓
Fontconfig✓✓
Pango✓✓
stb_truetype✓✓✓
msdfgen✓✓
Slug✓✓
Skia✓✓✓✓✓✓
Core Text✓✓✓✓✓✓
DirectWrite✓✓✓✓✓✓
Ghosttypart✓✓✓✓
crossfont✓✓✓
Fontations✓✓
ttf-parser✓✓
rustybuzz✓
Allsorts✓✓✓
swash✓✓✓✓
fontdb✓match
cosmic-text✓✓✓✓✓
Parley + Fontique✓✓✓
fontdue✓
ab_glyph✓✓
Vello✓✓
glyphonatlas
font-kit✓✓✓✓
SixLabors.Fonts✓✓✓✓✓
Typography✓✓✓✓✓
SharpFontbindbindbind
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.

PlacementSubjects
mutable state on the facettf-parser, ab_glyph, FreeType (FT_Set_Var_Design_Coordinates)
on the sized font / instance objectHarfBuzz, Core Text, Pango, Skia (SkFontArguments), go-text
instance is a new faceDirectWrite, SixLabors.Fonts (CreateVariationInstance)
passed per call as a borrowed locationFontations, Vello, Parley
applied by rewriting tablesfontTools (varLib.instancer)
parsed but not applied, or not supportedTypography, 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.

YearMilestone
2005Loop and Blinn publish resolution-independent curve rendering on the GPU, the ancestor of Slug.
2007Valve's single-channel signed distance fields for text.
2014msdfgen begins as Viktor Chlumský's master's thesis: multi-channel fields keep corners.
2016Variable fonts arrive in OpenType 1.8 (September); crossfont starts as Alacritty's font stack.
2017Lengyel's Slug paper in JCGT.
2019Allsorts ships inside the Prince typesetter; SharpFont's last commit.
2021COLR version 1 colour fonts are specified in OpenType 1.9 (December).
2023Typography's last commit.
2026At 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, fvar axes, and the same text shaped with calt and liga on 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, cmap coverage), 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.
bash
dub run --single docs/research/font-libraries/examples/outline-sink-raster.d -- /path/to/font.ttf g

Suggested reading paths ​

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: