Your text looks wrong. Arabic renders backwards, Hindi characters break, Thai glyphs collide. Typf fixes this in under a millisecond.
Render "Hello, مرحبا, 你好!" correctly the first time. No excuses.
Note: Typf is a community project by FontLab and is currently published under an evaluation license.
git clone https://github.com/fontlaborg/typf.git
cd typf
cargo build --release
./target/release/typf render "Hello, World!" -o hello.png -s 48
open hello.pngThat's it. Your text is rendered.
You're building a tool that draws text and the usual "just use the OS" path isn't working. Maybe you're cross-platform and macOS, Windows, and Linux give different results. Maybe you're rendering Arabic and the letters aren't joining. Maybe you're generating PNGs in a server process with no display. Typf gives you one consistent pipeline you control end to end.
Most developers don't know why "fi" is sometimes one glyph, why Arabic letters change shape mid-word, or why "AV" looks tighter than "AX". Here's the short version:
Shaping is the step most people skip and then wonder why things look wrong. A font is not a lookup table of character → picture. It's a program. For every run of text it specifies: which glyph IDs to use, whether to merge two characters into a ligature, how much to shift adjacent glyphs toward each other (kerning), and in what direction the whole run flows. Arabic letters have four contextual forms; Devanagari consonants stack into vertical conjuncts; even plain Latin has ligatures (fi, fl) and kerning pairs. The shaper reads the font's OpenType GSUB/GPOS tables and resolves all of that into a flat list of positioned glyph IDs.
Rendering takes that list and draws it. Each glyph in the font is stored as Bézier curves. The renderer traces those curves and either rasterizes them into a pixel grid (bitmap output) or emits SVG path data (vector output).
Export encodes the in-memory pixels or paths as PNG, PNM, SVG, JSON, etc.
"Hello, مرحبا, 你好!"
│
▼
┌─────────────┐
│ Shaper │ OpenType rules → which glyphs, where, in what direction
└──────┬──────┘
│ ShapingResult: glyph IDs + (x, y) positions
▼
┌─────────────┐
│ Renderer │ Bézier curves → pixels or vector paths
└──────┬──────┘
│ RenderOutput: RGBA bitmap or SVG/JSON
▼
┌─────────────┐
│ Exporter │ Pixels/paths → PNG, PNM, SVG, JSON bytes
└─────────────┘
Every box is swappable via a Rust trait. Five shaper implementations × seven renderer implementations = 35 valid backend combinations.
- Complex scripts: Arabic, Hindi, Thai, Hebrew, CJK — correct shaping for all
- Mixed directions: RTL and LTR in the same line, handled automatically
- Fast output: PNG, SVG, JSON in <1ms with optional two-level caching
- Small footprint: 500 KB minimal build; pay only for the backends you enable
- 35 combinations: 5 shapers × 7 renderers, all sharing the same pipeline API
- Color fonts: COLR v0/v1, SVG glyphs, bitmap glyphs (sbix/CBDT/EBDT)
- GPU rendering: Vello acceleration where available; CPU fallback everywhere else
| Need | Command | Speed | Quality |
|---|---|---|---|
| Fastest data | none + JSON |
25K ops/sec | Glyph data only |
| Complex scripts | harfbuzz + zeno |
3K ops/sec | 247 grayscales |
| macOS best | coretext + coregraphics |
4K ops/sec | 254 levels |
| Pure Rust | harfbuzz + opixa |
2K ops/sec | 25 levels (mono) |
| Backend | Scripts | Features | Performance | Platform |
|---|---|---|---|---|
| none | Latin only | Simple LTR | 25K ops/sec | All |
| harfbuzz | All (200+) | Full OpenType | 4K ops/sec | All |
| icu-hb | All + normalization | Unicode + OpenType | 3.5K ops/sec | All |
| coretext | All | Native macOS | 4.5K ops/sec | macOS only |
| Backend | Anti-alias | Color | Output | Performance | Platform |
|---|---|---|---|---|---|
| opixa | Monochrome | No | Bitmap | 2K ops/sec | All (pure Rust) |
| skia | 256 levels | Yes (COLR/SVG/bitmap) | Bitmap/SVG | 3.5K ops/sec | All |
| zeno | 256 levels | Yes (COLR/SVG/bitmap) | Bitmap/SVG | 3K ops/sec | All (pure Rust) |
| vello-cpu | 256 levels | Yes (COLR/bitmap) | Bitmap | 3.5K ops/sec | All (pure Rust) |
| vello | 256 levels | Bitmap | 10K+ ops/sec | GPU required | |
| coregraphics | 256 levels | Yes (sbix/COLR) | Bitmap | 4K ops/sec | macOS only |
| json | N/A | N/A | JSON data | 25K ops/sec | All |
The Vello renderers use compute-centric GPU rendering for maximum performance:
| Backend | Hardware | Performance | Use Case |
|---|---|---|---|
| vello | GPU (Metal/Vulkan/DX12) | 10K+ ops/sec | High-throughput, large text |
| vello-cpu | CPU only | 3.5K ops/sec | Server, no GPU |
# GPU rendering (requires GPU)
typf render "Hello" --renderer vello -o out.png
# CPU rendering (pure Rust, no GPU)
typf render "Hello" --renderer vello-cpu -o out.pngBoth use the Vello engine with skrifa for font parsing. In this repo, the GPU renderer currently does not render bitmap or COLR glyph types yet (use vello-cpu for color fonts). Build with --features render-vello or --features render-vello-cpu.
For maximum performance, linra renderers combine shaping and rendering in a single OS call:
| Backend | Speed vs Separate | Platform | Use Case |
|---|---|---|---|
| coretext-linra | 2.52x faster | macOS only | High-throughput rendering |
The linra renderer bypasses the intermediate glyph extraction step, allowing CoreText to optimize the entire pipeline internally.
| Format | Size | Antialiasing | Use Case |
|---|---|---|---|
| PNG | Small | Yes | Web, print |
| SVG | Scalable | Vector | Icons, logos |
| JSON | Smallest | N/A | Analysis, debug |
| PGM/PPM | Large | Yes/No | Testing, legacy |
| Feature | none | harfbuzz | icu-hb | coretext |
|---|---|---|---|---|
| OpenType Layout (GPOS/GSUB) | ❌ | ✅ | ✅ | ✅ |
| OpenType Variations (fvar) | ✅ | ✅ | ✅ | ✅ |
| Complex scripts (Arabic, Devanagari) | ❌ | ✅ | ✅ | ✅ |
| Emoji (segmentation) | ❌ | ✅ | ✅ |
| Format | opixa | skia | zeno | vello-cpu | vello | coregraphics | svg |
|---|---|---|---|---|---|---|---|
| TrueType outlines (glyf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CFF outlines (CFF ) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| CFF2 outlines (CFF2) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Variable fonts (gvar) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| COLR v0 (layered colors) | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | |
| COLR v1 (gradients) | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | |
| SVG glyphs (SVG table) | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | |
| Bitmap glyphs (CBDT/sbix) | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
Legend: ✅ Full support |
Note: Color glyph support in skia/zeno requires the
resvgfeature for SVG glyphs andbitmapfeature for CBDT/sbix.vello-cpusupports COLR/bitmap color fonts;vello(GPU) currently renders outlines only.
Typf includes scan-resistant caches (Moka TinyLFU) for shaping and rendering results. Caching is disabled by default to ensure predictable behavior and memory usage. See the Caching chapter for the full strategy.
Rust:
use typf::cache_config;
// Enable caching for repeated operations
cache_config::set_caching_enabled(true);
// Check status
if cache_config::is_caching_enabled() {
println!("Caching is ON");
}
// Disable when done
cache_config::set_caching_enabled(false);Python:
import typf
typf.set_caching_enabled(True) # Enable
typf.is_caching_enabled() # Check: returns True/False
typf.set_caching_enabled(False) # DisableEnvironment variable:
TYPF_CACHE=1 ./your_app # Enable at startup| Scenario | Caching | Reason |
|---|---|---|
| One-shot CLI renders | Off (default) | No repeated work |
| Interactive UI | On | Same text re-rendered often |
| Batch processing different texts | Off | Each text unique |
| Batch processing same text/fonts | On | Cache hits save time |
| Memory-constrained environment | Off | Caches use memory |
Typf now uses the Moka TinyLFU caching system for improved performance:
- Shaping cache: Keys on text + font + size + language + features + variations
- Glyph cache: Keys on shaped result + render params + font
- TinyLFU admission: Tracks frequency of both hits AND misses for smarter caching
- Time-to-idle: 10-minute automatic cleanup prevents unbounded memory growth
- Scan-resistant: Optimized for workloads with many unique text inputs (font matching)
- Scoped test control:
cache_config::scoped_caching_enabled()prevents test interference
# Minimal (500KB)
cargo build --release --no-default-features --features minimal
# Everything
cargo build --release --all-features
# SVG export (23× faster than PNG)
cargo build --release --features export-svg
./target/release/typf render "Scalable" -o out.svg -s 48The linra CLI supports both Rust (typf) and Python (typfpy) with identical syntax.
Show available backends:
typf info
typf info --shapers --renderers --formatsBasic rendering:
# Simple text
typf render "Hello World" -o output.png
# With custom font and size
typf render "Typography" -f /path/to/font.ttf -s 128 -o big.png
# SVG output
typf render "Vector" -f font.ttf -O svg -o vector.svgAdvanced options:
# Arabic text with proper shaping
typf render "مرحبا بالعالم" \
-f arabic.ttf \
--shaper hb \
--language ar \
--script Arab \
--direction rtl \
-o arabic.png
# Custom colors (RRGGBBAA hex)
typf render "Colored Text" \
-c FF0000FF \
-b FFFFFFFF \
-o colored.png
# Font features
typf render "Ligatures" \
-f font.ttf \
-F "liga,kern,dlig" \
-o features.png
# Unicode escapes
typf render "Wave \u{1F44B}" -o emoji.png
# Glyph source control (color fonts)
# Prefer COLR over SVG glyphs
typf render "Emoji" -f color.ttf \
--glyph-source prefer=colr1,colr0,svg \
-o emoji.png
# Disable color glyphs (force outline rendering)
typf render "Text" -f color.ttf \
--glyph-source deny=colr0,colr1,svg,sbix,cbdt,ebdt \
-o mono.pngBatch processing:
# Create jobs file
cat > jobs.jsonl << 'EOF'
{"text": "Title", "size": 72, "output": "title.png"}
{"text": "Subtitle", "size": 48, "output": "subtitle.png"}
{"text": "Body", "size": 16, "output": "body.png"}
EOF
# Process all jobs
typf batch -i jobs.jsonl -o ./rendered/Python CLI (identical syntax):
typfpy info
typfpy render "Hello" -f font.ttf -o output.png -s 72See CLI_MIGRATION.md for complete documentation.
Rust:
use typf::{Shaper, Renderer, Exporter};
let text = "Hello, مرحبا";
let shaped = shaper.shape(text, font, ¶ms)?;
let rendered = renderer.render(&shaped, font, &render_params)?;
let exported = exporter.export(&rendered)?;Python:
import typf
result = typf.render_text("Hello, مرحبا", font_path="arial.ttf")
result.save("output.png")cd typf-tester
python typfme.py benchTests all backend combos on your hardware. Results go to output/.
For comprehensive performance testing with JSON output:
# Quick sanity check (level 0)
cargo run -p typf-bench --release -- -i test-fonts -l 0
# Full benchmark (level 1-5, higher = more extensive)
cargo run -p typf-bench --release -- -i /path/to/fonts -l 2
# JSON output for CI comparison
cargo run -p typf-bench --release -- -i test-fonts -l 1 --json -o benchmark.jsonThe benchmark tool tests all shaper × renderer combinations across fonts, sizes, and text samples.
v5.0.20 - Production ready with enhanced caching, baseline consistency, and visual regression testing.
- ✅ 6-stage pipeline with optimized Moka TinyLFU caching architecture
- ✅ 5 shapers (added HarfBuzz Rust), 7 renderers (35 combinations) with consistent baselines
- ✅ PNM, PNG, SVG, JSON export with bitmap glyph embedding
- ✅ Linra CLI (Rust + Python) with color palette support and zero-copy optimization
- ✅ Python bindings (PyO3) with zero-copy font access and enhanced FFI
- ✅ Linux, macOS, Windows, WASM with platform-specific optimizations
- ✅ 490 tests passing across workspace including 21 SSIM visual regression tests
- ✅ macOS native backends (CoreText + CoreGraphics) with linra single-pass rendering
- ✅ Comprehensive backend documentation and architectural examples
- ✅ COLR v0/v1 color glyph support (skia/zeno/vello-cpu) with improved CPAL palette handling
- ✅ SVG table glyph support via resvg (skia/zeno) with proper gzip decompression
- ✅ Bitmap glyph support (sbix/CBDT/EBDT) with indexed PNG decoding and size selection
- ✅ Configurable glyph source selection (
--glyph-source) with CLI integration - ✅ GPU-accelerated rendering via Vello (Metal/Vulkan/DX12; outline-only with warnings for color fonts)
- ✅ High-quality CPU rendering via Vello CPU with zero-copy font bytes optimization
- ✅ Enhanced font metrics API with standardized baseline computation across all renderers
- ✅ Comprehensive caching system with scoped test control and Moka TinyLFU admission policies
Bitmap width: ~10,000 pixels max
- 48px font: ~200 characters
- 24px font: ~400 characters
- 12px font: ~800 characters
Fix: Use smaller fonts, line wrapping, or SVG (no width limit).
Build errors:
- "undeclared type" →
cargo build --all-features - "Package not found" →
cargo build --no-default-features --features minimal
Runtime errors:
- "no attribute 'Typf'" → Rebuild Python bindings
- "Invalid bitmap dimensions" → Text too wide, use smaller font or SVG
- "Font not found" → Check path and format (TrueType/OpenType)
- Quickstart - Use typf in your Rust project
- Architecture - Pipeline, backends, and data flow
- Documentation - 24 chapters
- Examples - Working code samples
- Contributing - Development setup
- TASKS.md - Roadmap