Skip to content

feat(docx): write a page reference as a PAGEREF field Word keeps current - #729

Merged
DemchaAV merged 1 commit into
2.5-devfrom
feature/docx-page-references
Sep 23, 2026
Merged

DemchaAV merged 1 commit into
2.5-devfrom
feature/docx-page-references

Conversation

@DemchaAV

@DemchaAV DemchaAV commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Why

addTableOfContents(...) and addPageReference(...) print the page an anchor lands on. The layout resolves that page and lays it out as text (PageReferenceDefinition). The DOCX backend had no case for PageReferenceNode and dropped it: a table of contents reached Word with its entries and no page numbers, and a "see page N" wrote nothing.

A number written as plain text would be right only until the reader edits the document. So a page reference has to be a field the editor recomputes. For that field it also has to be decided when fields update, and what the file shows before they do.

What changed

  • DocxSemanticBackend.writePageReference writes the node as a paragraph with a PAGEREF <bookmark> \h field.
    • It is a live page reference and a hyperlink to the target, like the entry's label beside it.
    • The field's stored result is the page the layout resolved (DocxLayoutMetrics.laidOutText), so the file opens reading the PDF's numbers.
    • Paragraph properties come from the same paragraph the layout builds for the node, so alignment, spacing and the measured line height match.
  • A reference is a field only when its anchor gets a bookmark in this export (bookmarkedAnchorsIn: paragraph anchors plus the block anchors from fix(docx): make a block's anchor a bookmark Word can link to #728). Otherwise it is the placeholder text the page prints. Word turns a PAGEREF to a missing bookmark into "Error! Bookmark not defined." the first time the field updates.
  • Field policy, stated in the recipe (new "Fields, and when they update" section):
    • PAGE, NUMPAGES and SECTIONPAGES in zones are updated by the editor on every layout. LibreOffice is the exception: it does not update SECTIONPAGES.
    • PAGEREF is updated by LibreOffice on layout, and by Word on a field update (F9, or printing with field updates on).
    • Every field stores the layout's number.
    • w:updateFields is deliberately not set. It would make Word ask on every open to recompute numbers that already read correctly.
  • Docs: new capability-matrix row for page references; CHANGELOG under v2.5.0.

Verification

In an editor, including after the numbers go stale. A 5-page document with a three-entry table of contents: one paragraph anchor and two section anchors.

  • LibreOffice shows 2 / 3 / 5, matching the engine's PDF and the target pages.
  • In a copy with each stored PAGEREF result rewritten to 99, LibreOffice still shows 2 / 3 / 5. It recomputes the field rather than trusting the stored number.
  • Word was not available in this environment. Its behaviour (updates on F9 / print) is stated as Word's documented behaviour, not as measured here.

Gate. Full reactor gate (clean verify over core, render-pdf/docx/pptx, templates, testing, qa, coverage) → BUILD SUCCESS. Examples after install: 93 green. No committed preview moved.

New DocxPageReferenceTest (4):

  • a table of contents writes one PAGEREF … \h per entry, storing pages 2 / 3 / 4, and every name is a bookmark the document has, section anchors included;
  • a standalone addPageReference is a field too;
  • a reference to a missing anchor is plain placeholder text with no field;
  • settings.xml has no updateFields.

Forcing the field for a missing anchor fails the missing-anchor test.

Known limit, separate. A table-of-contents entry is a row with auto-width label columns, and in LibreOffice the labels wrap mid-word. The editor's substitute font is wider than the engine's measurement, and rows do not get the 1pt editor slack that table auto columns have. This predates this change and is tracked as its own task.

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

} else {
CTSimpleField field = para.getCTP().addNewFldSimple();
field.setInstr(" PAGEREF " + bookmark + " \\h ");
run = new XWPFRun(field.addNewR(), para);
Base automatically changed from fix/docx-block-anchors to 2.5-dev September 23, 2026 15:03
The DOCX export dropped every PageReferenceNode, so a table of contents
reached Word with its entries and no page numbers. Each is now a
PAGEREF field to the anchor's bookmark, as a hyperlink, storing the
page the layout resolved; the editor recomputes it as the document
changes. A reference to an anchor with no bookmark is written as its
placeholder text instead of a field Word would turn into an error.
w:updateFields is not set. The recipe states which field each page
number is and what updates it.
@DemchaAV
DemchaAV force-pushed the feature/docx-page-references branch from 458e349 to dd5f371 Compare September 23, 2026 15:04
@DemchaAV
DemchaAV merged commit 31ea6b7 into 2.5-dev Sep 23, 2026
13 checks passed
@DemchaAV
DemchaAV deleted the feature/docx-page-references branch September 23, 2026 15:14
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.

2 participants