Skip to content

Commit fad9055

Browse files
committed
Prioritise certain file extensions in findfont
It's entirely possible that multiple forms of a font may be present on one system, for instance an OTF and also a PFB from texlive. We care about this because different formats have different levels of capability, and we don't want to choose a worse one simply because it's the first format considered. As such, a format component is added to the font scoring heuristic.
1 parent 765516d commit fad9055

File tree

1 file changed

+15
-12
lines changed

1 file changed

+15
-12
lines changed

src/findfonts.jl

+15-12
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ family_name(x::FTFont) = lowercase(x.family_name)
4646
style_name(x::FTFont) = lowercase(x.style_name)
4747

4848
const REGULAR_STYLES = ("regular", "normal", "standard", "book", "roman", "medium")
49+
const FONT_EXTENSION_PRIORITY = ("otc", "otf", "ttc", "ttf", "cff", "woff2", "woff", "pfa", "pfb", "pfr", "fnt", "pcf", "bdf")
4950

5051
"""
5152
score_font(searchparts::Vector{<:AbstractString}, fontpath::String)
52-
score_font(searchparts::Vector{<:AbstractString}, family::String, style::String)
53+
score_font(searchparts::Vector{<:AbstractString}, family::String, style::String, ext::String)
5354
5455
Score a font match using the list user-specified `searchparts`. The font match
55-
can either be a path to a font file (`fontpath`), or a `family` and `style`.
56+
can either be a path to a font file (`fontpath`), or a `family`, `style`, and
57+
`ext`.
5658
5759
Each part of the search string is searched in the family name first which has to
5860
match once to include the font in the candidate list. For fonts with a family
@@ -84,7 +86,7 @@ Then this is how this function would match different search strings:
8486
- "times" => no match
8587
- "arial" => no match
8688
"""
87-
function score_font(searchparts::Vector{<:AbstractString}, family::String, style::String)::Tuple{Int, Int, Int, Int}
89+
function score_font(searchparts::Vector{<:AbstractString}, family::String, style::String, ext::String)::Tuple{Int, Int, Int, Int, Int}
8890
regularity = minimum((length(REGULAR_STYLES) + 1 - findfirst(==(regsty), REGULAR_STYLES)::Int
8991
for regsty in REGULAR_STYLES if occursin(regsty, style)), init=typemax(Int))
9092
if regularity == typemax(Int)
@@ -96,7 +98,7 @@ function score_font(searchparts::Vector{<:AbstractString}, family::String, style
9698

9799
# return early if family name doesn't have a match
98100
any(occursin(part, family) for part in searchparts) ||
99-
return (0, 0, regularity, fontlength_penalty)
101+
return (0, 0, regularity, fontlength_penalty, ext_priority)
100102

101103
family_score, style_score = 0, 0
102104
for (i, part) in enumerate(Iterators.reverse(searchparts))
@@ -107,18 +109,18 @@ function score_font(searchparts::Vector{<:AbstractString}, family::String, style
107109
end
108110
end
109111

110-
return (family_score + style_score, family_score, regularity, fontlength_penalty)
112+
return (family_score + style_score, family_score, regularity, fontlength_penalty, ext_priority)
111113
end
112114

113-
function score_font(searchparts::Vector{<:AbstractString}, fontpath::String)::Tuple{Int, Int, Int, Int}
115+
function score_font(searchparts::Vector{<:AbstractString}, fontpath::String)::Tuple{Int, Int, Int, Int, Int}
114116
if (finfo = font_info(fontpath)) |> !isnothing
115-
score_font(searchparts, finfo.family, finfo.style)
117+
score_font(searchparts, finfo.family, finfo.style, finfo.ext)
116118
else
117-
(0, 0, 0, typemin(Int))
119+
(0, 0, 0, typemin(Int), length(FONT_EXTENSION_PRIORITY)+1)
118120
end
119121
end
120122

121-
const FONTINFO_CACHE = Dict{String, Union{NamedTuple{(:family, :style), Tuple{String, String}}, Nothing}}()
123+
const FONTINFO_CACHE = Dict{String, Union{NamedTuple{(:family, :style, :ext), Tuple{String, String, String}}, Nothing}}()
122124

123125
function font_info(fontpath::String)
124126
if isfile(fontpath)
@@ -128,7 +130,7 @@ function font_info(fontpath::String)
128130
family = family_name(font)
129131
style = style_name(font)
130132
finalize(font)
131-
(; family=family, style=style)
133+
(; family=family, style=style, ext=last(split(fontpath, '.')))
132134
end
133135
end
134136
end
@@ -174,14 +176,15 @@ function findfont_nocache(searchstring::String, fontfolders::Vector{<:AbstractSt
174176
# \W splits at all groups of non-word characters (like space, -, ., etc)
175177
searchparts = unique(split(lowercase(searchstring), r"\W+", keepempty=false))
176178
max_score1 = sum(length, searchparts) + sum(1:length(searchparts))
177-
best_1i, best_file, best_score = 0, nothing, (0, 0, false, typemin(Int))
179+
best_1i, best_file, best_score = 0, nothing, (0, 0, 0, typemin(Int), 0)
178180
for (i, fontfile) in enumerate(fontfiles_guess_sorted(searchparts, fontfolders))
179-
# we can compare all four tuple elements of the score at once
181+
# we can compare all five tuple elements of the score at once
180182
# in order of importance:
181183
# 1. number of family and style match characters (with priority factor)
182184
# 2. number of family match characters (with priority factor)
183185
# 3. is font a "regular" style variant?
184186
# 4. the negative length of the font name, the shorter the better
187+
# 5. the font file extension priority
185188
score = score_font(searchparts, fontfile)
186189
if first(score) > 0 && score >= best_score
187190
best_1i = i

0 commit comments

Comments
 (0)