Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating table #1588

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open

Conversation

AaronGullickson
Copy link
Contributor

@AaronGullickson AaronGullickson commented Feb 25, 2024

Summary

This PR addresses issue #157, specifically the ability to create latex floating environment tables with the use of the table + tabular environments rather than the longtable environment. Currently, I believe this is a major limitation of gt because tables are often broken up across pages, and most people expect tables to operate in a similar manner to figures.

I added an option to tab_option called latex.use.longtable which defaults to FALSE. I then use a few ifelse clauses in utils_render_latex.R to get the proper output. Specification of the positioning of the float works well from the quarto code chunk option tbl-pos.

If I run the following code:

head(state.x77) |> 
  as.data.frame() |>
  gt() |>
  tab_options(latex.use.longtable = FALSE) |>
  as_latex()

Here is the latex output with the option set to FALSE:

"\\begin{table}\n\\begin{tabular}{rrrrrrrr}\n\\toprule\nPopulation & Income & Illiteracy & Life Exp & Murder & HS Grad & Frost & Area \\\\ \n\\midrule\\addlinespace[2.5pt]\n3615 & 3624 & 2.1 & 69.05 & 15.1 & 41.3 & 20 & 50708 \\\\ \n365 & 6315 & 1.5 & 69.31 & 11.3 & 66.7 & 152 & 566432 \\\\ \n2212 & 4530 & 1.8 & 70.55 & 7.8 & 58.1 & 15 & 113417 \\\\ \n2110 & 3378 & 1.9 & 70.66 & 10.1 & 39.9 & 65 & 51945 \\\\ \n21198 & 5114 & 1.1 & 71.71 & 10.3 & 62.6 & 20 & 156361 \\\\ \n2541 & 4884 & 0.7 & 72.06 & 6.8 & 63.9 & 166 & 103766 \\\\ \n\\bottomrule\n\\end{tabular}\n\\end{table}"

Here is the latex output with the option set to TRUE:

"\\begin{longtable}{rrrrrrrr}\n\\toprule\nPopulation & Income & Illiteracy & Life Exp & Murder & HS Grad & Frost & Area \\\\ \n\\midrule\\addlinespace[2.5pt]\n3615 & 3624 & 2.1 & 69.05 & 15.1 & 41.3 & 20 & 50708 \\\\ \n365 & 6315 & 1.5 & 69.31 & 11.3 & 66.7 & 152 & 566432 \\\\ \n2212 & 4530 & 1.8 & 70.55 & 7.8 & 58.1 & 15 & 113417 \\\\ \n2110 & 3378 & 1.9 & 70.66 & 10.1 & 39.9 & 65 & 51945 \\\\ \n21198 & 5114 & 1.1 & 71.71 & 10.3 & 62.6 & 20 & 156361 \\\\ \n2541 & 4884 & 0.7 & 72.06 & 6.8 & 63.9 & 166 & 103766 \\\\ \n\\bottomrule\n\\end{longtable}\n"

I have also supplied an example qmd document in tests/gt-examples/03-latex/latex-15-floating.qmd.

I am submitting this PR currently as a draft as I have not fully stress tested it nor have I written a proper testthat test (not very experienced with those). I am happy to proceed with doing so, if the maintainers think I am on the right path here, but wanted to get feedback before continuing.

Related GitHub Issues and PRs

Checklist

Fixes: #157

@AaronGullickson
Copy link
Contributor Author

Correction, I did not commit the qmd file because these are apparently ignored by the project.

With the tabular* environment you need to specify a width for the table. This will default to \linewidth so tables will fill the whole page horizontally by default. However, it will also respect the use of other percentages in the table.width option.
@AaronGullickson
Copy link
Contributor Author

Commit 51da31e add tests on the latex format for the new case.

@AaronGullickson
Copy link
Contributor Author

AaronGullickson commented Feb 26, 2024

I have decided to use the tabular* environment rather than the tabular environment. This environment makes it possible to easily adjust the width of the table to conform to the expectation of the table.width argument in tab_options. By default floating environment tables will be full width (e.g. 100%) but users can adjust this expectation using either pixels or percentages. This change should not affect any other formatting issues because longtable and tabular* both use the same formatting for the interior of the table.

I have also cleaned up all of the issues with testthat failing because it expected a longtable header and the default is now a tabular.

I have a qmd file that I have used for testing. It won't upload, so I provide it below.

---
title: "latex-15-floating"
format: 
  pdf:
    keep-tex: true
---

```{r}
#| label: setup
#| include: false

library(gt)
```

Lorem ipsum dolor sit amet, ac purus ut. Sit lectus ac, diam, turpis elementum, tempus. Diam eros eget et placerat lectus odio pulvinar ac. Et tristique phasellus feugiat et. Pretium sociosqu. Nisi, sed et dis vel lacus congue aenean turpis, nec. Convallis, et cursus non ligula at at quis diam pulvinar. Nec tempor tempus quis at tincidunt blandit vitae, eros. Neque aliquet vel non auctor posuere, mi aptent. In in dictum ad, nisl eros mollis sed.

Nullam curabitur, sapien. Tellus habitant, neque. Quis vivamus tortor nulla aliquet aliquet. Aliquet leo, tristique viverra pellentesque lectus lacus quis aliquam egestas. Nunc sed et malesuada magnis, luctus. Et, ultricies vitae ad sed. Habitant ante aliquet hendrerit nisl in ac arcu massa, et. Convallis, faucibus ipsum nisl amet aliquam elementum. Imperdiet est habitasse, diam sapien dapibus himenaeos. A erat tellus mollis, tristique in sem erat cras. Cubilia id in sed non risus neque fringilla a mollis magnis nec. Purus egestas, bibendum. At potenti sem curabitur leo elit class in, sem massa et congue.                                                                                 

Sed dapibus feugiat iaculis erat nibh lobortis sagittis eleifend convallis in. Tristique ac ipsum curabitur amet et leo interdum! Donec mus pharetra. Sed, metus non mauris phasellus semper ut pulvinar fermentum sodales. Bibendum mollis dignissim sed, in dui. Pellentesque sed in elit montes sed tincidunt euismod, eros sit ultrices! Etiam at metus ut aenean libero amet. Feugiat facilisis posuere justo quis faucibus vitae nullam nulla. Scelerisque sapien hendrerit lobortis, pulvinar non nostra amet vehicula ac sed. Ex eget eleifend luctus fusce. Tincidunt in pulvinar eget rhoncus eu eget vitae lacus ornare nunc lacinia amet lacus! Eros interdum suscipit aliquet donec tincidunt phasellus est hendrerit.

Taciti ac, nec non ornare amet litora nascetur nunc elementum. Sollicitudin ut sit conubia et habitasse, integer. Egestas egestas cubilia id facilisi. Tempor ut nec facilisi congue, varius tortor. Aliquam viverra a ipsum aptent eu eu. Id purus metus volutpat malesuada et. In nec consequat eros sed nec at mauris ligula. Ut sapien, eget, in in, potenti torquent, semper mollis ligula vel. Sem quisque varius eros lobortis ut ac mus, dui. Senectus mattis eget, himenaeos pellentesque vitae varius adipiscing, taciti justo. Mauris morbi nunc, laoreet a, non, iaculis libero varius dis tincidunt tortor a.                                                                                                        
Nibh, amet sit sociis, eros in. Ac odio eros luctus at, mauris, tincidunt. Tristique phasellus etiam vel quis, netus tincidunt, tristique eros. Scelerisque massa integer ac aliquet porta non taciti. In ante, consectetur nullam urna, nunc volutpat pellentesque libero tortor nisi! Ante amet habitasse in est dis pellentesque. Nec turpis quis, natoque sagittis ac vestibulum morbi velit mattis. Purus convallis ac luctus eu purus et egestas. 

Consectetur at at magna viverra mauris. Leo augue in nullam laoreet. Eu adipiscing suspendisse eros dis torquent integer et sed tortor ac habitasse et nec. Ullamcorper aliquam maecenas platea et ut integer bibendum justo. Gravida cum est et porttitor ut purus in. Urna magnis ad at senectus in laoreet ut tempor, varius velit. Ac varius lorem, facilisi, blandit, cum ante mauris eu dui enim pharetra. Felis sed odio faucibus netus eget sed vestibulum. Nostra non libero suscipit sed sed egestas. Proin, nulla. Felis suscipit nec pellentesque id gravida ipsum massa. Sagittis consequat quis consequat. Dictumst lectus molestie, est, ut vitae consectetur. Leo mauris eget congue. 

Sed dapibus feugiat iaculis erat nibh lobortis sagittis eleifend convallis in. Tristique ac ipsum curabitur amet et leo interdum! Donec mus pharetra. Sed, metus non mauris phasellus semper ut pulvinar fermentum sodales. Bibendum mollis dignissim sed, in dui. Pellentesque sed in elit montes sed tincidunt euismod, eros sit ultrices! Etiam at metus ut aenean libero amet. Feugiat facilisis posuere justo quis faucibus vitae nullam nulla. Scelerisque sapien hendrerit lobortis, pulvinar non nostra amet vehicula ac sed. Ex eget eleifend luctus fusce. Tincidunt in pulvinar eget rhoncus eu eget vitae lacus ornare nunc lacinia amet lacus! Eros interdum suscipit aliquet donec tincidunt phasellus est hendrerit.

```{r}
#| label: tbl-state
#| tbl-cap: Some State Information
#| tbl-pos: "!t"
#| echo: false

temp <- head(state.x77) |> 
  as.data.frame()

temp <- cbind(State=row.names(head(state.x77)), temp)

temp |>
  subset(select=c("State","Population","Income","Frost","Murder")) |>
  gt(rowname_col = "State") |>
  tab_spanner(label = "Bad Things", columns = c("Frost", "Murder")) |>
  fmt_number(decimals = 0, columns = c("Population", "Income")) |>
  cols_width(State ~ pct(30))  |>
  tab_source_note(md("*Source:* This is a test.")) |>
  tab_options(latex.use.longtable = FALSE)
```

See @tbl-state.

aciti ac, nec non ornare amet litora nascetur nunc elementum. Sollicitudin ut sit conubia et habitasse, integer. Egestas egestas cubilia id facilisi. Tempor ut nec facilisi congue, varius tortor. Aliquam viverra a ipsum aptent eu eu. Id purus metus volutpat malesuada et. In nec consequat eros sed nec at mauris ligula. Ut sapien, eget, in in, potenti torquent, semper mollis ligula vel. Sem quisque varius eros lobortis ut ac mus, dui. Senectus mattis eget, himenaeos pellentesque vitae varius adipiscing, taciti justo. Mauris morbi nunc, laoreet a, non, iaculis libero varius dis tincidunt tortor a.                                                                                                        
Nibh, amet sit sociis, eros in. Ac odio eros luctus at, mauris, tincidunt. Tristique phasellus etiam vel quis, netus tincidunt, tristique eros. Scelerisque massa integer ac aliquet porta non taciti. In ante, consectetur nullam urna, nunc volutpat pellentesque libero tortor nisi! Ante amet habitasse in est dis pellentesque. Nec turpis quis, natoque sagittis ac vestibulum morbi velit mattis. Purus convallis ac luctus eu purus et egestas. 

Consectetur at at magna viverra mauris. Leo augue in nullam laoreet. Eu adipiscing suspendisse eros dis torquent integer et sed tortor ac habitasse et nec. Ullamcorper aliquam maecenas platea et ut integer bibendum justo. Gravida cum est et porttitor ut purus in. Urna magnis ad at senectus in laoreet ut tempor, varius velit. Ac varius lorem, facilisi, blandit, cum ante mauris eu dui enim pharetra. Felis sed odio faucibus netus eget sed vestibulum. Nostra non libero suscipit sed sed egestas. Proin, nulla. Felis suscipit nec pellentesque id gravida ipsum massa. Sagittis consequat quis consequat. Dictumst lectus molestie, est, ut vitae consectetur. Leo mauris eget congue.   

Sed dapibus feugiat iaculis erat nibh lobortis sagittis eleifend convallis in. Tristique ac ipsum curabitur amet et leo interdum! Donec mus pharetra. Sed, metus non mauris phasellus semper ut pulvinar fermentum sodales. Bibendum mollis dignissim sed, in dui. Pellentesque sed in elit montes sed tincidunt euismod, eros sit ultrices! Etiam at metus ut aenean libero amet. Feugiat facilisis posuere justo quis faucibus vitae nullam nulla. Scelerisque sapien hendrerit lobortis, pulvinar non nostra amet vehicula ac sed. Ex eget eleifend luctus fusce. Tincidunt in pulvinar eget rhoncus eu eget vitae lacus ornare nunc lacinia amet lacus! Eros interdum suscipit aliquet donec tincidunt phasellus est hendrerit.

Taciti ac, nec non ornare amet litora nascetur nunc elementum. Sollicitudin ut sit conubia et habitasse, integer. Egestas egestas cubilia id facilisi. Tempor ut nec facilisi congue, varius tortor. Aliquam viverra a ipsum aptent eu eu. Id purus metus volutpat malesuada et. In nec consequat eros sed nec at mauris ligula. Ut sapien, eget, in in, potenti torquent, semper mollis ligula vel. Sem quisque varius eros lobortis ut ac mus, dui. Senectus mattis eget, himenaeos pellentesque vitae varius adipiscing, taciti justo. Mauris morbi nunc, laoreet a, non, iaculis libero varius dis tincidunt tortor a.                                                                                                        
Nibh, amet sit sociis, eros in. Ac odio eros luctus at, mauris, tincidunt. Tristique phasellus etiam vel quis, netus tincidunt, tristique eros. Scelerisque massa integer ac aliquet porta non taciti. In ante, consectetur nullam urna, nunc volutpat pellentesque libero tortor nisi! Ante amet habitasse in est dis pellentesque. Nec turpis quis, natoque sagittis ac vestibulum morbi velit mattis. Purus convallis ac luctus eu purus et egestas. 

Consectetur at at magna viverra mauris. Leo augue in nullam laoreet. Eu adipiscing suspendisse eros dis torquent integer et sed tortor ac habitasse et nec. Ullamcorper aliquam maecenas platea et ut integer bibendum justo. Gravida cum est et porttitor ut purus in. Urna magnis ad at senectus in laoreet ut tempor, varius velit. Ac varius lorem, facilisi, blandit, cum ante mauris eu dui enim pharetra. Felis sed odio faucibus netus eget sed vestibulum. Nostra non libero suscipit sed sed egestas. Proin, nulla. Felis suscipit nec pellentesque id gravida ipsum massa. Sagittis consequat quis consequat. Dictumst lectus molestie, est, ut vitae consectetur. Leo mauris eget congue.   

The file that is produced is attached:

latex-15-floating.pdf

@AaronGullickson AaronGullickson marked this pull request as ready for review February 26, 2024 20:36
@rich-iannone
Copy link
Member

@AaronGullickson Apologies for not commenting earlier but thank you for the work done here! We have another PR underway that focuses on LaTeX output (#1595). It would be good to have that merged first before reviewing this one. (After that one is merged, the merge conflict here could be also addressed.)

@AaronGullickson
Copy link
Contributor Author

@rich-iannone Sounds good. One important thing to consider when reviewing is which case (floating table or longtable) should be the default. I set it up here with floating table as the default, because I think that will be a more common case - i.e. if a table is not too long for a page, it should float by default. However, this will be a breaking change for existing documents in the sense that someone with a multipage table will now have it running off the page. I think its worth it for the long term benefit, but wanted to raise the issue here.

Merge branch 'master' into floating-table

# Conflicts:
#	tests/testthat/test-as_latex.R
Instead of trying to add \begin{table} and \end{table} within various functions, I moved this step to the export.R as_latex function to create a wrapping environment that is either \begingroup-\endgroup for longtables or \begin{table}-\end{table} for floating tables. This will allow floating tables to be able to use fontsize changes and any other future changes that are meant to be added below the enclosing environment.
@AaronGullickson
Copy link
Contributor Author

I made some changes to how the wrapping table environment is injected as described in the comments on commit 0e4503d. Basically, longtable should be surrounded with \begingroup and \endgroup but tabular should be surrounded by \begin{table} and \end{table}. This approach is much saner and makes it easier for both approaches to use things like the font size changes.

I have also updated tests and I think its ready to go. Its failing something with word, but I think that is unrelated to anything I am doing.

@AaronGullickson
Copy link
Contributor Author

I am actually going to make one more change - I want to see if I can give the user the ability to specify the float position with a table option, rather than depend on Quarto.

@AaronGullickson
Copy link
Contributor Author

Ok, never mind about the floating position. When using captions from a code chunk, pandoc injects caption information into the table immediately after the curly closing bracket of \begin{table} which breaks the pre-existing floating position and you just end up with unintentional text in the table. So its better to specify floating position with the code chunk option tbl-pos which works correctly.

I added a news item and all checks are passing. It should be ready for review.

@kbrevoort
Copy link
Contributor

@AaronGullickson May I ask a question about this PR as an interested user who would love for {gt} to include a float option?

I believe that the tbl-pos chunk option only works in Quarto. That won't be available to users who want to keep working with RMarkdown or people who want to use {gt} to generate tables that they can upload to Overleaf or use with a local install of TeX Live. Is it possible for those users to specify a floating position without editing the code by hand each time they generate a table?

@AaronGullickson
Copy link
Contributor Author

@kbrevoort Thats a good point. I did try to add that as another tab_option but found that it will not work correctly in Quarto. Let my try it again and see if it works in R Markdown. I can just add some documentation to the option to tell Quarto users not to do it this way, I suppose.

@rich-iannone
Copy link
Member

Just wanted to chime in here and offer that the check_quarto() function could be used at render time to determine whether the rendering is occurring in a Quarto environment. This could be used in a conditional to enable options that are specific to Quarto.

Adds a latex.tbl.pos option to tab_options. The value here will be applied to latex output when a floating table is used to specify the floating position. It checks whether we are in a quarto environment to avoid messing up quarto output since floating position should be specified in tbl-pos chunk option in quarto docs.
@AaronGullickson
Copy link
Contributor Author

I added a latex.tbl.pos option that can be used for floating tables. I used the check_quarto() function to ensure it does not mess up quarto output. I think the PR should be ready unless you think there is anything more it needs.

@AaronGullickson
Copy link
Contributor Author

Fixed a couple of issues, but some tests are failing in an area I didn't touch so not sure what is going on there.

@AaronGullickson
Copy link
Contributor Author

Also not sure where to do the license thing.

@AaronGullickson
Copy link
Contributor Author

Fixed it. Forgot tests checked the total number of options. Had to increment expectations.

@kuriwaki
Copy link

kuriwaki commented May 5, 2024

Thank you @AaronGullickson. This new option latex.use.longtable = TRUE removes the \begin{table} environment (which is great) and changes tabular* into a longtable tabular. Would it be straightforward to add a similar option, something like latex.float (that defaults to TRUE), which, if set to FALSE, will simply remove the \begin{table} and caption but keep the tabular*?

In #157, I was thinking of use-cases where it's up to the user to generate the correct captions and environment for a non-floating table.

@CLAassistant
Copy link

CLAassistant commented May 8, 2024

CLA assistant check
All committers have signed the CLA.

@AaronGullickson
Copy link
Contributor Author

Thank you @AaronGullickson. This new option latex.use.longtable = TRUE removes the \begin{table} environment (which is great) and changes tabular* into a longtable tabular. Would it be straightforward to add a similar option, something like latex.float (that defaults to TRUE), which, if set to FALSE, will simply remove the \begin{table} and caption but keep the tabular*?

In #157, I was thinking of use-cases where it's up to the user to generate the correct captions and environment for a non-floating table.

It should be easier to set something like this once this PR is merged.

@nielsbock
Copy link

The option to convert from a longtable to a table environment is a great feature that will make gt much more compatible with latex! I've noticed that adding a title using tab_header() will cause an error when knitting to pdf. Inserting a title:

head(state.x77) |> 
  as.data.frame() |>
  gt() |>
  tab_options(latex.use.longtable = FALSE) |> 
  tab_header(title = "this is a title") |>
  gtsave("latex-table.tex")

essentially creates an unnumbered caption that is placed within the tabular environment which causes the error:

\begin{table}[!t]
\fontsize{12.0pt}{14.4pt}\selectfont
\begin{tabular*}{\linewidth}{@{\extracolsep{\fill}}rrrrrrrr}
\caption*{
{\large this is a title}
} \\ 
\toprule
Population & Income & Illiteracy & Life Exp & Murder & HS Grad & Frost & Area \\ 
\midrule\addlinespace[2.5pt]
3615 & 3624 & 2.1 & 69.05 & 15.1 & 41.3 & 20 & 50708 \\ 
365 & 6315 & 1.5 & 69.31 & 11.3 & 66.7 & 152 & 566432 \\ 
2212 & 4530 & 1.8 & 70.55 & 7.8 & 58.1 & 15 & 113417 \\ 
2110 & 3378 & 1.9 & 70.66 & 10.1 & 39.9 & 65 & 51945 \\ 
21198 & 5114 & 1.1 & 71.71 & 10.3 & 62.6 & 20 & 156361 \\ 
2541 & 4884 & 0.7 & 72.06 & 6.8 & 63.9 & 166 & 103766 \\ 
\bottomrule
\end{tabular*}
\end{table}

placing the caption just outside the tabular environment, for instance just after\begin{table} solves the issue:

\begin{table}[!t]
\caption*{
{\large this is a title}
}
\fontsize{12.0pt}{14.4pt}\selectfont
\begin{tabular*}{\linewidth}{@{\extracolsep{\fill}}rrrrrrrr}
\toprule
Population & Income & Illiteracy & Life Exp & Murder & HS Grad & Frost & Area \\ 
\midrule\addlinespace[2.5pt]
3615 & 3624 & 2.1 & 69.05 & 15.1 & 41.3 & 20 & 50708 \\ 
365 & 6315 & 1.5 & 69.31 & 11.3 & 66.7 & 152 & 566432 \\ 
2212 & 4530 & 1.8 & 70.55 & 7.8 & 58.1 & 15 & 113417 \\ 
2110 & 3378 & 1.9 & 70.66 & 10.1 & 39.9 & 65 & 51945 \\ 
21198 & 5114 & 1.1 & 71.71 & 10.3 & 62.6 & 20 & 156361 \\ 
2541 & 4884 & 0.7 & 72.06 & 6.8 & 63.9 & 166 & 103766 \\ 
\bottomrule
\end{tabular*}
\end{table}

If this is a known issue, please mark my comment as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add options for specialized environments before and after LaTeX table code
6 participants