Skip to content

fix(layout): respect section and table metrics (fixes #319)#396

Open
jedrazb wants to merge 3 commits intomainfrom
fix/issue-319-section-table-metrics
Open

fix(layout): respect section and table metrics (fixes #319)#396
jedrazb wants to merge 3 commits intomainfrom
fix/issue-319-section-table-metrics

Conversation

@jedrazb
Copy link
Copy Markdown
Contributor

@jedrazb jedrazb commented May 5, 2026

Summary

Builds on @juanmendez-git's fix from #320 to address regressions and edge cases surfaced by an audit against more complex documents. Fixes #319.

The community PR's commit (afaaf66) is preserved as the first commit; a follow-up commit hardens the changes against documents the original PR didn't exercise.

What the original PR does

  • Each section's own page size, margins, columns, and section-start behavior is honored across measurement and pagination.
  • <w:lastRenderedPageBreak/> markers and leading hard page breaks are preserved through parser → ProseMirror → flow layout.
  • Auto-fit table grids with incomplete or zero-width columns are normalized; cell widths and implicit full-row spans are preserved.
  • Paragraph spacing inside table cells is applied during measurement and rendering.

Edge cases hardened in the follow-up commit

  • Phantom empty pagesforcePageBreak is idempotent when the current page is empty. A section break followed by a pageBreakBefore=true paragraph (e.g. an "Attachment" heading after a section change) used to produce a blank page between the body and the attachment.
  • Vertically merged tablesinferImplicitSingleCellRowSpans no longer expands cells that are vMerge continuations or that already declared their gridSpan. The previous version corrupted any DOCX with vertically merged columns or sparse single-cell rows.
  • Partial section margin overrides — a section overriding only marginRight or marginBottom now honors the override. Previously the gate required marginTop or marginLeft, so right/bottom-only overrides were silently dropped.
  • Round-trip safety for <w:lastRenderedPageBreak/> — the marker now survives fromProseDoc and is re-emitted by the paragraph serializer, so save+reload doesn't silently lose Word's recorded break.

Test plan

  • bun run typecheck
  • bun test packages/core — 404 / 404 passing
  • New unit tests:
    • force-page-break-empty.test.ts — paginator no-ops on empty pages
    • table-vmerge-implicit-rowspan.test.ts — vMerge protection
    • rendered-page-break-roundtrip.test.ts<w:lastRenderedPageBreak/> survives save+reload
  • New e2e test: issue-319-section-pagination.spec.ts asserts the issue's fixture renders without phantom empty pages, with both portrait and landscape sections.

🤖 Generated with Claude Code

juanmendez-git and others added 2 commits May 5, 2026 17:19
Use each DOCX section's page geometry during layout and preserve table/cell metrics so paged rendering better matches Word.

Made-with: Cursor
Builds on the community fix in afaaf66 to address regressions and edge
cases surfaced by an audit against more complex documents:

- forcePageBreak is idempotent when the current page is empty; eliminates
  phantom empty pages between a section break and a following
  pageBreakBefore=true paragraph (the failing case in the issue fixture).

- inferImplicitSingleCellRowSpans skips cells that are vMerge
  continuations or already declared their gridSpan; preserves vertically
  merged columns and sparse single-cell rows.

- Section margin override honors any single side that's set; previously
  a section overriding only `marginRight` or `marginBottom` was silently
  dropped because the gate required `marginTop` or `marginLeft`.

- renderedPageBreakBefore round-trips through fromProseDoc and the
  paragraph serializer (`<w:lastRenderedPageBreak/>` is re-emitted),
  so save+reload no longer loses the break Word recorded.

Tests:
- packages/core/src/layout-engine/__tests__/force-page-break-empty.test.ts
- packages/core/src/docx/__tests__/table-vmerge-implicit-rowspan.test.ts
- packages/core/src/prosemirror/conversion/__tests__/rendered-page-break-roundtrip.test.ts
- e2e/tests/issue-319-section-pagination.spec.ts

Co-Authored-By: Juan Diego Mendez <juan.mendez@biorce.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docx-editor Ready Ready Preview, Comment May 5, 2026 4:45pm

Request Review

Continues the audit-driven cleanup on top of the earlier edge-case fixes.

Parser:
- paragraphStartsWithRenderedPageBreak now scans past leading empty
  rPr-only runs and recurses through hyperlink / sdt / smartTag /
  fldSimple / customXml / ins / del / moveFrom / moveTo wrappers, so
  Word's `<w:lastRenderedPageBreak/>` placement inside any of those
  containers is honored. fldChar / instrText / pgNum and the rest of
  the visible run-content set are recognized.
- Header/footer parser strips renderedPageBreakBefore — HF reflows on
  every page and a leading rendered break in a header would force a
  body-level page break for every header repetition.
- paragraphHasPageBreak (ProseMirror conversion) now considers
  fieldChar / instrText / footnoteRef / endnoteRef and recurses through
  hyperlink, insertion, deletion and field wrappers.

Layout engine:
- continuous section breaks now call updatePageLayout so the next
  natural overflow page adopts the new section's geometry
  (ECMA-376 §17.6.22).
- Unified SectionMeasureMetrics and SectionLayoutConfig into a single
  exported type used by both the engine and the React paged editor.
- Cell content layout reverted to block flow; the brief flex-column
  switch broke margin collapse, baseline alignment and floating-image
  interaction in nested tables.

Layout bridge:
- New `tableWidthUtils` helpers (resolveTableWidthPx, resolveCellWidthPx,
  countTableColumns, normalizeTableColumnWidths) extracted into a shared
  module. PagedEditor now imports them so the React side stops
  carrying hand-rolled twip math.
- pct width math is always /5000 per ECMA-376 §17.18.111. The previous
  "treat values <= 100 as plain percentage" heuristic mis-rendered
  cells whose authoring tool wrote small valid pct values.

Tests added (12 new):
- packages/core/src/docx/__tests__/rendered-page-break-edge-cases.test.ts
- packages/core/src/docx/__tests__/header-footer-rendered-page-break.test.ts
- packages/core/src/layout-bridge/__tests__/tableWidthUtils.test.ts
- packages/core/src/layout-engine/__tests__/continuous-section-geometry.test.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

DOCX layout does not respect section geometry, rendered page breaks, and table metrics

2 participants