Как сохранить панель просмотра как изображение через командную строку?

Скажем, у меня есть следующий HTML-код, просматриваемый в панели просмотра

tempDir <- tempfile()
dir.create(tempDir)
htmlFile <- file.path(tempDir, "index.html")
write('<h1> Content</h1>', htmlFile, append = TRUE)
write('<h2> Content</h2>', htmlFile, append = TRUE)
write('lorem ipsum...', htmlFile, append = TRUE)
viewer <- getOption("viewer")
viewer(htmlFile)

Когда у меня есть этот html в панели просмотра, я могу нажать кнопку "Сохранить как изображение":

enter image description here

И у меня есть html-контент как png, например:

enter image description here

Есть ли способ сделать это с помощью командной строки? Я знаю о rstudioapi::savePlotAsImage(), поэтому я ищу своего рода saveViewerAsImage.

Edit: Я знаю, что мы можем сделать это с пакетом {webshot}, но я ищу функцию RStudio, которая это делает.

Ответ 1

Вот предложение. Стратегия такова:

  1. пусть зритель построит png
  2. отправьте png от зрителя до R

Пусть зритель построит png

Изображение canvas имеет метод .toDataURL() который возвращает URI данных, содержащий представление изображения в формате png (мы также можем получить формат jpeg).

Библиотека html2canvas может быть использована для снятия скриншота: эта библиотека отображает текущую страницу как изображение в canvas.

Таким образом, можно комбинировать эти две функции в средстве просмотра:

  • сделать скриншот с html2canvas
  • преобразуйте этот снимок экрана в png используя .toDataURL()

Однако в библиотеке html2canvas используется JavaScript Promise, которые не поддерживаются программой просмотра RStudio (Windows version): требуется полипол.

Отправьте png от зрителя до R

Эта задача может быть достигнута с помощью WebSockets.

Пакет httpuv можно использовать для создания веб-сервера. Этот сервер будет обслуживать HTML страницу, которая будет открыта в средстве просмотра RStudio.

Связь WebSocket устанавливается между сервером httpuv и средством просмотра RStudio.

Из командной строки R вы можете отправить сообщение WebSocket в средство просмотра RStudio: получая это сообщение, зритель берет скриншот и отправляет его обратно на сервер.

Код

Извините, этот код довольно длинный для ответа SO.

library(httpuv)

# Initialize variables
png <- NULL
websocket <- NULL

# Download Javascript libraries
polyfill_promise <- readLines('https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.auto.min.js')
html2canvas <- readLines('https://html2canvas.hertzen.com/dist/html2canvas.min.js')

# Configure the httpuv server
app <- list(
  call = function(req) {
    wsUrl = paste(sep='',
                  '"',
                  "ws://",
                  ifelse(is.null(req$HTTP_HOST), req$SERVER_NAME, req$HTTP_HOST),
                  '"')

    list(
      status = 200L,
      headers = list(
        'Content-Type' = 'text/html'
      ),
      body = paste0(collapse = "\r\n",
        c("<!DOCTYPE html>",
          "<html>",
          "<head>",
          # polyfill the RStudio viewer to support JavaScript promises
          '<script type="text/javascript">',
          polyfill_promise,
          "</script>",
          # use html2canvas library
          '<script type="text/javascript">',
          html2canvas,
          "</script>",
          "</head>",
          "<body>",
          html_body,
          "</body>",
          '<script type="text/javascript">',
          # Configure the client-side websocket connection:
          sprintf("var ws = new WebSocket(%s);", wsUrl),
          # When a websocket message is received:
          "ws.onmessage = function(event) {",
            # Take a screenshot of the HTML body element
          "  html2canvas(document.body).then(function(canvas) {",
            # Transform it to png
          "    var dataURL = canvas.toDataURL();",
            # Send it back to the server
          "    ws.send(dataURL);",
          "  });",
          "};",
          "</script>",
          "</html>"
        )
      )
    )
  },
  # Configure the server-side websocket connection
  onWSOpen = function(ws) {
    # because we need to send websocket message from the R command line:
    websocket <<- ws
    # when a websocket message is received from the client
    ws$onMessage(function(binary, message) {
      png <<- message
    })
  }
)

# From your question:
html_body <- c(
  '<h1> Content</h1>', 
  '<h2> Content</h2>', 
  'lorem ipsum...'
)

# Start the server:
server <- startDaemonizedServer("0.0.0.0", 9454, app)

# Open the RStudio viewer:
rstudioapi::viewer("http://localhost:9454")
# Wait to see the result...

# Send a websocket message from the command line:
websocket$send("go") # send any message

# Write the png image to disk:
writeBin(
  RCurl::base64Decode(
    gsub("data:image/png;base64,", "", png), 
    "raw"
  ), 
  "screenshot.png"
)

# Close the websocket connection
websocket$close()

# Stop the server
stopDaemonizedServer(server)