mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
+56
-33
@@ -146,13 +146,13 @@ func (fsys *FontFS) matchStyle(pattern string, bold, italic bool) []string {
|
|||||||
// 入参: patterns 匹配模式列表, bold 是否粗体, italic 是否斜体
|
// 入参: patterns 匹配模式列表, bold 是否粗体, italic 是否斜体
|
||||||
// 返回: []string 字体文件列表
|
// 返回: []string 字体文件列表
|
||||||
func (fsys *FontFS) matchPatternsStyle(patterns []string, bold, italic bool) []string {
|
func (fsys *FontFS) matchPatternsStyle(patterns []string, bold, italic bool) []string {
|
||||||
var matches []fontFileMatch
|
|
||||||
seen := make(map[string]int)
|
|
||||||
files := fontFileCandidates(fsys.names, path.Base)
|
files := fontFileCandidates(fsys.names, path.Base)
|
||||||
|
matches := make([]fontFileMatch, 0, len(files))
|
||||||
|
seen := make(map[string]int, len(files))
|
||||||
for _, matcher := range newFontPatternMatchers(patterns) {
|
for _, matcher := range newFontPatternMatchers(patterns) {
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
rank := matcher.rank(file.base)
|
rank := matcher.rankCandidate(file)
|
||||||
appendFontFileMatch(&matches, seen, matcher.pattern, file.name, rank, bold, italic)
|
appendFontFileMatch(&matches, seen, matcher, file, rank, bold, italic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sortFontFileMatches(matches)
|
sortFontFileMatches(matches)
|
||||||
@@ -212,6 +212,9 @@ func matchFontPatternRank(pattern, name string) int {
|
|||||||
type fontFileCandidate struct {
|
type fontFileCandidate struct {
|
||||||
name string
|
name string
|
||||||
base string
|
base string
|
||||||
|
lowerBase string
|
||||||
|
normalized string
|
||||||
|
fold string
|
||||||
}
|
}
|
||||||
|
|
||||||
// fontFileCandidates 筛选字体文件候选
|
// fontFileCandidates 筛选字体文件候选
|
||||||
@@ -221,7 +224,14 @@ func fontFileCandidates(names []string, base func(string) string) []fontFileCand
|
|||||||
files := make([]fontFileCandidate, 0, len(names))
|
files := make([]fontFileCandidate, 0, len(names))
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if isFontFileName(name) {
|
if isFontFileName(name) {
|
||||||
files = append(files, fontFileCandidate{name: name, base: base(name)})
|
baseName := base(name)
|
||||||
|
files = append(files, fontFileCandidate{
|
||||||
|
name: name,
|
||||||
|
base: baseName,
|
||||||
|
lowerBase: strings.ToLower(baseName),
|
||||||
|
normalized: fontNormalizeName(baseName),
|
||||||
|
fold: strings.ToLower(name),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return files
|
return files
|
||||||
@@ -269,16 +279,26 @@ func newFontPatternMatchers(patterns []string) []fontPatternMatcher {
|
|||||||
// 入参: name 字体文件名
|
// 入参: name 字体文件名
|
||||||
// 返回: int 匹配等级
|
// 返回: int 匹配等级
|
||||||
func (m fontPatternMatcher) rank(name string) int {
|
func (m fontPatternMatcher) rank(name string) int {
|
||||||
rawName := name
|
return m.rankCandidate(fontFileCandidate{
|
||||||
exactPath, _ := path.Match(m.pattern, name)
|
base: name,
|
||||||
lowerPath, _ := path.Match(m.lowerPattern, strings.ToLower(name))
|
lowerBase: strings.ToLower(name),
|
||||||
|
normalized: fontNormalizeName(name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// rankCandidate 获取字体文件候选匹配等级
|
||||||
|
// 入参: file 字体文件候选
|
||||||
|
// 返回: int 匹配等级
|
||||||
|
func (m fontPatternMatcher) rankCandidate(file fontFileCandidate) int {
|
||||||
|
exactPath, _ := path.Match(m.pattern, file.base)
|
||||||
|
lowerPath, _ := path.Match(m.lowerPattern, file.lowerBase)
|
||||||
if m.stem == "" {
|
if m.stem == "" {
|
||||||
if exactPath || lowerPath {
|
if exactPath || lowerPath {
|
||||||
return fontMatchFuzzy
|
return fontMatchFuzzy
|
||||||
}
|
}
|
||||||
return fontMatchNone
|
return fontMatchNone
|
||||||
}
|
}
|
||||||
name = fontNormalizeName(name)
|
name := file.normalized
|
||||||
if name == m.stem {
|
if name == m.stem {
|
||||||
return fontMatchExact
|
return fontMatchExact
|
||||||
}
|
}
|
||||||
@@ -287,7 +307,7 @@ func (m fontPatternMatcher) rank(name string) int {
|
|||||||
return fontMatchExact
|
return fontMatchExact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fontFileKnownStyleSuffix(m.styleSuffix(rawName)) {
|
if fontFileKnownStyleSuffix(m.styleSuffixNormalized(name)) {
|
||||||
return fontMatchExact
|
return fontMatchExact
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(name, m.stem) {
|
if strings.HasPrefix(name, m.stem) {
|
||||||
@@ -309,20 +329,19 @@ func (m fontPatternMatcher) rank(name string) int {
|
|||||||
return fontMatchNone
|
return fontMatchNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// styleSuffix 获取字体文件样式后缀
|
// styleSuffixNormalized 获取规范字体名称的样式后缀
|
||||||
// 入参: name 字体文件名
|
// 入参: name 规范字体名称
|
||||||
// 返回: string 样式后缀
|
// 返回: string 样式后缀
|
||||||
func (m fontPatternMatcher) styleSuffix(name string) string {
|
func (m fontPatternMatcher) styleSuffixNormalized(name string) string {
|
||||||
key := fontNormalizeName(name)
|
if m.stem == "" || name == "" {
|
||||||
if m.stem == "" || key == "" {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, m.stem) {
|
if strings.HasPrefix(name, m.stem) {
|
||||||
return strings.TrimPrefix(key, m.stem)
|
return strings.TrimPrefix(name, m.stem)
|
||||||
}
|
}
|
||||||
for _, alias := range m.aliases {
|
for _, alias := range m.aliases {
|
||||||
if strings.HasPrefix(key, alias) {
|
if strings.HasPrefix(name, alias) {
|
||||||
return strings.TrimPrefix(key, alias)
|
return strings.TrimPrefix(name, alias)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
@@ -336,25 +355,24 @@ type fontFileMatch struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// appendFontFileMatch 追加字体文件匹配结果
|
// appendFontFileMatch 追加字体文件匹配结果
|
||||||
// 入参: matches 匹配结果, seen 已匹配文件, pattern 匹配模式, name 字体文件名, rank 匹配等级, bold 是否粗体, italic 是否斜体
|
// 入参: matches 匹配结果, seen 已匹配文件, matcher 匹配器, file 字体文件候选, rank 匹配等级, bold 是否粗体, italic 是否斜体
|
||||||
func appendFontFileMatch(matches *[]fontFileMatch, seen map[string]int, pattern, name string, rank int, bold, italic bool) {
|
func appendFontFileMatch(matches *[]fontFileMatch, seen map[string]int, matcher fontPatternMatcher, file fontFileCandidate, rank int, bold, italic bool) {
|
||||||
if rank == fontMatchNone || !isFontFileName(name) {
|
if rank == fontMatchNone {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
key := strings.ToLower(name)
|
|
||||||
next := fontFileMatch{
|
next := fontFileMatch{
|
||||||
name: name,
|
name: file.name,
|
||||||
rank: rank,
|
rank: rank,
|
||||||
styleRank: fontFileStyleRank(pattern, name, bold, italic),
|
styleRank: fontFileStyleRank(matcher.styleSuffixNormalized(file.normalized), bold, italic),
|
||||||
sortName: strings.ToLower(path.Base(name)),
|
sortName: file.lowerBase,
|
||||||
}
|
}
|
||||||
if index, ok := seen[key]; ok {
|
if index, ok := seen[file.fold]; ok {
|
||||||
if fontFileMatchLess(next, (*matches)[index]) {
|
if fontFileMatchLess(next, (*matches)[index]) {
|
||||||
(*matches)[index] = next
|
(*matches)[index] = next
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
seen[key] = len(*matches)
|
seen[file.fold] = len(*matches)
|
||||||
*matches = append(*matches, next)
|
*matches = append(*matches, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,10 +409,10 @@ func fontFileMatchNames(matches []fontFileMatch) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fontFileStyleRank 获取字体文件样式匹配等级
|
// fontFileStyleRank 获取字体文件样式匹配等级
|
||||||
// 入参: pattern 匹配模式, name 字体文件名, bold 是否粗体, italic 是否斜体
|
// 入参: suffix 样式后缀, bold 是否粗体, italic 是否斜体
|
||||||
// 返回: int 样式匹配等级
|
// 返回: int 样式匹配等级
|
||||||
func fontFileStyleRank(pattern, name string, bold, italic bool) int {
|
func fontFileStyleRank(suffix string, bold, italic bool) int {
|
||||||
fileBold, fileItalic := fontFileStyle(pattern, name)
|
fileBold, fileItalic := fontFileStyleFromSuffix(suffix)
|
||||||
rank := 0
|
rank := 0
|
||||||
if fileBold != bold {
|
if fileBold != bold {
|
||||||
rank += 2
|
rank += 2
|
||||||
@@ -402,7 +420,6 @@ func fontFileStyleRank(pattern, name string, bold, italic bool) int {
|
|||||||
if fileItalic != italic {
|
if fileItalic != italic {
|
||||||
rank += 2
|
rank += 2
|
||||||
}
|
}
|
||||||
suffix := fontFileStyleSuffix(pattern, name)
|
|
||||||
if !bold && !italic && !fileBold && !fileItalic && suffix != "" && !fontFileRegularSuffix(suffix) {
|
if !bold && !italic && !fileBold && !fileItalic && suffix != "" && !fontFileRegularSuffix(suffix) {
|
||||||
rank++
|
rank++
|
||||||
}
|
}
|
||||||
@@ -413,7 +430,13 @@ func fontFileStyleRank(pattern, name string, bold, italic bool) int {
|
|||||||
// 入参: pattern 匹配模式, name 字体文件名
|
// 入参: pattern 匹配模式, name 字体文件名
|
||||||
// 返回: bool 是否粗体, bool 是否斜体
|
// 返回: bool 是否粗体, bool 是否斜体
|
||||||
func fontFileStyle(pattern, name string) (bool, bool) {
|
func fontFileStyle(pattern, name string) (bool, bool) {
|
||||||
suffix := fontFileStyleSuffix(pattern, name)
|
return fontFileStyleFromSuffix(fontFileStyleSuffix(pattern, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileStyleFromSuffix 获取样式后缀对应的字体样式
|
||||||
|
// 入参: suffix 样式后缀
|
||||||
|
// 返回: bool 是否粗体, bool 是否斜体
|
||||||
|
func fontFileStyleFromSuffix(suffix string) (bool, bool) {
|
||||||
bold := suffix == "b" ||
|
bold := suffix == "b" ||
|
||||||
suffix == "bd" ||
|
suffix == "bd" ||
|
||||||
suffix == "bold" ||
|
suffix == "bold" ||
|
||||||
|
|||||||
+4
-4
@@ -225,14 +225,14 @@ func fontFSMatches(fsys fs.FS, patterns []string) []string {
|
|||||||
// 入参: fsys 字体文件系统, patterns 匹配模式列表, bold 是否粗体, italic 是否斜体
|
// 入参: fsys 字体文件系统, patterns 匹配模式列表, bold 是否粗体, italic 是否斜体
|
||||||
// 返回: []string 字体文件列表
|
// 返回: []string 字体文件列表
|
||||||
func fontFSMatchesStyle(fsys fs.FS, patterns []string, bold, italic bool) []string {
|
func fontFSMatchesStyle(fsys fs.FS, patterns []string, bold, italic bool) []string {
|
||||||
var matches []fontFileMatch
|
|
||||||
seen := make(map[string]int)
|
|
||||||
names, _ := fs.Glob(fsys, "*")
|
names, _ := fs.Glob(fsys, "*")
|
||||||
candidates := fontFileCandidates(names, path.Base)
|
candidates := fontFileCandidates(names, path.Base)
|
||||||
|
matches := make([]fontFileMatch, 0, len(candidates))
|
||||||
|
seen := make(map[string]int, len(candidates))
|
||||||
for _, matcher := range newFontPatternMatchers(patterns) {
|
for _, matcher := range newFontPatternMatchers(patterns) {
|
||||||
for _, file := range candidates {
|
for _, file := range candidates {
|
||||||
rank := matcher.rank(file.base)
|
rank := matcher.rankCandidate(file)
|
||||||
appendFontFileMatch(&matches, seen, matcher.pattern, file.name, rank, bold, italic)
|
appendFontFileMatch(&matches, seen, matcher, file, rank, bold, italic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sortFontFileMatches(matches)
|
sortFontFileMatches(matches)
|
||||||
|
|||||||
@@ -159,14 +159,14 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
|||||||
// 入参: dir 目录, patterns 模式列表, bold 是否粗体, italic 是否斜体
|
// 入参: dir 目录, patterns 模式列表, bold 是否粗体, italic 是否斜体
|
||||||
// 返回: []string 文件列表
|
// 返回: []string 文件列表
|
||||||
func (r *Renderer) matchFontFiles(dir string, patterns []string, bold, italic bool) []string {
|
func (r *Renderer) matchFontFiles(dir string, patterns []string, bold, italic bool) []string {
|
||||||
var matches []fontFileMatch
|
|
||||||
index := make(map[string]int)
|
|
||||||
files, _ := filepath.Glob(filepath.Join(dir, "*"))
|
files, _ := filepath.Glob(filepath.Join(dir, "*"))
|
||||||
candidates := fontFileCandidates(files, filepath.Base)
|
candidates := fontFileCandidates(files, filepath.Base)
|
||||||
|
matches := make([]fontFileMatch, 0, len(candidates))
|
||||||
|
index := make(map[string]int, len(candidates))
|
||||||
for _, matcher := range newFontPatternMatchers(patterns) {
|
for _, matcher := range newFontPatternMatchers(patterns) {
|
||||||
for _, file := range candidates {
|
for _, file := range candidates {
|
||||||
rank := matcher.rank(file.base)
|
rank := matcher.rankCandidate(file)
|
||||||
appendFontFileMatch(&matches, index, matcher.pattern, file.name, rank, bold, italic)
|
appendFontFileMatch(&matches, index, matcher, file, rank, bold, italic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sortFontFileMatches(matches)
|
sortFontFileMatches(matches)
|
||||||
|
|||||||
+24
-23
@@ -55,7 +55,7 @@ func (r *Renderer) textObjectGlyphTransforms(fontID string, text TextObject) map
|
|||||||
if fontID == "" || len(text.CGTransform) == 0 {
|
if fontID == "" || len(text.CGTransform) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
result := make(map[int]textGlyphTransform)
|
result := make(map[int]textGlyphTransform, len(text.CGTransform))
|
||||||
for _, transform := range text.CGTransform {
|
for _, transform := range text.CGTransform {
|
||||||
ids := parseInts(transform.Glyphs)
|
ids := parseInts(transform.Glyphs)
|
||||||
glyphCount := len(ids)
|
glyphCount := len(ids)
|
||||||
@@ -69,9 +69,9 @@ func (r *Renderer) textObjectGlyphTransforms(fontID string, text TextObject) map
|
|||||||
if codeCount <= 0 {
|
if codeCount <= 0 {
|
||||||
codeCount = 1
|
codeCount = 1
|
||||||
}
|
}
|
||||||
glyphs := make([]textGlyph, 0, glyphCount)
|
glyphs := make([]textGlyph, glyphCount)
|
||||||
for i := 0; i < glyphCount; i++ {
|
for i := range glyphs {
|
||||||
glyphs = append(glyphs, r.textGlyphFromID(fontID, ids[i]))
|
glyphs[i] = r.textGlyphFromID(fontID, ids[i])
|
||||||
}
|
}
|
||||||
result[transform.CodePosition] = textGlyphTransform{
|
result[transform.CodePosition] = textGlyphTransform{
|
||||||
CodeCount: codeCount,
|
CodeCount: codeCount,
|
||||||
@@ -108,9 +108,9 @@ func textCodeGlyphs(runes []rune, transforms map[int]textGlyphTransform, codeOff
|
|||||||
// 入参: runes 文本字符
|
// 入参: runes 文本字符
|
||||||
// 返回: []textGlyph 绘制字形列表
|
// 返回: []textGlyph 绘制字形列表
|
||||||
func textRuneGlyphs(runes []rune) []textGlyph {
|
func textRuneGlyphs(runes []rune) []textGlyph {
|
||||||
glyphs := make([]textGlyph, 0, len(runes))
|
glyphs := make([]textGlyph, len(runes))
|
||||||
for _, r := range runes {
|
for i, r := range runes {
|
||||||
glyphs = append(glyphs, textGlyph{Text: string(r), GlyphID: -1})
|
glyphs[i] = textGlyph{Text: string(r), GlyphID: -1}
|
||||||
}
|
}
|
||||||
return glyphs
|
return glyphs
|
||||||
}
|
}
|
||||||
@@ -208,32 +208,33 @@ func (r *Renderer) fontGlyphRune(fontID string, glyphID int) (rune, bool) {
|
|||||||
// 入参: indexStr 索引字符串, fontID 字体ID
|
// 入参: indexStr 索引字符串, fontID 字体ID
|
||||||
// 返回: []rune 字形列表
|
// 返回: []rune 字形列表
|
||||||
func (r *Renderer) parseIndexRunes(indexStr string, fontID string) []rune {
|
func (r *Renderer) parseIndexRunes(indexStr string, fontID string) []rune {
|
||||||
var gids []int
|
|
||||||
parts := strings.Fields(indexStr)
|
parts := strings.Fields(indexStr)
|
||||||
|
result := make([]rune, 0, len(parts))
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
if strings.Contains(p, "-") {
|
if startText, endText, ok := strings.Cut(p, "-"); ok {
|
||||||
sub := strings.Split(p, "-")
|
if !strings.Contains(endText, "-") {
|
||||||
if len(sub) == 2 {
|
start, _ := strconv.Atoi(startText)
|
||||||
start, _ := strconv.Atoi(sub[0])
|
end, _ := strconv.Atoi(endText)
|
||||||
end, _ := strconv.Atoi(sub[1])
|
|
||||||
for k := start; k <= end; k++ {
|
for k := start; k <= end; k++ {
|
||||||
gids = append(gids, k)
|
result = append(result, r.textIndexRune(fontID, k))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val, _ := strconv.Atoi(p)
|
val, _ := strconv.Atoi(p)
|
||||||
gids = append(gids, val)
|
result = append(result, r.textIndexRune(fontID, val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var res []rune
|
return result
|
||||||
for _, gid := range gids {
|
}
|
||||||
if rVal, ok := r.fontGlyphRune(fontID, gid); ok {
|
|
||||||
res = append(res, rVal)
|
// textIndexRune 获取索引字形对应的字符
|
||||||
continue
|
// 入参: fontID 字体ID, glyphID 字形ID或CID
|
||||||
|
// 返回: rune 字符
|
||||||
|
func (r *Renderer) textIndexRune(fontID string, glyphID int) rune {
|
||||||
|
if mapped, ok := r.fontGlyphRune(fontID, glyphID); ok {
|
||||||
|
return mapped
|
||||||
}
|
}
|
||||||
res = append(res, rune(gid))
|
return rune(glyphID)
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// textGlyphAdvanceLimit 获取显式字形推进宽度
|
// textGlyphAdvanceLimit 获取显式字形推进宽度
|
||||||
|
|||||||
Reference in New Issue
Block a user