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
|
package style
import (
"fmt"
"golang.org/x/exp/shiny/unit"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"golang.org/x/image/math/fixed"
)
const (
DefaultFontSize = 12.0 // points (1/72")
DefaultDpi = 72.0 // dots per inch
DefaultHinting = font.HintingFull
)
// Style defines the colors and font faces that widgets are drawn with.
type Style struct {
font *opentype.Font
faceOpts opentype.FaceOptions
face font.Face // for measurements
// TODO
}
// New returns a new Style with the given options.
// The Style should be closed after use.
func New(opts ...Option) (*Style, error) {
o := parseOpts(opts...)
fnt, err := opentype.Parse(o.font)
if err != nil {
return nil, err
}
face, err := opentype.NewFace(fnt, &o.FaceOptions)
if err != nil {
return nil, err
}
return &Style{
fnt,
o.FaceOptions,
face,
}, nil
}
func (s *Style) Close() error {
return s.face.Close()
}
// NewFace returns a new font.Face using the Style's Font and FaceOptions.
func (s *Style) NewFace() (font.Face, error) {
return opentype.NewFace(s.font, &s.faceOpts)
}
// Convert implements the golang.org/x/exp/shiny/unit.Converter
// interface.
func (s *Style) Convert(v unit.Value, to unit.Unit) unit.Value {
px := fixed26ToFloat(s.Pixels(v))
var f float64
switch to {
case unit.Px:
f = px
case unit.Dp:
f = s.pixelsToInches(px) * unit.DensityIndependentPixelsPerInch
case unit.Pt:
f = s.pixelsToInches(px) * unit.PointsPerInch
case unit.Mm:
f = s.pixelsToInches(px) * unit.MillimetresPerInch
case unit.In:
f = s.pixelsToInches(px)
case unit.Em:
f = px / fixed26ToFloat(s.face.Metrics().Height)
case unit.Ex:
f = px / fixed26ToFloat(s.face.Metrics().XHeight)
case unit.Ch:
f = px / fixed26ToFloat(s.zeroWidth())
default:
panic(fmt.Sprintf("unreachable: impossible %T: %v", to, to))
}
return unit.Value{f, to}
}
// Pixels implements the golang.org/x/exp/shiny/unit.Converter
// interface.
func (s *Style) Pixels(v unit.Value) fixed.Int26_6 {
f := floatToFixed26(v.F)
switch v.U {
case unit.Px:
return f
case unit.Dp:
return s.inchesToPixels(v.F / unit.DensityIndependentPixelsPerInch)
case unit.Pt:
return s.inchesToPixels(v.F / unit.PointsPerInch)
case unit.Mm:
return s.inchesToPixels(v.F / unit.MillimetresPerInch)
case unit.In:
return s.inchesToPixels(v.F)
case unit.Em:
return f.Mul(s.face.Metrics().Height)
case unit.Ex:
return f.Mul(s.face.Metrics().XHeight)
case unit.Ch:
return f.Mul(s.zeroWidth())
default:
panic(fmt.Sprintf("unreachable: impossible %T: %v", v.U, v.U))
}
}
func (s *Style) pixelsToInches(px float64) (in float64) {
return px / s.faceOpts.DPI
}
func (s *Style) inchesToPixels(in float64) (px fixed.Int26_6) {
return floatToFixed26(in * s.faceOpts.DPI)
}
func (s *Style) zeroWidth() (px fixed.Int26_6) {
if advance, ok := s.face.GlyphAdvance('0'); ok {
return advance
} else {
return floatToFixed26(0.5).Mul(s.face.Metrics().Height) // 0.5em
}
}
|