Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c0dfe10
basic table components; no advanced styling
christopherkenny Mar 27, 2026
e4a10fc
basic colors; remove accidental scratch commit
christopherkenny Mar 27, 2026
239452a
improve styling
christopherkenny Mar 27, 2026
1183530
support labels, breaking
christopherkenny Mar 27, 2026
e576554
fix x-refs
christopherkenny Mar 27, 2026
9f75dbf
make sure table doesn't emit extra text
christopherkenny Mar 27, 2026
60416c0
fold stacked text calls into one call
christopherkenny Mar 27, 2026
48c3c90
collapse nested text with no intervening fns
christopherkenny Mar 27, 2026
0ab53e2
look at issues
christopherkenny Mar 27, 2026
431c003
tighten testing
christopherkenny Mar 28, 2026
1538036
polish
christopherkenny Mar 28, 2026
0a0296f
condense testing surface
christopherkenny Mar 28, 2026
2c74a14
more polish
christopherkenny Mar 28, 2026
683d497
reduce id overengineering
christopherkenny Mar 31, 2026
be90bf0
improve markdown in typst
christopherkenny Apr 1, 2026
681b2df
better handling of valid text
christopherkenny Apr 1, 2026
c757959
up
christopherkenny Apr 2, 2026
f784ff1
fix missing
christopherkenny Apr 2, 2026
bf28957
improve html parity
christopherkenny Apr 2, 2026
cdd9eea
missing dots passing
christopherkenny Apr 2, 2026
f36b142
vec & body fns
christopherkenny Apr 2, 2026
bd9a433
review yesterday changes
christopherkenny Apr 2, 2026
852dc71
better match latex for html tags
christopherkenny Apr 2, 2026
f7a6355
fix context; fix fn number
christopherkenny Apr 3, 2026
205b12f
extract support + closer html
christopherkenny Apr 3, 2026
d401bbd
test gt grp, dont force cap
christopherkenny Apr 3, 2026
4d943c6
polish for PR
christopherkenny Apr 3, 2026
4de146d
remove context exprs
christopherkenny Apr 3, 2026
741c04a
simplify
christopherkenny Apr 3, 2026
517ed53
revert change that broke case for #2022
christopherkenny Apr 4, 2026
70e9e91
fix docs; basic table styling
christopherkenny Apr 4, 2026
010a8ff
remove old test
christopherkenny Apr 5, 2026
ed42c6c
Update _pkgdown.yml
christopherkenny Apr 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export(as_gtable)
export(as_latex)
export(as_raw_html)
export(as_rtf)
export(as_typst)
export(as_word)
export(cell_borders)
export(cell_fill)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

* Expand functionality of `gt_group()` to allow `gt_group` objects to be combined with `gt_tbls` (#2128)

* Added Typst output support with the new `as_typst()` export function. Typst output is also supported in `knit_print()`, `gtsave(.typ)`, Quarto rendering, and styled tables with captions, notes, summaries, labels, and markdown content.

# gt 1.3.0

## New features
Expand Down
107 changes: 107 additions & 0 deletions R/export.R
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,113 @@ as_word_tbl_body <- function(
as.character(word_tbl)
}

# as_typst() -------------------------------------------------------------------
#' Output a **gt** object as Typst
#'
#' @description
#'
#' Get the Typst content from a `gt_tbl` object as a single-element character
#' vector. This object can be placed inside a raw Typst block (e.g.,
#' ```` ```{=typst} ```` in Quarto) or written to a `.typ` file for inclusion
#' in a Typst document.
#'
#' @inheritParams gtsave
#'
#' @param container *Top-level Typst container*
#'
#' `singl-kw:[auto|table|figure]` // *default:* `"auto"`
#'
#' Determines the top-level Typst construct used for the export. With
#' `"auto"`, auxiliary content such as titles, subtitles, captions,
#' footnotes, or source notes causes the table to be wrapped in a
#' `figure(...)` with `kind: table`; otherwise, a bare `table(...)` is
#' emitted. Use `"table"` to always emit a bare table; any auxiliary content
#' that requires figure semantics will be dropped with a warning. Use
#' `"figure"` to always emit a figure-wrapped table.
#'
#' @param label *Optional Typst label*
#'
#' `NULL`, `scalar<character>`, or `scalar<logical>` // *default:* `NULL`
#'
#' An optional label to append to the emitted Typst object using Typst's
#' `<label>` syntax. By default (`NULL`), a label is emitted only when the
#' underlying **gt** table has a non-missing `id` value (set via [gt()]). Use
#' `TRUE` to force the same automatic behavior, `FALSE` to suppress label
#' emission, or supply a string to force a specific label.
#'
#' @param breakable *Make figure-wrapped Typst output break across pages?*
#'
#' `scalar<logical>` // *default:* `FALSE`
#'
#' When `TRUE` and the Typst export uses a `figure(...)`, the emitted Typst
#' applies a local `show` rule that sets `block(breakable: true)` for
#' `figure.where(kind: table)`. This has no effect for bare `table(...)`
#' output.
#'
#' @section Examples:
#'
#' Use a subset of the [`gtcars`] dataset to create a **gt** table. Add a header
#' with [tab_header()] and then export the table as Typst code using the
#' `as_typst()` function.
#'
#' ```r
#' tab_typst <-
#' gtcars |>
#' dplyr::select(mfr, model, msrp) |>
#' dplyr::slice(1:5) |>
#' gt() |>
#' tab_header(
#' title = md("Data listing from **gtcars**"),
#' subtitle = md("`gtcars` is an R dataset")
#' ) |>
#' as_typst()
#' ```
#'
#' What's returned is a single-element vector containing the Typst code for the
#' table and any associated notes.
#'
#' @family table export functions
#' @section Function ID:
#' 13-10
#'
#' @section Function Introduced:
#' `v1.4.0`
#'
#' @export
as_typst <- function(
data,
container = c("auto", "table", "figure"),
label = NULL,
breakable = FALSE
) {

# Perform input object validation
stop_if_not_gt_tbl(data = data)

container <-
rlang::arg_match0(
container,
values = c("auto", "table", "figure")
)

if (!is.null(label) && !isFALSE(label) && !isTRUE(label)) {
check_string(label, allow_empty = FALSE, allow_na = FALSE)
}

check_bool(breakable, allow_na = FALSE)

# Build table data using a Typst-specific rendering context so that text,
# units, and footnote marks are resolved as native Typst content.
data <- build_data(data = data, context = "typst")

as_typst_string(
data = data,
container = container,
label = label,
breakable = breakable
)
}

# as_gtable() ------------------------------------------------------------------
#' Transform a **gt** table to a `gtable` object
#'
Expand Down
13 changes: 7 additions & 6 deletions R/extract.R
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,11 @@
#'
#' @param output *Output format*
#'
#' `singl-kw:[html|latex|rtf|word]` // *default:* `"html"`
#' `singl-kw:[html|latex|rtf|word|grid|typst]` // *default:* `"html"`
#'
#' The output format of the resulting data frame. This can either be
#' `"html"` (the default), `"latex"`, `"rtf"`, or `"word"`.
#' `"html"` (the default), `"latex"`, `"rtf"`, `"word"`, `"grid"`, or
#' `"typst"`.
#'
#' @return A data frame or tibble object containing the table body.
#'
Expand Down Expand Up @@ -187,7 +188,7 @@ extract_body <- function(
incl_hidden_cols = FALSE,
incl_stub_cols = TRUE,
...,
output = c("html", "latex", "rtf", "word", "grid")
output = c("html", "latex", "rtf", "word", "grid", "typst")
) {

# Perform input object validation
Expand All @@ -209,7 +210,7 @@ extract_body <- function(
output <-
rlang::arg_match0(
output,
values = c("html", "latex", "rtf", "word", "grid")
values = c("html", "latex", "rtf", "word", "grid", "typst")
)

rlang::check_dots_empty()
Expand Down Expand Up @@ -613,7 +614,7 @@ extract_cells <- function(
data,
columns,
rows = everything(),
output = c("auto", "plain", "html", "latex", "rtf", "word", "grid")
output = c("auto", "plain", "html", "latex", "rtf", "word", "grid", "typst")
) {

# Perform input object validation
Expand All @@ -623,7 +624,7 @@ extract_cells <- function(
output <-
rlang::arg_match0(
output,
values = c("auto", "plain", "html", "latex", "rtf", "word", "grid")
values = c("auto", "plain", "html", "latex", "rtf", "word", "grid", "typst")
)

if (output == "auto") {
Expand Down
13 changes: 13 additions & 0 deletions R/fmt.R
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,7 @@ context_missing_text <- function(missing_text, context) {
html = ,
grid = ,
latex = ,
typst = ,
word =
{
if (!is_asis && missing_text == "---") {
Expand Down Expand Up @@ -923,6 +924,7 @@ context_plusminus_mark <- function(plusminus_mark, context) {
html = ,
latex = ,
grid = ,
typst = ,
word =
{
if (!is_asis && plusminus_mark == " +/- ") {
Expand Down Expand Up @@ -985,6 +987,7 @@ context_lte_mark <- function(context) {
switch(
context,
grid = ,
typst = ,
word = ,
html = "\U02264",
latex = "$\\leq$",
Expand All @@ -1001,6 +1004,7 @@ context_gte_mark <- function(context) {
switch(
context,
grid = ,
typst = ,
word = ,
html = "\U02265",
latex = "$\\geq$",
Expand Down Expand Up @@ -1181,6 +1185,15 @@ context_symbol_str <- function(context, symbol) {
#)
}
},
typst = {
if (inherits(symbol, "AsIs")) {
symbol
} else {
typst_escape_markup(
get_currency_str(currency = symbol, fallback_to_code = TRUE)
)
}
},
get_currency_str(currency = symbol, fallback_to_code = TRUE)
)

Expand Down
28 changes: 28 additions & 0 deletions R/format_data.R
Original file line number Diff line number Diff line change
Expand Up @@ -3000,6 +3000,13 @@ fmt_fraction <- function(
gsub(" ", "", non_fraction_part),
paste0("{\\super ", num_vec, "}/{\\sub ", denom_vec, "}")
)
} else if (context == "typst") {

x_str[has_a_fraction] <-
paste0(
gsub(" ", "", non_fraction_part),
paste0("`", num_vec, "/", denom_vec, "`")
)
}
}

Expand Down Expand Up @@ -10467,6 +10474,9 @@ fmt_markdown <- function(
rtf = function(x) {
markdown_to_rtf(x)
},
typst = function(x) {
markdown_to_typst(x)
},
word = function(x) {
markdown_to_xml(x)
},
Expand Down Expand Up @@ -10715,6 +10725,24 @@ fmt_passthrough <- function(

x_str
},
typst = function(x) {

# Create `x_str` with same length as `x`
x_str <- rep_len(NA_character_, length(x))

# Handle formatting of pattern
x_str <-
apply_pattern_fmt_x(
pattern,
values = x
)

if (escape) {
x_str <- process_text(text = x_str, context = "typst")
}

x_str
},
default = function(x) {

# Create `x_str` with same length as `x`
Expand Down
Loading
Loading