mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
1 parent
be697e7045
commit
58ec89ba23
5 files changed
+244
-18
No files matched your search
@@ -10,7 +10,7 @@ require (
|
||||
github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c
|
||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||
github.com/xiaoqidun/jbig2 v1.1.1
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004101230-2c0a061a6bb9
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004113655-d823d12ace54
|
||||
golang.org/x/crypto v0.57.0
|
||||
golang.org/x/image v0.46.0
|
||||
golang.org/x/text v0.42.0
|
||||
|
||||
@@ -62,8 +62,8 @@ github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI=
|
||||
github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004101230-2c0a061a6bb9 h1:bIYVlgg5J18Az+lIMWJk1FgyitZMVMkF7zj5+LGkQoc=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004101230-2c0a061a6bb9/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004113655-d823d12ace54 h1:j87SNncDFLPpUYwZb9HU6AZXMb+C4lQipQb5U6V4FAk=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004113655-d823d12ace54/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||
|
||||
+42
-4
@@ -155,8 +155,17 @@ func normalizeCFFCharstringsAt(data []byte, unitsPerEm uint16) ([]byte, error) {
|
||||
if !changed {
|
||||
return data, nil
|
||||
}
|
||||
return replaceCFFCharstrings(data, dict, topStart, topSize, charStart, charSize, chars, explicitCharset), nil
|
||||
}
|
||||
|
||||
// replaceCFFCharstrings 替换字形索引并调整后续数据及顶层字典的偏移
|
||||
// 入参: data 原始字体, dict 输出字典, topStart 顶层索引起点, topSize 顶层索引大小
|
||||
// 入参: charStart 字形索引起点, charSize 字形索引大小, chars 字形程序, explicitCharset 追加字符集
|
||||
// 返回: []byte 重建后的字体数据
|
||||
func replaceCFFCharstrings(data []byte, dict cffDict, topStart, topSize, charStart, charSize int, chars [][]byte, explicitCharset []byte) []byte {
|
||||
encodedChars := encodeCFFIndex(chars)
|
||||
topEnd := topStart + topSize
|
||||
topData, _ := getCFFIndexData(data, topStart)
|
||||
originalDict := parseCFFDict(topData)
|
||||
newTop := encodeCFFIndex([][]byte{encodeCFFDict(dict)})
|
||||
for range 6 {
|
||||
@@ -179,7 +188,7 @@ func normalizeCFFCharstringsAt(data []byte, unitsPerEm uint16) ([]byte, error) {
|
||||
dict[op] = []float64{float64(offset + shift)}
|
||||
}
|
||||
}
|
||||
if values := dict[18]; len(values) == 2 {
|
||||
if values := dict[18]; len(values) == 2 && values[0] != 0 {
|
||||
original := originalDict[18]
|
||||
offset := int(original[1])
|
||||
if offset >= charStart+charSize {
|
||||
@@ -202,7 +211,7 @@ func normalizeCFFCharstringsAt(data []byte, unitsPerEm uint16) ([]byte, error) {
|
||||
result = append(result, encodedChars...)
|
||||
result = append(result, data[charStart+charSize:]...)
|
||||
result = append(result, explicitCharset...)
|
||||
return result, nil
|
||||
return result
|
||||
}
|
||||
|
||||
// sanitizeCFF 尝试清洗CFF数据,转换CID字体并合并FontMatrix
|
||||
@@ -243,7 +252,14 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kind, err := cffCharstringType(topDict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, isCID := topDict[1230]; !isCID {
|
||||
if kind == 1 {
|
||||
return normalizeCFFType1(data, topDict)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
fdArrOff, err := cffDictOffset(data, topDict, 1236)
|
||||
@@ -266,7 +282,7 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fdMatrix != [6]float64{1, 0, 0, 1, 0, 0} {
|
||||
if kind == 1 || fdMatrix != [6]float64{1, 0, 0, 1, 0, 0} {
|
||||
return sanitizeMultiFDCFF(data, hdrSize, nameIndexData, topDict, stringIndexData, globalSubrIndexData, fdArrOff, fdCount)
|
||||
}
|
||||
charStringsOff, err := cffDictOffset(data, topDict, 17)
|
||||
@@ -344,6 +360,10 @@ func normalizeCFFPrivate(dict cffDict) error {
|
||||
// 入参: stringIndexData 字符串索引, globalSubrIndexData 全局子程序索引, fdArrOff FDArray偏移, fdCount FD数量
|
||||
// 返回: []byte 清洗后的CFF数据, error 错误信息
|
||||
func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict cffDict, stringIndexData []byte, globalSubrIndexData []byte, fdArrOff int, fdCount int) ([]byte, error) {
|
||||
kind, err := cffCharstringType(topDict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
charStringsOff, err := cffDictOffset(data, topDict, 17)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -375,6 +395,10 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
return nil, fmt.Errorf("invalid CFF FDArray count")
|
||||
}
|
||||
fonts := make([]type2Font, fdCount)
|
||||
var type1Fonts []*type1Program
|
||||
if kind == 1 {
|
||||
type1Fonts = make([]*type1Program, fdCount)
|
||||
}
|
||||
privateDicts := make([]cffDict, fdCount)
|
||||
matrices := make([][6]float64, fdCount)
|
||||
transformed := false
|
||||
@@ -408,6 +432,13 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
if err := normalizeCFFPrivate(privateDicts[fd]); err != nil {
|
||||
return nil, fmt.Errorf("CFF font dictionary %d: %w", fd, err)
|
||||
}
|
||||
if kind == 1 {
|
||||
type1Fonts[fd], err = readCFFType1Program(data, charStrings, fontDict[18], true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CFF font dictionary %d: %w", fd, err)
|
||||
}
|
||||
fonts[fd].def, fonts[fd].nominal = 0, 0
|
||||
}
|
||||
}
|
||||
target := &fonts[0]
|
||||
if transformed {
|
||||
@@ -417,6 +448,12 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
var stack [48]float64
|
||||
for gid, cs := range charStrings {
|
||||
font := &fonts[fdSelect[gid]]
|
||||
if kind == 1 {
|
||||
cs, err = type1Fonts[fdSelect[gid]].outline(type1Glyph{name: fmt.Sprintf("gid%d", gid), data: cs})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var output bytes.Buffer
|
||||
steps := 0
|
||||
state := type2State{font: font, args: stack[:0], widthTarget: target, width: font.def, seed: font.seed + uint64(gid), steps: &steps, output: &output, stripHints: true}
|
||||
@@ -437,6 +474,7 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
delete(topDict, 1234)
|
||||
delete(topDict, 15)
|
||||
delete(topDict, 16)
|
||||
delete(topDict, 1206)
|
||||
if transformed {
|
||||
scale := 1 / float64(units)
|
||||
topDict[1207] = []float64{scale, 0, 0, scale, 0, 0}
|
||||
@@ -451,7 +489,7 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
}
|
||||
}
|
||||
delete(privateDict, 19)
|
||||
if transformed {
|
||||
if transformed || kind == 1 {
|
||||
privateDict = cffDict{20: {0}, 21: {0}}
|
||||
}
|
||||
if err := normalizeCFFPrivate(privateDict); err != nil {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2025-2026 肖其顿 (XIAO QI DUN)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ofdgo
|
||||
|
||||
import "fmt"
|
||||
|
||||
// cffCharstringType 读取CFF字形程序类型,缺省为Type2
|
||||
// 入参: dict 顶层字典
|
||||
// 返回: int 字形程序类型, error 不支持的类型
|
||||
func cffCharstringType(dict cffDict) (int, error) {
|
||||
values, ok := dict[1206]
|
||||
if !ok {
|
||||
return 2, nil
|
||||
}
|
||||
if len(values) != 1 || values[0] != 1 && values[0] != 2 {
|
||||
return 0, fmt.Errorf("unsupported CFF charstring type")
|
||||
}
|
||||
return int(values[0]), nil
|
||||
}
|
||||
|
||||
// readCFFType1Program 读取Type1字形及无偏置局部子程序,保留源字形顺序
|
||||
// 入参: data CFF字体, chars 字形程序, private 私有字典范围, cid 是否为CID字体
|
||||
// 返回: *type1Program 执行资源, error 字符集或私有字典错误
|
||||
func readCFFType1Program(data []byte, chars [][]byte, private []float64, cid bool) (*type1Program, error) {
|
||||
resources := &type2Font{}
|
||||
if _, err := readType2Private(data, private, resources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
program := &type1Program{subrs: resources.locals, lenIV: -1}
|
||||
if cid {
|
||||
return program, nil
|
||||
}
|
||||
identifiers, _, _, stringsOffset, ok := getCFFCharsetInfo(data, len(chars))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid CFF Type1 charset")
|
||||
}
|
||||
strings, _, err := readType2Index(data, stringsOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
program.glyphs = make([]type1Glyph, len(chars))
|
||||
for gid, charstring := range chars {
|
||||
name := ".notdef"
|
||||
if gid != 0 {
|
||||
sid := identifiers[gid]
|
||||
if sid < len(cffStandardStrings) {
|
||||
name = cffStandardStrings[sid]
|
||||
} else if sid-len(cffStandardStrings) < len(strings) {
|
||||
name = string(strings[sid-len(cffStandardStrings)])
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid CFF Type1 glyph name")
|
||||
}
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("invalid CFF Type1 glyph name")
|
||||
}
|
||||
}
|
||||
program.glyphs[gid] = type1Glyph{name: name, data: charstring}
|
||||
}
|
||||
return program, nil
|
||||
}
|
||||
|
||||
// normalizeCFFType1 转换非CID字体的Type1程序,保留字符集、编码及字形编号
|
||||
// 入参: data CFF字体, dict 顶层字典
|
||||
// 返回: []byte Type2格式CFF, error 字形程序或范围错误
|
||||
func normalizeCFFType1(data []byte, dict cffDict) ([]byte, error) {
|
||||
_, nameSize := getCFFIndexCount(data, int(data[2]))
|
||||
topStart := int(data[2]) + nameSize
|
||||
_, topSize := getCFFIndexCount(data, topStart)
|
||||
charStart, err := cffDictOffset(data, dict, 17)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chars, charEnd, err := readType2Index(data, charStart)
|
||||
if err != nil || len(chars) == 0 || charStart < topStart+topSize {
|
||||
return nil, fmt.Errorf("invalid CFF Type1 charstrings")
|
||||
}
|
||||
program, err := readCFFType1Program(data, chars, dict[18], false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
converted := make([][]byte, len(chars))
|
||||
for gid, glyph := range program.glyphs {
|
||||
converted[gid], err = program.outline(glyph)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
dict[1206], dict[18] = []float64{2}, []float64{0, 0}
|
||||
return replaceCFFCharstrings(data, dict, topStart, topSize, charStart, charEnd-charStart, converted, nil), nil
|
||||
}
|
||||
+97
-11
@@ -46,6 +46,7 @@ type type1Program struct {
|
||||
glyphs []type1Glyph
|
||||
subrs [][]byte
|
||||
lenIV int
|
||||
matrix []float64
|
||||
}
|
||||
|
||||
// type1Glyph 保存字形名称及指令
|
||||
@@ -63,6 +64,7 @@ type type1Scanner struct {
|
||||
// type1Outline 保存字形指令的操作数栈及轮廓位置
|
||||
type type1Outline struct {
|
||||
program *type1Program
|
||||
steps *int
|
||||
active map[string]bool
|
||||
args []float64
|
||||
postscript []float64
|
||||
@@ -91,12 +93,10 @@ func parseType1Program(data []byte) (type1Program, error) {
|
||||
if err != nil {
|
||||
return type1Program{}, err
|
||||
}
|
||||
marker := []byte("eexec")
|
||||
start := bytes.Index(data, marker)
|
||||
if start < 0 {
|
||||
return type1Program{}, fmt.Errorf("Type1 font lacks eexec data")
|
||||
matrix, start, err := readType1Header(data)
|
||||
if err != nil {
|
||||
return type1Program{}, err
|
||||
}
|
||||
start += len(marker)
|
||||
for start < len(data) && type1Space(data[start]) {
|
||||
start++
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func parseType1Program(data []byte) (type1Program, error) {
|
||||
}
|
||||
decrypted := type1Decrypt(encrypted, 55665)[4:]
|
||||
scanner := type1Scanner{data: decrypted}
|
||||
program := type1Program{lenIV: 4}
|
||||
program := type1Program{lenIV: 4, matrix: matrix}
|
||||
section := ""
|
||||
for {
|
||||
token := scanner.next()
|
||||
@@ -212,6 +212,46 @@ func parseType1Program(data []byte) (type1Program, error) {
|
||||
return program, nil
|
||||
}
|
||||
|
||||
// readType1Header 读取明文字体矩阵及加密数据起点
|
||||
// 入参: data Type1程序
|
||||
// 返回: []float64 字体矩阵, int 加密起点, error 头部错误
|
||||
func readType1Header(data []byte) ([]float64, int, error) {
|
||||
scanner := type1Scanner{data: data}
|
||||
var matrix []float64
|
||||
for {
|
||||
switch scanner.next() {
|
||||
case "":
|
||||
return nil, 0, fmt.Errorf("Type1 font lacks eexec data")
|
||||
case "eexec":
|
||||
return matrix, scanner.pos, nil
|
||||
case "/FontMatrix":
|
||||
closing := ""
|
||||
switch scanner.next() {
|
||||
case "[":
|
||||
closing = "]"
|
||||
case "{":
|
||||
closing = "}"
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("invalid Type1 FontMatrix array")
|
||||
}
|
||||
matrix = make([]float64, 6)
|
||||
for index := range matrix {
|
||||
value, err := strconv.ParseFloat(scanner.next(), 64)
|
||||
if err != nil || !finite(value) {
|
||||
return nil, 0, fmt.Errorf("invalid Type1 FontMatrix value")
|
||||
}
|
||||
matrix[index] = value
|
||||
}
|
||||
if scanner.next() != closing {
|
||||
return nil, 0, fmt.Errorf("invalid Type1 FontMatrix length")
|
||||
}
|
||||
if _, err := readCFFMatrix(cffDict{1207: matrix}, [6]float64{}); err != nil {
|
||||
return nil, 0, fmt.Errorf("invalid Type1 FontMatrix: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flattenType1 将PFB分段容器转换为连续的Type1程序
|
||||
// 入参: data 字体数据
|
||||
// 返回: []byte 连续程序, error 错误信息
|
||||
@@ -312,11 +352,41 @@ func (s *type1Scanner) next() string {
|
||||
return ""
|
||||
}
|
||||
start := s.pos
|
||||
if bytes.IndexByte([]byte("[]{}"), s.data[s.pos]) >= 0 {
|
||||
if s.data[s.pos] == '(' {
|
||||
depth := 1
|
||||
s.pos++
|
||||
for s.pos < len(s.data) && depth > 0 {
|
||||
switch s.data[s.pos] {
|
||||
case '\\':
|
||||
if s.pos+1 < len(s.data) {
|
||||
s.pos++
|
||||
}
|
||||
case '(':
|
||||
depth++
|
||||
case ')':
|
||||
depth--
|
||||
}
|
||||
s.pos++
|
||||
}
|
||||
return string(s.data[start:s.pos])
|
||||
}
|
||||
if s.data[s.pos] == '<' && (s.pos+1 == len(s.data) || s.data[s.pos+1] != '<') {
|
||||
for s.pos < len(s.data) && s.data[s.pos] != '>' {
|
||||
s.pos++
|
||||
}
|
||||
if s.pos < len(s.data) {
|
||||
s.pos++
|
||||
}
|
||||
return string(s.data[start:s.pos])
|
||||
}
|
||||
if bytes.IndexByte([]byte("[]{}()<>"), s.data[s.pos]) >= 0 {
|
||||
s.pos++
|
||||
return string(s.data[start:s.pos])
|
||||
}
|
||||
for s.pos < len(s.data) && !type1Space(s.data[s.pos]) && bytes.IndexByte([]byte("[]{}%"), s.data[s.pos]) < 0 {
|
||||
if s.data[s.pos] == '/' {
|
||||
s.pos++
|
||||
}
|
||||
for s.pos < len(s.data) && !type1Space(s.data[s.pos]) && bytes.IndexByte([]byte("[]{}%()/<>"), s.data[s.pos]) < 0 {
|
||||
s.pos++
|
||||
}
|
||||
return string(s.data[start:s.pos])
|
||||
@@ -375,7 +445,14 @@ func (s *type1Outline) run(data []byte, depth int) (bool, error) {
|
||||
if depth > 32 {
|
||||
return false, fmt.Errorf("Type1 subroutine recursion")
|
||||
}
|
||||
if s.steps == nil {
|
||||
s.steps = new(int)
|
||||
}
|
||||
for index := 0; index < len(data); index++ {
|
||||
(*s.steps)++
|
||||
if *s.steps > 1<<20 {
|
||||
return false, fmt.Errorf("Type1 execution limit exceeded")
|
||||
}
|
||||
op := int(data[index])
|
||||
if op >= 32 {
|
||||
var value float64
|
||||
@@ -401,6 +478,9 @@ func (s *type1Outline) run(data []byte, depth int) (bool, error) {
|
||||
value = float64(int32(binary.BigEndian.Uint32(data[index+1 : index+5])))
|
||||
index += 4
|
||||
}
|
||||
if len(s.args) >= 24 {
|
||||
return false, fmt.Errorf("Type1 operand limit exceeded")
|
||||
}
|
||||
s.args = append(s.args, value)
|
||||
continue
|
||||
}
|
||||
@@ -443,7 +523,7 @@ func (s *type1Outline) run(data []byte, depth int) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
return false, fmt.Errorf("unterminated Type1 charstring")
|
||||
}
|
||||
|
||||
// operator 执行单个Type1绘制指令并清空已消费的操作数
|
||||
@@ -601,7 +681,7 @@ func (s *type1Outline) operator(op int) error {
|
||||
return fmt.Errorf("unsupported Type1 OtherSubr %d", number)
|
||||
}
|
||||
case 1217:
|
||||
if len(s.postscript) == 0 {
|
||||
if len(s.postscript) == 0 || len(a) >= 24 {
|
||||
return fmt.Errorf("invalid Type1 pop")
|
||||
}
|
||||
s.args = append(a, s.postscript[0])
|
||||
@@ -629,6 +709,9 @@ func (s *type1Outline) operator(op int) error {
|
||||
// 入参: code 标准编码, x 横向原点, y 纵向原点
|
||||
// 返回: error 字形缺失、循环或指令错误
|
||||
func (s *type1Outline) component(code, x, y float64) error {
|
||||
if len(s.active) >= 32 {
|
||||
return fmt.Errorf("Type1 component recursion")
|
||||
}
|
||||
if code < 0 || code > 255 || code != math.Trunc(code) {
|
||||
return fmt.Errorf("invalid Type1 component code")
|
||||
}
|
||||
@@ -643,7 +726,7 @@ func (s *type1Outline) component(code, x, y float64) error {
|
||||
if glyph.name != name {
|
||||
continue
|
||||
}
|
||||
child := type1Outline{program: s.program, active: s.active, originX: x, originY: y, pointX: s.pointX, pointY: s.pointY}
|
||||
child := type1Outline{program: s.program, steps: s.steps, active: s.active, originX: x, originY: y, pointX: s.pointX, pointY: s.pointY}
|
||||
s.active[name] = true
|
||||
ended, err := child.run(glyph.data, 0)
|
||||
delete(s.active, name)
|
||||
@@ -798,6 +881,9 @@ func (p *type1Program) toCFF(name string) ([]byte, error) {
|
||||
charIndex := encodeCFFIndex(chars)
|
||||
topDict := make([]byte, 12)
|
||||
topDict[0], topDict[5], topDict[6], topDict[11] = 29, 15, 29, 17
|
||||
if p.matrix != nil {
|
||||
topDict = append(topDict, encodeCFFDict(cffDict{1207: p.matrix})...)
|
||||
}
|
||||
topIndex := encodeCFFIndex([][]byte{topDict})
|
||||
charsetOffset := 4 + len(nameIndex) + len(topIndex) + len(stringIndex) + len(globalSubrs)
|
||||
charOffset := charsetOffset + len(charset)
|
||||
|
||||
Reference in new issue
Block a user