# Best way to locate and load a font?

**URL:** <https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996>\
**Category:** General Usage\
**Tags:** question, fonts\
**Created:** [January 3, 2022, 7:58pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996 "2022-01-03T19:58:59Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [January 3, 2022, 7:58pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/1 "2022-01-03T19:58:59Z")

</div>

After filing  
[https://github.com/JuliaGraphics/FreeTypeAbstraction.jl/issues/67](https://github.com/JuliaGraphics/FreeTypeAbstraction.jl/issues/67)  
I have been looking for the best way to locate and load a font.  
(initially for [DynamicGrids.jl](https://github.com/cesaraustralia/DynamicGrids.jl/issues/194), which is otherwise super fast, so it mattered, but the question should be of wider interest).

[`fontconfig`](https://github.com/JuliaGraphics/Fontconfig.jl) is about 2000 times faster to locate the right font:

```julia
using FreeTypeAbstraction
using Fontconfig

font_file(pattern::Fontconfig.Pattern) = Fontconfig.format(Fontconfig.match(pattern), "%{file}")
font_file(str::String) = font_file(Fontconfig.Pattern(str))

# str is either a direct path, or a fontconfig spec such as "cantarell:bold"
function load_font(str::String) 
    path = isfile(str) ? str : font_file(str)
    return FreeTypeAbstraction.FTFont(path)
end

julia> load_font("cantarell:bold")
FTFont (family = Cantarell, style = Bold)

julia> using BenchmarkTools
julia> @btime (load_font("cantarell") |> finalize) # finalize to avoid "too many open files" error
  1.246 ms (15 allocations: 2.62 KiB)

```

While

```julia
julia> using FreeTypeAbstraction
julia> using BenchmarkTools
julia> @btime FreeTypeAbstraction.findfont("cantarell")
  2.321 s (250478 allocations: 24.99 MiB)
FTFont (family = Cantarell, style = Regular)

```

Currently [Fontconfig.jl](https://github.com/JuliaGraphics/Fontconfig.jl) and [FreeTypeAbstraction.jl](https://github.com/JuliaGraphics/FreeTypeAbstraction.jl) are independent, which seems good.

Adding to `FreeTypeAbstraction.findfont` a cache similar to [what Makie does](https://github.com/JuliaPlots/Makie.jl/blob/9a4821bcaf1cf0f09841e659dd2a6eb81afe6527/src/conversions.jl#L922-L958) might make sense.  
But a one time full scan overhead will remain,  
unless a font properties cache is added.  
But that looks like reinventing [`fontconfig`](http://www.freedesktop.org/wiki/Software/fontconfig/), and [Fontconfig.jl](https://github.com/JuliaGraphics/Fontconfig.jl) looks good now.

Did I overlook a package that gather both functionalities ?  
(Found nothing, either here on discourse, on stackoverflow, or on [juliapackages](https://juliapackages.com/packages?search=font))

Or maybe is there an unregistered package in preparation ?  
If not, would it be interesting ?

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [January 4, 2022, 1:49pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/2 "2022-01-04T13:49:30Z")

</div>

I think integrating these packages by depending on Fontconfig.jl is a good idea for FreeTypeAbstraction, and probably anywhere else fonts are used. Im not sure if another package is necessary, but fixing the ecosystem from the bottom up so there a no hidden performance hits is the best approach, especially for people like me who know nothing about fonts.

Probably no-one has got around to this yet, as you dont really notice the problems on a system with very few fonts.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 4, 2022, 3:18pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/3 "2022-01-04T15:18:34Z")

</div>

The reason I wrote the findfont function how it currently is, was that I found it very difficult to choose the fonts I wanted in matplotlib, which also uses fontconfig I believe. It was difficult to select the font with the exact weight I wanted whenever it was more complicated than bold/italic. Many fonts have multiple weights such as semibold, roman, thin, light, etc. So I chose to use a function that goes just by the name (almost no user knows what weight 500 means for a font), but is a bit relaxed in what it matches, so that fonts with complicated names can be specified partially.

So can Fontconfig be used in a similar way? If we just want the fast matching, maybe we can just write a cache file ourselves with all the names.

---

<div class="post-metadata">

**Author:** ![lobingera](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lobingera/32/211_2.png) [@lobingera](https://discourse.julialang.org/u/lobingera)\
**Post date:** [January 4, 2022, 4:20pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/4 "2022-01-04T16:20:43Z")

</div>

> [@jules](#):
>
> So can Fontconfig be used in a similar way?

> <https://github.com/JuliaGraphics/Fontconfig.jl/blob/master/README.md>

---

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [January 4, 2022, 4:51pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/5 "2022-01-04T16:51:57Z")

</div>

Thanks for the intel.

`fontconfig` also monitors for disk changes and updates its cache accordingly.  
But a manual `update_cache()` could be fine too  
(it is really necessary only when one modifies the fonts, so no big deal)

More important indeed, `fontconfig` is tolerant to spaces and case:

```julia
load_font("SourceCode Pro")
FTFont (family = Source Code Pro, style = Regular)
load_font("sourcecodepro")
FTFont (family = Source Code Pro, style = Regular)

```

but falls back to a generic font if the name is incomplete:

```julia
load_font("SourceCode")
FTFont (family = FreeMono, style = Regular)

```

* * *

> Many fonts have multiple weights such as semibold, roman, thin, light, etc. So I chose to use a function that goes just by the name

Indeed, `fontconfig` is less permissive, one has to separate the weight field with a colon (`:`).

```julia
# findfont is nice and permissive
julia> FreeTypeAbstraction.findfont("helvetica bold")
FTFont (family = Helvetica, style = Bold)

# Because ':' is missing, fontconfig would be looking for "bold"
# in the font name (not the filename), and fall back to a generic font.
julia> load_font("helvetica bold")  
FTFont (family = FreeMono, style = Regular)

# with the correct separator, fontconfig works fine
julia> load_font("helvetica:bold")
FTFont (family = Helvetica, style = Bold)

# and falls back to regular style if the requested weight is not found
julia> load_font("helvetica:light")
FTFont (family = Helvetica, style = Regular)

# just like findfont does
julia> FreeTypeAbstraction.findfont("helvetica light")
FTFont (family = Helvetica, style = Regular)

```

For others, the `FreeTypeAbstraction` selection algorithm can be found in [`match_font`](https://github.com/JuliaGraphics/FreeTypeAbstraction.jl/blob/6f898ca225a6407a3ca351a89d86d41ef7d6879b/src/findfonts.jl#L81).

Bottom line: tough choice; I like your design skills and glad you are the ones to decide 🙂.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [January 4, 2022, 6:36pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/6 "2022-01-04T18:36:13Z")

</div>

Ha I also read about the colon functionality of fontconfig today, I was not aware that it had this. Maybe it was not documented in matplotlib or something. Anyway, with that functionality I’d probably be fine with using fontconfig if the other benefits are deemed important enough. On the other hand, I kind of like the robustness against partial names in my implementation. I chose that because I often used the font “Helvetica Lt Std Light” and the lt Std is annoying to type, and I sometimes couldn’t remember the exact abbreviations.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [January 4, 2022, 7:12pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/7 "2022-01-04T19:12:06Z")

</div>

I guess the other option here is doing whatever fontconfig does, but in Julia? If someone want to write that? Then the syntax can be whatever we like.

---

<div class="post-metadata">

**Author:** ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)\
**Post date:** [January 4, 2022, 8:27pm UTC](https://discourse.julialang.org/t/best-way-to-locate-and-load-a-font/73996/8 "2022-01-04T20:27:02Z")

</div>

> I often used the font “Helvetica Lt Std Light” and the lt Std is annoying to type

The standard solution with fontconfig is [font replacement](https://wiki.archlinux.org/title/Font_configuration#Replace_or_set_default_fonts).  
Pro: this would work for all applications based on fontconfig.  
Con: need manual configuration (clean, but not as user-friendly as your solution).
