Saving ggplot graph to PDF with fonts embedded in r

hmhensen picture hmhensen · Sep 1, 2017 · Viewed 8.2k times · Source

I've been following advice I've found online for saving a ggplot graph to PDF but I can't quite get it to work. I'm using the extrafont package to produce charts with text in Calibri, but my charts are printing out with no text. I don't know what I'm missing. I can't find any mistakes in my process. Hopefully, someone else can help.

Here's the code and process I used:

library(extrafont)
font_import(pattern="[C/c]alibri")
loadfonts(device="win")

I installed GhostScript at this time. Then ran the following to set the GhostScript location.

Sys.setenv(R_GSCMD = "C:\\Program Files\\gs\\gs9.21\\bin\\gswin64c.exe")

I then produced a chart using ggplot called "chart". The chart looked perfect in RStudio, but not in PDF.

ggsave("chart.pdf", plot = chart, width = 6, height = 4)

Here I get warnings showing stuff like this:

In grid.Call(C_textBounds, as.graphicsAnnot(x$label), ... : font family 'Calibri' not found in PostScript font database

Apparently, these warnings are supposed to happen? Then I run...

embed_fonts("chart.pdf", outfile="chart_embed.pdf")

Unfortunately, after all this, the final "embed" chart looks no different than the original chart produced, neither of which have any text.

In case it helps, here's the code to produce the chart:

a <- ggplot(data=stats, aes(x=Date))
Chart <- a + geom_point(aes(y=NevadaTotalNonfarmAllEmployees)) + 
      xlab("Date") + 
      ylab("Nonfarm Jobs") + 
      ggtitle("Nevada Total Jobs") + 
      theme(axis.title.x = element_text(size=15, family = "Calibri"),
            axis.title.y = element_text(size=15, family = "Calibri"),
            axis.text.x = element_text(size=10, family = "Calibri"),
            axis.text.y = element_text(size=10, family = "Calibri"),
            plot.title = element_text(hjust=0.5, size=20, family = "Calibri"))

I've been pulling my hair out trying to figure this out. Or maybe it's not the code but something else? Either way, thanks for any assistance.

Answer

Andrew picture Andrew · Oct 2, 2017

There are a couple issues at play here: (1) loading fonts into R and (2) using a PDF-writing library that works correctly with custom embedded fonts.

First, as others have mentioned, on Windows you generally need to run extrafont::font_import() to register many of your system fonts with R, but it can take a while and can miss TTF and other types of fonts. One way around this is to load fonts into R on the fly, without loading the full database, using windowsFonts(name_of_font_inside_r = windowsFont("Name of actual font")), like so:

windowsFonts(Calibri = windowsFont("Calibri"))

This makes just that one font accessible in R. You can check with windowsFonts(). You have to run this line each time the script is run—the font loading doesn't persist across sessions. Once the font has been loaded, you can use it normally:

library(tidyverse)
df <- data_frame(x = 1:10, y = 2:11)

p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  labs(title = "Yay Calibri") +
  theme_light(base_family = "Calibri")
p

Calibrified

Second, R's built-in PDF-writing device on both Windows and macOS doesn't handle font embedding very well. However, R now includes the Cairo graphics library, which can embed fonts just fine. You can specify the Cairo device in ggsave() to use it, which is easier than dealing with GhostScript:

ggsave(p, filename = "whatever.pdf", device = cairo_pdf, 
       width = 4, height = 3, units = "in")