Font
A high-performance text rendering library for Odin. Parses TrueType (.ttf), OpenType/CFF (.otf), and variable fonts with bitmap rasterization, SDF, MSDF, GSUB shaping, and text layout — all in pure Odin with zero dependencies.
Feature Comparison
| Feature | Font | stb_truetype | fontdue | msdfgen | FreeType |
| TrueType outlines | ✓ | ✓ | ✓ | — | ✓ |
| CFF/OTF outlines | ✓ | ✓ | ✓ | — | ✓ |
| Bitmap rasterization | ✓ | ✓ | ✓ | — | ✓ |
| SDF generation | ✓ | ✓ | — | ✓ | — |
| MSDF generation | ✓ | — | — | ✓ | — |
| MTSDF generation | ✓ | — | — | ✓ | — |
| GSUB substitution | ✓ | — | — | — | — |
| GPOS kerning | ✓ | partial | ✓ | — | ✓ |
| Text layout | ✓ | — | ✓ | — | — |
| Variable fonts | ✓ | — | — | — | ✓ |
| COLR/CPAL color | ✓ | — | — | — | ✓ |
| Autohinting | ✓ | — | — | — | ✓ |
| Font atlas packing | ✓ | ✓ | — | ✓ | — |
| OS/2 metadata | ✓ | — | — | — | ✓ |
| Glyph names | ✓ | — | — | — | ✓ |
| TrueType hinting VM | — | — | — | — | ✓ |
| Zero dependencies | ✓ | ✓ | ✓ | — | — |
Quick Start
import tt "font"
// Load a font
font: tt.Font_Info
tt.init_font(&font, raw_data(font_data), 0)
scale := tt.scale_for_pixel_height(&font, 24.0)
// Render a glyph to bitmap
w, h, xoff, yoff: i32
bitmap := tt.get_codepoint_bitmap(&font, scale, scale, 'A', &w, &h, &xoff, &yoff)
defer tt.free_bitmap(bitmap)
// Lay out a string (with kerning + GSUB)
result := tt.layout_text(&font, "Hello!", {
scale = scale, max_width = 400, wrap = .Word, apply_gsub = true,
})
defer delete(result.glyphs)
// Generate MSDF for GPU text
msdf := tt.get_codepoint_msdf(&font, scale, 'A', 4, 128, 32.0, &w, &h, &xoff, &yoff)
defer tt.free_msdf(msdf)
// Variable font at weight=700
coords: tt.Var_Coords
tags := [?]string{"wght"}
vals := [?]f32{700}
tt.set_variation(&font, &coords, tags[:], vals[:])
bitmap_bold := tt.get_glyph_bitmap_var(&font, scale, scale, glyph, &coords, &w, &h, &xoff, &yoff)
API Overview
Core — Font Loading
| Procedure | Purpose |
init_font(info, data, offset) | Initialize a font from raw bytes |
scale_for_pixel_height(info, px) | Get scale factor for a target pixel height |
find_glyph_index(info, codepoint) | Map Unicode codepoint → glyph index |
Rendering — Bitmaps
| Procedure | Purpose |
get_codepoint_bitmap(info, sx, sy, cp, ...) | Render a glyph to a new grayscale bitmap |
make_codepoint_bitmap(info, buf, w, h, stride, sx, sy, cp) | Render into an existing buffer |
get_glyph_bitmap_hinted(info, sx, sy, gi, mode, ...) | Render with autohinting |
get_glyph_bitmap_var(info, sx, sy, gi, coords, ...) | Render at specific variation coordinates |
free_bitmap(bmp) | Free a rendered bitmap |
Rendering — Distance Fields
| Procedure | Purpose |
get_codepoint_sdf(info, scale, cp, pad, ...) | Single-channel signed distance field |
get_codepoint_msdf(info, scale, cp, pad, ...) | Multi-channel SDF (3 channels, sharp corners) |
get_codepoint_mtsdf(info, scale, cp, pad, ...) | Multi-channel + true distance (4 channels) |
Text — Layout & Shaping
| Procedure | Purpose |
layout_text(info, text, settings) | Position glyphs with wrapping, alignment, kerning |
apply_gsub_default(info, glyphs) | Apply default GSUB substitutions |
apply_gsub_feature(info, tag, glyphs) | Apply a specific feature (e.g. "liga") |
get_codepoint_kern_advance(info, cp1, cp2) | Get kerning between two codepoints |
Metrics
| Procedure | Purpose |
get_font_vmetrics(info, &asc, &desc, &gap) | Ascent, descent, line gap |
get_codepoint_hmetrics(info, cp, &adv, &lsb) | Advance width, left side bearing |
get_codepoint_box(info, cp, &x0, &y0, &x1, &y1) | Glyph bounding box |
get_os2_metrics(info, &os2) | Weight, width, x-height, cap-height |
get_glyph_name(info, gi) | PostScript glyph name |
Variable Fonts
| Procedure | Purpose |
is_variable_font(info) | Check if font has variation axes |
get_variation_axes(info, axes) | Enumerate axes (weight, width, etc.) |
set_variation(info, coords, tags, values) | Set axis values with avar remapping |
get_glyph_hmetrics_var(info, gi, coords, &adv, &lsb) | Metrics at specific axes |
Atlas Packing
| Procedure | Purpose |
pack_begin(ctx, pixels, w, h, stride, pad) | Start packing glyphs into atlas |
pack_font_ranges(ctx, data, index, ranges, n) | Pack character ranges |
pack_end(ctx) | Finish packing |
get_packed_quad(chardata, w, h, idx, &x, &y, &q, align) | Get UV coords for rendering |
Performance
Benchmarked against stb_truetype (C), fontdue (Rust), and msdfgen (C++) on AdwaitaSans-Regular.ttf:
| Operation | stb (C) | Font | fontdue (Rust) | msdfgen (C++) |
| init | 46 ns | 48 ns | 11M ns | — |
| cmap lookup | 10.8 ns | 5.5 ns | 2.2 ns | — |
| glyph shape | 59 ns | 6 ns | — | 1,074 ns |
| bitmap | 1,146 ns | 2,669 ns | 941 ns | — |
| SDF | 76,862 ns | 67,684 ns | — | 106,242 ns |
| MSDF | — | 242,689 ns | — | 803,245 ns |
| kerning | 64 ns | 19 ns | 4.2 ns | — |
| var bitmap | — | 2,867 ns | — | — |
Testing
Tests and benchmarks are on the testing branch:
git checkout testing
./tests/run_tests.sh # Oracle comparison vs stb_truetype
./tests/bench/run_bench.sh # Four-way performance benchmark
./tests/correctness/run_correctness.sh # Pixel-level comparison vs FreeType
License
Sharkk Minimal License — see license.md