Skip to content

Support Pandoc floating tables and native two-column table layouts - #15121

Open
EnochxynL wants to merge 6 commits into
quarto-dev:mainfrom
EnochxynL:fix/pandoc-float-tables
Open

EnochxynL wants to merge 6 commits into
quarto-dev:mainfrom
EnochxynL:fix/pandoc-float-tables

Conversation

@EnochxynL

@EnochxynL EnochxynL commented Oct 20, 2026 •

Copy link
Copy Markdown

Description

Related to #14846.

Markdown tables in classoption: twocolumn documents currently produce
longtable, which fails during PDF compilation. Upgrade the bundled Pandoc to
3.10.1 and automatically use its .float class for ordinary native tables,
including cross-referenced tables. Single-column documents retain the existing
default and can opt into .float individually. For floating cross-referenced
tables, tbl-pos supplies the placement unless latex-placement is explicitly set.

Extend native table layouts to full-width and margin tables, bottom captions,
and unlabelled panel tables. Pandoc serializes the complete table body, preserving
widths, merged cells, citations, and table notes; Quarto supplies the enclosing
environment and captions where needed.

When loading sidenotes, preserve both the document class's native table* and
figure* definitions and the package's margin-wide versions. Select between them
at use time so double-column floats retain their full text width, including
documents that switch columns in the body.

Automatic selection uses explicit classoption: twocolumn; it does not infer
unknown journal-class defaults or parse arbitrary column-switching commands.
Such documents can explicitly use .float or a full-width layout class.
Existing raw longtable conversion paths remain outside this change.

Validation

  • Deno type checking and all nine tests in
    tests/smoke/crossref/tables-float.test.ts and
    tests/smoke/crossref/latex.test.ts pass.
  • Actual LuaLaTeX compilation checks numbering, subtable references, margin
    captions, column switching, citations, and notes. Runtime dimension assertions
    check that double-column floats retain the full text width.
  • LaTeX assertions cover scalar/list class options, explicit .float, placement
    precedence, widths, merged cells, and avoidance of nested table floats.
  • deno fmt --check for the changed TypeScript and git diff --check pass.
  • The complete repository test suite has not been run locally.

Additional local validation used four arXiv documents with the project's Markdown
table serializer disabled. An adapter mapped its layout settings to native Quarto
attributes; the independent HTML-table converter remained enabled. All four
compiled, retained their page counts (23 / 7 / 50 / 25), and preserved all 36
table-label numbers. Extracted PDF text matched the baseline after removing
whitespace, with no unresolved references or increase in overflow warnings.
Ara supplies five native Markdown tables; the other papers primarily exercise raw
LaTeX passthrough. These local benchmark artifacts are not included in this PR;
the synthetic regression fixtures are included.

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR

The issue is associated with this PR; automatic closure is left for maintainers
to confirm after reviewing its scope. All automated checks on the latest PR revision pass. The complete repository test suite has not been run locally. No website documentation PR has been created.

AI-assisted PR
  • AI tool used: OpenAI Codex (ChatGPT).
  • Codebase grounding: Local quarto-cli clone, existing source and tests,
    GitHub issue Support Pandoc's .float table class to avoid longtable in twocolumn layouts #14846, related Quarto discussions, and Pandoc 3.10.1 release notes
    and writer source.
  • Human review: I have reviewed the implementation and test changes, documented my understanding below, and reviewed the successful CI results. The local automated checks and four-paper benchmark were performed with Codex assistance.

Human Review

Original Issue

jgm/pandoc#1023

By default, Pandoc outputs tables as longtable (used in LaTeX for typesetting long tables that span pages). This works for single-column documents, but for two-column documents LaTeX's longtable does not support two columns, so it reports:

longtable not in 1-column mode

In 2026, Pandoc 3.10.1 added another output: an ordinary table + tabular (native LaTeX uses the table environment for floating tables and tabular for table content; spanning both columns requires table*); the position is specified via latex-placement. This approach supports two-column documents ("supports two columns" does not mean "must float". It is just that Pandoc's .float interface chooses to provide the combination table + tabular). To enable it, add .float to the table; issue 1023 was closed accordingly.

#14846

The Quarto issue also points out this problem and notes:

  • At the time, Quarto bundled Pandoc 3.10, and an upgrade is needed to use .float.
  • Quarto adds its own caption, cross-reference, numbering, and layout handling for numbered tables, some of which assume the underlying implementation is longtable. Therefore, one cannot simply add .float to all tables; those paths must also be adapted.

https://github.com/Selbosh/aaai2026-quarto

This repository template handles the same class of problem, but the patch is simplified:

  • It always generates table[ht] and does not fully handle table* spanning both columns.
  • It only reads column alignment and does not preserve column widths or merged cells.
  • It uses stringify() to flatten cells and captions into text, which loses bold, links, and other structures.
  • It also incidentally supports reading raw HTML tables and converting them.

Changed Functionality

  1. latex_table_floats(): automatically determines which tables should automatically be given the .float marker. It does not generate LaTeX table code.
    • Confirm that the output target is LaTeX/PDF.
    • Read classoption and determine whether the document explicitly enables twocolumn. If not, do not automatically modify tables.
    • Traverse the document and skip cross-column tables and margin tables, multi-table panels, sub-tables, and tables inside other floats. These objects are handled by dedicated layout code. If another table floating environment is generated inside them, illegal nesting may occur.
    • Add .float to ordinary standalone tables. Handle two internal representations at the same time: native Table, and referenced tables wrapped by Quarto as FloatRefTarget.
  2. customnodes/floatreftarget.lua: if .float is present, pass the figPos variable that Quarto parses from tbl-pos into latex-placement for positioning (an existing explicit latex-placement takes precedence and will not be overwritten).
  3. layout/latex.lua: rewrite latexTabular() (the function that obtains table content).
    • The old implementation process was: complete Pandoc Table → to_simple_table → Lua manual concatenation → tabular. This approach makes it difficult to preserve column widths, merged cells, and complex cell content.
    • The new implementation is changed to: copy of complete Pandoc Table → add .float → Pandoc generates table + tabular → remove outer table → leave table content for Quarto layout. Specifically, this includes:
      • Clone the table to avoid modifying the original object.
      • Temporarily clear the caption and ID, leaving them to the outer Quarto handling.
      • Add .float and call Pandoc's LaTeX writer.
      • Remove the outer table generated by Pandoc.
      • Preserve tabular output such as column widths, row spans, and column spans.
      • Set vertical alignment as required by the layout.
      • Request loading of the multirow package needed for merged rows.
    • latexWriteTable(): when generating LaTeX in advance, use placeholders to preserve the opportunity for subsequent bibliography citation processing.
    • latexTableNoteEnvironment(): add savenotes when necessary to avoid losing footnote text in tables.
  4. customnodes/floatreftarget.lua: handling of complex tables with captions and references. For special environments such as cross-column and margin environments, or bottom-captioned tables with .float, the outer environment and caption are controlled by Quarto; ordinary single-column bottom-captioned tables without the .float marker keep the original longtable path. New handling includes:
    • Call latexTabular() to obtain the table content;
    • Use the outer environment selected by Quarto;
    • Change the environment for margin tables from marginfigure to margintable;
    • Prefer an explicit latex-placement;
    • Avoid an inner .float table generating another table;
    • When needed, add a footnote-collection environment for the final float.
  5. handle_table_columns(): handling of layout tables that do not go through the reference-object path. If margin or cross-column attributes are checked first, another margintable environment may be wrapped, so prioritize recognizing the render-as-tabular layout marker.
    • Cross-column native table: wrap the result of latexTabular() with table*.
    • Margin native table: wrap the result with margintable.
    • Table inside a margin Div: pass the margin attribute to the table handling function.
    • Table inside a cross-column Div: first convert it to tabular to avoid residual longtable inside the outer layout.
    • When footnotes are present, likewise add a footnote-collection environment.
    • Note not yet cleaned up: previously, longtable was retained, comment markers were added, and then TypeScript modified the column widths; now, the layout environment and tabular are generated directly. Therefore, the original beginColumnComment() and endColumnComment() functions are no longer needed. The accompanying TypeScript processor (tableColumnMarginLineProcessor() in src\format\pdf\format-pdf.ts) is no longer triggered in the current built-in path and is temporarily retained, leaving it to the maintainers to decide whether to clean it up.
  6. layout/meta.lua: avoid sidenotes breaking two-column floats. When Quarto uses margin layout, it loads sidenotes, but this package redefines table* and figure*. Its definitions are suitable for layouts that expand into the margin in single-column documents; in two-column documents, however, they may cause floats that should span two columns to have incorrect widths. Dynamically select when used:
    • Currently two-column → use the native cross-two-column environment
    • Currently single-column → use the sidenotes margin-expansion environment
  7. preserve_markdown_code_languages(): a new problem in Pandoc 3.10.1. It no longer treats any ordinary class as a language name, but still recognizes explicit language-* markers.
    • Before Markdown output, explicitly pass code block languages according to the original convention, without maintaining a language whitelist.
    • Preserve existing explicit language markers and Markdown output that supports full attributes.
    • Continue letting Pandoc handle fences, escaping, and nesting.
    • Preserve arbitrary language markers so that downstream platforms that support them have a chance to handle them correctly; Quarto does not guarantee that the target platform recognizes them.
  8. tests/docs/smoke-all/markdown/definition-list/definition-list-div.qmd: modify the test assertion. For a Typst syntax, the old test, according to the code form generated by Pandoc 3.10, required / Inner term: #block[ to appear. After the upgrade, the interpreted content still exists, but the wrapper is omitted, becoming the compact text / Inner term: Inner definition., so the assertion fails. We changed the assertion to / Inner term:\s*(?:#block\[\s*)?Inner definition\. so that the test is compatible with the behavior of Pandoc 3.10.1.

@posit-snyk-bot

posit-snyk-bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@EnochxynL
EnochxynL marked this pull request as ready for review October 27, 2026 02:13
@EnochxynL
EnochxynL marked this pull request as draft October 27, 2026 02:15
@EnochxynL
EnochxynL marked this pull request as ready for review October 27, 2026 05:08
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.

2 participants