Steven Bedrick

ggplot, Custom Fonts, and PDF Output

« Back to notes

ggplot is a wonderful library, but one area of rough edges is with the use of non-standard fonts, especially with PDF output. The fabulous R Graphics Cookbook has an entire chapter on the subject; the short version of the story is some fussing around with the extrafont package will get you most of the way there, as long as your font is TrueType (as opposed to OpenType):

library(tidyverse)
library(palmerpenguins)
library(extrafont)
# font_import()  # to be called just once, after installing the extrafont package (or whenever you install a new font)
loadfonts() # call this once per session

Now, we can take a perfectly-nice-but-with-vanilla-fonts plot…

penguin_plot <- penguins %>% 
  ggplot(aes(x=flipper_length_mm, fill=species)) + 
  geom_histogram(alpha=0.6, position="identity") + 
  theme_minimal() + 
  scale_fill_viridis_d("Species") +
  xlab("Flipper Length (mm)") +
  ylab("Frequency") + 
  ggtitle("Distribution of flipper length, by species")
penguin_plot

… And turn it into something a little bit more jazzy:

penguin_plot + theme(
  text=element_text(family="Trattatello")
)

PDF Output

The issue is that if we save this as a PDF using ggsave(), it will not actually embed the font itself into the resulting file. As such, if somebody without our font installed tries to view the PDF (like, say, a publisher of a journal), it will appear incorrectly. What’s more, at least on my computer, something is odd about how ggsave creates the PDF such that the file looks just fine in Preview.app, but has a font substitution when viewed in Adobe Acrobat.

This problem pops up now and again with different PDF creation software (see this discussion). Fortunately, there’s an easy workaround: from Preview, open your figure and re-export it as a PDF (“File”->“Export as PDF…”); the resulting file should have the font embedded correctly, and will display properly wherever it may find itself.

If this process sounds annoyingly manual, you’re not wrong; one of the posts on the discussion that I linked to above has an AppleScript-based solution, as well as one that uses Ruby to call the built-in PDFKit API on Mac OS X (aside: PDFKit is the library that Preview.app is built on top of, and is chock-a-block full of handy functionality for working with PDF files). Benjamin Jack was kind enough to post a Python-based solution; note that you’ll first need to install the pyobjc-core package.

What about OpenType?

What about OpenType fonts? Here, the best I’ve been able to do is to use the showtext package, which hacks around the issue entirely:

library(showtext)

# You can also pass in the paths to bold/italic/etc. faces
font_add("Sofa Sans Regular", "FaceType - SofaSansHand-Regular.otf")

penguin_plot + theme(
  text=element_text(family="Sofa Sans")
)

PDFs made using showtext will display just fine, but, importantly, they will not contain any of the actual text (for titles, etc.). The individual glyphs will be drawn correctly, but they will be encoded as polygons without the associated text itself. As such, it will not be possible to select, copy, search, etc. the text in the figure, which (depending on your use case) may be a deal-breaker. The good news is that since showtext-derived PDF files don’t, from the perspective of the PDF file itself, actually contain text at all, there’s no need to mess around with font embedding!


« Back to notes