From b59f189e6aee5815b434de67bab1b846d31465b5 Mon Sep 17 00:00:00 2001 From: Vincent Arel-Bundock Date: Fri, 19 Jan 2024 10:05:38 -0500 Subject: [PATCH] `tt_grid()` (#82) Markdown uses Grid format for pandoc-compatible tables with column spans --- R/build_tt.R | 24 ++- R/format_tt.R | 9 -- R/group_tt.R | 9 +- R/print.R | 13 +- R/save_tt.R | 139 +++++++++--------- R/style_tt.R | 2 +- R/tt.R | 44 +++--- R/tt_bootstrap.R | 2 +- R/tt_grid.R | 112 ++++++++++++++ README.md | 13 +- README.qmd | 11 +- .../_tinysnapshot/markdown-nocolnames.txt | 26 ++-- inst/tinytest/test-html.R | 12 +- inst/tinytest/test-latex.R | 28 ++-- inst/tinytest/test-markdown.R | 7 +- man/format_tt.Rd | 3 - man/group_tt.Rd | 3 + man/knit_print.tinytable.Rd | 6 +- man/save_tt.Rd | 17 ++- man/style_tt.Rd | 2 +- man/tt.Rd | 21 +-- vignettes/tutorial.qmd | 40 +++-- 22 files changed, 356 insertions(+), 187 deletions(-) create mode 100755 R/tt_grid.R diff --git a/R/build_tt.R b/R/build_tt.R index 607ed2e7..08609c13 100644 --- a/R/build_tt.R +++ b/R/build_tt.R @@ -1,10 +1,12 @@ # internal function # style_tt() stores style calls and we only want to evaluate them at the end because # some rows may be added, which changes how the style is applied -build_tt <- function(x) { +build_tt <- function(x, output = NULL) { m <- meta(x) + output <- sanitize_output(output) out <- x + out <- meta(out, "output", output) # format data before drawing the table for (l in m$lazy_format) { @@ -13,27 +15,47 @@ build_tt <- function(x) { l[["x"]] <- tmp out <- eval(l) } + # shouldn't have to add this everywhere, but I'm too lazy to check + out <- meta(out, "output", output) # draw the table lazy_tt <- meta(x, "lazy_tt") lazy_tt[["x"]] <- out + if (output == "html") { + lazy_tt[[1]] <- quote(tt_bootstrap) + } else if (output == "latex") { + lazy_tt[[1]] <- quote(tt_tabularray) + } else if (output == "markdown") { + lazy_tt[[1]] <- quote(tt_grid) + } out <- eval(lazy_tt) + out <- meta(out, "output", output) # group the table (before style) for (l in m$lazy_group) { l[["x"]] <- out + if (output == "html") { + l[[1]] <- quote(group_bootstrap) + } else if (output == "latex") { + l[[1]] <- quote(group_tabularray) + } else if (output == "markdown") { + l[[1]] <- quote(group_grid) + } out <- eval(l) } + out <- meta(out, "output", output) # style the table for (l in m$lazy_style) { l[["x"]] <- out out <- eval(l) } + out <- meta(out, "output", output) m <- meta(x) m$lazy_style <- list() attr(out, "tinytable_meta") <- m + out <- meta(out, "output", output) return(out) } diff --git a/R/format_tt.R b/R/format_tt.R index 142e5dd0..00598537 100644 --- a/R/format_tt.R +++ b/R/format_tt.R @@ -1,7 +1,3 @@ -# output is selected automatically if format_tt is called in tt() -# x is inserted automatically if format_tt is called in tt() - - #' Format columns of a data frame #' #' This function formats the columns of a data frame based on the column type (logical, date, numeric). @@ -37,7 +33,6 @@ #' format_tt <- function(x, j = NULL, - output = NULL, digits = getOption("digits"), num_fmt = "significant", num_zero = TRUE, @@ -56,7 +51,6 @@ format_tt <- function(x, if (inherits(out, "tinytable")) { cal <- call("format_tt_lazy", j = j, - output = output, digits = digits, num_fmt = num_fmt, num_zero = num_zero, @@ -73,7 +67,6 @@ format_tt <- function(x, out <- format_tt_lazy(out, j = j, - output = output, digits = digits, num_fmt = num_fmt, num_zero = num_zero, @@ -91,7 +84,6 @@ format_tt <- function(x, format_tt_lazy <- function(x, j = NULL, - output = NULL , digits, num_fmt = "significant", num_zero = FALSE, @@ -130,7 +122,6 @@ format_tt_lazy <- function(x, assert_function(identity) assert_string(sprintf, null.ok = TRUE) - output <- sanitize_output(output) # column index NULL or regex or integer vector if (is.null(j)) { diff --git a/R/group_tt.R b/R/group_tt.R index c3664186..7a1c92a8 100644 --- a/R/group_tt.R +++ b/R/group_tt.R @@ -3,11 +3,11 @@ #' @export #' @inheritParams tt #' @inheritParams style_tt +#' @return An object of class `tt` representing the table. #' @param indent integer number of `pt` to use when indenting the non-labelled rows. group_tt <- function(x, i = NULL, j = NULL, indent = 1) { if (is.null(meta(x))) stop("`x` must be generated by `tinytable::tt()`.", call. = FALSE) - if (!isTRUE(meta(x, "output") %in% c("html", "latex"))) return(x) if (is.null(i) && is.null(j)) stop("At least one of `i` or `j` must be specified.", call. = FALSE) assert_integerish(indent, lower = 0) @@ -22,11 +22,8 @@ group_tt <- function(x, i = NULL, j = NULL, indent = 1) { # we don't need this as a list, and we use some sorting later i <- unlist(i) - if (meta(out)$output == "latex") { - cal <- call("group_tabularray", i = i, j = j, indent = indent) - } else if (meta(out)$output == "html") { - cal <- call("group_bootstrap", i = i, j = j, indent = indent) - } + # the actual function is subbed in build_tt for html and grid + cal <- call("group_tabularray", i = i, j = j, indent = indent) out <- meta(out, "lazy_group", c(meta(out)$lazy_group, list(cal))) diff --git a/R/print.R b/R/print.R index 3d87be47..7c5883fd 100644 --- a/R/print.R +++ b/R/print.R @@ -2,9 +2,12 @@ #' #' @keywords internal #' @export -knit_print.tinytable <- function(x, ...) { +knit_print.tinytable <- function(x, + output = getOption("tinytable_print_output", default = NULL), + ...) { + # lazy styles get evaluated here, at the very end - out <- build_tt(x) + out <- build_tt(x, output = output) if (meta(out)$output == "html") { # from htmltools:::html_preserve @@ -23,9 +26,11 @@ knit_print.tinytable <- function(x, ...) { #' @export -print.tinytable <- function(x, ...) { +print.tinytable <- function(x, + output = getOption("tinytable_print_output", default = NULL), + ...){ # lazy styles get evaluated here, at the very end - out <- build_tt(x) + out <- build_tt(x, output = output) if (meta(out, "output") == "latex") { class(out) <- "character" diff --git a/R/save_tt.R b/R/save_tt.R index ea67f2b1..49188114 100644 --- a/R/save_tt.R +++ b/R/save_tt.R @@ -3,60 +3,79 @@ #' This function saves an object of class tinytable to a specified file and format, with an option to overwrite existing files. #' #' @param x The tinytable object to be saved. -#' @param filename A string representing the path to the file where the object should be saved. The supported file formats are: .html, .png, .md, .pdf, and .tex. +#' @param output String or file path. +#' + If `output` is "markdown", "latex", or "html", the table is returned in a string as an `R` object. +#' + If `output` is a valid file path, the table is saved to file. The supported extensions are: .html, .png, .pdf, .tex and .md (with aliases .txt, .Rmd and .qmd). #' @param overwrite A logical value indicating whether to overwrite an existing file. -#' @return invisible(TRUE) +#' @return A string or `TRUE` when the table is written to file. #' @export #' @examples -#' \dontrun{ #' #' library(tinytable) -#' tab <- tt(mtcars[1:4, 1:4]) -#' save_tt(tt, "path/to/file.txt") +#' filename <- file.path(tempdir(), "table.tex") +#' tt(mtcars[1:4, 1:4]) |> save_tt(filename) #' -#' } -#' -save_tt <- function(x, filename, overwrite = FALSE) { -m <- meta(x) +save_tt <- function(x, output, overwrite = FALSE) { + m <- meta(x) -assert_string(filename) -assert_flag(overwrite) -if (file.exists(filename) && !overwrite) { - stop("File already exists and overwrite is set to FALSE.", call. = FALSE) -} -if (is.null(m)) { - stop("`x` must be an object produced by the `tinytable::tt()` function.", call. = FALSE) -} + assert_string(output) + assert_flag(overwrite) + if (file.exists(output) && !overwrite) { + stop("File already exists and overwrite is set to FALSE.", call. = FALSE) + } + if (is.null(m)) { + stop("`x` must be an object produced by the `tinytable::tt()` function.", call. = FALSE) + } + + if (identical(output, "markdown")) { + out <- build_tt(x, output = "markdown") + return(as.character(out)) + } else if (identical(output, "html")) { + out <- build_tt(x, output = "html") + return(as.character(out)) + } else if (identical(output, "latex")) { + out <- build_tt(x, output = "latex") + return(as.character(out)) + } -file_ext <- tools::file_ext(filename) + file_ext <- tools::file_ext(output) -sanity_file_extension(x, file_ext) + output_format <- switch(file_ext, + "png" = "html", + "html" = "html", + "pdf" = "latex", + "tex" = "latex", + "md" = "markdown", + "Rmd" = "markdown", + "qmd" = "markdown", + "txt" = "markdown", + stop("The supported file extensions are: .png, .html, .pdf, .tex, and .md.", call. = FALSE)) -# evaluate styles at the very end of the pipeline, just before writing -x <- build_tt(x) + # evaluate styles at the very end of the pipeline, just before writing + x <- build_tt(x, output = output_format) -if (file_ext %in% c("html", "tex", "md", "Rmd", "qmd", "txt")) { - write(x, file = filename) + if (file_ext %in% c("html", "tex", "md", "Rmd", "qmd", "txt")) { + write(x, file = output) -} else if (file_ext == "png") { - assert_dependency("webshot2") - d <- tempdir() - f <- file.path(d, "index.html") - write(x, file = f) - webshot2::webshot( - f, - file = filename, - selector = "body > div > table", - zoom = 4) + } else if (file_ext == "png") { + assert_dependency("webshot2") + d <- tempdir() + f <- file.path(d, "index.html") + write(x, file = f) + webshot2::webshot( + f, + file = output, + selector = "body > div > table", + zoom = 4) -} else if (file_ext == "pdf") { - assert_dependency("tinytex") - # \documentclass{standalone} does not support \begin{table} - tmp <- strsplit(x, "\\n")[[1]] - tmp <- tmp[!grepl("\\begin{table}", tmp, fixed = TRUE)] - tmp <- tmp[!grepl("\\end{table}", tmp, fixed = TRUE)] - tmp <- paste(tmp, collapse = "\n") - tmp <- sprintf(" + } else if (file_ext == "pdf") { + assert_dependency("tinytex") + # \documentclass{standalone} does not support \begin{table} + tmp <- strsplit(x, "\\n")[[1]] + tmp <- tmp[!grepl("\\begin{table}", tmp, fixed = TRUE)] + tmp <- tmp[!grepl("\\end{table}", tmp, fixed = TRUE)] + tmp <- paste(tmp, collapse = "\n") + tmp <- sprintf(" \\documentclass{standalone} \\usepackage{tabularray} \\usepackage{float} @@ -69,38 +88,12 @@ if (file_ext %in% c("html", "tex", "md", "Rmd", "qmd", "txt")) { \\begin{document} %s \\end{document}", - tmp) - d <- tempdir() - f <- file.path(d, "index.tex") - write(tmp, f) - tinytex::xelatex(f, pdf_file = filename) + tmp) + d <- tempdir() + f <- file.path(d, "index.tex") + write(tmp, f) + tinytex::xelatex(f, pdf_file = output) } return(invisible(TRUE)) -} - - -sanity_file_extension <- function(x, file_ext) { - m <- meta(x) - - # Define the expected output for each extension - expected_output <- switch(file_ext, - "png" = "html", - "html" = "html", - "pdf" = "latex", - "tex" = "latex", - "md" = "markdown", - "Rmd" = "markdown", - "qmd" = "markdown", - "txt" = "markdown", - stop("Unsupported file extension", call. = FALSE)) - - # Check if the actual output matches the expected output - if (!is.null(m) && !is.null(m$output) && m$output != expected_output) { - stop(paste("For", file_ext, "files, the `output` argument should be:", expected_output), call. = FALSE) - } - - # If everything is fine, return a success message or perform other actions - return(paste("File extension and output format are compatible.")) -} - + } diff --git a/R/style_tt.R b/R/style_tt.R index 72921377..93dc35d0 100644 --- a/R/style_tt.R +++ b/R/style_tt.R @@ -31,7 +31,7 @@ #' @param tabularray_inner A string that specifies the "inner" settings of a tabularray LaTeX table. #' @param tabularray_outer A string that specifies the "outer" settings of a tabularray LaTeX table. #' @param ... extra arguments are ignored -#' @return Returns a modified `tinytable` object with the applied styles. +#' @return An object of class `tt` representing the table. #' @template latex_preamble #' @export #' @examples diff --git a/R/tt.R b/R/tt.R index 985b3218..d340264e 100644 --- a/R/tt.R +++ b/R/tt.R @@ -1,13 +1,15 @@ #' Draw a Tiny Table #' -#' The `tt` function renders a table in different formats (HTML, Markdown, or LaTeX) with various styling options. The table can be customize with additional functions: +#' @description +#' The `tt` function renders a table in different formats (HTML, Markdown, or LaTeX) with various styling options. The table can be customized with additional functions: #' #' * `style_tt()` to style fonts, colors, alignment, etc. #' * `format_tt()` to format numbers, dates, strings, etc. -#' * `save_tt()` to save the table to a file. +#' * `save_tt()` to save the table to a file or return the table as a string. +#' +#' `tinytable` attempts to determine the appropriate way to print the table based on interactive use, RStudio availability, and output format in RMarkdown or Quarto documents. Users can call `print(x, output="markdown")` to print the table in a specific format. Alternatively, they can set a global option: `options("tinytable_print_output"="markdown")` #' #' @param x A data frame or data table to be rendered as a table. -#' @param output The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents. #' @param digits Number of significant digits to keep for numeric variables. When `digits` is an integer, `tt()` calls `format_tt(x, digits = digits)` before proceeding to draw the table. Users who need more control can proceed in two steps: (1) format the data with `format_tt()` or other functions, and (2) pass the formatted data to `tt()` for drawing. See `?format_tt` for more details on formating options (ex: decimal, scientific notation, dates, boolean variables, etc.). #' @param caption A string that will be used as the caption of the table. #' @param width A numeric value between 0 and 1 indicating the proportion of the line width that the table should cover. @@ -20,7 +22,6 @@ #' @return An object of class `tt` representing the table. #' @template latex_preamble #' -#' @examplesIf getOption("tt_local", default = FALSE) #' @examples #' library(tinytable) #' x <- mtcars[1:4, 1:5] @@ -28,22 +29,22 @@ #' tt(x) #' #' tt(x, -#' theme = "striped", -#' width = 0.5, -#' caption = "Data about cars.") -#' +#' theme = "striped", +#' width = 0.5, +#' caption = "Data about cars.") +#' #' @export tt <- function(x, - output = NULL, digits = NULL, caption = NULL, width = NULL, notes = NULL, theme = "default", - placement = getOption("tt_tabularray_placement", default = NULL)) { + placement = getOption("tinytable_tabularray_placement", default = NULL)) { + + output <- meta(x, "output") # sanity checks - output <- sanitize_output(output) assert_data_frame(x) assert_string(caption, null.ok = TRUE) assert_numeric(width, len = 1, lower = 0, upper = 1, null.ok = TRUE) @@ -70,15 +71,18 @@ tt <- function(x, class(out) <- c("tinytable", class(out)) # build table - if (output == "latex") { - cal <- call("tt_tabularray", x = out, caption = caption, theme = theme, width = width, notes = notes, placement = placement) - - } else if (output == "html"){ - cal <- call("tt_bootstrap", x = out, caption = caption, theme = theme, width = width, notes = notes) - - } else { - cal <- call("tt_markdown", x = out, caption = caption) - } + cal <- call("tt_tabularray", x = out, caption = caption, theme = theme, width = width, notes = notes, placement = placement) + # if (output == "latex") { + # + # } else if (output == "html"){ + # cal <- call("tt_bootstrap", x = out, caption = caption, theme = theme, width = width, notes = notes) + # + # } else if (output == "markdown") { + # cal <- call("tt_grid", x = out, caption = caption) + # + # } else { + # stop("here be dragons") + # } out <- meta(out, "lazy_tt", cal) diff --git a/R/tt_bootstrap.R b/R/tt_bootstrap.R index c85fa0d3..eeacad80 100644 --- a/R/tt_bootstrap.R +++ b/R/tt_bootstrap.R @@ -1,4 +1,4 @@ -tt_bootstrap <- function(x, caption, theme, width, notes) { +tt_bootstrap <- function(x, caption, theme, width, notes, ...) { template <- template_bootstrap(theme) m <- meta(x) diff --git a/R/tt_grid.R b/R/tt_grid.R new file mode 100755 index 00000000..b3f75192 --- /dev/null +++ b/R/tt_grid.R @@ -0,0 +1,112 @@ +grid_line <- function(col_widths, char = "-") { + line_sep <- lapply(col_widths, function(k) strrep(char, k)) + line_sep <- paste(line_sep, collapse = "+") + line_sep <- paste0("+", line_sep, "+") + return(line_sep) +} + + +tt_grid <- function(x, col_widths = NULL, ...) { + + m <- meta(x) + + if (is.null(col_widths)) { + col_widths <- m$col_widths + } + + tab <- as.matrix(x) + if (!is.null(names(x))) { + header <- TRUE + tab <- rbind(colnames(x), tab) + } else { + header <- FALSE + } + + # pad for readability + padded <- sprintf(" %s ", tab) + tab <- matrix(padded, ncol = ncol(tab)) + + col_widths_auto <- NULL + for (j in 1:ncol(x)) { + if (is.null(col_widths)) { + tab[, j] <- format(tab[, j]) + col_widths_auto[j] <- nchar(tab[1, j]) + } else { + tab[, j] <- format(tab[, j], width = col_widths[j]) + } + } + + if (is.null(col_widths)) col_widths <- col_widths_auto + + rule_head <- grid_line(col_widths, "=") + rule_line <- grid_line(col_widths, "-") + + body <- apply(tab, 1, paste, collapse = "|") + body <- paste0("|", body, "|") + if (header) { + tab <- c("\n", rule_line, body[1], rule_head, body[2:length(body)], rule_line, "\n") + } else { + tab <- c("\n", rule_line, body, rule_line, "\n") + } + + out <- paste(tab, collapse = "\n") + + # rebuild output + attr(out, "tinytable_meta") <- m + out <- meta(out, "col_widths", col_widths) + out <- meta(out, "output", "grid") + class(out) <- c("tinytable", "knitr_kable") + + # output + return(out) +} + + +empty_cells <- function(lst) { + # Find the largest number in the list + max_num <- max(unlist(lst)) + + # Create the full range from 1 to the largest number + full_range <- 1:max_num + + # Find missing numbers (holes) + missing_nums <- setdiff(full_range, unlist(lst)) + + # Create new elements for missing numbers + new_elements <- split(missing_nums, cumsum(c(1, diff(missing_nums) != 1))) + + # Name the new elements with empty strings and merge with original list + names(new_elements) <- rep(" ", length(new_elements)) + filled_list <- c(lst, new_elements) + + # Sort the list by the minimum number in each element + filled_list[order(sapply(filled_list, min))] +} + + +group_grid <- function(x, j, ...) { + header <- empty_cells(j) + cw <- meta(x, "col_widths") + cw <- sapply(header, function(k) sum(cw[k]) + length(cw[k]) - 1) + txt <- t(matrix(names(cw))) + out <- tt_grid(txt, cw) + out <- strsplit(out, split = "\\n")[[1]] + out <- out[out != "\\n"] + out <- out[!out %in% c("\\n", "")] + x <- strsplit(x, split = "\\n")[[1]] + x <- x[!x %in% c("\\n", "")] + out <- out[1:(length(out) - 1)] + out <- paste(c(out, x), collapse = "\n") + return(out) +} + + +df <- data.frame( + Fruit = c("Bananas", "Oranges"), + Price = c("$1.34", "$2.10"), + Features = c(2, 4.3), + Color = c("Yellow", "Orange"), + Smell = c("Bad", "Good") +) + + diff --git a/README.md b/README.md index f2684a4c..51330f32 100644 --- a/README.md +++ b/README.md @@ -66,13 +66,24 @@ three pillars: ## Installation -You can install `tinytable` from [GitHub](https://github.com/) with: +Install the stable version from CRAN: + +``` r +```r +install.packages("tinytable") +``` + +`tinytable` is a relatively new package with rapid development. If you +want to benefit from the latest features—showcased on the package +website—you may want to install the development version from Github. ``` r library(remotes) install_github("vincentarelbundock/tinytable") ``` +Restart `R` completely for the installation to take effect. + ## Get started - [Tutorial diff --git a/README.qmd b/README.qmd index 8b08d678..741f13c6 100644 --- a/README.qmd +++ b/README.qmd @@ -48,13 +48,22 @@ To achieve these goals, the design philosophy of `tinytable` rests on three pill ## Installation -You can install `tinytable` from [GitHub](https://github.com/) with: +Install the stable version from CRAN: +``` r +```r +install.packages("tinytable") +``` + +`tinytable` is a relatively new package with rapid development. If you want to benefit from the latest features---showcased on the package website---you may want to install the development version from Github. ``` r library(remotes) install_github("vincentarelbundock/tinytable") ``` +Restart `R` completely for the installation to take effect. + + ## Get started * [Tutorial (HTML)](https://vincentarelbundock.github.io/tinytable/vignettes/tutorial.html) diff --git a/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt b/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt index 5097abce..eca4486a 100644 --- a/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt +++ b/inst/tinytest/_tinysnapshot/markdown-nocolnames.txt @@ -1,11 +1,17 @@ -| 5.1 | 3.5 | 1.4 | 0.2 | setosa | -| 4.9 | 3 | 1.4 | 0.2 | setosa | -| 4.7 | 3.2 | 1.3 | 0.2 | setosa | -| 4.6 | 3.1 | 1.5 | 0.2 | setosa | -| 5 | 3.6 | 1.4 | 0.2 | setosa | -| 5.4 | 3.9 | 1.7 | 0.4 | setosa | -| 4.6 | 3.4 | 1.4 | 0.3 | setosa | -| 5 | 3.4 | 1.5 | 0.2 | setosa | -| 4.4 | 2.9 | 1.4 | 0.2 | setosa | -| 4.9 | 3.1 | 1.5 | 0.1 | setosa | + + ++-----+-----+-----+-----+--------+ +| 5.1 | 3.5 | 1.4 | 0.2 | setosa | +| 4.9 | 3.0 | 1.4 | 0.2 | setosa | +| 4.7 | 3.2 | 1.3 | 0.2 | setosa | +| 4.6 | 3.1 | 1.5 | 0.2 | setosa | +| 5.0 | 3.6 | 1.4 | 0.2 | setosa | +| 5.4 | 3.9 | 1.7 | 0.4 | setosa | +| 4.6 | 3.4 | 1.4 | 0.3 | setosa | +| 5.0 | 3.4 | 1.5 | 0.2 | setosa | +| 4.4 | 2.9 | 1.4 | 0.2 | setosa | +| 4.9 | 3.1 | 1.5 | 0.1 | setosa | ++-----+-----+-----+-----+--------+ + + diff --git a/inst/tinytest/test-html.R b/inst/tinytest/test-html.R index f534ee64..072d77d9 100644 --- a/inst/tinytest/test-html.R +++ b/inst/tinytest/test-html.R @@ -1,6 +1,7 @@ source("helpers.R") using("tinysnapshot") +options(tinytable_print_output = "html") clean <- function(x) { @@ -12,15 +13,15 @@ clean <- function(x) { x <- mtcars[1:4, 1:5] -tab <- tt(x, output = "html", theme = "striped") +tab <- tt(x, theme = "striped") expect_equal_to_reference(clean(tab), "_tinysnapshot/html-striped.rds") -tab <- tt(x, output = "html", theme = "striped") |> +tab <- tt(x, theme = "striped") |> style_tt(color = "orange") expect_equal_to_reference(clean(tab), "_tinysnapshot/html-striped_orange.rds") # tutorial.qmd: vectorized settings -tab <- tt(x, output = "html") |> +tab <- tt(x) |> style_tt( j = 2:3, color = c("orange", "green"), @@ -28,7 +29,7 @@ tab <- tt(x, output = "html") |> expect_equal_to_reference(clean(tab), "_tinysnapshot/html-vectorized_color_j.rds") # Issue #58 -tab <- tt(iris[1:10,], output = "html") |> +tab <- tt(iris[1:10,]) |> style_tt(align = "c") |> group_tt(j = list("Sepal" = 1:2, "Petal" = 3:4)) expect_equal_to_reference(clean(tab), "_tinysnapshot/html-issue58.rds") @@ -40,7 +41,7 @@ colnames(k) <- NULL bg <- hcl.colors(20, "Inferno") fg <- ifelse(as.matrix(k) < 17, tail(bg, 1), head(bg, 1)) fs <- 1:20 -tab <- tt(k, output = "html", width = .5, theme = "void") |> +tab <- tt(k, width = .5, theme = "void") |> style_tt( i = 1:4, j = 1:5, @@ -50,3 +51,4 @@ tab <- tt(k, output = "html", width = .5, theme = "void") |> expect_equal_to_reference(clean(tab), "_tinysnapshot/html-heatmap.rds") +options(tinytable_print_output = NULL) diff --git a/inst/tinytest/test-latex.R b/inst/tinytest/test-latex.R index b4065d52..26b41ab5 100644 --- a/inst/tinytest/test-latex.R +++ b/inst/tinytest/test-latex.R @@ -1,56 +1,62 @@ source("helpers.R") using("tinysnapshot") +options(tinytable_print_output = "latex") x <- mtcars[1:4, 1:5] expect_snapshot_print( - tt(x, output = "latex"), + tt(x), label = "latex-default") k <- x colnames(k) <- NULL expect_snapshot_print( - tt(k, output = "latex"), + tt(k), label = "latex-nohead") # Align expect_snapshot_print( - tt(x, output = "latex") |> style_tt(j = 1:5, align = "ccllr"), + tt(x) |> style_tt(j = 1:5, align = "ccllr"), label = "latex-align") # Themes expect_snapshot_print( - tt(x, output = "latex", theme = "striped"), + tt(x, theme = "striped"), label = "latex-theme_striped") expect_snapshot_print( - tt(x, output = "latex", theme = "grid"), + tt(x, theme = "grid"), label = "latex-theme_grid") expect_snapshot_print( - tt(x, output = "latex", theme = "void"), + tt(x, theme = "void"), label = "latex-theme_void") # Styles expect_snapshot_print( - tt(x, output = "latex") |> style_tt(i = 1:4, color = "orange"), + tt(x) |> style_tt(i = 1:4, color = "orange"), label = "latex-row_color") expect_snapshot_print( - tt(x, output = "latex") |> style_tt(j = 1:4, color = "orange"), + tt(x) |> style_tt(j = 1:4, color = "orange"), label = "latex-col_color") expect_snapshot_print( - tt(x, output = "latex") |> style_tt(i = 1:2, j = 1:4, color = "orange"), + tt(x) |> style_tt(i = 1:2, j = 1:4, color = "orange"), label = "latex-cell_color") # Lazy style: group after style is respected -a <- tt(mtcars[1:4, 1:4], output = "latex") |> +a <- tt(mtcars[1:4, 1:4]) |> style_tt(color = "orange", background = "black") |> group_tt(j = list("blah" = 1:2, "bar" = 3:4)) -b <- tt(mtcars[1:4, 1:4], output = "latex") |> +b <- tt(mtcars[1:4, 1:4]) |> group_tt(j = list("blah" = 1:2, "bar" = 3:4)) |> style_tt(color = "orange", background = "black") expect_snapshot_print(a, label = "latex-group_style_order") expect_equal(as.character(a), as.character(b)) + + + + +options(tinytable_print_output = NULL) diff --git a/inst/tinytest/test-markdown.R b/inst/tinytest/test-markdown.R index 6f16cf76..a38e79d5 100644 --- a/inst/tinytest/test-markdown.R +++ b/inst/tinytest/test-markdown.R @@ -1,10 +1,13 @@ source("helpers.R") using("tinysnapshot") +options(tinytable_print_output = "markdown") # markdown without labels k = iris[1:10,] colnames(k) <- NULL expect_snapshot_print( - tt(k, output = "markdown"), - label = "markdown-nocolnames") + tt(k), label = "markdown-nocolnames") + + +options(tinytable_print_output = NULL) diff --git a/man/format_tt.Rd b/man/format_tt.Rd index ac664419..bd327609 100644 --- a/man/format_tt.Rd +++ b/man/format_tt.Rd @@ -7,7 +7,6 @@ format_tt( x, j = NULL, - output = NULL, digits = getOption("digits"), num_fmt = "significant", num_zero = TRUE, @@ -26,8 +25,6 @@ format_tt( \item{j}{Column indices where the styling should be applied. Can be a single value, a vector, or a Perl-style regular expression applied to column names of the original data frame. If \code{colspan} is used, \code{j} must be of length 1.} -\item{output}{The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents.} - \item{digits}{Number of significant digits or decimal places.} \item{num_fmt}{The format for numeric values; one of 'significant', 'decimal', or 'scientific'.} diff --git a/man/group_tt.Rd b/man/group_tt.Rd index 79561cf9..967aa3a4 100644 --- a/man/group_tt.Rd +++ b/man/group_tt.Rd @@ -15,6 +15,9 @@ group_tt(x, i = NULL, j = NULL, indent = 1) \item{indent}{integer number of \code{pt} to use when indenting the non-labelled rows.} } +\value{ +An object of class \code{tt} representing the table. +} \description{ Spanning labels to identify groups of rows or columns } diff --git a/man/knit_print.tinytable.Rd b/man/knit_print.tinytable.Rd index 114b3698..e14aa66f 100644 --- a/man/knit_print.tinytable.Rd +++ b/man/knit_print.tinytable.Rd @@ -4,7 +4,11 @@ \alias{knit_print.tinytable} \title{Print a tinytable object in knitr} \usage{ -knit_print.tinytable(x, ...) +knit_print.tinytable( + x, + output = getOption("tinytable_print_output", default = NULL), + ... +) } \description{ Print a tinytable object in knitr diff --git a/man/save_tt.Rd b/man/save_tt.Rd index 971d9842..623f127b 100644 --- a/man/save_tt.Rd +++ b/man/save_tt.Rd @@ -4,28 +4,29 @@ \alias{save_tt} \title{Save a Tiny Table to File} \usage{ -save_tt(x, filename, overwrite = FALSE) +save_tt(x, output, overwrite = FALSE) } \arguments{ \item{x}{The tinytable object to be saved.} -\item{filename}{A string representing the path to the file where the object should be saved. The supported file formats are: .html, .png, .md, .pdf, and .tex.} +\item{output}{String or file path. +\itemize{ +\item If \code{output} is "markdown", "latex", or "html", the table is returned in a string as an \code{R} object. +\item If \code{output} is a valid file path, the table is saved to file. The supported extensions are: .html, .png, .pdf, .tex and .md (with aliases .txt, .Rmd and .qmd). +}} \item{overwrite}{A logical value indicating whether to overwrite an existing file.} } \value{ -invisible(TRUE) +A string or \code{TRUE} when the table is written to file. } \description{ This function saves an object of class tinytable to a specified file and format, with an option to overwrite existing files. } \examples{ -\dontrun{ library(tinytable) -tab <- tt(mtcars[1:4, 1:4]) -save_tt(tt, "path/to/file.txt") - -} +filename <- file.path(tempdir(), "table.tex") +tt(mtcars[1:4, 1:4]) |> save_tt(filename) } diff --git a/man/style_tt.Rd b/man/style_tt.Rd index 6fa4081d..fb00de62 100644 --- a/man/style_tt.Rd +++ b/man/style_tt.Rd @@ -83,7 +83,7 @@ style_tt( \item{...}{extra arguments are ignored} } \value{ -Returns a modified \code{tinytable} object with the applied styles. +An object of class \code{tt} representing the table. } \description{ This function applies styling to a table created by \code{tt()}. It allows customization of text style (bold, italic, monospace), text and background colors, font size, cell width, text alignment, column span, and indentation. The function supports both LaTeX (tabularray) and HTML (bootstrap) formats. diff --git a/man/tt.Rd b/man/tt.Rd index 72ed4733..78c9c949 100644 --- a/man/tt.Rd +++ b/man/tt.Rd @@ -6,20 +6,17 @@ \usage{ tt( x, - output = NULL, digits = NULL, caption = NULL, width = NULL, notes = NULL, theme = "default", - placement = getOption("tt_tabularray_placement", default = NULL) + placement = getOption("tinytable_tabularray_placement", default = NULL) ) } \arguments{ \item{x}{A data frame or data table to be rendered as a table.} -\item{output}{The format of the output table. Can be "html", "latex", or "markdown". If NULL, the format is automatically detected in Quarto or Rmarkdown documents.} - \item{digits}{Number of significant digits to keep for numeric variables. When \code{digits} is an integer, \code{tt()} calls \code{format_tt(x, digits = digits)} before proceeding to draw the table. Users who need more control can proceed in two steps: (1) format the data with \code{format_tt()} or other functions, and (2) pass the formatted data to \code{tt()} for drawing. See \code{?format_tt} for more details on formating options (ex: decimal, scientific notation, dates, boolean variables, etc.).} \item{caption}{A string that will be used as the caption of the table.} @@ -40,14 +37,14 @@ tt( An object of class \code{tt} representing the table. } \description{ -The \code{tt} function renders a table in different formats (HTML, Markdown, or LaTeX) with various styling options. The table can be customize with additional functions: -} -\details{ +The \code{tt} function renders a table in different formats (HTML, Markdown, or LaTeX) with various styling options. The table can be customized with additional functions: \itemize{ \item \code{style_tt()} to style fonts, colors, alignment, etc. \item \code{format_tt()} to format numbers, dates, strings, etc. -\item \code{save_tt()} to save the table to a file. +\item \code{save_tt()} to save the table to a file or return the table as a string. } + +\code{tinytable} attempts to determine the appropriate way to print the table based on interactive use, RStudio availability, and output format in RMarkdown or Quarto documents. Users can call \code{print(x, output="markdown")} to print the table in a specific format. Alternatively, they can set a global option: \code{options("tinytable_print_output"="markdown")} } \section{LaTeX preamble}{ @@ -66,16 +63,14 @@ When rendering Quarto and Rmarkdown documents, \code{tinytable} will populate th } \examples{ -\dontshow{if (getOption("tt_local", default = FALSE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} -\dontshow{\}) # examplesIf} library(tinytable) x <- mtcars[1:4, 1:5] tt(x) tt(x, - theme = "striped", - width = 0.5, - caption = "Data about cars.") + theme = "striped", + width = 0.5, + caption = "Data about cars.") } diff --git a/vignettes/tutorial.qmd b/vignettes/tutorial.qmd index e333034b..fd4697fd 100644 --- a/vignettes/tutorial.qmd +++ b/vignettes/tutorial.qmd @@ -35,7 +35,7 @@ Load the library and set some global options: library(tinytable) options(digits = 3) # how many significant digits to print by default -options("tt_tabularray_placement" = "H") # for LaTeX +options("tinytable_tabularray_placement" = "H") # for LaTeX ``` Draw a first table: @@ -48,31 +48,39 @@ tt(x) ## Output formats -`tinytable` can produce tables in HTML, Markdown, or LaTeX (PDF) format. To choose, we use the `output` argument: +`tinytable` can produce tables in HTML, Markdown, LaTeX, PDF, or PNG format. An appropriate output format for printing is automatically selected based on (1) whether the function is called interactively, (2) is called within RStudio, and (3) the output format of the Rmarkdown or Quarto document, if applicable. Alternatively, users can specify the print format in `print()` or by setting a global option: ```{r} #| eval: false -tt(x, output = "html") -tt(x, output = "latex") -tt(x, output = "markdown") -``` +tt(x) |> print("markdown") +tt(x) |> print("html") +tt(x) |> print("latex") -When calling `tinytable` from a Quarto or Rmarkdown document, `tinytable` detects the output format automatically and generates an HTML or LaTeX table as appropriate. This means that we do not need to explicitly specify the `output` format. +options(tinytable_print_output = "markdown") +``` With the `save_tt()` function, users can also save tables directly to PNG images or PDF documents, or any of the basic formats. All we need to do is supply a valid file name with the appropriate extension (ex: `.png`, `.html`, `.pdf`, etc.): ```r -# PNG -tt(x, output = "html") |> save_tt("path/to/file.png") +tt(x) |> save_tt("path/to/file.png") +tt(x) |> save_tt("path/to/file.pdf") +tt(x) |> save_tt("path/to/file.html") +tt(x) |> save_tt("path/to/file.tex") +tt(x) |> save_tt("path/to/file.md") +``` -# HTML -tt(x, output = "html") |> save_tt("path/to/file.html") +`save_tt()` can also return a string with the table in it, for further processing in `R`. In the first case, the table is printed to console with `cat()`. In the second case, it returns as a single string as an `R` object. -# PDF -tt(x, output = "latex") |> save_tt("path/to/file.pdf") +```{r} +tt(x) |> + group_tt(j = list("Hello" = 2:3, "World" = 4:5)) |> + print("markdown") +``` -# Text -tt(x, output = "markdown") |> save_tt("path/to/file.txt") +```{r} +tt(x) |> + group_tt(j = list("Hello" = 2:3, "World" = 4:5)) |> + save_tt("markdown") ``` @@ -724,7 +732,7 @@ cell{3,5}{1,4} = {bg=purple7}, cell{2}{2} = {r=4,c=2}{bg=azure7}, " mtcars[1:5, 1:4] |> - tt(output = "latex", theme = "void") |> + tt(theme = "void") |> style_tt(tabularray_inner = inner) ```