Skip to content

go-gui-org/go-glyph

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

145 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go-Glyph

Go version License Ask DeepWiki

High-performance text rendering library for Go with pluggable rendering backends. Provides text shaping, layout, rasterization, and editing with platform-appropriate shapers and rasterizers per operating system.

OS Shaper Rasterizer
Linux / BSD Pango + HarfBuzz FreeType + FontConfig
macOS CoreText CoreText / CoreGraphics
Windows GDI + DirectWrite GDI + DirectWrite
Android FreeType FreeType
WASM Canvas2D Canvas2D

screenshot

Features

  • Text shaping via Pango - full Unicode, BiDi, complex scripts
  • Glyph rasterization via FreeType2 with subpixel positioning
  • Multi-page glyph atlas with automatic packing and eviction
  • Layout caching for efficient per-frame rendering
  • Rich text - mixed fonts, sizes, colors, and styles in one block
  • Pango markup support for inline styling
  • Text decorations - underline, strikethrough, stroke/outline
  • Gradient text - horizontal, vertical, and diagonal color gradients
  • Word wrapping with word, character, and word-char modes
  • Text alignment - left, center, right
  • Affine transforms - rotation, skew, scale, translation
  • Glyph placements - per-glyph positioning (text on a path)
  • Hit testing and cursor position queries
  • Text mutation - insert, delete, selection, undo/redo
  • IME support with composition/preedit rendering
  • Accessibility - screen reader announcements, text field nodes
  • Pluggable backends — Ebitengine, SDL2, GPU (Metal/OpenGL), Web (WASM), Android, iOS

Prerequisites

Library requirements depend on the target platform:

  • Linux / BSD: Pango (+ PangoFT2), FreeType2, FontConfig, GLib
  • macOS: No native libraries required (uses CoreText)
  • Windows: No native libraries required for the root package (uses GDI)
  • Android: FreeType2 (bundled with NDK)
  • WASM: No native libraries required (uses Canvas2D)

SDL2 is required for the SDL2 and GPU backends on all platforms.

Windows (MSYS2)

The root package and Ebitengine backend require no native libraries on Windows (CGO_ENABLED=0). The SDL2 and GPU backends need SDL2 via MSYS2:

pacman -S mingw-w64-x86_64-SDL2 mingw-w64-x86_64-pkg-config

Build from an MSYS2 MinGW 64-bit shell, or add the MinGW bin/ directory to PATH so pkg-config and the SDL2 DLL are found.

macOS (Homebrew)

The root package and Ebitengine backend use CoreText (no brew packages needed). The SDL2 and GPU backends additionally require:

brew install pango freetype fontconfig glib sdl2

Ubuntu / Debian

sudo apt install libpango1.0-dev libfreetype-dev \
    libfontconfig1-dev libglib2.0-dev libsdl2-dev

Fedora

sudo dnf install pango-devel freetype-devel \
    fontconfig-devel glib2-devel SDL2-devel

Installation

go get github.com/go-gui-org/go-glyph@latest

Quick Start

Minimal Ebitengine example:

package main

import (
    "log"

    "github.com/hajimehoshi/ebiten/v2"
    "github.com/go-gui-org/go-glyph"
    glyphebi "github.com/go-gui-org/go-glyph/backend/ebitengine"
)

type Game struct {
    ts      *glyph.TextSystem
    backend *glyphebi.Backend
}

func (g *Game) Update() error { return nil }

func (g *Game) Draw(screen *ebiten.Image) {
    g.backend.SetTarget(screen)

    _ = g.ts.DrawText(20, 20, "Hello, World!", glyph.TextConfig{
        Style: glyph.TextStyle{
            FontName: "Sans 24",
            Color:    glyph.Color{A: 255},
        },
    })

    g.ts.Commit()
}

func (g *Game) Layout(w, h int) (int, int) { return w, h }

func main() {
    scale := ebiten.Monitor().DeviceScaleFactor()
    backend := glyphebi.New(nil, float32(scale))

    ts, err := glyph.NewTextSystem(backend)
    if err != nil {
        log.Fatal(err)
    }
    defer ts.Free()

    ebiten.SetWindowSize(800, 600)
    if err := ebiten.RunGame(&Game{ts: ts, backend: backend}); err != nil {
        log.Fatal(err)
    }
}

Core Concepts

screenshot

TextSystem

The main entry point. Manages a Context (Pango/FreeType state), a Renderer (glyph atlas + draw calls), and a layout cache.

ts, err := glyph.NewTextSystem(backend)
defer ts.Free()

// Simple rendering (uses cache automatically):
ts.DrawText(x, y, "text", cfg)
ts.Commit() // Upload atlas textures, call once per frame.

For pre-computed layouts:

layout, err := ts.LayoutText("text", cfg)
ts.DrawLayout(layout, x, y)

TextConfig

Controls all aspects of text rendering:

cfg := glyph.TextConfig{
    Style: glyph.TextStyle{
        FontName:      "Sans 16",       // Pango font description
        Typeface:      glyph.TypefaceBold,
        Color:         glyph.Color{R: 255, A: 255},
        Underline:     true,
        Strikethrough:  false,
        LetterSpacing: 2.0,             // Extra spacing (points)
        StrokeWidth:   1.5,             // Outline width (points)
        StrokeColor:   glyph.Color{A: 255},
    },
    Block: glyph.BlockStyle{
        Wrap:   glyph.WrapWord,
        Width:  400,                    // Wrap width (-1 = none)
        Align:  glyph.AlignCenter,
        Indent: 20,                     // First-line indent
    },
    UseMarkup: false,
    Gradient:  nil,
}

Layout

A computed text layout containing glyph positions, line breaks, character rectangles, and logical attributes. Created by LayoutText, LayoutRichText, or LayoutTextCached.

DrawBackend

Interface for plugging in a rendering framework. See DrawBackend in the package documentation. Each backend package provides its own implementation.

Backends

Backend Package Notes
Ebitengine go-glyph/backend/ebitengine Pure Go game engine
SDL2 go-glyph/backend/sdl2 SDL2 renderer
GPU go-glyph/backend/gpu Metal (macOS), OpenGL 3.3 (Linux/Windows)
Web go-glyph/backend/web WASM via Canvas2D
Android go-glyph/backend/android Android (FreeType)
iOS go-glyph/backend/ios iOS (CoreText)

Import the package matching the target framework.

Ebitengine

import glyphebi "github.com/go-gui-org/go-glyph/backend/ebitengine"

backend := glyphebi.New(nil, float32(dpiScale))
// Call backend.SetTarget(screen) each frame before drawing.

SDL2

import glyphsdl "github.com/go-gui-org/go-glyph/backend/sdl2"

backend := glyphsdl.New(sdlRenderer, float32(dpiScale))
defer backend.Destroy()

GPU (Metal / OpenGL)

Uses Metal on macOS and OpenGL 3.3 on Linux. The API is identical on both platforms:

import glyphgpu "github.com/go-gui-org/go-glyph/backend/gpu"

// Create window with gpu.WindowFlag() (Metal or OpenGL).
window, _ := sdl.CreateWindow("demo",
    sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
    800, 600, sdl.WINDOW_SHOWN|gpu.WindowFlag())

backend, err := glyphgpu.New(sdlWindow, float32(dpiScale))
defer backend.Destroy()

// Per frame:
backend.BeginFrame()
// ... draw text ...
backend.EndFrame(0.96, 0.96, 0.96, 1.0, logicalW, logicalH)

Text Styling

screenshot

Bold, Italic

glyph.TextStyle{FontName: "Sans 18", Typeface: glyph.TypefaceBold}
glyph.TextStyle{FontName: "Sans 18", Typeface: glyph.TypefaceItalic}
glyph.TextStyle{FontName: "Sans 18", Typeface: glyph.TypefaceBoldItalic}

Underline and Strikethrough

glyph.TextStyle{FontName: "Sans 16", Underline: true}
glyph.TextStyle{FontName: "Sans 16", Strikethrough: true}

Letter Spacing

glyph.TextStyle{FontName: "Sans 16", LetterSpacing: 3.0}  // wider
glyph.TextStyle{FontName: "Sans 16", LetterSpacing: -1.0}  // tighter

Stroke / Outline

glyph.TextStyle{
    FontName:    "Sans 28",
    Color:       glyph.Color{R: 255, G: 255, B: 255, A: 255},
    StrokeWidth: 2.0,
    StrokeColor: glyph.Color{A: 255},
}

Gradients

cfg := glyph.TextConfig{
    Style: glyph.TextStyle{FontName: "Sans 28"},
    Gradient: &glyph.GradientConfig{
        Direction: glyph.GradientHorizontal,
        Stops: []glyph.GradientStop{
            {Color: glyph.Color{R: 255, A: 255}, Position: 0},
            {Color: glyph.Color{B: 255, A: 255}, Position: 1},
        },
    },
}

Layout Features

Word Wrapping

glyph.BlockStyle{Wrap: glyph.WrapWord, Width: 300}
glyph.BlockStyle{Wrap: glyph.WrapChar, Width: 300}
glyph.BlockStyle{Wrap: glyph.WrapWordChar, Width: 300}

Alignment

glyph.BlockStyle{Width: 400, Align: glyph.AlignLeft}
glyph.BlockStyle{Width: 400, Align: glyph.AlignCenter}
glyph.BlockStyle{Width: 400, Align: glyph.AlignRight}

Indentation

glyph.BlockStyle{Width: 400, Indent: 30}   // first-line indent
glyph.BlockStyle{Width: 400, Indent: -30}  // hanging indent

Rich Text

Render multiple styles in a single layout:

rt := glyph.RichText{
    Runs: []glyph.StyleRun{
        {Text: "Bold ", Style: glyph.TextStyle{
            FontName: "Sans 16",
            Typeface: glyph.TypefaceBold,
            Color:    glyph.Color{A: 255},
        }},
        {Text: "and italic", Style: glyph.TextStyle{
            FontName: "Sans 16",
            Typeface: glyph.TypefaceItalic,
            Color:    glyph.Color{R: 200, A: 255},
        }},
    },
}
layout, err := ts.LayoutRichText(rt, cfg)
ts.DrawLayout(layout, x, y)

Pango Markup

cfg := glyph.TextConfig{
    Style:     glyph.TextStyle{FontName: "Sans 16"},
    UseMarkup: true,
}
ts.DrawText(x, y, "<b>Bold</b> and <i>italic</i>", cfg)

Text Queries

All query methods operate on a pre-computed Layout:

layout, _ := ts.LayoutText(text, cfg)

// Hit testing: byte index at screen coordinates.
idx := layout.HitTest(mouseX-originX, mouseY-originY)

// Character bounding box.
rect, ok := layout.GetCharRect(byteIndex)

// Cursor position geometry.
cursor, ok := layout.GetCursorPos(byteIndex)

// Closest byte index (clamps to text bounds).
idx = layout.GetClosestOffset(localX, localY)

// Selection rectangles.
rects := layout.GetSelectionRects(start, end)

// Cursor navigation.
next := layout.MoveCursorRight(byteIndex)
prev := layout.MoveCursorLeft(byteIndex)
wordNext := layout.MoveCursorWordRight(byteIndex)
wordPrev := layout.MoveCursorWordLeft(byteIndex)
lineStart := layout.MoveCursorLineStart(byteIndex)
lineEnd := layout.MoveCursorLineEnd(byteIndex)
up := layout.MoveCursorUp(byteIndex, preferredX)
down := layout.MoveCursorDown(byteIndex, preferredX)

// Word / paragraph boundaries.
start, end := layout.GetWordAtIndex(byteIndex)
pStart, pEnd := layout.GetParagraphAtIndex(byteIndex, text)

Text Mutation

Package-level functions for editing text:

// Insert and delete.
result := glyph.InsertText(text, cursor, "hello")
result = glyph.DeleteBackward(text, layout, cursor)
result = glyph.DeleteForward(text, layout, cursor)

// Word/line deletion.
result = glyph.DeleteToWordBoundary(text, layout, cursor)
result = glyph.DeleteToWordEnd(text, layout, cursor)
result = glyph.DeleteToLineStart(text, layout, cursor)
result = glyph.DeleteToLineEnd(text, layout, cursor)

// Selection operations.
result = glyph.DeleteSelection(text, cursor, anchor)
result = glyph.InsertReplacingSelection(text, cursor, anchor, "new")
selected := glyph.GetSelectedText(text, cursor, anchor)
clipboard, result := glyph.CutSelection(text, cursor, anchor)

Undo / Redo

um := glyph.NewUndoManager(100)

// After each mutation:
um.RecordMutation(result, insertedText, cursorBefore, anchorBefore)

// Undo / redo:
if undoResult := um.Undo(currentText); undoResult != nil {
    text = undoResult.Text
    cursor = undoResult.Cursor
}
if redoResult := um.Redo(currentText); redoResult != nil {
    text = redoResult.Text
    cursor = redoResult.Cursor
}

Advanced Rendering

Affine Transforms

transform := glyph.AffineRotation(0.3).
    Multiply(glyph.AffineSkew(0.2, 0))

ts.DrawLayoutTransformed(layout, x, y, transform)

Rotated Text

ts.DrawLayoutRotated(layout, x, y, angleRadians)

Glyph Placements (Text on a Path)

Position each glyph independently:

glyphs := layout.GlyphPositions()
placements := make([]glyph.GlyphPlacement, len(glyphs))
for i, g := range glyphs {
    t := float64(g.X) / totalWidth
    placements[i] = glyph.GlyphPlacement{
        X:     pathX(t),
        Y:     pathY(t),
        Angle: pathAngle(t),
    }
}
ts.DrawLayoutPlaced(layout, placements)

Subpixel Rendering

Glyph positions use subpixel offsets for smooth text at all sizes. The renderer quantizes to 4 horizontal subpixel bins.

Subsystems

IME (Input Method Editor)

The ime package provides a Bridge interface for platform-native IME integration. Composition state (preedit text, clause styling, cursor offset) is tracked in CompositionState and rendered with clause underlines and cursor feedback.

Accessibility

The accessibility package provides a Manager for building an accessibility tree with text nodes and text field nodes. An Announcer handles screen reader announcements (character echo, word echo, line changes, selection changes) with debouncing.

Examples

Example Description
examples/demo Ebitengine demo - basic text, styles, wrapping, emoji, CJK, RTL
examples/demo_sdl2 Same demo using SDL2 backend
examples/demo_gpu Same demo using GPU backend (Metal/OpenGL)
examples/showcase_gpu Feature gallery (22 sections)
examples/showcase_android Feature gallery for Android
examples/showcase_ios Feature gallery for iOS
examples/showcase_web Feature gallery for WASM/Web
examples/showcase_sections Per-section feature demos

Run an example:

cd examples/demo && go run .

Architecture

TextSystem
  +-- Context (platform text engine)
  |     +-- Text shaping and layout computation
  |     +-- Font resolution and metrics
  +-- Renderer
  |     +-- GlyphAtlas (multi-page, shelf-packed)
  |     +-- Glyph rasterization and caching
  |     +-- Draw call emission
  +-- Layout cache (hash-keyed, time-based eviction)
  +-- DrawBackend (interface)
        +-- Ebitengine / SDL2 / GPU / Web / Android / iOS

5-pass rendering pipeline per layout:

  1. Background rectangles
  2. Stroke setup
  3. Stroke outlines
  4. Fill glyphs (subpixel positioning, emoji scaling, gradients)
  5. Decorations (underline, strikethrough)

Documentation

See the DeepWiki glossary for API reference.

License

See LICENSE for details.

About

High-performance text rendering library for Go, powered by Pango, FreeType, and FontConfig. Provides text shaping, layout, rasterization, and editing with pluggable rendering backends.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors