How to Make PDF Download in Shiny App Response to User Inputs

How to make pdf download in shiny app response to user inputs?

I apologize that it took me this long to get back to this. After looking at what I've done, it turns out it was a little more involved than I remembered.

Here's my example app code

library(shiny)
library(ggplot2)
library(magrittr)

ui <- shinyUI(
fluidPage(
column(
width = 2,
selectInput(
inputId = "x_var",
label = "Select the X-variable",
choices = names(mtcars)
),
selectInput(
inputId = "y_var",
label = "Select the Y-variable",
choices = names(mtcars)
),
selectInput(
inputId = "plot_type",
label = "Select the plot type",
choices = c("scatter plot", "boxplot")
),
downloadButton(
outputId = "downloader",
label = "Download PDF"
)
),
column(
width = 3,
tableOutput("table")
),
column(
width = 7,
plotOutput("plot")
)
)
)

server <- shinyServer(function(input, output, session){

#****************************************
#* Reactive Values

table <- reactive({
mtcars[, c(input[["x_var"]], input[["y_var"]])]
})

plot <- reactive({
p <- ggplot(data = mtcars,
mapping = aes_string(x = input[["x_var"]],
y = input[["y_var"]]))
if (input[["plot_type"]] == "scatter plot")
{
p + geom_point()
}
else
{
p + geom_boxplot()
}
})

#****************************************
#* Output Components

output$table <-
renderTable({
table()
})

output$plot <-
renderPlot({
plot()
})

#****************************************
#* Download Handlers

output$downloader <-
downloadHandler(
"results_from_shiny.pdf",
content =
function(file)
{
rmarkdown::render(
input = "report_file.Rmd",
output_file = "built_report.pdf",
params = list(table = table(),
plot = plot())
)
readBin(con = "built_report.pdf",
what = "raw",
n = file.info("built_report.pdf")[, "size"]) %>%
writeBin(con = file)
}
)
})

shinyApp(ui, server)

And here is my RMD (entitled report_file.Rmd)

---
title: "Parameterized Report for Shiny"
output: pdf_document
params:
table: 'NULL'
plot: 'NULL'
---

```{r}
params[["plot"]]
```

```{r}
params[["table"]]
```

Some highlights to look for

  • Notice the exists of params in the YAML front matter of the RMarkdown script. This allows us to pass in a list of values to be used in the script when we invoke rmarkdown::render(..., params = list(...))
  • I always build my PDF to a dummy file. That way it's easy to find.
  • The reason I always build to a dummy file is that to get the download handler to work, you need to read the bit-content of the PDF and push it to the file argument using writeBin. See my downloadHandler construction.
  • Using the parameterized report means you don't have to recreate your outputs in the rmarkdown script. The work was done in the Shiny app, the parameterized report just helps you send the objects correctly.
    It isn't quite the same as passing files back and forth (although if it could be that easy, I'd love to know it).

Read more about parameterized reports here: http://rmarkdown.rstudio.com/developer_parameterized_reports.html

Shiny app run code to generate a pdf then offer that pdf to user for download

You are using the downloadHandler incorrectly. You do not need an observe()-function here, as shiny provides "reactivity", meaning you can bind objects to ui-elements which are updated whenever the object changes.
Therefore, you only need to assign a downloadHandler to your download-button and tell it how to generate the file you want to provide:

library(shiny)

ui <- fluidPage( # just the download-button and a textInput for the filename
textInput("pdfname", "Filename", "My.pdf"),
downloadButton("outputButton", "Download PDF")
)

server <- function(session, input, output) {
# No need for an observer! Just assign the downloadHandler to the button.
output$outputButton <- downloadHandler(input$pdfname, function(theFile) {
# The first parameter is the name given to the file provided for download to the user.
# The parameter in the function (theFile) is a placeholder for the name that is later
# assigned to the download-file.

# Now you can call your pdf-generating function...
makePdf()

# ... and use file.copy to provide the file "in" the save-button
file.copy(from = "/results/myGenerated.pdf", to = theFile)
})
}

# Sample pdf-generating function:
makePdf <- function(){
pdf(file = "/results/myGenerated.pdf")
plot(cars)
dev.off()
}

shinyApp(ui = ui, server = server)

However, you do not need to store your files first, you might want to directly save them "in" the download button:

library(shiny)

ui <- fluidPage( # As above
textInput("pdfname", "Filename", "My.pdf"),
downloadButton("outputButton", "Download PDF")
)

server <- function(input, output) {
output$outputButton <- downloadHandler(input$pdfname, function(theFile) {
# Here, your pdf-generator is provided with the "filename" that is used
# to provide the file for the user.
makePdf(theFile)
})
}

# Sample pdf-generating function:
makePdf <- function(filename){
pdf(file = filename)
plot(cars)
dev.off()
}

shinyApp(ui = ui, server = server)

Producing PDF report using user input files - Shiny

Problem because in shiny

data_set is reactive so you cant use it like

<<echo = FALSE , message = F, >>=
data_set
@

in your .Rnw

So try

.Rnw

\documentclass{article}
\usepackage{tabularx}

\begin{document}

\begin{table}[H]
\begin{tabularx}
\textbf{Test Name:}& \Sexpr{input$filename} \\
\textbf{Date:}& \\
\end{tabularx}
\end{table}

<<echo = FALSE , message = F, >>=
data_set_1
@

\end{document}

handler

downloadHandler(
filename = reactive({paste0(input$filename,'.pdf')}),

content = function(file) {
data_set_1=data_set()
out = knit2pdf('pdf_shell.Rnw', clean = TRUE)
file.rename(out, file) # move pdf to file for downloading
},

contentType = 'application/pdf'
)

Or may be simple data_set() in .Rnw

Download handler in R shiny does not produce a PDF file (using rmarkdown::render())

Here is a working solution if you have latex installed:

#LIBRARIES
library (shiny)
library(shinydashboard)
library (ggplot2)
library (dplyr)

blues <- c( "#013560", "#0065AD", "#007BD3", "#0091F9", "#9FD4F9", "#EEEEEE")

#sidebar vordefinieren
Codepan <- div(
id = 'sidebar_cr',
actionButton (inputId = "Button", label = "Go!"),
"some intro text and then the download button",
downloadButton("report", "Meine Ergebnisse als PDF speichern")
)

sidebar <-
dashboardSidebar(Codepan)

#Body vordefinieren

body <- dashboardBody(




fluidRow(

box(title = "Deine Mediennutzung",
status= "success", solidHeader = TRUE, height=400,width = 11,
plotOutput(outputId= "PlotM", height= 300))


),



fluidRow(
box(
width = 11, title = "Deine Ergebnisse", solidHeader = TRUE, status = "success",
column(width= 6, plotOutput("plotEAT", height = 250))
))
)

# hier beginnt die eigentliche App:
ui <-
dashboardPage(
dashboardHeader(title = "title", disable = FALSE),
sidebar,
body
)

server <- function(input, output) {



#2 Plot comparison Feedback



MediaCompare3 <- eventReactive(input$Button, {
det <-data.frame(group = c("a-du", "a-ges", "b-sport", "c-age"),
means = c(16, 22, 31, 15)
)





})

plotM <- reactive({
ggplot(MediaCompare3(), aes(x = group, y = means)) +
geom_bar(stat = "identity", fill = "#013560", color = "#013560") +
# scale_x_discrete(labels=c("Du", "Gesamtdurchschnitt",paste("Durchschnitt",Daten[toupper(input$Code), "Sportart_zurueckkodiert"]), paste("Durchschnitt", (Daten[toupper(input$Code), "SP01_01"]-1),"-",(Daten[toupper(input$Code), "SP01_01"]+1), "Jahre" )))+
xlab(NULL) +
ylab("Mediennutzung in Minuten")+
#geom_hline(yintercept = as.numeric(as.character(Daten[toupper(input$Code), "Nutzungsdauer_DM_Gesamt"])), lty = 8, col = "Red")+
geom_text(aes(label = round(means, 0)), vjust =2, colour = "white", size= 8)#+
#geom_label(label="Du", x = 0.5,y = as.numeric(as.character(Daten[toupper(input$Code), "Nutzungsdauer_DM_Gesamt"])), color ="red")
})

output$PlotM <- renderPlot({
plotM()
})








forplotEAT <- eventReactive (input$Button, {
df<- data.frame(Komp = rep(c("Einstellungen zu Essen", "Sozialer Vergleich"), 3),
groupW = c("ADu", "ADu",
"AGesamt", "AGesamt",
"ZGeschlecht","ZGeschlecht"),
valuesW = c (19, 20, 21, 34, 12, 17


))



})

plotEAT <- reactive({
Geschlecht <- "girls"
ggplot(forplotEAT(), aes(x = Komp, y = valuesW, fill = groupW)) +
geom_bar(position = "dodge", stat = "identity",color = "#404040", show.legend = TRUE)+
#scale_fill_discrete(name = "Vergleichsgruppen", labels=c("Du", "Gesamtdurchschnitt", Geschlecht))+
xlab("Gruppe")+
ylab("Ergebnis")+
geom_text(aes(label = round(valuesW, 1)), vjust =2, colour = "white", size= 5, position = position_dodge(width= 0.9))+
scale_fill_manual(values= blues, name = "Vergleichsgruppen", labels=c("Du", "Gesamtdurchschnitt", Geschlecht))#+
#coord_flip()+
#geom_hline(yintercept = 10, lty = 8, col = "Red")
})

output$plotEAT <-renderPlot ({
plotEAT()
})

output$report<-

downloadHandler(
"Result.pdf",
content =
function(file){
rmarkdown::render(
input= "report_file.Rmd",
output_file = "built_report.pdf",
params = list (plotM = plotM(),
plotEAT= plotEAT())
)
readBin (con = "built_report.pdf",
what = "raw",
n = file.info ("built_report.pdf")[, "size"])%>%
writeBin (con = file)
} )









}

shinyApp(ui=ui, server=server)

How to download a PDF file in a Shiny app

Take a look in the downloadHandler function documentation, it has two arguments without default values: filename and content.

filename is basically the name of the file that will be downloaded. It has not to be inside a function. filename = "your-pdf-name.pdf" works as much as defining it inside the argumentless function.

content, in the other hand, creates a tempfile with the content that is going to be downloaded. In most cases you're going to create a file that is going to be fulfilled with something you have created in you app.

How that is not your case, my solution provides something we call "gambiarra" in Brasil: it copies the file you want to download to the tempfile that shiny needs to the downloadHandler works. (I've tried just define it as the path to the file but it doesn't work)

ui <- fluidPage(
downloadLink("downloadData", "Download")
)

server <- function(input, output) {

output$downloadData <- downloadHandler(
filename = "your-pdf-name.pdf",
content = function(file) {
file.copy("www/teste.pdf", file)
}
)
}

shinyApp(ui, server)

Download DT::datatable as pdf in shiny app

You can try the pdf button of the Buttons datatables extension. In this way you don't need a downloadHandler. Otherwise, below is a solution using the good old xtable package. For more sophisticated tables, use kableExtra.

library(shiny)
library(DT)
library(xtable)
library(withr)
library(shinybusy)

ui <- fluidPage(
add_busy_spinner(spin = "cube-grid", onstart = FALSE),
column(
width = 3,
downloadButton(
outputId = "downloader",
label = "Download PDF"
)
),
column(
width = 3,
DTOutput("table")
)

)

server <- function(input, output, session){

table <- reactive({
mtcars
})

#****************************************
#* Output Components

output[["table"]] <- renderDT({
datatable(table())
})

#****************************************
#* Download Handlers

output[["downloader"]] <- downloadHandler(
filename = "results_from_shiny.pdf",
content = function(file){
texfile <- paste0(tools::file_path_sans_ext(file), ".tex")
latex <- print.xtable(
xtable(table()), print.results = FALSE,
floating = FALSE, scalebox = "0.7"
)
writeLines(
c(
"\\documentclass[12pt]{standalone}",
"\\usepackage{graphics}",
"\\usepackage{caption}",
"\\begin{document}",
"\\minipage{\\textwidth}",
latex,
"\\captionof{table}{My caption}",
"\\endminipage",
"\\end{document}"
),
texfile
)
with_dir(
dirname(texfile),
tools::texi2pdf(texfile, clean = TRUE)
)
}
)
}

shinyApp(ui, server)

Export R Shiny Page to PDF

First of all , you should really produce a reproducible example not just a sample of your code. We should copy and paste your code and it will run.

The idea

  1. Since you are using ggplot2 which is king of grid plots, I think one easy option to save plots/tables is to use gridExtra package. Using grid.arrange or arrangeGrobs you can save your grobs to predefined device. Then, downloadhandler will do the download.

  2. To not regenerate all the plots each time, I think one solution is to save them in a global variable that you update each time you change the plot. Here reactiveValues come in rescue to store plots and tables ad dynamic variable.

Solution

ui.R

library(shiny)

shinyUI(fluidPage(

# Application title
titlePanel("Save ggplot plot/table without regenration"),

# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
downloadButton('export')
),

# Show a plot of the generated distribution
mainPanel(
plotOutput("p1"),
plotOutput("p2"),
tableOutput("t1")
)
)
))

server.R

library(shiny)
library(ggplot2)
library(gridExtra)

shinyServer(function(input, output) {
## vals will contain all plot and table grobs
vals <- reactiveValues(p1=NULL,p2=NULL,t1=NULL)

## Note that we store the plot grob before returning it
output$p1 <- renderPlot({
vals$p1 <- qplot(speed, dist, data = cars)
vals$p1
})

output$p2 <- renderPlot({
vals$p2 <- qplot(mpg, wt, data = mtcars, colour = cyl)
vals$p2
})
## same thing for th etable grob
output$t1 <- renderTable({
dx <- head(mtcars)
vals$t1 <- tableGrob(dx)
dx
})
## clicking on the export button will generate a pdf file
## containing all grobs
output$export = downloadHandler(
filename = function() {"plots.pdf"},
content = function(file) {
pdf(file, onefile = TRUE)
grid.arrange(vals$p1,vals$p2,vals$t1)
dev.off()
}
)
})

Upload an image to R shiny downloadable to pdf

The issue is that RenderImage will delete the file after usage unless deleteFile = False. This is the line to change, Remove this:

 re1 <- reactive({gsub("\\\\", "/", input$file1$datapath)})

Add this:

output$uploaded_image <- renderImage({list(src = input$file1$datapath)}, deleteFile = FALSE)


Related Topics



Leave a reply



Submit