Skip to content

Commit 52e2bca

Browse files
committed
🐛 Recognize variable font named instances #18400
1 parent fcf1ab0 commit 52e2bca

1 file changed

Lines changed: 74 additions & 10 deletions

File tree

kernel/util/font.go

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package util
1818

1919
import (
20+
"encoding/binary"
2021
"errors"
2122
"os"
2223
"sort"
@@ -74,8 +75,7 @@ func loadFonts() (ret []*Font) {
7475
//LogInfof("[%s] [%s]", fontPath, family)
7576
}
7677
} else if strings.HasSuffix(strings.ToLower(fontPath), ".otf") || strings.HasSuffix(strings.ToLower(fontPath), ".ttf") {
77-
f := parseTTFFontFamily(fontPath)
78-
if nil != f {
78+
for _, f := range parseTTFFontFamily(fontPath) {
7979
if existFont(f, ret) {
8080
continue
8181
}
@@ -114,15 +114,12 @@ func parseTTCFontFamily(fontPath string) (ret []*Font) {
114114
}
115115

116116
for _, f := range fonts {
117-
font := parseFont(f)
118-
if nil != font {
119-
ret = append(ret, font)
120-
}
117+
ret = append(ret, parseFont(f)...)
121118
}
122119
return
123120
}
124121

125-
func parseTTFFontFamily(fontPath string) *Font {
122+
func parseTTFFontFamily(fontPath string) (ret []*Font) {
126123
defer logging.Recover()
127124

128125
fontFile, err := os.Open(fontPath)
@@ -140,9 +137,76 @@ func parseTTFFontFamily(fontPath string) *Font {
140137
return parseFont(font)
141138
}
142139

143-
func parseFont(font *sfnt.Font) *Font {
144-
ret, _ := parseFontInfo(font)
145-
return ret
140+
func parseFont(font *sfnt.Font) (ret []*Font) {
141+
defaultFont, err := parseFontInfo(font)
142+
if err != nil {
143+
return nil
144+
}
145+
ret = append(ret, defaultFont)
146+
ret = append(ret, parseFontVariations(font, defaultFont.Family)...)
147+
return
148+
}
149+
150+
func parseFontVariations(font *sfnt.Font, family string) (ret []*Font) {
151+
defer logging.Recover()
152+
153+
// 可变字体通过 fvar 表声明字重等可变轴,named instances 提供了命名的字重实例
154+
table, err := font.Table(sfnt.MustNamedTag("fvar"))
155+
if err != nil {
156+
return nil
157+
}
158+
buf := table.Bytes()
159+
if len(buf) < 16 {
160+
return nil
161+
}
162+
axesArrayOffset := int(binary.BigEndian.Uint16(buf[4:6]))
163+
axisCount := int(binary.BigEndian.Uint16(buf[8:10]))
164+
axisSize := int(binary.BigEndian.Uint16(buf[10:12]))
165+
instanceCount := int(binary.BigEndian.Uint16(buf[12:14]))
166+
instanceSize := int(binary.BigEndian.Uint16(buf[14:16]))
167+
if axisSize < 20 || instanceSize < 4+axisCount*4 || len(buf) < axesArrayOffset+axisCount*axisSize+instanceCount*instanceSize {
168+
return nil
169+
}
170+
171+
// 找到 wght 轴在实例坐标中的位置
172+
wghtAxisIndex := -1
173+
for i := 0; i < axisCount; i++ {
174+
base := axesArrayOffset + i*axisSize
175+
if "wght" == string(buf[base:base+4]) {
176+
wghtAxisIndex = i
177+
break
178+
}
179+
}
180+
if wghtAxisIndex < 0 {
181+
return nil
182+
}
183+
184+
t, err := font.NameTable()
185+
if err != nil {
186+
return nil
187+
}
188+
entries := t.List()
189+
instancesBase := axesArrayOffset + axisCount*axisSize
190+
for i := 0; i < instanceCount; i++ {
191+
base := instancesBase + i*instanceSize
192+
subfamilyNameID := binary.BigEndian.Uint16(buf[base : base+2])
193+
// wght 坐标是 16.16 定点数,取整后即为 CSS font-weight
194+
weight := int(float64(int32(binary.BigEndian.Uint32(buf[base+4+wghtAxisIndex*4:base+8+wghtAxisIndex*4])))/65536 + 0.5)
195+
if weight < 1 || 1000 < weight {
196+
continue
197+
}
198+
subfamily := selectFontName(entries, sfnt.NameID(subfamilyNameID))
199+
if "" == subfamily {
200+
continue
201+
}
202+
203+
displayName := family
204+
if !strings.EqualFold(subfamily, "Regular") {
205+
displayName = family + " " + subfamily
206+
}
207+
ret = append(ret, &Font{Family: family, Weight: weight, DisplayName: displayName})
208+
}
209+
return
146210
}
147211

148212
func parseFontInfo(font *sfnt.Font) (*Font, error) {

0 commit comments

Comments
 (0)