kableExtra
kableExtra copied to clipboard
Extra dependencies aren't always added
This SO post describes the issue and a workaround. The problem is that the usepackage_latex()
calls in zzz.R
: https://github.com/haozhu233/kableExtra/blob/292f60715959ea952ff25a8aedfd782793b15f7b/R/zzz.R#L5-L18 won't be executed if kableExtra
is already loaded when rmarkdown::render()
is called.
This would be the typical state if you are calling rmarkdown::render()
explicitly, rather than having RStudio set up a separate session to call it via the Knit
button.
To Reproduce
Try to run this document test.Rmd
:
---
title: "Test document"
output: pdf_document
---
```{r setup}
library(dplyr)
```
```{r}
iris %>%
knitr::kable(format="latex") %>%
kableExtra::kable_styling() %>%
kableExtra::column_spec(2, width = "10em")
```
using rmarkdown::render("test.Rmd")
in an R session. It might succeed the first time, but the second and following calls will fail with a LaTeX error, because the LaTeX array
package is not used:
! Undefined control sequence.
<argument> r|>{\raggedleft \arraybackslash
}p{10em}|r|r|l
l.139 ...\raggedleft\arraybackslash}p{10em}|r|r|l}
Error: LaTeX failed to compile Untitled.tex. See https://yihui.org/tinytex/r/#debugging for debugging tips. See Untitled.log for more info.
A fix for this would be to run the usepackage_latex()
calls every time a kableExtra
function is called; a more efficient fix would be to set a flag when they are run, and check that flag every time. The flag needs to be cleared at the start of a knitr
run.
Here is a session rendering the above notebook twice, it fails on the second attempt. Running unloadNamespace("kableExtra")
before the third attempt makes it successful again.
Full console output of `rmarkdown::render("notebook.Rmd")` 3 times
> rmarkdown::render("notebook.Rmd")
processing file: notebook.Rmd
|.............. | 20%
ordinary text without R code
|............................ | 40%
label: setup
|.......................................... | 60%
ordinary text without R code
|........................................................ | 80%
label: unnamed-chunk-1
|......................................................................| 100%
ordinary text without R code
output file: notebook.knit.md
/usr/bin/pandoc +RTS -K512m -RTS notebook.knit.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output notebook.tex --lua-filter /home/paul/R/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /home/paul/R/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --highlight-style tango --pdf-engine pdflatex --variable graphics --variable 'geometry:margin=1in' --include-in-header /tmp/Rtmp1Khcp1/rmarkdown-str126b56c64270c.html
Output created: notebook.pdf
> rmarkdown::render("notebook.Rmd")
processing file: notebook.Rmd
|.............. | 20%
ordinary text without R code
|............................ | 40%
label: setup
|.......................................... | 60%
ordinary text without R code
|........................................................ | 80%
label: unnamed-chunk-1
|......................................................................| 100%
ordinary text without R code
output file: notebook.knit.md
/usr/bin/pandoc +RTS -K512m -RTS notebook.knit.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output notebook.tex --lua-filter /home/paul/R/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /home/paul/R/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --highlight-style tango --pdf-engine pdflatex --variable graphics --variable 'geometry:margin=1in'
! Undefined control sequence.
<argument> r|>{\raggedleft \arraybackslash
}p{10em}|r|r|l
l.128 ...\raggedleft\arraybackslash}p{10em}|r|r|l}
Error: LaTeX failed to compile notebook.tex. See https://yihui.org/tinytex/r/#debugging for debugging tips. See notebook.log for more info.
> unloadNamespace("kableExtra")
> rmarkdown::render("notebook.Rmd")
processing file: notebook.Rmd
|.............. | 20%
ordinary text without R code
|............................ | 40%
label: setup
|.......................................... | 60%
ordinary text without R code
|........................................................ | 80%
label: unnamed-chunk-1
|......................................................................| 100%
ordinary text without R code
output file: notebook.knit.md
/usr/bin/pandoc +RTS -K512m -RTS notebook.knit.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output notebook.tex --lua-filter /home/paul/R/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /home/paul/R/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --highlight-style tango --pdf-engine pdflatex --variable graphics --variable 'geometry:margin=1in' --include-in-header /tmp/Rtmp1Khcp1/rmarkdown-str126b54aee94e7.html
Output created: notebook.pdf
Note:
- adding
library(kableExtra)
to the notebook doesn't fix the issue. - as explained in my SO question, the real use case is a a loop within which I render the same notebook several times for different product groups. Similar to the use case described in bookdown where a report is rendered "for each state of a country".
Adding unloadNamespace("kableExtra")
to the setup code chunk before the first use of anything from the package fixes it for me.
The fix works for me, but I guess there is a better way to solve this in kableExtra without having to add unloadNamespace("kableExtra")
to the setup chunk.
I took a look, and couldn't spot a simple one. kableExtra
needs to call the usepackage_latex
functions once per document, but I don't think knitr
provides a hook that is run at the right time. The "document"
hook runs a little bit too late: the markdown that goes to Pandoc has already been produced. I guess you could write a document hook that edited the YAML header to add the dependencies that kableExtra
needs, but if the header already contains extra dependencies, that could be really messy.
Some kableExtra
functions add "kableExtra"
to the class of the object being printed, and then the knit_print.kableExtra
method will be called. Probably the conceptually easiest way to implement this would be to add that class name (or a different one) to all functions that might create extra package dependencies, then add them in the knit_print
method. (Or maybe all functions that take kable()
results and modify them.)
That's a lot of changes to make, but they're all small changes. I might try it and put in a PR.