Reputation: 1950
I've written a function inside a package that will take some data the user provides and make a pretty little Word document with a graph of those data. However, while the function works great when the working directory is a local drive, it fails when the drive is anything else. The errors are:
Quitting from lines 6-9 (skeleton.Rmd) Error in png(..., res = dpi, units = "in") : unable to start png() device In addition: Warning messages: 1: In dir.create(dirname(name), recursive = TRUE) : cannot create dir '\myemployer.com\data\sites', reason 'Permission denied' 2: In png(..., res = dpi, units = "in") : unable to open file '\myemployer.com\data\sites\myworkingdirectory/figure-docx/myplot-1.png' for writing 3: In png(..., res = dpi, units = "in") : opening device failed
I do have permission to write to my current working directory in general. In fact, when I call on a similar function in the same package that makes a table instead of a graph, it works fine.
I think what's going on is that Rmarkdown or knitr needs to temporarily save a png file of the graph in order to place it in the Word file, and it's saving it somewhere it doesn't have permission to do so. Can I get around this behavior by specifying something with rmarkdown::render?
I don't know how exactly to make a reproducible example when I'm referring to multiple files and the file structure of my package, but here's my best attempt at what info is needed. Here's the an example of the function:
myFun <- function(mydata, filename){
G <- ggplot2::ggplot(mydata, ggplot2::aes(x = A, y = B)) +
ggplot2::geom_point()
OutPath <- dirname(filename)
FileName <- basename(filename)
rmarkdown::render(system.file("rmarkdown/templates/myplot/skeleton/skeleton.Rmd",
package="mypackage"), output_dir = OutPath,
output_file = FileName, quiet = TRUE)
}
The file structure to get to skeleton.Rmd from the top level of my package: mypackage/inst/rmarkdown/templates/myplot/skeleton/skeleton.Rmd
The actual R markdown document is something like this:
---
title: "My Plot"
output: word_document
---
```{r}
G
```
and the error message "Quitting from lines 6-9" refers to the chunk where G is outputted.
To call on this, you would use:
myFun(mydata = data.frame(A = 1:10, B = 1:10),
filename = "my file.docx")
and, if it's working, you'd get a Word file in the current working directory called "my file.docx" that contains a graph.
Is there some way to work around rmarkdown's need to temporarily save this png file? Can I set the directory for this temporary file to something else where it would have write permissions?
Upvotes: 4
Views: 381
Reputation: 41260
A possible workaround would be to render the Markdown in a temporary directory, and to copy the generated file to its destination, see:
myFun <- function(mydata, filename){
G <- ggplot2::ggplot(mydata, ggplot2::aes(x = A, y = B)) +
ggplot2::geom_point()
tmpdir <- tempdir()
on.exit(unlink(tmpdir))
OutPath <- dirname(filename)
FileName <- basename(filename)
rmarkdown::render(system.file("rmarkdown/templates/myplot/skeleton/skeleton.Rmd",
package="mypackage"), output_dir = tmpdir,
output_file = FileName, quiet = TRUE)
file.copy(file.path(tmpdir,FileName,OutPath, overwrite = T))
}
Upvotes: 3
Reputation: 4708
Following on from the comments, this may help. You could make the plot within the rmd
file:
---
title: "Untitled"
date: "6/27/2022"
output: html_document
params:
input_df: NULL
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
mydata_rmarkdown <- params$input_df
```
## plot
```{r pressure, echo = FALSE}
G <- ggplot2::ggplot(mydata_rmarkdown, ggplot2::aes(x = A, y = B)) +
ggplot2::geom_point()
G
```
And alter you function to something like:
myFun <- function(mydata, filename){
OutPath <- dirname(filename)
FileName <- basename(filename)
rmarkdown::render(system.file("rmarkdown/templates/myplot/skeleton/skeleton.Rmd",
package = "mypackage"), output_dir = OutPath,
params = list(input_df = mydata)
output_file = FileName, quiet = TRUE)
}
Calling the function:
df1 <- data.frame(A = 1:10, B = 1:10)
df2 <- data.frame(A = 10:20, B = 10:20)
myFun(mydata = df1, filename = "my file.docx")
myFun(mydata = df2, filename = "my file.docx")
Its pretty limited in its current form as you would need df1
and df2
to have the same variable names etc. but may help you along.
Upvotes: 1