Nick
Nick

Reputation: 169

In a shiny app is there a reason downloadHandler() can't render a pdf from rmarkdown::render()?

I have a complicated shinny app, that works perfectly, with the exception of a download button intended to render a pdf from rmarkdown to generate a downloadable report.

In a nut shell downloadHandler() doesn't successfully render pdf from rmarkdown::render(), but Rstudio knits the pdf fine from the .Rmd file, and Rstudio can compile the pdf from the .tex file in temp files without issue.

Simplified code:

My shiny app uses a multi file layout, this minimal code reproducers the problem on my machine with much less complexity than my actual shiny app.

Simple Code that Reproduces the Error

ui.R

#
# This is the user-interface definition of a Shiny web application. You can
# run the application by clicking 'Run App' above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)

# Define UI for application that draws a histogram
shinyUI(fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30),
            downloadButton("Markdown", "Generate report")
        ),

        # Show a plot of the generated distribution
        mainPanel(
            plotOutput("distPlot")
        )
    )
))

server.R

#
# This is the server logic of a Shiny web application. You can run the
# application by clicking 'Run App' above.
#
# Find out more about building applications with Shiny here:
#
#    http://shiny.rstudio.com/
#

library(shiny)

# Define server logic required to draw a histogram
shinyServer(function(input, output) {

    output$distPlot <- renderPlot({

        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')

    })

    #options(tinytex.verbose = TRUE)
    ##---- Markdown ----
    source('06_Save_pdf_report.R', local = TRUE)
    
})

06_Save_pdf_report.R

output$Markdown <- downloadHandler(
    # For PDF output, change this to "report.pdf"
  filename = "report.pdf",
  content = function(file) {
    # Copy the report file to a temporary directory before processing it, in
    # case we don't have write permissions to the current working dir (which
    # can happen when deployed).

    
    tempReport <- file.path(tempdir(), "07_tester.Rmd")
    file.copy("07_tester.Rmd", tempReport, overwrite = TRUE)
    
    # Knit the document, passing in the `params` list, and eval it in a
    # child of the global environment (this isolates the code in the document
    # from the code in this app).
    rmarkdown::render(tempReport, 
                      output_format = "pdf_document",
                      output_file = file
                      )
    }
)

07_tester.Rmd

---
title: "07_tester.Rmd"
author: "CB"
date: "2/10/2021"
output: pdf_document
---

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

## R Markdown

This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see <http://rmarkdown.rstudio.com>.

When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

```{r cars}
summary(cars)
```

## Including Plots

You can also testingwithout plots, for example:

```{r pressure, echo=FALSE}
summary(cars)
```

Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot.

Ok the issue and error

If I knit the 07_tester.Rmd file I get a pdf no problem. When I run the shiny app and click the Generate report button, I see normal rmarkdown activity in the console, at the end a window pops up for where to save a file, of note it does not contain the filename in the 06_Save_pdf_report.R code. After you click save button the following error appears in the console:

Warning: Error in : LaTeX failed to compile C:\Users\USER\AppData\Local\Temp\RtmpQD0jkj\file49b85d6276e8.tex. See https://yihui.org/tinytex/r/#debugging for debugging tips.
  [No stack trace available]

No warnings or errors before that, and the app continues to run without issue after. No file was actually saved or downloaded anywhere.

If I go to the C:\Users\USER\AppData\Local\Temp\RtmpQD0jkj\ and open the file49b85d6276e8.tex file in Rstudio, I can compile it as pdf with no error. From the shiny apps attempts the temp folder also contains a texput.log file and it shows:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020/W32TeX) (preloaded format=pdflatex 2021.2.10)  10 FEB 2021 18:04
entering extended mode
 restricted \write18 enabled.
 %&-line parsing enabled.
**C:/Users/USER/AppData/Local/Temp/RtmpQD0jkj/file49b85d6276e8.tex
! Emergency stop.
<to be read again> 
                   \protect 
<*> C:/Users/USER
                  /AppData/Local/Temp/RtmpQD0jkj/file49b85d6276e8.tex 
Here is how much of TeX's memory you used:
 4 strings out of 481250
 115 string characters out of 5913850
 266128 words of memory out of 5000000
 17047 multiletter control sequences out of 15000+600000
 403430 words of font info for 27 fonts, out of 8000000 for 9000
 14 hyphenation exceptions out of 8191
 3i,0n,0p,1b,6s stack positions out of 5000i,500n,10000p,200000b,80000s

!  ==> Fatal error occurred, no output PDF file produced!

I have searched high and low online and not found anything helpful. I must be doing something simple wrong, any pointing in the right direction would be greatly appreciated.

Upvotes: 2

Views: 1615

Answers (1)

Tyler Rinker
Tyler Rinker

Reputation: 109864

As pointed out in the comments by @RemkoDuursma and then the response from @PatrickBucher:

output_file is not supposed to be used in that way. It's just the intermediate LaTeX file. The location of the PDF file is returned from render. This file just needs to be moved to the place the file parameter is pointing to:

library(rmarkdown)
library(tinytex)
library(shiny)

shinyApp(
    #
    # This is the user-interface definition of a Shiny web application. You can
    # run the application by clicking 'Run App' above.
    #
    # Find out more about building applications with Shiny here:
    #
    #    http://shiny.rstudio.com/
    #
    
    
    
    # Define UI for application that draws a histogram
    ui = shinyUI(fluidPage(
    
        # Application title
        titlePanel("Old Faithful Geyser Data"),
    
        # Sidebar with a slider input for number of bins
        sidebarLayout(
            sidebarPanel(
                sliderInput("bins",
                            "Number of bins:",
                            min = 1,
                            max = 50,
                            value = 30),
                downloadButton("Markdown", "Generate report")
            ),
    
            # Show a plot of the generated distribution
            mainPanel(
                plotOutput("distPlot")
            )
        )
    )),
  server = function(input, output) {
      
    #
    # This is the server logic of a Shiny web application. You can run the
    # application by clicking 'Run App' above.
    #
    # Find out more about building applications with Shiny here:
    #
    #    http://shiny.rstudio.com/
    #
    
    
    # Define server logic required to draw a histogram

    output$distPlot <- renderPlot({

        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')

    })

    #options(tinytex.verbose = TRUE)
    ##---- Markdown ----
    output$Markdown <- downloadHandler(
        # For PDF output, change this to "report.pdf"
      filename = "report.pdf",
      content = function(file) {
        # Copy the report file to a temporary directory before processing it, in
        # case we don't have write permissions to the current working dir (which
        # can happen when deployed).
    
        
        tempReport <- file.path(tempdir(), "07_tester.Rmd")
        file.copy("07_tester.Rmd", tempReport, overwrite = TRUE)
        
        # Knit the document, passing in the `params` list, and eval it in a
        # child of the global environment (this isolates the code in the document
        # from the code in this app).
        output <- rmarkdown::render(
            input = tempReport
        )
        file.copy(output, file) 
    })
    
  }
)

Note I Answered here as well because I had the same problem, arrived here first, and missed the dupe comment for a while. Hoping this saves someone some time.

Upvotes: 3

Related Questions