KableExtra: colortbl's \cellcolor not filling entire cell when used in combination with \makecell

Viewed 326

I'm looking for a workaround solution for a known issue where \cellcolor from the colortbl package does not work properly with \makecell. As mentioned, there probably already exists a workaround in Latex, but I'm hoping for a solution in terms of the R package kableExtra when producing pdfs using rmarkdown. Here's a screenshot; as can be seen, some cells are not filled entirely.

Here's a minimally reproducible example in rmarkdown:

library(kableExtra)
library(tidyverse)
# Data --------------------------------------------------------------------
df <- tribble(
  ~col1, ~col2, 
  "really long text that needs to be broken into multiple lines so it can fit", 4,
  "really long text that needs to be broken", 4,
  "really long text that needs a fix", 4,
) %>%
  modify_at(
    .x = .,
    .at = 1,
    .f = stringr::str_wrap,
    width = 25
  )
# Table -------------------------------------------------------------------
df %>%
  mutate(across(.cols = 1, .fns = linebreak, align = "l")) %>%
  kbl(x = ., escape = FALSE) %>%
  row_spec(row = 1:3, background = "#e5e5e5")

One possible fix is to specify keep_tex: true in the YAML and fix the issue in the .tex file manually before using pandoc. But I'm generating many tables and this can't possibly be efficient. Any suggestion of a potential workaround would be greatly appreciated.

2 Answers

Why not use column_spec to force the line wrap rather than using makecell and linewrap?...


library(tibble)
library(dplyr) #for the pipe
library(kableExtra)


df <- tribble(
  ~col1, ~col2, 
  "really long text that needs to be broken into multiple lines so it can fit", 4,
  "really long text that needs to be broken", 4,
  "really long text that needs a fix", 4,
)


kbl(df) %>%
  column_spec(1, width = "30mm") %>% 
  row_spec(row = 1:3, background = "#e5e5e5")

enter image description here

Another solution that worked for my use case (where column_spec(width) fails), is based on leandriis's comment in this post. Specifically, we wrap the \colorbox command around the \makecell command to have a completely colored cell. For example \colorbox[HTML]{hexcode}{\makecell{text}}. This requires that we create our own custom linebreak function, which not only wraps our text in \makecell but also wraps that latex code in a \colorbox.

# Custom linebreak --------------------------------------------------------
linebreak_custom <- function(x, align = "l", linebreaker = "\n") {
  ifelse(
    # If '\n' is detected in a string, TRUE, or else FALSE
    test = str_detect(x, linebreaker),
    # For TRUE's, wrap text in \makecell and then in \colorbox
    yes = paste0(
      "\\cellcolor[HTML]{e5e5e5}{\\colorbox[HTML]{e5e5e5}{\\makecell[", align, "]{",
      str_replace_all(x, linebreaker, "\\\\\\\\"), "}}}"
    ),
    # Return as is if no '\n' detected
    no = x
  )
}
# Data --------------------------------------------------------------------
df <- tribble(
  ~col1, ~col2,
  "really long text that needs to be broken into multiple lines so it can fit", 4,
  "really long text that needs to be broken", 4,
  "really long text that needs a fix", 4,
) %>%
  modify_at(
    .x = .,
    .at = 1,
    .f = stringr::str_wrap,
    width = 25
  )
# Table -------------------------------------------------------------------
df %>%
  mutate(across(.cols = 1, .fns = linebreak_custom, align = "l")) %>%
  kbl(x = ., escape = FALSE) %>%
  row_spec(row = 1:3, background = "#e5e5e5")

This produces the desired output:

As noted, this solution may not be robust to future versions of kableExtra (as it involves, quite frankly, "hacking" the source code), and the solution suggested by Peter above should still be better in many use cases. I've created an issue via Github to bring this to their attention, so perhaps there could be an official fix in their code in the future.

Related