An Environment for Reliably Rendering Figures in R

Fathom Data is working on a project to reproduce the figures from the CORE textbook The Economy using R and {ggplot2}. There’s a strict style guide which specifies the figure aesthetics including colours and font. We’re a team of seven people working on as many different setups. The principle challenges have been package versions and fonts.

We’ve solved the problem by creating a custom Docker image, which provides both packages and fonts. In retrospect, we should have done this much earlier.


This is the Dockerfile describing the image.

FROM rocker/verse:4.0.4

RUN apt-get update -qq && \
    apt-get install -y -qq ttf-ubuntu-font-family && \
    apt-get -y purge fonts-roboto fonts-roboto-unhinted fonts-lmodern fonts-texgyre && \
    # Clean up after apt.
    rm -rf /var/lib/apt/lists/*

RUN install2.r -e \
      extrafont \
      ggnewscale \
      ggtext \
      googledrive \
      gridExtra \
      here \
      htmlwidgets \
      janitor \
      patchwork \
      plotly \
      readxl \
      rsvg \
      showtext \

RUN wget && \
    R CMD INSTALL svglite_1.2.3.2.tar.gz && \
    rm svglite_1.2.3.2.tar.gz

# Install Google Fonts.
ARG GOOGLE_TTF=/usr/share/fonts/truetype/google-fonts
RUN git clone git:// && \
    mkdir -p $GOOGLE_TTF && \
    find fonts -regex '.*Asap-[a-zA-Z]+\.ttf$' -exec install -Dm644 {} $GOOGLE_TTF \; && \
    rm -rf fonts/ && \
    fc-cache -f > /dev/null

RUN R -e "library(extrafont); font_import(prompt = FALSE); loadfonts(quiet = FALSE)"

COPY core ./core

RUN cd core && \
    R -e "devtools::build(); devtools::install(dependencies = FALSE)"


Here are the key points:

  • It’s based on the 4.0.4 version of the rocker/verse image, which means that it immediately includes the most recent version of R, RStudio, all of the {tidyverse} as well as TeX and other publishing-related packages.
  • Google Fonts are cloned from their GitHub repository, but only Asap is retained.
  • All of the R packages that we’re using are baked into the image.
  • To get a specific (older) version of {svglite} we download and install from the .tar.gz archive.
  • The {extrafont} package is used to import system TTF fonts into R.
  • The {core} package (a submodule on our repository), built and maintained by Megan Beckett, is copied across onto the image, built and installed. Specify dependencies = FALSE to ensure that {svglite} is not updated!
  • A default password is set. There’s probably no harm in this since the image will only be used locally.


Create a volume to persist work between sessions.

docker volume create core

Run the image, mounting the volume and exposing port 8787.

docker run -it --rm \
	--name rcore \
	-p 8787:8787 \
	--mount source=core,target=/home/rstudio \

And access at 🚀


Validating that the requirements are satisfied.

First check that we have the required version of {svglite}.

> packageVersion("svglite")

Looks good.

What about fonts?

> library(extrafont)
> fonts()
 [1] "Asap"             "Asap Medium"      "Asap SemiBold"    "DejaVu Sans"
 [5] "DejaVu Sans Mono" "DejaVu Serif"     "Ubuntu"           "Ubuntu Light"
 [9] "Ubuntu Thin"      "Ubuntu Condensed" "Ubuntu Mono" 

Yup, everything that we need is there.


All of the data for the figures are stored in Google Sheets. So an important component of the workflow is authenticating with Google. We wanted this to occur immediately upon loading the {core} package and the authentication details to be persisted between sessions. Matt Dennis implemented this using the use_oob option to googledrive::drive_auth(), checking first for an interactive session to ensure that it wasn’t triggered during package build.

.onLoad <- function(libname, pkgname) {
  if (interactive()) {
    googledrive::drive_auth(use_oob = TRUE)

With this setup we have everything we need to create the figures in a stable, reproducible environment. No need to install fonts or packages. Rather than worrying about technical issues, we can focus on the figures.

📢 Thanks to the maintainers of the version-stable Rocker images, which made putting this together a lot easier.