Skip to content

Read PPTX merged-cell attributes from a:tc, not a:tcPr - #1079

Closed
Yi-111-a wants to merge 1 commit into
deeplethe:devfrom
Yi-111-a:fix-pptx-merged-cell-attrs
Closed

Yi-111-a wants to merge 1 commit into
deeplethe:devfrom
Yi-111-a:fix-pptx-merged-cell-attrs

Conversation

@Yi-111-a

@Yi-111-a Yi-111-a commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Why

#1074. In DrawingML, gridSpan, hMerge and vMerge are attributes of a:tc
(CT_TableCell); a:tcPr (CT_TableCellProperties) has no such attributes.
pptx_xml_to_text read them only from a:tcPr, so a merged table written by any
real producer was extracted as if every cell were plain:

| Summary | COVERED | Total |      <- actual
|  | Summary | Total |          <- expected

What changes

  • pptx_xml_to_text reads the three merge attributes off a:tc in the Start
    handler, and the two a:tcPr handlers are dropped — they existed only for that
    lookup. The shared bit is a small apply_merge_attrs closure, so the Start
    path and the old duplicated blocks no longer drift.
  • The merge fixtures in pptx_tables.rs put the attributes on a:tc instead of
    a:tcPr. They previously exercised the same wrong structure the parser
    expected, which is why the existing tests could not catch this.
  • New merged_cell_attributes_on_a_tc_preserve_the_grid_shape spells the markup
    out directly rather than going through the helpers, so it keeps testing a real
    producer's shape (<a:tc gridSpan="2">) if the helpers change later.
  • a_vertical_merge_continuation_stays_an_empty_cell used a textless
    continuation cell, so clearing its text was a no-op and it passed whether or
    not the vMerge lookup worked. That cell now carries the text PowerPoint
    repeats, and the assertion pins that it is dropped.

No a:tcPr fallback is kept: those are not a:tcPr attributes in any version of
the schema, so reading them there was the bug. Say the word if you would rather
tolerate producers that write them there anyway — it is three lines.

How it was checked

cargo +1.95 test -p utopia-ingest --lib --test pptx_tables --test pptx_order --test pptx_breaks --test office_text_references → 108 + 12 + 4 + 2 + 4 passed,
0 failed. The three merge tests are the discriminating ones: reverting only
parsers.rs fails exactly those three and passes the other nine, and the failure
output is the | Summary | COVERED | Total | line quoted above. cargo fmt -p utopia-ingest -- --check is clean.

Two things I did not run, so CI is the gate on them:

  • cargo clippy --workspace --all-targets -- -D warnings reports two
    collapsible_match findings in parsers.rs, and both reproduce on unmodified
    dev (parsers.rs:313 and the a:t arm at parsers.rs:1107 there, 1110
    with this change) — neither is in code this PR touches, and it adds no new
    findings. I left them alone rather than mixing an unrelated lint cleanup into a
    bugfix; note CI pins stable, so if your stable predates these they may not
    fire for you.
  • cargo test --workspace. This change is confined to pptx_xml_to_text, so I ran
    the ingest tests that cover it, but the full workspace build did not fit the
    disk I had (it filled the volume and the linker failed on space). Worth a
    look on CI, particularly the utopia-store suites that need
    UTOPIA_DATABASE_URL.

Before review

  • Every commit is signed off (git commit -s)
  • cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings and cargo test --workspace pass
    (fmt and the ingest tests do; clippy and the full workspace run do not — both
    explained above, neither caused by this change)
  • For changes under web/: pnpm build and pnpm test pass — not applicable, no web/ change
  • SQL under crates/utopia-store/ was tested with UTOPIA_DATABASE_URL set — not applicable, no SQL change
  • A new migration takes the next free number on dev — not applicable, no migration
  • UI strings are in both web/src/i18n/en.ts and zh.ts — not applicable, no UI string
  • A change to the data model, the ontology contract or a public API has its ADR in docs/decisions/ — not applicable

Disclosure

Written with opencode (space-bunny-free). The parser change, the fixture
changes and the tests are all generated; the diagnosis, the DrawingML
CT_TableCell reference, the choice to drop the a:tcPr path and the decision to
strengthen the vMerge test are the issue reporter's and mine. It needs a human
read before merge — in particular please sanity-check that dropping the a:tcPr
lookup is what you want rather than a tolerated fallback.

…, so real merged tables stop extracting as plain cells.

Co-Authored-By: opencode space-bunny-free <noreply@opencode.ai>
Signed-off-by: Yi-111-a <153097222+Yi-111-a@users.noreply.github.com>
@WaylandYang

Copy link
Copy Markdown
Contributor

Thanks @Yi-111-a. This is the same fix as #1077, which @Floating-Y opened three hours earlier together with the issue, so I am landing that one and closing this. Your reading of CT_TableCell against CT_TableCellProperties is right, and it is the reason the fix is correct.

If you would like something that is not taken: the fixtures in pptx_tables.rs are hand-written XML, which is how this slipped through. A test that reads a deck written by a real producer would have caught it, and the same goes for the Word and spreadsheet readers.

@WaylandYang WaylandYang closed this Oct 4, 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.

2 participants