feat(字体图像): 优化字体图像性能
build.yaml / build (push) Successful in 54s

This commit is contained in:
xiaoqidun committed 2026-10-15 21:58:12 +08:00
1 parent 6926ceafc9
commit 0b1dc7e6c8
7 files changed
+148 -25

No files matched your search

+18 -8
View File
@@ -18,7 +18,7 @@ import (
"encoding/binary"
"fmt"
"maps"
"sort"
"slices"
"sync"
"github.com/go-text/typesetting/font/opentype/tables"
@@ -34,10 +34,11 @@ type SFNT struct {
reverse map[uint16][]rune
}
// sfntCmap 保存编码记录及对应字符映射
// sfntCmap 保存编码记录及对应字符映射,标记已解析的共享子表
type sfntCmap struct {
platform, encoding, format uint16
mapping map[rune]uint16
duplicate bool
}
// ParseSFNT 校验独立字体的必需表、度量范围及字符映射
@@ -86,6 +87,9 @@ func ParseSFNTTables(table map[string][]byte) (*SFNT, error) {
}
f.cmaps = cmaps
for _, cmap := range f.cmaps {
if cmap.duplicate {
continue
}
for _, glyph := range cmap.mapping {
if glyph >= f.count {
return nil, fmt.Errorf("cmap glyph index out of range")
@@ -150,7 +154,7 @@ func readSFNTCmaps(data []byte) ([]sfntCmap, error) {
}
parsed[offset] = mapping
}
result = append(result, sfntCmap{binary.BigEndian.Uint16(record), binary.BigEndian.Uint16(record[2:]), format, mapping})
result = append(result, sfntCmap{platform: binary.BigEndian.Uint16(record), encoding: binary.BigEndian.Uint16(record[2:]), format: format, mapping: mapping, duplicate: found})
}
return result, nil
}
@@ -164,11 +168,11 @@ func (f *SFNT) UnitsPerEm() uint16 { return f.units }
func (f *SFNT) NumGlyphs() uint16 { return f.count }
// CmapEntries 返回各编码记录的映射项总数,共享子表按记录分别计数
// 返回: int 用于缓存预算的映射项数量
func (f *SFNT) CmapEntries() int {
count := 0
// 返回: uint64 用于缓存预算的映射项数量
func (f *SFNT) CmapEntries() uint64 {
var count uint64
for _, cmap := range f.cmaps {
count += len(cmap.mapping)
count += uint64(len(cmap.mapping))
}
return count
}
@@ -178,6 +182,9 @@ func (f *SFNT) CmapEntries() int {
// 返回: uint16 字形编号
func (f *SFNT) GlyphIndex(char rune) uint16 {
for _, cmap := range f.cmaps {
if cmap.duplicate {
continue
}
if gid := cmap.mapping[char]; gid != 0 {
return gid
}
@@ -193,6 +200,9 @@ func (f *SFNT) glyphCharacters(gid uint16) []rune {
f.reverse = make(map[uint16][]rune)
seen := make(map[rune]bool)
for _, cmap := range f.cmaps {
if cmap.duplicate {
continue
}
for char, glyph := range cmap.mapping {
if glyph != 0 && !seen[char] {
f.reverse[glyph] = append(f.reverse[glyph], char)
@@ -201,7 +211,7 @@ func (f *SFNT) glyphCharacters(gid uint16) []rune {
}
}
for _, chars := range f.reverse {
sort.Slice(chars, func(i, j int) bool { return chars[i] < chars[j] })
slices.Sort(chars)
}
})
return f.reverse[gid]
+19 -5
View File
@@ -134,7 +134,8 @@ func SubsetSFNT(data []byte, glyphs []uint16, mapped bool) ([]byte, error) {
if err != nil {
return nil, err
}
if subset := SubsetSourceSFNT(fixed, usage); subset != nil {
sfnt.Tables = tables
if subset := subsetSourceSFNT(sfnt, usage, len(fixed)); subset != nil {
return subset, nil
}
return fixed, nil
@@ -147,7 +148,7 @@ func SubsetSFNT(data []byte, glyphs []uint16, mapped bool) ([]byte, error) {
// 入参: data 字体数据, usage 全包用字记录
// 返回: []byte 更小的字体子集,无确定收益时为空
func SubsetSourceSFNT(data []byte, usage *Usage) []byte {
if usage.Unsafe || len(usage.Characters)+len(usage.Glyphs) == 0 || bytes.HasPrefix(data, []byte("ttcf")) {
if usage == nil || usage.Unsafe || len(usage.Characters)+len(usage.Glyphs) == 0 || bytes.HasPrefix(data, []byte("ttcf")) {
return nil
}
fontTables, err := FileTables(data, 0)
@@ -162,7 +163,17 @@ func SubsetSourceSFNT(data []byte, usage *Usage) []byte {
}
}
sfnt, err := ParseSFNTTables(fontTables)
if err != nil || !sfnt.IsTrueType && !sfnt.IsCFF || fontEmbeddingFlags(sfnt)&0x0100 != 0 {
if err != nil {
return nil
}
return subsetSourceSFNT(sfnt, usage, len(data))
}
// subsetSourceSFNT 复用已校验的字体表裁剪字形,仅返回小于原编码的结果
// 入参: sfnt 已解析字体, usage 用字记录, size 原编码长度
// 返回: []byte 更小的字体子集,无确定收益时为空
func subsetSourceSFNT(sfnt *SFNT, usage *Usage, size int) []byte {
if usage == nil || usage.Unsafe || len(usage.Characters)+len(usage.Glyphs) == 0 || !sfnt.IsTrueType && !sfnt.IsCFF || fontEmbeddingFlags(sfnt)&0x0100 != 0 {
return nil
}
var unicodeTables []sfntCmap
@@ -178,6 +189,9 @@ func SubsetSourceSFNT(data []byte, usage *Usage) []byte {
return nil
}
glyphs := maps.Clone(usage.Glyphs)
if glyphs == nil {
glyphs = make(map[uint16]bool)
}
glyphs[0] = true
mapping := make(map[rune]uint16, len(usage.Characters))
for char := range usage.Characters {
@@ -210,7 +224,7 @@ func SubsetSourceSFNT(data []byte, usage *Usage) []byte {
tables["cmap"] = buildCmapWithVariations(sfnt.NumGlyphs(), mapping, variations)
delete(tables, "DSIG")
result, err := SerializeOTF(tables)
if err != nil || len(result) >= len(data) {
if err != nil || len(result) >= size {
return nil
}
return result
@@ -258,7 +272,7 @@ func SubsetSourceSFNT(data []byte, usage *Usage) []byte {
clear(tables["head"][8:12])
delete(tables, "DSIG")
result, err := SerializeOTF(tables)
if err != nil || len(result) >= len(data) {
if err != nil || len(result) >= size {
return nil
}
return result