diff --git a/R/docx_add.R b/R/docx_add.R index 6e0a919e..11f6b813 100644 --- a/R/docx_add.R +++ b/R/docx_add.R @@ -20,23 +20,34 @@ body_add_break <- function(x, pos = "after") { #' @inheritParams body_add_break #' @param src image filename, the basename of the file must not contain any blank. #' @param style paragraph style -#' @param width height in inches -#' @param height height in inches +#' @param width,height image size in units expressed by the unit argument. +#' Defaults to "in"ches. +#' @param unit One of the following units in which the width and height +#' arguments are expressed: "in", "cm" or "mm". #' @examples #' doc <- read_docx() #' #' img.file <- file.path(R.home("doc"), "html", "logo.jpg") #' if (file.exists(img.file)) { #' doc <- body_add_img(x = doc, src = img.file, height = 1.06, width = 1.39) +#' +#' # Set the unit in which the width and height arguments are expressed +#' doc <- body_add_img( +#' x = doc, src = img.file, +#' height = 2.69, width = 3.53, +#' unit = "cm" +#' ) #' } #' #' print(doc, target = tempfile(fileext = ".docx")) #' @family functions for adding content -body_add_img <- function(x, src, style = NULL, width, height, pos = "after") { +body_add_img <- function(x, src, style = NULL, width, height, unit = "in", pos = "after") { if (is.null(style)) { style <- x$default_styles$paragraph } + unit <- check_unit(unit, c("in", "cm", "mm")) + file_type <- gsub("(.*)(\\.[a-zA-Z0-0]+)$", "\\2", src) if (file_type %in% ".svg") { @@ -55,7 +66,7 @@ body_add_img <- function(x, src, style = NULL, width, height, pos = "after") { style_id <- get_style_id(data = x$styles, style = style, type = "paragraph") - ext_img <- external_img(new_src, width = width, height = height) + ext_img <- external_img(new_src, width = width, height = height, unit = unit) xml_elt <- runs_to_p_wml(ext_img, add_ns = TRUE, style_id = style_id) body_add_xml(x = x, str = xml_elt, pos = pos) @@ -112,8 +123,10 @@ body_add_docx <- function(x, src, pos = "after") { #' @inheritParams body_add_break #' @param value ggplot object #' @param style paragraph style -#' @param width height in inches -#' @param height height in inches +#' @param width,height plot size in units expressed by the unit argument. +#' Defaults to a width of 6 and a height of 5 "in"ches. +#' @param unit One of the following units in which the width and height +#' arguments are expressed: "in", "cm" or "mm". #' @param res resolution of the png image in ppi #' @param scale Multiplicative scaling factor, same as in ggsave #' @param pos where to add the new element relative to the cursor, @@ -129,24 +142,37 @@ body_add_docx <- function(x, src, pos = "after") { #' #' if (capabilities(what = "png")) { #' doc <- body_add_gg(doc, value = gg_plot, style = "centered") +#' +#' # Set the unit in which the width and height arguments are expressed +#' doc <- body_add_gg(doc, value = gg_plot, style = "centered", unit = "cm") #' } #' #' print(doc, target = tempfile(fileext = ".docx")) #' } #' @family functions for adding content #' @importFrom ragg agg_png -body_add_gg <- function(x, value, width = 6, height = 5, res = 300, style = "Normal", scale = 1, pos = "after", ...) { +body_add_gg <- function(x, value, width = 6, height = 5, unit = "in", res = 300, style = "Normal", scale = 1, pos = "after", ...) { if (!requireNamespace("ggplot2")) { stop("package ggplot2 is required to use this function") } stopifnot(inherits(value, "gg")) + + if ("units" %in% names(list(...))) { + cli::cli_abort( + c("Found a {.arg units} argument. Did you mean {.arg unit}?") + ) + } + + unit <- check_unit(unit, c("in", "cm", "mm")) + + file <- tempfile(fileext = ".png") - agg_png(filename = file, width = width, height = height, scaling = scale, units = "in", res = res, background = "transparent", ...) + agg_png(filename = file, width = width, height = height, scaling = scale, units = unit, res = res, background = "transparent", ...) print(value) dev.off() on.exit(unlink(file)) - body_add_img(x, src = file, style = style, width = width, height = height, pos = pos) + body_add_img(x, src = file, style = style, width = width, height = height, pos = pos, unit = unit) } @@ -364,8 +390,10 @@ body_add_toc <- function(x, level = 3, pos = "after", style = NULL, separator = #' @inheritParams body_add_break #' @param value plot instructions, see [plot_instr()]. #' @param style paragraph style -#' @param width height in inches -#' @param height height in inches +#' @param width,height plot size in units expressed by the unit argument. +#' Defaults to a width of 6 and a height of 5 "in"ches. +#' @param unit One of the following units in which the width and height +#' arguments are expressed: "in", "cm" or "mm". #' @param res resolution of the png image in ppi #' @param pos where to add the new element relative to the cursor, #' one of "after", "before", "on". @@ -375,21 +403,25 @@ body_add_toc <- function(x, level = 3, pos = "after", style = NULL, separator = #' doc <- read_docx() #' #' if (capabilities(what = "png")) { -#' doc <- body_add_plot(doc, -#' value = plot_instr( +#' p <- plot_instr( #' code = { #' barplot(1:5, col = 2:6) #' } -#' ), -#' style = "centered" -#' ) +#' ) +#' +#' doc <- body_add_plot(doc, value = p, style = "centered") +#' +#' # Set the unit in which the width and height arguments are expressed +#' doc <- body_add_plot(doc, value = p, style = "centered", unit = "cm") #' } #' #' print(doc, target = tempfile(fileext = ".docx")) #' @family functions for adding content -body_add_plot <- function(x, value, width = 6, height = 5, res = 300, style = "Normal", pos = "after", ...) { +body_add_plot <- function(x, value, width = 6, height = 5, unit = "in", res = 300, style = "Normal", pos = "after", ...) { + unit <- check_unit(unit, c("in", "cm", "mm")) + file <- tempfile(fileext = ".png") - agg_png(filename = file, width = width, height = height, units = "in", res = res, background = "transparent", ...) + agg_png(filename = file, width = width, height = height, units = unit, res = res, background = "transparent", ...) tryCatch( { eval(value$code) @@ -399,7 +431,7 @@ body_add_plot <- function(x, value, width = 6, height = 5, res = 300, style = "N } ) on.exit(unlink(file)) - body_add_img(x, src = file, style = style, width = width, height = height, pos = pos) + body_add_img(x, src = file, style = style, width = width, height = height, unit = unit, pos = pos) } @@ -886,30 +918,36 @@ body_add.run_columnbreak <- function(x, value, style = NULL, ...) { #' @export #' @describeIn body_add add a ggplot object. -#' @param width height in inches -#' @param height height in inches +#' @param width,height plot size in units expressed by the unit argument. +#' Defaults to a width of 6 and a height of 5 "in"ches. +#' @param unit One of the following units in which the width and height +#' arguments are expressed: "in", "cm" or "mm". #' @param res resolution of the png image in ppi #' @param scale Multiplicative scaling factor, same as in ggsave -body_add.gg <- function(x, value, width = 6, height = 5, res = 300, style = "Normal", scale = 1, ...) { +body_add.gg <- function(x, value, width = 6, height = 5, unit = "in", res = 300, style = "Normal", scale = 1, ...) { if (!requireNamespace("ggplot2")) { stop("package ggplot2 is required to use this function") } + unit <- check_unit(unit, c("in", "cm", "mm")) + file <- tempfile(fileext = ".png") - agg_png(filename = file, width = width, height = height, units = "in", res = res, scaling = scale, background = "transparent", ...) + agg_png(filename = file, width = width, height = height, units = unit, res = res, scaling = scale, background = "transparent", ...) print(value) dev.off() on.exit(unlink(file)) - value <- external_img(src = file, width = width, height = height) + value <- external_img(src = file, width = width, height = height, unit = unit) body_add(x, value, style = style) } #' @export #' @describeIn body_add add a base plot with a [plot_instr] object. -body_add.plot_instr <- function(x, value, width = 6, height = 5, res = 300, style = "Normal", ...) { +body_add.plot_instr <- function(x, value, width = 6, height = 5, unit = "in", res = 300, style = "Normal", ...) { + unit <- check_unit(unit, c("in", "cm", "mm")) + file <- tempfile(fileext = ".png") - agg_png(filename = file, width = width, height = height, units = "in", res = res, scaling = 1, background = "transparent", ...) + agg_png(filename = file, width = width, height = height, units = unit, res = res, scaling = 1, background = "transparent", ...) tryCatch( { eval(value$code) @@ -920,12 +958,11 @@ body_add.plot_instr <- function(x, value, width = 6, height = 5, res = 300, styl ) on.exit(unlink(file)) - value <- external_img(src = file, width = width, height = height) + value <- external_img(src = file, width = width, height = height, unit = unit) body_add(x, value, style = style) } - #' @export #' @describeIn body_add pour content of an external docx file with with a [block_pour_docx] object body_add.block_pour_docx <- function(x, value, ...) { diff --git a/R/utils.R b/R/utils.R index cf69f6dc..0200f5c2 100644 --- a/R/utils.R +++ b/R/utils.R @@ -334,6 +334,24 @@ stop_if_not_rpptx <- function(x, arg = NULL) { stop_if_not_class(x, "rpptx", arg) } +check_unit <- function(unit, choices, several.ok = FALSE) { + if (!several.ok && length(unit) != 1) { + cli::cli_abort( + c("{.arg unit} is not length 1.", + "x" = "{.arg unit} must be {.emph a string}." + ) + ) + } + if (!unit %in% choices) { + cli::cli_abort( + c("{.arg unit} should be one of {.or {choices}}.", + "x" = "{.arg unit} was {.emph {unit}\"}." + ) + ) + } + + unit +} # htmlEscapeCopy ---- diff --git a/man/body_add.Rd b/man/body_add.Rd index c1833ec5..95e99113 100644 --- a/man/body_add.Rd +++ b/man/body_add.Rd @@ -56,13 +56,23 @@ body_add(x, value, ...) value, width = 6, height = 5, + unit = "in", res = 300, style = "Normal", scale = 1, ... ) -\method{body_add}{plot_instr}(x, value, width = 6, height = 5, res = 300, style = "Normal", ...) +\method{body_add}{plot_instr}( + x, + value, + width = 6, + height = 5, + unit = "in", + res = 300, + style = "Normal", + ... +) \method{body_add}{block_pour_docx}(x, value, ...) @@ -93,9 +103,11 @@ argument \code{path} from \link{read_docx}).} \item{alignment}{columns alignement, argument length must match with columns length, values must be "l" (left), "r" (right) or "c" (center).} -\item{width}{height in inches} +\item{width, height}{plot size in units expressed by the unit argument. +Defaults to a width of 6 and a height of 5 "in"ches.} -\item{height}{height in inches} +\item{unit}{One of the following units in which the width and height +arguments are expressed: "in", "cm" or "mm".} \item{res}{resolution of the png image in ppi} diff --git a/man/body_add_gg.Rd b/man/body_add_gg.Rd index 92e3cb8a..12dc5f56 100644 --- a/man/body_add_gg.Rd +++ b/man/body_add_gg.Rd @@ -9,6 +9,7 @@ body_add_gg( value, width = 6, height = 5, + unit = "in", res = 300, style = "Normal", scale = 1, @@ -21,9 +22,11 @@ body_add_gg( \item{value}{ggplot object} -\item{width}{height in inches} +\item{width, height}{plot size in units expressed by the unit argument. +Defaults to a width of 6 and a height of 5 "in"ches.} -\item{height}{height in inches} +\item{unit}{One of the following units in which the width and height +arguments are expressed: "in", "cm" or "mm".} \item{res}{resolution of the png image in ppi} @@ -48,6 +51,9 @@ if (require("ggplot2")) { if (capabilities(what = "png")) { doc <- body_add_gg(doc, value = gg_plot, style = "centered") + + # Set the unit in which the width and height arguments are expressed + doc <- body_add_gg(doc, value = gg_plot, style = "centered", unit = "cm") } print(doc, target = tempfile(fileext = ".docx")) diff --git a/man/body_add_img.Rd b/man/body_add_img.Rd index aea9934d..65a248c3 100644 --- a/man/body_add_img.Rd +++ b/man/body_add_img.Rd @@ -4,7 +4,7 @@ \alias{body_add_img} \title{Add an image in a 'Word' document} \usage{ -body_add_img(x, src, style = NULL, width, height, pos = "after") +body_add_img(x, src, style = NULL, width, height, unit = "in", pos = "after") } \arguments{ \item{x}{an rdocx object} @@ -13,9 +13,11 @@ body_add_img(x, src, style = NULL, width, height, pos = "after") \item{style}{paragraph style} -\item{width}{height in inches} +\item{width, height}{image size in units expressed by the unit argument. +Defaults to "in"ches.} -\item{height}{height in inches} +\item{unit}{One of the following units in which the width and height +arguments are expressed: "in", "cm" or "mm".} \item{pos}{where to add the new element relative to the cursor, one of "after", "before", "on".} @@ -29,6 +31,13 @@ doc <- read_docx() img.file <- file.path(R.home("doc"), "html", "logo.jpg") if (file.exists(img.file)) { doc <- body_add_img(x = doc, src = img.file, height = 1.06, width = 1.39) + + # Set the unit in which the width and height arguments are expressed + doc <- body_add_img( + x = doc, src = img.file, + height = 2.69, width = 3.53, + unit = "cm" + ) } print(doc, target = tempfile(fileext = ".docx")) diff --git a/man/body_add_plot.Rd b/man/body_add_plot.Rd index 55b2d342..8b443fea 100644 --- a/man/body_add_plot.Rd +++ b/man/body_add_plot.Rd @@ -9,6 +9,7 @@ body_add_plot( value, width = 6, height = 5, + unit = "in", res = 300, style = "Normal", pos = "after", @@ -20,9 +21,11 @@ body_add_plot( \item{value}{plot instructions, see \code{\link[=plot_instr]{plot_instr()}}.} -\item{width}{height in inches} +\item{width, height}{plot size in units expressed by the unit argument. +Defaults to a width of 6 and a height of 5 "in"ches.} -\item{height}{height in inches} +\item{unit}{One of the following units in which the width and height +arguments are expressed: "in", "cm" or "mm".} \item{res}{resolution of the png image in ppi} @@ -40,14 +43,16 @@ Add a plot as a png image into an rdocx object. doc <- read_docx() if (capabilities(what = "png")) { - doc <- body_add_plot(doc, - value = plot_instr( + p <- plot_instr( code = { barplot(1:5, col = 2:6) } - ), - style = "centered" - ) + ) + + doc <- body_add_plot(doc, value = p, style = "centered") + + # Set the unit in which the width and height arguments are expressed + doc <- body_add_plot(doc, value = p, style = "centered", unit = "cm") } print(doc, target = tempfile(fileext = ".docx")) diff --git a/man/body_replace_gg_at_bkm.Rd b/man/body_replace_gg_at_bkm.Rd index 72f14b11..25114ab6 100644 --- a/man/body_replace_gg_at_bkm.Rd +++ b/man/body_replace_gg_at_bkm.Rd @@ -38,9 +38,8 @@ body_replace_plot_at_bkm( \item{value}{a ggplot object for body_replace_gg_at_bkm() or a set plot instructions body_replace_plot_at_bkm(), see plot_instr().} -\item{width}{height in inches} - -\item{height}{height in inches} +\item{width, height}{plot size in units expressed by the unit argument. +Defaults to a width of 6 and a height of 5 "in"ches.} \item{res}{resolution of the png image in ppi} diff --git a/tests/testthat/test-docx-add.R b/tests/testthat/test-docx-add.R index 2313aa81..3bd6848a 100644 --- a/tests/testthat/test-docx-add.R +++ b/tests/testthat/test-docx-add.R @@ -4,6 +4,45 @@ getncheck <- function(x, str){ child_ } +plot_with_unit_and_check <- function(value, body_add_fun, width = 6, height = 5, ...) { + .in_to_emu <- 914400 + .cm_to_emu <- 360000 + .mm_to_emu <- 36000 + + x <- read_docx() + x <- body_add_fun(x, value, width = width, height = height, ... ) + x <- body_add_fun(x, value, unit = "in", width = width, height = height, ... ) + x <- body_add_fun(x, value, unit = "cm", width = width, height = height,... ) + x <- body_add_fun(x, value, unit = "mm", width = width, height = height,... ) + x <- cursor_end(x) + node <- docx_current_block_xml(x) + + expect_equal( + as.numeric( + xml_attr(xml_find_all(node, "//wp:extent"), "cx") + ) / c(.in_to_emu, .in_to_emu, .cm_to_emu, .mm_to_emu), + rep(width, 4) + ) + expect_equal( + as.numeric( + xml_attr(xml_find_all(node, "//wp:extent"), "cy") + ) / c(.in_to_emu, .in_to_emu, .cm_to_emu, .mm_to_emu), + rep(height, 4) + ) + # Non valid unit + expect_error( + body_add_fun(x, value, unit = "px", ... ) + ) + # Has "units=" + expect_error( + body_add_fun(x, value, units = "cm", ... ) + ) + # Has multiple units + expect_error( + body_add_fun(x, value, unit = c("cm", "in", "mm"), ... ) + ) +} + test_that("body_add_break", { x <- read_docx() x <- body_add_break(x) @@ -117,6 +156,14 @@ test_that("body_add_img", { getncheck(node, "w:r/w:drawing") }) +test_that("body_add_img with units", { + + img.file <- file.path( R.home("doc"), "html", "logo.jpg" ) + + plot_with_unit_and_check(img.file, body_add_img, height = 2.69, width = 3.53) + +}) + test_that("external_img add", { img.file <- file.path( R.home("doc"), "html", "logo.jpg" ) x <- read_docx() @@ -130,6 +177,7 @@ test_that("external_img add", { getncheck(node, "w:r/w:drawing") }) + test_that("ggplot add", { testthat::skip_if_not(requireNamespace("ggplot2", quietly = TRUE)) library("ggplot2") @@ -143,6 +191,29 @@ test_that("ggplot add", { getncheck(node, "w:r/w:drawing") }) +test_that("ggplot add with unit", { + testthat::skip_if_not(requireNamespace("ggplot2", quietly = TRUE)) + library("ggplot2") + + gg_plot <- ggplot(data = iris ) + + geom_point(mapping = aes(Sepal.Length, Petal.Length)) + + plot_with_unit_and_check(gg_plot, body_add_gg) + plot_with_unit_and_check(gg_plot, body_add) +}) + +test_that("plot add with unit", { + base_plot <- plot_instr( + code = { + barplot(1:5, col = 2:6) + } + ) + + # Base plot errors for "mm" with default pointsize of 12. + plot_with_unit_and_check(base_plot, body_add_plot, pointsize = 1) + plot_with_unit_and_check(base_plot, body_add, pointsize = 1) +}) + test_that("fpar add", { bold_face <- shortcuts$fp_bold(font.size = 20) bold_redface <- update(bold_face, color = "red")