From 226688bbdc0faf3cc9d3e2a6ac3d2027a534458d Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Sun, 11 Oct 2026 01:36:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AD=97=E4=BD=93=E5=A4=84=E7=90=86):=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AD=97=E4=BD=93=E5=8E=8B=E7=BC=A9=EF=BC=8C?= =?UTF-8?q?=E9=99=8D=E4=BD=8E=E8=A3=81=E5=89=AA=E5=BC=80=E9=94=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ofdgo_font_cff_subset.go | 29 +++++++++++++++++++++++++---- ofdgo_font_type2.go | 9 ++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/ofdgo_font_cff_subset.go b/ofdgo_font_cff_subset.go index 5208437..7f69060 100644 --- a/ofdgo_font_cff_subset.go +++ b/ofdgo_font_cff_subset.go @@ -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 } diff --git a/ofdgo_font_type2.go b/ofdgo_font_type2.go index 30a6d2c..9ea19fc 100644 --- a/ofdgo_font_type2.go +++ b/ofdgo_font_type2.go @@ -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)