From 2c4a5918821f7683b71679383e9da029b8d678e0 Mon Sep 17 00:00:00 2001 From: Melkiades Date: Fri, 9 Aug 2024 15:11:42 +0200 Subject: [PATCH 1/6] finalizing vignette --- vignettes/dev-guide/dg_printing.Rmd | 432 ++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 vignettes/dev-guide/dg_printing.Rmd diff --git a/vignettes/dev-guide/dg_printing.Rmd b/vignettes/dev-guide/dg_printing.Rmd new file mode 100644 index 0000000000..a169254ada --- /dev/null +++ b/vignettes/dev-guide/dg_printing.Rmd @@ -0,0 +1,432 @@ +--- +title: "Printing Machinery" +author: "Davide Garolini" +date: '`r Sys.Date()`' +output: + html_document: + theme: spacelab + toc: true + toc_float: + collapsed: false +editor_options: + chunk_output_type: console +--- + +```{r setup, include=FALSE} +knitr::opts_chunk$set(echo = TRUE) +``` + + +## Disclaimer + +In comparison to other entries of the dev-guide, this is intended to keep track of the general concepts and processing pipeline behind the printing machinery. It is not intended to be a complete documentation of the machinery itself, but rather a collection of notes that can be used to understand the machinery and its internals. Hence, be aware that this is a working document that captures a snapshot of the machinery at a certain point in time. It is not meant to be fully maintained, but it can be used as a starting point for one. Differently from other parts of the dev-guide, this will contain the current state of `rlistings`' printing machinery, which is often a simplified version of the machinery that is used in `rtables`. + + +### How `print` works + +Lets track down what is going under the hood when a standard table is printed. The following is the code that is executed when a table is printed: + +```{r} +library(rtables) +library(dplyr) +lyt <- basic_table() %>% + split_rows_by("SEX", split_fun = keep_split_levels(c("F", "M"))) %>% + split_cols_by("ARM") %>% + analyze("BMRKR1") %>% + print() +tbl <- build_table(lyt, ex_adsl) %>% + print() +``` + +We see that also a layout object (`PreDataTableLayouts`) is created and printed. This is because `print` is a generic function that dispatches to different methods depending on the class of the object. In this case, the S4 class of the object is `PreDataTableLayouts` and the method that is called is `print`. In the case of {rtables} the method is dispatched towards the `show` method of the class `PreDataTableLayouts`. It can be found by searching `A Pre-data Table Layout` into {rtables} source code. I think R dispatcher for `print` methods looks for `show` S4 methods instead if there are no S3 or S4 `print` methods available. Indeed, this is the code that is executed: + +```{r, eval=FALSE} +setMethod( + "show", "PreDataTableLayouts", + function(object) { + cat("A Pre-data Table Layout\n") + cat("\nColumn-Split Structure:\n") + docat_predataxis(object@col_layout) + cat("\nRow-Split Structure:\n") + docat_predataxis(object@row_layout) + cat("\n") + invisible(object) + } +) +``` + +This was evident if we searched for methods associated with the class `PreDataTableLayouts`, where only `show` is connected to a sort of printing machinery: + +```{r, eval=TRUE} +methods(class = "PreDataTableLayouts") +``` + +Now, lets see the same for our result table `tbl`: + +```{r} +class(tbl) %>% print() + +getClass("TableTree") %>% print() # Main object representing a table in {rtables} + +methods(class = "TableTree") %>% print() # more than 70 methods but no print method +``` + +Again, the class itself has only the `show` method. Nonetheless, if you search for `VTableTree"` you will find the `print` method for the `TableTree` class. This is because `VTableTree` is a virtual class that is inherited by `TableTree` and is almost identical to the `show` method for `TableTree` objects. All the different statements in this case (`show` or `print`) do the same thing, i.e. they call `toString` and `cat` on the object. Hence, we know that every table is printed by `toString` with `\n` as separator for different lines so that `cat` renders it in its final format. + +### From `matrix_form` to `toString` + +If we have source code of `formatters`, `rtables`, and `rlistings` in our local we can search for `"toString"` S4 method definition across these source folders. We will find generics in `formatters` and three different `setMethod(...)`. `toString` is properly defined in `formatters`, but it is also present in `rlistings` and`rtables`. Let's take a look at the latter first. + +```{r, eval=FALSE} +setMethod("toString", "VTableTree", function(x, + widths = NULL, + col_gap = 3, + hsep = horizontal_sep(x), + indent_size = 2, + tf_wrap = FALSE, + max_width = NULL) { + toString( + matrix_form(x, + indent_rownames = TRUE, + indent_size = indent_size # Only modifies the rownames in matrix_form + ), + widths = widths, col_gap = col_gap, + hsep = hsep, + tf_wrap = tf_wrap, + max_width = max_width + ) +}) +``` + +This is only a wrapper/dispatcher to the core toString function in `formatters`, beside the `indent_size` specification. This is based on the "rendering-ready" class `MatrixPrintForm` that is produced by `matrix_form`. The latter is the first core transformation that we need to know to understand the printing process. All exporters and printers are based on `MatrixPrintForm` objects, hence any bug or problem needs to tracked down to this function or `toString`. If we take a look at `toString` for `"listing_df"` in `rlistings`, we will find a shallow wrapper that dispatches to `MatrixPrintForm` objects: + +```{r, eval=FALSE} +setMethod("toString", "listing_df", function(x, ...) { + toString(matrix_form(x), ...) +}) +``` + +Hence lets take a look at `"matrix_form"` (if there are quotes, it is an S4 function from now on). Beside generics and self calls (`setMethod("matrix_form", "MatrixPrintForm", [...] obj)`), `rlistings` and `rtables` have their own "constructor" of `MatrixPrintForm` (the real one can be found in `formatters`). Let's start with the latter `"matrix_form"` which is dispatched when dealing with `VTableTree`s. +```{r, eval=FALSE} +# Entering matrix_form for VTableTree +trace("matrix_form", signature = "VTableTree", tracer = browser, exit = browser) +matrix_form(tbl) +untrace("matrix_form", signature = "VTableTree") +``` + +Now lets see the newly commented code for `matrix_form`. With `#->` I will comment some suggestions for further understandings. + +```{r, eval=FALSE} +setMethod( + "matrix_form", "VTableTree", + function(obj, + indent_rownames = FALSE, + expand_newlines = TRUE, + indent_size = 2) { + stopifnot(is(obj, "VTableTree")) + + #-> Read .tbl_header_mat and subfunctions (based largely on cinfo) it can help for understanding + # column structure and how it is printed (we can add a description of this process xxx) + # Note: it contains the display of column counts directives and specifics + header_content <- .tbl_header_mat(obj) # first col are for row.names or topleft info + nr_header <- nrow(header_content$body) # colcounts were added in .tbl_header_mat + + #-> As before, reading this function can help understanding how the content of the table is transformed + # in row content and how the structure of the table is preserved in a compact manner. It is complex + # function as it is a recursive one with the different dispatcher but following how different section_div + # are printed (with the dedicated assignment function) can help understanding the table structure and its + # row-wise transformation. + # Summary of row contents - reprint_inds specifies which rows to reprint (hence the grouping) + sr <- make_row_df(obj) + + # With get_formatted_cells we get relevant information inside the table tree + body_content_strings <- if (NROW(sr) == 0) { + character() + } else { + #-> get_formatted_cells is an interesting function to understand the structure of the table as + # it is design to extract only the "data" of the table as strings. Note how the label rows are + # taken from make_row_df instead. Check shell = TRUE afterwards to see how the format are retrieved. + cbind(as.character(sr$label), get_formatted_cells(obj)) + } + + formats_strings <- if (NROW(sr) == 0) { + character() + } else { + cbind("", get_formatted_cells(obj, shell = TRUE)) + } + + #-> Here spans are extracted for each row. Spans are rarely modified beyond its standard values. + # Takes the flatten spans for each row and repeats them according to the number elements + tsptmp <- lapply(collect_leaves(obj, TRUE, TRUE), function(rr) { + sp <- row_cspans(rr) + rep(sp, times = sp) + }) + + ## the 1 is for row labels + body_spans <- if (nrow(obj) > 0) { + cbind(1L, do.call(rbind, tsptmp)) + } else { + matrix(1, nrow = 0, ncol = ncol(obj) + 1) + } + + body_aligns <- if (NROW(sr) == 0) { + character() + } else { + cbind("left", get_cell_aligns(obj)) #-> extracts align values for each cell + } + + body <- rbind(header_content$body, body_content_strings) + + # Init column format for header (empty if not for column counts) + hdr_fmt_blank <- matrix("", + nrow = nrow(header_content$body), + ncol = ncol(header_content$body) + ) + # If column counts are displayed, add column count format + if (disp_ccounts(obj)) { + hdr_fmt_blank[nrow(hdr_fmt_blank), ] <- c("", rep(colcount_format(obj), ncol(obj))) + } + + formats <- rbind(hdr_fmt_blank, formats_strings) + + spans <- rbind(header_content$span, body_spans) + row.names(spans) <- NULL + + aligns <- rbind( + matrix(rep("center", length(header_content$body)), + nrow = nrow(header_content$body) + ), + body_aligns + ) + + aligns[, 1] <- "left" # row names and topleft (still needed for topleft) + + # Main indentation of the table rownames #-> Main indentation facility + if (indent_rownames) { + body[, 1] <- indent_string(body[, 1], c(rep(0, nr_header), sr$indent), + incr = indent_size + ) + formats[, 1] <- indent_string(formats[, 1], c(rep(0, nr_header), sr$indent), + incr = indent_size + ) + } + + #-> referential strings are added to the table. get_ref_matrix is the core of this process + # along with format_fnote_ref that in this case is used to format the reference string and their + # indices. Note that the footnotes for the header is taken from the output of .tbl_header_mat + # Handling of references in header and body + col_ref_strs <- matrix(vapply(header_content$footnotes, function(x) { + if (length(x) == 0) { + "" + } else { + paste(vapply(x, format_fnote_ref, ""), collapse = " ") + } + }, ""), ncol = ncol(body)) + body_ref_strs <- get_ref_matrix(obj) + body <- matrix( + paste0( + body, + rbind( + col_ref_strs, #-> col_ref_strs are added to the body as a separate section + body_ref_strs + ) + ), + nrow = nrow(body), + ncol = ncol(body) + ) + + # Solve \n in titles + if (any(grepl("\n", all_titles(obj)))) { + if (any(grepl("\n", main_title(obj)))) { + tmp_title_vec <- .quick_handle_nl(main_title(obj)) + main_title(obj) <- tmp_title_vec[1] + subtitles(obj) <- c(tmp_title_vec[-1], .quick_handle_nl(subtitles(obj))) + } else { + subtitles(obj) <- .quick_handle_nl(subtitles(obj)) + } + } + + # Solve \n in footers + main_footer(obj) <- .quick_handle_nl(main_footer(obj)) + prov_footer(obj) <- .quick_handle_nl(prov_footer(obj)) + + + #-> this is still under development as indicated by xxx. The idea is to allow \n also in peculiar + # cases, such as page titles and referential footnotes. The latter are resolved in toString (pagination + # will not count them as more than one line each), while for the former we do not have any coverage yet. + # xxx \n in page titles are not working atm (I think) + # ref_fnotes <- strsplit(get_formatted_fnotes(obj), "\n", fixed = TRUE) + ref_fnotes <- get_formatted_fnotes(obj) # pagination will not count extra lines coming from here + pag_titles <- page_titles(obj) + + MatrixPrintForm( + strings = body, #-> FUNDAMENTAL: this is the matrix that contains all the cell strings + spans = spans, + aligns = aligns, + formats = formats, + ## display = display, purely a function of spans, handled in constructor now + row_info = sr, #-> FUNDAMENTAL: this is the data.frame that contains all the information about the rows + # it is the most complex data brought forward into toString + ## line_grouping handled internally now line_grouping = 1:nrow(body), + ref_fnotes = ref_fnotes, + nlines_header = nr_header, ## this is fixed internally + nrow_header = nr_header, + expand_newlines = expand_newlines, + has_rowlabs = TRUE, + has_topleft = TRUE, #-> I think topleft material is handled later in toString + main_title = main_title(obj), + subtitles = subtitles(obj), + page_titles = pag_titles, + main_footer = main_footer(obj), + prov_footer = prov_footer(obj), + table_inset = table_inset(obj), + header_section_div = header_section_div(obj), + horizontal_sep = horizontal_sep(obj), + indent_size = indent_size + ) + } +) +``` + +Now lets see the `matrix_form` in `rlistings`: +```{r, eval=FALSE} +library(rlistings) +lsting <- as_listing(mtcars) +trace("matrix_form", signature = "listing_df", tracer = browser, exit = browser) +mf <- matrix_form(lsting) +untrace("matrix_form", signature = "listing_df") +``` + + +```{r, eval=FALSE} +setMethod( + "matrix_form", "listing_df", + rix_form <- function(obj, indent_rownames = FALSE) { #-> I have no idea why here there is an assignment xxx + ## we intentionally silently ignore indent_rownames because listings have + ## no rownames, but formatters::vert_pag_indices calls matrix_form(obj, TRUE) + ## unconditionally. + + + # Keeping only displayed columns + cols <- attr(obj, "listing_dispcols") # this is the list of columns to be displayed + listing <- obj[, cols] + atts <- attributes(obj) + atts$names <- cols + attributes(listing) <- atts + keycols <- get_keycols(listing) + + + bodymat <- matrix("", + nrow = nrow(listing), + ncol = ncol(listing) + ) + + colnames(bodymat) <- names(listing) + + + # Print only first appearer of key columns if repeated + curkey <- "" + for (i in seq_along(keycols)) { + kcol <- keycols[i] + kcolvec <- listing[[kcol]] + #-> format_value transforms the values of the column into strings + kcolvec <- vapply(kcolvec, format_value, "", format = obj_format(kcolvec), na_str = obj_na_str(kcolvec)) + curkey <- paste0(curkey, kcolvec) + disp <- c(TRUE, tail(curkey, -1) != head(curkey, -1)) #-> This condition only show the first appearer of a key + bodymat[disp, kcol] <- kcolvec[disp] + } + + # Print all other columns directly + nonkeycols <- setdiff(names(listing), keycols) + if (length(nonkeycols) > 0) { + for (nonk in nonkeycols) { + vec <- listing[[nonk]] + vec <- vapply(vec, format_value, "", format = obj_format(vec), na_str = obj_na_str(vec)) + bodymat[, nonk] <- vec + } + } + + + fullmat <- rbind( + var_labels(listing, fill = TRUE), # Extracts the variable labels + bodymat + ) + + colaligns <- rbind( + rep("center", length(cols)), # Col names are always centered? + matrix(sapply(listing, obj_align), + ncol = length(cols), + nrow = nrow(fullmat) - 1, + byrow = TRUE + ) + ) + + MatrixPrintForm( + strings = fullmat, + spans = matrix(1, + nrow = nrow(fullmat), + ncol = ncol(fullmat) + ), + ref_fnotes = list(), + aligns = colaligns, + formats = matrix(1, + nrow = nrow(fullmat), + ncol = ncol(fullmat) + ), + row_info = make_row_df(obj), + nlines_header = 1, ## XXX this is probably wrong!!! + nrow_header = 1, + has_topleft = FALSE, + has_rowlabs = FALSE, + expand_newlines = TRUE, # Always expand newlines, but this happens later!! XXX to fix + main_title = main_title(obj), + subtitles = subtitles(obj), + page_titles = page_titles(obj), + main_footer = main_footer(obj), + prov_footer = prov_footer(obj) + ) + } +) +``` + +We device here the good developer to search and understand the various methods associated with `MatrixPrintForm` objects. It is relevant to remember how this printed form is meant to + +```{r} +# Example quick table +summary_list <- function(x, ...) as.list(summary(x)) +a_table <- qtable(ex_adsl, row_vars = "SEX", col_vars = "ARM", avar = "AGE", afun = summary_list) +tbl_methods <- methods(class = class(a_table)) +mpf_methods <- methods(class = class(matrix_form(a_table))[1]) # it is a list of values + +# Cleaning values +tbl_methods <- unique(sapply(strsplit(tbl_methods, ","), function(x) x[1])) +mpf_methods <- unique(sapply(strsplit(mpf_methods, ","), function(x) x[1])) +setdiff(tbl_methods, mpf_methods) +setdiff(mpf_methods, tbl_methods) # much less unique methods +intersect(tbl_methods, mpf_methods) # interesting to discover the different behaviors of same functions +``` + +Let's now take a look at the final function of all this: `toString` from `formatters`: + +```{r, eval=FALSE} +setMethod("toString", "MatrixPrintForm", function(x, + widths = NULL, + tf_wrap = FALSE, + max_width = NULL, + col_gap = mf_colgap(x), + hsep = NULL, + fontspec = font_spec(), + ttype_ok = FALSE) { + # part 1: checks and widths/max width estimation for columns - propose_column_widths and .handle_max_width + # + # part 2: wrapping for the table - do_cell_fnotes_wrap + # + # part 3: column gap and cell widths calculations (after wrapping) - .calc_cell_widths + # + # part 4: collapse text body and wrapping titles/footers + # + # part 5: final cat() +} +``` + +We rely on the future developer to fill in the blanks in the above description and to follow up the various functions to their core mechanics. From 0b85e0fbc0d6c2c5f9cf5efdcb363023905bb3a1 Mon Sep 17 00:00:00 2001 From: Melkiades Date: Fri, 9 Aug 2024 15:13:38 +0200 Subject: [PATCH 2/6] news --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 42c5cdf90a..c3c874b81f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,7 @@ * Added option to change `sep = "\t"` and set other parameters via `...` parameter propagation in `export_as_tsv`. * Grouped split functions documentation into one page with precise descriptions of each function and relative examples. + * Addition of developer's guide vignette about printing methods, specifically `matrix_form` and `toString`. ## rtables 0.6.9 ### Miscellaneous From 494de5befb69b47db5e4d34e074a34b83009345a Mon Sep 17 00:00:00 2001 From: Melkiades Date: Fri, 9 Aug 2024 15:19:17 +0200 Subject: [PATCH 3/6] fixing nl --- R/tt_toString.R | 7 ------- vignettes/dev-guide/dg_printing.Rmd | 28 ++++++++++++++-------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/R/tt_toString.R b/R/tt_toString.R index 4a5d7c9fb2..0b387cda66 100644 --- a/R/tt_toString.R +++ b/R/tt_toString.R @@ -361,13 +361,6 @@ ccvis_check_subtree <- function(ctree) { lapply(kids, ccvis_check_subtree) invisible(NULL) } -.quick_handle_nl <- function(str_v) { - if (any(grepl("\n", str_v))) { - return(unlist(strsplit(str_v, "\n", fixed = TRUE))) - } else { - return(str_v) - } -} .resolve_fn_symbol <- function(fn) { if (!is(fn, "RefFootnote")) { diff --git a/vignettes/dev-guide/dg_printing.Rmd b/vignettes/dev-guide/dg_printing.Rmd index a169254ada..5f6feb3e8f 100644 --- a/vignettes/dev-guide/dg_printing.Rmd +++ b/vignettes/dev-guide/dg_printing.Rmd @@ -235,20 +235,20 @@ setMethod( ncol = ncol(body) ) - # Solve \n in titles - if (any(grepl("\n", all_titles(obj)))) { - if (any(grepl("\n", main_title(obj)))) { - tmp_title_vec <- .quick_handle_nl(main_title(obj)) - main_title(obj) <- tmp_title_vec[1] - subtitles(obj) <- c(tmp_title_vec[-1], .quick_handle_nl(subtitles(obj))) - } else { - subtitles(obj) <- .quick_handle_nl(subtitles(obj)) - } - } - - # Solve \n in footers - main_footer(obj) <- .quick_handle_nl(main_footer(obj)) - prov_footer(obj) <- .quick_handle_nl(prov_footer(obj)) + # Solve \n in titles # This is something that is relevant in toString - NO MORE USED HERE + # if (any(grepl("\n", all_titles(obj)))) { + # if (any(grepl("\n", main_title(obj)))) { + # tmp_title_vec <- .quick_handle_nl(main_title(obj)) + # main_title(obj) <- tmp_title_vec[1] + # subtitles(obj) <- c(tmp_title_vec[-1], .quick_handle_nl(subtitles(obj))) + # } else { + # subtitles(obj) <- .quick_handle_nl(subtitles(obj)) + # } + # } + # + # # Solve \n in footers + # main_footer(obj) <- .quick_handle_nl(main_footer(obj)) + # prov_footer(obj) <- .quick_handle_nl(prov_footer(obj)) #-> this is still under development as indicated by xxx. The idea is to allow \n also in peculiar From 28051d6a776800116c11aa77e286c47019a45696 Mon Sep 17 00:00:00 2001 From: Melkiades Date: Fri, 9 Aug 2024 15:36:23 +0200 Subject: [PATCH 4/6] add to pkgdown --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 0ea7395e39..b784372603 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -100,6 +100,7 @@ articles: - dev-guide/dg_split_machinery - dev-guide/dg_tabulation - dev-guide/dg_table_hierarchy + - dev-guide/dg_printing - dev-guide/dg_debug_rtables - dev-guide/dg_notes From 1c95032d12943d6dbef8ae548427b700af143281 Mon Sep 17 00:00:00 2001 From: Melkiades Date: Fri, 9 Aug 2024 16:54:01 +0200 Subject: [PATCH 5/6] fix style --- vignettes/dev-guide/dg_printing.Rmd | 73 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/vignettes/dev-guide/dg_printing.Rmd b/vignettes/dev-guide/dg_printing.Rmd index 5f6feb3e8f..71049e0d8b 100644 --- a/vignettes/dev-guide/dg_printing.Rmd +++ b/vignettes/dev-guide/dg_printing.Rmd @@ -1,25 +1,24 @@ --- title: "Printing Machinery" author: "Davide Garolini" -date: '`r Sys.Date()`' -output: - html_document: - theme: spacelab - toc: true - toc_float: - collapsed: false +date: "`r Sys.Date()`" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Printing Machinery} + %\VignetteEncoding{UTF-8} + %\VignetteEngine{knitr::rmarkdown} editor_options: chunk_output_type: console --- -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) +```{r, echo=FALSE} +knitr::opts_chunk$set(comment = "#") ``` ## Disclaimer -In comparison to other entries of the dev-guide, this is intended to keep track of the general concepts and processing pipeline behind the printing machinery. It is not intended to be a complete documentation of the machinery itself, but rather a collection of notes that can be used to understand the machinery and its internals. Hence, be aware that this is a working document that captures a snapshot of the machinery at a certain point in time. It is not meant to be fully maintained, but it can be used as a starting point for one. Differently from other parts of the dev-guide, this will contain the current state of `rlistings`' printing machinery, which is often a simplified version of the machinery that is used in `rtables`. +In comparison to other entries of the developer guide, this is intended to keep track of the general concepts and processing pipeline behind the printing machinery. It is not intended to be a complete documentation of the machinery itself, but rather a collection of notes that can be used to understand the machinery and its internals. Hence, be aware that this is a working document that captures a snapshot of the machinery at a certain point in time. It is not meant to be fully maintained, but it can be used as a starting point for one. Differently from other parts of the developer guide, this will contain the current state of `rlistings`' printing machinery, which is often a simplified version of the machinery that is used in `rtables`. ### How `print` works @@ -29,12 +28,12 @@ Lets track down what is going under the hood when a standard table is printed. T ```{r} library(rtables) library(dplyr) -lyt <- basic_table() %>% - split_rows_by("SEX", split_fun = keep_split_levels(c("F", "M"))) %>% - split_cols_by("ARM") %>% - analyze("BMRKR1") %>% +lyt <- basic_table() %>% + split_rows_by("SEX", split_fun = keep_split_levels(c("F", "M"))) %>% + split_cols_by("ARM") %>% + analyze("BMRKR1") %>% print() -tbl <- build_table(lyt, ex_adsl) %>% +tbl <- build_table(lyt, ex_adsl) %>% print() ``` @@ -57,7 +56,7 @@ setMethod( This was evident if we searched for methods associated with the class `PreDataTableLayouts`, where only `show` is connected to a sort of printing machinery: -```{r, eval=TRUE} +```{r} methods(class = "PreDataTableLayouts") ``` @@ -98,7 +97,7 @@ setMethod("toString", "VTableTree", function(x, }) ``` -This is only a wrapper/dispatcher to the core toString function in `formatters`, beside the `indent_size` specification. This is based on the "rendering-ready" class `MatrixPrintForm` that is produced by `matrix_form`. The latter is the first core transformation that we need to know to understand the printing process. All exporters and printers are based on `MatrixPrintForm` objects, hence any bug or problem needs to tracked down to this function or `toString`. If we take a look at `toString` for `"listing_df"` in `rlistings`, we will find a shallow wrapper that dispatches to `MatrixPrintForm` objects: +This is only a wrapper/dispatcher to the core `toString` function in `formatters`, beside the `indent_size` specification. This is based on the "rendering-ready" class `MatrixPrintForm` that is produced by `matrix_form`. The latter is the first core transformation that we need to know to understand the printing process. All exporters and printers are based on `MatrixPrintForm` objects, hence any bug or problem needs to tracked down to this function or `toString`. If we take a look at `toString` for `"listing_df"` in `rlistings`, we will find a shallow wrapper that dispatches to `MatrixPrintForm` objects: ```{r, eval=FALSE} setMethod("toString", "listing_df", function(x, ...) { @@ -124,21 +123,21 @@ setMethod( expand_newlines = TRUE, indent_size = 2) { stopifnot(is(obj, "VTableTree")) - - #-> Read .tbl_header_mat and subfunctions (based largely on cinfo) it can help for understanding - # column structure and how it is printed (we can add a description of this process xxx) + + #-> Read .tbl_header_mat and subfunctions (based largely on cinfo) it can help for understanding + # column structure and how it is printed (we can add a description of this process xxx) # Note: it contains the display of column counts directives and specifics header_content <- .tbl_header_mat(obj) # first col are for row.names or topleft info nr_header <- nrow(header_content$body) # colcounts were added in .tbl_header_mat - + #-> As before, reading this function can help understanding how the content of the table is transformed - # in row content and how the structure of the table is preserved in a compact manner. It is complex + # in row content and how the structure of the table is preserved in a compact manner. It is complex # function as it is a recursive one with the different dispatcher but following how different section_div # are printed (with the dedicated assignment function) can help understanding the table structure and its # row-wise transformation. # Summary of row contents - reprint_inds specifies which rows to reprint (hence the grouping) sr <- make_row_df(obj) - + # With get_formatted_cells we get relevant information inside the table tree body_content_strings <- if (NROW(sr) == 0) { character() @@ -154,7 +153,7 @@ setMethod( } else { cbind("", get_formatted_cells(obj, shell = TRUE)) } - + #-> Here spans are extracted for each row. Spans are rarely modified beyond its standard values. # Takes the flatten spans for each row and repeats them according to the number elements tsptmp <- lapply(collect_leaves(obj, TRUE, TRUE), function(rr) { @@ -210,7 +209,7 @@ setMethod( incr = indent_size ) } - + #-> referential strings are added to the table. get_ref_matrix is the core of this process # along with format_fnote_ref that in this case is used to format the reference string and their # indices. Note that the footnotes for the header is taken from the output of .tbl_header_mat @@ -234,7 +233,7 @@ setMethod( nrow = nrow(body), ncol = ncol(body) ) - + # Solve \n in titles # This is something that is relevant in toString - NO MORE USED HERE # if (any(grepl("\n", all_titles(obj)))) { # if (any(grepl("\n", main_title(obj)))) { @@ -245,12 +244,12 @@ setMethod( # subtitles(obj) <- .quick_handle_nl(subtitles(obj)) # } # } - # + # # # Solve \n in footers # main_footer(obj) <- .quick_handle_nl(main_footer(obj)) # prov_footer(obj) <- .quick_handle_nl(prov_footer(obj)) - + #-> this is still under development as indicated by xxx. The idea is to allow \n also in peculiar # cases, such as page titles and referential footnotes. The latter are resolved in toString (pagination # will not count them as more than one line each), while for the former we do not have any coverage yet. @@ -266,7 +265,7 @@ setMethod( formats = formats, ## display = display, purely a function of spans, handled in constructor now row_info = sr, #-> FUNDAMENTAL: this is the data.frame that contains all the information about the rows - # it is the most complex data brought forward into toString + # it is the most complex data brought forward into toString ## line_grouping handled internally now line_grouping = 1:nrow(body), ref_fnotes = ref_fnotes, nlines_header = nr_header, ## this is fixed internally @@ -305,13 +304,13 @@ setMethod( ## we intentionally silently ignore indent_rownames because listings have ## no rownames, but formatters::vert_pag_indices calls matrix_form(obj, TRUE) ## unconditionally. - - + + # Keeping only displayed columns cols <- attr(obj, "listing_dispcols") # this is the list of columns to be displayed listing <- obj[, cols] atts <- attributes(obj) - atts$names <- cols + atts$names <- cols attributes(listing) <- atts keycols <- get_keycols(listing) @@ -418,15 +417,15 @@ setMethod("toString", "MatrixPrintForm", function(x, fontspec = font_spec(), ttype_ok = FALSE) { # part 1: checks and widths/max width estimation for columns - propose_column_widths and .handle_max_width - # + # # part 2: wrapping for the table - do_cell_fnotes_wrap - # + # # part 3: column gap and cell widths calculations (after wrapping) - .calc_cell_widths - # + # # part 4: collapse text body and wrapping titles/footers - # + # # part 5: final cat() -} +}) ``` We rely on the future developer to fill in the blanks in the above description and to follow up the various functions to their core mechanics. From e7e1b52cf9edc5eebfb496e215f51a85549ad7e2 Mon Sep 17 00:00:00 2001 From: Davide Garolini Date: Wed, 14 Aug 2024 16:52:05 +0200 Subject: [PATCH 6/6] Update vignettes/dev-guide/dg_printing.Rmd Co-authored-by: Abinaya Yogasekaram <73252787+ayogasekaram@users.noreply.github.com> Signed-off-by: Davide Garolini --- vignettes/dev-guide/dg_printing.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/dev-guide/dg_printing.Rmd b/vignettes/dev-guide/dg_printing.Rmd index 71049e0d8b..e0bca1b877 100644 --- a/vignettes/dev-guide/dg_printing.Rmd +++ b/vignettes/dev-guide/dg_printing.Rmd @@ -18,7 +18,7 @@ knitr::opts_chunk$set(comment = "#") ## Disclaimer -In comparison to other entries of the developer guide, this is intended to keep track of the general concepts and processing pipeline behind the printing machinery. It is not intended to be a complete documentation of the machinery itself, but rather a collection of notes that can be used to understand the machinery and its internals. Hence, be aware that this is a working document that captures a snapshot of the machinery at a certain point in time. It is not meant to be fully maintained, but it can be used as a starting point for one. Differently from other parts of the developer guide, this will contain the current state of `rlistings`' printing machinery, which is often a simplified version of the machinery that is used in `rtables`. +In comparison to other entries of the developer guide, this is intended to keep track of the general concepts and processing pipeline behind the printing machinery. It is not intended to be a complete documentation of the machinery itself, but rather a collection of notes that can be used to understand the machinery and its internals. Hence, be aware that this is a working document that captures a snapshot of the machinery at a certain point in time. It is not meant to be fully maintained, but it can be used as a starting point for one. Compared to other parts of the developer guide, this will contain the current state of `rlistings`' printing machinery, which is often a simplified version of the machinery that is used in `rtables`. ### How `print` works