mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
feat(字体渲染): 加强字体兼容性
This commit is contained in:
+184
-13
@@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"math"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -28,16 +29,15 @@ import (
|
||||
// 入参: cffData CFF字体数据
|
||||
// 返回: []byte OTF字体数据, map[rune]uint16 字符映射, error 错误信息
|
||||
func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) {
|
||||
sanitized, err := sanitizeCFF(cffData)
|
||||
if err == nil {
|
||||
cffData = sanitized
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
numGlyphs, err := parseCFFAndCountGlyphs(cffData)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mapping := getCmapFromCFF(cffData, int(numGlyphs))
|
||||
sanitized, err := sanitizeCFF(cffData)
|
||||
if err == nil {
|
||||
cffData = sanitized
|
||||
}
|
||||
widths, err := parseCFFWidths(cffData, numGlyphs)
|
||||
if err != nil {
|
||||
widths = make([]uint16, numGlyphs)
|
||||
@@ -64,12 +64,11 @@ func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mapping := getCmapFromCFF(cffData, int(numGlyphs))
|
||||
tables := make(map[string][]byte)
|
||||
tables["CFF "] = cffData
|
||||
tables["head"] = buildHeadTable(unitsPerEm)
|
||||
tables["hhea"] = buildHheaTable(uint16(numGlyphs))
|
||||
tables["maxp"] = buildMaxpTable(uint16(numGlyphs))
|
||||
tables["maxp"] = buildCFFMaxpTable(uint16(numGlyphs))
|
||||
tables["OS/2"] = buildOS2Table()
|
||||
tables["name"] = buildNameTable()
|
||||
tables["post"] = buildPostTable()
|
||||
@@ -747,12 +746,12 @@ func readCFFOffset(data []byte, pos, size int) int {
|
||||
return val
|
||||
}
|
||||
|
||||
// getCmapFromCFF 从 CFF 数据中恢复 Unicode 映射
|
||||
// getCFFCharsetInfo 读取 CFF 字符集和 ROS 信息
|
||||
// 入参: data CFF数据, numGlyphs 字形数量
|
||||
// 返回: map[rune]uint16 恢复的映射表
|
||||
func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
// 返回: []int SID或CID列表, string Registry, string Ordering, int 字符串索引偏移, bool 是否成功
|
||||
func getCFFCharsetInfo(data []byte, numGlyphs int) ([]int, string, string, int, bool) {
|
||||
if len(data) < 4 {
|
||||
return nil
|
||||
return nil, "", "", 0, false
|
||||
}
|
||||
hdrSize := int(data[2])
|
||||
offset := hdrSize
|
||||
@@ -763,9 +762,10 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
offset += szTD
|
||||
stringIndexOff := offset
|
||||
if topDictData == nil {
|
||||
return nil
|
||||
return nil, "", "", 0, false
|
||||
}
|
||||
td := parseCFFDict(topDictData)
|
||||
registry, ordering := getCFFROS(data, stringIndexOff, td)
|
||||
charsetOff := 0
|
||||
if vals, ok := td[15]; ok && len(vals) > 0 {
|
||||
charsetOff = int(vals[0])
|
||||
@@ -784,9 +784,42 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
sids[i] = i
|
||||
}
|
||||
} else {
|
||||
return nil, "", "", 0, false
|
||||
}
|
||||
return sids, registry, ordering, stringIndexOff, true
|
||||
}
|
||||
|
||||
// getCmapFromCFF 从 CFF 数据中恢复 Unicode 映射
|
||||
// 入参: data CFF数据, numGlyphs 字形数量
|
||||
// 返回: map[rune]uint16 恢复的映射表
|
||||
func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
sids, registry, ordering, stringIndexOff, ok := getCFFCharsetInfo(data, numGlyphs)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
mapping := make(map[rune]uint16)
|
||||
if registry == "Adobe" && ordering == "GB1" {
|
||||
for gid, cid := range sids {
|
||||
if gid == 0 {
|
||||
continue
|
||||
}
|
||||
if r, ok := adobeGB1CIDToUnicode(cid); ok {
|
||||
mapping[r] = uint16(gid)
|
||||
} else {
|
||||
mapping[0xE000+rune(gid)] = uint16(gid)
|
||||
}
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
if registry != "" {
|
||||
for gid := range sids {
|
||||
if gid == 0 {
|
||||
continue
|
||||
}
|
||||
mapping[0xE000+rune(gid)] = uint16(gid)
|
||||
}
|
||||
return mapping
|
||||
}
|
||||
for gid, sid := range sids {
|
||||
if gid == 0 {
|
||||
continue
|
||||
@@ -812,6 +845,79 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
return mapping
|
||||
}
|
||||
|
||||
// getCFFCIDRuneMap 获取 CID 到包装字体字符的映射
|
||||
// 入参: data CFF或OpenType字体数据
|
||||
// 返回: map[uint16]rune CID映射
|
||||
func getCFFCIDRuneMap(data []byte) map[uint16]rune {
|
||||
cffData := getCFFData(data)
|
||||
if cffData == nil {
|
||||
return nil
|
||||
}
|
||||
numGlyphs, err := parseCFFAndCountGlyphs(cffData)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sids, registry, _, _, ok := getCFFCharsetInfo(cffData, numGlyphs)
|
||||
if !ok || registry == "" {
|
||||
return nil
|
||||
}
|
||||
mapping := getCmapFromCFF(cffData, numGlyphs)
|
||||
if len(mapping) == 0 {
|
||||
return nil
|
||||
}
|
||||
gidRunes := make(map[uint16]rune)
|
||||
for run, gid := range mapping {
|
||||
gidRunes[gid] = run
|
||||
}
|
||||
result := make(map[uint16]rune)
|
||||
for gid, cid := range sids {
|
||||
if gid == 0 || cid < 0 || cid > 0xFFFF {
|
||||
continue
|
||||
}
|
||||
if run, ok := gidRunes[uint16(gid)]; ok {
|
||||
result[uint16(cid)] = run
|
||||
}
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getCFFData 获取字体中的 CFF 数据
|
||||
// 入参: data 字体数据
|
||||
// 返回: []byte CFF数据
|
||||
func getCFFData(data []byte) []byte {
|
||||
if isBareCFFData(data) {
|
||||
return data
|
||||
}
|
||||
if len(data) < 12 {
|
||||
return nil
|
||||
}
|
||||
tag := string(data[0:4])
|
||||
u32Tag := binary.BigEndian.Uint32(data[0:4])
|
||||
if tag != "OTTO" && tag != "true" && u32Tag != 0x00010000 {
|
||||
return nil
|
||||
}
|
||||
numTables := int(binary.BigEndian.Uint16(data[4:6]))
|
||||
for i := 0; i < numTables; i++ {
|
||||
pos := 12 + i*16
|
||||
if pos+16 > len(data) {
|
||||
return nil
|
||||
}
|
||||
if string(data[pos:pos+4]) != "CFF " {
|
||||
continue
|
||||
}
|
||||
offset := int(binary.BigEndian.Uint32(data[pos+8 : pos+12]))
|
||||
length := int(binary.BigEndian.Uint32(data[pos+12 : pos+16]))
|
||||
if offset < 0 || length < 0 || offset+length > len(data) {
|
||||
return nil
|
||||
}
|
||||
return data[offset : offset+length]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseCFFCharset 解析 CFF 字符集并返回 SID 列表
|
||||
// 入参: data CFF数据, offset 偏移量, numGlyphs 字形数量
|
||||
// 返回: []int SID列表
|
||||
@@ -884,6 +990,71 @@ func readStringIndexItem(data []byte, offset int, idx int) string {
|
||||
return string(data[start : start+length])
|
||||
}
|
||||
|
||||
// getCFFROS 读取 CID 字体 ROS 信息
|
||||
// 入参: data CFF数据, stringIndexOff 字符串索引偏移, td 顶层字典
|
||||
// 返回: string Registry, string Ordering
|
||||
func getCFFROS(data []byte, stringIndexOff int, td cffDict) (string, string) {
|
||||
vals, ok := td[1230]
|
||||
if !ok || len(vals) < 2 {
|
||||
return "", ""
|
||||
}
|
||||
registry := getCFFSIDString(data, stringIndexOff, int(vals[0]))
|
||||
ordering := getCFFSIDString(data, stringIndexOff, int(vals[1]))
|
||||
return registry, ordering
|
||||
}
|
||||
|
||||
// getCFFSIDString 读取 CFF SID 字符串
|
||||
// 入参: data CFF数据, stringIndexOff 字符串索引偏移, sid 字符串ID
|
||||
// 返回: string 字符串内容
|
||||
func getCFFSIDString(data []byte, stringIndexOff int, sid int) string {
|
||||
if sid >= 0 && sid < len(cffStandardStrings) {
|
||||
return cffStandardStrings[sid]
|
||||
}
|
||||
if sid > 390 {
|
||||
return readStringIndexItem(data, stringIndexOff, sid-391)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// adobeGB1CIDToUnicode 将 Adobe-GB1 CID 转为 Unicode
|
||||
// 入参: cid 字符CID
|
||||
// 返回: rune Unicode字符, bool 是否成功
|
||||
func adobeGB1CIDToUnicode(cid int) (rune, bool) {
|
||||
switch cid {
|
||||
case 329:
|
||||
return '“', true
|
||||
case 330:
|
||||
return '”', true
|
||||
case 821:
|
||||
return '、', true
|
||||
case 822:
|
||||
return '。', true
|
||||
case 829:
|
||||
return '《', true
|
||||
case 830:
|
||||
return '》', true
|
||||
}
|
||||
n := cid + 471
|
||||
if n <= 0 {
|
||||
return 0, false
|
||||
}
|
||||
row := (n-1)/94 + 1
|
||||
cell := (n-1)%94 + 1
|
||||
if row < 16 || row > 87 || cell < 1 || cell > 94 {
|
||||
return 0, false
|
||||
}
|
||||
gbk := []byte{byte(row + 0xA0), byte(cell + 0xA0)}
|
||||
decoded, err := simplifiedchinese.GBK.NewDecoder().Bytes(gbk)
|
||||
if err != nil || len(decoded) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
rs := []rune(string(decoded))
|
||||
if len(rs) != 1 {
|
||||
return 0, false
|
||||
}
|
||||
return rs[0], true
|
||||
}
|
||||
|
||||
// getUnicodeFromName 根据字形名称获取对应的Unicode字符
|
||||
// 入参: name 字形名称
|
||||
// 返回: rune Unicode字符
|
||||
|
||||
Reference in New Issue
Block a user