Saving ggplot graph to PDF with fonts embedded in r
Asked Answered
T

3

12

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.

Tier answered 1/9, 2017 at 21:43 Comment(3)
I noticed that changing the height and width in ggsave() helped. Now there are letters next on the chart, but it is all typed on one spot. So it's all the letters typed over themselves. For example, the title has all its letters centered on one spot so the word is unreadable. It appears to may be a sizing issue. Does anyone have any tips on sizing?Tier
You can save some typing on the theme: theme(text=element_text(family = "Calibri"), axis.text=element_text(size=10), axis.title=element_text(size=15), plot.title = element_text(hjust=0.5, size=20)).Libation
That's useful. Thanks eipi10!Tier
M
17

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")
Miyokomizar answered 2/10, 2017 at 19:8 Comment(1)
For anyone with the same problem as I had: I was using rmarkdown::render() to generate a PDF report from a .Rmd file I created. In that .Rmd file, you can use knitr::opts_chunk$set(dev = "cairo_pdf") to apply this to all chunks, or just set it on the chunk you wish with {r, dev="cairo_pdf"}.Dail
L
5

I’ve found it safer to explicitly register fonts using pdfFonts (and/or postscriptFonts).

The documentation contains an example but also take a look at my fonts module. With this, registering a new font is as easy as writing

fonts$register_font('Calibri')

Internally, this creates a font specification using Type1Font, ensures that names are set correctly, and invokes pdfFonts.

It also ensures that the complete set of font metrics to exist (which is done using extrafont::ttf_import).

This way is considerably faster than generating font metrics for all fonts using font_import, and it gives you more control.

Lutyens answered 2/9, 2017 at 9:6 Comment(0)
T
2

I think you missed the initialization step font_import(). Be forewarned, executing this command can take a bit longer time.

First, you can see what fonts you have available with the command windowsFonts(). The current fonts in my graphing device are;

$serif
[1] "TT Times New Roman"

$sans
[1] "TT Arial"

$mono
[1] "TT Courier New"

Thereafter, you can import the extrafont library and the loadfonts(device = "win"). I also recommend to execute these commands in the R console and not in RStudio. I suggest this because when you are importing the fonts using font_import() in RStudio, it may not show the y/n prompt.

Below I provide a minimum reproducible example;

    library(ggplot2)
    library(extrafont)
    font_import()
    # tell where ghostscript is located. This is required for saving the font in pdf
    Sys.setenv(R_GSCMD = "C:\\Program Files\\gs\\gs9.21\\bin\\gswin64c.exe") # I have installed 64-bit version of GhostScript. This is why I've used gswin64c.exe. If you have installed 32-bit version of GhostScript, use gswin32c.exe. Failure to specify the correct installed GhostScript will yield error message, "GhostScript not found"
    # create a plot object
p <- ggplot(mtcars, aes(x=wt, y=mpg)) + 
  geom_point()+
  ggtitle("Fuel Efficiency of 32 Cars")+
  xlab("Weight (x1000 lb)") + ylab("Miles per Gallon") +
  theme_bw()+
  theme(text=element_text(family="ArialMT", size=14))
# show the plot
print(p)

plot

# save the plot as pdf  
ggsave("figures//ggplot_arialmt.pdf", p, width=20, height=20, 
   device = "pdf", units = "cm")

Note

Its only the ArialMT font that seems to work with ggsave(). See this SO post. Using any other font for saving to pdf, renders the figure with characters on top of another. This is also an open issue for ggsave and has not been answered since 2013.

Toback answered 2/9, 2017 at 3:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.