feat(字体处理): 修复字体变换,完善子集导入
build.yaml / build (push) Successful in 5m46s

This commit is contained in:
xiaoqidun committed 2026-10-04 18:20:34 +08:00
1 parent 60eb5ac6a1
commit be697e7045
10 files changed
+508 -220

No files matched your search

+36 -27
View File
@@ -16,7 +16,6 @@ package ofdgo
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"slices"
@@ -37,6 +36,7 @@ type type2Font struct {
type type2State struct {
font *type2Font
widthTarget *type2Font
matrix *[6]float64
args []float64
transient [32]float64
stored uint32
@@ -60,37 +60,24 @@ type type2State struct {
// 入参: data 字体数据, offset 索引偏移
// 返回: [][]byte 索引项, int 索引结束位置, error 错误信息
func readType2Index(data []byte, offset int) ([][]byte, int, error) {
if offset < 0 || offset > len(data)-2 {
return nil, 0, fmt.Errorf("truncated CFF index")
}
count := int(binary.BigEndian.Uint16(data[offset:]))
if count == 0 {
return nil, offset + 2, nil
}
if offset > len(data)-3 {
return nil, 0, fmt.Errorf("truncated CFF index header")
count, base, end, err := scanCFFIndex(data, offset)
if err != nil || count == 0 {
return nil, end, err
}
width := int(data[offset+2])
if width < 1 || width > 4 || count+1 > (len(data)-offset-3)/width {
return nil, 0, fmt.Errorf("invalid CFF index offsets")
}
base := offset + 3 + (count+1)*width
items := make([][]byte, count)
previous := uint64(0)
previous := 1
for index := 0; index <= count; index++ {
value := uint64(0)
for _, b := range data[offset+3+index*width : offset+3+(index+1)*width] {
value = value<<8 | uint64(b)
}
if value < 1 || value-1 > uint64(len(data)-base) || value < previous || index == 0 && value != 1 {
return nil, 0, fmt.Errorf("invalid CFF index range")
}
if index != 0 {
items[index-1] = data[base+int(previous)-1 : base+int(value)-1]
items[index-1] = data[base+previous-1 : base+int(value)-1]
}
previous = value
previous = int(value)
}
return items, base + int(previous) - 1, nil
return items, end, nil
}
// readType2Font 读取单FD字体的Type2执行资源,CID字体先经字体规范化
@@ -197,6 +184,13 @@ func readType2Private(data []byte, values []float64, font *type2Font) (cffDict,
// 入参: data CFF字体数据
// 返回: [][]byte 字形程序, bool 是否变更, error 错误信息
func normalizeType2Programs(data []byte) ([][]byte, bool, error) {
return normalizeType2ProgramsMatrix(data, nil)
}
// normalizeType2ProgramsMatrix 规范化字形指令并按需变换轮廓与字宽
// 入参: data CFF字体数据, matrix 设计坐标变换,nil时保留原坐标
// 返回: [][]byte 字形程序, bool 是否变更, error 错误信息
func normalizeType2ProgramsMatrix(data []byte, matrix *[6]float64) ([][]byte, bool, error) {
font, err := readType2Font(data)
if err != nil {
return nil, false, err
@@ -206,7 +200,10 @@ func normalizeType2Programs(data []byte) ([][]byte, bool, error) {
var stack [48]float64
for gid, program := range font.chars {
if len(program) == 0 {
continue
if matrix == nil {
continue
}
program = []byte{14}
}
steps := 0
state := type2State{font: font, args: stack[:0], width: font.def, seed: font.seed + uint64(gid), steps: &steps}
@@ -214,12 +211,15 @@ func normalizeType2Programs(data []byte) ([][]byte, bool, error) {
if err != nil {
continue
}
if !state.changed {
if !state.changed && matrix == nil {
continue
}
var output bytes.Buffer
steps = 0
state = type2State{font: font, args: stack[:0], width: font.def, seed: font.seed + uint64(gid), steps: &steps, output: &output}
if matrix != nil {
state.matrix, state.widthTarget, state.stripHints = matrix, &type2Font{}, true
}
if _, err := state.run(program, 0); err != nil {
return nil, false, fmt.Errorf("CFF glyph %d: %w", gid, err)
}
@@ -286,7 +286,11 @@ func (s *type2State) takeWidth(explicit bool) error {
s.args = s.args[:len(s.args)-1]
}
if s.output != nil && !s.component && s.widthTarget != nil && s.width != s.widthTarget.def {
return encodeType2Number(s.output, s.width-s.widthTarget.nominal)
width := s.width
if s.matrix != nil {
width *= s.matrix[0]
}
return encodeType2Number(s.output, width-s.widthTarget.nominal)
}
return nil
}
@@ -420,14 +424,19 @@ func (s *type2State) run(data []byte, depth int) (bool, error) {
if !finite(s.x) || !finite(s.y) {
return false, fmt.Errorf("invalid Type2 current point")
}
if s.component {
if s.matrix != nil {
x, y := s.transformedPoint()
if err := s.emit(21, []float64{x - s.outX, y - s.outY}); err != nil {
return false, err
}
} else if s.component {
if err := s.emit(21, []float64{s.x + s.originX - s.outX, s.y + s.originY - s.outY}); err != nil {
return false, err
}
} else if err := s.emit(op, s.args); err != nil {
return false, err
}
s.outX, s.outY = s.x+s.originX, s.y+s.originY
s.outX, s.outY = s.transformedPoint()
s.pathStarted = true
s.args = s.args[:0]
case 14:
@@ -498,7 +507,7 @@ func (s *type2State) appendComponent(code, x, y float64) error {
if !ok {
return fmt.Errorf("missing Type2 component %s", name)
}
child := type2State{font: s.font, 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}
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}
_, err := child.run(s.font.chars[gid], 0)
if err != nil {
return fmt.Errorf("invalid Type2 component %s: %w", name, err)