feat(渲染能力): 支持椭圆渐变,修正虚线长度及短线显示
build.yaml / build (push) Successful in 35s

This commit is contained in:
xiaoqidun committed 2026-09-09 13:54:34 +08:00
1 parent f27c5b666e
commit 3cd6cb3afd
10 files changed
+128 -103

No files matched your search

+2 -2
View File
@@ -49,9 +49,9 @@ func FixFontDataAggressive(data []byte, fixCmap, fixName bool) (bool, []byte, ma
return fixed, newData, mapping, mc, err return fixed, newData, mapping, mc, err
} }
// isBareCFFData 检查是否为 CFF 裸字体数据 // isBareCFFData 检查是否为CFF裸字体数据
// 入参: data 字体数据 // 入参: data 字体数据
// 返回: bool 是否为 CFF 裸数据 // 返回: bool 是否为CFF裸数据
func isBareCFFData(data []byte) bool { func isBareCFFData(data []byte) bool {
if len(data) < 4 { if len(data) < 4 {
return false return false
+31 -31
View File
@@ -26,7 +26,7 @@ import (
"golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/encoding/simplifiedchinese"
) )
// wrapCFFToOTF 将 CFF 裸数据包装为 OpenType 字体格式 // wrapCFFToOTF 将CFF裸数据包装为OpenType字体格式
// 入参: cffData CFF字体数据 // 入参: cffData CFF字体数据
// 返回: []byte OTF字体数据, map[rune]uint16 字符映射, error 错误信息 // 返回: []byte OTF字体数据, map[rune]uint16 字符映射, error 错误信息
func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) { func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) {
@@ -79,7 +79,7 @@ func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) {
return data, mapping, err return data, mapping, err
} }
// cffDict 使用 float64 存储所有数值,以统一处理整数和实数 // cffDict 使用float64存储所有数值,以统一处理整数和实数
type cffDict map[int][]float64 type cffDict map[int][]float64
// sanitizeCFF 尝试清洗CFF数据,转换CID字体并合并FontMatrix // sanitizeCFF 尝试清洗CFF数据,转换CID字体并合并FontMatrix
@@ -246,7 +246,7 @@ func sanitizeCFF(data []byte) ([]byte, error) {
return newCFF.Bytes(), nil return newCFF.Bytes(), nil
} }
// sanitizeMultiFDCFF 清洗多 FD 的 CID CFF 数据 // sanitizeMultiFDCFF 清洗多FD的CID CFF数据
// 入参: data 原始CFF数据, hdrSize 头部大小, nameIndexData 名称索引, topDict 顶层字典 // 入参: data 原始CFF数据, hdrSize 头部大小, nameIndexData 名称索引, topDict 顶层字典
// 入参: stringIndexData 字符串索引, globalSubrIndexData 全局子程序索引, fdArrOff FDArray偏移, fdCount FD数量 // 入参: stringIndexData 字符串索引, globalSubrIndexData 全局子程序索引, fdArrOff FDArray偏移, fdCount FD数量
// 返回: []byte 清洗后的CFF数据, error 错误信息 // 返回: []byte 清洗后的CFF数据, error 错误信息
@@ -332,7 +332,7 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
return newCFF.Bytes(), nil return newCFF.Bytes(), nil
} }
// parseCFFAndCountGlyphs 解析 CFF 头部并统计字形数量 // parseCFFAndCountGlyphs 解析CFF头部并统计字形数量
// 入参: data CFF数据 // 入参: data CFF数据
// 返回: int 字形数量, error 错误信息 // 返回: int 字形数量, error 错误信息
func parseCFFAndCountGlyphs(data []byte) (int, error) { func parseCFFAndCountGlyphs(data []byte) (int, error) {
@@ -370,7 +370,7 @@ func parseCFFAndCountGlyphs(data []byte) (int, error) {
return 0, fmt.Errorf("failed to parse top dict") return 0, fmt.Errorf("failed to parse top dict")
} }
// multiplyAffine 2x3 仿射矩阵乘法 // multiplyAffine 2x3仿射矩阵乘法
// 入参: a 矩阵A, b 矩阵B // 入参: a 矩阵A, b 矩阵B
// 返回: []float64 结果矩阵 // 返回: []float64 结果矩阵
func multiplyAffine(a, b []float64) []float64 { func multiplyAffine(a, b []float64) []float64 {
@@ -384,7 +384,7 @@ func multiplyAffine(a, b []float64) []float64 {
} }
} }
// parseCFFDict 解析 CFF 字典数据 // parseCFFDict 解析CFF字典数据
// 入参: data 字典数据 // 入参: data 字典数据
// 返回: cffDict 解析后的字典映射 // 返回: cffDict 解析后的字典映射
func parseCFFDict(data []byte) cffDict { func parseCFFDict(data []byte) cffDict {
@@ -442,7 +442,7 @@ func parseCFFDict(data []byte) cffDict {
return dict return dict
} }
// parseCFFReal 解析 CFF 实数编码 // parseCFFReal 解析CFF实数编码
// 入参: data 数据切片 // 入参: data 数据切片
// 返回: string 实数字符串, int 消耗字节数 // 返回: string 实数字符串, int 消耗字节数
func parseCFFReal(data []byte) (string, int) { func parseCFFReal(data []byte) (string, int) {
@@ -478,7 +478,7 @@ func parseCFFReal(data []byte) (string, int) {
return sb.String(), i return sb.String(), i
} }
// encodeCFFDict 编码 CFF 字典 (仅使用 float64 操作数) // encodeCFFDict 编码CFF字典 (仅使用float64操作数)
// 入参: dict CFF字典映射 // 入参: dict CFF字典映射
// 返回: []byte 编码后的字典数据 // 返回: []byte 编码后的字典数据
func encodeCFFDict(dict cffDict) []byte { func encodeCFFDict(dict cffDict) []byte {
@@ -503,7 +503,7 @@ func encodeCFFDict(dict cffDict) []byte {
return buf.Bytes() return buf.Bytes()
} }
// encodeNumberCFF 编码单个数值到 CFF 格式 // encodeNumberCFF 编码单个数值到CFF格式
// 入参: buf 缓冲区, val 数值 // 入参: buf 缓冲区, val 数值
func encodeNumberCFF(buf *bytes.Buffer, val float64) { func encodeNumberCFF(buf *bytes.Buffer, val float64) {
if val == math.Trunc(val) { if val == math.Trunc(val) {
@@ -557,7 +557,7 @@ func encodeNumberCFF(buf *bytes.Buffer, val float64) {
} }
} }
// encodeCFFIndex 编码 CFF 索引结构 // encodeCFFIndex 编码CFF索引结构
// 入参: items 数据项列表 // 入参: items 数据项列表
// 返回: []byte 编码后的索引数据 // 返回: []byte 编码后的索引数据
func encodeCFFIndex(items []([]byte)) []byte { func encodeCFFIndex(items []([]byte)) []byte {
@@ -602,7 +602,7 @@ func putOffset(buf *bytes.Buffer, val int, size int) {
buf.Write(tmp[4-size:]) buf.Write(tmp[4-size:])
} }
// getCFFIndexCount 读取 CFF 索引的计数和大小 // getCFFIndexCount 读取CFF索引的计数和大小
// 入参: data CFF数据, offset 偏移量 // 入参: data CFF数据, offset 偏移量
// 返回: int 数量, int 索引结构总大小 // 返回: int 数量, int 索引结构总大小
func getCFFIndexCount(data []byte, offset int) (int, int) { func getCFFIndexCount(data []byte, offset int) (int, int) {
@@ -635,7 +635,7 @@ func getCFFIndexCount(data []byte, offset int) (int, int) {
return count, 3 + (count+1)*offSize + (dataEnd - 1) return count, 3 + (count+1)*offSize + (dataEnd - 1)
} }
// getCFFIndexData 读取 CFF 索引的数据块 // getCFFIndexData 读取CFF索引的数据块
// 入参: data CFF数据, offset 偏移量 // 入参: data CFF数据, offset 偏移量
// 返回: []byte 索引数据(已去除offsets), int 索引结构总大小 // 返回: []byte 索引数据(已去除offsets), int 索引结构总大小
func getCFFIndexData(data []byte, offset int) ([]byte, int) { func getCFFIndexData(data []byte, offset int) ([]byte, int) {
@@ -665,7 +665,7 @@ func getCFFIndexData(data []byte, offset int) ([]byte, int) {
return data[start : start+length], size return data[start : start+length], size
} }
// readCFFIndexItems 读取 CFF 索引中的所有数据项 // readCFFIndexItems 读取CFF索引中的所有数据项
// 入参: data CFF数据, offset 索引偏移 // 入参: data CFF数据, offset 索引偏移
// 返回: [][]byte 数据项列表 // 返回: [][]byte 数据项列表
func readCFFIndexItems(data []byte, offset int) [][]byte { func readCFFIndexItems(data []byte, offset int) [][]byte {
@@ -698,7 +698,7 @@ func readCFFIndexItems(data []byte, offset int) [][]byte {
return items return items
} }
// parseCFFFDSelect 解析 CID CFF 的 FDSelect // parseCFFFDSelect 解析CID CFF的FDSelect
// 入参: data CFF数据, offset FDSelect偏移, numGlyphs 字形数量 // 入参: data CFF数据, offset FDSelect偏移, numGlyphs 字形数量
// 返回: []int 字形对应的FD索引 // 返回: []int 字形对应的FD索引
func parseCFFFDSelect(data []byte, offset int, numGlyphs int) []int { func parseCFFFDSelect(data []byte, offset int, numGlyphs int) []int {
@@ -754,7 +754,7 @@ func parseCFFFDSelect(data []byte, offset int, numGlyphs int) []int {
return result return result
} }
// readCFFLocalSubrs 读取 CID CFF 的本地子程序 // readCFFLocalSubrs 读取CID CFF的本地子程序
// 入参: data CFF数据, fdArrOff FDArray偏移, fdCount FD数量 // 入参: data CFF数据, fdArrOff FDArray偏移, fdCount FD数量
// 返回: [][][]byte 本地子程序列表, []cffDict Private字典列表 // 返回: [][][]byte 本地子程序列表, []cffDict Private字典列表
func readCFFLocalSubrs(data []byte, fdArrOff int, fdCount int) ([][][]byte, []cffDict) { func readCFFLocalSubrs(data []byte, fdArrOff int, fdCount int) ([][][]byte, []cffDict) {
@@ -792,7 +792,7 @@ type type2Operand struct {
outStart int outStart int
} }
// inlineType2CharString 内联 Type2 CharString 子程序调用 // inlineType2CharString 内联Type2 CharString子程序调用
// 入参: data CharString数据, localSubrs 本地子程序, globalSubrs 全局子程序, depth 递归深度 // 入参: data CharString数据, localSubrs 本地子程序, globalSubrs 全局子程序, depth 递归深度
// 返回: []byte 内联后的CharString数据 // 返回: []byte 内联后的CharString数据
func inlineType2CharString(data []byte, localSubrs, globalSubrs [][]byte, depth int) []byte { func inlineType2CharString(data []byte, localSubrs, globalSubrs [][]byte, depth int) []byte {
@@ -881,7 +881,7 @@ func inlineType2CharString(data []byte, localSubrs, globalSubrs [][]byte, depth
return out return out
} }
// removeType2Hints 移除 Type2 CharString 的 hint 指令 // removeType2Hints 移除Type2 CharString的hint指令
// 入参: data CharString数据 // 入参: data CharString数据
// 返回: []byte 移除hint后的CharString数据 // 返回: []byte 移除hint后的CharString数据
func removeType2Hints(data []byte) []byte { func removeType2Hints(data []byte) []byte {
@@ -954,7 +954,7 @@ func removeType2Hints(data []byte) []byte {
return out return out
} }
// cffSubrBias 获取 Type2 子程序偏移 // cffSubrBias 获取Type2子程序偏移
// 入参: count 子程序数量 // 入参: count 子程序数量
// 返回: int 偏移量 // 返回: int 偏移量
func cffSubrBias(count int) int { func cffSubrBias(count int) int {
@@ -967,7 +967,7 @@ func cffSubrBias(count int) int {
return 32768 return 32768
} }
// parseCFFWidths 从 CFF 数据中解析 Glyph 宽度 // parseCFFWidths 从CFF数据中解析Glyph宽度
// 入参: data CFF数据, numGlyphs 字形数量 // 入参: data CFF数据, numGlyphs 字形数量
// 返回: []uint16 宽度列表, error 错误信息 // 返回: []uint16 宽度列表, error 错误信息
func parseCFFWidths(data []byte, numGlyphs int) ([]uint16, error) { func parseCFFWidths(data []byte, numGlyphs int) ([]uint16, error) {
@@ -1037,7 +1037,7 @@ func parseCFFWidths(data []byte, numGlyphs int) ([]uint16, error) {
return nil, fmt.Errorf("no charstrings") return nil, fmt.Errorf("no charstrings")
} }
// scanCharStringWidth 扫描 CharString 获取宽度 // scanCharStringWidth 扫描CharString获取宽度
// 入参: data CharString数据, nominal, def 默认宽度值 // 入参: data CharString数据, nominal, def 默认宽度值
// 返回: float64 宽度值 // 返回: float64 宽度值
func scanCharStringWidth(data []byte, nominal, def float64) float64 { func scanCharStringWidth(data []byte, nominal, def float64) float64 {
@@ -1099,7 +1099,7 @@ func parseShortInt(data []byte, idx int) float64 {
return float64(int16(binary.BigEndian.Uint16(data[idx+1:]))) return float64(int16(binary.BigEndian.Uint16(data[idx+1:])))
} }
// parseNumberType2 解析 Number (Type 2) // parseNumberType2 解析Number (Type 2)
// 入参: data 数据, idx 索引 // 入参: data 数据, idx 索引
// 返回: float64 浮点值 // 返回: float64 浮点值
func parseNumberType2(data []byte, idx int) float64 { func parseNumberType2(data []byte, idx int) float64 {
@@ -1135,7 +1135,7 @@ func readCFFOffset(data []byte, pos, size int) int {
return val return val
} }
// getCFFCharsetInfo 读取 CFF 字符集和 ROS 信息 // getCFFCharsetInfo 读取CFF字符集和ROS信息
// 入参: data CFF数据, numGlyphs 字形数量 // 入参: data CFF数据, numGlyphs 字形数量
// 返回: []int SID或CID列表, string Registry, string Ordering, int 字符串索引偏移, bool 是否成功 // 返回: []int SID或CID列表, string Registry, string Ordering, int 字符串索引偏移, bool 是否成功
func getCFFCharsetInfo(data []byte, numGlyphs int) ([]int, string, string, int, bool) { func getCFFCharsetInfo(data []byte, numGlyphs int) ([]int, string, string, int, bool) {
@@ -1178,7 +1178,7 @@ func getCFFCharsetInfo(data []byte, numGlyphs int) ([]int, string, string, int,
return sids, registry, ordering, stringIndexOff, true return sids, registry, ordering, stringIndexOff, true
} }
// getCmapFromCFF 从 CFF 数据中恢复 Unicode 映射 // getCmapFromCFF 从CFF数据中恢复Unicode映射
// 入参: data CFF数据, numGlyphs 字形数量 // 入参: data CFF数据, numGlyphs 字形数量
// 返回: map[rune]uint16 恢复的映射表 // 返回: map[rune]uint16 恢复的映射表
func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 { func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
@@ -1234,7 +1234,7 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
return mapping return mapping
} }
// getCFFCIDRuneMap 获取 CID 到包装字体字符的映射 // getCFFCIDRuneMap 获取CID到包装字体字符的映射
// 入参: data CFF或OpenType字体数据 // 入参: data CFF或OpenType字体数据
// 返回: map[uint16]rune CID映射 // 返回: map[uint16]rune CID映射
func getCFFCIDRuneMap(data []byte) map[uint16]rune { func getCFFCIDRuneMap(data []byte) map[uint16]rune {
@@ -1280,7 +1280,7 @@ func getCFFCIDRuneMap(data []byte) map[uint16]rune {
return result return result
} }
// getCFFData 获取字体中的 CFF 数据 // getCFFData 获取字体中的CFF数据
// 入参: data 字体数据 // 入参: data 字体数据
// 返回: []byte CFF数据 // 返回: []byte CFF数据
func getCFFData(data []byte) []byte { func getCFFData(data []byte) []byte {
@@ -1314,7 +1314,7 @@ func getCFFData(data []byte) []byte {
return nil return nil
} }
// parseCFFCharset 解析 CFF 字符集并返回 SID 列表 // parseCFFCharset 解析CFF字符集并返回SID列表
// 入参: data CFF数据, offset 偏移量, numGlyphs 字形数量 // 入参: data CFF数据, offset 偏移量, numGlyphs 字形数量
// 返回: []int SID列表 // 返回: []int SID列表
func parseCFFCharset(data []byte, offset int, numGlyphs int) []int { func parseCFFCharset(data []byte, offset int, numGlyphs int) []int {
@@ -1357,7 +1357,7 @@ func parseCFFCharset(data []byte, offset int, numGlyphs int) []int {
return sids return sids
} }
// readStringIndexItem 读取 CFF 字符串索引项 // readStringIndexItem 读取CFF字符串索引项
// 入参: data CFF数据, offset 索引偏移, idx 索引号 // 入参: data CFF数据, offset 索引偏移, idx 索引号
// 返回: string 读取的字符串 // 返回: string 读取的字符串
func readStringIndexItem(data []byte, offset int, idx int) string { func readStringIndexItem(data []byte, offset int, idx int) string {
@@ -1386,7 +1386,7 @@ func readStringIndexItem(data []byte, offset int, idx int) string {
return string(data[start : start+length]) return string(data[start : start+length])
} }
// getCFFROS 读取 CID 字体 ROS 信息 // getCFFROS 读取CID字体ROS信息
// 入参: data CFF数据, stringIndexOff 字符串索引偏移, td 顶层字典 // 入参: data CFF数据, stringIndexOff 字符串索引偏移, td 顶层字典
// 返回: string Registry, string Ordering // 返回: string Registry, string Ordering
func getCFFROS(data []byte, stringIndexOff int, td cffDict) (string, string) { func getCFFROS(data []byte, stringIndexOff int, td cffDict) (string, string) {
@@ -1399,7 +1399,7 @@ func getCFFROS(data []byte, stringIndexOff int, td cffDict) (string, string) {
return registry, ordering return registry, ordering
} }
// getCFFSIDString 读取 CFF SID 字符串 // getCFFSIDString 读取CFF SID字符串
// 入参: data CFF数据, stringIndexOff 字符串索引偏移, sid 字符串ID // 入参: data CFF数据, stringIndexOff 字符串索引偏移, sid 字符串ID
// 返回: string 字符串内容 // 返回: string 字符串内容
func getCFFSIDString(data []byte, stringIndexOff int, sid int) string { func getCFFSIDString(data []byte, stringIndexOff int, sid int) string {
@@ -1412,7 +1412,7 @@ func getCFFSIDString(data []byte, stringIndexOff int, sid int) string {
return "" return ""
} }
// adobeGB1CIDToUnicode 将 Adobe-GB1 CID 转为 Unicode // adobeGB1CIDToUnicode 将Adobe-GB1 CID转为Unicode
// 入参: cid 字符CID // 入参: cid 字符CID
// 返回: rune Unicode字符, bool 是否成功 // 返回: rune Unicode字符, bool 是否成功
func adobeGB1CIDToUnicode(cid int) (rune, bool) { func adobeGB1CIDToUnicode(cid int) (rune, bool) {
@@ -1541,7 +1541,7 @@ func getUnicodeFromName(name string) rune {
return 0 return 0
} }
// cffStandardStrings CFF 标准字符串表 // cffStandardStrings CFF标准字符串表
var cffStandardStrings = []string{ var cffStandardStrings = []string{
".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "quotedblright", "guillemotleft", "guillemotright", "dagger", "daggerdbl", "fi", "fl", "endash", "emdash", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "second", "circumflex", "breve", "dotaccent", "dieresis", "grave", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emspace", ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", "currency", "quotesingle", "quotedblleft", "quotedblright", "guillemotleft", "guillemotright", "dagger", "daggerdbl", "fi", "fl", "endash", "emdash", "paragraph", "bullet", "quotesinglbase", "quotedblbase", "second", "circumflex", "breve", "dotaccent", "dieresis", "grave", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "emspace",
"AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "2dotlead", "nbspace", "1dotlead", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold", "AE", "ordfeminine", "Lslash", "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe", "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth", "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar", "degree", "thorn", "threequarters", "twosuperior", "registered", "minus", "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis", "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave", "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde", "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall", "parenleftsuperior", "parenrightsuperior", "2dotlead", "nbspace", "1dotlead", "zerooldstyle", "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior", "threequartersemdash", "periodsuperior", "questionsmall", "asuperior", "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior", "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall", "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall", "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash", "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall", "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior", "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior", "oneinferior", "twoinferior", "threeinferior", "fourinferior", "fiveinferior", "sixinferior", "seveninferior", "eightinferior", "nineinferior", "centinferior", "dollarinferior", "periodinferior", "commainferior", "Agravesmall", "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall", "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall", "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall", "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black", "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold",
+5 -5
View File
@@ -18,7 +18,7 @@ import (
"encoding/binary" "encoding/binary"
) )
// fixTrueType 尝试修复 TrueType/OpenType 字体数据 // fixTrueType 尝试修复TrueType/OpenType字体数据
// 入参: data 原始字体数据, fixCmap 是否修复cmap, fixName 是否修复name // 入参: data 原始字体数据, fixCmap 是否修复cmap, fixName 是否修复name
// 返回: bool 是否修复, []byte 修复后数据, map[rune]uint16 字符映射, bool 是否缺失cmap, error 错误信息 // 返回: bool 是否修复, []byte 修复后数据, map[rune]uint16 字符映射, bool 是否缺失cmap, error 错误信息
func fixTrueType(data []byte, fixCmap, fixName bool) (bool, []byte, map[rune]uint16, bool, error) { func fixTrueType(data []byte, fixCmap, fixName bool) (bool, []byte, map[rune]uint16, bool, error) {
@@ -192,7 +192,7 @@ func fixTrueType(data []byte, fixCmap, fixName bool) (bool, []byte, map[rune]uin
return true, finalData, mapping, missingCmap, nil return true, finalData, mapping, missingCmap, nil
} }
// hasBadPostTable 检查 post 表是否会被 canvas/font 拒绝 // hasBadPostTable 检查post表是否会被canvas/font拒绝
func hasBadPostTable(data []byte, numGlyphs uint16, isTrueType bool) bool { func hasBadPostTable(data []byte, numGlyphs uint16, isTrueType bool) bool {
if len(data) < 32 { if len(data) < 32 {
return true return true
@@ -217,7 +217,7 @@ func hasBadPostTable(data []byte, numGlyphs uint16, isTrueType bool) bool {
} }
} }
// hasUsableCmap 检查是否存在可用的 cmap 子表 // hasUsableCmap 检查是否存在可用的cmap子表
// 入参: data cmap表数据 // 入参: data cmap表数据
// 返回: bool 是否可用 // 返回: bool 是否可用
func hasUsableCmap(data []byte) bool { func hasUsableCmap(data []byte) bool {
@@ -249,8 +249,8 @@ func hasUsableCmap(data []byte) bool {
return usable return usable
} }
// supportedCmapFormat 判断 cmap 子表格式是否可用 // supportedCmapFormat 判断cmap子表格式是否可用
// 入参: format cmap 子表格式, platformID 平台ID, encodingID 编码ID // 入参: format cmap子表格式, platformID 平台ID, encodingID 编码ID
// 返回: bool 是否可用 // 返回: bool 是否可用
func supportedCmapFormat(format, platformID, encodingID uint16) bool { func supportedCmapFormat(format, platformID, encodingID uint16) bool {
switch format { switch format {
+20 -20
View File
@@ -48,7 +48,7 @@ func calcTableChecksum(data []byte) uint32 {
return sum return sum
} }
// parseCmapMappings 解析 cmap 字符映射 // parseCmapMappings 解析cmap字符映射
// 入参: data cmap表数据 // 入参: data cmap表数据
// 返回: map[rune]uint16 字符到字形映射 // 返回: map[rune]uint16 字符到字形映射
func parseCmapMappings(data []byte) map[rune]uint16 { func parseCmapMappings(data []byte) map[rune]uint16 {
@@ -87,7 +87,7 @@ func parseCmapMappings(data []byte) map[rune]uint16 {
return result return result
} }
// parseCmapFormat0 解析 cmap format 0 // parseCmapFormat0 解析cmap format 0
// 入参: data 子表数据, result 字符映射 // 入参: data 子表数据, result 字符映射
func parseCmapFormat0(data []byte, result map[rune]uint16) { func parseCmapFormat0(data []byte, result map[rune]uint16) {
if len(data) < 262 { if len(data) < 262 {
@@ -101,7 +101,7 @@ func parseCmapFormat0(data []byte, result map[rune]uint16) {
} }
} }
// parseCmapFormat4 解析 cmap format 4 // parseCmapFormat4 解析cmap format 4
// 入参: data 子表数据, result 字符映射 // 入参: data 子表数据, result 字符映射
func parseCmapFormat4(data []byte, result map[rune]uint16) { func parseCmapFormat4(data []byte, result map[rune]uint16) {
if len(data) < 16 { if len(data) < 16 {
@@ -152,7 +152,7 @@ func parseCmapFormat4(data []byte, result map[rune]uint16) {
} }
} }
// parseCmapFormat6 解析 cmap format 6 // parseCmapFormat6 解析cmap format 6
// 入参: data 子表数据, result 字符映射 // 入参: data 子表数据, result 字符映射
func parseCmapFormat6(data []byte, result map[rune]uint16) { func parseCmapFormat6(data []byte, result map[rune]uint16) {
if len(data) < 10 { if len(data) < 10 {
@@ -168,7 +168,7 @@ func parseCmapFormat6(data []byte, result map[rune]uint16) {
} }
} }
// parseCmapFormat12 解析 cmap format 12 // parseCmapFormat12 解析cmap format 12
// 入参: data 子表数据, result 字符映射 // 入参: data 子表数据, result 字符映射
func parseCmapFormat12(data []byte, result map[rune]uint16) { func parseCmapFormat12(data []byte, result map[rune]uint16) {
if len(data) < 16 { if len(data) < 16 {
@@ -207,7 +207,7 @@ func addPackedGlyphMapping(mapping map[rune]uint16, numGlyphs uint16) {
} }
} }
// buildHeadTable 构建 head 表 // buildHeadTable 构建head表
// 入参: unitsPerEm 每em单位数 // 入参: unitsPerEm 每em单位数
// 返回: []byte head表数据 // 返回: []byte head表数据
func buildHeadTable(unitsPerEm uint16) []byte { func buildHeadTable(unitsPerEm uint16) []byte {
@@ -233,7 +233,7 @@ func buildHeadTable(unitsPerEm uint16) []byte {
return buf.Bytes() return buf.Bytes()
} }
// buildHheaTable 构建 hhea 表 // buildHheaTable 构建hhea表
// 入参: numGlyphs 字形数量 // 入参: numGlyphs 字形数量
// 返回: []byte hhea表数据 // 返回: []byte hhea表数据
func buildHheaTable(numGlyphs uint16) []byte { func buildHheaTable(numGlyphs uint16) []byte {
@@ -259,7 +259,7 @@ func buildHheaTable(numGlyphs uint16) []byte {
return buf.Bytes() return buf.Bytes()
} }
// buildCFFMaxpTable 构建 CFF 轮廓使用的 maxp 0.5 表 // buildCFFMaxpTable 构建CFF轮廓使用的maxp 0.5表
// 入参: numGlyphs 字形数量 // 入参: numGlyphs 字形数量
// 返回: []byte maxp表数据 // 返回: []byte maxp表数据
func buildCFFMaxpTable(numGlyphs uint16) []byte { func buildCFFMaxpTable(numGlyphs uint16) []byte {
@@ -269,7 +269,7 @@ func buildCFFMaxpTable(numGlyphs uint16) []byte {
return buf.Bytes() return buf.Bytes()
} }
// buildTrueTypeMaxpTable 构建 TrueType 轮廓使用的 maxp 1.0 表 // buildTrueTypeMaxpTable 构建TrueType轮廓使用的maxp 1.0表
// 入参: numGlyphs 字形数量 // 入参: numGlyphs 字形数量
// 返回: []byte maxp表数据 // 返回: []byte maxp表数据
func buildTrueTypeMaxpTable(numGlyphs uint16) []byte { func buildTrueTypeMaxpTable(numGlyphs uint16) []byte {
@@ -282,13 +282,13 @@ func buildTrueTypeMaxpTable(numGlyphs uint16) []byte {
return buf.Bytes() return buf.Bytes()
} }
// buildOS2Table 构建 OS/2 表 (使用默认 Metrics) // buildOS2Table 构建OS/2表 (使用默认Metrics)
// 返回: []byte OS/2表数据 // 返回: []byte OS/2表数据
func buildOS2Table() []byte { func buildOS2Table() []byte {
return buildOS2TableWithMetrics(800, -200) return buildOS2TableWithMetrics(800, -200)
} }
// buildOS2TableWithMetrics 构建 OS/2 表 // buildOS2TableWithMetrics 构建OS/2表
// 入参: ascender 上升部, descender 下降部 // 入参: ascender 上升部, descender 下降部
// 返回: []byte OS/2表数据 // 返回: []byte OS/2表数据
func buildOS2TableWithMetrics(ascender, descender int16) []byte { func buildOS2TableWithMetrics(ascender, descender int16) []byte {
@@ -337,7 +337,7 @@ func buildOS2TableWithMetrics(ascender, descender int16) []byte {
return os2.Bytes() return os2.Bytes()
} }
// buildNameTable 构建 name 表 (最小化) // buildNameTable 构建name表 (最小化)
// 返回: []byte name表数据 // 返回: []byte name表数据
func buildNameTable() []byte { func buildNameTable() []byte {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -347,7 +347,7 @@ func buildNameTable() []byte {
return buf.Bytes() return buf.Bytes()
} }
// buildPostTable 构建 post 表 (版本 3.0, 无字形名称) // buildPostTable 构建post表 (版本3.0, 无字形名称)
// 返回: []byte post表数据 // 返回: []byte post表数据
func buildPostTable() []byte { func buildPostTable() []byte {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
@@ -363,7 +363,7 @@ func buildPostTable() []byte {
return buf.Bytes() return buf.Bytes()
} }
// buildHmtxTable 构建 hmtx 表 // buildHmtxTable 构建hmtx表
// 入参: widths 宽度列表 // 入参: widths 宽度列表
// 返回: []byte hmtx表数据 // 返回: []byte hmtx表数据
func buildHmtxTable(widths []uint16) []byte { func buildHmtxTable(widths []uint16) []byte {
@@ -375,7 +375,7 @@ func buildHmtxTable(widths []uint16) []byte {
return buf.Bytes() return buf.Bytes()
} }
// cmapSegment cmap 表段结构 // cmapSegment cmap表段结构
// 字段: start 开始字符, end 结束字符, delta 增量, offset 偏移 // 字段: start 开始字符, end 结束字符, delta 增量, offset 偏移
type cmapSegment struct { type cmapSegment struct {
start, end uint16 start, end uint16
@@ -383,7 +383,7 @@ type cmapSegment struct {
offset uint16 offset uint16
} }
// buildCmapTable 构建 cmap 表 (Format 4) // buildCmapTable 构建cmap表 (Format 4)
// 入参: numGlyphs 字形数量, mapping 字符映射 // 入参: numGlyphs 字形数量, mapping 字符映射
// 返回: []byte cmap表数据 // 返回: []byte cmap表数据
func buildCmapTable(numGlyphs uint16, mapping map[rune]uint16) []byte { func buildCmapTable(numGlyphs uint16, mapping map[rune]uint16) []byte {
@@ -489,7 +489,7 @@ func buildCmapTable(numGlyphs uint16, mapping map[rune]uint16) []byte {
return mainBuf.Bytes() return mainBuf.Bytes()
} }
// shouldBuildCmapFormat12 判断 format 4 是否会溢出 16 位 length // shouldBuildCmapFormat12 判断format 4是否会溢出16位length
func shouldBuildCmapFormat12(mapping map[rune]uint16) bool { func shouldBuildCmapFormat12(mapping map[rune]uint16) bool {
if mapping == nil { if mapping == nil {
return false return false
@@ -526,7 +526,7 @@ func shouldBuildCmapFormat12(mapping map[rune]uint16) bool {
return length > 0xFFFF return length > 0xFFFF
} }
// buildCmapTableFormat12 构建 cmap 表 (Format 12) // buildCmapTableFormat12 构建cmap表 (Format 12)
// 入参: numGlyphs 字形数量, mapping 字符映射 // 入参: numGlyphs 字形数量, mapping 字符映射
// 返回: []byte cmap表数据 // 返回: []byte cmap表数据
func buildCmapTableFormat12(numGlyphs uint16, mapping map[rune]uint16) []byte { func buildCmapTableFormat12(numGlyphs uint16, mapping map[rune]uint16) []byte {
@@ -599,7 +599,7 @@ func buildCmapTableFormat12(numGlyphs uint16, mapping map[rune]uint16) []byte {
return mainBuf.Bytes() return mainBuf.Bytes()
} }
// otfTableRecord OTF 表记录结构 // otfTableRecord OTF表记录结构
// 字段: tag 标签, checksum 校验和, offset 偏移, length 长度, data 数据 // 字段: tag 标签, checksum 校验和, offset 偏移, length 长度, data 数据
type otfTableRecord struct { type otfTableRecord struct {
tag string tag string
@@ -609,7 +609,7 @@ type otfTableRecord struct {
data []byte data []byte
} }
// serializeOTF 序列化 OpenType 字体结构 // serializeOTF 序列化OpenType字体结构
// 入参: tables 表数据映射 // 入参: tables 表数据映射
// 返回: []byte 完整字体数据, error 错误信息 // 返回: []byte 完整字体数据, error 错误信息
func serializeOTF(tables map[string][]byte) ([]byte, error) { func serializeOTF(tables map[string][]byte) ([]byte, error) {
+3 -3
View File
@@ -80,7 +80,7 @@ func decodeImageConfigData(data []byte) (image.Config, string, error) {
return image.Config{}, "", err return image.Config{}, "", err
} }
// decodeBMPConfig 解码 BMP 尺寸 // decodeBMPConfig 解码BMP尺寸
// 入参: data 图片数据 // 入参: data 图片数据
// 返回: image.Config 图片尺寸, error 错误信息 // 返回: image.Config 图片尺寸, error 错误信息
func decodeBMPConfig(data []byte) (image.Config, error) { func decodeBMPConfig(data []byte) (image.Config, error) {
@@ -100,7 +100,7 @@ func decodeBMPConfig(data []byte) (image.Config, error) {
return image.Config{ColorModel: color.NRGBAModel, Width: width, Height: height}, nil return image.Config{ColorModel: color.NRGBAModel, Width: width, Height: height}, nil
} }
// decodeBMPImage 解码 BMP 图片 // decodeBMPImage 解码BMP图片
// 入参: data 图片数据 // 入参: data 图片数据
// 返回: image.Image 图片对象, error 错误信息 // 返回: image.Image 图片对象, error 错误信息
func decodeBMPImage(data []byte) (image.Image, error) { func decodeBMPImage(data []byte) (image.Image, error) {
@@ -153,7 +153,7 @@ func decodeBMPImage(data []byte) (image.Image, error) {
return img, nil return img, nil
} }
// parseBMPHeader 解析 BMP 文件头 // parseBMPHeader 解析BMP文件头
// 入参: data 图片数据 // 入参: data 图片数据
// 返回: int 像素偏移, int 宽度, int 高度, uint16 位数, uint32 压缩方式, error 错误信息 // 返回: int 像素偏移, int 宽度, int 高度, uint16 位数, uint32 压缩方式, error 错误信息
func parseBMPHeader(data []byte) (int, int, int, uint16, uint32, error) { func parseBMPHeader(data []byte) (int, int, int, uint16, uint32, error) {
+2
View File
@@ -196,6 +196,8 @@ type AxialShd struct {
type RadialShd struct { type RadialShd struct {
MapType string `xml:"MapType,attr"` MapType string `xml:"MapType,attr"`
MapUnit float64 `xml:"MapUnit,attr"` MapUnit float64 `xml:"MapUnit,attr"`
Eccentricity float64 `xml:"Eccentricity,attr"`
Angle float64 `xml:"Angle,attr"`
Extend string `xml:"Extend,attr"` Extend string `xml:"Extend,attr"`
StartPoint string `xml:"StartPoint,attr"` StartPoint string `xml:"StartPoint,attr"`
StartRadius float64 `xml:"StartRadius,attr"` StartRadius float64 `xml:"StartRadius,attr"`
+13 -8
View File
@@ -167,8 +167,8 @@ func parseFillPaint(fillColor *FillColor, x, y, pageH float64) any {
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH); gradient != nil { if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
return newShdPaint(gradient, fillColor.AxialShd.Extend, fillColor.AxialShd.MapType, fillColor.AxialShd.MapUnit) return newShdPaint(gradient, fillColor.AxialShd.Extend, fillColor.AxialShd.MapType, fillColor.AxialShd.MapUnit)
} }
if gradient := parseRadialShdGradient(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); gradient != nil { if paint := parseRadialShdPaint(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); paint != nil {
return newShdPaint(gradient, fillColor.RadialShd.Extend, fillColor.RadialShd.MapType, fillColor.RadialShd.MapUnit) return paint
} }
if fillColor.AxialShd != nil { if fillColor.AxialShd != nil {
return parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha) return parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha)
@@ -211,8 +211,8 @@ func parseStrokePaint(strokeColor *StrokeColor, x, y, pageH float64) any {
if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH); gradient != nil { if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
return newShdPaint(gradient, strokeColor.AxialShd.Extend, strokeColor.AxialShd.MapType, strokeColor.AxialShd.MapUnit) return newShdPaint(gradient, strokeColor.AxialShd.Extend, strokeColor.AxialShd.MapType, strokeColor.AxialShd.MapUnit)
} }
if gradient := parseRadialShdGradient(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); gradient != nil { if paint := parseRadialShdPaint(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); paint != nil {
return newShdPaint(gradient, strokeColor.RadialShd.Extend, strokeColor.RadialShd.MapType, strokeColor.RadialShd.MapUnit) return paint
} }
if strokeColor.AxialShd != nil { if strokeColor.AxialShd != nil {
return parseShdColor(strokeColor.AxialShd.Segment, strokeColor.Alpha) return parseShdColor(strokeColor.AxialShd.Segment, strokeColor.Alpha)
@@ -339,10 +339,10 @@ func axialShdClip(ctx *canvas.Context, gradient *canvas.LinearGradient, extend i
return area.ToPath().Transform(axis) return area.ToPath().Transform(axis)
} }
// parseRadialShdGradient 解析径向渐变 // parseRadialShdPaint 解析径向渐变画刷
// 入参: radialShd 径向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度 // 入参: radialShd 径向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度
// 返回: canvas.Gradient 渐变对象 // 返回: *shdPaint 渐变画刷
func parseRadialShdGradient(radialShd *RadialShd, alpha *int, x, y, pageH float64) canvas.Gradient { func parseRadialShdPaint(radialShd *RadialShd, alpha *int, x, y, pageH float64) *shdPaint {
if radialShd == nil || radialShd.EndRadius <= 0 { if radialShd == nil || radialShd.EndRadius <= 0 {
return nil return nil
} }
@@ -357,7 +357,12 @@ func parseRadialShdGradient(radialShd *RadialShd, alpha *int, x, y, pageH float6
} }
startPoint := canvas.Point{X: x + start[0], Y: pageH - (y + start[1])} startPoint := canvas.Point{X: x + start[0], Y: pageH - (y + start[1])}
endPoint := canvas.Point{X: x + end[0], Y: pageH - (y + end[1])} endPoint := canvas.Point{X: x + end[0], Y: pageH - (y + end[1])}
return gradient.ToRadial(startPoint, radialShd.StartRadius, endPoint, radialShd.EndRadius) paint := newShdPaint(gradient.ToRadial(startPoint, radialShd.StartRadius, endPoint, radialShd.EndRadius), radialShd.Extend, radialShd.MapType, radialShd.MapUnit)
if e := radialShd.Eccentricity; 0 < e && e < 1 {
paint.view = canvas.Identity.Translate(startPoint.X, startPoint.Y).Rotate(-radialShd.Angle).Scale(1, math.Sqrt(1-e*e)).Translate(-startPoint.X, -startPoint.Y)
paint.gradient = gradient.ToRadial(startPoint, radialShd.StartRadius, paint.view.Inv().Dot(endPoint), radialShd.EndRadius)
}
return paint
} }
// colorToRGBA 转换颜色对象 // colorToRGBA 转换颜色对象
+10 -11
View File
@@ -211,7 +211,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
style.fillPaint = style.fillColor style.fillPaint = style.fillColor
} }
if shouldFill { if shouldFill {
fillPaint, shadingClip := resolveShdPaint(ctx, style.fillPaint) fillPaint, shadingClip, fillView := resolveShdPaint(ctx, style.fillPaint)
fillClip := intersectClipPath(clipPath, shadingClip) fillClip := intersectClipPath(clipPath, shadingClip)
fp := p fp := p
fillRule := canvas.NonZero fillRule := canvas.NonZero
@@ -232,7 +232,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
ctx.SetFillRule(fillRule) ctx.SetFillRule(fillRule)
ctx.SetFill(fillPaint) ctx.SetFill(fillPaint)
ctx.SetStrokeColor(canvas.Transparent) ctx.SetStrokeColor(canvas.Transparent)
ctx.DrawPath(0, 0, fp) drawShdPath(ctx, fp, fillView)
} }
} }
shouldStroke := true shouldStroke := true
@@ -247,28 +247,27 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
if style.strokePaint == nil { if style.strokePaint == nil {
style.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha) style.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha)
} }
strokePaint, shadingClip := resolveShdPaint(ctx, style.strokePaint) strokePaint, shadingClip, strokeView := resolveShdPaint(ctx, style.strokePaint)
ctx.SetFillColor(canvas.Transparent) ctx.SetFillColor(canvas.Transparent)
ctx.SetStroke(strokePaint) ctx.SetStroke(strokePaint)
ctx.SetStrokeWidth(style.lineWidth) ctx.SetStrokeWidth(style.lineWidth)
ctx.SetStrokeCapper(style.lineCap) ctx.SetStrokeCapper(style.lineCap)
ctx.SetStrokeJoiner(style.lineJoin) ctx.SetStrokeJoiner(style.lineJoin)
if len(style.dashPattern) > 0 { sp := p
ctx.SetDashes(style.dashOffset, style.dashPattern...)
}
strokeClip := intersectClipPath(clipPath, shadingClip)
if strokeClip != nil {
sp := p.Copy()
if len(style.dashPattern) > 0 { if len(style.dashPattern) > 0 {
sp = sp.Dash(style.dashOffset, style.dashPattern...) sp = sp.Dash(style.dashOffset, style.dashPattern...)
ctx.SetDashes(0)
} }
strokeClip := intersectClipPath(clipPath, shadingClip)
if strokeClip != nil || strokeView != canvas.Identity {
sp = sp.Copy()
sp = sp.Stroke(style.lineWidth, style.lineCap, style.lineJoin, canvas.Tolerance) sp = sp.Stroke(style.lineWidth, style.lineCap, style.lineJoin, canvas.Tolerance)
sp = applyClipPath(sp, strokeClip) sp = applyClipPath(sp, strokeClip)
ctx.SetFill(strokePaint) ctx.SetFill(strokePaint)
ctx.SetStrokeColor(canvas.Transparent) ctx.SetStrokeColor(canvas.Transparent)
ctx.DrawPath(0, 0, sp) drawShdPath(ctx, sp, strokeView)
} else { } else {
ctx.DrawPath(0, 0, p) ctx.DrawPath(0, 0, sp)
} }
} }
ctx.Pop() ctx.Pop()
+9 -6
View File
@@ -89,10 +89,11 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
if fillPaint == nil { if fillPaint == nil {
fillPaint = fillColor fillPaint = fillColor
} }
fillPaint, shadingClip := resolveShdPaint(ctx, fillPaint) fillPaint, shadingClip, fillView := resolveShdPaint(ctx, fillPaint)
fillClip := intersectClipPath(clipPath, shadingClip) fillClip := intersectClipPath(clipPath, shadingClip)
strokeStyle := newPathStyle(nil, defaultStroke, 0, obj.Alpha) strokeStyle := newPathStyle(nil, defaultStroke, 0, obj.Alpha)
strokeClip := clipPath strokeClip := clipPath
strokeView := canvas.Identity
if shouldStroke { if shouldStroke {
if dp != nil { if dp != nil {
strokeStyle.applyDrawParam(dp, bx, by, pageH, obj.Alpha) strokeStyle.applyDrawParam(dp, bx, by, pageH, obj.Alpha)
@@ -107,7 +108,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
strokeStyle.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha) strokeStyle.strokePaint = colorWithAlpha(canvas.Black, obj.Alpha)
} }
strokeStyle.scale(ctm) strokeStyle.scale(ctm)
strokeStyle.strokePaint, shadingClip = resolveShdPaint(ctx, strokeStyle.strokePaint) strokeStyle.strokePaint, shadingClip, strokeView = resolveShdPaint(ctx, strokeStyle.strokePaint)
strokeClip = intersectClipPath(strokeClip, shadingClip) strokeClip = intersectClipPath(strokeClip, shadingClip)
} }
fontStyle := canvas.FontRegular fontStyle := canvas.FontRegular
@@ -240,7 +241,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
if shouldFill && fillPaint != nil { if shouldFill && fillPaint != nil {
ctx.SetFill(fillPaint) ctx.SetFill(fillPaint)
ctx.SetStrokeColor(canvas.Transparent) ctx.SetStrokeColor(canvas.Transparent)
ctx.DrawPath(0, 0, applyClipPath(path.Copy(), fillClip)) drawShdPath(ctx, applyClipPath(path.Copy(), fillClip), fillView)
} }
if len(strokeStyle.dashPattern) > 0 { if len(strokeStyle.dashPattern) > 0 {
path = path.Dash(strokeStyle.dashOffset, strokeStyle.dashPattern...) path = path.Dash(strokeStyle.dashOffset, strokeStyle.dashPattern...)
@@ -248,7 +249,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
path = path.Stroke(strokeStyle.lineWidth, strokeStyle.lineCap, strokeStyle.lineJoin, canvas.Tolerance) path = path.Stroke(strokeStyle.lineWidth, strokeStyle.lineCap, strokeStyle.lineJoin, canvas.Tolerance)
ctx.SetFill(strokeStyle.strokePaint) ctx.SetFill(strokeStyle.strokePaint)
ctx.SetStrokeColor(canvas.Transparent) ctx.SetStrokeColor(canvas.Transparent)
ctx.DrawPath(0, 0, applyClipPath(path, strokeClip)) drawShdPath(ctx, applyClipPath(path, strokeClip), strokeView)
if hasUnderline { if hasUnderline {
underline := &canvas.Path{} underline := &canvas.Path{}
underline.MoveTo(0, -sizeMM*0.1) underline.MoveTo(0, -sizeMM*0.1)
@@ -256,8 +257,10 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
underline = underline.Stroke(sizeMM*0.05, canvas.ButtCap, canvas.MiterJoin, canvas.Tolerance) underline = underline.Stroke(sizeMM*0.05, canvas.ButtCap, canvas.MiterJoin, canvas.Tolerance)
if shouldFill { if shouldFill {
ctx.SetFillColor(fillColor) ctx.SetFillColor(fillColor)
}
ctx.DrawPath(0, 0, applyClipPath(underline.Transform(transform), clipPath)) ctx.DrawPath(0, 0, applyClipPath(underline.Transform(transform), clipPath))
} else {
drawShdPath(ctx, applyClipPath(underline.Transform(transform), strokeClip), strokeView)
}
} }
continue continue
} }
@@ -274,7 +277,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
textTransform = textTransform.Mul(glyphMatrix) textTransform = textTransform.Mul(glyphMatrix)
} }
glyphPath = applyClipPath(glyphPath.Copy().Transform(textTransform.Scale(scaleX, 1)), fillClip) glyphPath = applyClipPath(glyphPath.Copy().Transform(textTransform.Scale(scaleX, 1)), fillClip)
ctx.DrawPath(0, 0, glyphPath) drawShdPath(ctx, glyphPath, fillView)
if hasUnderline { if hasUnderline {
uw := sizeMM * 0.05 uw := sizeMM * 0.05
off := sizeMM * 0.1 off := sizeMM * 0.1
+33 -17
View File
@@ -23,6 +23,7 @@ import (
type shdPaint struct { type shdPaint struct {
gradient canvas.Gradient gradient canvas.Gradient
view canvas.Matrix
extend int extend int
mapType string mapType string
period float64 period float64
@@ -44,46 +45,49 @@ func newShdPaint(gradient canvas.Gradient, extend, mapType string, mapUnit float
if mapUnit > 0 && length > 0 { if mapUnit > 0 && length > 0 {
period = mapUnit / length period = mapUnit / length
} }
return &shdPaint{gradient: gradient, extend: flags, mapType: mapType, period: period} return &shdPaint{gradient: gradient, view: canvas.Identity, extend: flags, mapType: mapType, period: period}
} }
// resolveShdPaint 解析渐变画刷和裁剪区域 // resolveShdPaint 解析渐变画刷和裁剪区域
// 入参: ctx 画布上下文, paint 画刷 // 入参: ctx 画布上下文, paint 画刷
// 返回: any 画刷, *canvas.Path 裁剪区域 // 返回: any 画刷, *canvas.Path 裁剪区域, canvas.Matrix 渐变变换
func resolveShdPaint(ctx *canvas.Context, paint any) (any, *canvas.Path) { func resolveShdPaint(ctx *canvas.Context, paint any) (any, *canvas.Path, canvas.Matrix) {
s, ok := paint.(*shdPaint) s, ok := paint.(*shdPaint)
if !ok { if !ok {
return paint, nil return paint, nil, canvas.Identity
} }
switch g := s.gradient.(type) { switch g := s.gradient.(type) {
case *canvas.LinearGradient: case *canvas.LinearGradient:
clip := axialShdClip(ctx, g, s.extend) clip := axialShdClip(ctx, g, s.extend)
if s.mapType != "Reflect" { if s.mapType != "Reflect" {
return g, clip return g, clip, s.view
} }
d := g.End.Sub(g.Start) d := g.End.Sub(g.Start)
axis := canvas.Matrix{{d.X, -d.Y, g.Start.X}, {d.Y, d.X, g.Start.Y}} axis := canvas.Matrix{{d.X, -d.Y, g.Start.X}, {d.Y, d.X, g.Start.Y}}
area := shdCanvasBounds(ctx).Transform(axis.Inv()) area := shdCanvasBounds(ctx).Transform(axis.Inv())
lo, hi := s.rangeLimits(area.X0, area.X1) lo, hi := s.rangeLimits(area.X0, area.X1)
if hi <= lo { if hi <= lo {
return g, &canvas.Path{} return g, &canvas.Path{}, s.view
} }
gradient := reflectShdGrad(g.Grad, s.period, lo, hi) gradient := reflectShdGrad(g.Grad, s.period, lo, hi)
return gradient.ToLinear(g.Start.Add(d.Mul(lo)), g.Start.Add(d.Mul(hi))), clip return gradient.ToLinear(g.Start.Add(d.Mul(lo)), g.Start.Add(d.Mul(hi))), clip, s.view
case *canvas.RadialGradient: case *canvas.RadialGradient:
d, dr := g.C1.Sub(g.C0), g.R1-g.R0 d, dr := g.C1.Sub(g.C0), g.R1-g.R0
length := d.Length() length := d.Length()
if length >= math.Abs(dr) { if length >= math.Abs(dr) {
return g, nil return g, nil, s.view
}
bounds := shdCanvasBounds(ctx).Transform(s.view.Inv())
clip := radialShdClip(g, s.extend, bounds)
if clip != nil && s.view != canvas.Identity {
clip = clip.Transform(s.view)
} }
clip := radialShdClip(ctx, g, s.extend)
if s.mapType != "Reflect" { if s.mapType != "Reflect" {
return g, clip return g, clip, s.view
} }
lo, hi := 0.0, 1.0 lo, hi := 0.0, 1.0
if s.extend != 0 { if s.extend != 0 {
distance := 0.0 distance := 0.0
bounds := shdCanvasBounds(ctx)
for _, point := range []canvas.Point{{X: bounds.X0, Y: bounds.Y0}, {X: bounds.X1, Y: bounds.Y0}, {X: bounds.X1, Y: bounds.Y1}, {X: bounds.X0, Y: bounds.Y1}} { for _, point := range []canvas.Point{{X: bounds.X0, Y: bounds.Y0}, {X: bounds.X1, Y: bounds.Y0}, {X: bounds.X1, Y: bounds.Y1}, {X: bounds.X0, Y: bounds.Y1}} {
distance = math.Max(distance, point.Sub(g.C0).Length()) distance = math.Max(distance, point.Sub(g.C0).Length())
} }
@@ -96,12 +100,24 @@ func resolveShdPaint(ctx *canvas.Context, paint any) (any, *canvas.Path) {
hi = math.Min(hi, -g.R0/dr) hi = math.Min(hi, -g.R0/dr)
} }
if hi <= lo { if hi <= lo {
return g, &canvas.Path{} return g, &canvas.Path{}, s.view
} }
gradient := reflectShdGrad(g.Grad, s.period, lo, hi) gradient := reflectShdGrad(g.Grad, s.period, lo, hi)
return gradient.ToRadial(g.C0.Add(d.Mul(lo)), math.Max(0, g.R0+dr*lo), g.C0.Add(d.Mul(hi)), math.Max(0, g.R0+dr*hi)), clip return gradient.ToRadial(g.C0.Add(d.Mul(lo)), math.Max(0, g.R0+dr*lo), g.C0.Add(d.Mul(hi)), math.Max(0, g.R0+dr*hi)), clip, s.view
} }
return s.gradient, nil return s.gradient, nil, s.view
}
// drawShdPath 绘制渐变路径并保持图形轮廓不变
// 入参: ctx 画布上下文, path 填充路径, view 渐变变换
func drawShdPath(ctx *canvas.Context, path *canvas.Path, view canvas.Matrix) {
if view == canvas.Identity {
ctx.DrawPath(0, 0, path)
return
}
origin := ctx.CoordView().Dot(canvas.Point{})
m := ctx.CoordSystemView().Mul(ctx.View()).Translate(origin.X, origin.Y).Mul(view)
ctx.RenderPath(path.Copy().Transform(view.Inv()), ctx.Style, m)
} }
// rangeLimits 限制渐变延伸区间 // rangeLimits 限制渐变延伸区间
@@ -131,9 +147,9 @@ func shdCanvasBounds(ctx *canvas.Context) canvas.Rect {
} }
// radialShdClip 获取包含双圆的径向渐变裁剪区域 // radialShdClip 获取包含双圆的径向渐变裁剪区域
// 入参: ctx 画布上下文, g 径向渐变, extend 延伸方向 // 入参: g 径向渐变, extend 延伸方向, bounds 渐变坐标系下的画布边界
// 返回: *canvas.Path 裁剪区域 // 返回: *canvas.Path 裁剪区域
func radialShdClip(ctx *canvas.Context, g *canvas.RadialGradient, extend int) *canvas.Path { func radialShdClip(g *canvas.RadialGradient, extend int, bounds canvas.Rect) *canvas.Path {
inner, outer := g.C0, g.C1 inner, outer := g.C0, g.C1
r0, r1 := g.R0, g.R1 r0, r1 := g.R0, g.R1
if r1 < r0 { if r1 < r0 {
@@ -147,7 +163,7 @@ func radialShdClip(ctx *canvas.Context, g *canvas.RadialGradient, extend int) *c
if extend&2 == 0 { if extend&2 == 0 {
clip = canvas.Circle(r1).Translate(outer.X, outer.Y) clip = canvas.Circle(r1).Translate(outer.X, outer.Y)
} else { } else {
clip = shdCanvasBounds(ctx).ToPath() clip = bounds.ToPath()
} }
if extend&1 == 0 && r0 > 0 { if extend&1 == 0 && r0 > 0 {
clip = clip.Not(canvas.Circle(r0).Translate(inner.X, inner.Y)) clip = clip.Not(canvas.Circle(r0).Translate(inner.X, inner.Y))