diff --git a/DESCRIPTION b/DESCRIPTION index 7ab5ac43..83c42a63 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: officer Title: Manipulation of Microsoft Word and PowerPoint Documents -Version: 0.6.7.012 +Version: 0.6.7.013 Authors@R: c( person("David", "Gohel", , "david.gohel@ardata.fr", role = c("aut", "cre")), person("Stefan", "Moog", , "moogs@gmx.de", role = "aut"), diff --git a/NEWS.md b/NEWS.md index e71f19f7..ed6f897b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,10 @@ top to bottom and left to right. - `ph_location_type()` now throws an error if the `id` for a `type` is out of range (#602) and a more informative error message if the type is not present in layout (#601). - `plot_layout_properties()` assignment order fixed for `labels= FALSE` (#604) +- `layout_properties()` gains a `type_idx` column to index phs of the same type on a layout. Indexing is performed based on ph position, following a top-to-bottom, left-to-right order. (#606). +- `plot_layout_properties()` plots more information by default now: layout name, ph label, ph id, ph type + index by default (#606). +- `ph_location_type()`: new `type_idx` arg replaces the deprecated `id` arg (#606). + ## Features diff --git a/R/ph_location.R b/R/ph_location.R index 0cece9f1..0e5ca94a 100644 --- a/R/ph_location.R +++ b/R/ph_location.R @@ -1,4 +1,7 @@ -get_ph_loc <- function(x, layout, master, type, position_right, position_top, id = NULL) { + +# id is deprecated and replaced by type_idx. Will be removed soon +get_ph_loc <- function(x, layout, master, type, type_idx = NULL, position_right, position_top, + id = NULL, ph_id = NULL) { props <- layout_properties(x, layout = layout, master = master) types_on_layout <- unique(props$type) props <- props[props$type %in% type, , drop = FALSE] @@ -10,7 +13,7 @@ get_ph_loc <- function(x, layout, master, type, position_right, position_top, id "i" = cli::col_grey("see {.code layout_properties(x, '{layout}', '{master}')}") ), call = NULL) } - + # id and type_idx are both used for now. 'id' is deprecated. The following code block can be removed in the future. if (!is.null(id)) { if (!id %in% 1L:nr) { cli::cli_abort( @@ -22,7 +25,23 @@ get_ph_loc <- function(x, layout, master, type, position_right, position_top, id call = NULL ) } - props <- props[id, , drop = FALSE] + # the ordering of 'type_idx' (top->bottom, left-righ) is different than for the 'id' arg (index + # along the id colomn). Here, we restore the old ordering, to avoid a breaking change. + props <- props[order(props$type, as.integer(props$id)), ] # set order for type idx. Removing the line would result in the default layout properties order, i.e., top->bottom left->right. + props$.id <- stats::ave(props$type, props$master_name, props$name, props$type, FUN = seq_along) + props <- props[props$.id == id, , drop = FALSE] + } else if (!is.null(type_idx)) { + if (!type_idx %in% props$type_idx) { + cli::cli_abort( + c( + "{.arg type_idx} is out of range.", + "x" = "Must be between {.val {1L}} and {.val {max(props$type_idx)}} for ph type {.val {type}}.", + "i" = cli::col_grey("see {.code layout_properties(..., layout = '{layout}', master = '{master}')} for indexes of type '{type}'") + ), + call = NULL + ) + } + props <- props[props$type_idx == type_idx, , drop = FALSE] } else { if (position_right) { props <- props[props$offx + 0.0001 > max(props$offx), ] @@ -45,20 +64,24 @@ get_ph_loc <- function(x, layout, master, type, position_right, position_top, id } -as_ph_location <- function(x, ...){ - if( !is.data.frame(x) ){ - stop("x should be a data.frame") +as_ph_location <- function(x, ...) { + if (!is.data.frame(x)) { + cli::cli_abort( + c("{.arg x} must be a data frame.", + "x" = "You provided {.cls {class(x)[1]}} instead.") + ) } - ref_names <- c( "width", "height", "left", "top", - "ph_label", "ph", "type", "rotation", "fld_id", "fld_type") - if (!all(is.element(ref_names, names(x) ))) { + ref_names <- c( + "width", "height", "left", "top", "ph_label", "ph", "type", "rotation", "fld_id", "fld_type" + ) + if (!all(is.element(ref_names, names(x)))) { stop("missing column values:", paste0(setdiff(ref_names, names(x)), collapse = ",")) } - out <- x[ref_names] as.list(out) } + #' @export #' @title Eval a location on the current slide #' @description Eval a shape location against the current slide. @@ -197,6 +220,7 @@ fortify_location.location_template <- function( x, doc, ...){ fortify_location.location_manual(x) } + #' @export #' @title Location of a placeholder based on a type #' @description The function will use the type name of the placeholder (e.g. body, title), @@ -206,14 +230,20 @@ fortify_location.location_template <- function( x, doc, ...){ #' @param position_right the parameter is used when a selection with above #' parameters does not provide a unique position (for example #' layout 'Two Content' contains two element of type 'body'). -#' If \code{TRUE}, the element the most on the right side will be selected, +#' If `TRUE`, the element the most on the right side will be selected, #' otherwise the element the most on the left side will be selected. -#' @param position_top same than \code{position_right} but applied +#' @param position_top same than `position_right` but applied #' to top versus bottom. -#' @param id index of the placeholder. If two body placeholder, there can be -#' two different index: 1 and 2 for the first and second body placeholders defined -#' in the layout. If this argument is used, \code{position_right} and \code{position_top} -#' will be ignored. +#' @param type_idx Type index of the placeholder. If there is more than one +#' placeholder of a type (e.g., `body`), the type index can be supplied to uniquely +#' identify a ph. The index is a running number starting at 1. It is assigned by +#' placeholder position (top -> bottom, left -> right). See [plot_layout_properties()] +#' for details. If `idx` argument is used, `position_right` and `position_top` +#' are ignored. +#' @param id (**DEPRECATED, use `type_idx` instead**) Index of the placeholder. +#' If two body placeholder, there can be two different index: 1 and 2 for the +#' first and second body placeholders defined in the layout. If this argument +#' is used, `position_right` and `position_top` will be ignored. #' @param newlabel a label to associate with the placeholder. #' @param ... unused arguments #' @family functions for placeholder location @@ -244,7 +274,23 @@ fortify_location.location_template <- function( x, doc, ...){ #' #' fileout <- tempfile(fileext = ".pptx") #' print(doc, target = fileout) -ph_location_type <- function(type = "body", position_right = TRUE, position_top = TRUE, newlabel = NULL, id = NULL, ...) { +#' +ph_location_type <- function(type = "body", type_idx = NULL, position_right = TRUE, position_top = TRUE, + newlabel = NULL, id = NULL, ...) { + # the following two warnings can be deleted after the deprecated id arg is removed. + if (!is.null(id) && !is.null(type_idx)) { + cli::cli_warn("{.arg id} is ignored if {.arg type_idx} is provided ") + } + if (!is.null(id) && is.null(type_idx)) { + cli::cli_warn( + c( + "!" = "The {.arg id} argument in {.fn ph_location_type} is deprecated as of {.pkg officer} 0.6.7.", + "i" = "Please use the {.arg type_idx} argument instead.", + "x" = cli::col_red("Caution: new index logic in {.arg type_idx} (see docs).") + ) + ) + } + ph_types <- c( "ctrTitle", "subTitle", "dt", "ftr", "sldNum", "title", "body", "pic", "chart", "tbl", "dgm", "media", "clipArt" @@ -257,30 +303,39 @@ ph_location_type <- function(type = "body", position_right = TRUE, position_top call = NULL ) } - x <- list(type = type, position_right = position_right, position_top = position_top, id = id, label = newlabel) + x <- list( + type = type, type_idx = type_idx, position_right = position_right, + position_top = position_top, id = id, label = newlabel + ) class(x) <- c("location_type", "location_str") x } #' @export -fortify_location.location_type <- function( x, doc, ...){ - +fortify_location.location_type <- function(x, doc, ...) { slide <- doc$slide$get_slide(doc$cursor) xfrm <- slide$get_xfrm() args <- list(...) - layout <- ifelse(is.null(args$layout), unique( xfrm$name ), args$layout) - master <- ifelse(is.null(args$master), unique( xfrm$master_name ), args$master) - out <- get_ph_loc(doc, layout = layout, master = master, - type = x$type, position_right = x$position_right, - position_top = x$position_top, id = x$id) - if( !is.null(x$label) ) + layout <- ifelse(is.null(args$layout), unique(xfrm$name), args$layout) + master <- ifelse(is.null(args$master), unique(xfrm$master_name), args$master) + + # to avoid a breaking change, the deprecated id is passed along. + # As type_idx uses a different index order than id, this is necessary until the id arg is removed. + out <- get_ph_loc(doc, + layout = layout, master = master, + type = x$type, position_right = x$position_right, + position_top = x$position_top, type_idx = x$type_idx, + id = x$id, ph_id = NULL # id is deprecated and will be removed soon + ) + if (!is.null(x$label)) { out$ph_label <- x$label + } out - } + #' @export #' @title Location of a named placeholder #' @description The function will use the label of a placeholder @@ -314,6 +369,7 @@ ph_location_label <- function( ph_label, newlabel = NULL, ...){ x } + #' @export fortify_location.location_label <- function( x, doc, ...){ @@ -345,6 +401,7 @@ fortify_location.location_label <- function( x, doc, ...){ } + #' @export #' @title Location of a full size element #' @description The function will return the location corresponding diff --git a/R/ppt_class_dir_collection.R b/R/ppt_class_dir_collection.R index 5a242830..b88c7c96 100644 --- a/R/ppt_class_dir_collection.R +++ b/R/ppt_class_dir_collection.R @@ -7,7 +7,7 @@ dir_collection <- R6Class( dir_ <- file.path(package_dir, container$dir_name()) private$package_dir <- package_dir filenames <- list.files(path = dir_, pattern = "\\.xml$", full.names = TRUE) - filenames <- sort_vec_by_index(filenames) # see issue 596 + filenames <- sort_vec_by_index(filenames) # see issue #596 private$collection <- lapply( filenames, function(x, container){ container$clone()$feed(x) }, container = container) diff --git a/R/pptx_informations.R b/R/pptx_informations.R index ed52c788..a5f47997 100644 --- a/R/pptx_informations.R +++ b/R/pptx_informations.R @@ -7,7 +7,7 @@ #' my_pres <- add_slide(my_pres) #' my_pres <- add_slide(my_pres) #' length(my_pres) -#' @family functions for reading presentation informations +#' @family functions for reading presentation information length.rpptx <- function( x ){ x$slide$length() } @@ -22,7 +22,7 @@ length.rpptx <- function( x ){ #' my_pres <- add_slide(my_pres, #' layout = "Two Content", master = "Office Theme") #' slide_size(my_pres) -#' @family functions for reading presentation informations +#' @family functions for reading presentation information slide_size <- function(x) { pres <- x$presentation$get() dimensions <- xml_attrs(xml_find_first(pres, "p:sldSz")) @@ -35,45 +35,60 @@ slide_size <- function(x) { #' @export #' @title Presentation layouts summary -#' @description Get informations about slide layouts and +#' @description Get information about slide layouts and #' master layouts into a data.frame. This function returns #' a data.frame containing all layout and master names. #' @inheritParams length.rpptx #' @examples #' my_pres <- read_pptx() #' layout_summary ( x = my_pres ) -#' @family functions for reading presentation informations +#' @family functions for reading presentation information layout_summary <- function( x ){ data <- x$slideLayouts$get_metadata() data.frame(layout = data$name, master = data$master_name, stringsAsFactors = FALSE) } + #' @export #' @title Slide layout properties -#' @description Get information about a particular slide layout -#' into a data.frame. -#' @inheritParams length.rpptx -#' @param layout slide layout name to use -#' @param master master layout name where \code{layout} is located +#' @description Detailed information about the placeholders on the slide layouts (label, position, etc.). +#' See *Value* section below for more info. +#' @param x an `rpptx` object +#' @param layout slide layout name. If `NULL`, returns all layouts. +#' @param master master layout name where `layout` is located. If `NULL`, returns all masters. +#' @returns Returns a data frame with one row per placeholder and the following columns: +#' * `master_name`: Name of master (a `.pptx` file may have more than one) +#' * `name`: Name of layout +#' * `type`: Placeholder type +#' * `type_idx`: Running index for phs of the same type. Ordering by ph position +#' (top -> bottom, left -> right) +#' * `id`: A unique placeholder id (assigned by PowerPoint automatically, starts at 2, potentially non-consecutive) +#' * `ph_label`: Placeholder label (can be set by the user in PowerPoint) +#' * `ph`: Placholder XML fragment (usually not needed) +#' * `offx`,`offy`: placeholder's distance from left and top edge (in inch) +#' * `cx`,`cy`: width and height of placeholder (in inch) +#' * `rotation`: rotation in degrees +#' * `fld_id` is generally stored as a hexadecimal or GUID value +#' * `fld_type`: a unique identifier for a particular field +#' #' @examples #' x <- read_pptx() -#' layout_properties ( x = x, layout = "Title Slide", master = "Office Theme" ) -#' layout_properties ( x = x, master = "Office Theme" ) -#' layout_properties ( x = x, layout = "Two Content" ) -#' layout_properties ( x = x ) -#' @family functions for reading presentation informations -layout_properties <- function( x, layout = NULL, master = NULL ){ - +#' layout_properties(x = x, layout = "Title Slide", master = "Office Theme") +#' layout_properties(x = x, master = "Office Theme") +#' layout_properties(x = x, layout = "Two Content") +#' layout_properties(x = x) +#' @family functions for reading presentation information +layout_properties <- function(x, layout = NULL, master = NULL) { data <- x$slideLayouts$get_xfrm_data() - - if( !is.null(layout) && !is.null(master) ){ - data <- data[data$name == layout & data$master_name %in% master,] - } else if( is.null(layout) && !is.null(master) ){ - data <- data[data$master_name %in% master,] - } else if( !is.null(layout) && is.null(master) ){ - data <- data[data$name == layout,] + if (!is.null(layout) && !is.null(master)) { + data <- data[data$name == layout & data$master_name %in% master, ] + } else if (is.null(layout) && !is.null(master)) { + data <- data[data$master_name %in% master, ] + } else if (!is.null(layout) && is.null(master)) { + data <- data[data$name == layout, ] } - data <- data[,c("master_name", "name", "type", "id", "ph_label", "ph", "offx", "offy", "cx", "cy", "rotation", "fld_id", "fld_type")] + data <- data[, c("master_name", "name", "type", "type_idx", "id", "ph_label", "ph", + "offx", "offy", "cx", "cy", "rotation", "fld_id", "fld_type")] data[["offx"]] <- data[["offx"]] / 914400 data[["offy"]] <- data[["offy"]] / 914400 data[["cx"]] <- data[["cx"]] / 914400 @@ -83,56 +98,102 @@ layout_properties <- function( x, layout = NULL, master = NULL ){ data } + #' @export #' @title Slide layout properties plot -#' @description Plot slide layout properties and print informations -#' into defined placeholders. This can be useful to help -#' visualise placeholders locations and identifier. -#' @param x an rpptx object -#' @param layout slide layout name to use -#' @param master master layout name where \code{layout} is located -#' @param labels if \code{TRUE}, placeholder labels will be printed, if \code{FALSE} -#' placeholder types and identifiers will be printed. -#' @param title if \code{FALSE}, a title with the layout name will be printed. +#' @description Plot slide layout properties into corresponding placeholders. +#' This can be useful to help visualize placeholders locations and identifiers. +#' *All* information in the plot stems from the [layout_properties()] output. +#' See *Details* section for more info. +#' @details +#' The plot contains all relevant information to reference a placeholder via the `ph_location_*` +#' function family: +#' +#' * `label`: ph label (red, center) to be used in [ph_location_label()]. +#' _NB_: The label can be assigned by the user in PowerPoint. +#' * `type[idx]`: ph type + type index in brackets (blue, upper left) to be used in [ph_location_type()]. +#' _NB_: The index is consecutive and is sorted by ph position (top -> bottom, left -> right). +#' * `id`: ph id (green, upper right) to be used in `ph_location_id()` (forthcoming). +#' _NB_: The id is set by PowerPoint automatically and lack a meaningful order. +#' +#' @param x an `rpptx` object +#' @param layout slide layout name. +#' @param master master layout name where `layout` is located. +#' @param title if `TRUE` (default), adds a title with the layout name at the top. +#' @param labels if `TRUE` (default), adds placeholder labels (centered in *red*). +#' @param type if `TRUE` (default), adds the placeholder type and its index (in square brackets) +#' in the upper left corner (in *blue*). +#' @param id if `TRUE` (default), adds the placeholder's unique `id` (see column `id` from +#' [layout_properties()]) in the upper right corner (in *green*). +#' @param cex named list or vector to specify font size for `labels`, `type`, and `id`. Default is +#' `c(labels = .5, type = .5, id = .5)`. See [graphics::text()] for details on how `cex` works. #' @importFrom graphics plot rect text box #' @examples #' x <- read_pptx() #' plot_layout_properties(x = x, layout = "Title Slide", master = "Office Theme") #' plot_layout_properties(x = x, layout = "Two Content") -#' plot_layout_properties(x = x, layout = "Two Content", labels = FALSE) -#' @family functions for reading presentation informations +#' plot_layout_properties(x = x, layout = "Two Content", title = FALSE, type = FALSE, id = FALSE) +#' +#' # change font size +#' plot_layout_properties(x = x, layout = "Two Content", cex = c(labels = 1, id = .7, type = .7)) #' -plot_layout_properties <- function(x, layout = NULL, master = NULL, labels = TRUE, title = FALSE) { +#' @family functions for reading presentation information +#' +plot_layout_properties <- function(x, layout = NULL, master = NULL, labels = TRUE, title = TRUE, + type = TRUE, id = TRUE, cex = NULL) { old_par <- par(mar = c(2, 2, 1.5, 0)) on.exit(par(old_par)) + cex_default <- list(labels = .5, type = .5, id = .5) + cex_unknown <- setdiff(names(cex), names(cex_default)) + if (length(cex_unknown) > 0) { + cli::cli_abort(c("Unknown name {.val {cex_unknown}} in {.arg cex}", + "x" = "Allowed names are {.val {names(cex_default)}}", + "i" = cli::col_grey("{.arg cex} expects a named list or vector") + )) + } + .cex <- utils::modifyList(x = cex_default, val = as.list(cex), keep.null = TRUE) + dat <- layout_properties(x, layout = layout, master = master) - if (length(unique(dat$name)) != 1) { - stop("one single layout need to be choosen") + if (length(unique(dat$name)) > 1) { + cli::cli_abort(c("One single layout must be chosen", + "x" = "Did you supply a master?" + ), call = NULL) } - dat <- dat[order(dat$type, as.integer(dat$id)), ] # set order for type idx. Removing the line would result in the default layout properties order, i.e., top->bottom left->right. - dat$type_idx <- stats::ave(dat$type, dat$type, FUN = seq_along) # NB: returns character index + if (length(unique(dat$name)) < 1) { + cli::cli_abort(c("One layout must be chosen", + "x" = "Did you misspell the layout name?" + ), call = NULL) + } + # # order and type_idx now in xfrmize() + # dat <- dat[order(dat$type, as.integer(dat$id)), ] # set order for type idx. Removing the line would result in the default layout properties order, i.e., top->bottom left->right. + # dat$type_idx <- stats::ave(dat$type, dat$type, FUN = seq_along) # NB: returns character index s <- slide_size(x) h <- s$height w <- s$width + offx <- offy <- cx <- cy <- NULL # avoid R CMD CHECK problem list2env(dat[, c("offx", "offy", "cx", "cy")], environment()) # make available inside functions - if (labels) { - labels <- dat$ph_label - } else { - labels <- sprintf("type: '%s' - id: %s", dat$type, dat$type_idx) - } - plot(x = c(0, w), y = -c(0, h), asp = 1, type = "n", axes = FALSE, xlab = NA, ylab = NA) - if (title) { - title(main = paste("Layout:", layout)) - } rect(xleft = 0, xright = w, ybottom = 0, ytop = -h, border = "darkgrey") rect(xleft = offx, xright = offx + cx, ybottom = -offy, ytop = -(offy + cy)) - text(x = offx + cx / 2, y = -(offy + cy / 2), labels = labels, cex = 0.5, col = "red") mtext("y [inch]", side = 2, line = 0, cex = 1.2, col = "darkgrey") mtext("x [inch]", side = 1, line = 0, cex = 1.2, col = "darkgrey") + + if (title) { + title(main = paste("Layout:", layout)) + } + if (labels) { # centered + text(x = offx + cx / 2, y = -(offy + cy / 2), labels = dat$ph_label, cex = .cex$labels, col = "red", adj = c(.5, 1)) # adj-vert: avoid interference with type/id in small phs + } + if (type) { # upper left corner + .type_info <- paste0(dat$type, " [", dat$type_idx, "]") # type + index in brackets + text(x = offx, y = -offy, labels = .type_info, cex = .cex$type, col = "blue", adj = c(-.1, 1.2)) + } + if (id) { # upper right corner + text(x = offx + cx, y = -offy, labels = dat$id, cex = .cex$id, col = "darkgreen", adj = c(1.3, 1.2)) + } } @@ -158,7 +219,7 @@ plot_layout_properties <- function(x, layout = NULL, master = NULL, labels = TRU #' # annotated output in 'mydoc_annotate.pptx' #' # annotate_base(path = 'mydoc.pptx', output_file='mydoc_annotate.pptx') #' -#' @family functions for reading presentation informations +#' @family functions for reading presentation information annotate_base <- function(path = NULL, output_file = 'annotated_layout.pptx' ){ ppt <- read_pptx(path=path) while(length(ppt)>0){ @@ -225,7 +286,7 @@ annotate_base <- function(path = NULL, output_file = 'annotated_layout.pptx' ){ #' location = ph_location_type(type="body")) #' slide_summary(my_pres) #' slide_summary(my_pres, index = 1) -#' @family functions for reading presentation informations +#' @family functions for reading presentation information slide_summary <- function( x, index = NULL ){ l_ <- length(x) @@ -269,7 +330,7 @@ slide_summary <- function( x, index = NULL ){ #' @examples #' x <- read_pptx() #' color_scheme ( x = x ) -#' @family functions for reading presentation informations +#' @family functions for reading presentation information color_scheme <- function( x ){ x$masterLayouts$get_color_scheme() } diff --git a/R/read_docx.R b/R/read_docx.R index bd50ed4d..062fccc3 100644 --- a/R/read_docx.R +++ b/R/read_docx.R @@ -432,7 +432,7 @@ docx_set_character_style <- function(x, style_id, style_name, base_on, fp_t = fp #' doc_properties(x) #' @return a data.frame #' @family functions for Word document informations -#' @family functions for reading presentation informations +#' @family functions for reading presentation information doc_properties <- function(x) { if (inherits(x, "rdocx")) { cp <- x$doc_properties diff --git a/R/utils.R b/R/utils.R index 3e457ba3..43f9550d 100644 --- a/R/utils.R +++ b/R/utils.R @@ -78,6 +78,7 @@ fortify_master_xfrm <- function(master_xfrm) { master_xfrm } + xfrmize <- function(slide_xfrm, master_xfrm) { x <- as.data.frame(slide_xfrm) @@ -115,16 +116,16 @@ xfrmize <- function(slide_xfrm, master_xfrm) { x$fld_type_ref <- NULL x <- rbind(x, slide_xfrm_no_match, stringsAsFactors = FALSE) + i_master <- get_file_index(x$master_file) i_layout <- get_file_index(x$file) - x <- x[order(i_master, i_layout, x$offy, x$offx), , drop = FALSE] # natural sorting: top -> bottom, left -> right - x[ - !is.na(x$offx) & - !is.na(x$offy) & - !is.na(x$cx) & - !is.na(x$cy), - ] - rownames(x) <- NULL # no mixed up numbers + x <- x[order(i_master, i_layout, x$offy, x$offx), , drop = FALSE] # intuitive sorting: top -> bottom, left -> right + x <- x[!(is.na(x$offx) | is.na(x$offy) | is.na(x$cx) | is.na(x$cy)), ] + + x$type_idx <- stats::ave(x$type, x$master_file, x$file, x$type, FUN = seq_along) + x$type_idx <- as.numeric(x$type_idx) # NB: ave returns character + + rownames(x) <- NULL # prevent meaningless rownames x } diff --git a/man/annotate_base.Rd b/man/annotate_base.Rd index 9f1bf923..673ec5e4 100644 --- a/man/annotate_base.Rd +++ b/man/annotate_base.Rd @@ -34,7 +34,7 @@ annotate_base(output_file = tempfile(fileext = ".pptx")) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, \code{\link{layout_properties}()}, @@ -44,4 +44,4 @@ Other functions for reading presentation informations: \code{\link{slide_size}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/color_scheme.Rd b/man/color_scheme.Rd index 72f23246..d758710f 100644 --- a/man/color_scheme.Rd +++ b/man/color_scheme.Rd @@ -18,7 +18,7 @@ x <- read_pptx() color_scheme ( x = x ) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{doc_properties}()}, \code{\link{layout_properties}()}, @@ -28,4 +28,4 @@ Other functions for reading presentation informations: \code{\link{slide_size}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/doc_properties.Rd b/man/doc_properties.Rd index 5662f133..cd0389d2 100644 --- a/man/doc_properties.Rd +++ b/man/doc_properties.Rd @@ -28,7 +28,7 @@ Other functions for Word document informations: \code{\link{set_doc_properties}()}, \code{\link{styles_info}()} -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{layout_properties}()}, @@ -39,4 +39,4 @@ Other functions for reading presentation informations: \code{\link{slide_summary}()} } \concept{functions for Word document informations} -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/layout_properties.Rd b/man/layout_properties.Rd index ca1f6df2..781f2c61 100644 --- a/man/layout_properties.Rd +++ b/man/layout_properties.Rd @@ -7,25 +7,43 @@ layout_properties(x, layout = NULL, master = NULL) } \arguments{ -\item{x}{an rpptx object} +\item{x}{an \code{rpptx} object} -\item{layout}{slide layout name to use} +\item{layout}{slide layout name. If \code{NULL}, returns all layouts.} -\item{master}{master layout name where \code{layout} is located} +\item{master}{master layout name where \code{layout} is located. If \code{NULL}, returns all masters.} +} +\value{ +Returns a data frame with one row per placeholder and the following columns: +\itemize{ +\item \code{master_name}: Name of master (a \code{.pptx} file may have more than one) +\item \code{name}: Name of layout +\item \code{type}: Placeholder type +\item \code{type_idx}: Running index for phs of the same type. Ordering by ph position +(top -> bottom, left -> right) +\item \code{id}: A unique placeholder id (assigned by PowerPoint automatically, starts at 2, potentially non-consecutive) +\item \code{ph_label}: Placeholder label (can be set by the user in PowerPoint) +\item \code{ph}: Placholder XML fragment (usually not needed) +\item \code{offx},\code{offy}: placeholder's distance from left and top edge (in inch) +\item \code{cx},\code{cy}: width and height of placeholder (in inch) +\item \code{rotation}: rotation in degrees +\item \code{fld_id} is generally stored as a hexadecimal or GUID value +\item \code{fld_type}: a unique identifier for a particular field +} } \description{ -Get information about a particular slide layout -into a data.frame. +Detailed information about the placeholders on the slide layouts (label, position, etc.). +See \emph{Value} section below for more info. } \examples{ x <- read_pptx() -layout_properties ( x = x, layout = "Title Slide", master = "Office Theme" ) -layout_properties ( x = x, master = "Office Theme" ) -layout_properties ( x = x, layout = "Two Content" ) -layout_properties ( x = x ) +layout_properties(x = x, layout = "Title Slide", master = "Office Theme") +layout_properties(x = x, master = "Office Theme") +layout_properties(x = x, layout = "Two Content") +layout_properties(x = x) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, @@ -35,4 +53,4 @@ Other functions for reading presentation informations: \code{\link{slide_size}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/layout_summary.Rd b/man/layout_summary.Rd index eff7834c..05d2caa4 100644 --- a/man/layout_summary.Rd +++ b/man/layout_summary.Rd @@ -10,7 +10,7 @@ layout_summary(x) \item{x}{an rpptx object} } \description{ -Get informations about slide layouts and +Get information about slide layouts and master layouts into a data.frame. This function returns a data.frame containing all layout and master names. } @@ -19,7 +19,7 @@ my_pres <- read_pptx() layout_summary ( x = my_pres ) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, @@ -29,4 +29,4 @@ Other functions for reading presentation informations: \code{\link{slide_size}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/length.rpptx.Rd b/man/length.rpptx.Rd index 33581c6c..854fa5e9 100644 --- a/man/length.rpptx.Rd +++ b/man/length.rpptx.Rd @@ -19,7 +19,7 @@ my_pres <- add_slide(my_pres) length(my_pres) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, @@ -29,4 +29,4 @@ Other functions for reading presentation informations: \code{\link{slide_size}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/ph_location_type.Rd b/man/ph_location_type.Rd index 94067b58..49b7394c 100644 --- a/man/ph_location_type.Rd +++ b/man/ph_location_type.Rd @@ -6,6 +6,7 @@ \usage{ ph_location_type( type = "body", + type_idx = NULL, position_right = TRUE, position_top = TRUE, newlabel = NULL, @@ -17,6 +18,13 @@ ph_location_type( \item{type}{placeholder type to look for in the slide layout, one of 'body', 'title', 'ctrTitle', 'subTitle', 'dt', 'ftr', 'sldNum'.} +\item{type_idx}{Type index of the placeholder. If there is more than one +placeholder of a type (e.g., \code{body}), the type index can be supplied to uniquely +identify a ph. The index is a running number starting at 1. It is assigned by +placeholder position (top -> bottom, left -> right). See \code{\link[=plot_layout_properties]{plot_layout_properties()}} +for details. If \code{idx} argument is used, \code{position_right} and \code{position_top} +are ignored.} + \item{position_right}{the parameter is used when a selection with above parameters does not provide a unique position (for example layout 'Two Content' contains two element of type 'body'). @@ -28,10 +36,10 @@ to top versus bottom.} \item{newlabel}{a label to associate with the placeholder.} -\item{id}{index of the placeholder. If two body placeholder, there can be -two different index: 1 and 2 for the first and second body placeholders defined -in the layout. If this argument is used, \code{position_right} and \code{position_top} -will be ignored.} +\item{id}{(\strong{DEPRECATED, use \code{type_idx} instead}) Index of the placeholder. +If two body placeholder, there can be two different index: 1 and 2 for the +first and second body placeholders defined in the layout. If this argument +is used, \code{position_right} and \code{position_top} will be ignored.} \item{...}{unused arguments} } @@ -83,6 +91,7 @@ doc <- ph_with(x = doc, "Un titre", location = loc_ctrtitle) fileout <- tempfile(fileext = ".pptx") print(doc, target = fileout) + } \seealso{ Other functions for placeholder location: diff --git a/man/plot_layout_properties.Rd b/man/plot_layout_properties.Rd index 780a60e3..b7071f39 100644 --- a/man/plot_layout_properties.Rd +++ b/man/plot_layout_properties.Rd @@ -9,34 +9,62 @@ plot_layout_properties( layout = NULL, master = NULL, labels = TRUE, - title = FALSE + title = TRUE, + type = TRUE, + id = TRUE, + cex = NULL ) } \arguments{ -\item{x}{an rpptx object} +\item{x}{an \code{rpptx} object} -\item{layout}{slide layout name to use} +\item{layout}{slide layout name.} -\item{master}{master layout name where \code{layout} is located} +\item{master}{master layout name where \code{layout} is located.} -\item{labels}{if \code{TRUE}, placeholder labels will be printed, if \code{FALSE} -placeholder types and identifiers will be printed.} +\item{labels}{if \code{TRUE} (default), adds placeholder labels (centered in \emph{red}).} -\item{title}{if \code{FALSE}, a title with the layout name will be printed.} +\item{title}{if \code{TRUE} (default), adds a title with the layout name at the top.} + +\item{type}{if \code{TRUE} (default), adds the placeholder type and its index (in square brackets) +in the upper left corner (in \emph{blue}).} + +\item{id}{if \code{TRUE} (default), adds the placeholder's unique \code{id} (see column \code{id} from +\code{\link[=layout_properties]{layout_properties()}}) in the upper right corner (in \emph{green}).} + +\item{cex}{named list or vector to specify font size for \code{labels}, \code{type}, and \code{id}. Default is +\code{c(labels = .5, type = .5, id = .5)}. See \code{\link[graphics:text]{graphics::text()}} for details on how \code{cex} works.} } \description{ -Plot slide layout properties and print informations -into defined placeholders. This can be useful to help -visualise placeholders locations and identifier. +Plot slide layout properties into corresponding placeholders. +This can be useful to help visualize placeholders locations and identifiers. +\emph{All} information in the plot stems from the \code{\link[=layout_properties]{layout_properties()}} output. +See \emph{Details} section for more info. +} +\details{ +The plot contains all relevant information to reference a placeholder via the \verb{ph_location_*} +function family: +\itemize{ +\item \code{label}: ph label (red, center) to be used in \code{\link[=ph_location_label]{ph_location_label()}}. +\emph{NB}: The label can be assigned by the user in PowerPoint. +\item \code{type[idx]}: ph type + type index in brackets (blue, upper left) to be used in \code{\link[=ph_location_type]{ph_location_type()}}. +\emph{NB}: The index is consecutive and is sorted by ph position (top -> bottom, left -> right). +\item \code{id}: ph id (green, upper right) to be used in \code{ph_location_id()} (forthcoming). +\emph{NB}: The id is set by PowerPoint automatically and lack a meaningful order. +} } \examples{ x <- read_pptx() plot_layout_properties(x = x, layout = "Title Slide", master = "Office Theme") plot_layout_properties(x = x, layout = "Two Content") -plot_layout_properties(x = x, layout = "Two Content", labels = FALSE) +plot_layout_properties(x = x, layout = "Two Content", title = FALSE, type = FALSE, id = FALSE) + +# change font size +plot_layout_properties(x = x, layout = "Two Content", cex = c(labels = 1, id = .7, type = .7)) + } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, @@ -46,4 +74,4 @@ Other functions for reading presentation informations: \code{\link{slide_size}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/slide_size.Rd b/man/slide_size.Rd index 10ff378e..0d57e6ab 100644 --- a/man/slide_size.Rd +++ b/man/slide_size.Rd @@ -20,7 +20,7 @@ my_pres <- add_slide(my_pres, slide_size(my_pres) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, @@ -30,4 +30,4 @@ Other functions for reading presentation informations: \code{\link{plot_layout_properties}()}, \code{\link{slide_summary}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/man/slide_summary.Rd b/man/slide_summary.Rd index 577581b2..d6d3c5a9 100644 --- a/man/slide_summary.Rd +++ b/man/slide_summary.Rd @@ -34,7 +34,7 @@ slide_summary(my_pres) slide_summary(my_pres, index = 1) } \seealso{ -Other functions for reading presentation informations: +Other functions for reading presentation information: \code{\link{annotate_base}()}, \code{\link{color_scheme}()}, \code{\link{doc_properties}()}, @@ -44,4 +44,4 @@ Other functions for reading presentation informations: \code{\link{plot_layout_properties}()}, \code{\link{slide_size}()} } -\concept{functions for reading presentation informations} +\concept{functions for reading presentation information} diff --git a/tests/testthat/_snaps/pptx-info/plot-content-order-default.png b/tests/testthat/_snaps/pptx-info/plot-content-order-default.png new file mode 100644 index 00000000..ce77b9f3 Binary files /dev/null and b/tests/testthat/_snaps/pptx-info/plot-content-order-default.png differ diff --git a/tests/testthat/_snaps/pptx-info/plot-content-order-labels-only.png b/tests/testthat/_snaps/pptx-info/plot-content-order-labels-only.png new file mode 100644 index 00000000..4d0448f1 Binary files /dev/null and b/tests/testthat/_snaps/pptx-info/plot-content-order-labels-only.png differ diff --git a/tests/testthat/_snaps/pptx-info/plot-content-order-nolabel.png b/tests/testthat/_snaps/pptx-info/plot-content-order-nolabel.png deleted file mode 100644 index 421ddcfa..00000000 Binary files a/tests/testthat/_snaps/pptx-info/plot-content-order-nolabel.png and /dev/null differ diff --git a/tests/testthat/_snaps/pptx-info/plot-content-order.png b/tests/testthat/_snaps/pptx-info/plot-content-order.png deleted file mode 100644 index a9209c08..00000000 Binary files a/tests/testthat/_snaps/pptx-info/plot-content-order.png and /dev/null differ diff --git a/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-default.png b/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-default.png new file mode 100644 index 00000000..749c4e59 Binary files /dev/null and b/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-default.png differ diff --git a/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-labels-only.png b/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-labels-only.png new file mode 100644 index 00000000..6fa4f5c3 Binary files /dev/null and b/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-labels-only.png differ diff --git a/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-nolabel.png b/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-nolabel.png deleted file mode 100644 index cd255622..00000000 Binary files a/tests/testthat/_snaps/pptx-info/plot-titleslide-layout-nolabel.png and /dev/null differ diff --git a/tests/testthat/_snaps/pptx-info/plot-titleslide-layout.png b/tests/testthat/_snaps/pptx-info/plot-titleslide-layout.png deleted file mode 100644 index 97bb7d4f..00000000 Binary files a/tests/testthat/_snaps/pptx-info/plot-titleslide-layout.png and /dev/null differ diff --git a/tests/testthat/test-pptx-add.R b/tests/testthat/test-pptx-add.R index adbc857d..a99a6b8a 100644 --- a/tests/testthat/test-pptx-add.R +++ b/tests/testthat/test-pptx-add.R @@ -4,6 +4,7 @@ test_that("add wrong arguments", { expect_error(add_slide(doc, "Title and Content", "Office Tddheme"), fixed = TRUE) }) + test_that("add simple elements into placeholder", { skip_if_not_installed("doconv") skip_if_not(doconv::msoffice_available()) @@ -19,6 +20,7 @@ test_that("add simple elements into placeholder", { expect_snapshot_doc(x = doc, name = "pptx-add-simple", engine = "testthat") }) + test_that("add ggplot into placeholder", { skip_if_not_installed("doconv") skip_if_not_installed("ggplot2") @@ -45,6 +47,8 @@ test_that("add ggplot into placeholder", { ) expect_snapshot_doc(x = doc, name = "pptx-add-ggplot2", engine = "testthat") }) + + test_that("add base plot into placeholder", { skip_if_not_installed("doconv") skip_if_not(doconv::msoffice_available()) @@ -63,6 +67,7 @@ test_that("add base plot into placeholder", { expect_snapshot_doc(x = doc, name = "pptx-add-barplot", engine = "testthat") }) + test_that("add unordered_list into placeholder", { skip_if_not_installed("doconv") skip_if_not(doconv::msoffice_available()) @@ -87,6 +92,7 @@ test_that("add unordered_list into placeholder", { expect_snapshot_doc(x = doc, name = "pptx-add-ul", engine = "testthat") }) + test_that("add block_list into placeholder", { skip_if_not_installed("doconv") skip_if_not(doconv::msoffice_available()) @@ -114,6 +120,7 @@ test_that("add block_list into placeholder", { expect_snapshot_doc(x = doc, name = "pptx-add-blocklist", engine = "testthat") }) + test_that("add formatted par into placeholder", { bold_face <- shortcuts$fp_bold(font.size = 30) bold_redface <- update(bold_face, color = "red") @@ -152,6 +159,7 @@ test_that("add xml into placeholder", { expect_equal(sm[2, ]$text, "Hello world 1") }) + test_that("slidelink shape", { doc <- read_pptx() doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme") @@ -337,18 +345,32 @@ test_that("pptx ph locations", { test_that("pptx ph_location_type", { - opts <- options(cli.num_colors = 1) # suppress colors to check error message + opts <- options(cli.num_colors = 1) # suppress colors for easier error message check on.exit(options(opts)) x <- read_pptx() x <- x |> add_slide("Two Content") expect_no_error({ - x |> ph_with("correct ph type id", ph_location_type("body", id = 1)) + x |> ph_with("correct ph type id", ph_location_type("body", type_idx = 1)) }) + expect_warning({ + x |> ph_with("cannot supply id AND type_idx", ph_location_type("body", type_idx = 1, id = 1)) + }, regexp = "`id` is ignored if `type_idx` is provided", fixed = TRUE) + + expect_warning({ + x |> ph_with("id still working with warning to avoid breaking change", ph_location_type("body", id = 1)) + }, regexp = "The `id` argument in `ph_location_type()` is deprecated", fixed = TRUE) + + expect_error({ + x |> ph_with("out of range type id", ph_location_type("body", type_idx = 3)) # 3 does not exists => no error or warning + }, regexp = "`type_idx` is out of range.", fixed = TRUE) + expect_error({ + expect_warning({ x |> ph_with("out of range type id", ph_location_type("body", id = 3)) # 3 does not exists => no error or warning + }, regexp = " The `id` argument in `ph_location_type()` is deprecated", fixed = TRUE) }, regexp = "`id` is out of range.", fixed = TRUE) expect_error({ @@ -358,6 +380,10 @@ test_that("pptx ph_location_type", { expect_error({ x |> ph_with("xxx is unknown type", ph_location_type("xxx")) }, regexp = 'type "xxx" is unknown', fixed = TRUE) + + expect_no_error({ # for complete coverage + x |> ph_with(" ph type position_right", ph_location_type("body", position_right = TRUE)) + }) }) @@ -393,8 +419,42 @@ test_that("pptx ph labels", { xml_attr(all_nvpr, "name"), paste0("label", 1:4) ) + + expect_error({ + doc <- ph_with( + x = doc, value = "error if label does not exist", + location = ph_location_label(ph_label = "xxx") + ) + }) +}) + + +test_that("as_ph_location", { + ref_names <- c("width", "height", "left", "top", "ph_label", "ph", "type", "rotation", "fld_id", "fld_type") + l <- replicate(length(ref_names), "dummy", simplify = FALSE) + df <- as.data.frame(l) + names(df) <- ref_names + + expect_no_error({ + as_ph_location(df) + }) + + expect_error({ + as_ph_location(df[, -(1:2)]) + }, regexp = "missing column values:width,height", fixed = TRUE) + + expect_error({ + as_ph_location("wrong class supplied") + }, regexp = "`x` must be a data frame", fixed = TRUE) }) +test_that("get_ph_loc", { + x <- read_pptx() + get_ph_loc(x, "Comparison", "Office Theme", type = "body", + position_right = TRUE, position_top = FALSE) + +}) + unlink("*.pptx") diff --git a/tests/testthat/test-pptx-info.R b/tests/testthat/test-pptx-info.R index dc4c68df..adf1635b 100644 --- a/tests/testthat/test-pptx-info.R +++ b/tests/testthat/test-pptx-info.R @@ -87,22 +87,20 @@ test_that("plot layout properties", { png1 <- tempfile(fileext = ".png") png(png1, width = 7, height = 6, res = 150, units = "in") plot_layout_properties( - x = x, layout = "Title Slide", - master = "Office Theme" + x = x, layout = "Title Slide", master = "Office Theme" ) dev.off() png2 <- tempfile(fileext = ".png") png(png2, width = 7, height = 6, res = 150, units = "in") plot_layout_properties( - x = x, layout = "Title Slide", - master = "Office Theme", - labels = FALSE + x = x, layout = "Title Slide", master = "Office Theme", + labels = TRUE, type = FALSE, id = FALSE, title = FALSE ) dev.off() - expect_snapshot_doc(name = "plot-titleslide-layout", x = png1, engine = "testthat") - expect_snapshot_doc(name = "plot-titleslide-layout-nolabel", x = png2, engine = "testthat") + expect_snapshot_doc(name = "plot-titleslide-layout-default", x = png1, engine = "testthat") + expect_snapshot_doc(name = "plot-titleslide-layout-labels-only", x = png2, engine = "testthat") # issue #604 p <- test_path("docs_dir/test-content-order.pptx") @@ -111,23 +109,20 @@ test_that("plot layout properties", { png3 <- tempfile(fileext = ".png") png(png3, width = 7, height = 6, res = 150, units = "in") plot_layout_properties( - x = x, layout = "Many Contents", - master = "Office Theme", - labels = TRUE + x = x, layout = "Many Contents", master = "Office Theme" ) dev.off() png4 <- tempfile(fileext = ".png") png(png4, width = 7, height = 6, res = 150, units = "in") plot_layout_properties( - x = x, layout = "Many Contents", - master = "Office Theme", - labels = FALSE + x = x, layout = "Many Contents", master = "Office Theme", + labels = TRUE, type = FALSE, id = FALSE, title = FALSE ) dev.off() - expect_snapshot_doc(name = "plot-content-order", x = png3, engine = "testthat") - expect_snapshot_doc(name = "plot-content-order-nolabel", x = png4, engine = "testthat") + expect_snapshot_doc(name = "plot-content-order-default", x = png3, engine = "testthat") + expect_snapshot_doc(name = "plot-content-order-labels-only", x = png4, engine = "testthat") })