Skip to content

Commit

Permalink
Merge pull request #1495 from kbrevoort/issue-1465
Browse files Browse the repository at this point in the history
Issue 1465
  • Loading branch information
rich-iannone authored Dec 18, 2023
2 parents 8489cf2 + 61296a8 commit 418a1d7
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 0 deletions.
4 changes: 4 additions & 0 deletions R/export.R
Original file line number Diff line number Diff line change
Expand Up @@ -725,15 +725,19 @@ as_latex <- function(data) {
latex_packages <- NULL
}

table_width_bookends <- derive_table_width_bookends(data = data)

# Compose the LaTeX table
knitr::asis_output(
paste0(
table_width_bookends[1L],
table_start,
heading_component,
columns_component,
body_component,
table_end,
footer_component,
table_width_bookends[2L],
collapse = ""
),
meta = latex_packages
Expand Down
113 changes: 113 additions & 0 deletions R/utils_render_latex.R
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,45 @@ create_table_start_l <- function(data) {
">{\\raggedright\\arraybackslash}"
)

# Check if column width was set using gt::pct and
# convert to Latex friendly terminology (i.e.,
# '14.7%' becomes '0.147\\linewidth')
if (grepl('^[[:digit:].]+%$', col_widths[i])) {

table_width <- dt_options_get_value(data = data, option = 'table_width')

col_pct <- as.numeric(gsub('%$', '', col_widths[i])) / 100

if (table_width == 'auto') {

# Table width not specified, use all available space
col_scalar <- col_pct
tab_unit <- '\\linewidth'

} else if (endsWith(table_width, suffix = '%')) {

# If table width is expressed as a percentage, adjust the scaler
col_scalar <- col_pct * as.numeric(gsub('%', '', table_width)) / 100
tab_unit <- '\\linewidth'

} else {

# When table width is expressed in units, convert to points
col_scalar <- col_pct * convert_to_px(table_width) * 0.75 # 0.75 converts pixels to points
tab_unit <- 'pt'

}

col_widths[i] <-
paste0(
"\\dimexpr ",
col_scalar,
tab_unit,
"-2\\tabcolsep-1.5\\arrayrulewidth"
)

}

col_defs_i <- paste0(align, "p{", col_widths[i], "}")

} else {
Expand All @@ -205,11 +244,18 @@ create_table_start_l <- function(data) {
paste0(col_defs[seq_along(stub_layout)], "|")
}

# If a table width is specified, add an extra column
# space to fill in enough space to match the width
extra_sep <- ''
if (dt_options_get_value(data = data, option = 'table_width') != 'auto')
extra_sep <- '@{\\extracolsep{\\fill}}'

# Generate setup statements for table including default left
# alignments and vertical lines for any stub columns
paste0(
longtable_post_length,
"\\begin{longtable}{",
extra_sep,
paste(col_defs, collapse = ""),
"}\n",
collapse = ""
Expand Down Expand Up @@ -1103,3 +1149,70 @@ split_row_content <- function(x) {

split(row_content, ceiling(seq_along(row_content) / ncol(x)))
}

derive_table_width_bookends <- function(data) {

table_width <- dt_options_get_value(data = data, 'table_width')

# Bookends are not required if a table width is not specified
if (table_width == 'auto') {

bookends <- c('', '')

} else if (endsWith(table_width, "%")) {

tw <- as.numeric(gsub('%', '', table_width))

side_width <-
((100 - tw) / 200) %>%
format(scientific = FALSE, trim = TRUE)

bookends <-
c(
paste0(
"\\newlength\\holdLTleft",
"\\newlength\\holdLTright",
"\\setlength\\holdLTleft{\\LTleft}\\relax",
"\\setlength\\holdLTright{\\LTright}\\relax",
sprintf(
'\\setlength\\LTleft{%s\\linewidth}\n\\setlength\\LTright{%s\\linewidth}',
side_width,
side_width
),
collapse = "\n"
),
"\\setlength\\LTleft{\\holdLTleft}\n\\setlength\\LTright{\\holdLTright}"
)

} else {

width_in_pt <- 0.75 * convert_to_px(table_width)

halfwidth_in_pt <- format(width_in_pt / 2, scientific = FALSE, trim = TRUE)

bookends <-
c(
paste0(
"\\newlength\\holdLTleft",
"\\newlength\\holdLTright",
"\\setlength\\holdLTleft{\\LTleft}\\relax",
"\\setlength\\holdLTright{\\LTright}\\relax",
sprintf(
"\\setlength\\LTleft{\\dimexpr(0.5\\linewidth - %spt)}\n\\setlength\\LTright{\\dimexpr(0.5\\linewidth - %spt)}",
halfwidth_in_pt,
halfwidth_in_pt
),
collapse = '\n'
),
paste0(
"\\setlength\\LTleft{\\holdLTleft}",
"\\setlength\\LTright{\\holdLTright}",
collapse = "\n"
)
)

}

bookends

}
50 changes: 50 additions & 0 deletions tests/testthat/test-as_latex.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
test_that("Table width correctly output in LaTeX", {

gt_latex_width_1 <-
gt(exibble) %>%
tab_options(table.width = pct(90)) %>%
as_latex()

start_pt <- regexpr("begin\\{longtable", gt_latex_width_1)

expect_gt(start_pt, 0) # Verifies the long table command appears in the text

end_pt <- regexpr("end\\{longtable", gt_latex_width_1)

expect_gt(end_pt, 0)

# Verify that the holdLTleft and holdLTright variables are defined and set
latex_prefix <- substr(gt_latex_width_1, 1L, start_pt)

expect_match(latex_prefix, "\\\\newlength\\\\holdLTleft")

expect_match(latex_prefix, "\\\\newlength\\\\holdLTright")

expect_match(latex_prefix, "\\\\setlength\\\\holdLTleft\\{\\\\LTleft\\}\\\\relax")

expect_match(latex_prefix, "\\\\setlength\\\\holdLTright\\{\\\\LTright\\}\\\\relax")

# Verify that LTleft and LTright are correctly specified
expect_match(latex_prefix, "\\\\setlength\\\\LTleft\\{0.05\\\\linewidth\\}")

expect_match(latex_prefix, "\\\\setlength\\\\LTright\\{0.05\\\\linewidth\\}")

# Verify that after the longtable environment, the LTleft and LT right are
# changed back to their previous values
latex_suffix <- substr(gt_latex_width_1, end_pt, nchar(gt_latex_width_1))

expect_match(latex_suffix, "\\\\setlength\\\\LTleft\\{\\\\holdLTleft\\}")

expect_match(latex_suffix, "\\\\setlength\\\\LTright\\{\\\\holdLTright\\}")

# Test specification of a table width in pixels
gt_latex_width_2 <-
gt(exibble) %>%
tab_options(table.width = '600px') %>%
as_latex()

expect_match(gt_latex_width_2, "\\\\setlength\\\\LTleft\\{\\\\dimexpr\\(0.5\\\\linewidth - 225pt\\)\\}")

expect_match(gt_latex_width_2, "\\\\setlength\\\\LTright\\{\\\\dimexpr\\(0.5\\\\linewidth - 225pt\\)\\}")

})
110 changes: 110 additions & 0 deletions tests/testthat/test-cols_width.R
Original file line number Diff line number Diff line change
Expand Up @@ -897,3 +897,113 @@ test_that("The function `cols_width()` works correctly with a complex table", {
) %>%
expect_true()
})

test_that("The function `cols_width()` correctly specifies LaTeX table when column widths are specified by user as percentages", {

# Check that specific suggested packages are available
check_suggests()

# Create a `tbl_latex` object with `gt()` and size
# all columns in percentages
tbl_latex <-
gt(tbl) %>%
cols_width(
col_1 ~ pct(50),
col_2 ~ pct(30),
col_3 ~ pct(20),
col_4 ~ pct(10)
)

pct_string <- function(x, unit = '\\\\linewidth') {

prefix <- '>\\{\\\\(raggedright|raggedleft|centering)\\\\arraybackslash\\}'

sprintf(
'%sp\\{\\\\dimexpr %s%s-2\\\\tabcolsep-1.5\\\\arrayrulewidth\\}',
prefix,
format(x, scientific = FALSE, trim = TRUE),
unit
)

}

build_longtable_regex <- function(...) {

paste0(
c(
"\\\\begin\\{longtable\\}\\{",
"(@\\{\\\\extracolsep\\{\\\\fill\\}\\})*",
c(...),
"\\}\\n"
),
collapse = ''
)

}

latex_col_regex <-
paste0(
c(
"\\\\begin\\{longtable\\}\\{",
"(@\\{\\\\extracolsep\\{\\\\fill\\}\\})*",
# '>\\{\\\\ragged[[:alpha:]]+\\\\arraybackslash'
sprintf(">\\{\\\\(raggedright|raggedleft|centering)\\\\arraybackslash\\}p\\{\\\\dimexpr 0\\.%d\\\\linewidth-2\\\\tabcolsep-1.5\\\\arrayrulewidth}",
c(5L, 3L, 2L, 1L)),
"\\}\\n"
),
collapse = ''
)

# Expect that all column widths are expressed as percentage of \linewidth
c(0.5, 0.3, 0.2, 0.1) %>%
pct_string() %>%
build_longtable_regex() %>%
grepl(as_latex(tbl_latex)) %>%
expect_true()


# Check that LaTeX is correctly generated when only some
# column widths are specified as percentages
tbl_latex_partial <-
gt(tbl) %>%
cols_width(
col_1 ~ pct(30),
col_3 ~ pct(20)
)

c(
pct_string(0.3),
'r',
pct_string(0.2),
'r'
) %>%
build_longtable_regex() %>%
grepl(as_latex(tbl_latex_partial)) %>%
expect_true()

# Check that LaTeX longtable command is correctly generated
# when table_width is specified by the user as a percentage
tbl_latex_tw_pct <-
tbl_latex %>%
tab_options(table.width = pct(70))

(0.7 * c(0.5, 0.3, 0.2, 0.1)) %>%
pct_string() %>%
build_longtable_regex() %>%
grepl(as_latex(tbl_latex_tw_pct)) %>%
expect_true()

# Check that LaTeX longtable command is correctly generated
# when table width is specified by user in pixels
tbl_latex_tw_px <-
tbl_latex %>%
tab_options(table.width = '400px')

(400 * 0.75 * c(0.5, 0.3, 0.2, 0.1)) %>%
pct_string(unit = 'pt') %>%
build_longtable_regex() %>%
grepl(as_latex(tbl_latex_tw_px))


})

0 comments on commit 418a1d7

Please sign in to comment.