mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
This commit is contained in:
1 parent
60eb5ac6a1
commit
be697e7045
10 files changed
+508
-220
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-20261004082616-4fa2e8738fca
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004101230-2c0a061a6bb9
|
||||
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-20261004082616-4fa2e8738fca h1:9Q47dgBr5UXsOcXZ9X5xYO4xzxaJYldslT07WRiFCak=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261004082616-4fa2e8738fca/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ=
|
||||
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/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=
|
||||
|
||||
+223
-185
@@ -52,25 +52,17 @@ func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) {
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("CFF glyph widths: %w", err)
|
||||
}
|
||||
var unitsPerEm uint16 = 1000
|
||||
if len(cffData) > 4 {
|
||||
hdrSize := int(cffData[2])
|
||||
off := hdrSize
|
||||
_, sz := getCFFIndexCount(cffData, off)
|
||||
off += sz
|
||||
topDictData, _ := getCFFIndexData(cffData, off)
|
||||
if topDictData != nil {
|
||||
td := parseCFFDict(topDictData)
|
||||
if mat, ok := td[1207]; ok && len(mat) > 0 {
|
||||
if mat[0] != 0 {
|
||||
val := 1.0 / mat[0]
|
||||
if val > 0 {
|
||||
unitsPerEm = uint16(math.Round(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_, nameSize := getCFFIndexCount(cffData, int(cffData[2]))
|
||||
topData, _ := getCFFIndexData(cffData, int(cffData[2])+nameSize)
|
||||
top, err := readCFFDict(topData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
matrix, err := readCFFMatrix(top, [6]float64{0.001, 0, 0, 0.001, 0, 0})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
unitsPerEm := cffUnitsPerEm(matrix)
|
||||
tables := make(map[string][]byte)
|
||||
tables["CFF "] = cffData
|
||||
tables["head"] = buildHeadTable(unitsPerEm)
|
||||
@@ -89,6 +81,13 @@ func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) {
|
||||
// 入参: data CFF字体数据
|
||||
// 返回: []byte 标准化后的CFF数据, error 错误信息
|
||||
func normalizeCFFCharstrings(data []byte) ([]byte, error) {
|
||||
return normalizeCFFCharstringsAt(data, 0)
|
||||
}
|
||||
|
||||
// normalizeCFFCharstringsAt 统一CFF设计坐标,现有OpenType字体使用head表的设计单位
|
||||
// 入参: data CFF字体数据, unitsPerEm 设计单位,0时由字体矩阵确定
|
||||
// 返回: []byte 标准化后的CFF数据, error 错误信息
|
||||
func normalizeCFFCharstringsAt(data []byte, unitsPerEm uint16) ([]byte, error) {
|
||||
if len(data) < 4 || int(data[2]) >= len(data) {
|
||||
return nil, fmt.Errorf("invalid CFF header")
|
||||
}
|
||||
@@ -103,19 +102,44 @@ func normalizeCFFCharstrings(data []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
charOffset := dict[17]
|
||||
if len(charOffset) != 1 {
|
||||
return nil, fmt.Errorf("missing CFF charstrings")
|
||||
charStart, err := cffDictOffset(data, dict, 17)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
charStart := int(charOffset[0])
|
||||
_, charSize := getCFFIndexCount(data, charStart)
|
||||
if charStart < topStart+topSize || charSize == 0 || charStart+charSize > len(data) {
|
||||
return nil, fmt.Errorf("invalid CFF charstrings")
|
||||
}
|
||||
chars, changed, err := normalizeType2Programs(data)
|
||||
matrix, err := readCFFMatrix(dict, [6]float64{0.001, 0, 0, 0.001, 0, 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unitsPerEm == 0 {
|
||||
unitsPerEm = cffUnitsPerEm(matrix)
|
||||
}
|
||||
if unitsPerEm < 16 || unitsPerEm > 16384 {
|
||||
return nil, fmt.Errorf("invalid CFF design units")
|
||||
}
|
||||
var transform *[6]float64
|
||||
for i := range matrix {
|
||||
matrix[i] *= float64(unitsPerEm)
|
||||
}
|
||||
if matrix != [6]float64{1, 0, 0, 1, 0, 0} {
|
||||
transform = &matrix
|
||||
}
|
||||
chars, changed, err := normalizeType2ProgramsMatrix(data, transform)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if transform != nil {
|
||||
scale := 1 / float64(unitsPerEm)
|
||||
dict[1207] = []float64{scale, 0, 0, scale, 0, 0}
|
||||
delete(dict, 18)
|
||||
if err := transformCFFBounds(dict, matrix); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changed = true
|
||||
}
|
||||
var explicitCharset []byte
|
||||
if charset := dict[15]; len(charset) == 1 && (charset[0] == 1 || charset[0] == 2) {
|
||||
sids, _, _, _, ok := getCFFCharsetInfo(data, len(chars))
|
||||
@@ -185,109 +209,95 @@ func normalizeCFFCharstrings(data []byte) ([]byte, error) {
|
||||
// 入参: data 原始CFF数据
|
||||
// 返回: []byte 清洗后的CFF数据, error 错误信息
|
||||
func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
if len(data) < 4 {
|
||||
return nil, fmt.Errorf("data too short")
|
||||
if !isBareCFFData(data) {
|
||||
return nil, fmt.Errorf("invalid CFF header")
|
||||
}
|
||||
hdrSize := int(data[2])
|
||||
offset := hdrSize
|
||||
if offset >= len(data) {
|
||||
return nil, fmt.Errorf("truncated")
|
||||
names, end, err := readType2Index(data, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nameCount, nameSz := getCFFIndexCount(data, offset)
|
||||
if nameCount != 1 {
|
||||
if len(names) != 1 {
|
||||
return nil, fmt.Errorf("multi-font cff not supported")
|
||||
}
|
||||
nameIndexData := data[offset : offset+nameSz]
|
||||
offset += nameSz
|
||||
if offset >= len(data) {
|
||||
return nil, fmt.Errorf("truncated")
|
||||
nameIndexData := data[offset:end]
|
||||
tops, end, err := readType2Index(data, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topCount, topSz := getCFFIndexCount(data, offset)
|
||||
if topCount != 1 {
|
||||
if len(tops) != 1 {
|
||||
return nil, fmt.Errorf("top dict count != 1")
|
||||
}
|
||||
topDictData, _ := getCFFIndexData(data, offset)
|
||||
offset += topSz
|
||||
if offset >= len(data) {
|
||||
return nil, fmt.Errorf("truncated")
|
||||
_, stringEnd, err := readType2Index(data, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, strSz := getCFFIndexCount(data, offset)
|
||||
stringIndexData := data[offset : offset+strSz]
|
||||
offset += strSz
|
||||
if offset >= len(data) {
|
||||
return nil, fmt.Errorf("truncated")
|
||||
stringIndexData := data[end:stringEnd]
|
||||
_, globalEnd, err := readType2Index(data, stringEnd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, glbSz := getCFFIndexCount(data, offset)
|
||||
globalSubrIndexData := data[offset : offset+glbSz]
|
||||
topDict, err := readCFFDict(topDictData)
|
||||
globalSubrIndexData := data[stringEnd:globalEnd]
|
||||
topDict, err := readCFFDict(tops[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, isCID := topDict[1230]; !isCID {
|
||||
return data, nil
|
||||
}
|
||||
fdArrOffs, ok := topDict[1236]
|
||||
if !ok || len(fdArrOffs) == 0 {
|
||||
return nil, fmt.Errorf("cid without fdarray")
|
||||
}
|
||||
fdArrOff := int(fdArrOffs[0])
|
||||
if fdArrOff >= len(data) {
|
||||
return nil, fmt.Errorf("fdarray offset oob")
|
||||
}
|
||||
fdCount, _ := getCFFIndexCount(data, fdArrOff)
|
||||
if fdCount != 1 {
|
||||
return sanitizeMultiFDCFF(data, hdrSize, nameIndexData, topDict, stringIndexData, globalSubrIndexData, fdArrOff, fdCount)
|
||||
}
|
||||
fontDictData, _ := getCFFIndexData(data, fdArrOff)
|
||||
fontDict, err := readCFFDict(fontDictData)
|
||||
fdArrOff, err := cffDictOffset(data, topDict, 1236)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fdMat, ok := fontDict[1207]; ok && len(fdMat) == 6 {
|
||||
topMat, hasTop := topDict[1207]
|
||||
if !hasTop || len(topMat) != 6 {
|
||||
topMat = []float64{0.001, 0, 0, 0.001, 0, 0}
|
||||
}
|
||||
newMat := multiplyAffine(topMat, fdMat)
|
||||
topDict[1207] = newMat
|
||||
fdItems, _, err := readType2Index(data, fdArrOff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privVals, ok := fontDict[18]
|
||||
if !ok || len(privVals) != 2 {
|
||||
privVals = []float64{0, 0}
|
||||
fdCount := len(fdItems)
|
||||
if fdCount != 1 {
|
||||
return sanitizeMultiFDCFF(data, hdrSize, nameIndexData, topDict, stringIndexData, globalSubrIndexData, fdArrOff, fdCount)
|
||||
}
|
||||
fontDict, err := readCFFDict(fdItems[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fdMatrix, err := readCFFMatrix(fontDict, [6]float64{1, 0, 0, 1, 0, 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chars, charEnd, err := readType2Index(data, charStringsOff)
|
||||
if err != nil || len(chars) == 0 {
|
||||
return nil, fmt.Errorf("invalid CFF charstrings")
|
||||
}
|
||||
selectOffset, err := cffDictOffset(data, topDict, 1237)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := readType2FDSelect(data, selectOffset, len(chars), 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pDict, err := readType2Private(data, fontDict[18], &type2Font{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
privSize := int(privVals[0])
|
||||
privOff := int(privVals[1])
|
||||
var localSubrData []byte
|
||||
var privDictData []byte
|
||||
if privSize > 0 && privOff < len(data) && privOff+privSize <= len(data) {
|
||||
privDictData = data[privOff : privOff+privSize]
|
||||
}
|
||||
var subrsOffRel int
|
||||
if len(privDictData) > 0 {
|
||||
pDict, err := readCFFDict(privDictData)
|
||||
if values := pDict[19]; len(values) != 0 {
|
||||
start := int(fontDict[18][1]) + int(values[0])
|
||||
_, end, err := readType2Index(data, start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sVals, ok := pDict[19]; ok && len(sVals) > 0 {
|
||||
subrsOffRel = int(sVals[0])
|
||||
}
|
||||
localSubrData = data[start:end]
|
||||
}
|
||||
if subrsOffRel > 0 {
|
||||
subrsAbs := privOff + subrsOffRel
|
||||
if subrsAbs < len(data) {
|
||||
_, subSz := getCFFIndexCount(data, subrsAbs)
|
||||
if subrsAbs+subSz <= len(data) {
|
||||
localSubrData = data[subrsAbs : subrsAbs+subSz]
|
||||
}
|
||||
}
|
||||
}
|
||||
charStringsOffs, ok := topDict[17]
|
||||
if !ok || len(charStringsOffs) == 0 {
|
||||
return nil, fmt.Errorf("missing charstrings")
|
||||
}
|
||||
charStringsOff := int(charStringsOffs[0])
|
||||
_, charStrSz := getCFFIndexCount(data, charStringsOff)
|
||||
charStringsData := data[charStringsOff : charStringsOff+charStrSz]
|
||||
charStringsData := data[charStringsOff:charEnd]
|
||||
delete(topDict, 1230)
|
||||
delete(topDict, 1236)
|
||||
delete(topDict, 1237)
|
||||
@@ -295,11 +305,7 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
delete(topDict, 15)
|
||||
delete(topDict, 16)
|
||||
var finalPrivData []byte
|
||||
if len(privDictData) > 0 {
|
||||
pDict, err := readCFFDict(privDictData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pDict) > 0 {
|
||||
if err := normalizeCFFPrivate(pDict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -338,11 +344,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) {
|
||||
charStringsOffs, ok := topDict[17]
|
||||
if !ok || len(charStringsOffs) == 0 {
|
||||
return nil, fmt.Errorf("missing charstrings")
|
||||
charStringsOff, err := cffDictOffset(data, topDict, 17)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
charStringsOff := int(charStringsOffs[0])
|
||||
charStrings, _, err := readType2Index(data, charStringsOff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -371,16 +376,42 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
}
|
||||
fonts := make([]type2Font, fdCount)
|
||||
privateDicts := make([]cffDict, fdCount)
|
||||
matrices := make([][6]float64, fdCount)
|
||||
transformed := false
|
||||
topMatrix, err := readCFFMatrix(topDict, [6]float64{0.001, 0, 0, 0.001, 0, 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
units := cffUnitsPerEm(topMatrix)
|
||||
for i := range topMatrix {
|
||||
topMatrix[i] *= float64(units)
|
||||
}
|
||||
for fd, item := range fdItems {
|
||||
fonts[fd] = type2Font{data: data, chars: charStrings, globals: globalSubrs, seed: 1}
|
||||
fontDict, dictErr := readCFFDict(item)
|
||||
if dictErr != nil {
|
||||
return nil, dictErr
|
||||
}
|
||||
fdMatrix, matrixErr := readCFFMatrix(fontDict, [6]float64{1, 0, 0, 1, 0, 0})
|
||||
if matrixErr != nil {
|
||||
return nil, fmt.Errorf("CFF font dictionary %d: %w", fd, matrixErr)
|
||||
}
|
||||
matrices[fd], err = readCFFMatrix(cffDict{1207: multiplyAffine(topMatrix[:], fdMatrix[:])}, [6]float64{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CFF font dictionary %d: %w", fd, err)
|
||||
}
|
||||
transformed = transformed || matrices[fd] != [6]float64{1, 0, 0, 1, 0, 0}
|
||||
privateDicts[fd], err = readType2Private(data, fontDict[18], &fonts[fd])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CFF font dictionary %d: %w", fd, err)
|
||||
}
|
||||
if err := normalizeCFFPrivate(privateDicts[fd]); err != nil {
|
||||
return nil, fmt.Errorf("CFF font dictionary %d: %w", fd, err)
|
||||
}
|
||||
}
|
||||
target := &fonts[0]
|
||||
if transformed {
|
||||
target = &type2Font{}
|
||||
}
|
||||
inlined := make([][]byte, len(charStrings))
|
||||
var stack [48]float64
|
||||
@@ -388,7 +419,10 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
font := &fonts[fdSelect[gid]]
|
||||
var output bytes.Buffer
|
||||
steps := 0
|
||||
state := type2State{font: font, args: stack[:0], widthTarget: &fonts[0], width: font.def, seed: font.seed + uint64(gid), steps: &steps, output: &output, stripHints: true}
|
||||
state := type2State{font: font, args: stack[:0], widthTarget: target, width: font.def, seed: font.seed + uint64(gid), steps: &steps, output: &output, stripHints: true}
|
||||
if matrices[fdSelect[gid]] != [6]float64{1, 0, 0, 1, 0, 0} {
|
||||
state.matrix = &matrices[fdSelect[gid]]
|
||||
}
|
||||
if len(cs) == 0 {
|
||||
cs = []byte{14}
|
||||
}
|
||||
@@ -403,6 +437,13 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
delete(topDict, 1234)
|
||||
delete(topDict, 15)
|
||||
delete(topDict, 16)
|
||||
if transformed {
|
||||
scale := 1 / float64(units)
|
||||
topDict[1207] = []float64{scale, 0, 0, scale, 0, 0}
|
||||
if err := transformCFFBounds(topDict, topMatrix); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
privateDict := make(cffDict)
|
||||
if len(privateDicts) > 0 {
|
||||
for k, v := range privateDicts[0] {
|
||||
@@ -410,6 +451,9 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
}
|
||||
}
|
||||
delete(privateDict, 19)
|
||||
if transformed {
|
||||
privateDict = cffDict{20: {0}, 21: {0}}
|
||||
}
|
||||
if err := normalizeCFFPrivate(privateDict); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -449,8 +493,8 @@ func encodeSanitizedCFF(header, names []byte, dict cffDict, strings, globals, ch
|
||||
// 入参: data CFF数据
|
||||
// 返回: int 字形数量, error 错误信息
|
||||
func parseCFFAndCountGlyphs(data []byte) (int, error) {
|
||||
if len(data) < 4 {
|
||||
return 0, fmt.Errorf("data too short")
|
||||
if !isBareCFFData(data) {
|
||||
return 0, fmt.Errorf("invalid CFF header")
|
||||
}
|
||||
hdrSize := int(data[2])
|
||||
offset := hdrSize
|
||||
@@ -475,12 +519,13 @@ func parseCFFAndCountGlyphs(data []byte) (int, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if offsetVals, ok := dict[17]; ok && len(offsetVals) > 0 {
|
||||
charStrOff := int(offsetVals[0])
|
||||
if charStrOff > 0 && charStrOff < len(data) {
|
||||
count, _ := getCFFIndexCount(data, charStrOff)
|
||||
return count, nil
|
||||
}
|
||||
charStrOff, err := cffDictOffset(data, dict, 17)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
count, size := getCFFIndexCount(data, charStrOff)
|
||||
if count > 0 && size > 0 {
|
||||
return count, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("failed to parse top dict")
|
||||
@@ -527,7 +572,7 @@ func readCFFDict(data []byte) (cffDict, error) {
|
||||
op = 1200 + int(data[i])
|
||||
i++
|
||||
}
|
||||
if len(operands) == 0 {
|
||||
if len(operands) == 0 && !(op >= 6 && op <= 9 || op == 1212 || op == 1213) {
|
||||
return nil, fmt.Errorf("missing CFF dictionary operands")
|
||||
}
|
||||
dict[op] = operands
|
||||
@@ -640,6 +685,9 @@ func encodeCFFDict(dict cffDict) []byte {
|
||||
sort.Ints(keys)
|
||||
for _, op := range keys {
|
||||
vals := dict[op]
|
||||
if len(vals) == 0 && (op >= 6 && op <= 9 || op == 1212 || op == 1213) {
|
||||
continue
|
||||
}
|
||||
for _, val := range vals {
|
||||
encodeNumberCFF(buf, val)
|
||||
}
|
||||
@@ -656,7 +704,7 @@ func encodeCFFDict(dict cffDict) []byte {
|
||||
// encodeNumberCFF 编码单个数值到CFF格式
|
||||
// 入参: buf 缓冲区, val 数值
|
||||
func encodeNumberCFF(buf *bytes.Buffer, val float64) {
|
||||
if val == math.Trunc(val) {
|
||||
if val == math.Trunc(val) && val >= math.MinInt32 && val <= math.MaxInt32 {
|
||||
iv := int(val)
|
||||
if iv >= -107 && iv <= 107 {
|
||||
buf.WriteByte(byte(iv + 139))
|
||||
@@ -676,10 +724,11 @@ func encodeNumberCFF(buf *bytes.Buffer, val float64) {
|
||||
binary.Write(buf, binary.BigEndian, int32(iv))
|
||||
}
|
||||
} else {
|
||||
s := fmt.Sprintf("%g", val)
|
||||
s := strconv.FormatFloat(val, 'g', -1, 64)
|
||||
buf.WriteByte(30)
|
||||
var nibbles []byte
|
||||
for _, c := range s {
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
var n byte
|
||||
switch c {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
@@ -688,6 +737,12 @@ func encodeNumberCFF(buf *bytes.Buffer, val float64) {
|
||||
n = 0xA
|
||||
case 'E', 'e':
|
||||
n = 0xB
|
||||
if i+1 < len(s) && s[i+1] == '-' {
|
||||
n = 0xC
|
||||
i++
|
||||
} else if i+1 < len(s) && s[i+1] == '+' {
|
||||
i++
|
||||
}
|
||||
case '-':
|
||||
n = 0xE
|
||||
}
|
||||
@@ -756,95 +811,78 @@ func putOffset(buf *bytes.Buffer, val int, size int) {
|
||||
// 入参: data CFF数据, offset 偏移量
|
||||
// 返回: int 数量, int 索引结构总大小
|
||||
func getCFFIndexCount(data []byte, offset int) (int, int) {
|
||||
if offset+2 > len(data) {
|
||||
count, _, end, err := scanCFFIndex(data, offset)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
return count, end - offset
|
||||
}
|
||||
|
||||
// scanCFFIndex 无分配校验索引偏移、顺序和全部数据范围
|
||||
// 入参: data CFF数据, offset 索引偏移
|
||||
// 返回: int 项数, int 数据起点, int 索引终点, error 索引错误
|
||||
func scanCFFIndex(data []byte, offset int) (int, int, int, error) {
|
||||
if offset < 0 || offset > len(data)-2 {
|
||||
return 0, 0, 0, fmt.Errorf("truncated CFF index")
|
||||
}
|
||||
count := int(binary.BigEndian.Uint16(data[offset:]))
|
||||
if count == 0 {
|
||||
return 0, 2
|
||||
return 0, offset + 2, offset + 2, nil
|
||||
}
|
||||
if offset+3 > len(data) {
|
||||
return 0, 0
|
||||
if offset > len(data)-3 {
|
||||
return 0, 0, 0, fmt.Errorf("truncated CFF index header")
|
||||
}
|
||||
offSize := int(data[offset+2])
|
||||
if offSize < 1 || offSize > 4 {
|
||||
return 0, 0
|
||||
width := int(data[offset+2])
|
||||
if width < 1 || width > 4 || count+1 > (len(data)-offset-3)/width {
|
||||
return 0, 0, 0, fmt.Errorf("invalid CFF index offsets")
|
||||
}
|
||||
dataSizeLen := (count + 1) * offSize
|
||||
if offset+3+dataSizeLen > len(data) {
|
||||
return 0, 0
|
||||
base := offset + 3 + (count+1)*width
|
||||
previous := uint64(0)
|
||||
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 0, 0, 0, fmt.Errorf("invalid CFF index range")
|
||||
}
|
||||
previous = value
|
||||
}
|
||||
endOffsetPos := offset + 3 + count*offSize
|
||||
if endOffsetPos+offSize > len(data) {
|
||||
return 0, 0
|
||||
return count, base, base + int(previous) - 1, nil
|
||||
}
|
||||
|
||||
// cffDictOffset 读取必须为单个整数且位于字体数据内的字典偏移
|
||||
// 入参: data 字体数据, dict 字典, op 偏移操作符
|
||||
// 返回: int 偏移值, error 字典或范围错误
|
||||
func cffDictOffset(data []byte, dict cffDict, op int) (int, error) {
|
||||
values := dict[op]
|
||||
if len(values) != 1 || !finite(values[0]) || values[0] < 0 || values[0] >= float64(len(data)) || values[0] != math.Trunc(values[0]) {
|
||||
return 0, fmt.Errorf("invalid CFF dictionary offset %d", op)
|
||||
}
|
||||
dataEnd := readCFFOffset(data, endOffsetPos, offSize)
|
||||
if dataEnd < 1 {
|
||||
return 0, 0
|
||||
}
|
||||
return count, 3 + (count+1)*offSize + (dataEnd - 1)
|
||||
return int(values[0]), nil
|
||||
}
|
||||
|
||||
// getCFFIndexData 读取CFF索引的数据块
|
||||
// 入参: data CFF数据, offset 偏移量
|
||||
// 返回: []byte 索引数据(已去除offsets), int 索引结构总大小
|
||||
func getCFFIndexData(data []byte, offset int) ([]byte, int) {
|
||||
count, size := getCFFIndexCount(data, offset)
|
||||
if count == 0 {
|
||||
return nil, size
|
||||
count, base, end, err := scanCFFIndex(data, offset)
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
if offset+3 > len(data) {
|
||||
return nil, size
|
||||
if count == 0 {
|
||||
return nil, end - offset
|
||||
}
|
||||
offSize := int(data[offset+2])
|
||||
if offset+3+offSize > len(data) {
|
||||
return nil, size
|
||||
}
|
||||
off0 := readCFFOffset(data, offset+3, offSize)
|
||||
if offset+3+offSize*2 > len(data) {
|
||||
return nil, size
|
||||
}
|
||||
off1 := readCFFOffset(data, offset+3+offSize, offSize)
|
||||
dataStartRel := 3 + (count+1)*offSize
|
||||
dataStartAbs := offset + dataStartRel
|
||||
start := dataStartAbs + (off0 - 1)
|
||||
length := off1 - off0
|
||||
if start < 0 || length < 0 || start+length > len(data) {
|
||||
return nil, size
|
||||
}
|
||||
return data[start : start+length], size
|
||||
return data[base : base+off1-1], end - offset
|
||||
}
|
||||
|
||||
// readCFFIndexItems 读取CFF索引中的所有数据项
|
||||
// 入参: data CFF数据, offset 索引偏移
|
||||
// 返回: [][]byte 数据项列表
|
||||
func readCFFIndexItems(data []byte, offset int) [][]byte {
|
||||
count, _ := getCFFIndexCount(data, offset)
|
||||
if count == 0 || offset+3 > len(data) {
|
||||
return nil
|
||||
}
|
||||
offSize := int(data[offset+2])
|
||||
if offSize < 1 || offSize > 4 {
|
||||
return nil
|
||||
}
|
||||
dataStart := offset + 3 + (count+1)*offSize
|
||||
items := make([][]byte, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
p1 := offset + 3 + i*offSize
|
||||
p2 := p1 + offSize
|
||||
if p2+offSize > len(data) {
|
||||
return items
|
||||
}
|
||||
off1 := readCFFOffset(data, p1, offSize)
|
||||
off2 := readCFFOffset(data, p2, offSize)
|
||||
start := dataStart + off1 - 1
|
||||
length := off2 - off1
|
||||
if start < 0 || length < 0 || start+length > len(data) {
|
||||
items = append(items, nil)
|
||||
continue
|
||||
}
|
||||
items = append(items, data[start:start+length])
|
||||
}
|
||||
items, _, _ := readType2Index(data, offset)
|
||||
return items
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -57,7 +57,11 @@ func fixTrueType(data []byte, fixCmap, fixName bool) (bool, []byte, map[rune]uin
|
||||
if err != nil {
|
||||
return false, data, nil, false, err
|
||||
}
|
||||
normalized, err := normalizeCFFCharstrings(sanitized)
|
||||
var units uint16
|
||||
if head := existingTables["head"]; len(head) >= 20 {
|
||||
units = binary.BigEndian.Uint16(head[18:20])
|
||||
}
|
||||
normalized, err := normalizeCFFCharstringsAt(sanitized, units)
|
||||
if err != nil {
|
||||
return false, data, nil, false, err
|
||||
}
|
||||
|
||||
+5
-1
@@ -742,8 +742,12 @@ func encodeType2Number(target *bytes.Buffer, value float64) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fixed := math.Round(value * 65536)
|
||||
if fixed < math.MinInt32 || fixed > math.MaxInt32 {
|
||||
return fmt.Errorf("Type2 fixed operand out of range")
|
||||
}
|
||||
target.WriteByte(255)
|
||||
_ = binary.Write(target, binary.BigEndian, int32(math.Round(value*65536)))
|
||||
_ = binary.Write(target, binary.BigEndian, int32(fixed))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+36
-27
@@ -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)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
// readType2FDSelect 校验CID字体的逐字形FD归属和范围终点
|
||||
// 单FD子集缺少.notdef范围时使用唯一FD,不恢复其他缺失范围
|
||||
// 入参: data 字体数据, offset FDSelect偏移, count 字形数, fonts FD数量
|
||||
// 返回: []int FD索引, error 格式或范围错误
|
||||
func readType2FDSelect(data []byte, offset, count, fonts int) ([]int, error) {
|
||||
@@ -47,7 +48,8 @@ func readType2FDSelect(data []byte, offset, count, fonts int) ([]int, error) {
|
||||
if ranges == 0 || ranges > (len(data)-start-2)/3 {
|
||||
return nil, fmt.Errorf("invalid CFF FDSelect ranges")
|
||||
}
|
||||
if binary.BigEndian.Uint16(data[start:]) != 0 || int(binary.BigEndian.Uint16(data[start+3*ranges:])) != count {
|
||||
first := int(binary.BigEndian.Uint16(data[start:]))
|
||||
if first != 0 && !(first == 1 && fonts == 1) || int(binary.BigEndian.Uint16(data[start+3*ranges:])) != count {
|
||||
return nil, fmt.Errorf("invalid CFF FDSelect endpoints")
|
||||
}
|
||||
for index := 0; index < ranges; index++ {
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
// 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"
|
||||
"math"
|
||||
)
|
||||
|
||||
// readCFFMatrix 读取有限且可逆的字体矩阵,缺省时使用指定矩阵
|
||||
// 入参: dict 字体字典, fallback 缺省矩阵
|
||||
// 返回: [6]float64 字体矩阵, error 矩阵错误
|
||||
func readCFFMatrix(dict cffDict, fallback [6]float64) ([6]float64, error) {
|
||||
values, ok := dict[1207]
|
||||
if !ok {
|
||||
return fallback, nil
|
||||
}
|
||||
if len(values) != 6 {
|
||||
return [6]float64{}, fmt.Errorf("invalid CFF FontMatrix length")
|
||||
}
|
||||
for _, value := range values {
|
||||
if !finite(value) {
|
||||
return [6]float64{}, fmt.Errorf("invalid CFF FontMatrix value")
|
||||
}
|
||||
}
|
||||
determinant := values[0]*values[3] - values[1]*values[2]
|
||||
if !finite(determinant) || determinant == 0 {
|
||||
return [6]float64{}, fmt.Errorf("singular CFF FontMatrix")
|
||||
}
|
||||
return [6]float64(values), nil
|
||||
}
|
||||
|
||||
// cffUnitsPerEm 保留有效的等比设计单位,其他矩阵使用1000单位封装
|
||||
// 入参: matrix 字体矩阵
|
||||
// 返回: uint16 OpenType设计单位
|
||||
func cffUnitsPerEm(matrix [6]float64) uint16 {
|
||||
if matrix[0] > 0 && matrix[0] == matrix[3] && matrix[1] == 0 && matrix[2] == 0 {
|
||||
units := 1 / matrix[0]
|
||||
if units >= 16 && units <= 16384 && math.Abs(units-math.Round(units)) < 1e-8 {
|
||||
return uint16(math.Round(units))
|
||||
}
|
||||
}
|
||||
return 1000
|
||||
}
|
||||
|
||||
// transformCFFBounds 按变换后的四角更新字体整体边界
|
||||
// 入参: dict 顶层字典, matrix 设计坐标变换
|
||||
// 返回: error 边界或变换溢出错误
|
||||
func transformCFFBounds(dict cffDict, matrix [6]float64) error {
|
||||
box := dict[5]
|
||||
if box == nil {
|
||||
return nil
|
||||
}
|
||||
if len(box) != 4 {
|
||||
return fmt.Errorf("invalid CFF font bounds")
|
||||
}
|
||||
left, bottom, right, top := math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)
|
||||
for _, x := range []float64{box[0], box[2]} {
|
||||
for _, y := range []float64{box[1], box[3]} {
|
||||
tx, ty := matrix[0]*x+matrix[2]*y+matrix[4], matrix[1]*x+matrix[3]*y+matrix[5]
|
||||
if !finite(tx) || !finite(ty) {
|
||||
return fmt.Errorf("invalid CFF transformed bounds")
|
||||
}
|
||||
left, bottom, right, top = min(left, tx), min(bottom, ty), max(right, tx), max(top, ty)
|
||||
}
|
||||
}
|
||||
dict[5] = []float64{left, bottom, right, top}
|
||||
return nil
|
||||
}
|
||||
|
||||
// transformedPoint 将字形当前位置与组件原点变换到输出坐标
|
||||
// 返回: float64 横坐标, float64 纵坐标
|
||||
func (s *type2State) transformedPoint() (float64, float64) {
|
||||
x, y := s.x+s.originX, s.y+s.originY
|
||||
if s.matrix == nil {
|
||||
return x, y
|
||||
}
|
||||
m := s.matrix
|
||||
return m[0]*x + m[2]*y + m[4], m[1]*x + m[3]*y + m[5]
|
||||
}
|
||||
|
||||
// emitTransformedPath 展开紧凑路径指令并保留变换后的三次贝塞尔控制点
|
||||
// 入参: op 已校验的路径指令, a 操作数
|
||||
// 返回: error 输出编码错误
|
||||
func (s *type2State) emitTransformedPath(op int, a []float64) error {
|
||||
emit := func(op int, values ...float64) error {
|
||||
m := s.matrix
|
||||
var transformed [12]float64
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
x, y := values[i], values[i+1]
|
||||
transformed[i], transformed[i+1] = m[0]*x+m[2]*y, m[1]*x+m[3]*y
|
||||
}
|
||||
return s.emit(op, transformed[:len(values)])
|
||||
}
|
||||
switch op {
|
||||
case 5:
|
||||
for i := 0; i < len(a); i += 2 {
|
||||
if err := emit(5, a[i], a[i+1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case 6, 7:
|
||||
for i, value := range a {
|
||||
x, y := 0.0, 0.0
|
||||
if (i%2 == 0) == (op == 6) {
|
||||
x = value
|
||||
} else {
|
||||
y = value
|
||||
}
|
||||
if err := emit(5, x, y); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case 8, 24, 25:
|
||||
start, end := 0, len(a)
|
||||
if op == 24 {
|
||||
end -= 2
|
||||
} else if op == 25 {
|
||||
start = len(a) - 6
|
||||
}
|
||||
for i := 0; i < start; i += 2 {
|
||||
if err := emit(5, a[i], a[i+1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := start; i < end; i += 6 {
|
||||
if err := emit(8, a[i:i+6]...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if end < len(a) {
|
||||
return emit(5, a[end], a[end+1])
|
||||
}
|
||||
case 26, 27:
|
||||
first := 0.0
|
||||
if len(a)%4 == 1 {
|
||||
first, a = a[0], a[1:]
|
||||
}
|
||||
for i := 0; i < len(a); i += 4 {
|
||||
var err error
|
||||
if op == 26 {
|
||||
err = emit(8, first, a[i], a[i+1], a[i+2], 0, a[i+3])
|
||||
} else {
|
||||
err = emit(8, a[i], first, a[i+1], a[i+2], a[i+3], 0)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
first = 0
|
||||
}
|
||||
case 30, 31:
|
||||
horizontal := op == 31
|
||||
for i := 0; i+4 <= len(a); i += 4 {
|
||||
last := 0.0
|
||||
if i+5 == len(a) {
|
||||
last = a[i+4]
|
||||
}
|
||||
var err error
|
||||
if horizontal {
|
||||
err = emit(8, a[i], 0, a[i+1], a[i+2], last, a[i+3])
|
||||
} else {
|
||||
err = emit(8, 0, a[i], a[i+1], a[i+2], a[i+3], last)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
horizontal = !horizontal
|
||||
}
|
||||
case 1234:
|
||||
if err := emit(8, a[0], 0, a[1], a[2], a[3], 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return emit(8, a[4], 0, a[5], -a[2], a[6], 0)
|
||||
case 1235:
|
||||
if err := emit(8, a[:6]...); err != nil {
|
||||
return err
|
||||
}
|
||||
return emit(8, a[6:12]...)
|
||||
case 1236:
|
||||
if err := emit(8, a[0], a[1], a[2], a[3], a[4], 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return emit(8, a[5], 0, a[6], a[7], a[8], -a[1]-a[3]-a[7])
|
||||
case 1237:
|
||||
x, y := 0.0, 0.0
|
||||
for i := 0; i < 10; i += 2 {
|
||||
x, y = x+a[i], y+a[i+1]
|
||||
}
|
||||
lastX, lastY := -x, a[10]
|
||||
if math.Abs(x) > math.Abs(y) {
|
||||
lastX, lastY = a[10], -y
|
||||
}
|
||||
if err := emit(8, a[:6]...); err != nil {
|
||||
return err
|
||||
}
|
||||
return emit(8, a[6], a[7], a[8], a[9], lastX, lastY)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -258,11 +258,17 @@ func (s *type2State) draw(op int) error {
|
||||
if !valid || !finite(s.x+dx) || !finite(s.y+dy) {
|
||||
return fmt.Errorf("invalid Type2 path operands for operator %d", op)
|
||||
}
|
||||
if err := s.emit(op, a); err != nil {
|
||||
var err error
|
||||
if s.matrix != nil {
|
||||
err = s.emitTransformedPath(op, a)
|
||||
} else {
|
||||
err = s.emit(op, a)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.x, s.y = s.x+dx, s.y+dy
|
||||
s.outX, s.outY = s.x+s.originX, s.y+s.originY
|
||||
s.outX, s.outY = s.transformedPoint()
|
||||
s.args = s.args[:0]
|
||||
return nil
|
||||
}
|
||||
@@ -171,6 +171,20 @@ func pdfFontProgram(source *pdfgo.Font, type1 *type1Program) ([]byte, uint16, er
|
||||
return nil, 0, fmt.Errorf("embedded PDF font has no glyphs")
|
||||
}
|
||||
changed := false
|
||||
if cff := tables["CFF "]; len(cff) != 0 {
|
||||
sanitized, err := sanitizeCFF(cff)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
normalized, err := normalizeCFFCharstringsAt(sanitized, binary.BigEndian.Uint16(tables["head"][18:20]))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if !bytes.Equal(normalized, cff) {
|
||||
tables["CFF "] = normalized
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
var repairedLimit uint16
|
||||
metrics := int(binary.BigEndian.Uint16(tables["hhea"][34:36]))
|
||||
if metrics == 0 || metrics > int(count) {
|
||||
|
||||
Reference in new issue
Block a user