From 77a75cee399575e231a0dfb2fdd1b1e2f146e4ef Mon Sep 17 00:00:00 2001 From: Daniel Sjoberg Date: Sun, 5 Jul 2026 20:47:54 +0000 Subject: [PATCH] Add pr_section for fine-grained Word section control in save_flex_docx() Add a pr_section argument (and matching save_flex_docx-lst:pr_section theme element) accepting an officer::prop_section() to control page margins, page size, orientation, and section columns. The header/footer regions remain managed by save_flex_docx() (relocated caption/notes), so any header_default/footer_default on pr_section are ignored. For collections the same section is applied to every table with the paging type fixed to 'nextPage' to avoid blank pages. The argument overrides the theme element. Co-authored-by: Ona --- NEWS.md | 2 +- R/save_flex_docx.R | 79 ++++++++++++++-- R/sysdata.rda | Bin 20133 -> 20321 bytes data-raw/gtsummary_theme_elements.csv | 1 + man/save_flex_docx.Rd | 23 +++++ tests/testthat/test-save_flex_docx.R | 131 ++++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 9 deletions(-) diff --git a/NEWS.md b/NEWS.md index d3bb912b7..4fc869ca2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # gtsummary (development version) -* Added `save_flex_docx()` to save a gtsummary table or a flextable to a Word (`.docx`) file via flextable. The `header` and `footer` arguments place the table caption in the Word document's page header and the footnotes, source notes, and abbreviations in the page footer (in-cell footnote reference symbols are retained). The `page` and `page_location` arguments add a page-number line (e.g. `"Page {PAGE} of {NUMPAGES}"`) to a chosen header/footer region and alignment. A collection of tables is also accepted—a `tbl_split` object (from `tbl_split_by_rows()`/`tbl_split_by_columns()`) or a plain list of flextables—writing each table to its own Word section and page. For a flextable, its caption (`flextable::set_caption()`) is relocated to the Word header and its footer part (`flextable::add_footer_lines()`) to the Word footer. The caption, footnotes, source notes, abbreviations, and page-number line in the Word header/footer match the table body font (family and size) instead of the Word template default. By default each Word region also inherits the styling applied to the corresponding flextable part, so e.g. `flextable::fontsize(size = 6, part = "footer")` yields a size-6 Word footer. The `header_style` and `footer_style` arguments (and the `save_flex_docx-lst:header_style`/`save_flex_docx-lst:footer_style` theme elements) accept named `officer::fp_text()` property lists to style the Word header and footer regions separately, overriding the inherited styling; the theme elements apply when the part carries no styling to inherit. +* Added `save_flex_docx()` to save a gtsummary table or a flextable to a Word (`.docx`) file via flextable. The `header` and `footer` arguments place the table caption in the Word document's page header and the footnotes, source notes, and abbreviations in the page footer (in-cell footnote reference symbols are retained). The `page` and `page_location` arguments add a page-number line (e.g. `"Page {PAGE} of {NUMPAGES}"`) to a chosen header/footer region and alignment. A collection of tables is also accepted—a `tbl_split` object (from `tbl_split_by_rows()`/`tbl_split_by_columns()`) or a plain list of flextables—writing each table to its own Word section and page. For a flextable, its caption (`flextable::set_caption()`) is relocated to the Word header and its footer part (`flextable::add_footer_lines()`) to the Word footer. The caption, footnotes, source notes, abbreviations, and page-number line in the Word header/footer match the table body font (family and size) instead of the Word template default. By default each Word region also inherits the styling applied to the corresponding flextable part, so e.g. `flextable::fontsize(size = 6, part = "footer")` yields a size-6 Word footer. The `header_style` and `footer_style` arguments (and the `save_flex_docx-lst:header_style`/`save_flex_docx-lst:footer_style` theme elements) accept named `officer::fp_text()` property lists to style the Word header and footer regions separately, overriding the inherited styling; the theme elements apply when the part carries no styling to inherit. The `pr_section` argument (and the `save_flex_docx-lst:pr_section` theme element) accepts an `officer::prop_section()` object for fine-grained control of the Word section—page margins, page size, orientation, and columns—while the header/footer regions remain managed by `save_flex_docx()`; for a collection the same section is applied to every table with the paging `type` fixed to `"nextPage"`. * `modify_abbreviation()` and `remove_abbreviation()` now accept a character vector of abbreviations, allowing multiple abbreviations to be added or removed in a single call. `modify_abbreviation()` also gains `prefix`, `sep1`, and `sep2` arguments to customize the abbreviation source note's leading text (e.g. `c("Abbr.", "Abbrs.")`), the separator between the prefix and the abbreviations (e.g. `": "`), and the separator between abbreviations (e.g. `"; "`). Defaults are also configurable via the `modify_abbreviation-arg:prefix`, `modify_abbreviation-arg:sep1`, and `modify_abbreviation-arg:sep2` theme elements. (#2172) diff --git a/R/save_flex_docx.R b/R/save_flex_docx.R index d17823bfc..784c1932f 100644 --- a/R/save_flex_docx.R +++ b/R/save_flex_docx.R @@ -69,6 +69,18 @@ #' styling to inherit (e.g. an empty footer). The page-number line adopts the #' style of whichever region it is placed in (via `page_location`). Default is #' `NULL`. +#' @param pr_section (`officer::prop_section`)\cr +#' an optional [`officer::prop_section()`] object used as the base Word section, +#' giving fine-grained control over page margins, page size, orientation, and +#' section columns (e.g. +#' `officer::prop_section(page_margins = officer::page_mar(top = 0.5))`). The +#' section's header and footer regions are always managed by `save_flex_docx()` +#' (the relocated caption and notes), so any `header_default`/`footer_default` +#' set on `pr_section` are ignored. For a collection (`tbl_split` or a list of +#' flextables) the same `pr_section` is applied to every table's section, and +#' the paging `type` is fixed to `"nextPage"` (any `type` on `pr_section` is +#' ignored) so tables page correctly. Overrides the +#' `save_flex_docx-lst:pr_section` theme element. Default is `NULL`. #' @param ... These dots are for future extensions and must be empty. #' #' @export @@ -104,6 +116,15 @@ #' as_flex_table(tbl) |> #' flextable::set_caption("Table 1") #' save_flex_docx(ft, path = tempfile(fileext = ".docx")) +#' +#' # customize the Word page margins via a prop_section() +#' save_flex_docx( +#' tbl, +#' path = tempfile(fileext = ".docx"), +#' pr_section = officer::prop_section( +#' page_margins = officer::page_mar(top = 0.5, bottom = 0.5) +#' ) +#' ) save_flex_docx <- function(x, path, header = TRUE, @@ -115,6 +136,7 @@ save_flex_docx <- function(x, ), header_style = NULL, footer_style = NULL, + pr_section = NULL, ...) { set_cli_abort_call() @@ -146,6 +168,18 @@ save_flex_docx <- function(x, .check_flex_docx_style(footer_style, "footer_style") check_pkg_installed(c("flextable", "officer")) + # resolve `pr_section` with argument-over-theme precedence, then validate. the + # resolved base section controls page margins/size/orientation/columns; its + # header/footer defaults are later overwritten by the relocated caption/notes. + pr_section <- pr_section %||% get_theme_element("save_flex_docx-lst:pr_section", eval = TRUE) + if (!is.null(pr_section) && !inherits(pr_section, "prop_section")) { + cli::cli_abort( + "The {.arg pr_section} argument must be an {.cls officer::prop_section} + object (e.g. from {.fn officer::prop_section}) or {.code NULL}.", + call = get_cli_abort_call() + ) + } + # collections: one section (with its own header/footer) per table ------------ if (inherits(x, "tbl_split") || is_flextable_list) { if (length(x) == 0L) { @@ -163,7 +197,8 @@ save_flex_docx <- function(x, page = page, page_location = page_location, header_style = header_style, - footer_style = footer_style + footer_style = footer_style, + pr_section = pr_section ) ) } @@ -181,13 +216,19 @@ save_flex_docx <- function(x, ) # write the Word file -------------------------------------------------------- - # `prop_section()` is only supplied when a region has content; otherwise - # `save_as_docx()` uses its default section. - if (length(built$header_fpars) > 0L || length(built$footer_fpars) > 0L) { + # a section is supplied when a region has content, or when the caller passed a + # `pr_section` (so custom page margins/size apply even with no caption/notes). + # otherwise `save_as_docx()` uses its default section. + has_content <- length(built$header_fpars) > 0L || length(built$footer_fpars) > 0L + if (has_content || !is.null(pr_section)) { flextable::save_as_docx( built$ft, path = path, - pr_section = .flex_docx_prop_section(built$header_fpars, built$footer_fpars) + pr_section = .flex_docx_prop_section( + built$header_fpars, + built$footer_fpars, + base = pr_section + ) ) } else { flextable::save_as_docx(built$ft, path = path) @@ -207,7 +248,8 @@ save_flex_docx <- function(x, #' @keywords internal #' @noRd .save_flex_docx_collection <- function(x, path, header, footer, page, page_location, - header_style = NULL, footer_style = NULL) { + header_style = NULL, footer_style = NULL, + pr_section = NULL) { doc <- officer::read_docx() for (i in seq_along(x)) { @@ -224,10 +266,14 @@ save_flex_docx <- function(x, doc <- flextable::body_add_flextable(doc, built$ft) + # every section uses the same base `pr_section` (page margins/size/etc.), but + # `type = "nextPage"` is forced so tables page correctly without blank pages, + # overriding any `type` set in the user's `pr_section`. section <- .flex_docx_prop_section( built$header_fpars, built$footer_fpars, + base = pr_section, type = "nextPage" ) @@ -369,11 +415,28 @@ save_flex_docx <- function(x, #' @param header_fpars,footer_fpars (`list`)\cr lists of `officer::fpar` objects #' @param ... additional arguments passed to `officer::prop_section()` (e.g. #' `type`) +#' @param base (`officer::prop_section` or `NULL`)\cr an optional user-supplied +#' section whose properties (page margins, size, orientation, columns, and +#' `type`) are used as the base. Its `header_default`/`footer_default` are +#' always discarded: `save_flex_docx()` owns those regions. #' @return an `officer::prop_section` object #' @keywords internal #' @noRd -.flex_docx_prop_section <- function(header_fpars, footer_fpars, ...) { - section_args <- list(...) +.flex_docx_prop_section <- function(header_fpars, footer_fpars, base = NULL, ...) { + # start from the user's base section fields (dropping its header/footer + # defaults, which we always own), then let `...` overrides win (e.g. the forced + # `type = "nextPage"` for collections), and finally attach our relocated + # caption/notes as the header/footer defaults. + section_args <- list() + if (!is.null(base)) { + base_fields <- unclass(base) + base_fields[c( + "header_default", "header_even", "header_first", + "footer_default", "footer_even", "footer_first" + )] <- NULL + section_args <- base_fields + } + section_args <- utils::modifyList(section_args, list(...)) if (length(header_fpars) > 0L) { section_args$header_default <- do.call(officer::block_list, header_fpars) } diff --git a/R/sysdata.rda b/R/sysdata.rda index 150d851360a98b51abbb5e5e4b5ebe5eee4bad59..cb4b89f2d209568e5e91c0dcfa873540eac07102 100644 GIT binary patch literal 20321 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%0Dp2v^0)oy2CUWyS!=V%2Pz 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 flextable::set_caption("Table 1") save_flex_docx(ft, path = tempfile(fileext = ".docx")) + +# customize the Word page margins via a prop_section() +save_flex_docx( + tbl, + path = tempfile(fileext = ".docx"), + pr_section = officer::prop_section( + page_margins = officer::page_mar(top = 0.5, bottom = 0.5) + ) +) \dontshow{\}) # examplesIf} } \seealso{ diff --git a/tests/testthat/test-save_flex_docx.R b/tests/testthat/test-save_flex_docx.R index 5111d1a29..bc6cd81ba 100644 --- a/tests/testthat/test-save_flex_docx.R +++ b/tests/testthat/test-save_flex_docx.R @@ -650,3 +650,134 @@ test_that("save_flex_docx(list) inherits footer styling per section", { footer_parts, \(f) grepl("w:sz w:val=\"12\"", read_part(path, f)), logical(1) ))) }) + +# pr_section (fine-grained Word section control) ------------------------------ +test_that("save_flex_docx(pr_section) applies custom page margins, keeps caption/notes relocated", { + path <- withr::local_tempfile(fileext = ".docx") + save_flex_docx( + tbl, + path = path, + pr_section = officer::prop_section( + page_margins = officer::page_mar(top = 0.5, bottom = 0.5) + ) + ) + + body <- read_docx_part(path, "body") + # 0.5in = 720 twips + expect_match(body, "w:top=\"720\"") + expect_match(body, "w:bottom=\"720\"") + # caption still in header, notes still in footer + expect_match(read_docx_part(path, "header"), "Patient characteristics") + expect_match(read_docx_part(path, "footer"), "Data from the trial dataset") +}) + +test_that("save_flex_docx(pr_section) header/footer defaults are overridden by ours", { + path <- withr::local_tempfile(fileext = ".docx") + save_flex_docx( + tbl, + path = path, + pr_section = officer::prop_section( + page_margins = officer::page_mar(top = 0.5), + header_default = officer::block_list(officer::fpar(officer::ftext("USER HEADER"))), + footer_default = officer::block_list(officer::fpar(officer::ftext("USER FOOTER"))) + ) + ) + + # the function's relocated caption/notes win; the user's header/footer are dropped + expect_match(read_docx_part(path, "header"), "Patient characteristics") + expect_no_match(read_docx_part(path, "header"), "USER HEADER") + expect_match(read_docx_part(path, "footer"), "Data from the trial dataset") + expect_no_match(read_docx_part(path, "footer"), "USER FOOTER") +}) + +test_that("save_flex_docx(pr_section) applies even when there is no caption/footer content", { + path <- withr::local_tempfile(fileext = ".docx") + plain <- trial |> + tbl_summary(include = age) |> + remove_footnote_header(columns = everything()) + save_flex_docx( + plain, + path = path, + header = FALSE, + footer = FALSE, + pr_section = officer::prop_section( + page_margins = officer::page_mar(top = 0.5) + ) + ) + # the section (and its margins) is written even with no header/footer content + expect_match(read_docx_part(path, "body"), "w:top=\"720\"") +}) + +test_that("save_flex_docx(pr_section) validates the class", { + path <- withr::local_tempfile(fileext = ".docx") + expect_error( + save_flex_docx(tbl, path = path, pr_section = list(page_margins = 1)), + "prop_section" + ) +}) + +test_that("save_flex_docx(tbl_split, pr_section) applies to every section, forcing nextPage", { + path <- withr::local_tempfile(fileext = ".docx") + save_flex_docx( + split_obj, + path = path, + # user sets `type = "continuous"`, which must be ignored between tables + pr_section = officer::prop_section( + page_margins = officer::page_mar(top = 0.5), + type = "continuous" + ) + ) + + body <- read_docx_part(path, "body") + n_tables <- length(split_obj) + # one section per table, all forced to nextPage (user's `continuous` ignored) + expect_equal(length(gregexpr("\\s*") +}) + +test_that("save_flex_docx(list, pr_section) applies custom margins to every section", { + path <- withr::local_tempfile(fileext = ".docx") + ft <- as_flex_table(tbl) |> flextable::set_caption("Cap") + save_flex_docx( + list(ft, ft), + path = path, + pr_section = officer::prop_section( + page_margins = officer::page_mar(top = 0.5) + ) + ) + body <- read_docx_part(path, "body") + expect_equal(length(gregexpr("w:top=\"720\"", body)[[1]]), 2L) +}) + +test_that("save_flex_docx-lst:pr_section theme element applies and the argument overrides it", { + # theme sets top margin 0.5in (720 twips) + path <- withr::local_tempfile(fileext = ".docx") + with_gtsummary_theme( + list("save_flex_docx-lst:pr_section" = officer::prop_section( + page_margins = officer::page_mar(top = 0.5) + )), + save_flex_docx(tbl, path = path) + ) + expect_match(read_docx_part(path, "body"), "w:top=\"720\"") + + # the argument (top = 1in = 1440 twips) overrides the theme + path2 <- withr::local_tempfile(fileext = ".docx") + with_gtsummary_theme( + list("save_flex_docx-lst:pr_section" = officer::prop_section( + page_margins = officer::page_mar(top = 0.5) + )), + save_flex_docx( + tbl, + path = path2, + pr_section = officer::prop_section(page_margins = officer::page_mar(top = 1)) + ) + ) + body2 <- read_docx_part(path2, "body") + expect_match(body2, "w:top=\"1440\"") + expect_no_match(body2, "w:top=\"720\"") +})