6

I want to find a way to automatically wrap ggplot titles (or subtitles, or captions) to take up the full plot width and then wrap.

A previous question deals with how to wrap code nicely using a wrapper function, but you still have to specify width= manually. How can I re-write this wrapper function to automatically wrap the text based on the plot width of the plot?

My code so far:

wrapper <- function(x, ...)  {   
  paste(strwrap(x, ...), collapse = "\n") 
}

library("ggplot2")

my_title <- "This is a really long title of a plot that I want to nicely wrap and fit the plot width without having to manually add the backslash n, or having to specify with= manually"

ggplot(data = cars, aes(x = speed, y = dist)) +    
  geom_smooth() +   
  labs(title = wrapper(my_title, width = 100))

My idea: extract the plot width from ggplot somehow and then include it in the wrapper function, perhaps like this:

plot_width <- ???

wrapper <- function(x)  {   
  paste(strwrap(x, width = plot_width), collapse = "\n") 
}

How can I do this?

Or is there a better way?

captain
  • 543
  • 1
  • 3
  • 20
  • I'm sorry to bring bad news. A plot's width can't be known a priori as the graphical device is rescaleable, unless you manually set an aspect ratio or fix the width of plots/panels in the gtable to a non-'null' unit. The only solution for this is to 1. customise the title manually for each plot or 2. implement some drawtime calculation taking into account the plot's width and where the title should be broken up into new lines. Even the drawtime solution wouldn't go far, as the title's height has already been fixed at that stage. I would love for anyone to demonstrate the opposite. – teunbrand Apr 26 '19 at 17:44
  • This is a good point. I think what the answer to this question could still be useful if you're say, writing a custom plot-saving function which will automatically adjust the text wrapping in the plot when you save it to a file. This could take the dimensions of the file and use it to wrap the text in the plot accordingly. – bschneidr Apr 26 '19 at 17:50
  • Thanks @teunbrand for explaining the problems here. – captain Apr 26 '19 at 18:00
  • And thanks @bschneidr for your suggestion, this does sound like it could work. It's a bit above my level of coding at the moment, so any advice/answers will be appreciated. – captain Apr 26 '19 at 18:00

1 Answers1

6

You need to extract device width (using dev.size function). You can do it using wrapper function where argument dev_width is width of the current device. However, you still might need to adjust width for strwrap using dev_scaler argument (values around ~12 worked most of the time for me).

#' @param label character string to wrap
#' @param dev_width numeric value specifying width of current device
#' @param dev_scaler numeric value to scale dev_width (might be around ~12)
#' 
wrapper <- function(label, dev_width = dev.size("in")[1], dev_scaler = 12)  {   
  paste(strwrap(label, dev_width * dev_scaler), collapse = "\n") 
}

ggplot(data = cars, aes(x = speed, y = dist)) +    
  geom_smooth() +   
  labs(title = wrapper(my_title))
pogibas
  • 27,303
  • 19
  • 84
  • 117
  • Thanks a lot @ProGibas, I tried it just now. As you say, one still needs to manually adjust the width, so I am hoping that we can come up with a solution that does away with manual adjustments. – captain Apr 26 '19 at 18:03
  • @captain I doubt that it's possible as width depends on the font size and it's *very hard/impossible* to take that into account – pogibas Apr 26 '19 at 18:04
  • @PoGibas If you set plot defaults for your R session using a function like `theme_set(theme_classic(base_size = 10))` then this would be manageable. – bschneidr Apr 26 '19 at 18:14
  • Also, you can retrieve the font size of the default theme used in your R session with `theme_get()$text$size`, and from a specific plot you can get the font size if it differs from the default: `my_plot$theme$text$size` – bschneidr Apr 26 '19 at 18:28
  • @bschneidr Thanks! I get that, but I'm trying to figure out how can I pass that to `grid::calcStringMetric` (or maybe there's are better function to calculate strings width) – pogibas Apr 26 '19 at 18:29
  • @bschneidr magic function is `strwidth`, one just need to make a smart wrapper using it – pogibas Apr 26 '19 at 18:35
  • Ah, ok. I misunderstood you. In any case, [this Github gist](https://gist.github.com/bschneidr/5936fe47d19aec036cb69b0b40905310) shows a way to take your excellent answer and apply it so that plots in your R session will automatically print with wrapped labels. – bschneidr Apr 26 '19 at 18:39