5

In kableExtra >= 0.8.0, the canonical way to insert a linebreak into text piped into a table from a kableExtra function such as add_header_above or pack_rows is to add an \n directly.

However, this appears not to work with the escape = FALSE argument, which is required if the text also contains LaTeX code.

How can one force linebreaks in kableExtra functions with escape = FALSE?

library(dplyr)
library(knitr)
library(kableExtra)

starwars %>%
  filter(species == 'Gungan' | species == 'Droid') %>%
  arrange(species) %>%
  select(name, eye_color) %>%
  kbl(booktabs = TRUE) %>%
  pack_rows(
    index = c(
      'The droids: everybody\'s favourite' = 6, 
      'The Gungans: only beloved of \nthose aged under $3^2$' = 3), 
    escape = FALSE)
Westcroft_to_Apse
  • 1,503
  • 4
  • 20
  • 29

2 Answers2

3

ISSUE

The issue at hand is that you wish to escape part of your header (i.e., the break) and not escape another part (i.e., the math code).

Further Complications

This core issue is further complicated by a number of factors:

  1. when and how kableExtra is programmed to deal with escaping
  2. a desire to have a solution that works for both html and LaTeX output
  3. when and how R evaluates code

A SOLUTION

Here is a solution that will work for both html and LaTeX output, but it is not as clean and straight forward as your original code:

# a new version of `kableExtra::linebreak()` that takes into account what type 
# of output is desired as well as how much escaping is necessary
linebreak2 <- function(x, double_escape = TRUE, ...) {
  # if LaTeX insert text into a `\makecell[]{}` command and double escape
  if(knitr::is_latex_output())
    return(linebreak(x, double_escape = double_escape, ...))
  
  # if html output just replace `\n`s with `<br/>`s
  if(knitr::is_html_output())
    return(gsub("\n", "<br/>", x))
  
  # let x pass through for other types of output
  return(x)
}

# build the index named vector outside the pipe flow 
# in order to set the names using `linebreak2()`
index <- c(6, 3)
names(index) <- c(
  'The droids: everybody\'s favourite',
  linebreak2('The Gungans: only beloved of \nthose aged under $3^2$')
)

# proceed as before
starwars %>%
  filter(species == 'Gungan' | species == 'Droid') %>%
  arrange(species) %>%
  select(name, eye_color) %>%
  kbl(booktabs = TRUE) %>%
  pack_rows(index = index, escape = FALSE)

PDF Output

enter image description here

HTML Output

enter image description here

the-mad-statter
  • 5,650
  • 1
  • 10
  • 20
2

You could use html line break tag <br/>:

starwars %>%
  filter(species == 'Gungan' | species == 'Droid') %>%
  arrange(species) %>%
  select(name, eye_color) %>%
  kbl(booktabs = TRUE) %>%
  pack_rows(
    index = c(
      'The droids: everybody\'s favourite' = 6, 
      'The Gungans: only beloved of <br/> those aged under $3^2$' = 3), 
    escape = FALSE)

enter image description here

Waldi
  • 39,242
  • 6
  • 30
  • 78
  • Unfortunately, that only works when rendering to HTML -- as one would expect, given that
    is an HTML tag. When rendering to PDF, the tag appears as text in the rendered table itself.
    – Westcroft_to_Apse Jul 28 '22 at 14:07