diff --git a/NAMESPACE b/NAMESPACE index 050413eb65..05e448d96e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -89,6 +89,7 @@ S3method(fortify,summary.glht) S3method(ggplot,data.frame) S3method(ggplot,default) S3method(grid.draw,absoluteGrob) +S3method(grid.draw,ggplot) S3method(grobHeight,absoluteGrob) S3method(grobHeight,zeroGrob) S3method(grobWidth,absoluteGrob) diff --git a/NEWS b/NEWS index 16785040a4..5245c016ed 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ ggplot2 1.0.1.9000 ---------------------------------------------------------------- +* `ggsave()` has been simplified a little to make it easier to maintain. + It no longer checks that you're printing a ggplot2 object (so now also + works with any grid grob) (#970), and always requires a filename. + * `facet_wrap()` more carefully checks its `nrow` and `ncol` arguments to ensure that they're specified correctly (@richierocks, #962) diff --git a/R/save.r b/R/save.r index f2abc75567..10d64ff31c 100644 --- a/R/save.r +++ b/R/save.r @@ -1,75 +1,66 @@ -#' Save a ggplot with sensible defaults +#' Save a ggplot (or other grid object) with sensible defaults #' -#' ggsave is a convenient function for saving a plot. It defaults to -#' saving the last plot that you displayed, and for a default size uses -#' the size of the current graphics device. It also guesses the type of -#' graphics device from the extension. This means the only argument you -#' need to supply is the filename. +#' \code{ggsave()} is a convenient function for saving a plot. It defaults to +#' saving the last plot that you displayed, using the size of the current +#' graphics device. It also guesses the type of graphics device from the +#' extension. #' -#' \code{ggsave} currently recognises the extensions eps/ps, tex (pictex), -#' pdf, jpeg, tiff, png, bmp, svg and wmf (windows only). -#' -#' @param filename file name/filename of plot -#' @param plot plot to save, defaults to last plot displayed -#' @param device device to use, automatically extract from file name extension -#' @param path path to save plot to (if you just want to set path and not -#' filename) -#' @param scale scaling factor -#' @param width width (defaults to the width of current plotting window) -#' @param height height (defaults to the height of current plotting window) -#' @param units units for width and height when either one is explicitly specified (in, cm, or mm) -#' @param dpi dpi to use for raster graphics -#' @param limitsize when \code{TRUE} (the default), \code{ggsave} will not +#' @param filename File name to create on disk. +#' @param plot Plot to save, defaults to last plot displayed. +#' @param device Device to use. By default, extracted from extension. +#' \code{ggsave} currently recognises eps/ps, tex (pictex), pdf, jpeg, tiff, +#' png, bmp, svg and wmf (windows only). +#' @param path Path to save plot to (combined with filename). +#' @param scale Multiplicative scaling factor. +#' @param width,height Plot dimensions, defaults to size of current graphics +#' device. +#' @param units Units for width and height when specified explicitly (in, cm, +#' or mm) +#' @param dpi Resolution used for raster outputs. +#' @param limitsize When \code{TRUE} (the default), \code{ggsave} will not #' save images larger than 50x50 inches, to prevent the common error of #' specifying dimensions in pixels. -#' @param ... other arguments passed to graphics device +#' @param ... Other arguments passed on to graphics device #' @export #' @examples #' \dontrun{ -#' ratings <- ggplot(movies, aes(rating)) + -#' geom_histogram() -#' ggplot(movies, aes(length)) + -#' geom_histogram() -#' ggsave("length-hist.pdf") -#' ggsave("length-hist.png") -#' ggsave("ratings.pdf", ratings) -#' ggsave("ratings.pdf", ratings, width=4, height=4) -#' # make twice as big as on screen -#' ggsave("ratings.pdf", ratings, scale=2) +#' ggplot(movies, aes(rating)) + geom_histogram(binwidth = 0.1) +#' +#' ggsave("ratings.pdf") +#' ggsave("ratings.pdf") +#' +#' ggsave("ratings.pdf", width = 4, height = 4) +#' ggsave("ratings.pdf", width = 20, height = 20, units = "cm") +#' +#' unlink("ratings.pdf") +#' unlink("ratings.png") #' } -ggsave <- function(filename = default_name(plot), plot = last_plot(), - device = default_device(filename), path = NULL, scale = 1, - width = par("din")[1], height = par("din")[2], units = c("in", "cm", "mm"), - dpi = 300, limitsize = TRUE, ...) { - - if (!inherits(plot, "ggplot")) stop("plot should be a ggplot2 plot") +ggsave <- function(filename, plot = last_plot(), + device = default_device(filename), path = NULL, scale = 1, + width = NA, height = NA, units = c("in", "cm", "mm"), + dpi = 300, limitsize = TRUE, ...) { eps <- ps <- function(..., width, height) - grDevices::postscript(..., width=width, height=height, onefile=FALSE, + grDevices::postscript(..., width = width, height = height, onefile = FALSE, horizontal = FALSE, paper = "special") tex <- function(..., width, height) - grDevices::pictex(..., width=width, height=height) - pdf <- function(..., version="1.4") - grDevices::pdf(..., version=version) + grDevices::pictex(..., width = width, height = height) + pdf <- function(..., version = "1.4") + grDevices::pdf(..., version = version) svg <- function(...) grDevices::svg(...) wmf <- function(..., width, height) - grDevices::win.metafile(..., width=width, height=height) + grDevices::win.metafile(..., width = width, height = height) emf <- function(..., width, height) - grDevices::win.metafile(..., width=width, height=height) - + grDevices::win.metafile(..., width = width, height = height) png <- function(..., width, height) - grDevices::png(..., width=width, height=height, res = dpi, units = "in") + grDevices::png(..., width = width, height = height, res = dpi, units = "in") jpg <- jpeg <- function(..., width, height) - grDevices::jpeg(..., width=width, height=height, res = dpi, units = "in") + grDevices::jpeg(..., width = width, height = height, res = dpi, units = "in") bmp <- function(..., width, height) - grDevices::bmp(..., width=width, height=height, res = dpi, units = "in") + grDevices::bmp(..., width = width, height = height, res = dpi, units = "in") tiff <- function(..., width, height) - grDevices::tiff(..., width=width, height=height, res = dpi, units = "in") - - default_name <- function(plot) { - paste(digest.ggplot(plot), ".pdf", sep="") - } + grDevices::tiff(..., width = width, height = height, res = dpi, units = "in") default_device <- function(filename) { pieces <- strsplit(filename, "\\.")[[1]] @@ -77,51 +68,45 @@ ggsave <- function(filename = default_name(plot), plot = last_plot(), match.fun(ext) } - units <- match.arg(units) - convert_to_inches <- function(x, units) { - x <- switch(units, - `in` = x, - cm = x / 2.54, - mm = x / 2.54 /10 - ) - } + dim <- plot_dim(c(width, height), scale = scale, units = units, + limitsize = limitsize) - convert_from_inches <- function(x, units) { - x <- switch(units, - `in` = x, - cm = x * 2.54, - mm = x * 2.54 * 10 - ) + if (!is.null(path)) { + filename <- file.path(path, filename) } + device(file = filename, width = dim[1], height = dim[2], ...) + on.exit(capture.output(dev.off())) + grid.draw(plot) - # dimensions need to be in inches for all graphic devices - # convert width and height into inches when they are specified - if (!missing(width)) { - width <- convert_to_inches(width, units) - } - if (!missing(height)) { - height <- convert_to_inches(height, units) - } - # if either width or height is not specified, display an information message - # units are those specified by the user - if (missing(width) || missing(height)) { - message("Saving ", prettyNum(convert_from_inches(width * scale, units), digits=3), " x ", prettyNum(convert_from_inches(height * scale, units), digits=3), " ", units, " image") - } + invisible() +} + +plot_dim <- function(dim = c(NA, NA), scale = 1, units = c("in", "cm", "mm"), + limitsize = TRUE, dim_guess = FALSE) { + + + units <- match.arg(units) + to_inches <- function(x) x / c(`in` = 1, cm = 2.54, mm = 2.54 * 10)[units] + from_inches <- function(x) x * c(`in` = 1, cm = 2.54, mm = 2.54 * 10)[units] - width <- width * scale - height <- height * scale + dim <- to_inches(dim) * scale - if (limitsize && (width >= 50 || height >= 50)) { - stop("Dimensions exceed 50 inches (height and width are specified in inches/cm/mm, not pixels).", - " If you are sure you want these dimensions, use 'limitsize=FALSE'.") + if (any(is.na(dim))) { + dim[is.na(dim)] <- par("din") * scale + dim_f <- prettyNum(from_inches(dim), digits = 3) + message("Saving ", dim_f[1], " x ", dim_f[2], " ", units, " image") } - if (!is.null(path)) { - filename <- file.path(path, filename) + if (limitsize && any(dim >= 50)) { + stop("Dimensions exceed 50 inches (height and width are specified in '", + units, "' not pixels). If you're sure you a plot that big, use ", + "`limitsize = FALSE`.", call. = FALSE) } - device(file=filename, width=width, height=height, ...) - on.exit(capture.output(dev.off())) - print(plot) - invisible() + dim +} + +#' @export +grid.draw.ggplot <- function(x, recording = TRUE) { + print(x) } diff --git a/inst/tests/test-ggsave.R b/inst/tests/test-ggsave.R new file mode 100644 index 0000000000..5968181a0e --- /dev/null +++ b/inst/tests/test-ggsave.R @@ -0,0 +1,22 @@ +context("ggsave") + + +# plot_dim --------------------------------------------------------------- + +test_that("guesses and informs if dim not specified", { + png(width = 10, height = 10, units = "in", res = 300) + on.exit(capture.output(dev.off())) + + expect_message(out <- plot_dim(), "10 x 10") + expect_equal(out, c(10, 10)) +}) + +test_that("warned about large plot unless limitsize = FALSE", { + expect_error(plot_dim(c(50, 50)), "exceed 50 inches") + expect_equal(plot_dim(c(50, 50), limitsize = FALSE), c(50, 50)) +}) + +test_that("scale multiplies height & width", { + expect_equal(plot_dim(c(10, 10), scale = 1), c(10, 10)) + expect_equal(plot_dim(c(5, 5), scale = 2), c(10, 10)) +}) diff --git a/man/ggsave.Rd b/man/ggsave.Rd index 320af1efc5..ebb7f9c6ce 100644 --- a/man/ggsave.Rd +++ b/man/ggsave.Rd @@ -2,62 +2,57 @@ % Please edit documentation in R/save.r \name{ggsave} \alias{ggsave} -\title{Save a ggplot with sensible defaults} +\title{Save a ggplot (or other grid object) with sensible defaults} \usage{ -ggsave(filename = default_name(plot), plot = last_plot(), - device = default_device(filename), path = NULL, scale = 1, - width = par("din")[1], height = par("din")[2], units = c("in", "cm", - "mm"), dpi = 300, limitsize = TRUE, ...) +ggsave(filename, plot = last_plot(), device = default_device(filename), + path = NULL, scale = 1, width = NA, height = NA, units = c("in", + "cm", "mm"), dpi = 300, limitsize = TRUE, ...) } \arguments{ -\item{filename}{file name/filename of plot} +\item{filename}{File name to create on disk.} -\item{plot}{plot to save, defaults to last plot displayed} +\item{plot}{Plot to save, defaults to last plot displayed.} -\item{device}{device to use, automatically extract from file name extension} +\item{device}{Device to use. By default, extracted from extension. +\code{ggsave} currently recognises eps/ps, tex (pictex), pdf, jpeg, tiff, +png, bmp, svg and wmf (windows only).} -\item{path}{path to save plot to (if you just want to set path and not -filename)} +\item{path}{Path to save plot to (combined with filename).} -\item{scale}{scaling factor} +\item{scale}{Multiplicative scaling factor.} -\item{width}{width (defaults to the width of current plotting window)} +\item{width,height}{Plot dimensions, defaults to size of current graphics +device.} -\item{height}{height (defaults to the height of current plotting window)} +\item{units}{Units for width and height when specified explicitly (in, cm, +or mm)} -\item{units}{units for width and height when either one is explicitly specified (in, cm, or mm)} +\item{dpi}{Resolution used for raster outputs.} -\item{dpi}{dpi to use for raster graphics} - -\item{limitsize}{when \code{TRUE} (the default), \code{ggsave} will not +\item{limitsize}{When \code{TRUE} (the default), \code{ggsave} will not save images larger than 50x50 inches, to prevent the common error of specifying dimensions in pixels.} -\item{...}{other arguments passed to graphics device} +\item{...}{Other arguments passed on to graphics device} } \description{ -ggsave is a convenient function for saving a plot. It defaults to -saving the last plot that you displayed, and for a default size uses -the size of the current graphics device. It also guesses the type of -graphics device from the extension. This means the only argument you -need to supply is the filename. -} -\details{ -\code{ggsave} currently recognises the extensions eps/ps, tex (pictex), -pdf, jpeg, tiff, png, bmp, svg and wmf (windows only). +\code{ggsave()} is a convenient function for saving a plot. It defaults to +saving the last plot that you displayed, using the size of the current +graphics device. It also guesses the type of graphics device from the +extension. } \examples{ \dontrun{ -ratings <- ggplot(movies, aes(rating)) + - geom_histogram() -ggplot(movies, aes(length)) + - geom_histogram() -ggsave("length-hist.pdf") -ggsave("length-hist.png") -ggsave("ratings.pdf", ratings) -ggsave("ratings.pdf", ratings, width=4, height=4) -# make twice as big as on screen -ggsave("ratings.pdf", ratings, scale=2) +ggplot(movies, aes(rating)) + geom_histogram(binwidth = 0.1) + +ggsave("ratings.pdf") +ggsave("ratings.pdf") + +ggsave("ratings.pdf", width = 4, height = 4) +ggsave("ratings.pdf", width = 20, height = 20, units = "cm") + +unlink("ratings.pdf") +unlink("ratings.png") } }