From b5cce195c83328063a53faf2d4c7e1f0293732e7 Mon Sep 17 00:00:00 2001 From: "Thomas J. Leeper" Date: Wed, 22 Aug 2018 11:22:06 -0500 Subject: [PATCH 1/2] use magick package for resize() --- DESCRIPTION | 8 +++---- NAMESPACE | 1 + NEWS.md | 2 ++ R/image.R | 40 ++++--------------------------- R/utils.R | 43 ---------------------------------- README.Rmd | 2 +- README.md | 57 +++++++++++++++++++++++++++++++-------------- man/resize.Rd | 3 +-- vignettes/intro.Rmd | 2 +- 9 files changed, 54 insertions(+), 104 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 54a5490..f1595ae 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,6 +6,7 @@ Authors@R: c( person("Yihui", "Xie", role = "ctb"), person("Francois", "Guillem", role = "ctb"), person("Barret", "Schloerke", role = "ctb"), + person("Thomas", "Leeper", role = "ctb"), person("Nicolas", "Perriault", role = "ctb", comment = "The CasperJS library") ) Description: Takes screenshots of web pages, including Shiny applications and R @@ -15,6 +16,7 @@ Depends: Imports: magrittr, jsonlite, + magick, callr Suggests: httpuv, @@ -23,9 +25,7 @@ Suggests: shiny VignetteBuilder: knitr License: GPL-2 -SystemRequirements: PhantomJS (http://phantomjs.org) for taking screenshots, - ImageMagick (http://www.imagemagick.org) or GraphicsMagick - (http://www.graphicsmagick.org) and OptiPNG (http://optipng.sourceforge.net) - for manipulating images. +SystemRequirements: PhantomJS (http://phantomjs.org) for taking screenshots + and OptiPNG (http://optipng.sourceforge.net) for manipulating images. RoxygenNote: 6.0.1 URL: https://github.com/wch/webshot/ diff --git a/NAMESPACE b/NAMESPACE index c2f6351..cfd26f4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,4 +10,5 @@ export(resize) export(rmdshot) export(shrink) export(webshot) +import(magick) importFrom(magrittr,"%>%") diff --git a/NEWS.md b/NEWS.md index 4bc02a4..64a2f26 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,8 @@ webshot 0.5.0.9000 * Added `appshot.shiny.appobj` functionality (schloerke, [#55](https://github.com/wch/webshot/pull/55)) +* Removed system dependencies on GraphicsMagick/ImageMagick by using the **magick** package in `resize()`. + webshot 0.5.0 ============= diff --git a/R/image.R b/R/image.R index ef3e329..2b4bfb0 100644 --- a/R/image.R +++ b/R/image.R @@ -1,8 +1,7 @@ #' Resize an image #' #' This does not change size of the image in pixels, nor does it affect -#' appearance -- it is lossless compression. This requires GraphicsMagick -#' (recommended) or ImageMagick to be installed. +#' appearance -- it is lossless compression. #' #' @param filename Character vector containing the path of images to resize. #' @param geometry Scaling specification. Can be a percent, as in \code{"50\%"}, @@ -21,6 +20,7 @@ #' webshot("https://www.r-project.org/", "r-small-2.png") %>% #' resize("400x") #' } +#' @import magick #' @export resize <- function(filename, geometry) { mapply(resize_one, filename = filename, geometry = geometry, @@ -29,39 +29,9 @@ resize <- function(filename, geometry) { } resize_one <- function(filename, geometry) { - # Handle missing phantomjs - if (is.null(filename)) return(NULL) - - # First look for graphicsmagick, then imagemagick - prog <- Sys.which("gm") - - if (prog == "") { - # ImageMagick 7 has a "magick" binary - prog <- Sys.which("magick") - } - - if (prog == "") { - if (is_windows()) { - prog <- find_magic() - } else { - prog <- Sys.which("convert") - } - } - - if (prog == "") - stop("None of `gm`, `magick`, or `convert` were found in path. GraphicsMagick or ImageMagick must be installed and in path.") - - args <- c(filename, "-resize", geometry, filename) - - if (names(prog) %in% c("gm", "magick")) { - args <- c("convert", args) - } - - res <- system2(prog, args) - - if (res != 0) - stop ("Resizing with `gm convert`, `magick convert` or `convert` failed.") - + img <- image_read(filename) + img_out <- image_resize(img, geometry) + image_write(img_out, filename) filename } diff --git a/R/utils.R b/R/utils.R index 2bac9eb..cc67c1f 100644 --- a/R/utils.R +++ b/R/utils.R @@ -291,46 +291,3 @@ fix_windows_url <- function(url) { vapply(url, fix_one, character(1), USE.NAMES = FALSE) } - - -# Borrowed from animation package, with some adaptations. -find_magic = function() { - # try to look for ImageMagick in the Windows Registry Hive, the Program Files - # directory and the LyX installation - if (!inherits(try({ - magick.path = utils::readRegistry('SOFTWARE\\ImageMagick\\Current')$BinPath - }, silent = TRUE), 'try-error')) { - if (nzchar(magick.path)) { - convert = normalizePath(file.path(magick.path, 'convert.exe'), "/", mustWork = FALSE) - } - } else if ( - nzchar(prog <- Sys.getenv('ProgramFiles')) && - length(magick.dir <- list.files(prog, '^ImageMagick.*')) && - length(magick.path <- list.files(file.path(prog, magick.dir), pattern = '^convert\\.exe$', - full.names = TRUE, recursive = TRUE)) - ) { - convert = normalizePath(magick.path[1], "/", mustWork = FALSE) - } else if (!inherits(try({ - magick.path = utils::readRegistry('LyX.Document\\Shell\\open\\command', 'HCR') - }, silent = TRUE), 'try-error')) { - convert = file.path(dirname(gsub('(^\"|\" \"%1\"$)', '', magick.path[[1]])), c('..', '../etc'), - 'imagemagick', 'convert.exe') - convert = convert[file.exists(convert)] - if (length(convert)) { - convert = normalizePath(convert, "/", mustWork = FALSE) - } else { - warning('No way to find ImageMagick!') - return("") - } - } else { - warning('ImageMagick not installed yet!') - return("") - } - - if (!file.exists(convert)) { - # Found an ImageMagick installation, but not the convert.exe binary. - warning("ImageMagick's convert.exe not found at ", convert) - return("") - } - return(convert) -} diff --git a/README.Rmd b/README.Rmd index b1c68bf..95963e1 100644 --- a/README.Rmd +++ b/README.Rmd @@ -116,7 +116,7 @@ rmdshot("document.rmd", "document.png") ### Manipulating images -If you have GraphicsMagick (recommended) or ImageMagick installed, you can pass the result to `resize()` to resize the image after taking the screenshot. This can take any valid ImageMagick geometry specifictaion, like `"75%"`, or `"400x"` (for an image 400 pixels wide). However, you may get different (and often better) results by using the `zoom` option: the fonts and graphical elements will render more sharply. However, compared to simply resizing, zooming out may result in slightly different positioning of text and layout elements. +You can pass the result to `resize()` to resize the image after taking the screenshot. This can take any valid ImageMagick geometry specifictaion, like `"75%"`, or `"400x"` (for an image 400 pixels wide). However, you may get different (and often better) results by using the `zoom` option: the fonts and graphical elements will render more sharply. However, compared to simply resizing, zooming out may result in slightly different positioning of text and layout elements. You can also call `shrink()`, which runs [OptiPNG](http://optipng.sourceforge.net/) to shrink the PNG file losslessly. diff --git a/README.md b/README.md index 9c23ec9..6acc6c3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +--- +output: github_document +--- + webshot ======= @@ -6,25 +10,26 @@ webshot **Webshot** makes it easy to take screenshots of web pages from R. It can also: -- Run Shiny applications locally and take screenshots of the application. -- Render R Markdown documents and take screenshots of the document. Webshot can handle both static Rmd documents and interactive ones (those with `runtime: shiny`). +* Run Shiny applications locally and take screenshots of the application. +* Render R Markdown documents and take screenshots of the document. Webshot can handle both static Rmd documents and interactive ones (those with `runtime: shiny`). -Installation ------------- +## Installation Webshot can be installed from CRAN. Webshot also requires the external program [PhantomJS](http://phantomjs.org/). You may either download PhantomJS from its website, or use the function `webshot::install_phantomjs()` to install it automatically. -``` r + +```r install.packages("webshot") webshot::install_phantomjs() ``` -Usage ------ + +## Usage By default, `webshot` will use a 992x744 pixel viewport (a virtual browser window) and take a screenshot of the entire page, even the portion outside the viewport. -``` r + +```r library(webshot) webshot("https://www.r-project.org/", "r.png") webshot("https://www.r-project.org/", "r.pdf") # Can also output to PDF @@ -32,26 +37,30 @@ webshot("https://www.r-project.org/", "r.pdf") # Can also output to PDF You can clip it to just the viewport region: -``` r + +```r webshot("https://www.r-project.org/", "r-viewport.png", cliprect = "viewport") ``` You can also get screenshots of a portion of a web page using CSS selectors. If there are multiple matches for the CSS selector, it will use the first match. -``` r + +```r webshot("https://www.r-project.org/", "r-sidebar.png", selector = ".sidebar") ``` If you supply multiple CSS selectors, it will take a screenshot containing all of the selected items. -``` r + +```r webshot("https://www.r-project.org/", "r-selectors.png", selector = c("#getting-started", "#news")) ``` The clipping rectangle can be expanded to capture some area outside the selected items: -``` r + +```r webshot("https://www.r-project.org/", "r-expand.png", selector = "#getting-started", expand = c(40, 20, 40, 20)) @@ -59,7 +68,8 @@ webshot("https://www.r-project.org/", "r-expand.png", You can take higher-resolution screenshots with the `zoom` option. This isn't exactly the same as taking a screenshot with a HiDPI ("Retina") device: it is like increasing the zoom to 200% in a desktop browser and doubling the height and width of the browser window. This differs from using a HiDPI device because some web pages load different, higher-resolution images when they know they will be displayed on a HiDPI device (but using zoom will not report that there is a HiDPI device). -``` r + +```r webshot("https://www.r-project.org/", "r-sidebar-zoom.png", selector = ".sidebar", zoom = 2) ``` @@ -68,7 +78,8 @@ webshot("https://www.r-project.org/", "r-sidebar-zoom.png", All parameters of function `webshot`. That means that multiple screenshots can be taken with a single command. When taking a lot of screenshots, vectorization can divide by 5 the execution time. -``` r + +```r # Take a screenshot of different sites webshot(c("https://www.r-project.org/", "https://github.com/wch/webshot"), file = c("r.png", "webshot.png")) @@ -85,33 +96,40 @@ webshot("http://rstudio.github.io/leaflet/", selector = list("#features", "#installation")) ``` + + ### Screenshots of Shiny applications The `appshot()` function will run a Shiny app locally in a separate R process, and take a screenshot of it. After taking the screenshot, it will kill the R process that is running the Shiny app. -``` r + +```r # Get the directory of one of the Shiny examples appdir <- system.file("examples", "01_hello", package="shiny") appshot(appdir, "01_hello.png") ``` + ### Screenshots of R Markdown documents The `rmdshot()` function takes screenshots of R Markdown documents. For static R Markdown documents, it renders them to HTML in a temporary directory (using `rmarkdown::render()`)and then takes a screenshot. For dynamic R Markdown documents, it runs them using `rmarkdown::run()` in a separate R process and then takes a screenshot. After taking the screenshot, it will kill the R process that is running the document. -``` r + +```r rmdshot("document.rmd", "document.png") ``` + ### Manipulating images -If you have GraphicsMagick (recommended) or ImageMagick installed, you can pass the result to `resize()` to resize the image after taking the screenshot. This can take any valid ImageMagick geometry specifictaion, like `"75%"`, or `"400x"` (for an image 400 pixels wide). However, you may get different (and often better) results by using the `zoom` option: the fonts and graphical elements will render more sharply. However, compared to simply resizing, zooming out may result in slightly different positioning of text and layout elements. +You can pass the result to `resize()` to resize the image after taking the screenshot. This can take any valid ImageMagick geometry specifictaion, like `"75%"`, or `"400x"` (for an image 400 pixels wide). However, you may get different (and often better) results by using the `zoom` option: the fonts and graphical elements will render more sharply. However, compared to simply resizing, zooming out may result in slightly different positioning of text and layout elements. You can also call `shrink()`, which runs [OptiPNG](http://optipng.sourceforge.net/) to shrink the PNG file losslessly. -``` r + +```r webshot("https://www.r-project.org/", "r-small-resized.png") %>% resize("75%") %>% shrink() @@ -126,6 +144,9 @@ webshot("https://www.r-project.org/", "r-small.png") %>% shrink() ``` + + + To illustrate the difference between `resize()` and `zoom`, here is an image with `resize("50%")`: ![](tools/r-small-resized.png) diff --git a/man/resize.Rd b/man/resize.Rd index f891c32..beec8e9 100644 --- a/man/resize.Rd +++ b/man/resize.Rd @@ -17,8 +17,7 @@ for each image.} } \description{ This does not change size of the image in pixels, nor does it affect -appearance -- it is lossless compression. This requires GraphicsMagick -(recommended) or ImageMagick to be installed. +appearance -- it is lossless compression. } \examples{ if (interactive()) { diff --git a/vignettes/intro.Rmd b/vignettes/intro.Rmd index ff2d7a2..9c9b00c 100644 --- a/vignettes/intro.Rmd +++ b/vignettes/intro.Rmd @@ -115,7 +115,7 @@ appdir <- system.file("examples", "01_hello", package="shiny") appshot(appdir, delay = 3) ``` -There are two functions `resize()` and `shrink()` to manipulate images, which require GraphicsMagick (or ImageMagick) and OptiPNG, respectively. A simple example: +There are two functions `resize()` and `shrink()` to manipulate images, that latter of which requires OptiPNG, respectively. A simple example: ```{r eval=FALSE} # Result can be piped to other commands like resize() and shrink() From 7fa465e1bb6473eb41afc193ddfa0b146579a6cd Mon Sep 17 00:00:00 2001 From: "Thomas J. Leeper" Date: Wed, 22 Aug 2018 11:22:42 -0500 Subject: [PATCH 2/2] add utils to Imports/NAMESPACE --- R/appshot.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/appshot.R b/R/appshot.R index beea0f8..58905ed 100644 --- a/R/appshot.R +++ b/R/appshot.R @@ -107,7 +107,7 @@ appshot.shiny.appobj <- function( p <- r_background_process( function(url, file, ..., timeout) { # Wait for app to start - wait <- getFromNamespace("wait_until_server_exists", "webshot") + wait <- utils::getFromNamespace("wait_until_server_exists", "webshot") wait(url, timeout = timeout) webshot::webshot(url = url, file = file, ...) },