feat(字体处理): 优化字体压缩,降低裁剪开销
build.yaml / build (push) Successful in 43s

This commit is contained in:
xiaoqidun committed 2026-10-11 01:36:50 +08:00
1 parent 62c81e7061
commit 226688bbdc
2 files changed
+33 -5

No files matched your search

+25 -4
View File
@@ -51,7 +51,7 @@ func subsetNamedCFFFont(sfnt *sfntFont, glyphs []uint16) ([]byte, error) {
return serializeOTF(tables)
}
// subsetCFFCharstrings 清空未引用的Type2轮廓,保留原字宽、提示和子程序
// subsetCFFCharstrings 清空未引用的Type2轮廓及子程序,保留原字宽、提示和编号
// 入参: data 非CID的CFF数据, keep 保留的字形,调用后包含组合组件
// 返回: []byte 重建的CFF数据, error 结构或依赖错误
func subsetCFFCharstrings(data []byte, keep map[uint16]bool) ([]byte, error) {
@@ -60,12 +60,15 @@ func subsetCFFCharstrings(data []byte, keep map[uint16]bool) ([]byte, error) {
return nil, err
}
keep[0] = true
locals := make([]bool, len(font.locals))
globals := make([]bool, len(font.globals))
var stack [48]float64
for gid := range keep {
if int(gid) >= len(font.chars) {
return nil, fmt.Errorf("CFF glyph index out of range")
}
steps := 0
state := type2State{font: font, width: font.def, seed: font.seed + uint64(gid), steps: &steps, dependencies: keep}
state := type2State{font: font, args: stack[:0], width: font.def, seed: font.seed + uint64(gid), steps: &steps, dependencies: keep, localUsage: locals, globalUsage: globals}
if _, err := state.run(font.chars[gid], 0); err != nil {
return nil, err
}
@@ -78,7 +81,7 @@ func subsetCFFCharstrings(data []byte, keep map[uint16]bool) ([]byte, error) {
continue
}
steps := 0
state := type2State{font: font, width: font.def, widthOnly: true, seed: font.seed + uint64(gid), steps: &steps, dependencies: keep}
state := type2State{font: font, args: stack[:0], width: font.def, widthOnly: true, seed: font.seed + uint64(gid), steps: &steps, dependencies: keep}
if _, err := state.run(program, 0); err != nil {
return nil, err
}
@@ -100,9 +103,27 @@ func subsetCFFCharstrings(data []byte, keep map[uint16]bool) ([]byte, error) {
chars[gid] = encoded.Bytes()
empty[state.width] = chars[gid]
}
font.locals = subsetCFFSubroutines(font.locals, locals)
font.globals = subsetCFFSubroutines(font.globals, globals)
return rebuildNamedCFF(data, font, chars)
}
// subsetCFFSubroutines 将未引用的子程序替换为空返回,保持调用编号及偏移基数
// 入参: programs 子程序索引, used 引用标记
// 返回: [][]byte 裁剪后的子程序索引
func subsetCFFSubroutines(programs [][]byte, used []bool) [][]byte {
result := make([][]byte, len(programs))
empty := []byte{11}
for index, program := range programs {
if used[index] {
result[index] = program
} else {
result[index] = empty
}
}
return result
}
// rebuildNamedCFF 重建非CID的CFF偏移,保留字符集、编码和私有提示参数
// 入参: data 原始CFF数据, font 执行资源, chars 替换后的字形程序
// 返回: []byte 重建的CFF数据, error 不支持的结构
@@ -142,7 +163,7 @@ func rebuildNamedCFF(data []byte, font *type2Font, chars [][]byte) ([]byte, erro
if err != nil {
return nil, err
}
private, err := readType2Private(data, dict[18], font)
private, err := readType2Private(data, dict[18], &type2Font{})
if err != nil {
return nil, err
}
+8 -1
View File
@@ -55,6 +55,8 @@ type type2State struct {
seed uint64
steps *int
dependencies map[uint16]bool
localUsage []bool
globalUsage []bool
}
// readType2Index 读取并校验CFF索引的全部偏移,不复制程序字节
@@ -336,13 +338,18 @@ func (s *type2State) run(data []byte, depth int) (bool, error) {
value := s.args[len(s.args)-1]
s.args = s.args[:len(s.args)-1]
subrs := s.font.locals
usage := s.localUsage
if op == 29 {
subrs = s.font.globals
usage = s.globalUsage
}
value += float64(cffSubrBias(len(subrs)))
if value < 0 || value >= float64(len(subrs)) || value != math.Trunc(value) {
return false, fmt.Errorf("invalid Type2 subroutine number")
}
if usage != nil {
usage[int(value)] = true
}
ended, err := s.run(subrs[int(value)], depth+1)
if ended || err != nil {
return ended, err
@@ -503,7 +510,7 @@ func (s *type2State) appendComponent(code, x, y float64) error {
if s.dependencies != nil {
s.dependencies[uint16(gid)] = true
}
child := type2State{font: s.font, matrix: s.matrix, width: s.font.def, component: true, output: s.output, originX: x, originY: y, outX: s.outX, outY: s.outY, seed: s.font.seed + uint64(gid), steps: s.steps, stripHints: s.stripHints, dependencies: s.dependencies}
child := type2State{font: s.font, matrix: s.matrix, width: s.font.def, component: true, output: s.output, originX: x, originY: y, outX: s.outX, outY: s.outY, seed: s.font.seed + uint64(gid), steps: s.steps, stripHints: s.stripHints, dependencies: s.dependencies, localUsage: s.localUsage, globalUsage: s.globalUsage}
_, err := child.run(s.font.chars[gid], 0)
if err != nil {
return fmt.Errorf("invalid Type2 component %s: %w", name, err)