mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-05 17:35:36 +00:00
deps,text,widget,font/opentype: [API] add harfbuzz-powered text shaper
This commit introduces a new text shaping infrastructure powered by Benoit Kugler's Go source-port of harfbuzz. This shaper can properly display complex scripts and RTL text. This commit changes the signature of the text.Shaper function, which is a breaking API change. The new functionality is available via opentype.ParseHarfbuzz, which configures a text.Shaper leveraging the new backend. References: https://todo.sr.ht/~eliasnaur/gio/146 Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
+124
-1
@@ -5,7 +5,9 @@ package text
|
||||
import (
|
||||
"io"
|
||||
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/op/clip"
|
||||
"github.com/go-text/typesetting/font"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
@@ -23,9 +25,130 @@ type Line struct {
|
||||
Bounds fixed.Rectangle26_6
|
||||
}
|
||||
|
||||
// Range describes the position and quantity of a range of text elements
|
||||
// within a larger slice. The unit is usually runes of unicode data or
|
||||
// glyphs of shaped font data.
|
||||
type Range struct {
|
||||
// Count describes the number of items represented by the Range.
|
||||
Count int
|
||||
// Offset describes the start position of the represented
|
||||
// items within a larger list.
|
||||
Offset int
|
||||
}
|
||||
|
||||
// GlyphID uniquely identifies a glyph within a specific font.
|
||||
type GlyphID = font.GID
|
||||
|
||||
// Glyph contains the metadata needed to render a glyph.
|
||||
type Glyph struct {
|
||||
// ID is this glyph's identifier within the font it was shaped with.
|
||||
ID GlyphID
|
||||
// ClusterIndex is the identifier for the text shaping cluster that
|
||||
// this glyph is part of.
|
||||
ClusterIndex int
|
||||
// GlyphCount is the number of glyphs in the same cluster as this glyph.
|
||||
GlyphCount int
|
||||
// RuneCount is the quantity of runes in the source text that this glyph
|
||||
// corresponds to.
|
||||
RuneCount int
|
||||
// XAdvance and YAdvance describe the distance the dot moves when
|
||||
// laying out the glyph on the X or Y axis.
|
||||
XAdvance, YAdvance fixed.Int26_6
|
||||
// XOffset and YOffset describe offsets from the dot that should be
|
||||
// applied when rendering the glyph.
|
||||
XOffset, YOffset fixed.Int26_6
|
||||
}
|
||||
|
||||
// GlyphCluster provides metadata about a sequence of indivisible shaped
|
||||
// glyphs.
|
||||
type GlyphCluster struct {
|
||||
// Advance is the cumulative advance of all glyphs in the cluster.
|
||||
Advance fixed.Int26_6
|
||||
// Runes indicates the position and quantity of the runes represented by
|
||||
// this cluster within the text.
|
||||
Runes Range
|
||||
// Glyphs indicates the position and quantity of the glyphs within this
|
||||
// cluster in a Layout's Glyphs slice.
|
||||
Glyphs Range
|
||||
}
|
||||
|
||||
// RuneWidth returns the effective width of one rune for this cluster.
|
||||
// If the cluster contains multiple runes, the width of the glyphs of
|
||||
// the cluster is divided evenly among the runes.
|
||||
func (c GlyphCluster) RuneWidth() fixed.Int26_6 {
|
||||
if c.Runes.Count == 0 {
|
||||
return 0
|
||||
}
|
||||
return c.Advance / fixed.Int26_6(c.Runes.Count)
|
||||
}
|
||||
|
||||
type Layout struct {
|
||||
// Glyphs are the actual font characters for the text. The are ordered
|
||||
// from left to right regardless of the text direction of the underlying
|
||||
// text.
|
||||
Glyphs []Glyph
|
||||
// Clusters is metadata about the shaped glyphs. It is mostly useful for
|
||||
// interactive text widgets like editors. The order of clusters is logical,
|
||||
// so the first cluster will describe the beginning of the text and may
|
||||
// refer to the final glyphs in the Glyphs field if the text is RTL.
|
||||
Clusters []GlyphCluster
|
||||
Text string
|
||||
Advances []fixed.Int26_6
|
||||
// Runes describes the position of the text data this layout represents
|
||||
// within the overall body of text being shaped.
|
||||
Runes Range
|
||||
// Direction is the layout direction of the text.
|
||||
Direction system.TextDirection
|
||||
}
|
||||
|
||||
// Slice returns a layout starting at the glyph cluster index start
|
||||
// and running through the glyph cluster index end. The Offsets field
|
||||
// of the returned layout is adjusted to reflect the new rune range
|
||||
// covered by the layout. The returned layout will have no Clusters.
|
||||
func (l Layout) Slice(start, end int) Layout {
|
||||
if start == end || end == 0 || start == len(l.Clusters) {
|
||||
return Layout{}
|
||||
}
|
||||
newRuneStart := l.Clusters[start].Runes.Offset
|
||||
runesBefore := newRuneStart - l.Runes.Offset
|
||||
endCluster := l.Clusters[end-1]
|
||||
startCluster := l.Clusters[start]
|
||||
runesAfter := l.Runes.Offset + l.Runes.Count - (endCluster.Runes.Offset + endCluster.Runes.Count)
|
||||
|
||||
if l.Direction.Progression() == system.TowardOrigin {
|
||||
startCluster, endCluster = endCluster, startCluster
|
||||
}
|
||||
glyphStart := startCluster.Glyphs.Offset
|
||||
glyphEnd := endCluster.Glyphs.Offset + endCluster.Glyphs.Count
|
||||
|
||||
out := l
|
||||
out.Clusters = nil
|
||||
out.Glyphs = out.Glyphs[glyphStart:glyphEnd]
|
||||
out.Runes.Offset = newRuneStart
|
||||
out.Runes.Count -= runesBefore + runesAfter
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// equals returns true when l2 is logically equivalent to l.
|
||||
func (l Layout) equals(l2 Layout) bool {
|
||||
if len(l.Glyphs) != len(l2.Glyphs) || len(l.Clusters) != len(l2.Clusters) {
|
||||
return false
|
||||
}
|
||||
if l.Runes != l2.Runes || l.Direction != l2.Direction {
|
||||
return false
|
||||
}
|
||||
for i := range l.Clusters {
|
||||
if l.Clusters[i] != l2.Clusters[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for i := range l.Glyphs {
|
||||
if l.Glyphs[i] != l2.Glyphs[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Style is the font style.
|
||||
@@ -47,7 +170,7 @@ type Font struct {
|
||||
// Face implements text layout and shaping for a particular font. All
|
||||
// methods must be safe for concurrent use.
|
||||
type Face interface {
|
||||
Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error)
|
||||
Layout(ppem fixed.Int26_6, maxWidth int, lc system.Locale, txt io.RuneReader) ([]Line, error)
|
||||
Shape(ppem fixed.Int26_6, str Layout) clip.PathSpec
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user