jhelvy / renderthis

An R package for building xaringan slides into multiple outputs, including html, pdf, png, gif, pptx, and mp4.
https://jhelvy.github.io/renderthis
Other
172 stars 12 forks source link

Don't use absolute paths or `output_dir` when rendering from Rmd #29

Closed gadenbuie closed 3 years ago

gadenbuie commented 3 years ago

It's a bit of a quirk of rmarkdown::render(), but using an absolute path in output_file or using output_dir can cause problems that are often not easily apparent until working in an environment where relative paths are required. In particular, using either of the above will cause rmarkdown::render() to write figure outputs using absolute file paths. This becomes an issue when an HTML file is served to a browser where the paths need to be relative.

Here's a reprex that demonstrates the issue:

# disable the imgur uploading in {reprex}
knitr::opts_knit$set("upload.fun" = identity)

library(xaringanBuilder)

# A minimal slide document with a single plot
cat(
  "---",
  "output: xaringan::moon_reader",
  "---",
  "",
  "```{r}",
  "plot(faithful)",
  "```",
  sep = "\n",
  file = "demo.Rmd"
)

build_pdf("demo.Rmd")
#> ℹ Building demo.html from demo.Rmd
#> ℹ Building demo.pdf from demo.html
#> Error in force(expr): Failed to generate output. Reason: Failed to open http://127.0.0.1:7737/private/var/folders/zw/8z2vpz1s0cl3gwpyh5p72mwh0000gn/T/Rtmp3nVWkK/reprex-2a9138331d3c-fussy-agama/demo_files/figure-html/unnamed-chunk-3-1.png (HTTP status code: 404)
#> ✓ Building demo.pdf from demo.html ... done
#> 
#> ℹ Building demo.html from demo.Rmd✓ Building demo.html from demo.Rmd ... done

html <- readLines("demo.html")

grep("figure-html", html, value = TRUE)
#> [1] "![](/private/var/folders/zw/8z2vpz1s0cl3gwpyh5p72mwh0000gn/T/Rtmp3nVWkK/reprex-2a9138331d3c-fussy-agama/demo_files/figure-html/unnamed-chunk-3-1.png)&lt;!-- --&gt;"

The image link points to the full absolute file path of the the rendered plot, but the absolute path can't be found once the page is being served to a browser. (Notice the link starts with /private/var/....)

Instead we need the relative URL, e.g. demo_fies/figure-html/unnamed-chunk-3-1.png.

If we need to be able to support rendering the HTML with an output file in a different folder, it's best to move into the folder containing input.Rmd, render the file to a temporary file (in that folder), and then move the temp file to a new folder. Except in the case of xaringan, this is likely to fail without a bunch more work, unless the slides are self_contained: true.

In light of the above, I'd recommend temporarily moving into the directory containing the input file before calling rmarkdown::render() and giving just the basename(output_file) to the output_file argument.

jhelvy commented 3 years ago

Hmm, are there other situations where relative paths would be required? It's a pretty easy fix if it makes sense to always use relative instead of absolute paths.

gadenbuie commented 3 years ago

I just left a comment that overlaps with this in terms of absolute/relative paths. Building the html file is definitely a unique in that it basically needs to happen in the same directory as the source Rmd. Once you have the html file, everything else can be referenced by absolute path.

gadenbuie commented 3 years ago

I realized that another thing we can do to fix this is to force self_contained = TRUE when the user wants to render the HTML file into a directory that isn't the same as the input directory. This definitely makes life a bit easier.

And since turning on or off self_contained to build slides is a thing that people will want to do without having to muck with the YAML headings, I made self_contained an argument of build_html() in a6b24e0dd4c5f507a23199ceb54c0c2b61d7a0b1.