There are quite a few systems for font management in R. A key part of
our work is trying to make graphs and tables cross platform
reproducible, and consistent. This means dealing with fonts across
different platforms. Some of the older parts of R are designed around
the use of Adobe Type 1 fonts which are generally no longer in
mainstream use. Packages like extrafont
are attempting to
keep this working, but the main problem is the outdated font support in
the default graphics devices. Newer devices in the ragg
and
svglite
packages provide better font support for bitmap and
svg. The non default grDevices::cairo_pdf
and
grDevices::cairo_ps
work better for pdf and postscript
respectively. It is however, in our view, best to adopt an svg-first
workflow and make us of the wide ecosystem of modern web delivered
fonts. For pdf and postscript files exporting from the svg using
rsvg
or a headless chrome browser recovers a better output
that the native graphics devices.
Transparently ensure a font is available
Instead of specifying a font family as a character in a call to
ggplot, we can use a check_font()
call which tries to find
the font on your system and if not available downloads it from Google
fonts or Brick and registers it with systemfonts
, returning
the family name. This will happen transparently.
We then recommend that ggplot rendering is done via SVG which uses
systemfonts
and we attempt to make the output portable for
use by embedding web fonts into the svg outputs.
check_font("Helvetica")
## Helvetica
## "Helvetica"
Check what fonts are available
Find out what fonts ggrrr
can find on this system.
# list all available fonts
tmp = fonts_available()
# Check if specific fonts are available. Returns
fonts_available(c("Roboto","Arial","Helvetica"))
## [1] "Roboto" "Arial" "Helvetica"
Download a font from webfont providers
Checks if a local font is available. If not downloads and installs
the font and registers in the systemfonts
packages.
Currently supported are google
and brick
# clears any registered fonts
reset_fonts()
## This wipes font databases created by `systemfonts` and `ggrrr`.
## These will be rebuilt on demand by `ggrrr`.
## N.b. This will NOT remove any custom fonts installed on your system by `ggrrr`.Are you sure? (yes/No/cancel)
# if the fonts is named the names are used for the family on this system
check_font(c("Roboto","Arial","Kings","EB Garamond"))
## Roboto Arial Kings EB Garamond
## "Roboto" "Arial" "Kings" "EB Garamond"
systemfonts::registry_fonts()
## # A tibble: 0 × 7
## # ℹ 7 variables: path <chr>, index <int>, family <chr>, style <chr>,
## # weight <ord>, italic <lgl>, features <list>
Legacy (default graphics devices) support
If for some reason you really must use the legacy devices then
check_font can try and install the fonts you want to use in
grDevices
. This is complex and requires converting modern
fonts to Type 1 fonts, which is only possible for (locally) installed
.ttf
fonts. Your mileage may vary and UTF-8 support will be
patchy. check_fonts
will try and load the fonts but even if
a font is listed in grDevices::pdfFonts()
or
grDevices::postscriptFonts()
things are still not
guaranteed to work. TLDR; don’t use the legacy devices.
check_font(c("Roboto","Arial","Kings","EB Garamond"), .legacy=TRUE)
## Roboto Arial Kings EB Garamond
## "Roboto" "Arial" "Kings" "EB Garamond"
Comparing devices
The following code can be used to test out the capabilites of the different graphics devices:
check_font(c("Roboto","Kings","EB Garamond"), .legacy=TRUE)
## Roboto Kings EB Garamond
## "Roboto" "Kings" "EB Garamond"
plot = ggplot2::ggplot()+
ggplot2::theme_void(base_family="Roboto")+
ggplot2::geom_point()+ggplot2::theme(margins = ggplot2::margin(14,0,14,0))+
ggplot2::annotate("label",x=0,y=0,label="Kings: Em dash: \u2014 hello world", family="Kings")+
ggplot2::annotate("text",x=0,y=1,label="Roboto: UTF-8 subscript 2: \u2082", family="Roboto")+
ggplot2::annotate("text",x=0,y=2,label="EB Garamond: UTF-8 gte: \u2265", family="EB Garamond")
if (FALSE) {
# Does not work - "invalid font type" & "font family 'Kings' not found in PostScript font database"
# Native pdf device: font but no unicode support
tmp = tempfile(fileext = ".pdf")
grDevices::pdf(tmp, width=3, height = 1, units="in")
plot
grDevices::dev.off()
utils::browseURL(tmp)
# Cairo PDF: font and unicode support / Mac support limited
tmp = tempfile(fileext = ".pdf")
grDevices::cairo_pdf(tmp, width=3, height = 1, units="in")
plot
grDevices::dev.off()
utils::browseURL(tmp)
# Native png device: font and unicode support
tmp = tempfile(fileext = ".png")
grDevices::png(tmp, width=3, height = 1, units="in", res=300)
plot
grDevices::dev.off()
utils::browseURL(tmp)
# RAGG png: font and unicode support
tmp = tempfile(fileext = ".png")
ragg::agg_png(tmp, width=3, height = 1, units="in", res=300)
plot
grDevices::dev.off()
utils::browseURL(tmp)
# SVGLite: font and unicode support
tmp = tempfile(fileext = ".svg")
svglite::svglite(tmp, width=3, height = 1)
plot
grDevices::dev.off()
utils::browseURL(tmp)
# Native postscript:
# Does not work - "family 'Roboto' not included in postscript() device"
# however: names(grDevices::postscriptFonts()) includes Roboto
tmp = tempfile(fileext = ".eps")
grDevices::postscript(tmp, width=3, height = 1)
plot
grDevices::dev.off()
utils::browseURL(tmp)
# font and unicode support
# Cairo Postscript: font and unicode support / Mac support limited
tmp = tempfile(fileext = ".eps")
grDevices::cairo_ps(tmp, width=3, height = 1)
plot
grDevices::dev.off()
utils::browseURL(tmp)
}