mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
This commit is contained in:
1 parent
f27c5b666e
commit
3cd6cb3afd
10 files changed
+136
-111
No files matched your search
+2
-2
@@ -49,9 +49,9 @@ func FixFontDataAggressive(data []byte, fixCmap, fixName bool) (bool, []byte, ma
|
||||
return fixed, newData, mapping, mc, err
|
||||
}
|
||||
|
||||
// isBareCFFData 检查是否为 CFF 裸字体数据
|
||||
// isBareCFFData 检查是否为CFF裸字体数据
|
||||
// 入参: data 字体数据
|
||||
// 返回: bool 是否为 CFF 裸数据
|
||||
// 返回: bool 是否为CFF裸数据
|
||||
func isBareCFFData(data []byte) bool {
|
||||
if len(data) < 4 {
|
||||
return false
|
||||
|
||||
+31
-31
@@ -26,7 +26,7 @@ import (
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
)
|
||||
|
||||
// wrapCFFToOTF 将 CFF 裸数据包装为 OpenType 字体格式
|
||||
// wrapCFFToOTF 将CFF裸数据包装为OpenType字体格式
|
||||
// 入参: cffData CFF字体数据
|
||||
// 返回: []byte OTF字体数据, 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
|
||||
}
|
||||
|
||||
// cffDict 使用 float64 存储所有数值,以统一处理整数和实数
|
||||
// cffDict 使用float64存储所有数值,以统一处理整数和实数
|
||||
type cffDict map[int][]float64
|
||||
|
||||
// sanitizeCFF 尝试清洗CFF数据,转换CID字体并合并FontMatrix
|
||||
@@ -246,7 +246,7 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
return newCFF.Bytes(), nil
|
||||
}
|
||||
|
||||
// sanitizeMultiFDCFF 清洗多 FD 的 CID CFF 数据
|
||||
// sanitizeMultiFDCFF 清洗多FD的CID CFF数据
|
||||
// 入参: data 原始CFF数据, hdrSize 头部大小, nameIndexData 名称索引, topDict 顶层字典
|
||||
// 入参: stringIndexData 字符串索引, globalSubrIndexData 全局子程序索引, fdArrOff FDArray偏移, fdCount FD数量
|
||||
// 返回: []byte 清洗后的CFF数据, error 错误信息
|
||||
@@ -332,7 +332,7 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
return newCFF.Bytes(), nil
|
||||
}
|
||||
|
||||
// parseCFFAndCountGlyphs 解析 CFF 头部并统计字形数量
|
||||
// parseCFFAndCountGlyphs 解析CFF头部并统计字形数量
|
||||
// 入参: data CFF数据
|
||||
// 返回: 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")
|
||||
}
|
||||
|
||||
// multiplyAffine 2x3 仿射矩阵乘法
|
||||
// multiplyAffine 2x3仿射矩阵乘法
|
||||
// 入参: a 矩阵A, b 矩阵B
|
||||
// 返回: []float64 结果矩阵
|
||||
func multiplyAffine(a, b []float64) []float64 {
|
||||
@@ -384,7 +384,7 @@ func multiplyAffine(a, b []float64) []float64 {
|
||||
}
|
||||
}
|
||||
|
||||
// parseCFFDict 解析 CFF 字典数据
|
||||
// parseCFFDict 解析CFF字典数据
|
||||
// 入参: data 字典数据
|
||||
// 返回: cffDict 解析后的字典映射
|
||||
func parseCFFDict(data []byte) cffDict {
|
||||
@@ -442,7 +442,7 @@ func parseCFFDict(data []byte) cffDict {
|
||||
return dict
|
||||
}
|
||||
|
||||
// parseCFFReal 解析 CFF 实数编码
|
||||
// parseCFFReal 解析CFF实数编码
|
||||
// 入参: data 数据切片
|
||||
// 返回: string 实数字符串, int 消耗字节数
|
||||
func parseCFFReal(data []byte) (string, int) {
|
||||
@@ -478,7 +478,7 @@ func parseCFFReal(data []byte) (string, int) {
|
||||
return sb.String(), i
|
||||
}
|
||||
|
||||
// encodeCFFDict 编码 CFF 字典 (仅使用 float64 操作数)
|
||||
// encodeCFFDict 编码CFF字典 (仅使用float64操作数)
|
||||
// 入参: dict CFF字典映射
|
||||
// 返回: []byte 编码后的字典数据
|
||||
func encodeCFFDict(dict cffDict) []byte {
|
||||
@@ -503,7 +503,7 @@ func encodeCFFDict(dict cffDict) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// encodeNumberCFF 编码单个数值到 CFF 格式
|
||||
// encodeNumberCFF 编码单个数值到CFF格式
|
||||
// 入参: buf 缓冲区, val 数值
|
||||
func encodeNumberCFF(buf *bytes.Buffer, val float64) {
|
||||
if val == math.Trunc(val) {
|
||||
@@ -557,7 +557,7 @@ func encodeNumberCFF(buf *bytes.Buffer, val float64) {
|
||||
}
|
||||
}
|
||||
|
||||
// encodeCFFIndex 编码 CFF 索引结构
|
||||
// encodeCFFIndex 编码CFF索引结构
|
||||
// 入参: items 数据项列表
|
||||
// 返回: []byte 编码后的索引数据
|
||||
func encodeCFFIndex(items []([]byte)) []byte {
|
||||
@@ -602,7 +602,7 @@ func putOffset(buf *bytes.Buffer, val int, size int) {
|
||||
buf.Write(tmp[4-size:])
|
||||
}
|
||||
|
||||
// getCFFIndexCount 读取 CFF 索引的计数和大小
|
||||
// getCFFIndexCount 读取CFF索引的计数和大小
|
||||
// 入参: data CFF数据, offset 偏移量
|
||||
// 返回: 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)
|
||||
}
|
||||
|
||||
// getCFFIndexData 读取 CFF 索引的数据块
|
||||
// getCFFIndexData 读取CFF索引的数据块
|
||||
// 入参: data CFF数据, offset 偏移量
|
||||
// 返回: []byte 索引数据(已去除offsets), 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
|
||||
}
|
||||
|
||||
// readCFFIndexItems 读取 CFF 索引中的所有数据项
|
||||
// readCFFIndexItems 读取CFF索引中的所有数据项
|
||||
// 入参: data CFF数据, offset 索引偏移
|
||||
// 返回: [][]byte 数据项列表
|
||||
func readCFFIndexItems(data []byte, offset int) [][]byte {
|
||||
@@ -698,7 +698,7 @@ func readCFFIndexItems(data []byte, offset int) [][]byte {
|
||||
return items
|
||||
}
|
||||
|
||||
// parseCFFFDSelect 解析 CID CFF 的 FDSelect
|
||||
// parseCFFFDSelect 解析CID CFF的FDSelect
|
||||
// 入参: data CFF数据, offset FDSelect偏移, numGlyphs 字形数量
|
||||
// 返回: []int 字形对应的FD索引
|
||||
func parseCFFFDSelect(data []byte, offset int, numGlyphs int) []int {
|
||||
@@ -754,7 +754,7 @@ func parseCFFFDSelect(data []byte, offset int, numGlyphs int) []int {
|
||||
return result
|
||||
}
|
||||
|
||||
// readCFFLocalSubrs 读取 CID CFF 的本地子程序
|
||||
// readCFFLocalSubrs 读取CID CFF的本地子程序
|
||||
// 入参: data CFF数据, fdArrOff FDArray偏移, fdCount FD数量
|
||||
// 返回: [][][]byte 本地子程序列表, []cffDict Private字典列表
|
||||
func readCFFLocalSubrs(data []byte, fdArrOff int, fdCount int) ([][][]byte, []cffDict) {
|
||||
@@ -792,7 +792,7 @@ type type2Operand struct {
|
||||
outStart int
|
||||
}
|
||||
|
||||
// inlineType2CharString 内联 Type2 CharString 子程序调用
|
||||
// inlineType2CharString 内联Type2 CharString子程序调用
|
||||
// 入参: data CharString数据, localSubrs 本地子程序, globalSubrs 全局子程序, depth 递归深度
|
||||
// 返回: []byte 内联后的CharString数据
|
||||
func inlineType2CharString(data []byte, localSubrs, globalSubrs [][]byte, depth int) []byte {
|
||||
@@ -881,7 +881,7 @@ func inlineType2CharString(data []byte, localSubrs, globalSubrs [][]byte, depth
|
||||
return out
|
||||
}
|
||||
|
||||
// removeType2Hints 移除 Type2 CharString 的 hint 指令
|
||||
// removeType2Hints 移除Type2 CharString的hint指令
|
||||
// 入参: data CharString数据
|
||||
// 返回: []byte 移除hint后的CharString数据
|
||||
func removeType2Hints(data []byte) []byte {
|
||||
@@ -954,7 +954,7 @@ func removeType2Hints(data []byte) []byte {
|
||||
return out
|
||||
}
|
||||
|
||||
// cffSubrBias 获取 Type2 子程序偏移
|
||||
// cffSubrBias 获取Type2子程序偏移
|
||||
// 入参: count 子程序数量
|
||||
// 返回: int 偏移量
|
||||
func cffSubrBias(count int) int {
|
||||
@@ -967,7 +967,7 @@ func cffSubrBias(count int) int {
|
||||
return 32768
|
||||
}
|
||||
|
||||
// parseCFFWidths 从 CFF 数据中解析 Glyph 宽度
|
||||
// parseCFFWidths 从CFF数据中解析Glyph宽度
|
||||
// 入参: data CFF数据, numGlyphs 字形数量
|
||||
// 返回: []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")
|
||||
}
|
||||
|
||||
// scanCharStringWidth 扫描 CharString 获取宽度
|
||||
// scanCharStringWidth 扫描CharString获取宽度
|
||||
// 入参: data CharString数据, nominal, def 默认宽度值
|
||||
// 返回: 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:])))
|
||||
}
|
||||
|
||||
// parseNumberType2 解析 Number (Type 2)
|
||||
// parseNumberType2 解析Number (Type 2)
|
||||
// 入参: data 数据, idx 索引
|
||||
// 返回: float64 浮点值
|
||||
func parseNumberType2(data []byte, idx int) float64 {
|
||||
@@ -1135,7 +1135,7 @@ func readCFFOffset(data []byte, pos, size int) int {
|
||||
return val
|
||||
}
|
||||
|
||||
// getCFFCharsetInfo 读取 CFF 字符集和 ROS 信息
|
||||
// getCFFCharsetInfo 读取CFF字符集和ROS信息
|
||||
// 入参: data CFF数据, numGlyphs 字形数量
|
||||
// 返回: []int SID或CID列表, string Registry, string Ordering, 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
|
||||
}
|
||||
|
||||
// getCmapFromCFF 从 CFF 数据中恢复 Unicode 映射
|
||||
// getCmapFromCFF 从CFF数据中恢复Unicode映射
|
||||
// 入参: data CFF数据, numGlyphs 字形数量
|
||||
// 返回: 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
|
||||
}
|
||||
|
||||
// getCFFCIDRuneMap 获取 CID 到包装字体字符的映射
|
||||
// getCFFCIDRuneMap 获取CID到包装字体字符的映射
|
||||
// 入参: data CFF或OpenType字体数据
|
||||
// 返回: map[uint16]rune CID映射
|
||||
func getCFFCIDRuneMap(data []byte) map[uint16]rune {
|
||||
@@ -1280,7 +1280,7 @@ func getCFFCIDRuneMap(data []byte) map[uint16]rune {
|
||||
return result
|
||||
}
|
||||
|
||||
// getCFFData 获取字体中的 CFF 数据
|
||||
// getCFFData 获取字体中的CFF数据
|
||||
// 入参: data 字体数据
|
||||
// 返回: []byte CFF数据
|
||||
func getCFFData(data []byte) []byte {
|
||||
@@ -1314,7 +1314,7 @@ func getCFFData(data []byte) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseCFFCharset 解析 CFF 字符集并返回 SID 列表
|
||||
// parseCFFCharset 解析CFF字符集并返回SID列表
|
||||
// 入参: data CFF数据, offset 偏移量, numGlyphs 字形数量
|
||||
// 返回: []int SID列表
|
||||
func parseCFFCharset(data []byte, offset int, numGlyphs int) []int {
|
||||
@@ -1357,7 +1357,7 @@ func parseCFFCharset(data []byte, offset int, numGlyphs int) []int {
|
||||
return sids
|
||||
}
|
||||
|
||||
// readStringIndexItem 读取 CFF 字符串索引项
|
||||
// readStringIndexItem 读取CFF字符串索引项
|
||||
// 入参: data CFF数据, offset 索引偏移, idx 索引号
|
||||
// 返回: 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])
|
||||
}
|
||||
|
||||
// getCFFROS 读取 CID 字体 ROS 信息
|
||||
// getCFFROS 读取CID字体ROS信息
|
||||
// 入参: data CFF数据, stringIndexOff 字符串索引偏移, td 顶层字典
|
||||
// 返回: string Registry, string Ordering
|
||||
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
|
||||
}
|
||||
|
||||
// getCFFSIDString 读取 CFF SID 字符串
|
||||
// getCFFSIDString 读取CFF SID字符串
|
||||
// 入参: data CFF数据, stringIndexOff 字符串索引偏移, sid 字符串ID
|
||||
// 返回: string 字符串内容
|
||||
func getCFFSIDString(data []byte, stringIndexOff int, sid int) string {
|
||||
@@ -1412,7 +1412,7 @@ func getCFFSIDString(data []byte, stringIndexOff int, sid int) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// adobeGB1CIDToUnicode 将 Adobe-GB1 CID 转为 Unicode
|
||||
// adobeGB1CIDToUnicode 将Adobe-GB1 CID转为Unicode
|
||||
// 入参: cid 字符CID
|
||||
// 返回: rune Unicode字符, bool 是否成功
|
||||
func adobeGB1CIDToUnicode(cid int) (rune, bool) {
|
||||
@@ -1541,7 +1541,7 @@ func getUnicodeFromName(name string) rune {
|
||||
return 0
|
||||
}
|
||||
|
||||
// cffStandardStrings CFF 标准字符串表
|
||||
// cffStandardStrings CFF标准字符串表
|
||||
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",
|
||||
"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
@@ -18,7 +18,7 @@ import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// fixTrueType 尝试修复 TrueType/OpenType 字体数据
|
||||
// fixTrueType 尝试修复TrueType/OpenType字体数据
|
||||
// 入参: data 原始字体数据, fixCmap 是否修复cmap, fixName 是否修复name
|
||||
// 返回: bool 是否修复, []byte 修复后数据, map[rune]uint16 字符映射, bool 是否缺失cmap, 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
|
||||
}
|
||||
|
||||
// hasBadPostTable 检查 post 表是否会被 canvas/font 拒绝
|
||||
// hasBadPostTable 检查post表是否会被canvas/font拒绝
|
||||
func hasBadPostTable(data []byte, numGlyphs uint16, isTrueType bool) bool {
|
||||
if len(data) < 32 {
|
||||
return true
|
||||
@@ -217,7 +217,7 @@ func hasBadPostTable(data []byte, numGlyphs uint16, isTrueType bool) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// hasUsableCmap 检查是否存在可用的 cmap 子表
|
||||
// hasUsableCmap 检查是否存在可用的cmap子表
|
||||
// 入参: data cmap表数据
|
||||
// 返回: bool 是否可用
|
||||
func hasUsableCmap(data []byte) bool {
|
||||
@@ -249,8 +249,8 @@ func hasUsableCmap(data []byte) bool {
|
||||
return usable
|
||||
}
|
||||
|
||||
// supportedCmapFormat 判断 cmap 子表格式是否可用
|
||||
// 入参: format cmap 子表格式, platformID 平台ID, encodingID 编码ID
|
||||
// supportedCmapFormat 判断cmap子表格式是否可用
|
||||
// 入参: format cmap子表格式, platformID 平台ID, encodingID 编码ID
|
||||
// 返回: bool 是否可用
|
||||
func supportedCmapFormat(format, platformID, encodingID uint16) bool {
|
||||
switch format {
|
||||
|
||||
+20
-20
@@ -48,7 +48,7 @@ func calcTableChecksum(data []byte) uint32 {
|
||||
return sum
|
||||
}
|
||||
|
||||
// parseCmapMappings 解析 cmap 字符映射
|
||||
// parseCmapMappings 解析cmap字符映射
|
||||
// 入参: data cmap表数据
|
||||
// 返回: map[rune]uint16 字符到字形映射
|
||||
func parseCmapMappings(data []byte) map[rune]uint16 {
|
||||
@@ -87,7 +87,7 @@ func parseCmapMappings(data []byte) map[rune]uint16 {
|
||||
return result
|
||||
}
|
||||
|
||||
// parseCmapFormat0 解析 cmap format 0
|
||||
// parseCmapFormat0 解析cmap format 0
|
||||
// 入参: data 子表数据, result 字符映射
|
||||
func parseCmapFormat0(data []byte, result map[rune]uint16) {
|
||||
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 字符映射
|
||||
func parseCmapFormat4(data []byte, result map[rune]uint16) {
|
||||
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 字符映射
|
||||
func parseCmapFormat6(data []byte, result map[rune]uint16) {
|
||||
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 字符映射
|
||||
func parseCmapFormat12(data []byte, result map[rune]uint16) {
|
||||
if len(data) < 16 {
|
||||
@@ -207,7 +207,7 @@ func addPackedGlyphMapping(mapping map[rune]uint16, numGlyphs uint16) {
|
||||
}
|
||||
}
|
||||
|
||||
// buildHeadTable 构建 head 表
|
||||
// buildHeadTable 构建head表
|
||||
// 入参: unitsPerEm 每em单位数
|
||||
// 返回: []byte head表数据
|
||||
func buildHeadTable(unitsPerEm uint16) []byte {
|
||||
@@ -233,7 +233,7 @@ func buildHeadTable(unitsPerEm uint16) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// buildHheaTable 构建 hhea 表
|
||||
// buildHheaTable 构建hhea表
|
||||
// 入参: numGlyphs 字形数量
|
||||
// 返回: []byte hhea表数据
|
||||
func buildHheaTable(numGlyphs uint16) []byte {
|
||||
@@ -259,7 +259,7 @@ func buildHheaTable(numGlyphs uint16) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// buildCFFMaxpTable 构建 CFF 轮廓使用的 maxp 0.5 表
|
||||
// buildCFFMaxpTable 构建CFF轮廓使用的maxp 0.5表
|
||||
// 入参: numGlyphs 字形数量
|
||||
// 返回: []byte maxp表数据
|
||||
func buildCFFMaxpTable(numGlyphs uint16) []byte {
|
||||
@@ -269,7 +269,7 @@ func buildCFFMaxpTable(numGlyphs uint16) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// buildTrueTypeMaxpTable 构建 TrueType 轮廓使用的 maxp 1.0 表
|
||||
// buildTrueTypeMaxpTable 构建TrueType轮廓使用的maxp 1.0表
|
||||
// 入参: numGlyphs 字形数量
|
||||
// 返回: []byte maxp表数据
|
||||
func buildTrueTypeMaxpTable(numGlyphs uint16) []byte {
|
||||
@@ -282,13 +282,13 @@ func buildTrueTypeMaxpTable(numGlyphs uint16) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// buildOS2Table 构建 OS/2 表 (使用默认 Metrics)
|
||||
// buildOS2Table 构建OS/2表 (使用默认Metrics)
|
||||
// 返回: []byte OS/2表数据
|
||||
func buildOS2Table() []byte {
|
||||
return buildOS2TableWithMetrics(800, -200)
|
||||
}
|
||||
|
||||
// buildOS2TableWithMetrics 构建 OS/2 表
|
||||
// buildOS2TableWithMetrics 构建OS/2表
|
||||
// 入参: ascender 上升部, descender 下降部
|
||||
// 返回: []byte OS/2表数据
|
||||
func buildOS2TableWithMetrics(ascender, descender int16) []byte {
|
||||
@@ -337,7 +337,7 @@ func buildOS2TableWithMetrics(ascender, descender int16) []byte {
|
||||
return os2.Bytes()
|
||||
}
|
||||
|
||||
// buildNameTable 构建 name 表 (最小化)
|
||||
// buildNameTable 构建name表 (最小化)
|
||||
// 返回: []byte name表数据
|
||||
func buildNameTable() []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -347,7 +347,7 @@ func buildNameTable() []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// buildPostTable 构建 post 表 (版本 3.0, 无字形名称)
|
||||
// buildPostTable 构建post表 (版本3.0, 无字形名称)
|
||||
// 返回: []byte post表数据
|
||||
func buildPostTable() []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -363,7 +363,7 @@ func buildPostTable() []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// buildHmtxTable 构建 hmtx 表
|
||||
// buildHmtxTable 构建hmtx表
|
||||
// 入参: widths 宽度列表
|
||||
// 返回: []byte hmtx表数据
|
||||
func buildHmtxTable(widths []uint16) []byte {
|
||||
@@ -375,7 +375,7 @@ func buildHmtxTable(widths []uint16) []byte {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// cmapSegment cmap 表段结构
|
||||
// cmapSegment cmap表段结构
|
||||
// 字段: start 开始字符, end 结束字符, delta 增量, offset 偏移
|
||||
type cmapSegment struct {
|
||||
start, end uint16
|
||||
@@ -383,7 +383,7 @@ type cmapSegment struct {
|
||||
offset uint16
|
||||
}
|
||||
|
||||
// buildCmapTable 构建 cmap 表 (Format 4)
|
||||
// buildCmapTable 构建cmap表 (Format 4)
|
||||
// 入参: numGlyphs 字形数量, mapping 字符映射
|
||||
// 返回: []byte cmap表数据
|
||||
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()
|
||||
}
|
||||
|
||||
// shouldBuildCmapFormat12 判断 format 4 是否会溢出 16 位 length
|
||||
// shouldBuildCmapFormat12 判断format 4是否会溢出16位length
|
||||
func shouldBuildCmapFormat12(mapping map[rune]uint16) bool {
|
||||
if mapping == nil {
|
||||
return false
|
||||
@@ -526,7 +526,7 @@ func shouldBuildCmapFormat12(mapping map[rune]uint16) bool {
|
||||
return length > 0xFFFF
|
||||
}
|
||||
|
||||
// buildCmapTableFormat12 构建 cmap 表 (Format 12)
|
||||
// buildCmapTableFormat12 构建cmap表 (Format 12)
|
||||
// 入参: numGlyphs 字形数量, mapping 字符映射
|
||||
// 返回: []byte cmap表数据
|
||||
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()
|
||||
}
|
||||
|
||||
// otfTableRecord OTF 表记录结构
|
||||
// otfTableRecord OTF表记录结构
|
||||
// 字段: tag 标签, checksum 校验和, offset 偏移, length 长度, data 数据
|
||||
type otfTableRecord struct {
|
||||
tag string
|
||||
@@ -609,7 +609,7 @@ type otfTableRecord struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
// serializeOTF 序列化 OpenType 字体结构
|
||||
// serializeOTF 序列化OpenType字体结构
|
||||
// 入参: tables 表数据映射
|
||||
// 返回: []byte 完整字体数据, error 错误信息
|
||||
func serializeOTF(tables map[string][]byte) ([]byte, error) {
|
||||
|
||||
+3
-3
@@ -80,7 +80,7 @@ func decodeImageConfigData(data []byte) (image.Config, string, error) {
|
||||
return image.Config{}, "", err
|
||||
}
|
||||
|
||||
// decodeBMPConfig 解码 BMP 尺寸
|
||||
// decodeBMPConfig 解码BMP尺寸
|
||||
// 入参: data 图片数据
|
||||
// 返回: 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
|
||||
}
|
||||
|
||||
// decodeBMPImage 解码 BMP 图片
|
||||
// decodeBMPImage 解码BMP图片
|
||||
// 入参: data 图片数据
|
||||
// 返回: image.Image 图片对象, error 错误信息
|
||||
func decodeBMPImage(data []byte) (image.Image, error) {
|
||||
@@ -153,7 +153,7 @@ func decodeBMPImage(data []byte) (image.Image, error) {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// parseBMPHeader 解析 BMP 文件头
|
||||
// parseBMPHeader 解析BMP文件头
|
||||
// 入参: data 图片数据
|
||||
// 返回: int 像素偏移, int 宽度, int 高度, uint16 位数, uint32 压缩方式, error 错误信息
|
||||
func parseBMPHeader(data []byte) (int, int, int, uint16, uint32, error) {
|
||||
|
||||
+10
-8
@@ -194,14 +194,16 @@ type AxialShd struct {
|
||||
|
||||
// RadialShd 径向渐变
|
||||
type RadialShd struct {
|
||||
MapType string `xml:"MapType,attr"`
|
||||
MapUnit float64 `xml:"MapUnit,attr"`
|
||||
Extend string `xml:"Extend,attr"`
|
||||
StartPoint string `xml:"StartPoint,attr"`
|
||||
StartRadius float64 `xml:"StartRadius,attr"`
|
||||
EndPoint string `xml:"EndPoint,attr"`
|
||||
EndRadius float64 `xml:"EndRadius,attr"`
|
||||
Segment []ShdSegment `xml:"Segment"`
|
||||
MapType string `xml:"MapType,attr"`
|
||||
MapUnit float64 `xml:"MapUnit,attr"`
|
||||
Eccentricity float64 `xml:"Eccentricity,attr"`
|
||||
Angle float64 `xml:"Angle,attr"`
|
||||
Extend string `xml:"Extend,attr"`
|
||||
StartPoint string `xml:"StartPoint,attr"`
|
||||
StartRadius float64 `xml:"StartRadius,attr"`
|
||||
EndPoint string `xml:"EndPoint,attr"`
|
||||
EndRadius float64 `xml:"EndRadius,attr"`
|
||||
Segment []ShdSegment `xml:"Segment"`
|
||||
}
|
||||
|
||||
// ShdSegment 渐变分段
|
||||
|
||||
+13
-8
@@ -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 {
|
||||
return newShdPaint(gradient, fillColor.AxialShd.Extend, fillColor.AxialShd.MapType, fillColor.AxialShd.MapUnit)
|
||||
}
|
||||
if gradient := parseRadialShdGradient(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
|
||||
return newShdPaint(gradient, fillColor.RadialShd.Extend, fillColor.RadialShd.MapType, fillColor.RadialShd.MapUnit)
|
||||
if paint := parseRadialShdPaint(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); paint != nil {
|
||||
return paint
|
||||
}
|
||||
if fillColor.AxialShd != nil {
|
||||
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 {
|
||||
return newShdPaint(gradient, strokeColor.AxialShd.Extend, strokeColor.AxialShd.MapType, strokeColor.AxialShd.MapUnit)
|
||||
}
|
||||
if gradient := parseRadialShdGradient(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
|
||||
return newShdPaint(gradient, strokeColor.RadialShd.Extend, strokeColor.RadialShd.MapType, strokeColor.RadialShd.MapUnit)
|
||||
if paint := parseRadialShdPaint(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); paint != nil {
|
||||
return paint
|
||||
}
|
||||
if strokeColor.AxialShd != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// parseRadialShdGradient 解析径向渐变
|
||||
// parseRadialShdPaint 解析径向渐变画刷
|
||||
// 入参: radialShd 径向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度
|
||||
// 返回: canvas.Gradient 渐变对象
|
||||
func parseRadialShdGradient(radialShd *RadialShd, alpha *int, x, y, pageH float64) canvas.Gradient {
|
||||
// 返回: *shdPaint 渐变画刷
|
||||
func parseRadialShdPaint(radialShd *RadialShd, alpha *int, x, y, pageH float64) *shdPaint {
|
||||
if radialShd == nil || radialShd.EndRadius <= 0 {
|
||||
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])}
|
||||
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 转换颜色对象
|
||||
|
||||
+10
-11
@@ -211,7 +211,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
||||
style.fillPaint = style.fillColor
|
||||
}
|
||||
if shouldFill {
|
||||
fillPaint, shadingClip := resolveShdPaint(ctx, style.fillPaint)
|
||||
fillPaint, shadingClip, fillView := resolveShdPaint(ctx, style.fillPaint)
|
||||
fillClip := intersectClipPath(clipPath, shadingClip)
|
||||
fp := p
|
||||
fillRule := canvas.NonZero
|
||||
@@ -232,7 +232,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
||||
ctx.SetFillRule(fillRule)
|
||||
ctx.SetFill(fillPaint)
|
||||
ctx.SetStrokeColor(canvas.Transparent)
|
||||
ctx.DrawPath(0, 0, fp)
|
||||
drawShdPath(ctx, fp, fillView)
|
||||
}
|
||||
}
|
||||
shouldStroke := true
|
||||
@@ -247,28 +247,27 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
|
||||
if style.strokePaint == nil {
|
||||
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.SetStroke(strokePaint)
|
||||
ctx.SetStrokeWidth(style.lineWidth)
|
||||
ctx.SetStrokeCapper(style.lineCap)
|
||||
ctx.SetStrokeJoiner(style.lineJoin)
|
||||
sp := p
|
||||
if len(style.dashPattern) > 0 {
|
||||
ctx.SetDashes(style.dashOffset, style.dashPattern...)
|
||||
sp = sp.Dash(style.dashOffset, style.dashPattern...)
|
||||
ctx.SetDashes(0)
|
||||
}
|
||||
strokeClip := intersectClipPath(clipPath, shadingClip)
|
||||
if strokeClip != nil {
|
||||
sp := p.Copy()
|
||||
if len(style.dashPattern) > 0 {
|
||||
sp = sp.Dash(style.dashOffset, style.dashPattern...)
|
||||
}
|
||||
if strokeClip != nil || strokeView != canvas.Identity {
|
||||
sp = sp.Copy()
|
||||
sp = sp.Stroke(style.lineWidth, style.lineCap, style.lineJoin, canvas.Tolerance)
|
||||
sp = applyClipPath(sp, strokeClip)
|
||||
ctx.SetFill(strokePaint)
|
||||
ctx.SetStrokeColor(canvas.Transparent)
|
||||
ctx.DrawPath(0, 0, sp)
|
||||
drawShdPath(ctx, sp, strokeView)
|
||||
} else {
|
||||
ctx.DrawPath(0, 0, p)
|
||||
ctx.DrawPath(0, 0, sp)
|
||||
}
|
||||
}
|
||||
ctx.Pop()
|
||||
|
||||
@@ -89,10 +89,11 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
if fillPaint == nil {
|
||||
fillPaint = fillColor
|
||||
}
|
||||
fillPaint, shadingClip := resolveShdPaint(ctx, fillPaint)
|
||||
fillPaint, shadingClip, fillView := resolveShdPaint(ctx, fillPaint)
|
||||
fillClip := intersectClipPath(clipPath, shadingClip)
|
||||
strokeStyle := newPathStyle(nil, defaultStroke, 0, obj.Alpha)
|
||||
strokeClip := clipPath
|
||||
strokeView := canvas.Identity
|
||||
if shouldStroke {
|
||||
if dp != nil {
|
||||
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.scale(ctm)
|
||||
strokeStyle.strokePaint, shadingClip = resolveShdPaint(ctx, strokeStyle.strokePaint)
|
||||
strokeStyle.strokePaint, shadingClip, strokeView = resolveShdPaint(ctx, strokeStyle.strokePaint)
|
||||
strokeClip = intersectClipPath(strokeClip, shadingClip)
|
||||
}
|
||||
fontStyle := canvas.FontRegular
|
||||
@@ -240,7 +241,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
if shouldFill && fillPaint != nil {
|
||||
ctx.SetFill(fillPaint)
|
||||
ctx.SetStrokeColor(canvas.Transparent)
|
||||
ctx.DrawPath(0, 0, applyClipPath(path.Copy(), fillClip))
|
||||
drawShdPath(ctx, applyClipPath(path.Copy(), fillClip), fillView)
|
||||
}
|
||||
if len(strokeStyle.dashPattern) > 0 {
|
||||
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)
|
||||
ctx.SetFill(strokeStyle.strokePaint)
|
||||
ctx.SetStrokeColor(canvas.Transparent)
|
||||
ctx.DrawPath(0, 0, applyClipPath(path, strokeClip))
|
||||
drawShdPath(ctx, applyClipPath(path, strokeClip), strokeView)
|
||||
if hasUnderline {
|
||||
underline := &canvas.Path{}
|
||||
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)
|
||||
if shouldFill {
|
||||
ctx.SetFillColor(fillColor)
|
||||
ctx.DrawPath(0, 0, applyClipPath(underline.Transform(transform), clipPath))
|
||||
} else {
|
||||
drawShdPath(ctx, applyClipPath(underline.Transform(transform), strokeClip), strokeView)
|
||||
}
|
||||
ctx.DrawPath(0, 0, applyClipPath(underline.Transform(transform), clipPath))
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -274,7 +277,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
textTransform = textTransform.Mul(glyphMatrix)
|
||||
}
|
||||
glyphPath = applyClipPath(glyphPath.Copy().Transform(textTransform.Scale(scaleX, 1)), fillClip)
|
||||
ctx.DrawPath(0, 0, glyphPath)
|
||||
drawShdPath(ctx, glyphPath, fillView)
|
||||
if hasUnderline {
|
||||
uw := sizeMM * 0.05
|
||||
off := sizeMM * 0.1
|
||||
|
||||
+33
-17
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
type shdPaint struct {
|
||||
gradient canvas.Gradient
|
||||
view canvas.Matrix
|
||||
extend int
|
||||
mapType string
|
||||
period float64
|
||||
@@ -44,46 +45,49 @@ func newShdPaint(gradient canvas.Gradient, extend, mapType string, mapUnit float
|
||||
if mapUnit > 0 && length > 0 {
|
||||
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 解析渐变画刷和裁剪区域
|
||||
// 入参: ctx 画布上下文, paint 画刷
|
||||
// 返回: any 画刷, *canvas.Path 裁剪区域
|
||||
func resolveShdPaint(ctx *canvas.Context, paint any) (any, *canvas.Path) {
|
||||
// 返回: any 画刷, *canvas.Path 裁剪区域, canvas.Matrix 渐变变换
|
||||
func resolveShdPaint(ctx *canvas.Context, paint any) (any, *canvas.Path, canvas.Matrix) {
|
||||
s, ok := paint.(*shdPaint)
|
||||
if !ok {
|
||||
return paint, nil
|
||||
return paint, nil, canvas.Identity
|
||||
}
|
||||
switch g := s.gradient.(type) {
|
||||
case *canvas.LinearGradient:
|
||||
clip := axialShdClip(ctx, g, s.extend)
|
||||
if s.mapType != "Reflect" {
|
||||
return g, clip
|
||||
return g, clip, s.view
|
||||
}
|
||||
d := g.End.Sub(g.Start)
|
||||
axis := canvas.Matrix{{d.X, -d.Y, g.Start.X}, {d.Y, d.X, g.Start.Y}}
|
||||
area := shdCanvasBounds(ctx).Transform(axis.Inv())
|
||||
lo, hi := s.rangeLimits(area.X0, area.X1)
|
||||
if hi <= lo {
|
||||
return g, &canvas.Path{}
|
||||
return g, &canvas.Path{}, s.view
|
||||
}
|
||||
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:
|
||||
d, dr := g.C1.Sub(g.C0), g.R1-g.R0
|
||||
length := d.Length()
|
||||
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" {
|
||||
return g, clip
|
||||
return g, clip, s.view
|
||||
}
|
||||
lo, hi := 0.0, 1.0
|
||||
if s.extend != 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}} {
|
||||
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)
|
||||
}
|
||||
if hi <= lo {
|
||||
return g, &canvas.Path{}
|
||||
return g, &canvas.Path{}, s.view
|
||||
}
|
||||
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 限制渐变延伸区间
|
||||
@@ -131,9 +147,9 @@ func shdCanvasBounds(ctx *canvas.Context) canvas.Rect {
|
||||
}
|
||||
|
||||
// radialShdClip 获取包含双圆的径向渐变裁剪区域
|
||||
// 入参: ctx 画布上下文, g 径向渐变, extend 延伸方向
|
||||
// 入参: g 径向渐变, extend 延伸方向, bounds 渐变坐标系下的画布边界
|
||||
// 返回: *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
|
||||
r0, r1 := g.R0, g.R1
|
||||
if r1 < r0 {
|
||||
@@ -147,7 +163,7 @@ func radialShdClip(ctx *canvas.Context, g *canvas.RadialGradient, extend int) *c
|
||||
if extend&2 == 0 {
|
||||
clip = canvas.Circle(r1).Translate(outer.X, outer.Y)
|
||||
} else {
|
||||
clip = shdCanvasBounds(ctx).ToPath()
|
||||
clip = bounds.ToPath()
|
||||
}
|
||||
if extend&1 == 0 && r0 > 0 {
|
||||
clip = clip.Not(canvas.Circle(r0).Translate(inner.X, inner.Y))
|
||||
|
||||
Reference in new issue
Block a user