Repository navigation
test(docx): hold every page reference to a bookmark the file holds - #969
Merged
Merged
Conversation
Word reads a PAGEREF field naming a bookmark the file lacks as "Error! Bookmark not defined." once it updates its fields. The export writes a field only where it wrote the anchor a bookmark, and the number with a report note elsewhere; that was held shape by shape, by tests reading the fields and bookmarks a paragraph holds directly. DocxPageReferenceGuardTest reads every PAGEREF field, bookmark and paragraph from each XML part of the exported file and fails on a field naming a bookmark the body does not hold or a name Word does not take, or naming more than one bookmark, one in another case or one never closed. It runs over the template corpus and over four documents referring to an anchor on each kind of node that declares one, from each place a reference stands; the anchors written no bookmark must come out as their number, read off its paragraph, with a report note. The DOCX recipe and the capability matrix name what Word 16 and LibreOffice 24.2 read differently: a reference in a table's composed cell stores no number, and LibreOffice reads neither a bookmark named like one of its numbering sequences nor one of a block that opens with a table right after another table.
A reference whose anchor has no bookmark and no page reads nothing, as the page prints it; read off the paragraph after its label, a missing reference read the same. Each such reference now stands between paragraphs of its own, and the guard reads the one after it too. The reader names an instruction where no field's code is open, a simple field closed over an open one and a bookmark id opened twice, each now held by a test, and reads an anchor's kind only off a String component. Measured in Word 16 and LibreOffice 24.2, a bookmark running from a body paragraph into a table's cell, or out of one, is kept: LibreOffice drops only one opened on the paragraph written between two tables. The recipe says so, points to the repeated header row, and names the cases of `Table` measured; the capability matrix marks the DOCX page reference⚠️ , a documented deviation, as its legend has 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 reads a
PAGEREFfield that names a bookmark the file does not hold as "Error! Bookmark not defined." once it updates its fields. The export writes a field only where it wrote the anchor a bookmark, and otherwise the number the layout read, with a report note; #967 and #968 gave a layer stack's column and a block of drawings alone their bookmarks. That rule was held shape by shape: each test read the fields and bookmarks a paragraph holds directly, so a header's field, a text box's, a simple field, or a bookmark outside a paragraph went unread, as did any shape no test had built. The template corpus declares no anchor, so the fidelity gate never saw a page reference at all.What changed
DocxPageReferenceFields(qa, test scope) reads everyPAGEREFfield, bookmark and paragraph from each XML part of the exported package — the body, headers, footers, nested tables, text boxes — with StAX, rather than from the paragraphs a model lists. It reads complex fields whose instruction runs over several runs, nested fields, simple ones, and the keyword in any case.problems()names a field that names a bookmark the body does not hold, or a name Word does not give a bookmark (a letter, then letters, digits and underscores, 40 at most) — the two Word turns into its error — and one naming more than one bookmark, one in another case or one never closed, since the export names exactly the bookmark it wrote; and field marks out of order or of no type, an instruction where no field's code is open, a simple field closed over an open one, and a bookmark id opened twice.PageReferenceDocxDocuments— four documents that refer to pages, each listing every reference it holds and what it must be written as and read; the two whose shapes the export's report names (the split tables, the page nothing is written on) list those notes too, and the rest are held by the bookmarks their fields name:anchor_kinds: an anchor on each of the nine node kinds that declare one — paragraph, section, container, table, picture, barcode, rule, shape, ellipse — blocks of drawings alone, and anchors named by sentences past Word's 40 characters;reference_places: references standing in a table's composed cell, a layer stack's column, a panel, a row's column and the body, and anchors in each of those;unmarked: the anchors the export writes no bookmark for — a section that draws nothing, one whose first drawing fills a page nothing is written on, one composed in a table's cell, an anchor nothing declares — beside names that clean or fold to another's (a.anda,Introandintro);margin_sections: a block whose shape a row of a table split for page 2's margins carries, a block over a table split under a copy of its header row, an anchor in a header row the page repeats.A reference written as its number stands between paragraphs of its own, so the guard finds it, reads its number, and tells one reading nothing from one not written.
DocxPageReferenceGuardTest:measured geometrynote), and its references read no page;DocxFidelityCorpusTest.composedopens a corpus document on its page, so the guard exports it as the fidelity gate does;exportTouses it.Measured
The four documents in Word 16 (every field updated, as F9 on each story) and LibreOffice 24.2, then the same files with a page put in at the start:
tablePAGEREFas empty where its bookmark is named like one of its numbering sequences:Table(table,TABLEtoo),Figure,Illustration,DrawingandTextread nothing;Equation,ListingandCaptionread their page.separateFromTheTableAbove), which stays with the table below it, to the block's last line. LibreOffice drops a bookmark opened on that paragraph: opened on the panel's first line instead, the same field reads 2. A bookmark opened on an ordinary paragraph above a table, or closed on one below it, is kept: a section opening with a paragraph and ending with a table, and one the other way round, read their page in both editors, and 3 with a page put in.No template declares an anchor, so none of the three reaches the corpus; they are named here and in the recipe, not changed. The guard pins the composed cell's empty number as it stands, so a fix flips that expectation.
Notes
Verification
DocxPageReferenceGuardTest(11 tests) — above.2.5-dev: all 67 byte-identical; report notes 1491 → 1491.clean verifyof the reactor as CI runs it → BUILD SUCCESS (render-docx 2293, qa 1846); the four knowledge checks and core javadoc (0 warnings) pass; examples 93.Lane: test (qa guard over the semantic DOCX backend's page references), no public API change.