javascriptrjsonshinyshinyjs

How to print only first page calling shinyjs code


Here RShiny print current page it is shown how to print the current page with js code:

I would like to control the number of the page to be print. I want to print only the first page. Usually I do this like here:

enter image description here

But I would like to do this with code. Here is an example:

library(shiny)
library(shinyjs)

jsCode <- 'shinyjs.winprint = function() { window.print(); }'

ui <- fluidPage(
  useShinyjs(),
  
  tags$style(HTML("
  @media print {
    @page {
      size: A4 landscape; 
      margin: 10mm; 
    }
    body * {
      visibility: hidden;
    }
    #printArea, #printArea * {
      visibility: visible;
    }
    #printArea {
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      transform: scale(0.90); 
    }
  }
")),

extendShinyjs(text = jsCode, functions = c("winprint")),

actionButton("print", "PRINT"),

div(id = "printArea",  # start area to print

    tableOutput("table"),
    tableOutput("table1")
) # End of area to print
)

server <- function(input, output, session) {
  
  output$table <- renderTable({
    iris
  })
  
  output$table1 <- renderTable({
    mtcars
  })
  
  observeEvent(input$print, {
    shinyjs::runjs('shinyjs.winprint()')
  })
}

shinyApp(ui, server)

My desired output is this: enter image description here

Further explanation:

Typically, when printing to PDF, there is an option menu that allows you to select the page size, orientation (e.g., portrait or landscape), and other formatting preferences. Among these options is the ability to print specific pages by selecting Print then Pages and finally Custom to specify a range, such as 1:10, or to enter a single page number like 1 manually. Now, I aim to replicate this functionality programmatically. By utilizing the @media print { @page { ... } CSS directive, we can programmatically adjust some of these settings.

So I am wondering if this is possible. Just for your info the very first journey started here Can't take a screenshot from a shiny app with background image to use it as a report, and the reason is that my app has one site only with some buttons, info etc. at the bottom/ footnote, that I don't want to print. Believe me I tried all possible packages like shinyscreenshot, capture and derivates but @Stéphane Laurent 's solution was the only working in my case. But each pdf has a size of 7 MB. Now with the method shown above I can reduce the size to 200 KB. All works well unless the small issue that two sites are printed one desired and one empty. :-)


Solution

  • To achieve printing only the first page programmatically in your Shiny app, you can utilize JavaScript to set up a print range. In your jsCode, you can add a function to specify the page range for printing. Here's how you can modify your code to print only the first page:

    library(shiny)
    library(shinyjs)
    
    jsCode <- '
    shinyjs.winprint = function() { 
      window.print(); 
    }
    
    shinyjs.setPrintRange = function(page) {
      var style = document.createElement("style");
      style.innerHTML = "@media print { @page { size: A4 landscape; margin: 10mm; } body * { visibility: hidden; } #printArea, #printArea * { visibility: visible; } #printArea { position: absolute; left: 0; top: 0; width: 100%; transform: scale(0.90); } @page :first { size: A4 portrait; margin: 10mm; } @page :not(:first) { display: none; } }";
      document.head.appendChild(style);
    }
    '
    
    ui <- fluidPage(
      useShinyjs(),
      
      extendShinyjs(text = jsCode, functions = c("winprint", "setPrintRange")),
      
      actionButton("print", "PRINT"),
      
      div(id = "printArea",  # start area to print
          tableOutput("table"),
          tableOutput("table1")
      ) # End of area to print
    )
    
    server <- function(input, output, session) {
      
      output$table <- renderTable({
        iris
      })
      
      output$table1 <- renderTable({
        mtcars
      })
      
      observeEvent(input$print, {
        shinyjs::runjs('shinyjs.setPrintRange(1); shinyjs.winprint();')
      })
    }
    
    shinyApp(ui, server)

    In this modified code, I've added a new JavaScript function setPrintRange(page) that dynamically injects CSS into the page. This CSS specifies that only the first page will be displayed for printing. Then, in the observeEvent for the print button, I call this function before initiating the printing process using winprint(). This should ensure that only the first page is printed.