【问题标题】:How to save Viewer Pane as image through command line?如何通过命令行将查看器窗格保存为图像?
【发布时间】:2018-11-29 11:03:51
【问题描述】:

假设我在查看器窗格中查看了以下 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 函数。

【问题讨论】:

    标签: r rstudio


    【解决方案1】:

    这是一个建议。策略如下:

    1. 让查看器构建png
    2. png从查看器发送到R

    让查看器构建png

    一个canvas image拥有一个.toDataURL() method,它返回一个数据URI,其中包含png格式的图像表示(我们也可以得到jpeg格式)。

    html2canvas library 可用于截屏:此库将当前页面呈现为canvas 图像。

    所以,可以在查看器中组合这两个功能:

    • html2canvas截图
    • 使用.toDataURL()将此屏幕截图转换为png

    但是,html2canvas 库使用(Windows 版本)RStudio 查看器不支持的 JavaScript Promises:需要 polyfill

    png 从查看器发送到 R

    这个任务可以使用WebSockets来完成。

    httpuv 包可用于创建网络服务器。此服务器将提供一个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)
    

    【讨论】:

    • 我觉得wsUrl 可能是location.host
    • @YihuiXie 好收获!由于这简化了答案,我已经更新了它。感谢您的赞美和赏金!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    • 2016-06-30
    • 2011-12-04
    • 2016-10-17
    • 1970-01-01
    相关资源
    最近更新 更多