mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
text: replace Family with Shaper, add Font, Face
There is now a single shaping implementation, Shaper, for all fonts, replacing Family that only covered a single typeface. A typeface is identified by a name, where the empty string denotes the default typeface. Font is introduced to specify a particular font from the typeface, style, weight and size. Face is changed to an interface for a particular layout and shaping method. The text/shape package is renamed to text/opentype and contains a Face implementation based on golang.org/x/image/font/sfnt. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package text
|
||||
|
||||
import (
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/unit"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// Shaper implements layout and shaping of text and a cache of
|
||||
// computed results.
|
||||
//
|
||||
// Specify the default and fallback font by calling Register with the
|
||||
// empty Font.
|
||||
type Shaper struct {
|
||||
faces map[Font]*face
|
||||
}
|
||||
|
||||
type face struct {
|
||||
face Face
|
||||
layoutCache layoutCache
|
||||
pathCache pathCache
|
||||
}
|
||||
|
||||
func (s *Shaper) Register(font Font, tf Face) {
|
||||
if s.faces == nil {
|
||||
s.faces = make(map[Font]*face)
|
||||
}
|
||||
// Treat all font sizes equally.
|
||||
font.Size = unit.Value{}
|
||||
if font.Weight == 0 {
|
||||
font.Weight = Normal
|
||||
}
|
||||
s.faces[font] = &face{
|
||||
face: tf,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shaper) Layout(c unit.Converter, font Font, str string, opts LayoutOptions) *Layout {
|
||||
tf := s.faceForFont(font)
|
||||
return tf.layout(fixed.I(c.Px(font.Size)), str, opts)
|
||||
}
|
||||
|
||||
func (s *Shaper) Shape(c unit.Converter, font Font, str String) paint.ClipOp {
|
||||
tf := s.faceForFont(font)
|
||||
return tf.shape(fixed.I(c.Px(font.Size)), str)
|
||||
}
|
||||
|
||||
func (s *Shaper) faceForStyle(font Font) *face {
|
||||
tf := s.faces[font]
|
||||
if tf == nil {
|
||||
font := font
|
||||
font.Weight = Normal
|
||||
tf = s.faces[font]
|
||||
}
|
||||
if tf == nil {
|
||||
font := font
|
||||
font.Style = Regular
|
||||
tf = s.faces[font]
|
||||
}
|
||||
if tf == nil {
|
||||
font := font
|
||||
font.Style = Regular
|
||||
font.Weight = Normal
|
||||
tf = s.faces[font]
|
||||
}
|
||||
return tf
|
||||
}
|
||||
|
||||
func (s *Shaper) faceForFont(font Font) *face {
|
||||
font.Size = unit.Value{}
|
||||
tf := s.faceForStyle(font)
|
||||
if tf == nil {
|
||||
font.Typeface = ""
|
||||
tf = s.faceForStyle(font)
|
||||
}
|
||||
if tf == nil {
|
||||
panic("no default typeface defined")
|
||||
}
|
||||
return tf
|
||||
}
|
||||
|
||||
func (t *face) layout(ppem fixed.Int26_6, str string, opts LayoutOptions) *Layout {
|
||||
lk := layoutKey{
|
||||
ppem: ppem,
|
||||
str: str,
|
||||
opts: opts,
|
||||
}
|
||||
if l, ok := t.layoutCache.Get(lk); ok {
|
||||
return l
|
||||
}
|
||||
l := t.face.Layout(ppem, str, opts)
|
||||
t.layoutCache.Put(lk, l)
|
||||
return l
|
||||
}
|
||||
|
||||
func (t *face) shape(ppem fixed.Int26_6, str String) paint.ClipOp {
|
||||
pk := pathKey{
|
||||
ppem: ppem,
|
||||
str: str.String,
|
||||
}
|
||||
if clip, ok := t.pathCache.Get(pk); ok {
|
||||
return clip
|
||||
}
|
||||
clip := t.face.Shape(ppem, str)
|
||||
t.pathCache.Put(pk, clip)
|
||||
return clip
|
||||
}
|
||||
Reference in New Issue
Block a user