Repository navigation
fix(docx): write a page zone built otherwise for the first page into the title page's header or footer - #910
Merged
Merged
Conversation
…the title page's header or footer Word paginates, so a page zone's content was built once, for a page that was both the first and the last, and written on every page: a footer reading page.isFirst() ? "Original keep" : "Continuation sheet" read "Original keep" on every page, and the report said nothing. PageContext.unpaginated(boolean, boolean), new and @beta, gives a semantic export a context for a kind of page that still refuses the numbers. The export builds a zone for the first page and for the pages after it, and compares the two by value, down to every run, colour and picture. Built otherwise, the zone becomes the section's title-page part and the ordinary one, each measured on a page the layout draws it on and framed where the two lines cannot share one distance from the edge; the faces of both are shipped, and what both lines lose is said once. Word has no part for the last page: content built otherwise for it is written there as on the pages before it, and named. A document of one page builds its first page as the last. Zone tests that made content read otherwise on its first page through isLast() now do it through isPaginated(), which the page and the file still answer differently.
DemchaAV
marked this pull request as draft
October 16, 2026 12:15
…d line's own losses A zone's builds for the first page and the rest were compared record by record and by equals for anything else, so a barcode's or a table's options, which have no equals, read as otherwise and split a zone that is the same on every page. GraphCompose's own values are now read field by field, and maps by their values. A note the second line gives alike one its first line gave is given once, each once: two losses of the second line alike stay two, where a note the report held from anywhere was dropped. A zone building none for the first page, or for the pages between it and the last, where the page draws it there, is named. The last-page note says the page is written as built for the pages between, which on two pages is no page's before it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Word paginates, so the DOCX export built a page zone's content once — with
PageContext.unpaginated(), a page that is both the first and the last — and wrote it on every page. A footerpage.isFirst() ? "Original keep" : "Continuation sheet"read "Original keep" on all three pages in Word, where the PDF draws "Continuation sheet" from page 2, and the report said nothing. Content built otherwise for the last page was written on every page too, its note blaming the measurement rather than the page.What changed
PageContext.unpaginated(boolean first, boolean last), new,@Beta,@since 2.5.0: a semantic export's context for a kind of page.number()/total()still refuse;unpaginated()is(true, true). The Javadoc ofisFirst/isLastand ofDocumentPageZone(whoseappliesTotext still said a predicate "lands on every page") follows; the member is named indocs/api-stability.md.DocxSemanticBackend.applyPageZonesbuilds a zone for the first page —(true, layout runs to one page)— and for the pages after it —(false, false)— for the kinds it is drawn on only (ZoneBuilds). Built otherwise, it becomes two writers (zoneVariantsOf): the first page's part, withw:titlePg, and the rest's DEFAULT and EVEN parts. Built alike, one writer, as before.DocxNodeValues.alike: records component by component, GraphCompose's own other values — a colour, picture data, an SVG drawing, a barcode's or a table's options — field by field, lists, maps and arrays element by element; anything else byequals, so a value of another kind built afresh reads as otherwise and the zone is written twice alike rather than once with the other build's content.DocxZoneParts.readAlikeis not used for this — it compares what sets the line and skips a boxed picture's data.DocxLayoutMetrics.zoneText/zonePictures(zone, page),zoneFirstPage(zone, pages),zoneDistanceFromEdge(zone, pages, …). An unmeasured line drawn on none of its pages — the rest's, in a layout of one page — reads its distance from any page instead of falling back to the padding. Where the two lines cannot share one distance from the edge (other twips, or one not measured), each measured one is framed, as two zones of a kind are.reportTheLastPage: content built otherwise for the last page — or refused there for readingnumber()— gets an APPROXIMATED note saying the last page is written as built for the pages between; a layout of one page builds its first page as the last and needs none.reportAKindBuiltNone: a build of none for the first page, or for the pages between, where the layout draws the zone there, is DROPPED and named.zoneContentsIncollects both builds. Notes: a split zone's line notes say "on the first page" / "on the pages after the first"; a note the second line gives alike one the first line gave is given once, each once (DocxExportReport.Builder.skipRepeatsOf), so two losses of the second line alike stay two.page.number()only after the first page, never built before, is now refused there as on the first; content reading it only on the last page, refused before, now exports with the note unless the first page is the last.Verification
ExportAsFixedFormat) on a 3-page fixture: p1 "Original keep", p2–p3 "Continuation sheet", baseline 190.08 against the page's 190.05 — before, "Original keep" on all three. LibreOffice (Windows) the same;isFirst / isLastcontent reads Cover / Middle / Middle with the last-page note.DocxZoneByPageTest(18: first-page footer and header, later-only predicate, last-page note, one-page document, frames, colour, picture data, even pages, two sections, refusals, distance on the later pages, one note for both lines, two losses of the second line, a barcode zone written once, a kind built none),DocxNodeValuesTest(7),PageContextTest(3),DocxFontTableTest+1. Nine zone tests that made content read otherwise on its first page throughisLast()— the defect itself — now do it throughisPaginated(); their assertions are unchanged.authoring:unpaginated(boolean first, boolean last) [beta]);extract-api --check,check-stability-doc,check-claims --check,check-routesgreen; core javadoc BUILD SUCCESS, 0 warnings../mvnw -B -ntp clean verify→ BUILD SUCCESS (core 821, render-docx 1679, qa 1820).Lane: shared semantic backend (
render-docx) + canonical (corePageContext,@Beta).