0
votes

EDIT: Because I had set the format options in a global function, I have to set either latex_options or bootstrap_options in the kable_styling() call. I was using bootstrap_options which wasn't being read by the latex. My work-around is to make the tables twice, once in a chunk for html, and once in a chunk for latex. Not great, but it works if I click the Knit button and choose Knit to PDF. However, it throws the original error when I try to run it in the shiny app.


I have created a test version (MiniTest) of my project. What I need to do is have a shiny app run with a tab that will produce an html file for a user-chosen (reactive) Country, and provide an Excel download (I have that working so kept it out of this example), and a PDF download. I knit in an .Rmd which chooses the format and allows for parameterization. (The shiny part was set up by someone else, from whom I took over this project when they left before finishing it.)

I use kable and kableExtra to create and format tables, as I heard it words for both html and LaTeX output. The HTML is more as less as I want it. I can knit either html or PDF, and it runs, BUT when in the shiny app, only the html portion works. I think I have narrowed down the PDF issue(s) to column_spec crashing the download. If I comment out the column_spec lines in t01 and t02, the Download PDF runs. But I need that formatting. I'm sorry, but I've lost track of all the sites I have searched.


In global.R, I set:

countries <- c("ABC", "DEF", "GHI", "JKL")

In the .Rmd, I have YAML set up (with two-space indents for Country and output types):

params:
Country: ABC
output:
pdf_document: default
html_document: default

Relevant .Rmd chunks and inline code include:

knitr::opts_chunk$set(echo = FALSE)
options(knitr.table.format = function() {
  if (knitr::is_latex_output()) "latex" else "html"
})
library(shiny) 
library(htmlwidgets) 
library(shinythemes) 
library(shinydashboard) 
library(shinyjs) 
library(shinycssloaders)
library(markdown)
library(tidyr)
library(tidyverse)
library(janitor)
library(kableExtra)
options(scipen = 999)

mini <- mtcars %>%
  tibble::rownames_to_column(var = "car") %>%
  mutate(Country = c(rep("ABC", 8), rep("DEF", 8), rep("GHI", 8), rep("JKL", 8)))

## https://bookdown.org/yihui/rmarkdown-cookbook/font-color.html
colorize <- function(x, color) {
  if (knitr::is_latex_output()) {
    ## hack setting color='blue' instead of a hexcode with # that breaks the LaTeX code
    sprintf("\\textcolor{%s}{%s}", color = 'blue', x)    ## works, but isn't right blue
    } else if (knitr::is_html_output()) {
    sprintf("<span style='color: %s;'>%s</span>", color, x)
  } else x
}


## make two tables with `kable` and `kableExtra`
new_title <- paste0("Dynamically Changing Country Name in column", params$Country)
t01 <- mini %>% 
  filter(Country == params$Country) %>%
  select(car, mpg:hp) %>%
  rename({{new_title}} := car) %>%
  kable(align = c("l", "c", "c", "c", "c")) %>%
  kable_styling(full_width = FALSE, position = "left", bootstrap_options = c("striped", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(2:3, width = "5em") %>%
  row_spec(0, color = "#2A64AB") %>%
  row_spec(6, bold = TRUE)


t02_title <- paste0(params$Country, " Table with Dollar Signs in Var Names")
t02 <- mini %>%
  filter(Country == params$Country) %>%
  select(car, drat, wt) %>%
  mutate(car = case_when(car == "Mazda RX4" ~ "Mazda RX4 (US\\$)*", TRUE ~ as.character(car))) %>%
  ## want to blank out column names - removing them entirely would be best, but it fails
  kable(align = c("l", "r", "c"), escape = TRUE, col.names = c("", "", "")) %>%
  kable_styling(full_width = FALSE, position = "left", bootstrap_options = c("striped", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%
  column_spec(2, width = "10em") %>%
  footnote(general = "*Never smart to start with an asterisk, but here we are", general_title = "")


## make two charts with `ggplot2`

chart1 <- mini %>%
  filter(Country == params$Country) %>%
  select(car, mpg:hp) %>%
  ggplot2::ggplot(mapping = aes(x = mpg)) +
    geom_col(aes(y = `cyl`, fill = "cyl"), color = "black")
c1_title <- paste0("Some fab title here for ", params$Country)

chart2 <- mini %>%
  filter(Country == params$Country) %>%
  select(car, vs:carb) %>%
  ggplot2::ggplot(mapping = aes(x = carb)) +
    geom_col(aes(y = `gear`, fill = "gear"), color = "black")
c2_title <- paste0("Another chart, ", params$Country)


## make a "tiny" LaTeX environment that is only generated for LaTeX output, with chunk setting `include = knitr::is_latex_output()`.
knitr::asis_output('\n\n\\begin{tiny}')

## Table 1
t01

I expect a PDF to pop up, but instead a Save File box pops up asking to save "DownloadPDF" with no file extension. The ui.R is supposed to name it as "FactCountryName.pdf" where "CountryName" is input from the Country the user chose in the drop-down list. Regardless of whether I choose Save (nothing happens) or Cancel, my R throws the following error:

``` ! LaTeX Error: Illegal character in array arg. ```

If I comment out the line column_spec(1, bold = TRUE) %>%, the error changes to:

``` ! Use of \@array doesn't match its definition. \new@ifnextchar ...served@d = #1\def \reserved@a { #2}\def \reserved@b {#3}\f... l.74 ...m}|>{\centering\arraybackslash}p{5em}|c|c} ```

Please help!

1

1 Answers

0
votes

Turns out that using the Knit button in R automatically loads the required LaTeX packages, such as booktabs. Running the file in the Shiny app was not loading all the packages needed. All I had to do was specifically call the extra packages in the YAML (which I found by looking at the .tex file made from the PDF through Knit button).

---
params:  
  Country: ABC  
header-includes:  
- \usepackage{booktabs}
- \usepackage{longtable}
- \usepackage{array}
- \usepackage{multirow}
- \usepackage{wrapfig}
- \usepackage{float}
- \usepackage{colortbl}
- \usepackage{pdflscape}
- \usepackage{tabu}
- \usepackage{threeparttable}
- \usepackage{threeparttablex}
output:
  pdf_document:
    keep_tex: true
  html_document: default
---