How to store embedded file (includes) in custom Rmarkdown


#1

I created my own format in Rmarkdown based on this blogpost. I implemented it in my personal package and it works great. I also added custom files in includes argument of html_document.

My question is whether it’s possible to store my custom files (included in includes argument) after a click Knit button. Similarly to self_contained = F option which allows to store all Rmarkdown dependencies.

I should give you some context first. Let’s say I used my html format to create a report two months ago. Two weeks later I decided to implement major changes in my html format and updated my package.

After next two weeks, my boss came to me asking for adding minor changes in old report. Then, by clicking Knit button, the report was not able to create, because there was a new version of my html format, which was significantly different.

I see three possibilities how to deal with this request. Either I can install old version of my package (suboptimal), create a new html format every time I implement major changes or I can store my dependencies (header, footer, css files) in separate subdirectory (like a packrat). Then each report would be independent and immune to changes in my custom format.

Let me know if there is any better solution.

Cross-posted at https://github.com/rstudio/rmarkdown/issues/1212


#2

Here’s a basic example.
Assume that you have a myreport.Rmd file with the following header:

---
title: "Untitled"
author: "Romain Lesur"
date: "27 janvier 2018"
output: 
  html_document:
    includes:
      in_header: inheader.html
---

Using a hacky rmarkdown preprocessor, you can copy inheader.html file.
The following code is intended to be run in R console:

pre_processor <- function(metadata, 
                          input_file, 
                          runtime, 
                          knit_meta, 
                          files_dir, 
                          output_dir) {
  
  in_header <- metadata$output$html_document$includes$in_header
  if (!is.null(in_header)) file.copy(in_header, output_dir)
  
  invisible(NULL)
}

custom_output_format <- function() {
  rmarkdown::output_format(
    knitr = NULL,
    pandoc = NULL,
    pre_processor = pre_processor,
    base_format = rmarkdown::html_document()
  )
}

rmarkdown::render('myreport.Rmd', 
                   output_format = custom_output_format(), 
                   output_dir = 'output')

You get a output directory with the rendered report and inheader.html file inside.

In order to run a similar preprocessor on clicking Knit button, you have to include it in your personal package’s output_format (see below).

Turning in a package
Here is an adaptation of the quarterly_report function of this blogpost:

quarterly_report <- function(toc = TRUE) {

  # get the locations of resource files located within the package
  css <- system.file("reports/styles.css", package = "mypackage")
  header <- system.file("reports/quarterly/header.html", package = "mypackage")

  # call the base html_document function
  base_format <-
    rmarkdown::html_document(toc = toc,
                             fig_width = 6.5,
                             fig_height = 4,
                             theme = NULL,
                             css = css,
                             includes = includes(before_body = header))

  pre_processor <- function(metadata,
                            input_file,
                            runtime,
                            knit_meta,
                            files_dir,
                            output_dir) {
    purrr::walk(c(css, header), file.copy, output_dir)

    invisible(NULL)
  }

  rmarkdown::output_format(
    knitr = NULL,
    pandoc = NULL,
    pre_processor = pre_processor,
    base_format = base_format
  )
}

This solution is not 100% satisfying because I don’t think that rmarkdown pre_processor was created for having side-effects. But it works.