4343# ' specifying dimensions in pixels.
4444# ' @param bg Background colour. If `NULL`, uses the `plot.background` fill value
4545# ' from the plot theme.
46+ # ' @param create.dir Whether to create new directories if a non-existing
47+ # ' directory is specified in the `filename` or `path` (`TRUE`) or return an
48+ # ' error (`FALSE`, default). If `FALSE` and run in an interactive session,
49+ # ' a prompt will appear asking to create a new directory when necessary.
4650# ' @param ... Other arguments passed on to the graphics device function,
4751# ' as specified by `device`.
4852# ' @export
8488ggsave <- function (filename , plot = last_plot(),
8589 device = NULL , path = NULL , scale = 1 ,
8690 width = NA , height = NA , units = c(" in" , " cm" , " mm" , " px" ),
87- dpi = 300 , limitsize = TRUE , bg = NULL , ... ) {
88- if (length(filename ) != 1 ) {
89- if (length(filename ) == 0 ) {
90- cli :: cli_abort(" {.arg filename} cannot be empty." )
91- }
92- len <- length(filename )
93- filename <- filename [1 ]
94- cli :: cli_warn(c(
95- " {.arg filename} must have length 1, not length {len}." ,
96- " !" = " Only the first, {.file {filename}}, will be used."
97- ))
98- }
91+ dpi = 300 , limitsize = TRUE , bg = NULL ,
92+ create.dir = FALSE ,
93+ ... ) {
94+ filename <- check_path(path , filename , create.dir )
9995
10096 dpi <- parse_dpi(dpi )
10197 dev <- plot_dev(device , filename , dpi = dpi )
10298 dim <- plot_dim(c(width , height ), scale = scale , units = units ,
10399 limitsize = limitsize , dpi = dpi )
104100
105- if (! is.null(path )) {
106- filename <- file.path(path , filename )
107- }
108101 if (is_null(bg )) {
109102 bg <- calc_element(" plot.background" , plot_theme(plot ))$ fill %|| % " transparent"
110103 }
@@ -119,6 +112,56 @@ ggsave <- function(filename, plot = last_plot(),
119112 invisible (filename )
120113}
121114
115+ check_path <- function (path , filename , create.dir ,
116+ call = caller_env()) {
117+
118+ if (length(filename ) > 1 && is.character(filename )) {
119+ cli :: cli_warn(c(
120+ " {.arg filename} must have length 1, not {length(filename)}." ,
121+ " !" = " Only the first, {.file {filename[1]}}, will be used."
122+ ), call = call )
123+ filename <- filename [1 ]
124+ }
125+ check_string(filename , allow_empty = FALSE , call = call )
126+
127+ check_string(path , allow_empty = FALSE , allow_null = TRUE , call = call )
128+ if (! is.null(path )) {
129+ filename <- file.path(path , filename )
130+ } else {
131+ path <- dirname(filename )
132+ }
133+
134+ # Happy path: target file is in valid directory
135+ if (dir.exists(path )) {
136+ return (filename )
137+ }
138+
139+ check_bool(create.dir , call = call )
140+
141+ # Try to ask user to create a new directory
142+ if (interactive() && ! create.dir ) {
143+ cli :: cli_bullets(c(
144+ " Cannot find directory {.path {path}}." ,
145+ " i" = " Would you like to create a new directory?"
146+ ))
147+ create.dir <- utils :: menu(c(" Yes" , " No" )) == 1
148+ }
149+
150+ # Create new directory
151+ if (create.dir ) {
152+ dir.create(path , recursive = TRUE )
153+ if (dir.exists(path )) {
154+ cli :: cli_alert_success(" Created directory: {.path {path}}." )
155+ return (filename )
156+ }
157+ }
158+
159+ cli :: cli_abort(c(
160+ " Cannot find directory {.path {path}}." ,
161+ i = " Please supply an existing directory or use {.code create.dir = TRUE}."
162+ ), call = call )
163+ }
164+
122165# ' Parse a DPI input from the user
123166# '
124167# ' Allows handling of special strings when user specifies a DPI like "print".
0 commit comments