`ggsave()` doesn't render custom fonts when saving (+workaround)
I've been using a few scripts that save plots with custom fonts loaded via extrafont. This was working fine up until a few days ago, when ggsave stopped saving the plots w/the custom font (I update this plot daily, so I can see from the commit history that on 4/27 it was working & on 4/28 it wasn't).
For example, this renders in the IDE correctly:
library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 4.1.2
library(ggtext)
extrafont::loadfonts(device = "win")
ggplot(mtcars,
aes(x = hp,
y = qsec)) +
geom_point() +
theme(plot.title = element_markdown(family = "Vivaldi")) +
labs(title = "Here is a title rendered with **markdown**")

Created on 2022-04-29 by the reprex package (v2.0.1)
but when I save w/ggsave, here's what appears:

Rendering in the IDE is fine, & saving by exporting from the IDE is fine, it's just when I call ggsave. I found a workaround that seems to do the job - specifying the device in both the filename and device fields, but it feels a bit clunky:
# doesn't work
plot %>%
ggsave("plot_without_font.png")
# works
plot %>%
ggsave("plot_with_font.png",
device = png)
Not sure what the difference is between a few days ago & now (I haven't updated packages in the past few days, so I'm assuming there's some latent issue in my environment or session), but thought I'd highlight in case anyone else is running into the same issue!
I cannot reproduce the issue on my Windows. Rather, I see the opposite problem...
library(ggplot2)
library(ggtext)
extrafont::loadfonts(device = "win")
p <- ggplot(mtcars,
aes(x = hp,
y = qsec)) +
geom_point() +
# Sorry I don't install Vivaldi font
theme(plot.title = element_markdown(family = "Impact")) +
labs(title = "Here is a title rendered with **markdown**")
p
#> Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
#> family not found in Windows font database
#> Warning in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y, :
#> font family not found in Windows font database

file <- tempfile(fileext = ".png")
ggsave(file, p)
#> Saving 7 x 5 in image
knitr::include_graphics(file)

Created on 2022-04-29 by the reprex package (v2.0.1)
There has been no direct change in ggplot2 that changes anything related to extrafonts... However, ggplot2 now defaults to using the ragg device if installed and ragg doesn't care about extrafonts so maybe that is the issue. Do you have ragg installed?
ragg doesn't care about extrafonts so maybe that is the issue.
Ah, it can be. Then, maybe this issue is the case when ragg won't work with the font specification whereas the base R's png() + extrafont works?
@markjrieke I think you can use this code to render a reprex to check if the problem is related to ggplot2 or not (I guess not).
extrafont::loadfonts(device = "win")
png_file <- tempfile(fileext = ".png")
ragg_png_file <- tempfile(fileext = ".png")
png(png_file)
plot.new()
text(0.5, 0.5, "A", family = "Vivaldi", cex = 10)
dev.off()
# browseURL(png_file)
knitr::include_graphics(png_file)
ragg::agg_png(ragg_png_file)
plot.new()
text(0.5, 0.5, "A", family = "Vivaldi", cex = 10)
dev.off()
knitr::include_graphics(ragg_png_file)
# browseURL(ragg_png_file)
I have been having a similar issue, except with native fonts - for example family = "Times".
ggsave maintains the custom fonts when saving as a pdf file, but not when saving as a png or svg.
# ggsave_example.R
# Illustrate problem with setting fonts and ggsave for svg, png formats- though pdfs are fine
library(tidyverse)
theme_set(theme_bw() + theme(text = element_text(size = 12, family = "Times"), axis.text.x = element_text(angle = 45, vjust = 1, hjust=1, size = 12, color = "black")))
example_data <- tibble(letters = c("a", "b", "c"), numbers = c(1, 2, 3))
plot_to_save <- ggplot(data = example_data, aes(x = letters, y = numbers)) + geom_bar(stat = 'identity', show.legend = FALSE)
# Pdf keeps the Times font
ggsave('ggsave_pdf_ex.pdf')
# These don't keep the Times font
ggsave('ggsave_svg_ex.svg')
ggsave('ggsave_png_ex.png')
# Saving with base R works for svg
svg('base_svg_ex.svg')
print(plot_to_save)
dev.off()
# But not png
png('base_png_ex.png')
print(plot_to_save)
dev.off()
I also use extrafont and was confused because I didn't realize that ggsave uses ragg::agg_png for the 'png' device. Extrafont registers fonts with the base R devices. Instead of extrafont, you can use systemfonts which is used by ragg. It does a pretty good job of picking up most fonts on your system that are installed in the usual places - check with systemfonts::system_fonts(), but if you have some installed elsewhere, systemfonts::register_font will let you add them in a similar way that extrafont::font_import does underneath.
I have a similar issue, where it previously worked. When I run the plot in RStudio, the preview shows the correct font. However, when I save the plot as a png with ggsave, the font reverts back to Arial. systemfonts::system_fonts() has Latin Modern Roman in its registry. How should I test whether this is a ragg::agg_png issue?
sessionInfo()
R version 4.2.0 (2022-04-22)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS 13.0.1
packageVersion('ggplot2')
[1] ‘3.4.0’
packageVersion('ragg')
[1] ‘1.2.4’
If you have a plot p you can just save both to see if your custom font has worked:
p <- ggplot(mtcars, aes(disp, mpg)) +
geom_point()
ggsave("base_png.png", p, grDevices::png)
ggsave("ragg_png.png", p, ragg::agg_png)
But I don't think this is a bug in ggplot2, but rather a quirk of graphics devices, so there isn't anything to fix.