Using systemfonts to handle fonts in R

This text expects a basic understanding of fonts and typography. If you feel like you could use a brush-up you can consult this light introduction to the subject.

systemfonts is designed to give R a modern text rendering stack. That’s unfortunately impossible without coordination with the graphics device, which means that to use all these features you need a supported graphics device. There are currently two options:

You might notice there’s currently a big hole in this workflow: PDFs. This is something we plan to work on in the future.

A systemfonts based workflow

With all that said, how do you actually use systemfonts to use custom fonts in your plots? First, you’ll need to use ragg or svglite.

Using ragg

While there is no way to unilaterally make ragg::agg_png() the default everywhere, it’s possible to get close:

If you want to use a font installed on your computer, you’re done!

grid::grid.text(
  "Spectral 🎉",
  gp = grid::gpar(fontfamily = "Spectral", fontface = 2, fontsize = 30)
)

Or, if using ggplot2

library(ggplot2)
ggplot(na.omit(penguins)) +
  geom_point(aes(x = bill_len, y = body_mass, colour = species)) +
  labs(x = "Bill Length", y = "Body Mass", colour = "Species") +
  theme_minimal(base_family = "Spectral")

If the results don’t look as you expect, you can use various systemfonts helpers to diagnose the problem:

systemfonts::match_fonts("Spectral", weight = "bold")
#> # A tibble: 1 × 4
#>   path                                         index features   variations
#>   <chr>                                        <int> <list>     <list>    
#> 1 /Users/thomas/Library/Fonts/Spectral 700.ttf     0 <font_ftr> <fnt_vrtn>
systemfonts::font_fallback("🎉", family = "Spectral", weight = "bold")
#>                                          path index
#> 1 /System/Library/Fonts/Apple Color Emoji.ttc     0

If you want to see all the fonts that are available for use, you can use systemfonts::system_fonts()

systemfonts::system_fonts()

Extra font styles

As we discussed above, the R interface only allows you to select between four styles: plain, italic, bold, and bold-italic. If you want to use a thin font, you have no way of communicating this wish to the device. To overcome this, systemfonts provides register_variant() which allows you to register a font with a new typeface name. For example, to use the light font from the Spectral typeface you can register it as follows:

systemfonts::register_variant(
  name = "Spectral Light",
  family = "Spectral",
  weight = "light"
)

Now you can use Spectral Light where you would otherwise specify the typeface:

grid::grid.text(
  "Light weight is soo classy",
  gp = grid::gpar(fontfamily = "Spectral Light", fontsize = 30)
)

register_variant() also allows you to turn on font features otherwise hidden away:

systemfonts::register_variant(
  name = "Spectral Small Caps",
  family = "Spectral",
  features = systemfonts::font_feature(
    letters = "small_caps"
  )
)
grid::grid.text(
  "All caps — Small caps",
  gp = grid::gpar(fontfamily = "Spectral Small Caps", fontsize = 30)
)

Fonts from other places

Historically, systemfonts primary role was to access the font installed on your computer, the system fonts. But what if you’re using a computer where you don’t have the rights to install new fonts, or you don’t want the hassle of installing a font just to use it for a single plot? That’s the problem solved by systemfonts::add_font() which makes it easy to use a font based on a path. But in many cases you don’t even need that as systemfont now scans ./fonts and ~/fonts and adds any font files it find. This means that you can put personal fonts in a fonts folder in your home directory, and project fonts in a fonts directory at the root of the project. This is a great way to ensure that specific fonts are available when you deploy some code to a server.

And you don’t even need to leave R to populate these folders. systemfonts::get_from_google_fonts() will download and install a google font in ~/fonts:

systemfonts::get_from_google_fonts("Barrio")

grid::grid.text(
  "A new font a day keeps Tufte away",
  gp = grid::gpar(fontfamily = "Barrio", fontsize = 30)
)