wilkelab / cowplot

cowplot: Streamlined Plot Theme and Plot Annotations for ggplot2
https://wilkelab.org/cowplot/
701 stars 84 forks source link

interaction cowplot/ggtext/showtext; spacing text-elements #167

Closed wsteenhu closed 4 years ago

wsteenhu commented 4 years ago

I am currently preparing figures for a manuscript submission. I would like to use a custom font in the figures (in this case Myriad Pro). At first, I thought a combination of showtext/ggtext-package seemed to provide this (also for .Rmd-usage), yet I now find that in the context of cowplot, spacing of text-elements goes awry.

First an example heatmap without showtext:

library(tidyverse)
library(ggtext)
library(cowplot)

iris_test <- iris %>%
  mutate(Species = case_when(Species == "setosa" ~ "*Haemophilus haemolyticus* (6)",
                             Species == "versicolor" ~ "*M. nonliquefacience/catarrhalis* (1)",
                             TRUE ~ "*C. pseudodiphth./propinquum* (2)")) %>%
  pivot_longer(-Species, names_to = "variable", values_to = "values")

hm <- iris_test %>%
  ggplot(aes(x = Species, y = variable, fill = values)) +
  geom_tile() +
  theme(axis.text.x = element_markdown(angle = 45, hjust = 1),
        plot.margin = margin(5.5, 5.5, 5.5, 80))

cowplot::plot_grid(hm, hm, nrow = 1)

Schermafbeelding 2020-08-27 om 10 29 06

In this case, spacing of x-labels is fine.

Next, with showtext/custom font (first without cowplot; single heatmap):

library(showtext)
sysfonts::font_add("Myriad Pro", 
                   regular = "/Users/Wouter/Library/Fonts/myriad-pro-regular.otf", # adjust paths to custom font
                   italic = "/Users/Wouter/Library/Fonts/myriad-pro-italic.otf")

showtext_auto()

hm_st <- hm + 
  theme(text = element_text(family="Myriad Pro"))
hm_st

Schermafbeelding 2020-08-27 om 10 32 07

Again labels are fine.

Next, when using cowplot, x-label-spacing starts to shift, with large gaps between different elements.

cowplot::plot_grid(hm_st, hm_st, nrow = 1)

Schermafbeelding 2020-08-27 om 10 29 23

Strikingly, when interchanging the code above from element_markdown to element_text (removing ggtext from the equation), rerunning each code-block, the spacing problem again disappears.

Schermafbeelding 2020-08-27 om 10 31 22

I am not knowledgeable enough to solve this issue, to me, it appears to be an interaction between cowplot/showtext and ggtext, as problems disappear by dropping either one of the packages.

Of note: this is a more broad issue than only x-labels and also occurred to, for example, strip titles.

clauswilke commented 4 years ago

You need to set a null device that knows about your font. Depending on your system, this may be "png" or "cairo". The development version also has "agg", which should work on all systems.

Also, there have been some fixes recently to this code, so running the development version may be a good idea anyways.

https://wilkelab.org/cowplot/reference/set_null_device.html

wsteenhu commented 4 years ago

Thank you for your answer. In fact, screenshots in the issue described above were taken from within Rstudio (plot zoom pane). For the manuscript itself I ran .Rmd-scripts with the device set to "png" and "cairo" like you suggest, yet that gives me the same issues as described above.

Also, I would say that this might not have to do with the font not being recognised, since it doesn't happen when not using cowplot or when not using ggtext.

Anything I might be overlooking/anything else I can try to get this to work?

clauswilke commented 4 years ago

No, it's definitely the issue I point out. See reprex below. Your png and cairo graphics devices may not recognize the font. Try agg.

library(tidyverse)
library(ggtext)
library(cowplot)

iris_test <- iris %>%
  mutate(Species = case_when(Species == "setosa" ~ "*Haemophilus haemolyticus* (6)",
                             Species == "versicolor" ~ "*M. nonliquefacience/catarrhalis* (1)",
                             TRUE ~ "*C. pseudodiphth./propinquum* (2)")) %>%
  pivot_longer(-Species, names_to = "variable", values_to = "values")

hm <- iris_test %>%
  ggplot(aes(x = Species, y = variable, fill = values)) +
  geom_tile() +
  theme(axis.text.x = element_markdown(angle = 45, hjust = 1),
        plot.margin = margin(5.5, 5.5, 5.5, 80))

hm_st <- hm + 
  theme(text = element_text(family="Myriad Pro"))

# pdf device doesn't know the font, so we get tons of warnings
cowplot::set_null_device("pdf")
cowplot::plot_grid(hm_st)
#> Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y, : font
#> family 'Myriad Pro' not found in PostScript font database
#> Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
#> 'Myriad Pro' not found in PostScript font database

#> Warning in grid.Call(C_stringMetric, as.graphicsAnnot(x$label)): font family
#> 'Myriad Pro' not found in PostScript font database

# [...] many more warnings deleted

# agg device knows the font, so things work fine
cowplot::set_null_device("agg")
cowplot::plot_grid(hm_st)

Created on 2020-08-27 by the reprex package (v0.3.0)

wsteenhu commented 4 years ago

Thanks again for your help, much appreciated, sorry to keep bothering you with this. When I run your reprex it definitely gets better, but still the spacing isn't quite right (space between italic species name and number disappears).

Schermafbeelding 2020-08-28 om 08 09 27

Since in your example/on your system all spacing is right, this might have to do with my system/how I registered the font I reckon. Any advice on things to check/try to get this 100% right?

clauswilke commented 4 years ago

Now that I'm reading a bit more about showtext, I see many ways how this could go wrong in conjunction with cowplot and ggtext. You have three packages that all do some slightly shady things to get results you otherwise couldn't get. For this to work fully, you may have to write a cowplot null device that explicitly turns on showtext.

Also, ggtext uses font metrics caching, so if you generate a plot once without the font being available, the same plot will never come out right in that R session. You will have to restart your session to reset ggtext. (I'll disable this feature at some point in the future, as it is clearly adding additional problems.)

wsteenhu commented 4 years ago

Thanks again for this bit of background, much appreciated. Once I have the time I will have a go at a custom cowplot null device (not sure how complicated that is). I am already quite satisfied with the fact I can export almost all figures in an appropriate format using .Rmd/these packages. Hope I can take the last few steps soon.

clauswilke commented 4 years ago

It's just a few lines of code. Basically just a function that opens a graphics device. See example here: https://wilkelab.org/cowplot/reference/set_null_device.html