Skip to content

fix(metadata): detect sectPr nested in tables; ignore text-box sections (#51)#271

Merged
JSv4 merged 2 commits into
mainfrom
claude/issue-51-evaluation-fix-adgx05
Jul 12, 2026
Merged

fix(metadata): detect sectPr nested in tables; ignore text-box sections (#51)#271
JSv4 merged 2 commits into
mainfrom
claude/issue-51-evaluation-fix-adgx05

Conversation

@JSv4

@JSv4 JSv4 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #51.

WmlToHtmlConverter.GetDocumentMetadata() reported the wrong number of sections for documents whose section break lives inside a table cell. CollectSectionData scanned only the body's direct children:

var blockElements = body.Elements()
    .Where(e => e.Name == W.p || e.Name == W.tbl || e.Name == W.sectPr)
    .ToList();

so a w:sectPr carried by a paragraph nested in w:tbl/w:tr/w:tc/w:p/w:pPr was invisible. Such a document reported one section instead of two, and the per-section page dimensions and paragraph/table index ranges used for lazy-loading pagination were wrong. The method even carried a comment flagging this as a known limitation tracked by this issue.

This is the Document Metadata API in WmlToHtmlConverter.cs — it is not part of redlining, WmlComparer, or the DocxDiff engine, so no comparison code is touched.

The fix

CollectSectionData now walks the body as a single main story in document order:

  • It descends into tables (w:tbl → w:tr → w:tc) because a cell's content is part of the main story, so an in-cell section break is detected and attributed to the section it starts in.
  • Each w:p is treated as a leaf — only its direct w:pPr/w:sectPr is inspected, never its runs. Because a text box (w:txbxContent) is always nested inside a run, a w:sectPr inside a text box is excluded automatically, along with text-box paragraphs. A text box is a separate story that does not paginate the main document; counting its section properties would invent a phantom section and corrupt the metadata. Headers/footers/footnotes/endnotes/comments live in their own parts and are out of the body tree by construction.
  • Only body-level tables count toward the table total (a nested table is part of its containing cell's content), preserving the pre-fix counts for the common case.
w:sectPr placement Counts as a main-document section?
w:body/w:p/w:pPr/w:sectPr (body paragraph) ✅ (unchanged)
w:body/w:sectPr (trailing/final) ✅ (unchanged)
w:tbl/w:tr/w:tc/w:p/w:pPr/w:sectPr (table cell) new
…/w:txbxContent/…/w:sectPr (text box) ❌ separate story

The change is self-contained: CollectSectionData has exactly one caller (GetDocumentMetadata), and the public DocumentMetadata/SectionMetadata shape is unchanged, so the WASM/npm surfaces pick this up with no wire change.

Testing

  • New regression tests in DocumentMetadataTests.cs:
    • DM022_GetDocumentMetadata_DetectsSectionBreakInsideTableCell — an in-cell break now yields 2 sections, the table is attributed to the section it starts in, and paragraph/section indices stay contiguous.
    • DM023_GetDocumentMetadata_IgnoresSectionPropertiesInsideTextBox — a text-box w:sectPr does not create a section (single section, governed by the body w:sectPr), and text-box paragraphs are not counted.
  • Full suite: 2671 passed, 3 skipped, 0 failed (dotnet test).
  • Release build (-c Release, TreatWarningsAsErrors=true) is clean — no new warnings from the changed file.

Docs

  • CHANGELOG.md — entry under [Unreleased] → Fixed.
  • docs/ooxml_corner_cases.md — new section documenting why a table-cell w:sectPr is a section break while a text-box w:sectPr is not (separate-story rule).

🤖 Generated with Claude Code


Generated by Claude Code

claude and others added 2 commits July 12, 2026 21:08
…ns (#51)

GetDocumentMetadata's CollectSectionData scanned only the body's direct
children, so a w:sectPr carried by a paragraph inside a table cell was
never seen. Such a document reported one section instead of two, and the
per-section page dimensions and paragraph/table index ranges used for
lazy-loading pagination were wrong.

CollectSectionData now walks the body as a single main story in document
order, descending into tables (w:tbl -> w:tr -> w:tc) so an in-cell
section break is counted and attributed to the section it starts in. Each
w:p is treated as a leaf (only its direct w:pPr/w:sectPr is inspected,
never its runs), so a w:sectPr inside a text box -- a separate story that
does not paginate the main document -- is excluded automatically, along
with text-box paragraphs; counting it would invent a phantom section.
Only body-level tables count toward the table total, preserving the
previous counts for the common case.

Tests: DM022 (in-cell break detected, table attributed correctly) and
DM023 (text-box section properties ignored). The separate-story rule is
documented in docs/ooxml_corner_cases.md.
@JSv4 JSv4 merged commit 751d956 into main Jul 12, 2026
12 checks passed
@JSv4 JSv4 deleted the claude/issue-51-evaluation-fix-adgx05 branch July 12, 2026 21:48
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.

Document Metadata: Support nested sectPr in tables and text boxes

2 participants