Knitr: Cross-reference equations, figures and tables when bookdown is NOT already installed?



This question is identical to this question that I posted on StackOverflow last month, but for which I didn’t get a satisfactory answer. Apologies for the duplication. I’m posting here because I suspect the most elegant solution might be to handle this issue in RStudio, as I will explain later. I hope this might also kick-start some activity in this direction.

I’m aware that the canonical method for cross-referencing figures and tables in a PDF produced by knitr is to use bookdown::pdf_document2.

This is awesome! But suppose that I want to send my Rmd to a user who doesn’t have bookdown already installed? How can I ensure that the document will render if knitted in an environment in which bookdown is not already installed? I want to have everything self-contained in a single Rmd file. The consumer of the document might be someone with little or no prior experience of R, and has just installed R and RStudio but no packages. How can I ensure that if they click knit that they will get the PDF with cross-references? Alternatively, what if Binder or similar provide the means to execute (or, better still, knit to PDF) Rmd files in much the same way as is done with Jupyter Notebooks – I’d like to be able to bundle everything that is needed for rendering the PDF in an automated way in the Rmd file itself.

An example document looks like this:

title: "Cross-refs test"
author: "Me"
date: "13 December 2017"
    citation_package: natbib
    fig_caption: yes
    keep_tex: yes

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)

## Title

f\left(k\right)=\binom{n}{k}p^k\left(1-p\right)^{n-k} \label{eq:binom}

You may refer to it using `\@ref(eq:binom)`, e.g., see Equation \@ref(eq:binom).

So if this Rmd file is downloaded and knit by a brain-dead zombie, or by Binder, how can I make sure that this doesn’t happen:

Error in loadNamespace(name) : there is no package called 'bookdown'
Calls: <Anonymous> ... tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
Execution halted

I can’t assume that a consumer of my Rmd will know what to do in this situation, nor any automated process that I don’t control that renders a PDF from the Rmd that it grabs from a repo somewhere (like what Binder does).

If there are no cross-references, I can specify pdf_document as the output format and RStudio will read the YAML and if knitr and rmarkdown are not installed, installation of these will be triggered automatically (the user/brain-dead zombie just has to click on “OK” to accept installation) and the PDF will be rendered. So if these two packages are missing: no problem, at least when using RStudio. User gets Rmd, installs R and Rstudio, knits the Rmd, and the PDF appears. The same is not true if using bookdown to get cross-referencing.

It seems the YAML is interpreted before R code is executed, so the same kind of logic as this that is used to print the current time in a document (date: "`r Sys.time()`") doesn’t work. Otherwise, I’d r install.packages("bookdown") here.

So, is there a method to do cross-referencing without bookdown, but as simply and as elegantly as with bookdown, or is there someway of triggering knitr to install bookdown such that rendering won’t crash when encountering bookdown::pdf_document2?

Basically, what I want is cross-references in a knitted PDF with minimal user intervention to make it happen – 'cos my user might be a brain-dead zombie or robot.

I want to avoid hard-coding cross-refs. And I’d rather not use LaTeX. Just pure R Markdown and some knitr magic, if possible.

A possible solution to this issue would be for RStudio to recognise instances of bookdown::pdf_document2 in the YAML and trigger installation of bookdown, in the same way that the user is prompted to install knitr and rmarkdown if they are not already present: they need only click a button to proceed and complete the process of rendering the document. It would appear that this would be the most elegant solution. If this is not possible, what’s the next best solution?