Status: v0.14.0 (Released as part of gg v0.31.0)
This package implements a modern GPU-ready text pipeline for gogpu/gg, inspired by Ebitengine text/v2, go-text/typesetting, and vello.
FontSource (heavyweight, shared)
↓
Face (lightweight, per-size)
↓
Segmenter → Shaper → Layout → GlyphRenderer
│ │ │ │
Bidi/Script Cache Lines ┌────┴────┐
│ │
Vector Path MSDF
(quality) (perf)
│ │
└────┬────┘
↓
GPU Rendering
- Shaper interface — Converts text to positioned glyphs
- BuiltinShaper — Default using golang.org/x/image
- GoTextShaper — HarfBuzz-level shaping via go-text/typesetting (opt-in)
- Custom shapers — Plug in any implementation via SetShaper()
- 25+ Unicode scripts — Latin, Arabic, Hebrew, Han, Cyrillic, Thai, etc.
- Full Unicode Bidi Algorithm — Via golang.org/x/text/unicode/bidi
- Script inheritance — Common/Inherited characters resolved from context
- Alignment — Left, Center, Right, Justify (placeholder)
- Line wrapping — At MaxWidth with word boundaries
- Line spacing — Configurable multiplier
- Bidi-aware — Proper RTL/LTR segment ordering
- WrapMode enum — WrapWordChar (default), WrapNone, WrapWord, WrapChar
- UAX #14 simplified — Break classification (Space, Zero, Open, Close, Hyphen, Ideographic)
- CJK support — Break opportunities at ideograph boundaries
- Performance — 1,185 ns/op FindBreakOpportunities, 0 allocs
- 16-shard LRU — Concurrent access without lock contention
- 4K total entries — 256 per shard
- Zero-allocation hot path — Pre-allocated result storage
// Load font (heavyweight, do once)
source, err := text.NewFontSourceFromFile("Roboto-Regular.ttf")
if err != nil {
log.Fatal(err)
}
defer source.Close()
// Create face at specific size (lightweight)
face := source.Face(24)
// Use with gg.Context (dc = drawing context)
dc := gg.NewContext(800, 600)
dc.SetFont(face)
dc.DrawString("Hello, GoGPU!", 100, 100)// Shape text to positioned glyphs
glyphs := text.Shape("Hello", face)
for _, g := range glyphs {
fmt.Printf("GID=%d X=%.1f Y=%.1f\n", g.GID, g.X, g.Y)
}// Segment mixed-direction text
segments := text.SegmentText("Hello שלום مرحبا")
for _, seg := range segments {
fmt.Printf("'%s' Dir=%s Script=%s\n",
seg.Text, seg.Direction, seg.Script)
}
// RTL base direction
segments = text.SegmentTextRTL("مرحبا Hello")// Layout with options
opts := text.LayoutOptions{
MaxWidth: 400,
LineSpacing: 1.2,
Alignment: text.AlignCenter,
Direction: text.DirectionLTR,
WrapMode: text.WrapWordChar, // Word-first, char fallback (default)
}
layout := text.LayoutText(longText, face, opts)
// Access lines
for _, line := range layout.Lines {
fmt.Printf("Y=%.1f Width=%.1f Glyphs=%d\n",
line.Y, line.Width, len(line.Glyphs))
}
// Simple layout (no wrapping)
layout = text.LayoutTextSimple("Hello\nWorld", face)// WrapWordChar (default) — Word boundaries first, character fallback for long words
opts := text.LayoutOptions{
MaxWidth: 200,
WrapMode: text.WrapWordChar,
}
// WrapWord — Word boundaries only, long words overflow
opts.WrapMode = text.WrapWord
// WrapChar — Character boundaries, any character can break
opts.WrapMode = text.WrapChar
// WrapNone — No wrapping, text may exceed MaxWidth
opts.WrapMode = text.WrapNone
// Standalone wrapping API
results := text.WrapText("Hello World", face, 100, text.WrapWordChar)
for _, r := range results {
fmt.Printf("Line: '%s' (%d-%d)\n", r.Text, r.Start, r.End)
}
// Measure text width
width := text.MeasureText("Hello World", face)// Enable HarfBuzz-level shaping for ligatures, kerning, and complex scripts
shaper := text.NewGoTextShaper()
text.SetShaper(shaper)
defer text.SetShaper(nil) // Reset to BuiltinShaper
// Shape text — now uses go-text/typesetting HarfBuzz engine
glyphs := text.Shape("Hello", face)GoTextShaper is safe for concurrent use and caches parsed font data internally.
// Implement custom shaper
type MyShaper struct {
// ...
}
func (s *MyShaper) Shape(text string, face text.Face) []text.ShapedGlyph {
// Custom shaping logic
}
// Set as global shaper
text.SetShaper(&MyShaper{})
defer text.SetShaper(nil) // Reset to defaulttype ShapedGlyph struct {
GID GlyphID // Glyph index in font
Cluster int // Source character index
X, Y float64 // Position relative to origin
XAdvance float64 // Horizontal advance
YAdvance float64 // Vertical advance (for TTB)
}type Segment struct {
Text string // Segment text
Start int // Byte offset in original text
End int // End byte offset
Direction Direction // LTR or RTL
Script Script // Unicode script
Level int // Bidi embedding level
}type Layout struct {
Lines []Line // Positioned lines
Width float64 // Maximum line width
Height float64 // Total height
}
type Line struct {
Runs []ShapedRun // Runs with uniform style
Glyphs []ShapedGlyph // All positioned glyphs
Width float64 // Line width
Ascent float64 // Max ascent
Descent float64 // Max descent
Y float64 // Baseline Y position
}golang.org/x/image/font/opentype— TTF/OTF parsinggolang.org/x/text/unicode/bidi— Unicode Bidirectional Algorithmgithub.com/go-text/typesetting— HarfBuzz shaping engine (used by GoTextShaper)
LRU caching infrastructure for shaping results.
Multi-channel Signed Distance Field generation for GPU text rendering.
- Generator — Pure Go MSDF with edge coloring algorithm
- AtlasManager — Multi-atlas management with shelf packing
- ConcurrentAtlasManager — High-throughput sharded variant
Emoji and color font support.
- Detection — IsEmoji, IsZWJ, IsRegionalIndicator
- Sequences — ZWJ, flags, skin tones, keycaps
- COLRv0/v1 — Color glyph parsing and rendering
- sbix/CBDT — Bitmap emoji (PNG, JPEG, TIFF)
- text package: 87.6%
- text/cache package: 93.7%
- text/msdf package: 87.0%
- text/emoji package: 85.0%
- 0 linter issues
- BREAKING: Removed
sizeparameter —Shape(),LayoutText(),WrapText(),MeasureText()now derive size fromface.Size() - Shaper interface simplified —
Shape(text, face)instead ofShape(text, face, size) - LayoutText Y positions fixed — Lines now have correct cumulative Y positions
- Hard line breaks in WrapText —
\n,\r\n,\rrespected in wrapping
- WrapMode enum — WrapWordChar, WrapNone, WrapWord, WrapChar
- BreakClass — UAX #14 simplified line breaking
- WrapText() — Standalone wrapping API
- MeasureText() — Measure text advance width
- CJK support — Ideograph break opportunities
- context.Context — LayoutTextWithContext() cancellation
- Glyph-as-Path rendering (OutlineExtractor)
- GlyphCache LRU (16-shard, <50ns hit)
- MSDF generator (Pure Go)
- MSDF atlas with shelf packing
- Emoji support (COLRv1, sbix, ZWJ)
- Subpixel positioning (4/10 levels)
- Pluggable Shaper interface
- Extended shaping types
- Sharded LRU shaping cache
- Bidi/Script segmentation
- Multi-line Layout Engine