text/wrap.go

Functions Structs

Functions

func IsCJKRune

IsCJKRune returns true if the rune is a CJK character.

Used for script-aware text rendering (ADR-027: CJK hinting, bucket bypass)

and line break opportunities.

func IsCJKRune(r rune) bool {
	return (r >= 0x4E00 && r <= 0x9FFF) ||	// CJK Unified Ideographs
		(r >= 0x3400 && r <= 0x4DBF) ||	// CJK Extension A
		(r >= 0x20000 && r <= 0x2A6DF) ||	// CJK Extension B
		(r >= 0x3040 && r <= 0x309F) ||	// Hiragana
		(r >= 0x30A0 && r <= 0x30FF) ||	// Katakana
		(r >= 0xAC00 && r <= 0xD7AF) ||	// Hangul Syllables
		(r >= 0xFF00 && r <= 0xFFEF)	// Fullwidth forms
}

func MeasureText

MeasureText measures the total advance width of text.

The font size is obtained from face.Size().

func MeasureText(text string, face Face) float64 {
	if text == "" || face == nil {
		return 0
	}

	glyphs := Shape(text, face)
	var width float64
	for i := range glyphs {
		width += glyphs[i].XAdvance
	}
	return width
}

func String

String returns the string representation of the wrap mode.

func (m WrapMode) String() string {
	switch m {
	case WrapNone:
		return "None"
	case WrapWord:
		return "Word"
	case WrapChar:
		return "Char"
	case WrapWordChar:
		return "WordChar"
	default:
		return unknownStr
	}
}

func WrapText

WrapText wraps text to fit within maxWidth using the specified face and options.

Hard line breaks (\n, \r\n, \r) are respected — each paragraph is wrapped independently.

The font size is obtained from face.Size().

Returns a slice of wrapped line results.

func WrapText(text string, face Face, maxWidth float64, mode WrapMode) []WrapResult {
	if text == "" || maxWidth <= 0 {
		return []WrapResult{{Text: text, Start: 0, End: len(text)}}
	}

	if mode == WrapNone {
		return []WrapResult{{Text: text, Start: 0, End: len(text)}}
	}

	// Normalize line endings and split into paragraphs
	normalized := strings.ReplaceAll(text, "\r\n", "\n")
	normalized = strings.ReplaceAll(normalized, "\r", "\n")
	paragraphs := strings.Split(normalized, "\n")

	results := make([]WrapResult, 0, len(paragraphs))
	byteOffset := 0

	for _, para := range paragraphs {
		if para == "" {
			// Empty paragraph (blank line) — preserve as empty line
			results = append(results, WrapResult{
				Text:	"",
				Start:	byteOffset,
				End:	byteOffset,
			})
			byteOffset++	// skip the \n
			continue
		}

		// Wrap this paragraph
		paraResults := wrapParagraph(para, face, maxWidth, mode)

		// Adjust byte offsets relative to original text
		for i := range paraResults {
			paraResults[i].Start += byteOffset
			paraResults[i].End += byteOffset
		}

		results = append(results, paraResults...)
		byteOffset += len(para) + 1	// +1 for the \n separator
	}

	return results
}

Structs

type WrapResult struct

WrapResult represents a wrapped line of text.

type WrapResult struct {
	// Text is the content of this line.
	Text	string
	// Start is the byte offset in the original text.
	Start	int
	// End is the byte offset in the original text.
	End	int
}