0

I can create a shadow-text in an empty ggplot2 plot as described here. I want to create a huge number of such plots, where:

  • the text size is automatically adjusted in case the text is too long to fit the width of the empty plot
  • the text is automatically centered in the middle of the empty plot

Consider the following example:

library("ggrepel")                       # Load packages
library("ggplot2")

my_text <- c("Text 1",                   # Three different text elements
             "Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong Text 2",
             "Some Other Text 3")

for(i in 1:length(my_text)) {

  ggp <- ggplot() +                      # Draw ggplot2 plot with text only
    theme_void() +
    geom_text_repel(mapping = aes(1, 1, label = my_text[i]),
                    color = "blue",
                    bg.color = "red",
                    bg.r = 0.05)

  ggsave(ggp,                            # Save ggplot2 plot
         filename = paste0("my_text_", i, ".png"),
         bg = "transparent",
         width = 101.6,
         height = 57.15,
         scale = 0.05,
         dpi = 1000)
}

The output of the previous R code looks as follows:

enter image description here enter image description here enter image description here

As you can see, we have created three different images with shadow-text. However, none of these text elements is aligned exactly in the middle of the plot. Furthermore, the second text is too long to fit the plotting area and should therefore have a smaller font size.

How could I automatically adjust the font size if necessary, and how could I automatically center each text?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Joachim Schork
  • 2,025
  • 3
  • 25
  • 48

2 Answers2

1

Here's one option for how you could do that. Basically, you would want the size of the text to scale according to the length of the text. I would store your text in a data frame structure, with one column for text and the other for length:

library(ggplot2)
library(shadowtext)

my_text_df <- data.frame(
  text=c("Text 1", "Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong Text 2",
         "Some Other Text 3")
) 

my_text_df$length <- nchar(my_text_df$text)  # compute length of each string
base_text_size <- 13   # base size used for the plot
weighting <- 9    # some weighting value in calculation

for(i in 1:length(my_text_df)) {
  ggp <- ggplot() +                      # Draw ggplot2 plot with text only
    theme_void() +
    geom_shadowtext(
      mapping = aes(1, 1, label = my_text_df$text[i]),
      color = "blue", bg.color = "red", bg.r = 0.05,
      size= base_text_size * weighting/my_text_df$length[i]
    )

  ggsave(ggp,                            # Save ggplot2 plot
         filename = paste0("my_text_", i, ".png"),
         bg = "transparent",
         width = 101.6,
         height = 57.15,
         scale = 0.05,
         dpi = 1000)
}

That gives you these plots below. You could adjust the algorithm associated with size= to fit your needs - or base it off of a different calculation from each text string using a similar approach. In this case, I'm scaling linearly, but you may want to scale with 1/log or something like that... play around with the math to suit your needs.

enter image description here enter image description here enter image description here

chemdork123
  • 12,369
  • 2
  • 16
  • 32
  • Thank you for the response! This solves my problem with the font size very well. Unfortunately, the text is still not centered (e.g. Text 1 is too far on the right side). Do you maybe know a way how I could solve this problem as well? – Joachim Schork Mar 23 '21 at 15:57
  • I just noticed... this is due to use of `geom_text_repel` instead of just `geom_text()`. Is there a reason you use the `ggrepel` version? Used `geom_text()` now in the answer and it seems fixed. – chemdork123 Mar 23 '21 at 18:18
  • Thanks for getting back to me! The reason for ggrepel is that it creates a shadow-text, i.e. a red border behind the blue text. In your code, this red border is not shown anymore. – Joachim Schork Mar 24 '21 at 08:32
  • Please see my answer below. Thanks again for your help! – Joachim Schork Mar 24 '21 at 09:43
  • 1
    @JoachimSchork, you had posted originally in your question `geom_text_repel()`, although the images are from `geom_shadowtext()`. Now I see you *actually* meant to use `geom_shadowtext()`. I've made the adjustments to my answer, so it now has the red border around the text as you intended. – chemdork123 Mar 24 '21 at 12:22
  • The original images come from `geom_text_repel()`. The issue due to `geom_text_repel()` is fixed with `geom_shadowtext` (as described in my answer below). Anyway, now it works fine. Thank you very much! – Joachim Schork Mar 24 '21 at 12:53
0

Based on chemdork123's response and an additional Google search for other packages I have found a solution. As you can see below, I'm using the suggestion of chemdork123 to weight my text size, and I'm using the shadowtext package instead of the ggrepel package. Furthermore, I'm plotting invisible data points to be able to adjust my text positions:

library("ggplot2")
library("shadowtext")

data <- data.frame(x = 0:1,
                   y = 0:1)

my_text <- c("Text 1",                   # Three different text elements
             "Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong Text 2",
             "Some Other Text 3")

my_text_size <- ifelse(nchar(my_text) <= 70,
                       200 / 40,
                       200 / nchar(my_text))

for(i in 1:length(my_text)) {

  ggp <- ggplot(data, aes(x, y)) +       # Draw ggplot2 plot with text only
    geom_point(alpha = 0) +
    theme_void() +
    geom_shadowtext(mapping = aes(x = 0.5, y = 1, label = my_text[i]),
                    color = "blue",
                    bg.color = "red",
                    bg.r = 0.05,
                    size = my_text_size[i])

  ggsave(ggp,                            # Save ggplot2 plot
         filename = paste0("my_text_", i, ".png"),
         bg = "transparent",
         width = 101.6,
         height = 57.15,
         scale = 0.05,
         dpi = 1000)
}

Output:

enter image description here enter image description here enter image description here

Joachim Schork
  • 2,025
  • 3
  • 25
  • 48