Capturing wild widgets with webshot

March 4, 2016

(This article was first published on R –, and kindly contributed to R-bloggers)

NOTE: you won’t need to use this function if you use the development version of knitr

Winston Chang released his webshot package to CRAN this past week. The package wraps the immensely useful phantomjs utility and makes it dirt simple to capture whole or partial web pages in R. One beautiful bonus feature of webshot is that you can install phamtomjs with it (getting phantomjs to work on Windows is a pain).

You can do many things with the webshot package but I hastily drafted this post to put forth a means to generate a static image from an htmlwidget. I won’t elaborate much since I included a fully roxygen-doc’d function below, but the essence of capture_widget() is to pass in an htmlwidget object and have it rendered for you to a png file and get back either:

  • a file system path reference (e.g. /path/to/widget.png)
  • a markdown image reference (e.g. ![](file:///path/to/widget.png))
  • an html image reference (e.g. <img src='file:///path/to/widget.png'/>), or
  • an inline base64 encoded HTML imgage reference (e.g. <img src='data:image/png;base64,iVBORw...'/>)

which you can then use in R markdown documents knitted to PDF (or in any other context).

Take a look at the function, poke the tyres and drop suggestions in the comments. I’ll add this to one of my widgets soon so folks can submit complaints or enhancements via issues & PRs on github).

To use the function, just pipe a sized widget to it and use the output from it.

#' Capture a static (png) version of a widget (e.g. for use in a PDF knitr document)
#' Widgets are generally interactive beasts rendered in an HTML DOM with
#' javascript. That makes them unusable in PDF documents. However, many widgets
#' initial views would work well as static images. This function renders a widget
#' to a file and make it usable in a number of contexts.
#' What is returned depends on the value of code{output}. By default (code{"path"}),
#' the full disk path will be returned. If code{markdown} is specified, a markdown
#' string will be returned with a code{file:///...} URL. If code{html} is
#' specified, an code{<img src='file:///...'/>} tag will be returned and if
#' code{inline} is specified, a base64 encoded code{<img>} tag will be returned
#' (just like you'd see in a self-contained HTML file from code{knitr}).
#' @importFrom webshot webshot
#' @importFrom base64 img
#' @param wdgt htmlwidget to capture
#' @param output how to return the results of the capture (see Details section)
#' @param height,width it's important for many widget to be responsive in HTML
#'        documents. PDFs are static beasts and having a fixed image size works
#'        better for them. code{height} & code{width} will be passed into the
#'        rendering process, which means you should probably specify similar
#'        values in your widget creation process so the captured code{<div>}
#'        size matches the size you specify here.
#' @param png_render_path by default, this will be a temporary file location but
#'        a fully qualified filename (with extension) can be specified. It's up to
#'        the caller to free the storage when finished with the resource.
#' @return See Details
#' @export
capture_widget <- function(wdgt,
                           output=c("path", "markdown", "html", "inline"),
                           height, width,
                           png_render_path=tempfile(fileext=".png")) {
  wdgt_html_tf <- tempfile(fileext=".html")
  htmlwidgets::saveWidget(vl, wdgt_html_tf)
  webshot::webshot(url=sprintf("file://%s", wdgt_html_tf),
                   vwidth=width, vheight=height)
  # done with HTML
  switch(match.arg(output, c("path", "markdown", "html", "inline")),
         `markdown`=sprintf("![widget](file://%s)", png_render_path),
             `html`=sprintf("<img src='file://%s'/>", png_render_path),

To leave a comment for the author, please follow the link and comment on their blog: R – offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...

If you got this far, why not subscribe for updates from the site? Choose your flavor: e-mail, twitter, RSS, or facebook...

Comments are closed.


Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)