output plot from knitr custom engine chunk

Viewed 120

I've created a custom chunk engine for knitr with knitr::knit_engines$set() that takes some YAML specifications, interprets them, and creates a plot as output. If the knitr document is rendered to HTML I have it create shiny output which I convert to HTML with htmltools::knit_print.shiny.tag() and that works fine. However, if I render to pdf/docx/odf I create a static plot object instead of a shiny output. That bit is fine, but all I can get it to render is a text representation of the plot (ie. a list object), rather than an image of the plot.

I need to somehow pass an image (I assume) to the output of knitr::knit_engines$set(), but if I try saving the plot to a png and then passing that as output, I just get the text representation of the file path instead.

knitr::knit_engines$set(example = function(options) {
  #### read options$code and do stuff - works fine
  p <- ggplot(data = mpg) + geom_jitter(aes(x = cty, y = hwy))

  ##### output results
  # Shiny - outputs html text. Works fine
  if(knitr::opts_knit$get("rmarkdown.pandoc.to") == "html"){
    shiny::plotOutput(p) %>%
      htmltools::knit_print.shiny.tag()

  # Static output formats (pdf, etc)
  } else {
    #### attempt 1 ####
    p # returns list object
  
    #### attempt 2 ####
    ggsave(filename = here("temp", "out.png"), plot = p)
    knitr::include_graphics(here("temp", "out.png")) # Returns here("temp", "out.png")
  }
})

I've looked through the knitr/R/engine.R on Github for inspiration, but have just gotten lost instead.

1 Answers

The reason why this "fails" is that knitr::include_graphics() works this way: it returns a structure containing the path (and some additional arguments) which, in the case of PDF/LaTeX output, results in just the path being printed.

Instead, you need to pass the structure the output hook via knitr::engine_output:

imgpath <- here("temp", "out.pdf")
ggsave(plot = p,
       filename = imgpath, 
       device = "pdf", 
       width = 15, height = 8, units = "cm")
knitr::engine_output(
      options, 
      out = list(knitr::include_graphics(imgpath))
      )

(Note that I save the image as PDF which I find easier to handle than raster images.)

```{example}
This is your example engine chunk.
```

enter image description here

Related