From 3933afd1232971c4d7f2a365a29e3e4ecd0518e9 Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Fri, 2 Oct 2026 14:46:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AD=97=E4=BD=93=E5=A4=84=E7=90=86):=20?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=AD=97=E4=BD=93=E5=AD=97=E5=BD=A2=E4=B8=8E?= =?UTF-8?q?=E5=AD=97=E5=AE=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- go.sum | 4 +- ofdgo_font_cff.go | 275 +++++++++++++++++----------------------- ofdgo_font_cff_names.go | 128 +++++++++++++++++++ 4 files changed, 246 insertions(+), 163 deletions(-) create mode 100644 ofdgo_font_cff_names.go diff --git a/go.mod b/go.mod index 869a4ff..6f6837c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6 github.com/xiaoqidun/jbig2 v1.1.1 - github.com/xiaoqidun/pdfgo v0.0.0-20261002050201-1eed5d9912bf + github.com/xiaoqidun/pdfgo v0.0.0-20261002064448-3200323fdecb golang.org/x/crypto v0.57.0 golang.org/x/image v0.46.0 golang.org/x/text v0.42.0 diff --git a/go.sum b/go.sum index e2e0373..54c3b89 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk= github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI= github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ= -github.com/xiaoqidun/pdfgo v0.0.0-20261002050201-1eed5d9912bf h1:sST236XvU4MWCZjaH3d0hg4SsDJl6SNGL3qQdfgHP8w= -github.com/xiaoqidun/pdfgo v0.0.0-20261002050201-1eed5d9912bf/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ= +github.com/xiaoqidun/pdfgo v0.0.0-20261002064448-3200323fdecb h1:m/ESRIQE2ssRP/bHwrJvffIz1t+QYDMzL1B2fsHI7jc= +github.com/xiaoqidun/pdfgo v0.0.0-20261002064448-3200323fdecb/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= diff --git a/ofdgo_font_cff.go b/ofdgo_font_cff.go index 41c7dad..3b7dd77 100644 --- a/ofdgo_font_cff.go +++ b/ofdgo_font_cff.go @@ -22,16 +22,11 @@ import ( "sort" "strconv" "strings" + "unicode/utf8" "golang.org/x/text/encoding/simplifiedchinese" ) -// 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", -} - // cffDict 使用float64存储所有数值,以统一处理整数和实数 type cffDict map[int][]float64 @@ -95,7 +90,7 @@ func wrapCFFToOTF(cffData []byte) ([]byte, map[rune]uint16, error) { return data, mapping, err } -// normalizeCFFCharstrings 规范化Type2提示指令的等价编码,保留轮廓和提示数据 +// normalizeCFFCharstrings 展开预定义字符集并规范化Type2提示编码,保留轮廓和字宽 // 入参: data CFF字体数据 // 返回: []byte 标准化后的CFF数据, error 错误信息 func normalizeCFFCharstrings(data []byte) ([]byte, error) { @@ -121,6 +116,18 @@ func normalizeCFFCharstrings(data []byte) ([]byte, error) { } chars := readCFFIndexItems(data, charStart) changed := false + var explicitCharset []byte + if charset := dict[15]; len(charset) == 1 && (charset[0] == 1 || charset[0] == 2) { + sids, _, _, _, ok := getCFFCharsetInfo(data, len(chars)) + if !ok { + return nil, fmt.Errorf("invalid CFF charset") + } + explicitCharset = make([]byte, 1+2*(len(sids)-1)) + for gid := 1; gid < len(sids); gid++ { + binary.BigEndian.PutUint16(explicitCharset[1+2*(gid-1):], uint16(sids[gid])) + } + changed = true + } for index, charstring := range chars { normalized, inserted, err := explicitType2CounterStems(charstring) if err != nil { @@ -142,16 +149,21 @@ func normalizeCFFCharstrings(data []byte) ([]byte, error) { } encodedChars := encodeCFFIndex(chars) topEnd := topStart + topSize + originalDict := parseCFFDict(topData) newTop := encodeCFFIndex([][]byte{encodeCFFDict(dict)}) for range 6 { shift := len(newTop) - topSize shiftTail := shift + len(encodedChars) - charSize for _, op := range []int{15, 16, 17, 1236, 1237} { + if op == 15 && explicitCharset != nil { + dict[op] = []float64{float64(len(data) + shiftTail)} + continue + } values := dict[op] if len(values) != 1 || op == 15 && values[0] <= 2 || op == 16 && values[0] <= 1 { continue } - original := parseCFFDict(topData)[op] + original := originalDict[op] offset := int(original[0]) if offset >= charStart+charSize { dict[op] = []float64{float64(offset + shiftTail)} @@ -160,10 +172,12 @@ func normalizeCFFCharstrings(data []byte) ([]byte, error) { } } if values := dict[18]; len(values) == 2 { - original := parseCFFDict(topData)[18] + original := originalDict[18] offset := int(original[1]) if offset >= charStart+charSize { dict[18] = []float64{values[0], float64(offset + shiftTail)} + } else if offset >= topEnd { + dict[18] = []float64{values[0], float64(offset + shift)} } } updated := encodeCFFIndex([][]byte{encodeCFFDict(dict)}) @@ -173,12 +187,13 @@ func normalizeCFFCharstrings(data []byte) ([]byte, error) { } newTop = updated } - result := make([]byte, 0, len(data)+len(newTop)-topSize+len(encodedChars)-charSize) + result := make([]byte, 0, len(data)+len(newTop)-topSize+len(encodedChars)-charSize+len(explicitCharset)) result = append(result, data[:topStart]...) result = append(result, newTop...) result = append(result, data[topEnd:charStart]...) result = append(result, encodedChars...) result = append(result, data[charStart+charSize:]...) + result = append(result, explicitCharset...) return result, nil } @@ -1320,24 +1335,22 @@ func getCFFCharsetInfo(data []byte, numGlyphs int) ([]int, string, string, int, } td := parseCFFDict(topDictData) registry, ordering := getCFFROS(data, stringIndexOff, td) - charsetOff := 0 - if vals, ok := td[15]; ok && len(vals) > 0 { - charsetOff = int(vals[0]) + actual, err := parseCFFAndCountGlyphs(data) + if err != nil || numGlyphs < 1 || actual != numGlyphs { + return nil, "", "", 0, false } - sids := make([]int, numGlyphs) - sids[0] = 0 - if charsetOff > 2 { - sidsParsed := parseCFFCharset(data, charsetOff, numGlyphs) - copy(sids[1:], sidsParsed) - } else if charsetOff == 0 { - count := 228 - if numGlyphs-1 < count { - count = numGlyphs - 1 + charsetOff := 0 + if values, ok := td[15]; ok { + if len(values) != 1 || !finite(values[0]) || values[0] < 0 || values[0] > float64(len(data)) || values[0] != math.Trunc(values[0]) { + return nil, "", "", 0, false } - for i := 1; i <= count; i++ { - sids[i] = i - } - } else { + charsetOff = int(values[0]) + } + if values, cid := td[1230]; cid && (len(values) != 3 || charsetOff <= 2) { + return nil, "", "", 0, false + } + sids, err := parseCFFCharset(data, charsetOff, numGlyphs) + if err != nil { return nil, "", "", 0, false } return sids, registry, ordering, stringIndexOff, true @@ -1479,47 +1492,76 @@ func getCFFData(data []byte) []byte { return nil } -// parseCFFCharset 解析CFF字符集并返回SID列表 -// 入参: data CFF数据, offset 偏移量, numGlyphs 字形数量 -// 返回: []int SID列表 -func parseCFFCharset(data []byte, offset int, numGlyphs int) []int { - if offset >= len(data) { - return nil +// parseCFFCharset 按CFF规范第13节及附录C读取字符集,保留GID顺序 +// 入参: data CFF数据, offset 字符集预定义值或偏移, count 字形数量 +// 返回: []int SID或CID列表, error 错误信息 +func parseCFFCharset(data []byte, offset, count int) ([]int, error) { + charset := make([]int, count) + if offset <= 2 { + if offset == 0 { + if count > 229 { + return nil, fmt.Errorf("invalid ISOAdobe charset length") + } + for gid := range charset { + charset[gid] = gid + } + } else { + predefined := cffExpertCharset[:] + if offset == 2 { + predefined = cffExpertSubsetCharset[:] + } + if count > len(predefined) { + return nil, fmt.Errorf("invalid predefined CFF charset length") + } + for gid := range charset { + charset[gid] = int(predefined[gid]) + } + } + return charset, nil } - format := data[offset] - var sids []int - count := numGlyphs - 1 - pos := offset + 1 - switch format { - case 0: - for i := 0; i < count && pos+2 <= len(data); i++ { - sid := int(binary.BigEndian.Uint16(data[pos:])) - sids = append(sids, sid) + if offset >= len(data) { + return nil, fmt.Errorf("invalid CFF charset offset") + } + format, pos := data[offset], offset+1 + if format > 2 { + return nil, fmt.Errorf("invalid CFF charset format") + } + seen := map[int]bool{0: true} + for gid := 1; gid < count; { + if pos+2 > len(data) { + return nil, fmt.Errorf("truncated CFF charset") + } + first := int(binary.BigEndian.Uint16(data[pos:])) + pos += 2 + run := 1 + switch format { + case 1: + if pos == len(data) { + return nil, fmt.Errorf("truncated CFF charset range") + } + run += int(data[pos]) + pos++ + case 2: + if pos+2 > len(data) { + return nil, fmt.Errorf("truncated CFF charset range") + } + run += int(binary.BigEndian.Uint16(data[pos:])) pos += 2 } - case 1: - for len(sids) < count && pos+3 <= len(data) { - first := int(binary.BigEndian.Uint16(data[pos:])) - nLeft := int(data[pos+2]) - pos += 3 - for j := 0; j <= nLeft; j++ { - sids = append(sids, first+j) - } + if run > count-gid || first+run > 65536 { + return nil, fmt.Errorf("invalid CFF charset range") } - case 2: - for len(sids) < count && pos+4 <= len(data) { - first := int(binary.BigEndian.Uint16(data[pos:])) - nLeft := int(binary.BigEndian.Uint16(data[pos+2:])) - pos += 4 - for j := 0; j <= nLeft; j++ { - sids = append(sids, first+j) + for n := 0; n < run; n++ { + sid := first + n + if seen[sid] { + return nil, fmt.Errorf("duplicate CFF charset identifier") } + seen[sid] = true + charset[gid] = sid + gid++ } } - if len(sids) > count { - sids = sids[:count] - } - return sids + return charset, nil } // readStringIndexItem 读取CFF字符串索引项 @@ -1620,108 +1662,21 @@ func adobeGB1CIDToUnicode(cid int) (rune, bool) { // 入参: name 字形名称 // 返回: rune Unicode字符 func getUnicodeFromName(name string) rune { + name, _, _ = strings.Cut(name, ".") + if character := cffStandardNameUnicode[name]; character != 0 { + return character + } + digits := "" if strings.HasPrefix(name, "uni") && len(name) == 7 { - hexStr := strings.ToUpper(name[3:]) - if val, err := strconv.ParseInt(hexStr, 16, 32); err == nil { - return rune(val) + digits = name[3:] + } else if strings.HasPrefix(name, "u") && len(name) >= 5 && len(name) <= 7 { + digits = name[1:] + } + if digits != "" { + value, err := strconv.ParseUint(digits, 16, 32) + if err == nil && value <= utf8.MaxRune && utf8.ValidRune(rune(value)) { + return rune(value) } } - if strings.HasPrefix(name, "u") && len(name) >= 5 && len(name) <= 7 && !strings.HasPrefix(name, "uni") { - hexStr := strings.ToUpper(name[1:]) - if val, err := strconv.ParseInt(hexStr, 16, 32); err == nil { - return rune(val) - } - } - switch name { - case "space": - return ' ' - case "exclam": - return '!' - case "quotedbl": - return '"' - case "numbersign": - return '#' - case "dollar": - return '$' - case "percent": - return '%' - case "ampersand": - return '&' - case "quotesingle": - return '\'' - case "parenleft": - return '(' - case "parenright": - return ')' - case "asterisk": - return '*' - case "plus": - return '+' - case "comma": - return ',' - case "hyphen": - return '-' - case "period": - return '.' - case "slash": - return '/' - case "zero": - return '0' - case "one": - return '1' - case "two": - return '2' - case "three": - return '3' - case "four": - return '4' - case "five": - return '5' - case "six": - return '6' - case "seven": - return '7' - case "eight": - return '8' - case "nine": - return '9' - case "colon": - return ':' - case "semicolon": - return ';' - case "less": - return '<' - case "equal": - return '=' - case "greater": - return '>' - case "question": - return '?' - case "at": - return '@' - case "bracketleft": - return '[' - case "backslash": - return '\\' - case "bracketright": - return ']' - case "asciicircum": - return '^' - case "underscore": - return '_' - case "grave": - return '`' - case "braceleft": - return '{' - case "bar": - return '|' - case "braceright": - return '}' - case "asciitilde": - return '~' - } - if len(name) == 1 { - return rune(name[0]) - } return 0 } diff --git a/ofdgo_font_cff_names.go b/ofdgo_font_cff_names.go new file mode 100644 index 0000000..61a7254 --- /dev/null +++ b/ofdgo_font_cff_names.go @@ -0,0 +1,128 @@ +// Copyright 2025-2026 肖其顿 (XIAO QI DUN) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ofdgo + +import "strings" + +// cffExpertCharset 按CFF规范附录C保存Expert字符集的SID顺序 +var cffExpertCharset = [...]uint16{ + 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, + 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378, +} + +// cffExpertSubsetCharset 按CFF规范附录C保存ExpertSubset字符集的SID顺序 +var cffExpertSubsetCharset = [...]uint16{ + 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, + 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, + 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, +} + +// cffStandardStrings 按CFF标准SID顺序保存字形名称 +var cffStandardStrings = strings.Fields(` +.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 guillemotleft guilsinglleft +guilsinglright fi fl endash dagger daggerdbl periodcentered paragraph bullet quotesinglbase quotedblbase quotedblright +guillemotright ellipsis perthousand questiondown grave acute circumflex tilde macron breve dotaccent dieresis +ring cedilla hungarumlaut ogonek caron emdash 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 twodotenleader onedotenleader 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 +`) + +// cffStandardUnicode 按SID保存标准字形的单字符Unicode映射,不替换实际字形 +// https://github.com/adobe-type-tools/agl-aglfn +var cffStandardUnicode = [...]rune{ + 0x0, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x2019, 0x28, 0x29, 0x2A, + 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, + 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, + 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x2018, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, + 0xA1, 0xA2, 0xA3, 0x2044, 0xA5, 0x192, 0xA7, 0xA4, 0x27, 0x201C, 0xAB, 0x2039, + 0x203A, 0xFB01, 0xFB02, 0x2013, 0x2020, 0x2021, 0xB7, 0xB6, 0x2022, 0x201A, 0x201E, 0x201D, + 0xBB, 0x2026, 0x2030, 0xBF, 0x60, 0xB4, 0x2C6, 0x2DC, 0xAF, 0x2D8, 0x2D9, 0xA8, + 0x2DA, 0xB8, 0x2DD, 0x2DB, 0x2C7, 0x2014, 0xC6, 0xAA, 0x141, 0xD8, 0x152, 0xBA, + 0xE6, 0x131, 0x142, 0xF8, 0x153, 0xDF, 0xB9, 0xAC, 0xB5, 0x2122, 0xD0, 0xBD, + 0xB1, 0xDE, 0xBC, 0xF7, 0xA6, 0xB0, 0xFE, 0xBE, 0xB2, 0xAE, 0x2212, 0xF0, + 0xD7, 0xB3, 0xA9, 0xC1, 0xC2, 0xC4, 0xC0, 0xC5, 0xC3, 0xC7, 0xC9, 0xCA, + 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD1, 0xD3, 0xD4, 0xD6, 0xD2, 0xD5, + 0x160, 0xDA, 0xDB, 0xDC, 0xD9, 0xDD, 0x178, 0x17D, 0xE1, 0xE2, 0xE4, 0xE0, + 0xE5, 0xE3, 0xE7, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, 0xEC, 0xF1, + 0xF3, 0xF4, 0xF6, 0xF2, 0xF5, 0x161, 0xFA, 0xFB, 0xFC, 0xF9, 0xFD, 0xFF, + 0x17E, 0xF721, 0xF6F8, 0xF724, 0xF6E4, 0xF726, 0xF7B4, 0x207D, 0x207E, 0x2025, 0x2024, 0xF730, + 0xF731, 0xF732, 0xF733, 0xF734, 0xF735, 0xF736, 0xF737, 0xF738, 0xF739, 0xF6E2, 0xF6DE, 0xF6E8, + 0xF73F, 0xF6E9, 0xF6EA, 0xF6E0, 0xF6EB, 0xF6EC, 0xF6ED, 0xF6EE, 0xF6EF, 0x207F, 0xF6F0, 0xF6F1, + 0xF6F2, 0xF6F3, 0xFB00, 0xFB03, 0xFB04, 0x208D, 0x208E, 0xF6F6, 0xF6E6, 0xF760, 0xF761, 0xF762, + 0xF763, 0xF764, 0xF765, 0xF766, 0xF767, 0xF768, 0xF769, 0xF76A, 0xF76B, 0xF76C, 0xF76D, 0xF76E, + 0xF76F, 0xF770, 0xF771, 0xF772, 0xF773, 0xF774, 0xF775, 0xF776, 0xF777, 0xF778, 0xF779, 0xF77A, + 0x20A1, 0xF6DC, 0xF6DD, 0xF6FE, 0xF7A1, 0xF7A2, 0xF6F9, 0xF6FD, 0xF6FF, 0xF7A8, 0xF6F4, 0xF6F5, + 0xF6F7, 0xF7AF, 0x2012, 0xF6E5, 0xF6FB, 0xF6FC, 0xF7B8, 0xF7BF, 0x215B, 0x215C, 0x215D, 0x215E, + 0x2153, 0x2154, 0x2070, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x2080, 0x2081, 0x2082, + 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0xF6DF, 0xF6E3, 0xF6E7, 0xF6E1, 0xF7E0, + 0xF7E1, 0xF7E2, 0xF7E3, 0xF7E4, 0xF7E5, 0xF7E6, 0xF7E7, 0xF7E8, 0xF7E9, 0xF7EA, 0xF7EB, 0xF7EC, + 0xF7ED, 0xF7EE, 0xF7EF, 0xF7F0, 0xF7F1, 0xF7F2, 0xF7F3, 0xF7F4, 0xF7F5, 0xF7F6, 0xF6FA, 0xF7F8, + 0xF7F9, 0xF7FA, 0xF7FB, 0xF7FC, 0xF7FD, 0xF7FE, 0xF7FF, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +} + +// cffStandardNameUnicode 按需定位CFF标准字形的Unicode字符 +var cffStandardNameUnicode = func() map[string]rune { + result := make(map[string]rune, len(cffStandardStrings)) + for sid, character := range cffStandardUnicode { + if character != 0 { + result[cffStandardStrings[sid]] = character + } + } + return result +}()