Skip to content

fix(docx): hold a row's auto column's editor point in the gap after it - #948

Merged
DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-row-editor-slack
Oct 23, 2026
Merged

DemchaAV merged 2 commits into
2.5-devfrom
fix/docx-row-editor-slack

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Why

A row column sized to its content (DocumentRowColumn.auto()) is given a point of slack (EDITOR_COLUMN_SLACK_POINTS). An editor setting its text in its own face would otherwise wrap it: a table of contents' label broke mid-word in LibreOffice without it. withRowEditorSlack always took that point out of the row's weight columns. The auto column was written a point wider, and whatever stood right of it moved a point right. Nothing named it.

A rule in a placed row's column also counted its left margin twice. The column already starts past the child's margin (leftMarginInCell), and writeRule added it again as the rule's indent.

Measured on Windows, in Word:

Stroke Before
ReceiptModern's 8 leaders start +0.95 to +1.04pt
EditorialProposal's 3 signature lines start +0.88 to +1.02pt
VioletGrid's credential heading rules start +12.3pt (their 11.3pt margin again, and the point)
CharcoalGold's heading rules start +9.25pt (their 9pt margin again)

What changed

  • withRowEditorSlack counts the room an auto column already has (roomInItsColumn):
    • The column runs to the next child's start, past that child's left margin, so its text box holds that margin. CharcoalGold's heading words have the 9pt their rule stands in by, and take no point at all.
    • A paragraph's own right side counts as far as it does not write it as an indent. In a cell, a left-to-right paragraph not set right writes its right side 2pt short (EDITOR_SLACK_POINTS). A side a margin below zero pulls out stands at the cell's text, so it counts as nothing. The arithmetic is now one helper, writtenSides, shared with writeParagraph.
  • A paragraph set left takes what that leaves from the gap after its column, the column's right margin. The column keeps the page's width, and what stands right of it stays where the page sets it.
    • The row's padding after its last column is not a gap: a painted row's panel takes it out of its columns (withoutTheRowsPadding).
    • A paragraph set otherwise, or a child that is not a paragraph, takes none from a gap. Set right, a paragraph in an auto column claims its row's whole width on the page.
  • What neither holds still comes out of the weight columns, in proportion to their text boxes as before, and the row is named (right end). The note is skipped where the weight columns giving it hold only spacers, or the remainder is under a tenth of a point. Twenty corpus rows are named that nothing named before:
    • ReceiptModern's amounts at the row's end, 0.84 to 1.1pt left in Word and 0.86 to 1.02pt in LibreOffice;
    • VioletGrid's section heading rules, a point right;
    • SerifHeadline's section headings, Orange Ops' contact strip and Subscription's masthead.
  • writeRule counts a rule's left margin once in a placed row's column.
  • Docs:
    • the recipe's "Row columns" row and its rules paragraph;
    • the CHANGELOG (Public API);
    • the node and value ledgers' row columns entries (ROW_AUTO_ROOM).

Verification

  • DocxEditorSlackTest (+10 new, 3 updated):
    • New:
      • a neighbour's 9pt margin needs no point;
      • with no gap, the point comes from the weight column, named;
      • a 0.4pt gap gives what it has;
      • a section takes none from the gap;
      • a centred paragraph takes none from the gap;
      • a paragraph's own right side gives the room it does not indent: 3pt needs no point, 0.4pt leaves 0.6pt to the weight column;
      • a right side a margin below zero pulls out gives no room, beside a neighbour standing in by as much;
      • the row's padding is no gap;
      • a spacer's column is not named;
      • a 0.05pt remainder is not named.
    • Updated:
      • the table of contents' entry: the label's point comes out of the 6pt gap, and the page number's out of the leader, named;
      • the fixed-column test now runs with no gap;
      • the proportion test now puts its auto column at the row's end.
  • DocxRuleTest (+1): a rule with a 9pt margin in a row's column starts and ends at Word's reach in from its cell's text.
  • Fail-closed: 12 sabotages, each run alone, all caught. A thirteenth cannot change the output: taking a paragraph's whole right side as room, where a side wider than 2pt already gives 2pt, more than the point. The caught ones:
    • room not counted;
    • gap not used;
    • padding taken as a gap;
    • any child, or any paragraph, taking from the gap;
    • a paragraph's spared right side not counted;
    • a side pulled out taken as room;
    • a spacer's column named;
    • under a tenth named;
    • nothing named;
    • the rule's margin counted again;
    • weight columns giving by width.
  • Word and LibreOffice on Windows, the changed documents against the export before:
    • In Word, the leaders start within 0.06pt of the page's and end within 0.06pt. The signature lines are within 0.18pt, and VioletGrid's credential rules within 0.07pt.
    • CharcoalGold's heading rules stand with their words, which stand 0.7pt left in both editors.
    • LibreOffice sets each end of a rule 1.4 and 1.5pt in, as before.
    • No word changes or moves down in either editor. EditorialProposal's "Date:" stands 0.17pt right of the page's in Word, where it stood 0.19pt left.
  • Corpus: 4 of 62 DOCX change (cv-charcoal_gold, cv-violet_grid, proposal-editorial, receipt-modern). Notes go from 1365 to 1385.
  • Gate:
    • ./mvnw -B -ntp clean verify -pl <core, render-pdf, render-docx, render-pptx, templates, testing, qa, coverage> -am → BUILD SUCCESS: core 821, render-pdf 339, render-docx 2083, testing 5, render-pptx 142, templates 127, qa 1828.
    • The JDK 17 knowledge checks pass, and core javadoc has 0 warnings.
    • ./mvnw -f examples/pom.xml test → BUILD SUCCESS, 93 tests. The committed word-export-companion.docx does not change.

Notes for review:

  • A row composed in a table cell has no placed children, so its auto columns count no room. They take the point from the gap or the weight columns, as a placed row's would with no neighbour margin.
  • An auto column holding no text, such as a spacer or a picture, is still given the point, as before. Where its weight column gives it, the note speaks of "their text".

Lane: shared-engine (render-docx): no public API change.

- A row column sized to its content is given a point for an editor's
  face. It came out of the row's weight columns and moved what stood
  right of it a point right: ReceiptModern's leaders and
  EditorialProposal's signature lines started 0.9 to 1pt right of the
  page's in Word.
- The column already holds the next child's left margin, and a
  paragraph's own right side gives what the paragraph does not write as
  an indent (writtenSides, now shared with writeParagraph). A paragraph
  set left takes what that leaves from the gap after its column, which
  then stands where the page sets it.
- What neither holds still comes out of the weight columns, and the row
  is named (right end) unless they hold only spacers or it is under a
  tenth of a point: ReceiptModern's amounts at the row's end and
  VioletGrid's section heading rules, among twenty rows nothing named
  before.
- A rule in a row's column counts its left margin once. Counted again,
  CharcoalGold's heading rules started 9.25pt right of the page's in
  Word and VioletGrid's credential ones 12.3pt.
- Measured on Windows, in Word: leaders within 0.06pt, signature lines
  within 0.18pt, credential rules within 0.07pt; no word moves in Word
  or LibreOffice.
- Tests: the gap holding the point, the last column taking it from the
  weight column, a neighbour's margin and a paragraph's own right side
  as room, a thin gap, a section and a centred paragraph, the row's
  padding, a spacer, a tenth of a point, and a rule's margin in a row's
  column.
@DemchaAV
DemchaAV marked this pull request as draft October 23, 2026 10:06
- A row cell holds a paragraph's side at its text, so a right margin
  below zero gives an auto column no room: counted, a label pulled 2pt
  into a neighbour's 2pt margin got no point.
- writtenSides names its paragraph and says a cell holds a side below
  zero at its text.
- The rule test checks the right end of a rule in a row's column too.
- The CHANGELOG, the recipe and the javadoc give ReceiptModern's amounts
  as measured on all eight rows, 0.84 to 1.1pt left in Word and 0.86 to
  1.02pt in LibreOffice, and the leaders and signature lines 0.88 to
  1.04pt right before. The CHANGELOG says no line breaks differently or
  moves down, and no word moves across by more than 0.4pt.
@DemchaAV
DemchaAV marked this pull request as ready for review October 23, 2026 10:17
@DemchaAV
DemchaAV merged commit 5c2e62b into 2.5-dev Oct 23, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-row-editor-slack branch October 23, 2026 10:49
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