text

package
v0.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 19, 2025 License: MIT Imports: 12 Imported by: 2

README

text — Text Rendering for gg

Status: In Development (v0.2.0+)

This package implements modern text rendering for gogpu/gg, inspired by Ebitengine text/v2.

Architecture

FontSource (heavyweight, shared)
    ↓
Face (lightweight, per-size)
    ↓
Shaper → Layout → Rasterizer → Draw

Current Implementation (TASK-042)

Completed:

  • FontSource — TTF/OTF loading and management
  • Copy protection (Ebitengine pattern)
  • Options pattern (SourceOption, FaceOption)
  • Basic types (Direction, Hinting, Rect)
  • Face interface (stub)
  • Comprehensive tests

🚧 In Progress:

  • Face implementation (TASK-043)
  • Multi-level caching (TASK-044)
  • Text layout (TASK-045)

Usage

// 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 (when implemented)
ctx := gg.NewContext(800, 600)
ctx.SetFont(face)
ctx.DrawString("Hello, GoGPU!", 100, 100)

Design

See: docs/dev/research/gg-text-api-v2-design.md for full design document.

Dependencies

  • golang.org/x/image/font/opentype — TTF/OTF parsing
  • golang.org/x/text — (transitive) Character encoding

Future goal: Pure Go implementation with zero dependencies.

Documentation

Overview

Package text provides text rendering for gg. It implements a modern text API inspired by Ebitengine text/v2.

The text rendering pipeline follows a separation of concerns:

  • FontSource: Heavyweight, shared font resource (parses TTF/OTF files)
  • Face: Lightweight font instance at a specific size
  • FontParser: Pluggable font parsing backend (default: golang.org/x/image)

Example usage

// Load font (do once, share across application)
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
ctx := gg.NewContext(800, 600)
ctx.SetFont(face)
ctx.DrawString("Hello, GoGPU!", 100, 100)

Pluggable Parser Backend

The font parsing is abstracted through the FontParser interface. By default, golang.org/x/image/font/opentype is used. Custom parsers can be registered for alternative implementations:

// Register a custom parser
text.RegisterParser("myparser", myCustomParser)

// Use the custom parser
source, err := text.NewFontSource(data, text.WithParser("myparser"))

This design allows:

  • Easy migration to different font libraries
  • Pure Go implementations without external dependencies
  • Custom font formats or optimized parsers

Index

Constants

This section is empty.

Variables

View Source
var (
	// Latin Scripts
	RangeBasicLatin = UnicodeRange{0x0000, 0x007F} // ASCII
	RangeLatin1Sup  = UnicodeRange{0x0080, 0x00FF} // Latin-1 Supplement
	RangeLatinExtA  = UnicodeRange{0x0100, 0x017F} // Latin Extended-A
	RangeLatinExtB  = UnicodeRange{0x0180, 0x024F} // Latin Extended-B

	// Cyrillic Scripts
	RangeCyrillic = UnicodeRange{0x0400, 0x04FF} // Cyrillic

	// Greek Scripts
	RangeGreek = UnicodeRange{0x0370, 0x03FF} // Greek and Coptic

	// Middle Eastern Scripts
	RangeArabic = UnicodeRange{0x0600, 0x06FF} // Arabic
	RangeHebrew = UnicodeRange{0x0590, 0x05FF} // Hebrew

	// CJK Scripts
	RangeCJKUnified = UnicodeRange{0x4E00, 0x9FFF} // CJK Unified Ideographs
	RangeHiragana   = UnicodeRange{0x3040, 0x309F} // Hiragana
	RangeKatakana   = UnicodeRange{0x30A0, 0x30FF} // Katakana
	RangeHangul     = UnicodeRange{0xAC00, 0xD7AF} // Hangul Syllables

	// Emoji
	RangeEmoji        = UnicodeRange{0x1F600, 0x1F64F} // Emoticons
	RangeEmojiMisc    = UnicodeRange{0x1F300, 0x1F5FF} // Miscellaneous Symbols and Pictographs
	RangeEmojiSymbols = UnicodeRange{0x1F680, 0x1F6FF} // Transport and Map Symbols
	RangeEmojiFlags   = UnicodeRange{0x1F1E0, 0x1F1FF} // Regional Indicator Symbols (Flags)
)

Common Unicode ranges for filtering faces.

Functions

func Draw

func Draw(dst draw.Image, text string, face Face, x, y float64, col color.Color)

Draw renders text to a destination image. Position (x, y) is the baseline origin. The face must be a *sourceFace from this package.

func Measure

func Measure(text string, face Face) (width, height float64)

Measure returns the dimensions of text. Width is the horizontal advance, height is the font's line height.

func RegisterParser

func RegisterParser(name string, parser FontParser)

RegisterParser registers a custom font parser. This allows users to provide their own parsing implementation.

Types

type Cache

type Cache[K comparable, V any] struct {
	// contains filtered or unexported fields
}

Cache is a generic thread-safe LRU cache with soft limit. When the cache exceeds softLimit, oldest entries are evicted.

Cache is safe for concurrent use. Cache must not be copied after creation (has mutex).

func NewCache

func NewCache[K comparable, V any](softLimit int) *Cache[K, V]

NewCache creates a new cache with the given soft limit. A softLimit of 0 means unlimited.

func (*Cache[K, V]) Clear

func (c *Cache[K, V]) Clear()

Clear removes all entries from the cache.

func (*Cache[K, V]) Get

func (c *Cache[K, V]) Get(key K) (V, bool)

Get retrieves a value from the cache. Returns (value, true) if found, (zero, false) otherwise.

func (*Cache[K, V]) GetOrCreate

func (c *Cache[K, V]) GetOrCreate(key K, create func() V) V

GetOrCreate returns cached value or creates it. Thread-safe: create is called under lock to prevent duplicate creation.

func (*Cache[K, V]) Len

func (c *Cache[K, V]) Len() int

Len returns the number of entries in the cache.

func (*Cache[K, V]) Set

func (c *Cache[K, V]) Set(key K, value V)

Set stores a value in the cache. If the cache exceeds softLimit after insertion, oldest entries are evicted.

type Direction

type Direction int

Direction specifies text direction.

const (
	// DirectionLTR is left-to-right text (English, French, etc.)
	DirectionLTR Direction = iota
	// DirectionRTL is right-to-left text (Arabic, Hebrew)
	DirectionRTL
	// DirectionTTB is top-to-bottom text (traditional Chinese, Japanese)
	DirectionTTB
	// DirectionBTT is bottom-to-top text (rare)
	DirectionBTT
)

func (Direction) String

func (d Direction) String() string

String returns the string representation of the direction.

type DrawOptions

type DrawOptions struct {
	// Color for the text (default: black)
	Color color.Color
}

DrawOptions provides advanced options for text drawing. Reserved for future enhancements.

type Face

type Face interface {
	// Metrics returns the font metrics at this face's size.
	Metrics() Metrics

	// Advance returns the total advance width of the text in pixels.
	// This is the sum of all glyph advances.
	Advance(text string) float64

	// HasGlyph reports whether the font has a glyph for the given rune.
	HasGlyph(r rune) bool

	// Glyphs returns an iterator over all glyphs in the text.
	// The glyphs are positioned relative to the origin (0, 0).
	// Uses Go 1.25+ iter.Seq for zero-allocation iteration.
	Glyphs(text string) iter.Seq[Glyph]

	// AppendGlyphs appends glyphs for the text to dst and returns the extended slice.
	// This is useful for building glyph slices without allocation.
	AppendGlyphs(dst []Glyph, text string) []Glyph

	// Direction returns the text direction for this face.
	Direction() Direction

	// Source returns the FontSource this face was created from.
	Source() *FontSource

	// Size returns the size of this face in points.
	Size() float64
	// contains filtered or unexported methods
}

Face represents a font face at a specific size. This is a lightweight object that can be created from a FontSource. Face is safe for concurrent use.

type FaceOption

type FaceOption func(*faceConfig)

FaceOption configures Face creation.

func WithDirection

func WithDirection(d Direction) FaceOption

WithDirection sets the text direction for the face.

func WithHinting

func WithHinting(h Hinting) FaceOption

WithHinting sets the hinting mode for the face.

func WithLanguage

func WithLanguage(lang string) FaceOption

WithLanguage sets the language tag for the face (e.g., "en", "ja", "ar").

type FilteredFace

type FilteredFace struct {
	// contains filtered or unexported fields
}

FilteredFace wraps a face and restricts it to specific Unicode ranges. Only glyphs in the specified ranges are considered available. FilteredFace is safe for concurrent use.

func NewFilteredFace

func NewFilteredFace(face Face, ranges ...UnicodeRange) *FilteredFace

NewFilteredFace creates a FilteredFace. Only glyphs in the specified ranges are considered available. If no ranges are specified, all glyphs are available (no filtering).

func (*FilteredFace) Advance

func (f *FilteredFace) Advance(text string) float64

Advance implements Face.Advance. Only includes runes that are in the allowed ranges.

func (*FilteredFace) AppendGlyphs

func (f *FilteredFace) AppendGlyphs(dst []Glyph, text string) []Glyph

AppendGlyphs implements Face.AppendGlyphs. Only appends glyphs for runes in the allowed ranges.

func (*FilteredFace) Direction

func (f *FilteredFace) Direction() Direction

Direction implements Face.Direction.

func (*FilteredFace) Glyphs

func (f *FilteredFace) Glyphs(text string) iter.Seq[Glyph]

Glyphs implements Face.Glyphs. Only yields glyphs for runes in the allowed ranges.

func (*FilteredFace) HasGlyph

func (f *FilteredFace) HasGlyph(r rune) bool

HasGlyph implements Face.HasGlyph. Returns true only if the rune is in the allowed ranges and the wrapped face has it.

func (*FilteredFace) Metrics

func (f *FilteredFace) Metrics() Metrics

Metrics implements Face.Metrics.

func (*FilteredFace) Size

func (f *FilteredFace) Size() float64

Size implements Face.Size.

func (*FilteredFace) Source

func (f *FilteredFace) Source() *FontSource

Source implements Face.Source.

type FontMetrics

type FontMetrics struct {
	// Ascent is the distance from the baseline to the top of the font (positive).
	Ascent float64

	// Descent is the distance from the baseline to the bottom of the font (negative).
	Descent float64

	// LineGap is the recommended line gap between lines.
	LineGap float64

	// XHeight is the height of lowercase letters (like 'x').
	XHeight float64

	// CapHeight is the height of uppercase letters.
	CapHeight float64
}

FontMetrics holds font-level metrics at a specific size.

func (FontMetrics) Height

func (m FontMetrics) Height() float64

Height returns the total line height (ascent - descent + line gap).

type FontParser

type FontParser interface {
	// Parse parses font data (TTF or OTF) and returns a ParsedFont.
	Parse(data []byte) (ParsedFont, error)
}

FontParser is an interface for font parsing backends. This abstraction allows swapping the font parsing library (e.g., golang.org/x/image/font/opentype vs a pure Go implementation).

The default implementation uses golang.org/x/image/font/opentype.

type FontSource

type FontSource struct {
	// contains filtered or unexported fields
}

FontSource represents a loaded font file. One FontSource can create multiple Face instances at different sizes. FontSource is heavyweight and should be shared across the application.

FontSource is safe for concurrent use. FontSource must not be copied after creation (enforced by copyCheck).

func NewFontSource

func NewFontSource(data []byte, opts ...SourceOption) (*FontSource, error)

NewFontSource creates a FontSource from font data (TTF or OTF). The data slice is copied internally and can be reused after this call.

Options can be used to configure caching and parser backend.

func NewFontSourceFromFile

func NewFontSourceFromFile(path string, opts ...SourceOption) (*FontSource, error)

NewFontSourceFromFile loads a FontSource from a font file path.

func (*FontSource) Close

func (s *FontSource) Close() error

Close releases resources associated with the FontSource. All faces created from this source become invalid after Close.

func (*FontSource) Face

func (s *FontSource) Face(size float64, opts ...FaceOption) Face

Face creates a Face at the specified size (in points). Multiple faces can be created from the same FontSource.

Face is a lightweight object that shares caches with the FontSource.

func (*FontSource) Name

func (s *FontSource) Name() string

Name returns the font name.

func (*FontSource) Parsed

func (s *FontSource) Parsed() ParsedFont

Parsed returns the parsed font for advanced operations. This is primarily used by Face implementations.

type Glyph

type Glyph struct {
	// Rune is the Unicode character this glyph represents.
	// For ligatures, this may be the first character of the ligature.
	Rune rune

	// GID is the glyph index in the font.
	GID GlyphID

	// X, Y are the position of the glyph relative to the text origin.
	// The origin is at the baseline of the first character.
	X, Y float64

	// OriginX, OriginY are the absolute position of the glyph's origin point.
	// This is where the glyph should be drawn from.
	OriginX float64
	OriginY float64

	// Advance is the horizontal advance width of the glyph.
	// This is how much the cursor moves after drawing this glyph.
	Advance float64

	// Bounds is the bounding box of the glyph.
	// This defines the area the glyph occupies.
	Bounds Rect

	// Index is the byte position in the original string where this glyph starts.
	Index int

	// Cluster is the character cluster index.
	// Multiple glyphs can belong to the same cluster (e.g., ligatures).
	Cluster int
}

Glyph represents a single shaped glyph with its position and metrics. This is the output of text shaping and is ready for rendering.

type GlyphID

type GlyphID uint16

GlyphID is a unique identifier for a glyph within a font. The glyph ID is assigned by the font file and is font-specific.

type GlyphImage

type GlyphImage struct {
	// Mask is the alpha mask (grayscale image).
	// This represents the glyph's shape.
	Mask *image.Alpha

	// Bounds relative to glyph origin.
	// The origin is typically on the baseline at the left edge.
	Bounds image.Rectangle

	// Advance width in pixels.
	// This is how far the cursor should move after drawing this glyph.
	Advance float64
}

GlyphImage represents a rasterized glyph. This contains the alpha mask and positioning information.

func RasterizeGlyph

func RasterizeGlyph(parsed ParsedFont, glyphID GlyphID, ppem float64) *GlyphImage

RasterizeGlyph renders a glyph to an alpha mask. Uses golang.org/x/image/font for rasterization.

This function is primarily intended for future caching implementations and advanced use cases. For normal text drawing, use the Draw function instead.

Parameters:

  • parsed: The parsed font (must be *ximageParsedFont)
  • glyphID: The glyph index to rasterize
  • ppem: Pixels per em (font size)

Returns:

  • *GlyphImage with the rasterized glyph, or nil if rasterization fails

type GlyphKey

type GlyphKey struct {
	GID  GlyphID
	Size float64
}

GlyphKey identifies a rasterized glyph in the glyph cache.

type Hinting

type Hinting int

Hinting specifies font hinting mode.

const (
	// HintingNone disables hinting.
	HintingNone Hinting = iota
	// HintingVertical applies vertical hinting only.
	HintingVertical
	// HintingFull applies full hinting.
	HintingFull
)

func (Hinting) String

func (h Hinting) String() string

String returns the string representation of the hinting.

type Metrics

type Metrics struct {
	// Ascent is the distance from the baseline to the top of the font (positive).
	// This is the maximum height a glyph can reach above the baseline.
	Ascent float64

	// Descent is the distance from the baseline to the bottom of the font (positive, below baseline).
	// This is the maximum depth a glyph can reach below the baseline.
	// Note: Unlike FontMetrics.Descent, this is stored as a positive value.
	Descent float64

	// LineGap is the recommended gap between lines.
	LineGap float64

	// XHeight is the height of lowercase letters (like 'x').
	XHeight float64

	// CapHeight is the height of uppercase letters.
	CapHeight float64
}

Metrics holds font metrics at a specific size. These metrics are derived from the font file and scaled to the face size.

func (Metrics) LineHeight

func (m Metrics) LineHeight() float64

LineHeight returns the total line height (ascent + descent + line gap). This is the recommended vertical distance between baselines of consecutive lines.

type MultiFace

type MultiFace struct {
	// contains filtered or unexported fields
}

MultiFace combines multiple faces with fallback. When rendering, it uses the first face that has the glyph. MultiFace is safe for concurrent use.

func NewMultiFace

func NewMultiFace(faces ...Face) (*MultiFace, error)

NewMultiFace creates a MultiFace from faces. All faces must have the same direction. Returns error if faces is empty or directions don't match.

func (*MultiFace) Advance

func (m *MultiFace) Advance(text string) float64

Advance implements Face.Advance. Calculates total advance using the appropriate face for each rune.

func (*MultiFace) AppendGlyphs

func (m *MultiFace) AppendGlyphs(dst []Glyph, text string) []Glyph

AppendGlyphs implements Face.AppendGlyphs. Appends glyphs using the appropriate face for each rune.

func (*MultiFace) Direction

func (m *MultiFace) Direction() Direction

Direction implements Face.Direction.

func (*MultiFace) Glyphs

func (m *MultiFace) Glyphs(text string) iter.Seq[Glyph]

Glyphs implements Face.Glyphs. Returns an iterator over all glyphs, using the appropriate face for each rune.

func (*MultiFace) HasGlyph

func (m *MultiFace) HasGlyph(r rune) bool

HasGlyph implements Face.HasGlyph. Returns true if any face has the glyph.

func (*MultiFace) Metrics

func (m *MultiFace) Metrics() Metrics

Metrics implements Face.Metrics. Returns metrics from the first face.

func (*MultiFace) Size

func (m *MultiFace) Size() float64

Size implements Face.Size. Returns the size from the first face.

func (*MultiFace) Source

func (m *MultiFace) Source() *FontSource

Source implements Face.Source. Returns nil since MultiFace is a composite face.

type ParsedFont

type ParsedFont interface {
	// Name returns the font family name.
	// Returns empty string if not available.
	Name() string

	// FullName returns the full font name.
	// Returns empty string if not available.
	FullName() string

	// NumGlyphs returns the number of glyphs in the font.
	NumGlyphs() int

	// UnitsPerEm returns the units per em for the font.
	UnitsPerEm() int

	// GlyphIndex returns the glyph index for a rune.
	// Returns 0 if the glyph is not found.
	GlyphIndex(r rune) uint16

	// GlyphAdvance returns the advance width for a glyph at the given size (in points).
	// The ppem (pixels per em) is derived from size and DPI.
	GlyphAdvance(glyphIndex uint16, ppem float64) float64

	// GlyphBounds returns the bounding box for a glyph at the given size.
	GlyphBounds(glyphIndex uint16, ppem float64) Rect

	// Metrics returns the font metrics at the given size.
	Metrics(ppem float64) FontMetrics
}

ParsedFont represents a parsed font file. This interface abstracts the underlying font representation.

type Rect

type Rect struct {
	// Min is the top-left corner
	MinX, MinY float64
	// Max is the bottom-right corner
	MaxX, MaxY float64
}

Rect represents a rectangle for glyph bounds.

func (Rect) Empty

func (r Rect) Empty() bool

Empty reports whether the rectangle is empty.

func (Rect) Height

func (r Rect) Height() float64

Height returns the height of the rectangle.

func (Rect) Width

func (r Rect) Width() float64

Width returns the width of the rectangle.

type RuneToBoolMap

type RuneToBoolMap struct {
	// contains filtered or unexported fields
}

RuneToBoolMap is a memory-efficient map from rune to bool. Uses 2 bits per rune: (checked, hasGlyph). Optimized for sparse access patterns in Unicode space.

Each block covers 256 runes (512 bits = 64 bytes). Blocks are allocated on-demand only when a rune in that range is accessed.

RuneToBoolMap is safe for concurrent use. RuneToBoolMap must not be copied after creation (has mutex).

func NewRuneToBoolMap

func NewRuneToBoolMap() *RuneToBoolMap

NewRuneToBoolMap creates a new rune-to-bool map.

func (*RuneToBoolMap) Clear

func (m *RuneToBoolMap) Clear()

Clear removes all entries from the map.

func (*RuneToBoolMap) Get

func (m *RuneToBoolMap) Get(r rune) (hasGlyph, checked bool)

Get returns (hasGlyph, checked). If checked is false, the rune hasn't been queried yet.

func (*RuneToBoolMap) Set

func (m *RuneToBoolMap) Set(r rune, hasGlyph bool)

Set stores the hasGlyph value for a rune. Marks the rune as checked.

type ShapingKey

type ShapingKey struct {
	Text      string
	Size      float64
	Direction Direction
}

ShapingKey identifies shaped text in the shaping cache.

type SourceOption

type SourceOption func(*sourceConfig)

SourceOption configures FontSource creation.

func WithCacheLimit

func WithCacheLimit(n int) SourceOption

WithCacheLimit sets the maximum number of cached glyphs. A value of 0 disables the cache limit.

func WithParser

func WithParser(name string) SourceOption

WithParser specifies the font parser backend. The default is "ximage" which uses golang.org/x/image/font/opentype.

Custom parsers can be registered with RegisterParser. This allows using alternative font parsing libraries or a pure Go implementation in the future.

type UnicodeRange

type UnicodeRange struct {
	Start rune
	End   rune
}

UnicodeRange represents a contiguous range of code points.

func (UnicodeRange) Contains

func (ur UnicodeRange) Contains(r rune) bool

Contains reports whether the rune is in the range.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL