text/layout.go
Functions
func DefaultLayoutOptions
func DefaultLayoutOptions() LayoutOptions {
return LayoutOptions{
MaxWidth: 0, // No wrapping (no MaxWidth constraint)
LineSpacing: 1.0,
Alignment: AlignLeft,
Direction: DirectionLTR,
// WrapMode defaults to WrapWordChar (zero value)
}
}
func Height
Height returns the total height of the line (ascent + descent).
func (l *Line) Height() float64 {
return l.Ascent + l.Descent
}
func LayoutText
LayoutText performs text layout with the given options.
It segments text by direction/script, shapes each segment,
wraps lines if MaxWidth > 0, and positions lines with alignment.
The font size is obtained from face.Size().
For cancellable layout, use LayoutTextWithContext.
func LayoutText(text string, face Face, opts LayoutOptions) *Layout {
layout, _ := LayoutTextWithContext(context.Background(), text, face, opts)
return layout
}
func LayoutTextSimple
LayoutTextSimple is a convenience wrapper with default options.
The font size is obtained from face.Size().
func LayoutTextSimple(text string, face Face) *Layout {
return LayoutText(text, face, DefaultLayoutOptions())
}
func LayoutTextWithContext
LayoutTextWithContext performs text layout with the given options and cancellation support.
It segments text by direction/script, shapes each segment,
wraps lines if MaxWidth > 0, and positions lines with alignment.
The font size is obtained from face.Size().
The context can be used to cancel long-running layout operations.
When canceled, returns nil and ctx.Err().
func LayoutTextWithContext(ctx context.Context, text string, face Face, opts LayoutOptions) (*Layout, error) {
if text == "" {
return &Layout{}, nil
}
if face == nil {
return &Layout{}, nil
}
// Check for cancellation at start
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// Apply defaults for zero values
if opts.LineSpacing <= 0 {
opts.LineSpacing = 1.0
}
// Get font metrics for line height calculation
metrics := face.Metrics()
// Split by hard line breaks
paragraphs := splitParagraphs(text)
layout := &Layout{
Lines: make([]Line, 0, len(paragraphs)),
}
var y float64
for i, para := range paragraphs {
// Check for cancellation periodically (every 8 paragraphs)
if i%8 == 0 && i > 0 {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
}
paraLines := layoutParagraph(para, face, opts, metrics)
for j := range paraLines {
line := ¶Lines[j]
// Calculate baseline position: previous Y + line ascent
if len(layout.Lines) == 0 && j == 0 {
// Very first line of entire layout
y = line.Ascent
} else {
// Get previous line: from current paragraph or from layout.Lines
var prevLine *Line
if j > 0 {
prevLine = ¶Lines[j-1]
} else {
prevLine = &layout.Lines[len(layout.Lines)-1]
}
lineGap := metrics.LineGap * opts.LineSpacing
y = prevLine.Y + prevLine.Descent + lineGap + line.Ascent
}
line.Y = y
// Apply horizontal alignment
applyAlignment(line, opts.Alignment, opts.MaxWidth)
// Track layout bounds
if line.Width > layout.Width {
layout.Width = line.Width
}
}
layout.Lines = append(layout.Lines, paraLines...)
}
// Calculate total height
if len(layout.Lines) > 0 {
lastLine := &layout.Lines[len(layout.Lines)-1]
layout.Height = lastLine.Y + lastLine.Descent
}
return layout, nil
}
func String
String returns the string representation of the alignment.
func (a Alignment) String() string {
switch a {
case AlignLeft:
return "Left"
case AlignCenter:
return "Center"
case AlignRight:
return "Right"
case AlignJustify:
return "Justify"
default:
return unknownStr
}
}
Structs
type LayoutOptions struct
LayoutOptions configures text layout behavior.
type LayoutOptions struct {
// MaxWidth is the maximum line width in pixels.
// If 0, no line wrapping is performed (single-line paragraphs).
MaxWidth float64
// LineSpacing is a multiplier for line height.
// 1.0 uses the font's natural line height; 1.5 adds 50% extra space.
LineSpacing float64
// Alignment specifies horizontal text alignment.
Alignment Alignment
// Direction is the base text direction (LTR or RTL).
// Used for paragraph-level direction when no strong directional text is present.
Direction Direction
// WrapMode specifies how text is wrapped when it exceeds MaxWidth.
// Default is WrapWordChar which breaks at word boundaries first,
// then falls back to character boundaries for long words.
WrapMode WrapMode
}
type Line struct
Line represents a positioned line of text ready for rendering.
type Line struct {
// Runs contains the shaped runs that make up this line.
// Multiple runs occur with mixed scripts or directions.
Runs []ShapedRun
// Glyphs contains all glyphs from Runs, positioned for rendering.
// Glyph X positions are absolute within the layout.
Glyphs []ShapedGlyph
// Width is the total advance width of all glyphs in this line.
Width float64
// Ascent is the maximum ascent of all runs (distance above baseline).
Ascent float64
// Descent is the maximum descent of all runs (distance below baseline).
Descent float64
// Y is the baseline Y position of this line within the layout.
Y float64
}
type Layout struct
Layout represents the result of text layout.
type Layout struct {
// Lines contains all lines of laid out text.
Lines []Line
// Width is the maximum width among all lines.
Width float64
// Height is the total height of all lines.
Height float64
}
DefaultLayoutOptions returns sensible default layout options.