Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

156 Commits

Repository files navigation

Typf v5.0.20

CIRust

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.

Quick start

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.png

That's it. Your text is rendered.

Who needs this

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.

How text rendering actually works

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.

What it does

  • 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

Choose your setup

NeedCommandSpeedQuality
Fastest datanone + JSON25K ops/secGlyph data only
Complex scriptsharfbuzz + zeno3K ops/sec247 grayscales
macOS bestcoretext + coregraphics4K ops/sec254 levels
Pure Rustharfbuzz + opixa2K ops/sec25 levels (mono)

Backend comparison

Shapers

BackendScriptsFeaturesPerformancePlatform
noneLatin onlySimple LTR25K ops/secAll
harfbuzzAll (200+)Full OpenType4K ops/secAll
icu-hbAll + normalizationUnicode + OpenType3.5K ops/secAll
coretextAllNative macOS4.5K ops/secmacOS only

Renderers

BackendAnti-aliasColorOutputPerformancePlatform
opixaMonochromeNoBitmap2K ops/secAll (pure Rust)
skia256 levelsYes (COLR/SVG/bitmap)Bitmap/SVG3.5K ops/secAll
zeno256 levelsYes (COLR/SVG/bitmap)Bitmap/SVG3K ops/secAll (pure Rust)
vello-cpu256 levelsYes (COLR/bitmap)Bitmap3.5K ops/secAll (pure Rust)
vello256 levels⚠️ Limited (outline-only today)Bitmap10K+ ops/secGPU required
coregraphics256 levelsYes (sbix/COLR)Bitmap4K ops/secmacOS only
jsonN/AN/AJSON data25K ops/secAll

GPU Renderers (Vello)

The Vello renderers use compute-centric GPU rendering for maximum performance:

BackendHardwarePerformanceUse Case
velloGPU (Metal/Vulkan/DX12)10K+ ops/secHigh-throughput, large text
vello-cpuCPU only3.5K ops/secServer, 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.png

Both 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.

Linra Renderers (Single-Pass)

For maximum performance, linra renderers combine shaping and rendering in a single OS call:

BackendSpeed vs SeparatePlatformUse Case
coretext-linra2.52x fastermacOS onlyHigh-throughput rendering

The linra renderer bypasses the intermediate glyph extraction step, allowing CoreText to optimize the entire pipeline internally.

Export Formats

FormatSizeAntialiasingUse Case
PNGSmallYesWeb, print
SVGScalableVectorIcons, logos
JSONSmallestN/AAnalysis, debug
PGM/PPMLargeYes/NoTesting, legacy

Font Feature Support Matrix

Featurenoneharfbuzzicu-hbcoretext
OpenType Layout (GPOS/GSUB)
OpenType Variations (fvar)
Complex scripts (Arabic, Devanagari)
Emoji (segmentation)⚠️

Glyph Format Support

Formatopixaskiazenovello-cpuvellocoregraphicssvg
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 | ⚠️ Partial/via OS | ❌ Not supported

Note: Color glyph support in skia/zeno requires the resvg feature for SVG glyphs and bitmap feature for CBDT/sbix. vello-cpu supports COLR/bitmap color fonts; vello (GPU) currently renders outlines only.

Caching

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.

Enable caching

Rust:

use typf::cache_config;// Enable caching for repeated operations
cache_config::set_caching_enabled(true);// Check statusif cache_config::is_caching_enabled(){println!("Caching is ON");}// Disable when done
cache_config::set_caching_enabled(false);

Python:

importtypftypf.set_caching_enabled(True) # Enabletypf.is_caching_enabled() # Check: returns True/Falsetypf.set_caching_enabled(False) # Disable

Environment variable:

TYPF_CACHE=1 ./your_app # Enable at startup

When to use caching

ScenarioCachingReason
One-shot CLI rendersOff (default)No repeated work
Interactive UIOnSame text re-rendered often
Batch processing different textsOffEach text unique
Batch processing same text/fontsOnCache hits save time
Memory-constrained environmentOffCaches use memory

Cache behavior (v5.0.1+)

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

Build options

# 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 48

CLI usage

The linra CLI supports both Rust (typf) and Python (typfpy) with identical syntax.

Show available backends:

typf info
typf info --shapers --renderers --formats

Basic 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.svg

Advanced 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.png

Batch 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 72

See CLI_MIGRATION.md for complete documentation.

Use in code

Rust:

use typf::{Shaper,Renderer,Exporter};let text = "Hello, مرحبا";let shaped = shaper.shape(text, font,&params)?;let rendered = renderer.render(&shaped, font,&render_params)?;let exported = exporter.export(&rendered)?;

Python:

importtypfresult=typf.render_text("Hello, مرحبا", font_path="arial.ttf")
result.save("output.png")

Test your system

cd typf-tester
python typfme.py bench

Tests all backend combos on your hardware. Results go to output/.

Benchmark CLI

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.json

The benchmark tool tests all shaper × renderer combinations across fonts, sizes, and text samples.

Status

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

Limits

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).

Troubleshooting

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)

Learn more

License

EVALUATION LICENSE

About

Font renderer in Rust

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages