mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +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:
+10
-31
@@ -21,23 +21,9 @@ import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
type editorFont struct {
|
||||
// Family defines the font and style of the text.
|
||||
Family Family
|
||||
// Face specifies the font family configuration.
|
||||
Face Face
|
||||
// Size is the text size.
|
||||
Size unit.Value
|
||||
}
|
||||
|
||||
// Editor implements an editable and scrollable text area.
|
||||
type Editor struct {
|
||||
// Family defines the font and style of the text.
|
||||
Family Family
|
||||
// Face specifies the font family configuration.
|
||||
Face Face
|
||||
// Size is the text size.
|
||||
Size unit.Value
|
||||
Font Font
|
||||
// Material for drawing the text.
|
||||
Material op.MacroOp
|
||||
// Hint contains the text displayed to the user when the
|
||||
@@ -184,13 +170,8 @@ func (e *Editor) Focus() {
|
||||
}
|
||||
|
||||
// Layout flushes any remaining events and lays out the editor.
|
||||
func (e *Editor) Layout(gtx *layout.Context) {
|
||||
font := editorFont{
|
||||
e.Family,
|
||||
e.Face,
|
||||
e.Size,
|
||||
}
|
||||
e.layout(gtx, font)
|
||||
func (e *Editor) Layout(gtx *layout.Context, s *Shaper) {
|
||||
e.layout(gtx, s, e.Font)
|
||||
var stack op.StackOp
|
||||
stack.Push(gtx.Ops)
|
||||
if e.Len() > 0 {
|
||||
@@ -200,14 +181,14 @@ func (e *Editor) Layout(gtx *layout.Context) {
|
||||
paint.ColorOp{Color: color.RGBA{A: 0xaa}}.Add(gtx.Ops)
|
||||
e.HintMaterial.Add(gtx.Ops)
|
||||
}
|
||||
e.draw(gtx, font)
|
||||
e.draw(gtx, s, e.Font)
|
||||
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(gtx.Ops)
|
||||
e.Material.Add(gtx.Ops)
|
||||
e.drawCaret(gtx)
|
||||
stack.Pop()
|
||||
}
|
||||
|
||||
func (e *Editor) layout(gtx *layout.Context, font editorFont) {
|
||||
func (e *Editor) layout(gtx *layout.Context, s *Shaper, font Font) {
|
||||
for _, ok := e.Event(gtx); ok; _, ok = e.Event(gtx) {
|
||||
}
|
||||
if e.font != font {
|
||||
@@ -237,7 +218,7 @@ func (e *Editor) layout(gtx *layout.Context, font editorFont) {
|
||||
}
|
||||
|
||||
if !e.valid {
|
||||
e.layoutText(gtx, font)
|
||||
e.layoutText(gtx, s, font)
|
||||
e.valid = true
|
||||
}
|
||||
|
||||
@@ -278,7 +259,7 @@ func (e *Editor) layout(gtx *layout.Context, font editorFont) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Editor) draw(gtx *layout.Context, font editorFont) {
|
||||
func (e *Editor) draw(gtx *layout.Context, s *Shaper, font Font) {
|
||||
var stack op.StackOp
|
||||
stack.Push(gtx.Ops)
|
||||
off := image.Point{
|
||||
@@ -295,7 +276,6 @@ func (e *Editor) draw(gtx *layout.Context, font editorFont) {
|
||||
Width: e.viewWidth(),
|
||||
Offset: off,
|
||||
}
|
||||
fsize := float32(gtx.Px(font.Size))
|
||||
for {
|
||||
str, lineOff, ok := it.Next()
|
||||
if !ok {
|
||||
@@ -304,7 +284,7 @@ func (e *Editor) draw(gtx *layout.Context, font editorFont) {
|
||||
var stack op.StackOp
|
||||
stack.Push(gtx.Ops)
|
||||
op.TransformOp{}.Offset(lineOff).Add(gtx.Ops)
|
||||
font.Family.Shape(font.Face, fsize, str).Add(gtx.Ops)
|
||||
s.Shape(gtx, font, str).Add(gtx.Ops)
|
||||
paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(gtx.Ops)
|
||||
stack.Pop()
|
||||
}
|
||||
@@ -407,14 +387,13 @@ func (e *Editor) moveCoord(c unit.Converter, pos image.Point) {
|
||||
e.moveToLine(x, carLine)
|
||||
}
|
||||
|
||||
func (e *Editor) layoutText(c unit.Converter, font editorFont) {
|
||||
func (e *Editor) layoutText(c unit.Converter, s *Shaper, font Font) {
|
||||
txt := e.rr.String()
|
||||
if txt == "" {
|
||||
txt = e.Hint
|
||||
}
|
||||
opts := LayoutOptions{SingleLine: e.SingleLine, MaxWidth: e.maxWidth}
|
||||
fsize := float32(c.Px(font.Size))
|
||||
textLayout := font.Family.Layout(font.Face, fsize, txt, opts)
|
||||
textLayout := s.Layout(c, font, txt, opts)
|
||||
lines := textLayout.Lines
|
||||
dims := linesDimens(lines)
|
||||
for i := 0; i < len(lines)-1; i++ {
|
||||
|
||||
+4
-16
@@ -12,17 +12,13 @@ import (
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/unit"
|
||||
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// Label is a widget for laying out and drawing text.
|
||||
type Label struct {
|
||||
// Face defines the style of the text.
|
||||
Face Face
|
||||
// Size is the text size. If zero, a default size is used.
|
||||
Size unit.Value
|
||||
Font Font
|
||||
|
||||
// Material is a macro recording the material to draw the
|
||||
// text. Use a ColorOp for colored text.
|
||||
@@ -93,10 +89,9 @@ func (l *lineIterator) Next() (String, f32.Point, bool) {
|
||||
return String{}, f32.Point{}, false
|
||||
}
|
||||
|
||||
func (l Label) Layout(gtx *layout.Context, family Family) {
|
||||
func (l Label) Layout(gtx *layout.Context, s *Shaper) {
|
||||
cs := gtx.Constraints
|
||||
tsize := textSize(gtx, l.Size)
|
||||
textLayout := family.Layout(l.Face, tsize, l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
|
||||
textLayout := s.Layout(gtx, l.Font, l.Text, LayoutOptions{MaxWidth: cs.Width.Max})
|
||||
lines := textLayout.Lines
|
||||
if max := l.MaxLines; max > 0 && len(lines) > max {
|
||||
lines = lines[:max]
|
||||
@@ -123,7 +118,7 @@ func (l Label) Layout(gtx *layout.Context, family Family) {
|
||||
var stack op.StackOp
|
||||
stack.Push(gtx.Ops)
|
||||
op.TransformOp{}.Offset(off).Add(gtx.Ops)
|
||||
family.Shape(l.Face, tsize, str).Add(gtx.Ops)
|
||||
s.Shape(gtx, l.Font, str).Add(gtx.Ops)
|
||||
// Set a default color in case the material is empty.
|
||||
paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(gtx.Ops)
|
||||
l.Material.Add(gtx.Ops)
|
||||
@@ -153,10 +148,3 @@ func textPadding(lines []Line) (padTop int, padBottom int) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func textSize(c unit.Converter, s unit.Value) float32 {
|
||||
if s.V == 0 {
|
||||
s = unit.Sp(12)
|
||||
}
|
||||
return float32(c.Px(s))
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package shape
|
||||
package text
|
||||
|
||||
import (
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/text"
|
||||
"golang.org/x/image/font/sfnt"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
type layoutCache struct {
|
||||
m map[layoutKey]*layout
|
||||
head, tail *layout
|
||||
m map[layoutKey]*layoutElem
|
||||
head, tail *layoutElem
|
||||
}
|
||||
|
||||
type pathCache struct {
|
||||
@@ -19,10 +17,10 @@ type pathCache struct {
|
||||
head, tail *path
|
||||
}
|
||||
|
||||
type layout struct {
|
||||
next, prev *layout
|
||||
type layoutElem struct {
|
||||
next, prev *layoutElem
|
||||
key layoutKey
|
||||
layout *text.Layout
|
||||
layout *Layout
|
||||
}
|
||||
|
||||
type path struct {
|
||||
@@ -32,21 +30,19 @@ type path struct {
|
||||
}
|
||||
|
||||
type layoutKey struct {
|
||||
f *sfnt.Font
|
||||
ppem fixed.Int26_6
|
||||
str string
|
||||
opts text.LayoutOptions
|
||||
opts LayoutOptions
|
||||
}
|
||||
|
||||
type pathKey struct {
|
||||
f *sfnt.Font
|
||||
ppem fixed.Int26_6
|
||||
str string
|
||||
}
|
||||
|
||||
const maxSize = 1000
|
||||
|
||||
func (l *layoutCache) Get(k layoutKey) (*text.Layout, bool) {
|
||||
func (l *layoutCache) Get(k layoutKey) (*Layout, bool) {
|
||||
if lt, ok := l.m[k]; ok {
|
||||
l.remove(lt)
|
||||
l.insert(lt)
|
||||
@@ -55,15 +51,15 @@ func (l *layoutCache) Get(k layoutKey) (*text.Layout, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (l *layoutCache) Put(k layoutKey, lt *text.Layout) {
|
||||
func (l *layoutCache) Put(k layoutKey, lt *Layout) {
|
||||
if l.m == nil {
|
||||
l.m = make(map[layoutKey]*layout)
|
||||
l.head = new(layout)
|
||||
l.tail = new(layout)
|
||||
l.m = make(map[layoutKey]*layoutElem)
|
||||
l.head = new(layoutElem)
|
||||
l.tail = new(layoutElem)
|
||||
l.head.prev = l.tail
|
||||
l.tail.next = l.head
|
||||
}
|
||||
val := &layout{key: k, layout: lt}
|
||||
val := &layoutElem{key: k, layout: lt}
|
||||
l.m[k] = val
|
||||
l.insert(val)
|
||||
if len(l.m) > maxSize {
|
||||
@@ -73,12 +69,12 @@ func (l *layoutCache) Put(k layoutKey, lt *text.Layout) {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *layoutCache) remove(lt *layout) {
|
||||
func (l *layoutCache) remove(lt *layoutElem) {
|
||||
lt.next.prev = lt.prev
|
||||
lt.prev.next = lt.next
|
||||
}
|
||||
|
||||
func (l *layoutCache) insert(lt *layout) {
|
||||
func (l *layoutCache) insert(lt *layoutElem) {
|
||||
lt.next = l.head
|
||||
lt.prev = l.head.prev
|
||||
lt.prev.next = lt
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package shape
|
||||
package text
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
@@ -21,7 +21,7 @@ func TestLayoutLRU(t *testing.T) {
|
||||
testLRU(t, put, get)
|
||||
}
|
||||
|
||||
func TestuPathLRU(t *testing.T) {
|
||||
func TestPathLRU(t *testing.T) {
|
||||
c := new(pathCache)
|
||||
put := func(i int) {
|
||||
c.Put(pathKey{str: strconv.Itoa(i)}, paint.ClipOp{})
|
||||
@@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
/*
|
||||
Package shape implements text layout and shaping.
|
||||
*/
|
||||
package shape
|
||||
// Package opentype implements text layout and shaping for OpenType
|
||||
// files.
|
||||
package opentype
|
||||
|
||||
import (
|
||||
"math"
|
||||
@@ -19,65 +18,42 @@ import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// Family is an implementation of text.Family. It caches
|
||||
// layouts and paths.
|
||||
// A Family must specify at least the Regular font to be useful.
|
||||
type Family struct {
|
||||
Regular *sfnt.Font
|
||||
Italic *sfnt.Font
|
||||
Bold *sfnt.Font
|
||||
|
||||
layoutCache layoutCache
|
||||
pathCache pathCache
|
||||
buf sfnt.Buffer
|
||||
// Font implementats text.Face.
|
||||
type Font struct {
|
||||
font *sfnt.Font
|
||||
buf sfnt.Buffer
|
||||
}
|
||||
|
||||
// for returns a font for the given face.
|
||||
func (f *Family) fontFor(face text.Face) *sfnt.Font {
|
||||
var font *sfnt.Font
|
||||
switch {
|
||||
case face.Style == text.Italic:
|
||||
font = f.Italic
|
||||
case face.Weight >= 600:
|
||||
font = f.Bold
|
||||
type opentype struct {
|
||||
Font *sfnt.Font
|
||||
Hinting font.Hinting
|
||||
}
|
||||
|
||||
// NewFont parses an SFNT font, such as TTF or OTF data, from a []byte
|
||||
// data source.
|
||||
func Parse(src []byte) (*Font, error) {
|
||||
fnt, err := sfnt.Parse(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if font == nil {
|
||||
font = f.Regular
|
||||
return &Font{font: fnt}, nil
|
||||
}
|
||||
|
||||
// Must is a helper that wraps a call to a function returning (*Font,
|
||||
// error) and panics if the error is non-nil.
|
||||
func Must(font *Font, err error) *Font {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return font
|
||||
}
|
||||
|
||||
func (f *Family) Layout(face text.Face, size float32, str string, opts text.LayoutOptions) *text.Layout {
|
||||
fnt := f.fontFor(face)
|
||||
ppem := fixed.Int26_6(size * 64)
|
||||
lk := layoutKey{
|
||||
f: fnt,
|
||||
ppem: ppem,
|
||||
str: str,
|
||||
opts: opts,
|
||||
}
|
||||
if l, ok := f.layoutCache.Get(lk); ok {
|
||||
return l
|
||||
}
|
||||
l := layoutText(&f.buf, ppem, str, &opentype{Font: fnt, Hinting: font.HintingFull}, opts)
|
||||
f.layoutCache.Put(lk, l)
|
||||
return l
|
||||
func (f *Font) Layout(ppem fixed.Int26_6, str string, opts text.LayoutOptions) *text.Layout {
|
||||
return layoutText(&f.buf, ppem, str, &opentype{Font: f.font, Hinting: font.HintingFull}, opts)
|
||||
}
|
||||
|
||||
func (f *Family) Shape(face text.Face, size float32, str text.String) paint.ClipOp {
|
||||
fnt := f.fontFor(face)
|
||||
ppem := fixed.Int26_6(size * 64)
|
||||
pk := pathKey{
|
||||
f: fnt,
|
||||
ppem: ppem,
|
||||
str: str.String,
|
||||
}
|
||||
if p, ok := f.pathCache.Get(pk); ok {
|
||||
return p
|
||||
}
|
||||
p := textPath(&f.buf, ppem, &opentype{Font: fnt, Hinting: font.HintingFull}, str)
|
||||
f.pathCache.Put(pk, p)
|
||||
return p
|
||||
func (f *Font) Shape(ppem fixed.Int26_6, str text.String) paint.ClipOp {
|
||||
return textPath(&f.buf, ppem, &opentype{Font: f.font, Hinting: font.HintingFull}, str)
|
||||
}
|
||||
|
||||
func layoutText(buf *sfnt.Buffer, ppem fixed.Int26_6, str string, f *opentype, opts text.LayoutOptions) *text.Layout {
|
||||
@@ -229,3 +205,50 @@ func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, str text.String
|
||||
}
|
||||
return builder.End()
|
||||
}
|
||||
|
||||
func (f *opentype) GlyphAdvance(buf *sfnt.Buffer, ppem fixed.Int26_6, r rune) (advance fixed.Int26_6, ok bool) {
|
||||
g, err := f.Font.GlyphIndex(buf, r)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
adv, err := f.Font.GlyphAdvance(buf, g, ppem, f.Hinting)
|
||||
return adv, err == nil
|
||||
}
|
||||
|
||||
func (f *opentype) Kern(buf *sfnt.Buffer, ppem fixed.Int26_6, r0, r1 rune) fixed.Int26_6 {
|
||||
g0, err := f.Font.GlyphIndex(buf, r0)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
g1, err := f.Font.GlyphIndex(buf, r1)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
adv, err := f.Font.Kern(buf, g0, g1, ppem, f.Hinting)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return adv
|
||||
}
|
||||
|
||||
func (f *opentype) Metrics(buf *sfnt.Buffer, ppem fixed.Int26_6) font.Metrics {
|
||||
m, _ := f.Font.Metrics(buf, ppem, f.Hinting)
|
||||
return m
|
||||
}
|
||||
|
||||
func (f *opentype) Bounds(buf *sfnt.Buffer, ppem fixed.Int26_6) fixed.Rectangle26_6 {
|
||||
r, _ := f.Font.Bounds(buf, ppem, f.Hinting)
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *opentype) LoadGlyph(buf *sfnt.Buffer, ppem fixed.Int26_6, r rune) ([]sfnt.Segment, bool) {
|
||||
g, err := f.Font.GlyphIndex(buf, r)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
segs, err := f.Font.LoadGlyph(buf, g, ppem, nil)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return segs, true
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package shape
|
||||
|
||||
import (
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/sfnt"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
type opentype struct {
|
||||
Font *sfnt.Font
|
||||
Hinting font.Hinting
|
||||
}
|
||||
|
||||
func (f *opentype) GlyphAdvance(buf *sfnt.Buffer, ppem fixed.Int26_6, r rune) (advance fixed.Int26_6, ok bool) {
|
||||
g, err := f.Font.GlyphIndex(buf, r)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
adv, err := f.Font.GlyphAdvance(buf, g, ppem, f.Hinting)
|
||||
return adv, err == nil
|
||||
}
|
||||
|
||||
func (f *opentype) Kern(buf *sfnt.Buffer, ppem fixed.Int26_6, r0, r1 rune) fixed.Int26_6 {
|
||||
g0, err := f.Font.GlyphIndex(buf, r0)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
g1, err := f.Font.GlyphIndex(buf, r1)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
adv, err := f.Font.Kern(buf, g0, g1, ppem, f.Hinting)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return adv
|
||||
}
|
||||
|
||||
func (f *opentype) Metrics(buf *sfnt.Buffer, ppem fixed.Int26_6) font.Metrics {
|
||||
m, _ := f.Font.Metrics(buf, ppem, f.Hinting)
|
||||
return m
|
||||
}
|
||||
|
||||
func (f *opentype) Bounds(buf *sfnt.Buffer, ppem fixed.Int26_6) fixed.Rectangle26_6 {
|
||||
r, _ := f.Font.Bounds(buf, ppem, f.Hinting)
|
||||
return r
|
||||
}
|
||||
|
||||
func (f *opentype) LoadGlyph(buf *sfnt.Buffer, ppem fixed.Int26_6, r rune) ([]sfnt.Segment, bool) {
|
||||
g, err := f.Font.GlyphIndex(buf, r)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
segs, err := f.Font.LoadGlyph(buf, g, ppem, nil)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return segs, true
|
||||
}
|
||||
+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
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op/paint"
|
||||
"gioui.org/unit"
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
@@ -45,27 +46,27 @@ type LayoutOptions struct {
|
||||
SingleLine bool
|
||||
}
|
||||
|
||||
// Face specify a particular configuration of a Family.
|
||||
type Face struct {
|
||||
// Weight is the text weight. If zero, Normal is used instead.
|
||||
Weight Weight
|
||||
Style Style
|
||||
}
|
||||
|
||||
// Style is the font style.
|
||||
type Style int
|
||||
|
||||
// Weight is a font weight, in CSS units.
|
||||
type Weight int
|
||||
|
||||
// Family implements a font family. It can layout and shape text from
|
||||
// a Face and size.
|
||||
type Family interface {
|
||||
// Layout returns the text layout for a string given a set of
|
||||
// options.
|
||||
Layout(face Face, size float32, s string, opts LayoutOptions) *Layout
|
||||
// Path returns the ClipOp outline of a text.
|
||||
Shape(face Face, size float32, s String) paint.ClipOp
|
||||
// Font specify a particular typeface, style and size.
|
||||
type Font struct {
|
||||
// Typeface identifies a particular typeface design. The empty
|
||||
// string denotes the default typeface.
|
||||
Typeface string
|
||||
Size unit.Value
|
||||
Style Style
|
||||
// Weight is the text weight. If zero, Normal is used instead.
|
||||
Weight Weight
|
||||
}
|
||||
|
||||
// Face implements text layout and shaping for a particular font.
|
||||
type Face interface {
|
||||
Layout(ppem fixed.Int26_6, str string, opts LayoutOptions) *Layout
|
||||
Shape(ppem fixed.Int26_6, str String) paint.ClipOp
|
||||
}
|
||||
|
||||
type Alignment uint8
|
||||
Reference in New Issue
Block a user