Skip to content

fix(docx): name what a text header or footer shows otherwise in Word - #921

Merged
DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-band-notes
Oct 19, 2026
Merged

DemchaAV merged 1 commit into
2.5-devfrom
fix/docx-band-notes

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

A text header or footer (DocumentHeaderFooter) is written as one exact line of a Word header or
footer: its left slot, its centre slot at a centre tab and its right slot at a right tab, page tokens
as fields. Measured in Word 16 and LibreOffice on Windows, four things showed otherwise than on the
page, and the report said nothing.

Case Page Word LibreOffice
9pt text, header 11pt tall baseline 6.5pt from the top 8.64 8.60
9pt text, header 13pt tall 8.5 8.64 8.60
9pt text, footer 10pt tall baseline 1.0pt from the foot 2.04 2.21
that footer's 0.5pt separator 10.0pt from the foot 10.92 11.06
header 14pt, footer 12pt tall — where the page sets them where the page sets them
lettered numbers: page 27 / 28 / 53, total of 56 aa / ab / ba, bd aa / bb / aaa, ddd aa / ab / ba, bd
left slot reaching past the centre slot's start drawn over one another centre and right set after it, right at x=360, past the 328 margin the same
centre slot reaching the right slot's start drawn over one another right set after it, past the margin right on a line below
one slot wider than the line runs past the margins wrapped onto a line below wrapped onto a line below

The line is exact and starts at the page's edge. A shorter line, or text raised in it, would cut its
letters: Word draws an exact line's text only inside the line on screen.

What changed

  • DocxTextBands:
    • baselineFromEdge: where the page sets the baseline;
    • baselineMovedIn: how much further in Word stands it, where the line stops at the edge;
    • separatorMovedIn: the separator ruled past a line taller than its band;
    • slotsOverlap: the page's three slot positions against one another.
    • distanceFromEdge(band) now reads baselineFromEdge; same arithmetic, the corpus is byte-identical.
  • DocxSemanticBackend.reportBandText, given once per band from its writer: APPROXIMATED
    notes, subject page header / page footer:
    • a shallow band, with about how far its text moves, and its separator where it moves too, at
      0.1pt or more;
    • a band numbering in letters (LOWER_ALPHA / UPPER_ALPHA) with a {page} or {pages} token,
      where the layout runs to 28 pages or more, or with no layout behind it, "where the document
      runs that far";
    • overlapping slots, and a slot wider than the line, "on every page it is drawn on" or "on N of
      the M pages".
  • slotsSetOn reads each page the band is drawn on as the page does:
    • its numbering's offset and first page;
    • its tokens resolved for that page;
    • its width in the band's face, or in Helvetica, which the page draws a face its fonts lack in;
    • with no layout, the first page it is drawn on;
    • a band with no page token once.
  • Docs:
    • the DOCX recipe's text header section;
    • the capability matrix's header row;
    • DocxNodeFieldLedgerTest's headersAndFooters entry;
    • CHANGELOG v2.5.0.

Verification

  • DocxBandTextReportTest (new, 9 tests):
    • header 11pt (2.1pt) and 13pt (0.1pt), footer 10pt (1.2pt), each worded;
    • the footer's separator (1.0pt), and a header whose line fits no separator clause;
    • header 14pt and footer 12pt named nothing;
    • letters: 28 pages named for {page} and {pages}; 27 pages, roman numbers and a band with no
      token not named; no layout hedged;
    • overlap of left and centre, and of left and right with no centre; a short control not named;
    • on 3 of 12 pages, and on 3 of 11 when kept off the first page (widths built from Helvetica so a
      digit more on page 10 makes the overlap);
    • a slot wider than the line;
    • no layout with the band off page 1.
  • DocxTextBandsTest (+4): the shifts, the separator, the overlap geometry.
  • 18 sabotages, each undoing one part, all caught: each note, the threshold, the header and footer
    shares, page 28, the token and letter conditions, the no-layout branches, the first page counted,
    each overlap edge, unread tokens, the separator's width, a band read once counted once, a wide
    slot unmeasured.
  • Corpus: 62/62 DOCX byte-identical; report notes 1246 → 1246.
  • ./mvnw -B -ntp clean verify: BUILD SUCCESS (core 821, render-docx 1793, examples 93, qa 1820); the knowledge pack's --check passes.

Lane: render-docx (semantic backend). Report notes only; no output change.

A text band is written as one exact line of a Word header or footer,
its slots set by a centre and a right tab stop. Measured in Word and
LibreOffice on Windows, four things showed otherwise than on the page,
with nothing said:

- A band too shallow for its line, which is written from the page's
  edge: 9pt text in a header 11pt tall stood 2.14pt lower in Word than
  on the page (2.10 in LibreOffice), in a footer 10pt tall 1.04pt
  higher (1.21), that footer's separator 0.92pt higher (1.06).
- Page numbers in letters: Word letters a number past 26 by repeating
  one letter, page 28 bb and a total of 56 ddd, where the page and
  LibreOffice read ab and bd.
- Slots that overlap where the page sets them, which it draws over one
  another: both editors set a slot that would overlap the text before
  it after that text, Word on the same line as far as past the right
  margin, LibreOffice on it or on a line below.
- A slot wider than the line, which the page runs past the margins and
  both editors wrap onto a line below.

The report now names each (page header / page footer): the shallow
band with about how far its text and separator move, a lettered band
where the layout runs to 28 pages or more, or where it would with no
layout, and the slots with on how many of the band's pages, each page
read as the page reads it. The DOCX is written as before.
@DemchaAV
DemchaAV merged commit 7694b32 into 2.5-dev Oct 19, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the fix/docx-band-notes branch October 19, 2026 10:24
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