mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-08-30 12:12:40 +08:00
+179
-11
@@ -112,11 +112,16 @@ func (fsys *FontFS) Glob(pattern string) ([]string, error) {
|
|||||||
// 入参: names 字体名称列表
|
// 入参: names 字体名称列表
|
||||||
// 返回: string 匹配字体文件, bool 是否为名称匹配
|
// 返回: string 匹配字体文件, bool 是否为名称匹配
|
||||||
func (fsys *FontFS) Match(names ...string) (string, bool) {
|
func (fsys *FontFS) Match(names ...string) (string, bool) {
|
||||||
for _, pattern := range fontFilePatterns(names...) {
|
return fsys.MatchStyle(false, false, names...)
|
||||||
if matches := fsys.match(pattern); len(matches) > 0 {
|
}
|
||||||
|
|
||||||
|
// MatchStyle 匹配指定样式的字体名称
|
||||||
|
// 入参: bold 是否粗体, italic 是否斜体, names 字体名称列表
|
||||||
|
// 返回: string 匹配字体文件, bool 是否为名称匹配
|
||||||
|
func (fsys *FontFS) MatchStyle(bold, italic bool, names ...string) (string, bool) {
|
||||||
|
if matches := fsys.matchPatternsStyle(fontFilePatterns(names...), bold, italic); len(matches) > 0 {
|
||||||
return matches[0], true
|
return matches[0], true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if matches := fsys.fallbackFonts(); len(matches) > 0 {
|
if matches := fsys.fallbackFonts(); len(matches) > 0 {
|
||||||
return matches[0], false
|
return matches[0], false
|
||||||
}
|
}
|
||||||
@@ -127,17 +132,30 @@ func (fsys *FontFS) Match(names ...string) (string, bool) {
|
|||||||
// 入参: pattern 匹配模式
|
// 入参: pattern 匹配模式
|
||||||
// 返回: []string 字体文件列表
|
// 返回: []string 字体文件列表
|
||||||
func (fsys *FontFS) match(pattern string) []string {
|
func (fsys *FontFS) match(pattern string) []string {
|
||||||
buckets := make([][]string, fontMatchFuzzy+1)
|
return fsys.matchStyle(pattern, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchStyle 匹配指定样式的字体文件模式
|
||||||
|
// 入参: pattern 匹配模式, bold 是否粗体, italic 是否斜体
|
||||||
|
// 返回: []string 字体文件列表
|
||||||
|
func (fsys *FontFS) matchStyle(pattern string, bold, italic bool) []string {
|
||||||
|
return fsys.matchPatternsStyle([]string{pattern}, bold, italic)
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchPatternsStyle 匹配指定样式的字体文件模式
|
||||||
|
// 入参: patterns 匹配模式列表, bold 是否粗体, italic 是否斜体
|
||||||
|
// 返回: []string 字体文件列表
|
||||||
|
func (fsys *FontFS) matchPatternsStyle(patterns []string, bold, italic bool) []string {
|
||||||
|
var matches []fontFileMatch
|
||||||
|
seen := make(map[string]int)
|
||||||
|
for _, pattern := range patterns {
|
||||||
for _, name := range fsys.names {
|
for _, name := range fsys.names {
|
||||||
if rank := matchFontPatternRank(pattern, name); rank != fontMatchNone {
|
rank := matchFontPatternRank(pattern, name)
|
||||||
buckets[rank] = append(buckets[rank], name)
|
appendFontFileMatch(&matches, seen, pattern, name, rank, bold, italic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var matches []string
|
sortFontFileMatches(matches, bold, italic)
|
||||||
for rank := fontMatchExact; rank <= fontMatchFuzzy; rank++ {
|
return fontFileMatchNames(matches)
|
||||||
matches = append(matches, buckets[rank]...)
|
|
||||||
}
|
|
||||||
return matches
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallbackFonts 获取回退字体文件
|
// fallbackFonts 获取回退字体文件
|
||||||
@@ -187,6 +205,7 @@ func cleanFontName(name string) string {
|
|||||||
// 入参: pattern 匹配模式, name 字体文件名
|
// 入参: pattern 匹配模式, name 字体文件名
|
||||||
// 返回: int 匹配等级
|
// 返回: int 匹配等级
|
||||||
func matchFontPatternRank(pattern, name string) int {
|
func matchFontPatternRank(pattern, name string) int {
|
||||||
|
rawName := name
|
||||||
exactPath, _ := path.Match(pattern, name)
|
exactPath, _ := path.Match(pattern, name)
|
||||||
lowerPath, _ := path.Match(strings.ToLower(pattern), strings.ToLower(name))
|
lowerPath, _ := path.Match(strings.ToLower(pattern), strings.ToLower(name))
|
||||||
stem := fontPatternStem(pattern)
|
stem := fontPatternStem(pattern)
|
||||||
@@ -207,6 +226,9 @@ func matchFontPatternRank(pattern, name string) int {
|
|||||||
return fontMatchExact
|
return fontMatchExact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fontFileKnownStyleSuffix(fontFileStyleSuffix(pattern, rawName)) {
|
||||||
|
return fontMatchExact
|
||||||
|
}
|
||||||
if strings.HasPrefix(name, stem) {
|
if strings.HasPrefix(name, stem) {
|
||||||
return fontMatchPartial
|
return fontMatchPartial
|
||||||
}
|
}
|
||||||
@@ -228,6 +250,152 @@ func matchFontPatternRank(pattern, name string) int {
|
|||||||
return fontMatchNone
|
return fontMatchNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fontFileMatch struct {
|
||||||
|
name string
|
||||||
|
pattern string
|
||||||
|
rank int
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendFontFileMatch 追加字体文件匹配结果
|
||||||
|
// 入参: matches 匹配结果, seen 已匹配文件, pattern 匹配模式, name 字体文件名, rank 匹配等级, bold 是否粗体, italic 是否斜体
|
||||||
|
func appendFontFileMatch(matches *[]fontFileMatch, seen map[string]int, pattern, name string, rank int, bold, italic bool) {
|
||||||
|
if rank == fontMatchNone || !isFontFileName(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key := strings.ToLower(name)
|
||||||
|
next := fontFileMatch{name: name, pattern: pattern, rank: rank}
|
||||||
|
if index, ok := seen[key]; ok {
|
||||||
|
if fontFileMatchLess(next, (*matches)[index], bold, italic) {
|
||||||
|
(*matches)[index] = next
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seen[key] = len(*matches)
|
||||||
|
*matches = append(*matches, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortFontFileMatches 排序字体文件匹配结果
|
||||||
|
// 入参: matches 匹配结果, bold 是否粗体, italic 是否斜体
|
||||||
|
func sortFontFileMatches(matches []fontFileMatch, bold, italic bool) {
|
||||||
|
sort.SliceStable(matches, func(i, j int) bool {
|
||||||
|
return fontFileMatchLess(matches[i], matches[j], bold, italic)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileMatchLess 判断字体文件匹配结果优先级
|
||||||
|
// 入参: left 左侧匹配结果, right 右侧匹配结果, bold 是否粗体, italic 是否斜体
|
||||||
|
// 返回: bool 左侧是否优先
|
||||||
|
func fontFileMatchLess(left, right fontFileMatch, bold, italic bool) bool {
|
||||||
|
if left.rank != right.rank {
|
||||||
|
return left.rank < right.rank
|
||||||
|
}
|
||||||
|
leftStyle := fontFileStyleRank(left.pattern, left.name, bold, italic)
|
||||||
|
rightStyle := fontFileStyleRank(right.pattern, right.name, bold, italic)
|
||||||
|
if leftStyle != rightStyle {
|
||||||
|
return leftStyle < rightStyle
|
||||||
|
}
|
||||||
|
return strings.ToLower(path.Base(left.name)) < strings.ToLower(path.Base(right.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileMatchNames 获取字体文件匹配名称
|
||||||
|
// 入参: matches 匹配结果
|
||||||
|
// 返回: []string 字体文件列表
|
||||||
|
func fontFileMatchNames(matches []fontFileMatch) []string {
|
||||||
|
names := make([]string, 0, len(matches))
|
||||||
|
for _, match := range matches {
|
||||||
|
names = append(names, match.name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileStyleRank 获取字体文件样式匹配等级
|
||||||
|
// 入参: pattern 匹配模式, name 字体文件名, bold 是否粗体, italic 是否斜体
|
||||||
|
// 返回: int 样式匹配等级
|
||||||
|
func fontFileStyleRank(pattern, name string, bold, italic bool) int {
|
||||||
|
fileBold, fileItalic := fontFileStyle(pattern, name)
|
||||||
|
rank := 0
|
||||||
|
if fileBold != bold {
|
||||||
|
rank += 2
|
||||||
|
}
|
||||||
|
if fileItalic != italic {
|
||||||
|
rank += 2
|
||||||
|
}
|
||||||
|
suffix := fontFileStyleSuffix(pattern, name)
|
||||||
|
if !bold && !italic && !fileBold && !fileItalic && suffix != "" && !fontFileRegularSuffix(suffix) {
|
||||||
|
rank++
|
||||||
|
}
|
||||||
|
return rank
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileStyle 获取字体文件样式
|
||||||
|
// 入参: pattern 匹配模式, name 字体文件名
|
||||||
|
// 返回: bool 是否粗体, bool 是否斜体
|
||||||
|
func fontFileStyle(pattern, name string) (bool, bool) {
|
||||||
|
suffix := fontFileStyleSuffix(pattern, name)
|
||||||
|
bold := suffix == "b" ||
|
||||||
|
suffix == "bd" ||
|
||||||
|
suffix == "bold" ||
|
||||||
|
suffix == "bi" ||
|
||||||
|
suffix == "bolditalic" ||
|
||||||
|
suffix == "boldoblique"
|
||||||
|
italic := suffix == "i" ||
|
||||||
|
suffix == "it" ||
|
||||||
|
suffix == "italic" ||
|
||||||
|
suffix == "oblique" ||
|
||||||
|
suffix == "bi" ||
|
||||||
|
suffix == "bolditalic" ||
|
||||||
|
suffix == "boldoblique"
|
||||||
|
return bold, italic
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileRegularSuffix 判断是否为常规样式后缀
|
||||||
|
// 入参: suffix 样式后缀
|
||||||
|
// 返回: bool 是否为常规样式后缀
|
||||||
|
func fontFileRegularSuffix(suffix string) bool {
|
||||||
|
switch suffix {
|
||||||
|
case "r", "regular", "normal", "常规":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileKnownStyleSuffix 判断是否为已知样式后缀
|
||||||
|
// 入参: suffix 样式后缀
|
||||||
|
// 返回: bool 是否为已知样式后缀
|
||||||
|
func fontFileKnownStyleSuffix(suffix string) bool {
|
||||||
|
switch suffix {
|
||||||
|
case "r", "regular", "normal", "常规",
|
||||||
|
"b", "bd", "bold",
|
||||||
|
"i", "it", "italic", "oblique",
|
||||||
|
"bi", "bolditalic", "boldoblique":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fontFileStyleSuffix 获取字体文件样式后缀
|
||||||
|
// 入参: pattern 匹配模式, name 字体文件名
|
||||||
|
// 返回: string 样式后缀
|
||||||
|
func fontFileStyleSuffix(pattern, name string) string {
|
||||||
|
key := fontNormalizeName(name)
|
||||||
|
stem := fontPatternStem(pattern)
|
||||||
|
if stem == "" || key == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(key, stem) {
|
||||||
|
return strings.TrimPrefix(key, stem)
|
||||||
|
}
|
||||||
|
for _, alias := range fontExactCandidateNames(stem) {
|
||||||
|
alias = fontNormalizeName(alias)
|
||||||
|
if alias != "" && strings.HasPrefix(key, alias) {
|
||||||
|
return strings.TrimPrefix(key, alias)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// fontMemFile 内存字体文件
|
// fontMemFile 内存字体文件
|
||||||
type fontMemFile struct {
|
type fontMemFile struct {
|
||||||
*bytes.Reader
|
*bytes.Reader
|
||||||
|
|||||||
+23
-28
@@ -157,13 +157,12 @@ func (r *Renderer) fontInfo(font Font) FontInfo {
|
|||||||
// 入参: names 字体名称列表
|
// 入参: names 字体名称列表
|
||||||
// 返回: string 匹配字体文件, bool 是否为名称匹配
|
// 返回: string 匹配字体文件, bool 是否为名称匹配
|
||||||
func (r *Renderer) matchFont(names ...string) (string, bool) {
|
func (r *Renderer) matchFont(names ...string) (string, bool) {
|
||||||
|
patterns := fontFilePatterns(names...)
|
||||||
for _, dir := range r.fontDirs {
|
for _, dir := range r.fontDirs {
|
||||||
for _, pattern := range fontFilePatterns(names...) {
|
if matches := r.matchFontFiles(dir, patterns, false, false); len(matches) > 0 {
|
||||||
if matches := r.globFontFiles(dir, pattern); len(matches) > 0 {
|
|
||||||
return matches[0], true
|
return matches[0], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for _, fsys := range r.fontFS {
|
for _, fsys := range r.fontFS {
|
||||||
if matcher, ok := fsys.(interface {
|
if matcher, ok := fsys.(interface {
|
||||||
Match(...string) (string, bool)
|
Match(...string) (string, bool)
|
||||||
@@ -191,11 +190,9 @@ func (r *Renderer) matchFont(names ...string) (string, bool) {
|
|||||||
// 入参: fsys 字体文件系统, names 字体名称列表
|
// 入参: fsys 字体文件系统, names 字体名称列表
|
||||||
// 返回: string 匹配字体文件
|
// 返回: string 匹配字体文件
|
||||||
func matchFontFS(fsys fs.FS, names ...string) string {
|
func matchFontFS(fsys fs.FS, names ...string) string {
|
||||||
for _, pattern := range fontFilePatterns(names...) {
|
if matches := fontFSMatches(fsys, fontFilePatterns(names...)); len(matches) > 0 {
|
||||||
if matches := fontFSMatches(fsys, pattern); len(matches) > 0 {
|
|
||||||
return matches[0]
|
return matches[0]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +201,7 @@ func matchFontFS(fsys fs.FS, names ...string) string {
|
|||||||
// 返回: string 回退字体文件
|
// 返回: string 回退字体文件
|
||||||
func fallbackFontFS(fsys fs.FS) string {
|
func fallbackFontFS(fsys fs.FS) string {
|
||||||
for _, pattern := range fontFallbackFiles {
|
for _, pattern := range fontFallbackFiles {
|
||||||
if matches := fontFSMatches(fsys, pattern); len(matches) > 0 {
|
if matches := fontFSMatches(fsys, []string{pattern}); len(matches) > 0 {
|
||||||
return matches[0]
|
return matches[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,34 +215,32 @@ func fallbackFontFS(fsys fs.FS) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fontFSMatches 匹配字体文件系统中的字体文件
|
// fontFSMatches 匹配字体文件系统中的字体文件
|
||||||
// 入参: fsys 字体文件系统, pattern 匹配模式
|
// 入参: fsys 字体文件系统, patterns 匹配模式列表
|
||||||
// 返回: []string 字体文件列表
|
// 返回: []string 字体文件列表
|
||||||
func fontFSMatches(fsys fs.FS, pattern string) []string {
|
func fontFSMatches(fsys fs.FS, patterns []string) []string {
|
||||||
seen := make(map[string]bool)
|
return fontFSMatchesStyle(fsys, patterns, false, false)
|
||||||
buckets := make([][]string, fontMatchFuzzy+1)
|
}
|
||||||
add := func(name string, rank int) {
|
|
||||||
|
// fontFSMatchesStyle 匹配字体文件系统中的指定样式字体文件
|
||||||
|
// 入参: fsys 字体文件系统, patterns 匹配模式列表, bold 是否粗体, italic 是否斜体
|
||||||
|
// 返回: []string 字体文件列表
|
||||||
|
func fontFSMatchesStyle(fsys fs.FS, patterns []string, bold, italic bool) []string {
|
||||||
|
var matches []fontFileMatch
|
||||||
|
seen := make(map[string]int)
|
||||||
|
add := func(pattern, name string, rank int) {
|
||||||
if !isFontFileName(name) {
|
if !isFontFileName(name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rank == fontMatchNone || seen[name] {
|
appendFontFileMatch(&matches, seen, pattern, name, rank, bold, italic)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
seen[name] = true
|
names, _ := fs.Glob(fsys, "*")
|
||||||
buckets[rank] = append(buckets[rank], name)
|
for _, pattern := range patterns {
|
||||||
|
for _, name := range names {
|
||||||
|
add(pattern, name, matchFontPatternRank(pattern, path.Base(name)))
|
||||||
}
|
}
|
||||||
matches, _ := fs.Glob(fsys, pattern)
|
|
||||||
for _, name := range matches {
|
|
||||||
add(name, matchFontPatternRank(pattern, path.Base(name)))
|
|
||||||
}
|
}
|
||||||
all, _ := fs.Glob(fsys, "*")
|
sortFontFileMatches(matches, bold, italic)
|
||||||
for _, name := range all {
|
return fontFileMatchNames(matches)
|
||||||
add(name, matchFontPatternRank(pattern, path.Base(name)))
|
|
||||||
}
|
|
||||||
var result []string
|
|
||||||
for rank := fontMatchExact; rank <= fontMatchFuzzy; rank++ {
|
|
||||||
result = append(result, buckets[rank]...)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fontUsage 统计文档字体使用次数
|
// fontUsage 统计文档字体使用次数
|
||||||
|
|||||||
+3
-2
@@ -151,6 +151,7 @@ func FontSystemNames(names ...string) []string {
|
|||||||
// 入参: name 字体名称
|
// 入参: name 字体名称
|
||||||
// 返回: string 规范化后的字体名称
|
// 返回: string 规范化后的字体名称
|
||||||
func fontNormalizeName(name string) string {
|
func fontNormalizeName(name string) string {
|
||||||
|
name = strings.ReplaceAll(name, "\\", "/")
|
||||||
name = strings.TrimSuffix(path.Base(name), path.Ext(name))
|
name = strings.TrimSuffix(path.Base(name), path.Ext(name))
|
||||||
name = strings.ToLower(strings.TrimSpace(name))
|
name = strings.ToLower(strings.TrimSpace(name))
|
||||||
return fontNameReplacer.Replace(name)
|
return fontNameReplacer.Replace(name)
|
||||||
@@ -326,10 +327,10 @@ func fontRuleMatchLevel(rule fontMatchRule, name string) int {
|
|||||||
level = fontMatchExact
|
level = fontMatchExact
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if item != "" && (level == fontMatchNone || level > fontMatchPartial) && (strings.HasPrefix(name, item) || strings.HasPrefix(item, name)) {
|
if item != "" && (level == fontMatchNone || level > fontMatchPartial) && strings.HasPrefix(name, item) {
|
||||||
level = fontMatchPartial
|
level = fontMatchPartial
|
||||||
}
|
}
|
||||||
if item != "" && level == fontMatchNone && (strings.Contains(name, item) || strings.Contains(item, name)) {
|
if item != "" && level == fontMatchNone && strings.Contains(name, item) {
|
||||||
level = fontMatchFuzzy
|
level = fontMatchFuzzy
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
+37
-41
@@ -1269,20 +1269,19 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
|||||||
if of.Italic {
|
if of.Italic {
|
||||||
fontStyle |= canvas.FontItalic
|
fontStyle |= canvas.FontItalic
|
||||||
}
|
}
|
||||||
|
boldStyle := fontStyle&canvas.FontBold != 0
|
||||||
|
italicStyle := fontStyle&canvas.FontItalic != 0
|
||||||
|
patterns := fontFilePatterns(of.FontName, of.FamilyName)
|
||||||
for _, dir := range r.fontDirs {
|
for _, dir := range r.fontDirs {
|
||||||
for _, pattern := range fontFilePatterns(of.FontName, of.FamilyName) {
|
for _, m := range r.matchFontFiles(dir, patterns, boldStyle, italicStyle) {
|
||||||
matches := r.globFontFiles(dir, pattern)
|
|
||||||
for _, m := range matches {
|
|
||||||
if err := ff.LoadFontFile(m, fontStyle); err == nil {
|
if err := ff.LoadFontFile(m, fontStyle); err == nil {
|
||||||
r.FontMap[fontID] = ff
|
r.FontMap[fontID] = ff
|
||||||
return ff
|
return ff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for _, fsys := range r.fontFS {
|
for _, fsys := range r.fontFS {
|
||||||
for _, pattern := range fontFilePatterns(of.FontName, of.FamilyName) {
|
for _, m := range fontFSMatchesStyle(fsys, patterns, boldStyle, italicStyle) {
|
||||||
for _, m := range fontFSMatches(fsys, pattern) {
|
|
||||||
resData, err := fs.ReadFile(fsys, m)
|
resData, err := fs.ReadFile(fsys, m)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if err := ff.LoadFont(resData, 0, fontStyle); err == nil {
|
if err := ff.LoadFont(resData, 0, fontStyle); err == nil {
|
||||||
@@ -1292,7 +1291,6 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if !canLoadSystemFonts() {
|
if !canLoadSystemFonts() {
|
||||||
for _, fsys := range r.fontFS {
|
for _, fsys := range r.fontFS {
|
||||||
if matches, err := fs.Glob(fsys, "*"); err == nil {
|
if matches, err := fs.Glob(fsys, "*"); err == nil {
|
||||||
@@ -1316,63 +1314,61 @@ func (r *Renderer) loadFont(fontID string) *canvas.FontFamily {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, targetName := range fontSystemNames(name) {
|
for _, targetName := range fontSystemNames(name) {
|
||||||
|
for _, m := range r.matchFontFiles(systemFontDir(), fontFilePatterns(targetName), boldStyle, italicStyle) {
|
||||||
|
if err := ff.LoadFontFile(m, fontStyle); err == nil {
|
||||||
|
r.FontMap[fontID] = ff
|
||||||
|
return ff
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := ff.LoadSystemFont(targetName, fontStyle); err == nil {
|
if err := ff.LoadSystemFont(targetName, fontStyle); err == nil {
|
||||||
r.FontMap[fontID] = ff
|
r.FontMap[fontID] = ff
|
||||||
return ff
|
return ff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sysFontDir string
|
for _, m := range r.matchFontFiles(systemFontDir(), fontFilePatterns(name), boldStyle, italicStyle) {
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux":
|
|
||||||
sysFontDir = `/usr/share/fonts`
|
|
||||||
case "darwin":
|
|
||||||
sysFontDir = `/Library/Fonts`
|
|
||||||
default:
|
|
||||||
sysFontDir = `C:\Windows\Fonts`
|
|
||||||
}
|
|
||||||
for _, pattern := range fontFilePatterns(name) {
|
|
||||||
matches := r.globFontFiles(sysFontDir, pattern)
|
|
||||||
for _, m := range matches {
|
|
||||||
if err := ff.LoadFontFile(m, fontStyle); err == nil {
|
if err := ff.LoadFontFile(m, fontStyle); err == nil {
|
||||||
r.FontMap[fontID] = ff
|
r.FontMap[fontID] = ff
|
||||||
return ff
|
return ff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
r.FontMap[fontID] = defaultFont
|
r.FontMap[fontID] = defaultFont
|
||||||
return defaultFont
|
return defaultFont
|
||||||
}
|
}
|
||||||
|
|
||||||
// globFontFiles 查找字体文件
|
// matchFontFiles 查找字体文件
|
||||||
// 入参: dir 目录, pattern 模式
|
// 入参: dir 目录, patterns 模式列表, bold 是否粗体, italic 是否斜体
|
||||||
// 返回: []string 文件列表
|
// 返回: []string 文件列表
|
||||||
func (r *Renderer) globFontFiles(dir, pattern string) []string {
|
func (r *Renderer) matchFontFiles(dir string, patterns []string, bold, italic bool) []string {
|
||||||
seen := make(map[string]bool)
|
var matches []fontFileMatch
|
||||||
buckets := make([][]string, fontMatchFuzzy+1)
|
index := make(map[string]int)
|
||||||
add := func(name string, rank int) {
|
add := func(pattern, name string, rank int) {
|
||||||
if !isFontFileName(name) {
|
if !isFontFileName(name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rank == fontMatchNone || seen[name] {
|
appendFontFileMatch(&matches, index, pattern, name, rank, bold, italic)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
seen[name] = true
|
files, _ := filepath.Glob(filepath.Join(dir, "*"))
|
||||||
buckets[rank] = append(buckets[rank], name)
|
for _, pattern := range patterns {
|
||||||
|
for _, name := range files {
|
||||||
|
add(pattern, name, matchFontPatternRank(pattern, filepath.Base(name)))
|
||||||
}
|
}
|
||||||
matches, _ := filepath.Glob(filepath.Join(dir, pattern))
|
|
||||||
for _, m := range matches {
|
|
||||||
add(m, matchFontPatternRank(pattern, filepath.Base(m)))
|
|
||||||
}
|
}
|
||||||
matches, _ = filepath.Glob(filepath.Join(dir, "*"))
|
sortFontFileMatches(matches, bold, italic)
|
||||||
for _, m := range matches {
|
return fontFileMatchNames(matches)
|
||||||
add(m, matchFontPatternRank(pattern, filepath.Base(m)))
|
}
|
||||||
|
|
||||||
|
// systemFontDir 获取系统字体目录
|
||||||
|
// 返回: string 字体目录
|
||||||
|
func systemFontDir() string {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return `/usr/share/fonts`
|
||||||
|
case "darwin":
|
||||||
|
return `/Library/Fonts`
|
||||||
|
default:
|
||||||
|
return `C:\Windows\Fonts`
|
||||||
}
|
}
|
||||||
var result []string
|
|
||||||
for rank := fontMatchExact; rank <= fontMatchFuzzy; rank++ {
|
|
||||||
result = append(result, buckets[rank]...)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// textObjectFontID 获取文本对象字体ID
|
// textObjectFontID 获取文本对象字体ID
|
||||||
|
|||||||
Reference in New Issue
Block a user