mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 15:06:46 +08:00
This commit is contained in:
1 parent
b4d566019d
commit
201082daa0
9 files changed
+434
-16
No files matched your search
@@ -198,6 +198,9 @@ func ParseType1Program(data []byte) (Type1Program, error) {
|
||||
return Type1Program{}, fmt.Errorf("Type1 font has no glyphs")
|
||||
}
|
||||
for index := range program.subrs {
|
||||
if program.subrs[index] == nil {
|
||||
continue
|
||||
}
|
||||
program.subrs[index], err = type1CharString(program.subrs[index], program.lenIV)
|
||||
if err != nil {
|
||||
return Type1Program{}, err
|
||||
@@ -212,6 +215,61 @@ func ParseType1Program(data []byte) (Type1Program, error) {
|
||||
return program, nil
|
||||
}
|
||||
|
||||
// NormalizePDFType1 修复完整加密分段之后的PFB明文尾部,不补齐加密或字形数据
|
||||
// 入参: data 原始PFB程序
|
||||
// 返回: []byte 原程序或独立修复容器, bool 是否修复, error 容器或程序错误
|
||||
func NormalizePDFType1(data []byte) ([]byte, bool, error) {
|
||||
if len(data) < 6 || data[0] != 0x80 {
|
||||
return data, false, nil
|
||||
}
|
||||
binarySeen := false
|
||||
for offset := 0; offset+2 <= len(data) && data[offset] == 0x80; {
|
||||
kind := data[offset+1]
|
||||
if kind == 3 {
|
||||
return data, false, nil
|
||||
}
|
||||
if offset+6 > len(data) || kind != 1 && kind != 2 {
|
||||
return data, false, fmt.Errorf("incomplete PFB segment header")
|
||||
}
|
||||
length := uint64(binary.LittleEndian.Uint32(data[offset+2:]))
|
||||
remaining := len(data) - offset - 6
|
||||
if binarySeen && kind == 1 && remaining == 0 {
|
||||
result := append(bytes.Clone(data[:offset]), 0x80, 3)
|
||||
if _, err := ParseType1Program(result); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return result, true, nil
|
||||
}
|
||||
if length > uint64(remaining) {
|
||||
return nil, false, fmt.Errorf("incomplete PFB encrypted or text segment")
|
||||
}
|
||||
if binarySeen && kind == 1 && length == 0 {
|
||||
tail := data[offset+6:]
|
||||
if len(tail) < 2 || !bytes.Equal(tail[len(tail)-2:], []byte{0x80, 3}) {
|
||||
return data, false, fmt.Errorf("unbounded PFB text trailer")
|
||||
}
|
||||
text := bytes.TrimSpace(tail[:len(tail)-2])
|
||||
if !bytes.HasSuffix(text, []byte("cleartomark")) {
|
||||
return data, false, fmt.Errorf("PFB trailer lacks cleartomark")
|
||||
}
|
||||
for _, b := range bytes.TrimSuffix(text, []byte("cleartomark")) {
|
||||
if b != '0' && !type1Space(b) {
|
||||
return data, false, fmt.Errorf("unexpected PFB text trailer")
|
||||
}
|
||||
}
|
||||
result := bytes.Clone(data)
|
||||
binary.LittleEndian.PutUint32(result[offset+2:], uint32(len(tail)-2))
|
||||
if _, err := ParseType1Program(result); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return result, true, nil
|
||||
}
|
||||
binarySeen = binarySeen || kind == 2
|
||||
offset += 6 + int(length)
|
||||
}
|
||||
return data, false, nil
|
||||
}
|
||||
|
||||
// readType1Header 读取明文字体矩阵及加密数据起点
|
||||
// 入参: data Type1程序
|
||||
// 返回: []float64 字体矩阵, int 加密起点, error 头部错误
|
||||
|
||||
@@ -87,6 +87,108 @@ func FileTables(data []byte, index int) (map[string][]byte, error) {
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// NormalizePDFProgram 修复可确定的PDF内嵌SFNT封装,不补造TrueType轮廓或字形编号
|
||||
// 入参: data 原始字体程序
|
||||
// 返回: []byte 原程序或独立修复程序, bool 是否修复, error 无法确认的目录或度量错误
|
||||
func NormalizePDFProgram(data []byte) ([]byte, bool, error) {
|
||||
if len(data) < 12 || string(data[:4]) != "OTTO" && string(data[:4]) != "\x00\x01\x00\x00" && string(data[:4]) != "true" {
|
||||
return data, false, nil
|
||||
}
|
||||
count := int(binary.BigEndian.Uint16(data[4:6]))
|
||||
if count == 0 || count > (len(data)-12)/16 {
|
||||
return nil, false, fmt.Errorf("unrecoverable PDF font directory size")
|
||||
}
|
||||
first := len(data)
|
||||
for n := 0; n < count; n++ {
|
||||
record := data[12+n*16:]
|
||||
start, size := uint64(binary.BigEndian.Uint32(record[8:])), uint64(binary.BigEndian.Uint32(record[12:]))
|
||||
if size != 0 && start >= 12 && start+size <= uint64(len(data)) {
|
||||
first = min(first, int(start))
|
||||
}
|
||||
}
|
||||
changed := false
|
||||
if first < 12+count*16 {
|
||||
if (first-12)%16 != 0 {
|
||||
return nil, false, fmt.Errorf("ambiguous PDF font directory overlap")
|
||||
}
|
||||
actual := (first - 12) / 16
|
||||
for n := 0; n < count; n++ {
|
||||
record := data[12+n*16:]
|
||||
printable := true
|
||||
for _, b := range record[:4] {
|
||||
printable = printable && b >= 32 && b <= 126
|
||||
}
|
||||
if printable != (n < actual) {
|
||||
return nil, false, fmt.Errorf("ambiguous PDF font table count")
|
||||
}
|
||||
if n < actual {
|
||||
start, size := uint64(binary.BigEndian.Uint32(record[8:])), uint64(binary.BigEndian.Uint32(record[12:]))
|
||||
if size != 0 && (start < uint64(first) || start+size > uint64(len(data))) {
|
||||
return nil, false, fmt.Errorf("incomplete PDF font table")
|
||||
}
|
||||
}
|
||||
}
|
||||
data = bytes.Clone(data)
|
||||
binary.BigEndian.PutUint16(data[4:6], uint16(actual))
|
||||
changed = true
|
||||
}
|
||||
tables, err := FileTables(data, 0)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if head := tables["head"]; len(head) > 54 && len(head) <= 57 {
|
||||
tables["head"] = head[:54]
|
||||
changed = true
|
||||
}
|
||||
if hhea := tables["hhea"]; len(hhea) > 36 && len(hhea) <= 39 && binary.BigEndian.Uint32(hhea) == 0x00010000 {
|
||||
tables["hhea"] = hhea[:36]
|
||||
changed = true
|
||||
}
|
||||
if maxp := tables["maxp"]; tables["glyf"] != nil && len(maxp) > 32 && len(maxp) <= 35 && binary.BigEndian.Uint32(maxp) == 0x00010000 {
|
||||
tables["maxp"] = maxp[:32]
|
||||
changed = true
|
||||
}
|
||||
if post := tables["post"]; len(post) > 32 && binary.BigEndian.Uint32(post) == 0x00030000 {
|
||||
tables["post"] = post[:32]
|
||||
changed = true
|
||||
}
|
||||
if cvt := tables["cvt "]; len(cvt)%2 != 0 && cvt[len(cvt)-1] == 0 {
|
||||
tables["cvt "] = cvt[:len(cvt)-1]
|
||||
changed = true
|
||||
}
|
||||
if len(tables["CFF "]) != 0 && len(tables["head"]) == 0 && len(tables["maxp"]) == 0 && len(tables["hhea"]) == 0 && len(tables["hmtx"]) == 0 {
|
||||
program, _, err := WrapCFFToOTF(tables["CFF "])
|
||||
return program, true, err
|
||||
}
|
||||
if maxp := tables["maxp"]; len(tables["CFF "]) != 0 && tables["glyf"] == nil && len(maxp) >= 6 && (binary.BigEndian.Uint32(maxp) == 0x00010000 || binary.BigEndian.Uint32(maxp) == 0x00005000 && len(maxp) != 6) {
|
||||
count, err := parseCFFAndCountGlyphs(tables["CFF "])
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if count != int(binary.BigEndian.Uint16(maxp[4:6])) {
|
||||
program, _, err := WrapCFFToOTF(tables["CFF "])
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
rebuilt, err := FileTables(program, 0)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if len(tables["head"]) < 20 || !bytes.Equal(tables["head"][18:20], rebuilt["head"][18:20]) {
|
||||
return nil, false, fmt.Errorf("CFF and SFNT design units differ")
|
||||
}
|
||||
return program, true, nil
|
||||
}
|
||||
tables["maxp"] = buildCFFMaxpTable(uint16(count))
|
||||
changed = true
|
||||
}
|
||||
if !changed {
|
||||
return data, false, nil
|
||||
}
|
||||
program, err := SerializeOTF(tables)
|
||||
return program, true, err
|
||||
}
|
||||
|
||||
// calcTableChecksum 计算字体表校验和
|
||||
// 入参: data 表数据
|
||||
// 返回: uint32 校验和
|
||||
|
||||
Reference in new issue
Block a user