From a8d1bf9cf9c5cf6c72efb85b23b7384b94899c9a Mon Sep 17 00:00:00 2001 From: Gabe Becker Date: Wed, 29 May 2024 12:29:24 -0700 Subject: [PATCH 01/12] Changes from external PR for truetype font --- DESCRIPTION | 6 +- NEWS.md | 13 ++- R/00tabletrees.R | 21 +++- R/make_split_fun.R | 90 +++++++++++---- R/make_subset_expr.R | 16 ++- R/split_funs.R | 14 ++- R/tree_accessors.R | 15 ++- R/tt_dotabulation.R | 6 +- R/tt_paginate.R | 164 +++++++++++++++++++-------- R/tt_toString.R | 27 ++++- inst/WORDLIST | 64 ++++++----- man/build_table.Rd | 2 +- man/formatters_methods.Rd | 32 ++++-- man/int_methods.Rd | 9 ++ man/make_split_fun.Rd | 20 ++-- man/make_split_result.Rd | 40 ++++++- man/matrix_form-VTableTree-method.Rd | 10 +- man/paginate.Rd | 9 ++ man/tostring.Rd | 11 +- tests/testthat/test-exporters.R | 16 ++- tests/testthat/test-pagination.R | 22 +++- tests/testthat/test-split_funs.R | 29 ++++- 22 files changed, 477 insertions(+), 159 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 04b5dcf41..4dee1b2dd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -29,10 +29,10 @@ URL: https://github.com/insightsengineering/rtables, https://insightsengineering.github.io/rtables/ BugReports: https://github.com/insightsengineering/rtables/issues Depends: - formatters (>= 0.5.6), + R (>= 2.10), + formatters (>= 0.5.6.9007), magrittr (>= 1.5), - methods, - R (>= 2.10) + methods Imports: checkmate (>= 2.1.0), htmltools (>= 0.5.4), diff --git a/NEWS.md b/NEWS.md index d4347a482..421996f04 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,13 @@ * Added `top_level_section_div` for `basic_table` to set section dividers for top level rows. * Added `keep_label_rows` to `as_result_df` to have these lines visible. * `sort_at_path` now gives informative error messages when the given path does not exist. + * Add support for truetype fonts based on formatters `>= 0.5.6.9007`. Nearly all functions related to pagination or export now accept `fontspec` argument and pass it around accordingly. By @gmbecker + * Core splitting machinery can now be overridden in column space via `make_split_fun` provided that `core_split` associates the generated facets with subsetting expressions. Subsetting expressions remain unnecessary for splits in row space. By @gmbecker. + * ValueWrapper objects now carry around subsetting expressions for use during tabulation, by @gmbecker. + * `make_split_res`, `add_to_split_result` now accept a list of subsetting expressions which will be attached to the values, by @gmbecker. + * New `value_expr` internal getter and setter methods, by @gmbecker. + + ### Bug Fixes * Fixed `rlistings` decoration (e.g. titles and footers) expansion when there are new lines. Moved relevant handling from `rtables`' `matrix_form` function to `formatters`' dedicated `mform_handle_newlines` function. @@ -12,11 +19,13 @@ * Fixed `section_div` for analysis of multiple variables (`AnalyzeMultiVars`). * Fixed mismatch between indentation declared in row info (`mf_rinfo(mf)`) and actual selected indentation from `matrix_form(mf, indent_rownames = FALSE)`. * Fixed bug in `as_html` preventing indentation from being applied in `Viewer` output. + * `col_counts<-` and `col_total<-` methods now explicitly convert `value` to integer, by @gmbecker. + * `col_gap` is now respected in `nlines` row methods, and thus by `make_row_df`, by @gmbecker. + ### Miscellaneous * Removed deprecated functions `add_analyzed_var` and `trim_zero_rows`. - * Added `lifecycle` badge files for documentation. - * Deprecated the `gap` and `check_headers` arguments to `rbindl_rtables` using `lifecycle`. + * Updated versioned dependency on `formatters`to `0.5.6.9007` ## rtables 0.6.6 ### New Features diff --git a/R/00tabletrees.R b/R/00tabletrees.R index d80ad5421..01aaf4577 100644 --- a/R/00tabletrees.R +++ b/R/00tabletrees.R @@ -64,10 +64,12 @@ setClassUnion("functionOrNULL", c("NULL", "function")) setClassUnion("listOrNULL", c("NULL", "list")) ## TODO (?) make "list" more specific, e.g FormatList, or FunctionList? setClassUnion("FormatSpec", c("NULL", "character", "function", "list")) +setClassUnion("ExprOrNULL", c("NULL", "expression")) setClass("ValueWrapper", representation( value = "ANY", - label = "characterOrNULL" + label = "characterOrNULL", + subset_expression = "ExprOrNULL" ), contains = "VIRTUAL" ) @@ -80,7 +82,7 @@ setClass("SplitValue", representation(extra = "list") ) -SplitValue <- function(val, extr = list(), label = val) { +SplitValue <- function(val, extr = list(), label = val, sub_expr = NULL) { if (is(val, "SplitValue")) { if (length(splv_extra(val)) > 0) { extr <- c(splv_extra(val), extr) @@ -94,10 +96,15 @@ SplitValue <- function(val, extr = list(), label = val) { if (!is(label, "character")) { label <- as.character(label) } + + if (!is.null(sub_expr) && !is.expression(sub_expr)) + sub_expr <- as.expression(sub_expr) ## sometimes they will be "call" objects, etc check_ok_label(label) new("SplitValue", value = val, - extra = extr, label = label + extra = extr, + label = label, + subset_expression = sub_expr ) } @@ -107,13 +114,14 @@ setClass("LevelComboSplitValue", ) ## wrapped in user-facing `add_combo_facet` -LevelComboSplitValue <- function(val, extr, combolevels, label = val) { +LevelComboSplitValue <- function(val, extr, combolevels, label = val, sub_expr = NULL) { check_ok_label(label) new("LevelComboSplitValue", value = val, extra = extr, combolevels = combolevels, - label = label + label = label, + subset_expression = sub_expr ) } @@ -955,7 +963,7 @@ TreePos <- function(spls = list(), svlabels = character(), sub = NULL) { check_ok_label(svlabels, multi_ok = TRUE) - svals <- make_splvalue_vec(vals = svals) + svals <- make_splvalue_vec(vals = svals, subset_exprs = lapply(svals, value_expr)) if (is.null(sub)) { if (length(spls) > 0) { sub <- make_pos_subset( @@ -992,6 +1000,7 @@ make_child_pos <- function(parpos, svlabels = c(pos_splval_labels(parpos), newlab), sub = .combine_subset_exprs( pos_subset(parpos), + ## this will grab the value's custom subset expression if present make_subset_expr(newspl, nsplitval) ) ) diff --git a/R/make_split_fun.R b/R/make_split_fun.R index e1d97384d..4bd01df08 100644 --- a/R/make_split_fun.R +++ b/R/make_split_fun.R @@ -81,6 +81,8 @@ validate_split_result <- function(pinfo, component = NULL) { #' @param labels (`character`)\cr the labels associated with each facet. #' @param extras (`list` or `NULL`)\cr extra values associated with each of the facets which will be passed to #' analysis functions applied within the facet. +#' @param subset_exprs (`list`)\cr A list of subsetting expressions (e.g., +#' created with `quote()`) to be used during column subsetting. #' #' @return A named list representing the facets generated by the split with elements `values`, `datasplit`, and #' `labels`, which are the same length and correspond to each other element-wise. @@ -89,27 +91,37 @@ validate_split_result <- function(pinfo, component = NULL) { #' These functions performs various housekeeping tasks to ensure that the split result list is as the rtables #' internals expect it, most of which are not relevant to end users. #' +#' @note Column splitting will not work correctly if a split function +#' calls `make_split_result` without specifying subset expressions; +#' row splitting will work as normal. This is due to the fact that +#' subsetting expressions are used during column splitting to +#' represent the data associated with facets, while actual data +#' subsets are used during row splitting. +#' #' @examples #' splres <- make_split_result( #' values = c("hi", "lo"), #' datasplit = list(hi = mtcars, lo = mtcars[1:10, ]), -#' labels = c("more data", "less data") +#' labels = c("more data", "less data"), +#' subset_exprs = list(expression(TRUE), expression(seq_along(wt) <= 10)) #' ) #' #' splres2 <- add_to_split_result(splres, #' values = "med", #' datasplit = list(med = mtcars[1:20, ]), -#' labels = "kinda some data" +#' labels = "kinda some data", +#' subset_exprs = quote(seq_along(wt) <= 20) #' ) #' #' @family make_custom_split #' @rdname make_split_result #' @export -make_split_result <- function(values, datasplit, labels, extras = NULL) { +#' @family make_custom_split +make_split_result <- function(values, datasplit, labels, extras = NULL, subset_exprs = vector("list", length(values))) { if (length(values) == 1 && is(datasplit, "data.frame")) { datasplit <- list(datasplit) } - ret <- list(values = values, datasplit = datasplit, labels = labels) + ret <- list(values = values, datasplit = datasplit, labels = labels, subset_exprs = subset_exprs) if (!is.null(extras)) { ret$extras <- extras } @@ -120,9 +132,9 @@ make_split_result <- function(values, datasplit, labels, extras = NULL) { #' #' @rdname make_split_result #' @export -add_to_split_result <- function(splres, values, datasplit, labels, extras = NULL) { +add_to_split_result <- function(splres, values, datasplit, labels, extras = NULL, subset_exprs = NULL) { validate_split_result(splres) - newstuff <- make_split_result(values, datasplit, labels, extras) + newstuff <- make_split_result(values, datasplit, labels, extras, subset_exprs = list(subset_exprs)) ret <- lapply( names(splres), function(nm) c(splres[[nm]], newstuff[[nm]]) @@ -131,6 +143,7 @@ add_to_split_result <- function(splres, values, datasplit, labels, extras = NULL .fixupvals(ret) } + .can_take_spl_context <- function(f) any(c(".spl_context", "...") %in% names(formals(f))) #' Create a custom splitting function @@ -161,15 +174,17 @@ add_to_split_result <- function(splres, values, datasplit, labels, extras = NULL #' The preprocessing component is useful for things such as manipulating factor levels, e.g., to trim unobserved ones #' or to reorder levels based on observed counts, etc. #' -#' Customization of core splitting (2) is currently only supported in row splits. Core splitting functions override the -#' fundamental splitting procedure, and are only necessary in rare cases. These must accept `spl`, `df`, `vals`, and -#' `labels`, and can optionally accept `.spl_context`. They must return a named list with elements, all of the same -#' length, as follows: +#' Core splitting functions override the fundamental +#' splitting procedure, and are only necessary in rare cases. These +#' must accept `spl`, `df`, `vals`, `labels`, and can optionally +#' accept `.spl_context`. They should return a split result object +#' constructed via `make_split_result()`. +#' +#' In particular, if the custom split function will be used in +#' column space, subsetting expressions (e.g., as returned by +#' `quote()` or `bquote` must be provided, while they are +#' optional (and largely ignored, currently) in row space. #' -#' - `datasplit`, containing a list of `data.frame` objects. -#' - `values`, containing values associated with the facets, which must be `character` or `SplitValue` objects. -#' These values will appear in the paths of the resulting table. -#' - `labels`, containing the character labels associated with `values` #' #' Post-processing functions (3) must accept the result of the core split as their first argument (which can be #' anything), in addition to `spl`, and `fulldf`, and can optionally accept `.spl_context`. They must each return a @@ -275,14 +290,9 @@ make_split_fun <- function(pre = list(), core_split = NULL, post = list()) { if (is.null(core_split)) { ret <- do_base_split(spl = spl, df = df, vals = vals, labels = labels) - } else if (!in_col_split(.spl_context)) { + } else { ret <- core_split(spl = spl, df = df, vals = vals, labels = labels, .spl_context) validate_split_result(ret, component = "core_split") - } else { - stop( - "Use of custom split functions which override core splitting ", - "behavior is not currently supported in column space." - ) } for (post_fn in post) { @@ -334,12 +344,44 @@ make_split_fun <- function(pre = list(), core_split = NULL, post = list()) { #' @export add_combo_facet <- function(name, label = name, levels, extra = list()) { function(ret, spl, .spl_context, fulldf) { - val <- LevelComboSplitValue(val = name, extr = extra, combolevels = levels, label = label) + if (is(levels, "AllLevelsSentinel")) { + subexpr <- expression(TRUE) + datpart <- list(fulldf) + } else { + subexpr <- .combine_value_exprs(ret$value[levels]) + datpart <- list(do.call(rbind, ret$datasplit[levels])) + } + + val <- LevelComboSplitValue(val = name, extr = extra, combolevels = levels, label = label, + sub_expr = subexpr) add_to_split_result(ret, - values = list(val), labels = label, - datasplit = list(do.call(rbind, ret$datasplit[levels])) - ) + values = list(val), labels = label, + datasplit = datpart) + } +} + +.combine_value_exprs <- function(val_lst, spl) { + exprs <- lapply(val_lst, value_expr) + nulls <- vapply(exprs, is.null, TRUE) + if (all(nulls)) { + return(NULL) #default behavior all the way down the line, no need to do anything. + } else if (any(nulls)) { + exprs[nulls] <- lapply(val_lst[nulls], function(vali) make_subset_expr(spl, vali)) } + Reduce(.or_combine_exprs, exprs) +} + +## no NULLS coming in here, everything has been populated +## by either custom subsetting expressions or the result of make_subset_expr(spl, val) +.or_combine_exprs <- function(ex1, ex2) { + if (identical(ex1, expression(FALSE))) + return(ex2) + else if (identical(ex2, expression(FALSE))) + return(ex1) + else if (identical(ex1, expression(TRUE)) || + identical(ex2, expression(TRUE))) + return(TRUE) + as.expression(bquote((.(a)) | .(b), list(a = ex1[[1]], b = ex2[[1]]))) } #' @rdname add_combo_facet diff --git a/R/make_subset_expr.R b/R/make_subset_expr.R index 71bd2cc46..99f55afcd 100644 --- a/R/make_subset_expr.R +++ b/R/make_subset_expr.R @@ -7,6 +7,11 @@ setGeneric("make_subset_expr", function(spl, val) standardGeneric("make_subset_e setMethod( "make_subset_expr", "VarLevelSplit", function(spl, val) { + ## this is how custom split functions will communicate the correct expression + ## to the column modeling code + if (length(value_expr(val)) > 0) + return(value_expr(val)) + v <- unlist(rawvalues(val)) ## XXX if we're including all levels should even missing be included? if (is(v, "AllLevelsSentinel")) { @@ -23,6 +28,11 @@ setMethod( setMethod( "make_subset_expr", "MultiVarSplit", function(spl, val) { + ## this is how custom split functions will communicate the correct expression + ## to the column modeling code + if (length(value_expr(val)) > 0) + return(value_expr(val)) + ## v = rawvalues(val) ## as.expression(bquote(!is.na(.(a)), list(a = v))) expression(TRUE) @@ -139,6 +149,9 @@ setMethod( return(expression(TRUE)) } } + + if (is.null(ex2)) + ex2 <- expression(TRUE) stopifnot(is.expression(ex1), is.expression(ex2)) as.expression(bquote((.(a)) & .(b), list(a = ex1[[1]], b = ex2[[1]]))) } @@ -240,7 +253,7 @@ create_colinfo <- function(lyt, df, rtpos = TreePos(), if (identical(ex, expression(TRUE))) { nrow(alt_counts_df) } else if (identical(ex, expression(FALSE))) { - 0 + 0L } else { vec <- try(eval(ex, envir = alt_counts_df), silent = TRUE) if (is(vec, "try-error")) { @@ -263,6 +276,7 @@ create_colinfo <- function(lyt, df, rtpos = TreePos(), } }) counts[calcpos] <- calccounts[calcpos] + counts <- as.integer(counts) if (is.null(total)) { total <- sum(counts) } diff --git a/R/split_funs.R b/R/split_funs.R index cc60197cf..e7ea1c778 100644 --- a/R/split_funs.R +++ b/R/split_funs.R @@ -144,6 +144,14 @@ NULL } } + subsets <- partinfo$subset_exprs + if (is.null(subsets)) { + subsets <- vector(mode = "list", length = length(vals)) + ## use labels here cause we already did all that work + ## to get the names on the labels vector right + names(subsets) <- names(labels) + } + if (is.null(vals) && !is.null(extr)) { vals <- seq_along(extr) } @@ -173,7 +181,7 @@ NULL if (is.null(extr)) { extr <- rep(list(list()), length(vals)) } - vals <- make_splvalue_vec(vals, extr, labels = labels) + vals <- make_splvalue_vec(vals, extr, labels = labels, subset_exprs = subsets) } ## we're done with this so take it off partinfo$extras <- NULL @@ -675,7 +683,8 @@ setMethod( function(spl, df, vals, labels) value_labels(spl) ) -make_splvalue_vec <- function(vals, extrs = list(list()), labels = vals) { +make_splvalue_vec <- function(vals, extrs = list(list()), labels = vals, + subset_exprs) { if (length(vals) == 0) { return(vals) } @@ -691,6 +700,7 @@ make_splvalue_vec <- function(vals, extrs = list(list()), labels = vals) { mapply(SplitValue, val = vals, extr = extrs, label = labels, + sub_expr = subset_exprs, SIMPLIFY = FALSE ) } diff --git a/R/tree_accessors.R b/R/tree_accessors.R index 312f4fb1f..b31b13e4b 100644 --- a/R/tree_accessors.R +++ b/R/tree_accessors.R @@ -1761,6 +1761,15 @@ setMethod( #' @rdname int_methods setMethod("value_labels", "MultiVarSplit", function(obj) obj@var_labels) +#' @rdname int_methods +setGeneric("value_expr", function(obj) standardGeneric("value_expr")) +#' @rdname int_methods +setMethod("value_expr", "ValueWrapper", function(obj) obj@subset_expression) +#' @rdname int_methods +setMethod("value_expr", "ANY", function(obj) NULL) +## no setters for now, we'll see about that. + + #' @rdname int_methods setGeneric("spl_varlabels", function(obj) standardGeneric("spl_varlabels")) @@ -2167,7 +2176,8 @@ setGeneric("col_counts<-", function(obj, path = NULL, value) standardGeneric("co setMethod( "col_counts<-", "InstantiatedColumnInfo", function(obj, path = NULL, value) { - obj@counts[.path_to_pos(path, obj, cols = TRUE)] <- value + ## all methods funnel to this one so ensure integer-ness here. + obj@counts[.path_to_pos(path, obj, cols = TRUE)] <- as.integer(value) obj } ) @@ -2211,7 +2221,8 @@ setGeneric("col_total<-", function(obj, value) standardGeneric("col_total<-")) setMethod( "col_total<-", "InstantiatedColumnInfo", function(obj, value) { - obj@total_count <- value + ## all methods funnel to this one so ensure integer-ness here. + obj@total_count <- as.integer(value) obj } ) diff --git a/R/tt_dotabulation.R b/R/tt_dotabulation.R index 903f12eb4..64bd620b7 100644 --- a/R/tt_dotabulation.R +++ b/R/tt_dotabulation.R @@ -1132,7 +1132,7 @@ recursive_applysplit <- function(df, #' #' @inheritParams gen_args #' @inheritParams lyt_args -#' @param col_counts (`numeric` or `NULL`)\cr if non-`NULL`, column counts +#' @param col_counts (`numeric` or `NULL`)\cr `r lifecycle::badge("deprecated")` if non-`NULL`, column counts #' which override those calculated automatically during tabulation. Must specify "counts" for *all* #' resulting columns if non-`NULL`. `NA` elements will be replaced with the automatically calculated counts. #' @param col_total (`integer(1)`)\cr the total observations across all columns. Defaults to `nrow(df)`. @@ -1588,12 +1588,16 @@ splitvec_to_coltree <- function(df, splvec, pos = NULL, kids <- mapply( function(dfpart, value, partlab) { + ## we could pass subset expression in here but the spec + ## currently doesn't call for it in column space newprev <- context_df_row( split = obj_name(spl), value = value_names(value), full_parent_df = list(dfpart), cinfo = NULL ) + ## subset expressions handled inside make_child_pos, + ## value is (optionally, for the moment) carrying it around newpos <- make_child_pos(pos, spl, value, partlab) splitvec_to_coltree(dfpart, splvec, newpos, lvl + 1L, partlab, diff --git a/R/tt_paginate.R b/R/tt_paginate.R index 4a6eaa37a..506109a0e 100644 --- a/R/tt_paginate.R +++ b/R/tt_paginate.R @@ -17,12 +17,9 @@ #' @exportMethod nlines setMethod( "nlines", "TableRow", - function(x, colwidths, max_width) { - ## XXX this is wrong and needs to be fixed - ## should not be hardcoded here - col_gap <- 3L - fns <- sum(unlist(lapply(row_footnotes(x), nlines, max_width = max_width))) + - sum(unlist(lapply(cell_footnotes(x), nlines, max_width = max_width))) + function(x, colwidths, max_width, fontspec, col_gap = 3) { + fns <- sum(unlist(lapply(row_footnotes(x), nlines, max_width = max_width, fontspec = fontspec))) + + sum(unlist(lapply(cell_footnotes(x), nlines, max_width = max_width, fontspec = fontspec))) fcells <- as.vector(get_formatted_cells(x)) spans <- row_cspans(x) have_cw <- !is.null(colwidths) @@ -48,9 +45,18 @@ setMethod( ## rowext <- max(vapply(strsplit(c(obj_label(x), fcells), "\n", fixed = TRUE), ## length, ## 1L)) - rowext <- max(unlist(mapply(function(s, w) { - nlines(strsplit(s, "\n", fixed = TRUE), max_width = w) - }, s = c(obj_label(x), fcells), w = (colwidths %||% max_width) %||% 1000L, SIMPLIFY = FALSE))) + rowext <- max( + unlist( + mapply( + function(s, w) { + nlines(strsplit(s, "\n", fixed = TRUE), max_width = w, fontspec = fontspec) + }, + s = c(obj_label(x), fcells), + w = (colwidths %||% max_width) %||% vector("list", length(c(obj_label(x), fcells))), + SIMPLIFY = FALSE + ) + ) + ) rowext + fns } @@ -60,10 +66,10 @@ setMethod( #' @rdname formatters_methods setMethod( "nlines", "LabelRow", - function(x, colwidths, max_width) { + function(x, colwidths, max_width, fontspec = fontspec, col_gap = NULL) { if (labelrow_visible(x)) { - nlines(strsplit(obj_label(x), "\n", fixed = TRUE)[[1]], max_width = colwidths[1]) + - sum(unlist(lapply(row_footnotes(x), nlines, max_width = max_width))) + nlines(strsplit(obj_label(x), "\n", fixed = TRUE)[[1]], max_width = colwidths[1], fontspec = fontspec) + + sum(unlist(lapply(row_footnotes(x), nlines, max_width = max_width, fontspec = fontspec))) } else { 0L } @@ -74,8 +80,8 @@ setMethod( #' @rdname formatters_methods setMethod( "nlines", "RefFootnote", - function(x, colwidths, max_width) { - nlines(format_fnote_note(x), colwidths = colwidths, max_width = max_width) + function(x, colwidths, max_width, fontspec, col_gap = NULL) { + nlines(format_fnote_note(x), colwidths = colwidths, max_width = max_width, fontspec = fontspec) } ) @@ -83,12 +89,22 @@ setMethod( #' @rdname formatters_methods setMethod( "nlines", "InstantiatedColumnInfo", - function(x, colwidths, max_width) { + function(x, colwidths, max_width, fontspec, col_gap = 3) { h_rows <- .do_tbl_h_piece2(x) tl <- top_left(x) %||% rep("", length(h_rows)) main_nls <- vapply( seq_along(h_rows), - function(i) max(nlines(h_rows[[i]], colwidths = colwidths), nlines(tl[i], colwidths = colwidths[1])), + function(i) { + max( + nlines(h_rows[[i]], + colwidths = colwidths, + fontspec = fontspec, + col_gap = col_gap), + nlines(tl[i], + colwidths = colwidths[1], + fontspec = fontspec) + ) + }, 1L ) @@ -106,7 +122,8 @@ setMethod( vapply(unlist(coldf$col_fnotes), nlines, 1, - max_width = max_width + max_width = max_width, + fontspec = fontspec ), 2 * divider_height(x) ) @@ -187,7 +204,9 @@ setMethod( repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, - max_width = NULL) { + max_width = NULL, + fontspec = NULL, + col_gap = 3) { indent <- indent + indent_mod(tt) ## retained for debugging info orig_rownum <- rownum # nolint @@ -217,7 +236,8 @@ setMethod( nsibs = nsibs, nrowrefs = 0L, ncellrefs = 0L, - nreflines = 0L + nreflines = 0L, + fontspec = fontspec )) ) } @@ -232,7 +252,8 @@ setMethod( incontent = TRUE, repr_ext = repr_ext, repr_inds = repr_inds, - max_width = max_width + max_width = max_width, + fontspec = fontspec ) rownum <- max(newdf$abs_rownumber, na.rm = TRUE) @@ -258,7 +279,8 @@ setMethod( incontent = TRUE, repr_ext = repr_ext, repr_inds = repr_inds, - max_width = max_width + max_width = max_width, + fontspec = fontspec ) crnums <- contdf$abs_rownumber crnums <- crnums[!is.na(crnums)] @@ -288,7 +310,8 @@ setMethod( repr_inds = repr_inds, nsibs = newnsibs, sibpos = i, - max_width = max_width + max_width = max_width, + fontspec = fontspec ) # print(kiddfs$abs_rownumber) @@ -322,12 +345,23 @@ setMethod( repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, - max_width = NULL) { + max_width = NULL, + fontspec, + col_gap = 3) { indent <- indent + indent_mod(tt) rownum <- rownum + 1 rrefs <- row_footnotes(tt) crefs <- cell_footnotes(tt) - reflines <- sum(sapply(c(rrefs, crefs), nlines, colwidths = colwidths, max_width = max_width)) + reflines <- sum( + sapply( + c(rrefs, crefs), + nlines, + colwidths = colwidths, + max_width = max_width, + fontspec = fontspec, + col_gap = col_gap + ) + ) ## col_gap not strictly necessary as these aren't rows, but why not ret <- pagdfrow( row = tt, rnum = rownum, @@ -338,12 +372,13 @@ setMethod( repext = repr_ext, repind = repr_inds, indent = indent, - extent = nlines(tt, colwidths = colwidths, max_width = max_width), + extent = nlines(tt, colwidths = colwidths, max_width = max_width, fontspec = fontspec, col_gap = col_gap), ## these two are unlist calls cause they come in lists even with no footnotes nrowrefs = length(rrefs), ncellrefs = length(unlist(crefs)), nreflines = reflines, - trailing_sep = trailing_section_div(tt) + trailing_sep = trailing_section_div(tt), + fontspec = fontspec ) ret } @@ -363,11 +398,13 @@ setMethod( repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, - max_width = NULL) { + max_width = NULL, + fontspec, + col_gap = 3) { rownum <- rownum + 1 indent <- indent + indent_mod(tt) ret <- pagdfrow(tt, - extent = nlines(tt, colwidths = colwidths, max_width = max_width), + extent = nlines(tt, colwidths = colwidths, max_width = max_width, fontspec = fontspec, col_gap = col_gap), rnum = rownum, colwidths = colwidths, sibpos = sibpos, @@ -380,9 +417,12 @@ setMethod( ncellrefs = 0L, nreflines = sum(vapply(row_footnotes(tt), nlines, NA_integer_, colwidths = colwidths, - max_width = max_width + max_width = max_width, + fontspec = fontspec, + col_gap = col_gap )), - trailing_sep = trailing_section_div(tt) + trailing_sep = trailing_section_div(tt), + fontspec = fontspec ) if (!labelrow_visible(tt)) { ret <- ret[0, , drop = FALSE] @@ -488,13 +528,14 @@ setMethod( ## THIS INCLUDES BOTH "table stub" (i.e. column label and top_left) AND ## title/subtitle!!!!! -.header_rep_nlines <- function(tt, colwidths, max_width, verbose = FALSE) { - cinfo_lines <- nlines(col_info(tt), colwidths = colwidths, max_width = max_width) +.header_rep_nlines <- function(tt, colwidths, max_width, fontspec, verbose = FALSE) { + cinfo_lines <- nlines(col_info(tt), colwidths = colwidths, max_width = max_width, fontspec = fontspec) if (any(nzchar(all_titles(tt)))) { ## +1 is for blank line between subtitles and divider tlines <- sum(nlines(all_titles(tt), colwidths = colwidths, - max_width = max_width + max_width = max_width, + fontspec = fontspec )) + divider_height(tt) + 1L } else { tlines <- 0 @@ -512,12 +553,13 @@ setMethod( ## this is ***only*** lines that are expected to be repeated on multiple pages: ## main footer, prov footer, and referential footnotes on **columns** -.footer_rep_nlines <- function(tt, colwidths, max_width, have_cfnotes, verbose = FALSE) { +.footer_rep_nlines <- function(tt, colwidths, max_width, have_cfnotes, fontspec, verbose = FALSE) { flines <- nlines(main_footer(tt), colwidths = colwidths, - max_width = max_width - table_inset(tt) + max_width = max_width - table_inset(tt), + fontspec = fontspec ) + - nlines(prov_footer(tt), colwidths = colwidths, max_width = max_width) + nlines(prov_footer(tt), colwidths = colwidths, max_width = max_width, fontspec = fontspec) if (flines > 0) { dl_contrib <- if (have_cfnotes) 0 else divider_height(tt) flines <- flines + dl_contrib + 1L @@ -635,6 +677,8 @@ pag_tt_indices <- function(tt, nosplitin = character(), colwidths = NULL, max_width = NULL, + fontspec = NULL, + col_gap = 3, verbose = FALSE) { dheight <- divider_height(tt) @@ -644,7 +688,8 @@ pag_tt_indices <- function(tt, hlines <- .header_rep_nlines(tt, colwidths = colwidths, max_width = max_width, - verbose = verbose + verbose = verbose, + fontspec = fontspec ) ## if(any(nzchar(all_titles(tt)))) { ## tlines <- sum(nlines(all_titles(tt), colwidths = colwidths, max_width = max_width)) + @@ -661,9 +706,11 @@ pag_tt_indices <- function(tt, ## flines <- flines + dl_contrib + 1L ## } flines <- .footer_rep_nlines(tt, - colwidths = colwidths, max_width = max_width, - have_cfnotes = have_cfnotes, verbose = verbose - ) + colwidths = colwidths, + max_width = max_width, + have_cfnotes = have_cfnotes, + fontspec = fontspec, + verbose = verbose) ## row lines per page rlpp <- lpp - hlines - flines if (verbose) { @@ -679,7 +726,9 @@ pag_tt_indices <- function(tt, nosplitin = nosplitin, verbose = verbose, have_col_fnotes = have_cfnotes, - div_height = dheight + div_height = dheight, + col_gap = col_gap, + has_rowlabels = TRUE ) } @@ -773,7 +822,13 @@ paginate_table <- function(tt, colwidths = NULL, tf_wrap = FALSE, max_width = NULL, + fontspec = font_spec(font_family, font_size, lineheight), + col_gap = 3, verbose = FALSE) { + new_dev <- open_font_dev(fontspec) + if (new_dev) + on.exit(close_font_dev()) + if ((non_null_na(lpp) || non_null_na(cpp)) && (!is.null(page_type) || (!is.null(pg_width) && !is.null(pg_height)))) { # nolint pg_lcpp <- page_lcpp( @@ -784,7 +839,8 @@ paginate_table <- function(tt, pg_width = pg_width, pg_height = pg_height, margins = margins, - landscape = landscape + landscape = landscape, + fontspec = fontspec ) if (non_null_na(lpp)) { @@ -803,7 +859,15 @@ paginate_table <- function(tt, } if (is.null(colwidths)) { - colwidths <- propose_column_widths(matrix_form(tt, indent_rownames = TRUE)) + colwidths <- propose_column_widths( + matrix_form( + tt, + indent_rownames = TRUE, + fontspec = fontspec, + col_gap = col_gap + ), + fontspec = fontspec + ) } if (!tf_wrap) { @@ -815,7 +879,7 @@ paginate_table <- function(tt, max_width <- cpp } else if (identical(max_width, "auto")) { ## XXX this 3 is column sep width!!!!!!! - max_width <- sum(colwidths) + 3 * (length(colwidths) - 1) + max_width <- sum(colwidths) + col_gap * (length(colwidths) - 1) } if (!is.null(cpp) && !is.null(max_width) && max_width > cpp) { warning("max_width specified is wider than characters per page width (cpp).") @@ -837,16 +901,19 @@ paginate_table <- function(tt, colwidths = colwidths, tf_wrap = tf_wrap, max_width = max_width, - verbose = verbose + fontspec = fontspec, + verbose = verbose, + col_gap = col_gap ) return(unlist(ret, recursive = TRUE)) } inds <- paginate_indices(tt, page_type = page_type, - font_family = font_family, - font_size = font_size, - lineheight = lineheight, + fontspec = fontspec, + ## font_family = font_family, + ## font_size = font_size, + ## lineheight = lineheight, landscape = landscape, pg_width = pg_width, pg_height = pg_height, @@ -858,6 +925,7 @@ paginate_table <- function(tt, colwidths = colwidths, tf_wrap = tf_wrap, max_width = max_width, + col_gap = col_gap, verbose = verbose ) ## paginate_table apparently doesn't accept indent_size diff --git a/R/tt_toString.R b/R/tt_toString.R index 269e2db46..9a3a03173 100644 --- a/R/tt_toString.R +++ b/R/tt_toString.R @@ -47,16 +47,22 @@ setMethod("toString", "VTableTree", function(x, hsep = horizontal_sep(x), indent_size = 2, tf_wrap = FALSE, - max_width = NULL) { + max_width = NULL, + fontspec = font_spec(), + ttype_ok = FALSE) { toString( matrix_form(x, indent_rownames = TRUE, - indent_size = indent_size + indent_size = indent_size, + fontspec = fontspec, + col_gap = col_gap ), widths = widths, col_gap = col_gap, hsep = hsep, tf_wrap = tf_wrap, - max_width = max_width + max_width = max_width, + fontspec = fontspec, + ttype_ok = ttype_ok ) }) @@ -144,6 +150,10 @@ table_shell_str <- function(tt, widths = NULL, col_gap = 3, hsep = default_hsep( #' has indented row names (strings pre-fixed). #' @param expand_newlines (`flag`)\cr whether the matrix form generated should expand rows whose values contain #' newlines into multiple 'physical' rows (as they will appear when rendered into ASCII). Defaults to `TRUE`. +#' @param fontspec (`font_spec` or `NULL`)\cr Font specification that should be +#' assumed during wrapping, as returned by [formatters::font_spec()]. +#' @param col_gap (`numeric(1)`)\cr The column gap to assume between columns, in +#' number of spaces assuming `fontspec` (this reduces to number of characters for monospace fonts). #' #' @details #' The strings in the return object are defined as follows: row labels are those determined by `make_row_df` and cell @@ -189,11 +199,13 @@ setMethod( function(obj, indent_rownames = FALSE, expand_newlines = TRUE, - indent_size = 2) { + indent_size = 2, + fontspec = NULL, + col_gap = 3L) { stopifnot(is(obj, "VTableTree")) header_content <- .tbl_header_mat(obj) # first col are for row.names - sr <- make_row_df(obj) + sr <- make_row_df(obj, fontspec = fontspec) body_content_strings <- if (NROW(sr) == 0) { character() @@ -293,6 +305,7 @@ setMethod( formats = formats, ## display = display, purely a function of spans, handled in constructor now row_info = sr, + colpaths = make_col_df(obj)[["path"]], ## line_grouping handled internally now line_grouping = 1:nrow(body), ref_fnotes = ref_fnotes, nlines_header = nr_header, ## this is fixed internally @@ -308,7 +321,9 @@ setMethod( table_inset = table_inset(obj), header_section_div = header_section_div(obj), horizontal_sep = horizontal_sep(obj), - indent_size = indent_size + indent_size = indent_size, + fontspec = fontspec, + col_gap = col_gap ) } ) diff --git a/inst/WORDLIST b/inst/WORDLIST index 85eef8d0e..54e74bef2 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,36 +1,11 @@ -CRAN's +amongst +Bov Carreras +charset Cheatsheet Chohan -FFFL -Godwin -Heng -Hoffmann -Kelkhoff -Layouting -Lewandowski -Maximo -Modelling -NSE -Paszty -Pharma -Phuse -Pre -Qi -RStudio -Resync -Rua -STUDYID -Saibah -Stoilova -Subtable -Subtables -Tadeusz -Unstratified -Yung -amongst -charset combinatorial +CRAN's customizations de decrementing @@ -39,44 +14,75 @@ dplyr emph facetted facetting +FFFL flextable formatter funder +funs getter getters +Godwin +Heng +Hoffmann ing initializer iteratively +Kelkhoff labelled +Layouting layouting +Lewandowski mandatorily +Maximo +Modelling +monospace multivariable +NSE orderable +params +Paszty pathing +Pharma +Phuse postfix postprocessing +Pre pre priori programmatically +Qi +quartiles reindexed repo repped responder +Resync reusability roadmap +RStudio +Rua +Sabans +Saibah sortable spl +Stoilova +STUDYID subsplits +Subtable subtable subtable's +Subtables subtables summarization tableone +Tadeusz todo unaggregated unicode univariable unpruned +Unstratified unstratified useR xtable +Yung diff --git a/man/build_table.Rd b/man/build_table.Rd index 0ebaef7b9..37869f8b0 100644 --- a/man/build_table.Rd +++ b/man/build_table.Rd @@ -23,7 +23,7 @@ build_table( \item{alt_counts_df}{(\code{data.frame} or \code{tibble})\cr alternative full dataset the rtables framework will use \emph{only} when calculating column counts.} -\item{col_counts}{(\code{numeric} or \code{NULL})\cr if non-\code{NULL}, column counts +\item{col_counts}{(\code{numeric} or \code{NULL})\cr \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} if non-\code{NULL}, column counts which override those calculated automatically during tabulation. Must specify "counts" for \emph{all} resulting columns if non-\code{NULL}. \code{NA} elements will be replaced with the automatically calculated counts.} diff --git a/man/formatters_methods.Rd b/man/formatters_methods.Rd index c50ac3c5c..906509c90 100644 --- a/man/formatters_methods.Rd +++ b/man/formatters_methods.Rd @@ -119,13 +119,19 @@ \S4method{table_inset}{InstantiatedColumnInfo}(obj) <- value -\S4method{nlines}{TableRow}(x, colwidths = NULL, max_width = NULL) +\S4method{nlines}{TableRow}(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = 3) -\S4method{nlines}{LabelRow}(x, colwidths = NULL, max_width = NULL) +\S4method{nlines}{LabelRow}( + x, + colwidths = NULL, + max_width = NULL, + fontspec = fontspec, + col_gap = NULL +) -\S4method{nlines}{RefFootnote}(x, colwidths = NULL, max_width = NULL) +\S4method{nlines}{RefFootnote}(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = NULL) -\S4method{nlines}{InstantiatedColumnInfo}(x, colwidths = NULL, max_width = NULL) +\S4method{nlines}{InstantiatedColumnInfo}(x, colwidths = NULL, max_width = NULL, fontspec, col_gap = 3) \S4method{make_row_df}{VTableTree}( tt, @@ -139,7 +145,9 @@ repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, - max_width = NULL + max_width = NULL, + fontspec = NULL, + col_gap = 3 ) \S4method{make_row_df}{TableRow}( @@ -154,7 +162,9 @@ repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, - max_width = NULL + max_width = NULL, + fontspec = font_spec(), + col_gap = 3 ) \S4method{make_row_df}{LabelRow}( @@ -169,7 +179,9 @@ repr_inds = integer(), sibpos = NA_integer_, nsibs = NA_integer_, - max_width = NULL + max_width = NULL, + fontspec = font_spec(), + col_gap = 3 ) } \arguments{ @@ -184,6 +196,12 @@ \item{max_width}{(\code{numeric(1)})\cr width that strings should be wrapped to when determining how many lines they require.} +\item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for +calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} + +\item{col_gap}{(\code{numeric(1)})\cr width of gap between columns in number of spaces. +Only used by methods which must calculate span widths after wrapping.} + \item{tt}{(\code{TableTree} or related class)\cr a \code{TableTree} object representing a populated table.} \item{visible_only}{(\code{flag})\cr should only visible aspects of the table structure be reflected diff --git a/man/int_methods.Rd b/man/int_methods.Rd index a34c1a74b..0407afafe 100644 --- a/man/int_methods.Rd +++ b/man/int_methods.Rd @@ -247,6 +247,9 @@ \alias{value_labels,ValueWrapper-method} \alias{value_labels,LevelComboSplitValue-method} \alias{value_labels,MultiVarSplit-method} +\alias{value_expr} +\alias{value_expr,ValueWrapper-method} +\alias{value_expr,ANY-method} \alias{spl_varlabels} \alias{spl_varlabels,MultiVarSplit-method} \alias{spl_varlabels<-} @@ -885,6 +888,12 @@ value_labels(obj) \S4method{value_labels}{MultiVarSplit}(obj) +value_expr(obj) + +\S4method{value_expr}{ValueWrapper}(obj) + +\S4method{value_expr}{ANY}(obj) + spl_varlabels(obj) \S4method{spl_varlabels}{MultiVarSplit}(obj) diff --git a/man/make_split_fun.Rd b/man/make_split_fun.Rd index a730c270c..4afef8df7 100644 --- a/man/make_split_fun.Rd +++ b/man/make_split_fun.Rd @@ -42,16 +42,16 @@ because they will not be present when validity checking is done). The preprocessing component is useful for things such as manipulating factor levels, e.g., to trim unobserved ones or to reorder levels based on observed counts, etc. -Customization of core splitting (2) is currently only supported in row splits. Core splitting functions override the -fundamental splitting procedure, and are only necessary in rare cases. These must accept \code{spl}, \code{df}, \code{vals}, and -\code{labels}, and can optionally accept \code{.spl_context}. They must return a named list with elements, all of the same -length, as follows: -\itemize{ -\item \code{datasplit}, containing a list of \code{data.frame} objects. -\item \code{values}, containing values associated with the facets, which must be \code{character} or \code{SplitValue} objects. -These values will appear in the paths of the resulting table. -\item \code{labels}, containing the character labels associated with \code{values} -} +Core splitting functions override the fundamental +splitting procedure, and are only necessary in rare cases. These +must accept \code{spl}, \code{df}, \code{vals}, \code{labels}, and can optionally +accept \code{.spl_context}. They should return a split result object +constructed via \code{make_split_result()}. + +In particular, if the custom split function will be used in +column space, subsetting expressions (e.g., as returned by +\code{quote()} or \code{bquote} must be provided, while they are +optional (and largely ignored, currently) in row space. Post-processing functions (3) must accept the result of the core split as their first argument (which can be anything), in addition to \code{spl}, and \code{fulldf}, and can optionally accept \code{.spl_context}. They must each return a diff --git a/man/make_split_result.Rd b/man/make_split_result.Rd index 77363e4ee..408e8f134 100644 --- a/man/make_split_result.Rd +++ b/man/make_split_result.Rd @@ -5,9 +5,22 @@ \alias{add_to_split_result} \title{Construct split result object} \usage{ -make_split_result(values, datasplit, labels, extras = NULL) +make_split_result( + values, + datasplit, + labels, + extras = NULL, + subset_exprs = vector("list", length(values)) +) -add_to_split_result(splres, values, datasplit, labels, extras = NULL) +add_to_split_result( + splres, + values, + datasplit, + labels, + extras = NULL, + subset_exprs = NULL +) } \arguments{ \item{values}{(\code{character} or \code{list(SplitValue)})\cr the values associated with each facet.} @@ -19,6 +32,9 @@ add_to_split_result(splres, values, datasplit, labels, extras = NULL) \item{extras}{(\code{list} or \code{NULL})\cr extra values associated with each of the facets which will be passed to analysis functions applied within the facet.} +\item{subset_exprs}{(\code{list})\cr A list of subsetting expressions (e.g., +created with \code{quote()}) to be used during column subsetting.} + \item{splres}{(\code{list})\cr a list representing the result of splitting.} } \value{ @@ -33,21 +49,37 @@ post-processing within a custom split function. These functions performs various housekeeping tasks to ensure that the split result list is as the rtables internals expect it, most of which are not relevant to end users. } +\note{ +Column splitting will not work correctly if a split function +calls \code{make_split_result} without specifying subset expressions; +row splitting will work as normal. This is due to the fact that +subsetting expressions are used during column splitting to +represent the data associated with facets, while actual data +subsets are used during row splitting. +} \examples{ splres <- make_split_result( values = c("hi", "lo"), datasplit = list(hi = mtcars, lo = mtcars[1:10, ]), - labels = c("more data", "less data") + labels = c("more data", "less data"), + subset_exprs = list(expression(TRUE), expression(seq_along(wt) <= 10)) ) splres2 <- add_to_split_result(splres, values = "med", datasplit = list(med = mtcars[1:20, ]), - labels = "kinda some data" + labels = "kinda some data", + subset_exprs = quote(seq_along(wt) <= 20) ) } \seealso{ +Other make_custom_split: +\code{\link{add_combo_facet}()}, +\code{\link{drop_facet_levels}()}, +\code{\link{make_split_fun}()}, +\code{\link{trim_levels_in_facets}()} + Other make_custom_split: \code{\link{add_combo_facet}()}, \code{\link{drop_facet_levels}()}, diff --git a/man/matrix_form-VTableTree-method.Rd b/man/matrix_form-VTableTree-method.Rd index 3ebb6a4c1..76831bb77 100644 --- a/man/matrix_form-VTableTree-method.Rd +++ b/man/matrix_form-VTableTree-method.Rd @@ -8,7 +8,9 @@ obj, indent_rownames = FALSE, expand_newlines = TRUE, - indent_size = 2 + indent_size = 2, + fontspec = NULL, + col_gap = 3L ) } \arguments{ @@ -21,6 +23,12 @@ has indented row names (strings pre-fixed).} newlines into multiple 'physical' rows (as they will appear when rendered into ASCII). Defaults to \code{TRUE}.} \item{indent_size}{(\code{numeric(1)})\cr number of spaces to use per indent level. Defaults to 2.} + +\item{fontspec}{(\code{font_spec} or \code{NULL})\cr Font specification that should be +assumed during wrapping, as returned by \code{\link[formatters:font_spec]{formatters::font_spec()}}.} + +\item{col_gap}{(\code{numeric(1)})\cr The column gap to assume between columns, in +number of spaces assuming \code{fontspec} (this reduces to number of characters for monospace fonts).} } \value{ A list with the following elements: diff --git a/man/paginate.Rd b/man/paginate.Rd index 1403d97a9..d0ac79230 100644 --- a/man/paginate.Rd +++ b/man/paginate.Rd @@ -12,6 +12,8 @@ pag_tt_indices( nosplitin = character(), colwidths = NULL, max_width = NULL, + fontspec = NULL, + col_gap = 3, verbose = FALSE ) @@ -32,6 +34,8 @@ paginate_table( colwidths = NULL, tf_wrap = FALSE, max_width = NULL, + fontspec = font_spec(font_family, font_size, lineheight), + col_gap = 3, verbose = FALSE ) } @@ -53,6 +57,11 @@ footnotes) materials should be word-wrapped to. If \code{NULL}, it is set to the session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is used. Parameter is ignored if \code{tf_wrap = FALSE}.} +\item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for +calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} + +\item{col_gap}{(\code{numeric(1)})\cr space (in characters) between columns.} + \item{verbose}{(\code{flag})\cr whether additional information should be displayed to the user. Defaults to \code{FALSE}.} \item{page_type}{(\code{string})\cr name of a page type. See \code{\link[formatters]{page_types}}. Ignored diff --git a/man/tostring.Rd b/man/tostring.Rd index f5e53f4b1..a6e1933c1 100644 --- a/man/tostring.Rd +++ b/man/tostring.Rd @@ -12,7 +12,9 @@ hsep = horizontal_sep(x), indent_size = 2, tf_wrap = FALSE, - max_width = NULL + max_width = NULL, + fontspec = font_spec(), + ttype_ok = FALSE ) } \arguments{ @@ -36,6 +38,13 @@ must also be considered.} footnotes) materials should be word-wrapped to. If \code{NULL}, it is set to the current print width of the session (\code{getOption("width")}). If set to \code{"auto"}, the width of the table (plus any table inset) is used. Parameter is ignored if \code{tf_wrap = FALSE}.} + +\item{fontspec}{(\code{font_spec})\cr a font_spec object specifying the font information to use for +calculating string widths and heights, as returned by \code{\link[formatters:font_spec]{font_spec()}}.} + +\item{ttype_ok}{(\code{logical(1)})\cr should truetype (non-monospace) fonts be +allowed via \code{fontspec}. Defaults to \code{FALSE}. This parameter is primarily +for internal testing and generally should not be set by end users.} } \value{ A string representation of \code{x} as it appears when printed. diff --git a/tests/testthat/test-exporters.R b/tests/testthat/test-exporters.R index ec10f6793..c11103921 100644 --- a/tests/testthat/test-exporters.R +++ b/tests/testthat/test-exporters.R @@ -146,21 +146,29 @@ test_that("export_as_pdf works", { tmpf <- tempfile(fileext = ".pdf") expect_warning( - export_as_pdf(tbl, file = tmpf, landscape = TRUE, width = 3, paginate = FALSE), + export_as_pdf(tbl, file = tmpf, landscape = TRUE, height = 1000, width = 3, paginate = FALSE), "width of page 1 exceeds the available space" ) expect_true(file.exists(tmpf)) file.remove(tmpf) expect_warning( - export_as_pdf(tbl, file = tmpf, height = 3, paginate = FALSE), + export_as_pdf(tbl, file = tmpf, height = 3, width = 1000, paginate = FALSE), "height of page 1 exceeds the available space" ) res <- export_as_pdf(tbl, file = tmpf) - expect_equal(res$npages, 3) + + ## non-monospace fonts work + ## this tests the actual pagination behavior... + fspec <- font_spec("Times", 20, 1.2) + file.remove(tmpf) + expect_error(export_as_pdf(tbl, file = tmpf, fontspec = fspec), "non-monospace") + file.remove(tmpf) ## blank file created (currently, this could be better) + res <- export_as_pdf(tbl, file = tmpf, fontspec = fspec, ttype_ok = TRUE) }) + # test_that("exporting pdfs gives the correct values", { # if (check_pdf) { # lyt <- basic_table(title = " ") %>% @@ -198,7 +206,7 @@ test_that("exporting pdf does the inset", { table_inset(tbl) <- 100 tmpf <- tempfile(fileext = ".pdf") - expect_error(export_as_pdf(tbl, file = tmpf)) + expect_error(export_as_pdf(tbl, file = tmpf), "Width of row labels equal to or larger than") }) diff --git a/tests/testthat/test-pagination.R b/tests/testthat/test-pagination.R index 3e4ebf814..6a6f37a7e 100644 --- a/tests/testthat/test-pagination.R +++ b/tests/testthat/test-pagination.R @@ -334,8 +334,8 @@ test_that("cell and column wrapping works in pagination", { list(1:5, 6:10) ) expect_identical( - nlines(col_info(tt_for_wrap), colwidths = clw), - nlines(col_info(tt_for_wrap)) + 2L + nlines(col_info(tt_for_wrap), colwidths = clw, fontspec = NULL), + nlines(col_info(tt_for_wrap), fontspec = NULL) + 2L ) ## 2 new lines from wrapping pdf <- make_row_df(tt_for_wrap, colwidths = clw) @@ -450,7 +450,7 @@ test_that("Pagination works with non-default min_siblings", { suppressMessages( expect_error( paginate_table(tt, lpp = 3, min_siblings = 1), - "*Unable to find any valid pagination split for page 1 between rows 1 and 1*" + ".*Unable to find any valid pagination .*between rows 1 and 1.*" ) ) }) @@ -572,3 +572,19 @@ test_that("Pagination works with referential footnotes", { expect_equal(ref_fn_res4$ref_index, 1) expect_equal(ref_fn_res4$symbol, "3") }) + + +test_that("setting colgap during pagination works", { + + tt <- tt_to_export() + ## row labels take up 12, all other columns 10 + 3 (default colgap) + ## so 2 cols per page, 3 pages total + pags1 <- paginate_table(tt, lpp = NULL, cpp = 38) + expect_equal(length(pags1), 3) + ## increase col_gap by one prevents second column on each page + ## so 6 pages + pags2 <- paginate_table(tt, lpp = NULL, cpp = 38, col_gap = 4) + expect_equal(length(pags2), ncol(tt)) + ## too wide a column gap, no columns fit after labels + expect_error(suppressMessages(paginate_table(tt, lpp = NULL, cpp = 38, col_gap = 26))) +}) diff --git a/tests/testthat/test-split_funs.R b/tests/testthat/test-split_funs.R index 659a85dbb..3d25c97c0 100644 --- a/tests/testthat/test-split_funs.R +++ b/tests/testthat/test-split_funs.R @@ -339,7 +339,10 @@ test_that("make_split_fun works", { very_stupid_core <- function(spl, df, vals, labels, .spl_context) { make_split_result( c("stupid", "silly"), - datasplit = list(df[1:10, ], df[11:30, ]), labels = c("first 10", "second 20") + datasplit = list(df[1:10, ], df[11:30, ]), + labels = c("first 10", "second 20"), + subset_exprs = list(quote(seq_along(AGE) <= 10), + quote(seq_along(AGE) %in% 11:30)) ) } @@ -350,12 +353,14 @@ test_that("make_split_fun works", { levels = c("stupid", "silly") )) ) - lyt4a <- basic_table() %>% + lyt4a <- basic_table(show_colcounts = TRUE) %>% split_cols_by("ARM", split_fun = nonsense_splfun) %>% analyze("AGE") - ## not supported in column space, currently - expect_error(build_table(lyt4a, DM), "override core splitting") + tbl4a <- build_table(lyt4a, DM) + expect_equal(col_counts(tbl4a), + c(10L, 20L, 30L)) + lyt4b <- basic_table() %>% split_rows_by("ARM", split_fun = nonsense_splfun) %>% @@ -379,6 +384,20 @@ test_that("make_split_fun works", { 30, cell_values(tbl4b, pths[[5]])[[1]][[1]] ) + + ## add_comb_facet within make_split_fun in column space, regression test + + combofun <- add_combo_facet("combo", "Drug X or Combo", c("A: Drug X", "C: Combination")) + mysplfun <- make_split_fun(post = list(combofun)) + + lyt5 <- basic_table() %>% + split_cols_by("ARM", split_fun = mysplfun) %>% + analyze("STRATA1") + + tbl5 <- build_table(lyt5, ex_adsl) + ## each combo value is A count + C count + vals <- cell_values(tbl5) + expect_true(all(sapply(vals, function(vi) vi$combo == vi[[1]] + vi[[3]]))) }) test_that("spl_variable works", { @@ -412,3 +431,5 @@ test_that("spl_variable works", { "Split class MultiVarSplit not associated with a single variable" ) }) + +## combo levels From 199d41120f377e32b12f4d2e2f3ca87d7f821ebf Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 19:33:00 +0000 Subject: [PATCH 02/12] [skip style] [skip vbump] Restyle files --- R/00tabletrees.R | 5 +++-- R/make_split_fun.R | 22 +++++++++++++--------- R/make_subset_expr.R | 9 ++++++--- R/tt_paginate.R | 26 +++++++++++++++----------- tests/testthat/test-pagination.R | 1 - tests/testthat/test-split_funs.R | 12 ++++++++---- 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/R/00tabletrees.R b/R/00tabletrees.R index 01aaf4577..db8d04f6f 100644 --- a/R/00tabletrees.R +++ b/R/00tabletrees.R @@ -97,8 +97,9 @@ SplitValue <- function(val, extr = list(), label = val, sub_expr = NULL) { label <- as.character(label) } - if (!is.null(sub_expr) && !is.expression(sub_expr)) - sub_expr <- as.expression(sub_expr) ## sometimes they will be "call" objects, etc + if (!is.null(sub_expr) && !is.expression(sub_expr)) { + sub_expr <- as.expression(sub_expr) + } ## sometimes they will be "call" objects, etc check_ok_label(label) new("SplitValue", value = val, diff --git a/R/make_split_fun.R b/R/make_split_fun.R index 4bd01df08..788c1c70c 100644 --- a/R/make_split_fun.R +++ b/R/make_split_fun.R @@ -352,11 +352,14 @@ add_combo_facet <- function(name, label = name, levels, extra = list()) { datpart <- list(do.call(rbind, ret$datasplit[levels])) } - val <- LevelComboSplitValue(val = name, extr = extra, combolevels = levels, label = label, - sub_expr = subexpr) + val <- LevelComboSplitValue( + val = name, extr = extra, combolevels = levels, label = label, + sub_expr = subexpr + ) add_to_split_result(ret, - values = list(val), labels = label, - datasplit = datpart) + values = list(val), labels = label, + datasplit = datpart + ) } } @@ -364,7 +367,7 @@ add_combo_facet <- function(name, label = name, levels, extra = list()) { exprs <- lapply(val_lst, value_expr) nulls <- vapply(exprs, is.null, TRUE) if (all(nulls)) { - return(NULL) #default behavior all the way down the line, no need to do anything. + return(NULL) # default behavior all the way down the line, no need to do anything. } else if (any(nulls)) { exprs[nulls] <- lapply(val_lst[nulls], function(vali) make_subset_expr(spl, vali)) } @@ -374,13 +377,14 @@ add_combo_facet <- function(name, label = name, levels, extra = list()) { ## no NULLS coming in here, everything has been populated ## by either custom subsetting expressions or the result of make_subset_expr(spl, val) .or_combine_exprs <- function(ex1, ex2) { - if (identical(ex1, expression(FALSE))) + if (identical(ex1, expression(FALSE))) { return(ex2) - else if (identical(ex2, expression(FALSE))) + } else if (identical(ex2, expression(FALSE))) { return(ex1) - else if (identical(ex1, expression(TRUE)) || - identical(ex2, expression(TRUE))) + } else if (identical(ex1, expression(TRUE)) || + identical(ex2, expression(TRUE))) { return(TRUE) + } as.expression(bquote((.(a)) | .(b), list(a = ex1[[1]], b = ex2[[1]]))) } diff --git a/R/make_subset_expr.R b/R/make_subset_expr.R index 99f55afcd..0392df010 100644 --- a/R/make_subset_expr.R +++ b/R/make_subset_expr.R @@ -9,8 +9,9 @@ setMethod( function(spl, val) { ## this is how custom split functions will communicate the correct expression ## to the column modeling code - if (length(value_expr(val)) > 0) + if (length(value_expr(val)) > 0) { return(value_expr(val)) + } v <- unlist(rawvalues(val)) ## XXX if we're including all levels should even missing be included? @@ -30,8 +31,9 @@ setMethod( function(spl, val) { ## this is how custom split functions will communicate the correct expression ## to the column modeling code - if (length(value_expr(val)) > 0) + if (length(value_expr(val)) > 0) { return(value_expr(val)) + } ## v = rawvalues(val) ## as.expression(bquote(!is.na(.(a)), list(a = v))) @@ -150,8 +152,9 @@ setMethod( } } - if (is.null(ex2)) + if (is.null(ex2)) { ex2 <- expression(TRUE) + } stopifnot(is.expression(ex1), is.expression(ex2)) as.expression(bquote((.(a)) & .(b), list(a = ex1[[1]], b = ex2[[1]]))) } diff --git a/R/tt_paginate.R b/R/tt_paginate.R index 506109a0e..d3f3ff81e 100644 --- a/R/tt_paginate.R +++ b/R/tt_paginate.R @@ -97,12 +97,14 @@ setMethod( function(i) { max( nlines(h_rows[[i]], - colwidths = colwidths, - fontspec = fontspec, - col_gap = col_gap), + colwidths = colwidths, + fontspec = fontspec, + col_gap = col_gap + ), nlines(tl[i], - colwidths = colwidths[1], - fontspec = fontspec) + colwidths = colwidths[1], + fontspec = fontspec + ) ) }, 1L @@ -706,11 +708,12 @@ pag_tt_indices <- function(tt, ## flines <- flines + dl_contrib + 1L ## } flines <- .footer_rep_nlines(tt, - colwidths = colwidths, - max_width = max_width, - have_cfnotes = have_cfnotes, - fontspec = fontspec, - verbose = verbose) + colwidths = colwidths, + max_width = max_width, + have_cfnotes = have_cfnotes, + fontspec = fontspec, + verbose = verbose + ) ## row lines per page rlpp <- lpp - hlines - flines if (verbose) { @@ -826,8 +829,9 @@ paginate_table <- function(tt, col_gap = 3, verbose = FALSE) { new_dev <- open_font_dev(fontspec) - if (new_dev) + if (new_dev) { on.exit(close_font_dev()) + } if ((non_null_na(lpp) || non_null_na(cpp)) && (!is.null(page_type) || (!is.null(pg_width) && !is.null(pg_height)))) { # nolint diff --git a/tests/testthat/test-pagination.R b/tests/testthat/test-pagination.R index 6a6f37a7e..a939a1fe8 100644 --- a/tests/testthat/test-pagination.R +++ b/tests/testthat/test-pagination.R @@ -575,7 +575,6 @@ test_that("Pagination works with referential footnotes", { test_that("setting colgap during pagination works", { - tt <- tt_to_export() ## row labels take up 12, all other columns 10 + 3 (default colgap) ## so 2 cols per page, 3 pages total diff --git a/tests/testthat/test-split_funs.R b/tests/testthat/test-split_funs.R index 3d25c97c0..4c3be13c4 100644 --- a/tests/testthat/test-split_funs.R +++ b/tests/testthat/test-split_funs.R @@ -341,8 +341,10 @@ test_that("make_split_fun works", { c("stupid", "silly"), datasplit = list(df[1:10, ], df[11:30, ]), labels = c("first 10", "second 20"), - subset_exprs = list(quote(seq_along(AGE) <= 10), - quote(seq_along(AGE) %in% 11:30)) + subset_exprs = list( + quote(seq_along(AGE) <= 10), + quote(seq_along(AGE) %in% 11:30) + ) ) } @@ -358,8 +360,10 @@ test_that("make_split_fun works", { analyze("AGE") tbl4a <- build_table(lyt4a, DM) - expect_equal(col_counts(tbl4a), - c(10L, 20L, 30L)) + expect_equal( + col_counts(tbl4a), + c(10L, 20L, 30L) + ) lyt4b <- basic_table() %>% From 4b52ded0cd3b3ae618fe7963090c89a2b6bdc1d1 Mon Sep 17 00:00:00 2001 From: Gabe Becker Date: Wed, 29 May 2024 12:51:49 -0700 Subject: [PATCH 03/12] fix line lintr apparently can't get right --- R/make_split_fun.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/make_split_fun.R b/R/make_split_fun.R index 788c1c70c..aff197de1 100644 --- a/R/make_split_fun.R +++ b/R/make_split_fun.R @@ -381,8 +381,7 @@ add_combo_facet <- function(name, label = name, levels, extra = list()) { return(ex2) } else if (identical(ex2, expression(FALSE))) { return(ex1) - } else if (identical(ex1, expression(TRUE)) || - identical(ex2, expression(TRUE))) { + } else if (identical(ex1, expression(TRUE)) || identical(ex2, expression(TRUE))) { return(TRUE) } as.expression(bquote((.(a)) | .(b), list(a = ex1[[1]], b = ex2[[1]]))) From 8f6a145b30be447b8ac66d2cc5cd5ae781084583 Mon Sep 17 00:00:00 2001 From: Gabe Becker Date: Wed, 29 May 2024 13:08:07 -0700 Subject: [PATCH 04/12] apparently I have to touch new files to get urlchecker to pass... --- inst/WORDLIST | 3 +++ vignettes/introduction.Rmd | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/inst/WORDLIST b/inst/WORDLIST index 54e74bef2..2b137a203 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -60,6 +60,7 @@ Resync reusability roadmap RStudio +rtables Rua Sabans Saibah @@ -77,6 +78,7 @@ summarization tableone Tadeusz todo +truetype unaggregated unicode univariable @@ -84,5 +86,6 @@ unpruned Unstratified unstratified useR +ValueWrapper xtable Yung diff --git a/vignettes/introduction.Rmd b/vignettes/introduction.Rmd index 681ce562d..0c0651922 100644 --- a/vignettes/introduction.Rmd +++ b/vignettes/introduction.Rmd @@ -31,7 +31,7 @@ The content in this vignette is based on the following two resources: * The [`rtables` useR 2020 presentation](https://www.youtube.com/watch?v=CBQzZ8ZhXLA) by Gabriel Becker * [`rtables` - A Framework For Creating Complex Structured Reporting Tables Via -Multi-Level Faceted Computations](https://arxiv.org/pdf/2306.16610.pdf). +Multi-Level Faceted Computations](https://arxiv.org/pdf/2306.16610). The packages used in this vignette are `rtables` and `dplyr`: From d7cb8410138faaf3600b6f716dcce3c344e5037d Mon Sep 17 00:00:00 2001 From: Gabe Becker Date: Thu, 30 May 2024 07:56:14 -0700 Subject: [PATCH 05/12] manual vbump --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4dee1b2dd..0fe169640 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: rtables Title: Reporting Tables -Version: 0.6.7.9002 +Version: 0.6.7.9003 Date: 2024-05-13 Authors@R: c( person("Gabriel", "Becker", , "gabembecker@gmail.com", role = "aut", From 19c0fa256dda99c43f37ce2e5b33b47b923c4063 Mon Sep 17 00:00:00 2001 From: Davide Garolini Date: Fri, 31 May 2024 16:08:05 +0200 Subject: [PATCH 06/12] Update DESCRIPTION Co-authored-by: Joe Zhu Signed-off-by: Davide Garolini --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0fe169640..f58297f3f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -30,7 +30,7 @@ URL: https://github.com/insightsengineering/rtables, BugReports: https://github.com/insightsengineering/rtables/issues Depends: R (>= 2.10), - formatters (>= 0.5.6.9007), + formatters (>= 0.5.7.9000), magrittr (>= 1.5), methods Imports: From 92057bae9cf1c13f381c58acc514b5f9d489b613 Mon Sep 17 00:00:00 2001 From: Davide Garolini Date: Fri, 31 May 2024 16:08:12 +0200 Subject: [PATCH 07/12] Update DESCRIPTION Signed-off-by: Davide Garolini --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index f58297f3f..9a87c75ee 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: rtables Title: Reporting Tables -Version: 0.6.7.9003 +Version: 0.6.7.9002 Date: 2024-05-13 Authors@R: c( person("Gabriel", "Becker", , "gabembecker@gmail.com", role = "aut", From b97823f2799620ed790cace13aba3d408afa35b3 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 31 May 2024 14:10:23 +0000 Subject: [PATCH 08/12] [skip roxygen] [skip vbump] Roxygen Man Pages Auto Update --- man/formatters_methods.Rd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/formatters_methods.Rd b/man/formatters_methods.Rd index 906509c90..6a7767a00 100644 --- a/man/formatters_methods.Rd +++ b/man/formatters_methods.Rd @@ -248,7 +248,7 @@ Most arguments beyond \code{tt} and \code{visible_only} are present so that \cod } \note{ The technically present root tree node is excluded from the summary returned by -both \code{make_row_df} and \code{make_col_df} (see \code{\link[rtables:make_col_df]{rtables::make_col_df()}}), as it is simply the +both \code{make_row_df} and \code{make_col_df} (see relevant functions in\code{rtables}), as it is the row/column structure of \code{tt} and thus not useful for pathing or pagination. } \examples{ From 14374310af4f6f04b532623e80a0e03510e65ea8 Mon Sep 17 00:00:00 2001 From: Melkiades Date: Mon, 3 Jun 2024 09:28:56 +0200 Subject: [PATCH 09/12] correcting NEWS --- NEWS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 421996f04..ec3947d1e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,7 +3,7 @@ * Added `top_level_section_div` for `basic_table` to set section dividers for top level rows. * Added `keep_label_rows` to `as_result_df` to have these lines visible. * `sort_at_path` now gives informative error messages when the given path does not exist. - * Add support for truetype fonts based on formatters `>= 0.5.6.9007`. Nearly all functions related to pagination or export now accept `fontspec` argument and pass it around accordingly. By @gmbecker + * Add support for truetype fonts based on formatters `>= 0.5.6.9007`. Nearly all functions related to pagination or export now accept `fontspec` argument and pass it around accordingly, by @gmbecker. * Core splitting machinery can now be overridden in column space via `make_split_fun` provided that `core_split` associates the generated facets with subsetting expressions. Subsetting expressions remain unnecessary for splits in row space. By @gmbecker. * ValueWrapper objects now carry around subsetting expressions for use during tabulation, by @gmbecker. * `make_split_res`, `add_to_split_result` now accept a list of subsetting expressions which will be attached to the values, by @gmbecker. @@ -25,7 +25,8 @@ ### Miscellaneous * Removed deprecated functions `add_analyzed_var` and `trim_zero_rows`. - * Updated versioned dependency on `formatters`to `0.5.6.9007` + * Added `lifecycle` badge files for deprecated documentation. + * Deprecated the `gap` and `check_headers` arguments to `rbindl_rtables` using `lifecycle`. ## rtables 0.6.6 ### New Features From ff512ebe59f9183a068a61cc5489a0f8a445eefe Mon Sep 17 00:00:00 2001 From: Melkiades Date: Mon, 3 Jun 2024 09:30:44 +0200 Subject: [PATCH 10/12] automatic fix of wordlist? updated --- inst/WORDLIST | 68 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/inst/WORDLIST b/inst/WORDLIST index 2b137a203..7a2fe8159 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,11 +1,39 @@ -amongst -Bov +Bové +CRAN's Carreras -charset Cheatsheet Chohan +FFFL +Godwin +Heng +Hoffmann +Kelkhoff +Layouting +Lewandowski +Maximo +Modelling +NSE +Paszty +Pharma +Phuse +Pre +Qi +RStudio +Resync +Rua +STUDYID +Sabanés +Saibah +Stoilova +Subtable +Subtables +Tadeusz +Unstratified +ValueWrapper +Yung +amongst +charset combinatorial -CRAN's customizations de decrementing @@ -14,78 +42,48 @@ dplyr emph facetted facetting -FFFL flextable formatter +formatters funder -funs getter getters -Godwin -Heng -Hoffmann ing initializer iteratively -Kelkhoff labelled -Layouting layouting -Lewandowski mandatorily -Maximo -Modelling monospace multivariable -NSE orderable -params -Paszty pathing -Pharma -Phuse postfix postprocessing -Pre pre priori programmatically -Qi quartiles reindexed repo repped responder -Resync reusability roadmap -RStudio -rtables -Rua -Sabans -Saibah sortable spl -Stoilova -STUDYID subsplits -Subtable subtable subtable's -Subtables subtables summarization tableone -Tadeusz todo truetype unaggregated unicode univariable unpruned -Unstratified unstratified useR -ValueWrapper xtable -Yung From d28c170ccd0e7764d99ab186ba25b20531204377 Mon Sep 17 00:00:00 2001 From: Gabe Becker Date: Mon, 3 Jun 2024 15:28:12 -0700 Subject: [PATCH 11/12] Update version in description --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9a87c75ee..f58297f3f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: rtables Title: Reporting Tables -Version: 0.6.7.9002 +Version: 0.6.7.9003 Date: 2024-05-13 Authors@R: c( person("Gabriel", "Becker", , "gabembecker@gmail.com", role = "aut", From 733b7231568ea6804ea9a5429e8875119ea3b02f Mon Sep 17 00:00:00 2001 From: Davide Garolini Date: Tue, 4 Jun 2024 14:56:48 +0200 Subject: [PATCH 12/12] Update DESCRIPTION Signed-off-by: Davide Garolini --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index f58297f3f..9a87c75ee 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: rtables Title: Reporting Tables -Version: 0.6.7.9003 +Version: 0.6.7.9002 Date: 2024-05-13 Authors@R: c( person("Gabriel", "Becker", , "gabembecker@gmail.com", role = "aut",