1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
// SPDX-License-Identifier: Unlicense OR MIT
package text
import (
"io"
"strings"
"gioui.org/op"
"golang.org/x/image/math/fixed"
)
// Shaper implements layout and shaping of text.
type Shaper interface {
// Layout a text according to a set of options.
Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error)
// Shape a line of text and return a clipping operation for its outline.
Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp
// LayoutString is like Layout, but for strings.
LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line
// ShapeString is like Shape for lines previously laid out by LayoutString.
ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp
}
// A FontFace is a Font and a matching Face.
type FontFace struct {
Font Font
Face Face
}
// Cache implements cached layout and shaping of text from a set of
// registered fonts.
//
// If a font matches no registered shape, Cache falls back to the
// first registered face.
//
// The LayoutString and ShapeString results are cached and re-used if
// possible.
type Cache struct {
def Typeface
faces map[Font]*faceCache
}
type faceCache struct {
face Face
layoutCache layoutCache
pathCache pathCache
}
func (c *Cache) lookup(font Font) *faceCache {
var f *faceCache
f = c.faceForStyle(font)
if f == nil {
font.Typeface = c.def
f = c.faceForStyle(font)
}
return f
}
func (c *Cache) faceForStyle(font Font) *faceCache {
tf := c.faces[font]
if tf == nil {
font := font
font.Weight = Normal
tf = c.faces[font]
}
if tf == nil {
font := font
font.Style = Regular
tf = c.faces[font]
}
if tf == nil {
font := font
font.Style = Regular
font.Weight = Normal
tf = c.faces[font]
}
return tf
}
func NewCache(collection []FontFace) *Cache {
c := &Cache{
faces: make(map[Font]*faceCache),
}
for i, ff := range collection {
if ff.Font.Weight == 0 {
ff.Font.Weight = Normal
}
if i == 0 {
c.def = ff.Font.Typeface
}
c.faces[ff.Font] = &faceCache{face: ff.Face}
}
return c
}
func (s *Cache) Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) {
cache := s.lookup(font)
return cache.face.Layout(size, maxWidth, txt)
}
func (s *Cache) Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp {
cache := s.lookup(font)
return cache.face.Shape(size, layout)
}
func (s *Cache) LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line {
cache := s.lookup(font)
return cache.layout(size, maxWidth, str)
}
func (s *Cache) ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp {
cache := s.lookup(font)
return cache.shape(size, str, layout)
}
func (f *faceCache) layout(ppem fixed.Int26_6, maxWidth int, str string) []Line {
if f == nil {
return nil
}
lk := layoutKey{
ppem: ppem,
maxWidth: maxWidth,
str: str,
}
if l, ok := f.layoutCache.Get(lk); ok {
return l
}
l, _ := f.face.Layout(ppem, maxWidth, strings.NewReader(str))
f.layoutCache.Put(lk, l)
return l
}
func (f *faceCache) shape(ppem fixed.Int26_6, str string, layout []Glyph) op.CallOp {
if f == nil {
return op.CallOp{}
}
pk := pathKey{
ppem: ppem,
str: str,
}
if clip, ok := f.pathCache.Get(pk); ok {
return clip
}
clip := f.face.Shape(ppem, layout)
f.pathCache.Put(pk, clip)
return clip
}
|