diff --git a/ofdgo_font.go b/ofdgo_font.go index 5bf2cd8..2b24cc5 100644 --- a/ofdgo_font.go +++ b/ofdgo_font.go @@ -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 diff --git a/ofdgo_font_cff.go b/ofdgo_font_cff.go index f7b7f94..b11dc70 100644 --- a/ofdgo_font_cff.go +++ b/ofdgo_font_cff.go @@ -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", diff --git a/ofdgo_font_ttf.go b/ofdgo_font_ttf.go index c4285e1..17af175 100644 --- a/ofdgo_font_ttf.go +++ b/ofdgo_font_ttf.go @@ -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 { diff --git a/ofdgo_font_util.go b/ofdgo_font_util.go index 8003d2f..806c721 100644 --- a/ofdgo_font_util.go +++ b/ofdgo_font_util.go @@ -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) { diff --git a/ofdgo_image.go b/ofdgo_image.go index d188a50..4f32fa1 100644 --- a/ofdgo_image.go +++ b/ofdgo_image.go @@ -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) { diff --git a/ofdgo_page.go b/ofdgo_page.go index c8bdd69..6e4e45a 100644 --- a/ofdgo_page.go +++ b/ofdgo_page.go @@ -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 渐变分段 diff --git a/ofdgo_paint.go b/ofdgo_paint.go index 1e71357..6dcd7e8 100644 --- a/ofdgo_paint.go +++ b/ofdgo_paint.go @@ -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 转换颜色对象 diff --git a/ofdgo_renderer_path.go b/ofdgo_renderer_path.go index fe7e271..a4e0034 100644 --- a/ofdgo_renderer_path.go +++ b/ofdgo_renderer_path.go @@ -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() diff --git a/ofdgo_renderer_text.go b/ofdgo_renderer_text.go index 74c3d64..856b35c 100644 --- a/ofdgo_renderer_text.go +++ b/ofdgo_renderer_text.go @@ -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 diff --git a/ofdgo_shading.go b/ofdgo_shading.go index 58e2774..7d44e46 100644 --- a/ofdgo_shading.go +++ b/ofdgo_shading.go @@ -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))