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
Since you are using
ggplot2
which is king ofgrid
plots, I think one easy option to save plots/tables is to usegridExtra
package. Usinggrid.arrange
orarrangeGrobs
you can save your grobs to predefined device. Then, downloadhandler will do the download.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()
}
)
})
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 invokermarkdown::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 usingwriteBin
. See mydownloadHandler
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
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)
screenshot the whole shinydashboard page as pdf by hitting a button
tada
If we can do things all in the browser, why do we need the Shiny server? pure javascript, 0 line of server code added.
# app.R ##
library(shiny)
library(shinydashboard)
library(DT)
dbHeader <- dashboardHeader(
title = "fr"
)
ui <- dashboardPage(
dbHeader,
dashboardSidebar(),
dashboardBody(
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"),
tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.4.0/jspdf.umd.min.js"),
tags$script(HTML(
'
window.jsPDF = window.jspdf.jsPDF;
function capture(){
domtoimage.toPng(document.querySelector("body"))
.then(function(dataurl) {
var orientation = window.innerHeight >= window.innerWidth ? "portrait" : "landscape";
const doc = new jspdf.jsPDF({unit: "px", orientation: orientation, format: [window.innerHeight, window.innerWidth]});
doc.addImage(dataurl, "PNG", 0, 0, window.innerWidth, window.innerHeight);
doc.save("page.pdf");
});
}
'
)),
tags$hr(),
actionButton("generate", "Generate PDF", onclick="capture()"),
tabsetPanel(
id ="tabA",
type = "tabs",
tabPanel("Front",icon = icon("accusoft"),
plotOutput("ir")
),
tabPanel("Data", icon = icon("table"),
dataTableOutput("iris")
)
)
)
)
server <- function(input, output) {
output$ir<-renderPlot(
plot(iris)
)
output$iris<-renderDataTable(
iris
)
}
shinyApp(ui = ui, server = server)
Shiny to output a function that generates a pdf file itself
Thanks so much for the help from JackStat and Malanche. The following works for downloading the result!
library(shiny)
runApp(list(
#Load the exmaple from the msa package.
mySequenceFile <- system.file("examples", "exampleAA.fasta", package="msa"),
mySequences <- readAAStringSet(mySequenceFile),
myFirstAlignment <- msa(mySequences),
# A simple shiny app.
# Is it possible to see the generated pdf file on screen?
ui = fluidPage(downloadButton('downloadPDF')),
server = function(input, output) {
output$downloadPDF = downloadHandler(
filename = 'myreport.pdf',
content = function(file) {
msaPrettyPrint(
myFirstAlignment
, file = 'myreport.pdf'
, output="pdf"
, showNames="left"
, showLogo="top"
, consensusColor="BlueRed"
, logoColors="accessible area"
, askForOverwrite=FALSE)
file.rename("myreport.pdf", file) # move pdf to file for downloading
},
contentType = 'application/pdf'
)
}
))
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)
Related Topics
R Column Check If Contains Value from Another Column
Evaluate (I.E., Predict) a Smoothing Spline Outside R
How to Change the Default Font Size in Ggplot2
Clustering List for Hclust Function
Add Author Affiliation in R Markdown Beamer Presentation
How to Check If a Sequence of Numbers Is Monotonically Increasing (Or Decreasing)
Dplyr - Mean for Multiple Columns
Change Facet Label Text and Background Colour
Multinomial Logit in R: Mlogit Versus Nnet
Ggplot2: How to Remove Slash from Geom_Density Legend
R Aggregate Data in One Column Based on 2 Other Columns
Bookmarking and Saving the Bookmarks in R Shiny
Effectively Debugging Shiny Apps
How to Get Rstudio to Automatically Compile R Markdown Vignettes
Loop Over Rows of Dataframe Applying Function with If-Statement
Stl Decomposition of Time Series with Missing Values for Anomaly Detection