ggrepel icon indicating copy to clipboard operation
ggrepel copied to clipboard

Support for {gridtext}

Open teunbrand opened this issue 5 months ago • 4 comments

This PR aims to fix #169.

Briefly, this PR gives geom_text_repel() a grob argument that can be used to provide a different grob constructor than textGrob(). You can pass, for example, a gridtext::richtext_grob() to draw the labels.

devtools::load_all("~/packages/gridtext/") # explained later why I'm using devtools here
#> ℹ Loading gridtext
devtools::load_all("~/packages/ggrepel")
#> ℹ Loading ggrepel
#> Loading required package: ggplot2

logo <- system.file("extdata", "Rlogo.png", package = "gridtext")

df <- data.frame(
  x = c(0, 4.9,5,5.1, 10),
  y = c(0, 4.9,5,5.1, 10),
  labels = c(
    "Some <span style='color:blue'>*italic blue*</span> text",
    "Other <span style='color:red'>**bold red**</span> text",
    paste0("<img src='", logo, "' width = '50'/>"),
    "Text with some <sup>superscript</sup>",
    "Drink enough H<sub>2</sub>O."
  )
)

ggplot(df, aes(x, y)) +
  geom_point() +
  geom_text_repel(
    aes(label = labels), 
    grob = richtext_grob
  )

To control additional arguments to that grob constructor, that don't have the plumbing to go through the parameters or aesthetics, you pass a list to grob_args.

ggplot(df, aes(x, y)) +
  geom_point() +
  geom_text_repel(
    aes(label = labels), 
    grob = richtext_grob,
    grob_args = list(
      padding = margin(10, 10, 10, 10),
      box_gp = gpar(fill = "#88888844", col = "grey"),
      r = unit(10, "pt")
    )
  )

Created on 2024-01-13 with reprex v2.0.2

To get this to work I had to make the following changes:

  1. Install the plumbing for grob and grob_args to reach the place where they're used and avoid argument clashes and mismatches.
  2. I had to change how the sizes of labels are measured. For rich text grobs, the labels can have a lot of additional html/markdown markup that count towards stringWidth() or stringHeight() of the label, but are not part of the actual size of the label. Now, the grobs themselves are measured instead of just their labels.

I've marked this PR as a draft because there is a caveat. You cannot use any grob constructor as the grob argument. Well, you probably can but that'd return errors or draw the labels incorrectly. The grob must have a decent xDetails() or yDetails() method to be accurately measured. Currently, {gridtext} does not have these methods, but I've offered to implement these in https://github.com/wilkelab/gridtext/issues/33. That is why I'm using my local copy of {gridtext} in the example, because that points to a local repo with those changes implemented. It'd probably make sense to only merge this PR if {gridtext} accepts the other PR.

teunbrand avatar Jan 13 '24 20:01 teunbrand