Skip to content

A table is a table in every format - #750

Merged
WaylandYang merged 3 commits into
devfrom
feat/a-table-is-a-table-in-every-format
Sep 17, 2026
Merged

WaylandYang merged 3 commits into
devfrom
feat/a-table-is-a-table-in-every-format

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

Pitfall 26 of docs/design/prior-work.md, the open item left by #744: a table is a structure in every format, not only in HTML.

What lands

DOCX, spreadsheet and CSV tables go through the grid that #744 built for HTML. Until now a DOCX table came out as one paragraph per cell, a sheet as tab-joined lines and a CSV as pipe-joined lines with no header separator, so the block parser saw none of them as tables and the model read cells without their headings. utopia_ingest::table::render_grid takes rows of (text, span, indent) from any parser and runs the same classification and rendering: caption lines, stacked headers, sections folded into labels, symbols rejoined to figures.

  • DOCX (parsers::docx_xml_to_text): w:tbl rows and cells are collected with w:gridSpan as the span and the first paragraph's w:ind as the indent; a vertically merged continuation cell stays empty; a table nested in a cell is that cell's text. Paragraphs before and after are untouched.
  • Spreadsheets: each sheet is one grid under its # Sheet: heading; empty rows are skipped; a sheet that renders no table falls back to the tab-joined lines.
  • CSV / TSV: the records are one grid.
  • Two structural rules for grids that come with a header convention (first_is_header): the first row with two or more cells is the header even when its headings are years, and every later row with two cells or a lone figure is a record. A table with no figure anywhere (words only) is read as a header row followed by records, in every format.

The text layer of a PDF carries no table structure and is left as it is; a scanned PDF's tables already arrive from MinerU as pipe tables.

A runner to look before extracting: cargo run -p utopia-ingest --example render_doc -- <file> prints a file as the ingest layer reads it.

Checked

Unit tests: a DOCX table with a spanning header and "$" cells renders | Segment | Revenue | over | Cloud | $1,200 |; a nested table is its cell's text; a CSV whose headings are years keeps them as headings; a CSV of words is still a table. On three real files (a course-exemption roster .xlsx, a visa application form .docx, a simulation-export .csv): the sheet's title row becomes the caption and its header row the header, with the sequence-number rows as records; the CSV's comment lines become the caption and its 639 records a table; the form's tables render as tables, with the label-value interleaving of a form left as it is.

Not in this change

Extraction measurements on DOCX or spreadsheet corpora (none is in the benches yet; the NVDA cross-domain recut and the period-column check remain the open items of pitfall 26); PDF text-layer tables.

🤖 Generated with Claude Code

WaylandYang and others added 2 commits September 18, 2026 02:03
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
@WaylandYang
WaylandYang force-pushed the feat/a-table-is-a-table-in-every-format branch from a033c9f to 5366533 Compare September 17, 2026 18:03
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
@WaylandYang
WaylandYang merged commit 23a227c into dev Sep 17, 2026
4 checks passed
@WaylandYang
WaylandYang deleted the feat/a-table-is-a-table-in-every-format branch September 17, 2026 18:16
@WaylandYang WaylandYang mentioned this pull request Sep 19, 2026
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.

1 participant