gadenbuie / xaringanExtra

:ferris_wheel: A playground of enhancements and extensions for xaringan slides.
https://pkg.garrickadenbuie.com/xaringanExtra
Other
448 stars 36 forks source link

using decktape or pagedown::chrome_page with panelset #67

Closed yonicd closed 3 years ago

yonicd commented 3 years ago

Any ideas how to get decktape.js or chrome_page to convert a slide deck with panelsets to a pdf that can toggle through the panel sets as new pages in the pdf?

The main problem is that the slides get toggled through with a --key=ArrowRight instead of ArrowDown when using the generic command.

When I use react command it doesnt toggle through the slides with a key press but it counts the slides up front, as does chrome_page.

gadenbuie commented 3 years ago

Panelsets definitely don't print nicely. It's a little complicated since the panels only exist within one slide, whereas slide continuations are technically completely separate slides.

I have some vague ideas about how I might be able to create a "print mode" for the panelsets and I'm tracking progress to that end in #45.

yonicd commented 3 years ago

Not sure why tapedeck generic screws up fonts, it is actually moving through the panelset correctly

gadenbuie commented 3 years ago

Oh yeah, I don't use decktape often so I missed the fact that it can move through slides using a keypress action. Panelset modifies the typical remarkjs behavior and listens to ArrowRight events to move through panels, whereas ArrowDown will move through slides. Sounds like decktape could be very useful!

cderv commented 3 years ago

I don't think this is an issue with the printing tool. In case of chrome, as I said in https://github.com/rstudio/pagedown/issues/192#issuecomment-715180643, it just uses the Pages.printToPDF method of Chrome. I don't think it "navigates" the slide.

However, I think there are some special CSS rules for printed output like @media print (https://developer.mozilla.org/en-US/docs/Web/CSS/@media). I don't know if this is possible, but maybe something can be done to have some CSS that would position nicely the output of panelset. Maybe this requires some JS to... (https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeprint)

Anyway just some ideas I throw here just like that 😄

gadenbuie commented 3 years ago

I'm not sure if pagedown's chrome_print() would be the best place to start, but after thinking about this for a bit I wonder if we could use pagedown or chromote to recreate the method used by decktape.js.

In essence, rather than printing using the print CSS media, we'd keep screen emulation and print each slide, navigating between slides by sending an ArrowRight keypress event. Then, depending on implementation, we'd staple the PDFs together into the final PDF of all of the slides.

cderv commented 3 years ago

I wonder if we could use pagedown or chromote to recreate the method used by decktape.js.

It would be crrri (on github only) or chromote. Not pagedown IMO. it is not made for that compare to the other packages.

And yes, I think that would be possible. You can control the browser with those packages.

yonicd commented 3 years ago

@gadenbuie wouldnt it be ArrowRight?

yonicd commented 3 years ago

@cderv i got the basics down of navigating, but how do i collect each page to make it into a pdf? i guess i can save each page screenshot as a png or something and then convert to pdf /shrug

Also how do i know how many right arrow hits to move and how do i run the script on a loop through them?

# Helpers -----------------------------------------------------------------

# Press the 'Enter' key
press_enter_key <- function(client) {
  dispatch_enter_key_event <- function(client, type) {
    client$Input$dispatchKeyEvent(
      type = type, 
      windowsVirtualKeyCode = 13, 
      code = "Enter", 
      key = "Enter", 
      text = "\r", 
      unmodifiedText = "\r"
    )
  }

  promises::then(
    dispatch_enter_key_event(client, "keyDown"),
    ~ dispatch_enter_key_event(client, "keyUp")
  )
}

press_rightarrow_key <- function(client) {
  dispatch_enter_key_event <- function(client, type) {
    client$Input$dispatchKeyEvent(
      type = type, 
      windowsVirtualKeyCode = 39, 
      code = "ArrowRight", 
      key = "ArrowRight", 
      text = "", 
      unmodifiedText = ""
    )
  }

  promises::then(
    dispatch_enter_key_event(client, "keyDown"),
    ~ dispatch_enter_key_event(client, "keyUp")
  )
}

press_downarrow_key <- function(client) {
  dispatch_enter_key_event <- function(client, type) {
    client$Input$dispatchKeyEvent(
      type = type, 
      windowsVirtualKeyCode = 40, 
      code = "ArrowDown", 
      key = "ArrowDown", 
      text = "", 
      unmodifiedText = ""
    )
  }

  promises::then(
    dispatch_enter_key_event(client, "keyDown"),
    ~ dispatch_enter_key_event(client, "keyUp")
  )
}

# Main script -------------------------------------------------------------
library(crrri)
url <- 'https://pkg.garrickadenbuie.com/xaringanExtra/panelset'
chrome <- Chrome$new(bin = find_chrome_binary())

client <- chrome$connect(callback = function(client) {
  client$inspect()
})

Page <- client$Page
Input <- client$Input

Page$enable() %...>% {
  Page$navigate(url = url)
  Page$loadEventFired() # await the load event
} %...>% {
  press_rightarrow_key(client)
} %...>% {
  press_downarrow_key(client)
}

chrome$close()
gadenbuie commented 3 years ago

I also fiddled with this a bit and have a good process in place, but unfortunately it requires taking screenshots rather than printing to PDF.

The primary issue is that remarkjs includes CSS @media print rules that need to be ignored. I've explored two paths without success:

  1. Trying to get the Page.printToPDF method to ignore the print media rules.

  2. Using JavaScript to delete the @media print rules. This doesn't work because of Chrome's cross-site origin policy that won't let JavaScript modify CSS from other domains. I've tried launching chrome with --disable-web-security but that also didn't work.

Both of the above are techniques used by decktape.js via puppeteer. Maybe the API is different? It's possible that decktape.js bundles an older version of headless chrome to avoid the same origin policy problem. 🤷‍♂️

If you have any advice, @cderv or @yonicd, I'd appreciate it!

gadenbuie commented 3 years ago

It took me a while to work out the final details, but I've come up a with a solution that uses headless chrome's print method to print each slide to PDF (and then staple them together with pdftools). https://www.garrickadenbuie.com/blog/print-xaringan-chromote/

cderv commented 3 years ago

Nice ! Well done!

chromote or crrri are not on CRAN so hard to have this function live in a CRAN package.

But we could do the same trick as in pagedown and use neither to put it inside xaringan maybe ? it is just a pain to maintain without using any of this package.

So the function living in a file to source is also a nice idea.

gadenbuie commented 3 years ago

Thanks @cderv! I also don't feel a ton of pressure to incorporate this into a package because I think most users who use mostly the standard set of xaringan features will be fine using decktape or pagedown::chrome_print().

yonicd commented 3 years ago

most users :)