# 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 ```odin 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: ```bash 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](license.md)