如何通过命令行将查看器窗格保存为图像?

24

假设我在查看器窗格中有以下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时,我可以单击“保存为图像”按钮:

输入图像描述

然后我将HTML内容作为PNG文件保存,例如:

输入图像描述

有没有办法通过命令行来实现这一点?我知道关于rstudioapi::savePlotAsImage(),所以我正在寻找一种类似于 saveViewerAsImage 的方法。
编辑:我知道我们可以使用{webshot}包来实现这一点,但我正在寻找RStudio执行此操作的功能。
1个回答

16
这是一个提议。策略如下:
1.让观看者构建png
2.将png从观看者发送到R。
让观看者构建png canvas图像具有一个.toDataURL()方法,返回一个包含以png格式表示的图像的数据URI(我们也可以得到jpeg格式)。
可以使用html2canvas来截屏:该库将当前页面呈现为canvas图像。
因此,可以在观看者中结合这两个功能:
1.使用html2canvas进行截屏。
2.使用.toDataURL()将此截屏转换为png
然而,html2canvas库使用Promise,这在(Windows版本)RStudio查看器中不受支持:需要使用polyfill

将查看器中的png发送到R

可以使用WebSockets来完成此任务。

可以使用httpuv包创建Web服务器。该服务器将提供一个HTML页面,该页面将在RStudio查看器中打开。

httpuv服务器和RStudio查看器之间建立WebSocket通信。

从R命令行可以向RStudio查看器发送WebSocket消息:接收此消息后,查看器会拍摄屏幕截图并将其发送回服务器。

代码

很抱歉,此代码对于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) {
    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:
                      'var ws = new WebSocket("ws://" + location.host);',
                      # 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)

2
我觉得 wsUrl 可以改为 location.host - Yihui Xie
2
@YihuiXie 很好的发现!由于这简化了答案,我已经更新了它。感谢你的赞美和悬赏! - RLesur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接