Fonts: rusttype to swash
Drawing text is its own ecosystem.
Before a UI can shape or lay out text it needs glyphs: the outlines of individual letters, turned into pixel coverage. Rust's first popular answer was rusttype (2016), later deprecated in favour of ab_glyph, which does the same job with a smaller, faster API. Give it font bytes and a character, and it hands back an outline and, when drawn, a coverage value per pixel. There is no shaping and no layout. (Adjust the font path for your system.)
use ab_glyph::{Font, FontRef, PxScale, ScaleFont};
use book_error::{Context, Result};
fn main() -> Result<()> {
// Three different failures, three different variants: a missing file
// (Io), a corrupt font (Font), and a glyph with no outline (Missing).
let bytes = std::fs::read("/System/Library/Fonts/Supplemental/Arial.ttf")?;
let font = FontRef::try_from_slice(&bytes)?;
let scaled = font.as_scaled(PxScale::from(32.0));
// Glyph outlines in, coverage values out: no shaping, no layout.
let glyph = font.glyph_id('R').with_scale(32.0);
let outline = font.outline_glyph(glyph).context("font has no outline for 'R'")?;
let bounds = outline.px_bounds();
outline.draw(|_x, _y, coverage| {
let _ = coverage; // 0.0..=1.0 alpha for one pixel
});
println!("R is {}x{} px, advance {}", bounds.width(), bounds.height(), scaled.h_advance(font.glyph_id('R')));
Ok(())
} The older crate did the same work through a layout iterator that positioned glyphs one after another, which is the shape of the API that most 2016-era Rust GUIs were written against.
use book_error::{Context, Result};
use rusttype::{point, Font, Scale};
// rusttype (2016) is deprecated; ab_glyph is its maintained successor.
// The shape of the API shows what "just rasterize text" looked like.
fn main() -> Result<()> {
let bytes = std::fs::read("/System/Library/Fonts/Supplemental/Arial.ttf")?;
let font = Font::try_from_vec(bytes).context("file is not a valid TrueType/OpenType font")?;
let glyphs: Vec<_> = font.layout("Hi", Scale::uniform(32.0), point(0.0, 32.0)).collect();
for g in &glyphs {
if let Some(bb) = g.pixel_bounding_box() {
println!("glyph at {}..{}", bb.min.x, bb.max.x);
}
}
Ok(())
} Both crates have been overtaken by the fontations family from the Google Fonts team: read-fonts and skrifa parse font files, and swash (which depends on skrifa) scales, rasterizes and shapes. In our lockfiles skrifa is everywhere: egui, iced, Slint, Xilem, Dioxus Native, Floem and gpui all resolve it, and Vello, Parley, cosmic-text and swash depend on it directly. The layers above it, shaping and layout, are the next two pages.