Skip to content

Commit

Permalink
Merge pull request #7613 from quarto-dev/bugfix/7604
Browse files Browse the repository at this point in the history
make longtable environments work with new crossref system
  • Loading branch information
cscheid authored Nov 20, 2023
2 parents 5673c6f + 69fcf48 commit c14c91d
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 49 deletions.
54 changes: 53 additions & 1 deletion src/resources/filters/customnodes/floatreftarget.lua
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,12 @@ end, function(float)
arg = pandoc.Str(float.identifier)
})
latex_caption:insert(1, label_cmd)
local latex_caption_content = latex_caption

latex_caption = quarto.LatexInlineCommand({
name = caption_cmd_name,
opt_arg = fig_scap,
arg = pandoc.Span(quarto.utils.as_inlines(latex_caption or {}) or {}) -- unnecessary to do the "or {}" bit but the Lua analyzer doesn't know that
arg = pandoc.Span(quarto.utils.as_inlines(latex_caption_content or {}) or {}) -- unnecessary to do the "or {}" bit but the Lua analyzer doesn't know that
})

if float.parent_id then
Expand All @@ -287,6 +289,56 @@ end, function(float)
end
end

-- we need Pandoc to render its table ahead of time in order to
-- do the longtable fixups below
float.content = _quarto.ast.walk(quarto.utils.as_blocks(float.content), {
Table = function(tbl)
return pandoc.RawBlock("latex", pandoc.write(pandoc.Pandoc({tbl}), "latex"))
end
})

if float_type == "tbl" then
local raw
-- have to redo this as_blocks() call here because assigning to float.content
-- goes through our AST metaclasses which coalesce a singleton list to a single AST element
_quarto.ast.walk(quarto.utils.as_blocks(float.content), {
RawBlock = function(el)
if _quarto.format.isRawLatex(el) and el.text:match(_quarto.patterns.latexLongtablePattern) then
raw = el
end
end
})
-- special case for singleton longtable floats
if raw then
local longtable_content = raw.text:gsub(_quarto.patterns.latexLongtablePattern, "%2", 1)
-- split the content into params and actual content
-- params are everything in the first line of longtable_content
-- actual content is everything else
local params, content = longtable_content:match("^(.-)\n(.*)$")
local cap_loc = cap_location(float)
if float.parent_id then
-- need to fixup subtables because longtables don't support subcaptions,
-- and longtable captions increment the wrong counter
-- we try our best here

fatal("longtables are not supported in subtables.\n" ..
"This is not a Quarto bug - the LaTeX longtable environment doesn't support subcaptions.\n")
return {}
else
local result = pandoc.Blocks({latex_caption, pandoc.RawInline("latex", "\\tabularnewline")})
-- if cap_loc is top, insert content on bottom
if cap_loc == "top" then
result:insert(pandoc.RawBlock("latex", content))
else
result:insert(1, pandoc.RawBlock("latex", content))
end
result:insert(1, pandoc.RawBlock("latex", "\\begin{longtable}" .. params))
result:insert(pandoc.RawBlock("latex", "\\end{longtable}"))
return result
end
end
end

local figure_content
local pt = pandoc.utils.type(float.content)
if pt == "Block" then
Expand Down
7 changes: 2 additions & 5 deletions src/resources/filters/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,7 @@ local quarto_pre_filters = {
{ name = "pre-table-captions",
filter = table_captions(),
flags = { "has_table_captions" } },

{ name = "pre-longtable-no-caption-fixup",
filter = longtable_no_caption_fixup(),
flags = { "has_longtable_no_caption_fixup" } },


{ name = "pre-code-annotations",
filter = code_annotations(),
flags = { "has_code_annotations" } },
Expand Down Expand Up @@ -346,6 +342,7 @@ local quarto_post_filters = {
{ name = "layout-meta-inject-latex-packages", filter = layout_meta_inject_latex_packages() },

-- format fixups post rendering
{ name = "post-render-latex-fixups", filter = render_latex_fixups() },
{ name = "post-render-html-fixups", filter = render_html_fixups() },
{ name = "post-render-ipynb-fixups", filter = render_ipynb_fixups() },
{ name = "post-render-typst-fixups", filter = render_typst_fixups() },
Expand Down
22 changes: 21 additions & 1 deletion src/resources/filters/quarto-post/latex.lua
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,24 @@ function render_latex()
return pandoc.Div(calloutContents)
end
}
end
end


function render_latex_fixups()
if not _quarto.format.isLatexOutput() then
return {}
end

return {
RawBlock = function(raw)
if _quarto.format.isRawLatex(raw) then
if (raw.text:match(_quarto.patterns.latexLongtablePattern) and
not raw.text:match(_quarto.patterns.latexCaptionPattern)) then
raw.text = raw.text:gsub(
_quarto.patterns.latexLongtablePattern, "\\begin{longtable*}%2\\end{longtable*}", 1)
return raw
end
end
end
}
end
3 changes: 3 additions & 0 deletions src/resources/filters/quarto-pre/parsefiguredivs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ function parse_reftargets()

-- respect single table in latex longtable fixups above
if skip_outer_reftarget then
-- we also need to strip the div identifier here
-- or we end up with duplicate identifiers which latex doesn't like
div.identifier = ""
div.content = content
return div
end
Expand Down
15 changes: 0 additions & 15 deletions src/resources/filters/quarto-pre/table-captions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@ local patterns = require("modules/patterns")
kTblCap = "tbl-cap"
kTblSubCap = "tbl-subcap"

function longtable_no_caption_fixup()
return {
RawBlock = function(raw)
if _quarto.format.isRawLatex(raw) then
if (raw.text:match(_quarto.patterns.latexLongtablePattern) and
not raw.text:match(_quarto.patterns.latexCaptionPattern)) then
raw.text = raw.text:gsub(
_quarto.patterns.latexLongtablePattern, "\\begin{longtable*}%2\\end{longtable*}", 1)
return raw
end
end
end
}
end

function table_captions()
return {
Div = function(el)
Expand Down
29 changes: 29 additions & 0 deletions tests/docs/smoke-all/2023/11/16/7604.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: "7604"
format: pdf
---

```{r}
library(kableExtra)
```

```{r}
#| label: tbl-sad-broken
#| tbl-cap: "This table is cross-referenceable but no longer breaks across pages because it's inside `\\centering{}`"
rbind(mtcars, mtcars) |>
kbl(longtable = TRUE, booktabs = TRUE) |>
kable_styling()
```

::: {#tbl-also-sad}
```{r}
rbind(mtcars, mtcars) |>
kbl(longtable = TRUE, booktabs = TRUE) |>
kable_styling()
```

This table is also cross-referenceable but also doesn't break across pages because of the same `\centering{}` problem
:::


see @tbl-sad-broken and @tbl-also-sad.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ _quarto:
ensureFileRegexMatches:
-
- "\\label{tbl-test}"
- "\\label{tbl-test-two-tables}"
- "\\label{tbl-test-two-tables-1}"
- "\\label{tbl-test-two-tables-2}"
- []
---

Expand Down Expand Up @@ -37,23 +34,3 @@ kable(df,
```

See @tbl-test.

```{r}
#| echo: false
#| label: tbl-test-two-tables
#| tbl-cap: Overall caption
kable(df,
format = "latex",
longtable = TRUE,
booktabs = TRUE,
caption = "Table one")
kable(df,
format = "latex",
longtable = TRUE,
booktabs = TRUE,
caption = "Table two because screw you, that's why")
```

See @tbl-test-two-tables, @tbl-test-two-tables-1, and @tbl-test-two-tables-2.
9 changes: 5 additions & 4 deletions tests/smoke/crossref/tables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ testRender(knitrTablesQmd.input, "html", false, [

/* caption is inserted in the right place in table environment*/
renderVerifyLatexOutput(docs("crossrefs/knitr-tables-latex.qmd"), [
/\\begin{table}.*\\caption{\\label{tbl-1}.*}.*\\begin{longtable}\[.*\]{.*}.*\\end{longtable}/s,
/\\begin{table}.*\\caption{\\label{tbl-2}.*}.*\\centering.*\\begin{tabular}{.*}/s,
/\\begin{table}.*\\caption{\\label{tbl-3}.*}.*\\centering.*\\begin{longtable\*}{.*}/s,
/\\begin{table}.*\\caption{\\label{tbl-4}.*}.*\\centering\n\\begin{tabular}\[c\]{.*}/s,
/\\begin{longtable}\[.*\]{.*}.*\n+\\caption{\\label{tbl-1}.*}\n+.*\\tabularnewline/,
/\\begin{table}\n+\\caption{\\label{tbl-2}.*}.*\n+\\centering{?\n+\\begin{tabular}{.*}/,
/\\begin{longtable}{.*}.*\n+\\caption{\\label{tbl-3}.*}\n+.*\\tabularnewline/,
// two centering calls here is ugly, but we don't control the input we get
/\\begin{table}\n+\\caption{\\label{tbl-4}.*}.*\n+\\centering{?\n+\\centering\n+\\begin{tabular}\[c\]{.*}/,
]);

0 comments on commit c14c91d

Please sign in to comment.