kableExtra icon indicating copy to clipboard operation
kableExtra copied to clipboard

Extra dependencies aren't always added

Open dmurdoch opened this issue 1 year ago • 5 comments

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.

dmurdoch avatar Sep 05 '22 18:09 dmurdoch

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".

paulrougieux avatar Sep 06 '22 07:09 paulrougieux

Adding unloadNamespace("kableExtra") to the setup code chunk before the first use of anything from the package fixes it for me.

dmurdoch avatar Sep 06 '22 08:09 dmurdoch

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.

paulrougieux avatar Sep 06 '22 08:09 paulrougieux

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.

dmurdoch avatar Sep 06 '22 10:09 dmurdoch

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.

dmurdoch avatar Sep 06 '22 16:09 dmurdoch