Match ggplot2 font to Rmd/Qmd html theme

Viewed 48

I'm looking for a general way to automatically match the font of a ggplot with the font used by the html's theme. In the code below, I'd like to detect that the 'yeti' theme uses the 'Open Sans' font for body text. Similarly, if the theme was 'united', it should be detected that the font is 'Ubuntu'.

---
title: "Untitled"
output: 
  html_document:
    theme: yeti
date: "2022-09-14"
---


```{r setup, include=FALSE}
knitr::opts_chunk$set(dev = "ragg_png") # so that fonts render effortlessly
```


```{r plot}
library(ggplot2)

font <- magic_function_to_retrieve_current_font()

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  theme_gray(base_family = font)
```

In the code above, I'd like to know a function, here called magic_function_to_retrieve_current_font(), that does the following things:

  1. It detects that the document is being knit with the 'yeti' theme.
  2. It finds the theme's style sheet
  3. It looks up the CSS code for the body text in the style sheet
  4. From the body text properties, it extracts the 'font-family' property (and returns the first entry)

Now (3) and (4) I may figure out myself, but I'm having difficulty with steps (1) and (2). Is there some magic knitr/rmarkdown/quarto functions I could use to have access to the YAML/theme style sheet while knitting the document?

1 Answers

So @stefan's comments helped me a lot by nailing down the missing pieces. My current solution is the following:

---
title: "Untitled"
output: 
  html_document:
    theme: yeti
date: "2022-09-14"
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(dev = "ragg_png")
```

```{r}
get_current_theme <- function() {
  if (interactive()) {
    yaml_meta <- rmarkdown::yaml_front_matter(
      rstudioapi::getSourceEditorContext()$path
    )
  } else {
    yaml_meta <- rmarkdown::metadata
  }
  yaml_meta$output$html_document$theme
}

get_font <- function(theme = get_current_theme()) {
  # Lookup font from theme
  fnt <- bslib::bs_get_variables(
    bslib::bs_theme(bootswatch = theme),
    "font-family-sans-serif"
  )
  # Parse variables to family names
  fnt <- strsplit(fnt, split = ", ")[[1]]
  fnt <- gsub('\"', "", fnt, fixed = TRUE)
  
  # Verify font is available
  fnt_sys <- systemfonts::system_fonts()
  idx <- match(fnt, fnt_sys$family)
  ans <- fnt[!is.na(idx)][[1]]
  
  if (length(ans) == 0) {
    ans <- "" # default font family
  }
  ans
}
```

```{r plot}
library(ggplot2)

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  theme_gray(base_family = get_font())
```

Some regular body text to compare
Related