Repository navigation
fix(docx): stand a page zone's text on the page's baseline in Word - #867
Merged
Merged
Conversation
A page zone's line is written as an exact line, as tall as its tallest part's line on the page, or taller for a zone picture or a part the page fits smaller. Its distance from the edge puts that part's baseline where the page has it, so a lone part's padding and margin are held. The report counts parts off that baseline, and names a zone that reads otherwise on its first page as not measured. A zone sharing its kind with another page zone stands in a frame at its own height. A line reaching past a positive margin gets the margin written negative, and is named. A zone paragraph's anchor, and a picture the page sets off the baseline, are named. The zones output option moves from GAP to REPORTED in the field ledger. No gap is left, so GAP is removed as a fate.
…e a zone built as nothing A zone's paragraph is as many exact lines as its part of the most lines takes on the page. Word grows a footer up from its distance, so a footer of two lines stands a line further from the edge, its first line on the page's baseline; its frame is as tall as its lines, and its reach past the margin is counted line by line. Parts after a part of more lines than one stand on a later line in Word, so where they stand is said to be not measured. Content built for the first page is compared with the content written by text, face, size and weight, runs and pictures, not by text alone, so a zone set at another size on its first page keeps Word's line rather than an exact line its letters overflow. A zone whose content is none for no page in particular is named as not written. DocxZoneParts holds the parts' comparison and pictures, with a unit test. Bands and zones share DocxTextBands.distanceFromEdge, the negative margin and the hairline between equal frames.
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
A page zone (
session.chrome().zone(...)) is written as one line of a Word header or footer. That line was Word's single line for its face. Its top sat at the zone content's top in a header, and its foot at the content's foot in a footer (DocxLayoutMetrics.zoneDistanceFromEdge). So three things were left to Word, and nothing named them:Measured against the engine's PDF, LibreOffice stood a header's text up to 0.97pt higher than the page does.
Two zones of one kind, such as a cover's header on the first page and the running header on the rest, shared Word's one distance from the edge, and the last zone's distance won. The cover's header stood 15.9pt high in Word and 16.4pt high in LibreOffice.
A zone paragraph's anchor had no bookmark, and nothing named it. A zone whose content is none for no page in particular was not written, and nothing named that either.
DocxNodeFieldLedgerTestcarried these as thezonesoutput option'sGAP, the last gap in the ledger.What changed
zonePlacementworks out where a zone's line stands in Word (ZonePlacement), from the zone's text in the layout.DocxZoneParts.tallestPicture). Word stands such a picture on the baseline, and an exact line cuts whatever passes its top. So the line holds the picture under the four fifths above its baseline.autoSize). Word writes that part at its style's size, so the line is the style's.DocxTextBands.BASELINE_SHARE, measured on exact lines). So a lone part's padding and margin above and below are in that distance. A line the page sets against the edge stops at the edge. The arithmetic is the text band's, now oneDocxTextBands.distanceFromEdge(header, baselineFromEdge, line)both call.reportZoneLine, which counts a part off that baseline. The old model of a line Word placed itself (wordsBaseline) is gone. Parts after a part of more lines than one stand on a later line in Word, so where they stand is said to be not measured.DocxZoneParts.readAlikecompares two versions of the content: as written, for no page in particular, and as built for the first page the zone is drawn on (DocxLayoutMetrics.zoneFirstPage). It compares what sets the line: each part's text, face, size and weight, each run's, a picture's size and place, a page field's kind. Colour is left out, since it sets nothing of the line and a colour built afresh is not equal to itself. On a difference Word keeps its own line, and the note says the zone is not measured.writeZoneLinewrites the exact line.placeZonewrites the distance asw:pgMar/@w:headeror@w:footer.ZONE_REACH_CLEARANCE), counted line by line. The margin is then written negative, asplaceBandalready does for a text band, so Word holds the body at the margin, and the report names it. Text set against the margin reaches up to half a point past it in the default face under about 22pt, and Word moves the body down by that much, unnamed.holdTheBodyAtTheMarginwrites the negative margin for bands and zones alike.w:framePr) at its own height, as a text band sharing its kind already does.AT_LEAST), so a part Word sets in more lines goes on below them rather than hiding.separateFromAnEqualFrame.writeBandintoframeAcrossTheMargins. It makes the same calls in the same order, with the band'sEXACTrule, so bands write the same bytes.zoneParagraphLostnames two more things:DocxZoneParts.setOffTheBaseline).DROPPED), unless the layout shows it drawn on no page either. Its content function must return a node on every page; a zone absent from some pages says so throughappliesTo.DocxZoneParts(new, package-private) holds what a zone's parts are and how they compare, with its own unit test.zonesmoves fromGAPtoREPORTED;GAPis removed as a fate, along witheveryGapSaysWhatIsLost. A field added later is written, reported or inert in the change that adds it.Verification
./mvnw -B -ntp install -pl :graph-compose-render-docx→ BUILD SUCCESS, 1175 tests, 0 failures, 1 skipped (the property-gated fidelity probe).DocxZoneLineTest(new, 25 tests) reads each zone's baseline from the PDF the engine draws. Word's baseline it reads from the file: the distance from the edge or the frame's place, plus four fifths of the exact line.DocxZonePartsTest(new, 4 tests): content built afresh, its colours new objects, reads alike; other text, size, weight, page field, count of parts, a run's face, or a picture's size or place does not; the tallest picture and a picture off the baseline.DocxTextBandsTest: the shared distance puts a header's and a footer's baseline where asked and stops at either edge.DocxPageZonePositionTest: a header's distance follows its padding, 10pt for 10pt, and is under the 9pt to the content's top. The old test pinned the distance at the content's top, which this change moves by design.DocxZoneReportTest: a smaller part set lower than an 18pt one is the one part off. The 18pt part keeps its place, as Word's line now stands on its baseline. Before, both counted as off.The tests fail without the code they cover. 31 sabotages were run on the final code, each breaking one thing, and each made the tests that cover it fail:
Measured in Word 16.0.20430 and LibreOffice 26.8.0.3. Probe documents were exported before and after the change and converted by both editors. Each zone's baseline was compared with the engine's PDF, before → after:
Corpus bytes: the 62 corpus documents, exported deterministically (
DocxFidelityCorpusTest -Dgraphcompose.docxFidelity=export), are byte-identical to the export before the change. No corpus document has a page zone, and the text bands written throughframeAcrossTheMargins,separateFromAnEqualFrameandholdTheBodyAtTheMarginare unchanged.The report across the corpus is unchanged, to the note: 955 notes.
Documentation guards:
-Dtest='com.demcha.documentation.**'→ 166 tests, 0 failures;DocxPageZoneTest,DocxTransparentWrapperTest,TimelineRailAcrossBackendsTestandRtlAcrossBackendsTest→ 50 tests, 0 failures.The full reactor gate was not run, since no public signature, POM or workflow file changed.
Known limits
Lane: render-docx backend, plus tests and docs.