Repository navigation
fix(docx): raise a line Word sets low into the space above it, where both editors keep it - #875
Merged
Merged
Conversation
…both editors keep it - Word and LibreOffice stand an exact line's baseline four fifths of the way down it whatever the face (measured on the fifteen template faces and JetBrains Mono, 8 to 36pt, and in the corpus's lines from Word's own line tops). The text was moved to the page's baseline by its position only: half points, left alone under half a point, and scaled in LibreOffice by the face's height over its em (Spectral 1.53, Poppins 1.49). - raiseIntoTheSpaceAbove moves a line Word sets low by 0.1pt or more up into its paragraph's w:before and owes as much below it, so what follows stays; what the space cannot give stays on the position where the whole difference calls for one. A line set high keeps its position. - Not a paragraph that is not its Word paragraph's alone, a line in an overlay, a line cut to fit, a paragraph the layout moves to a new page or breaks over one. A column's later layer takes back the raise the line above it still owes. - Tests: the raise, what is owed, the floor, the exclusions and the column resume; the space tests that write default text allow for its raise (DocxExports.DEFAULT_LINE_RAISE, pinned from the layout's seat). - Word and LibreOffice (Windows) fidelity baselines rewritten: Word 27 documents nearer, LibreOffice 21; the recipe's Line height row and the CHANGELOG. - assets/readme/examples/word-export-companion.docx re-rendered: its spaces around raised lines move by the raise.
… and keep a raise owed past an empty layer - A panel's first line raised into its space above took the room the panel's top border reach comes out of (takeTheTopBorderInside): the line gave it back, the panel grew by what it owed, and the note said more than the line stood off. writePanelPiece now keeps the border's share of that space, less what the padding takes, out of the raise (panelsFirstLineKeeps). - A layer column's later layer that writes nothing no longer drops what the raised line above it owes: the cell ends with it. - raiseIntoTheSpaceAbove checks every condition it documents; the Javadoc names overlays (layer stacks among them), a container's stacked lines and the panel's border space. - Docs: the backend capability matrix's paragraph row, the recipe and the CHANGELOG list the same exclusions; the corpus figures corrected. - Tests: a panel's first line leaves its top border the space above it, the note unchanged; a later layer that writes nothing leaves the raise owed.
…se into the space above The CI artifact of the DOCX Fidelity job: 21 documents nearer the page, two medians 0.01pt further, as on Windows.
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 and LibreOffice stand an exact line's baseline four fifths of the way down it whatever the
face; the page sets it the face's ascent below the line's top. Measured on the fifteen faces the
templates use and JetBrains Mono, 8 to 36pt, one line per page, the 0.8 share held for every face
in both editors (Word within 0.04pt in median, inside its 0.12pt grid), and held in every context
of the DOCX fidelity corpus too — 3155 first lines in cells, after tables and in lists, their line
tops read from Word itself.
So the shift the export computes is right; how it applies it was not:
w:position, which counts in half points, and onlywhere the shift was half a point or more. A seat error of a quarter point — Helvetica's lines,
which the page seats 0.78 of the way down, stood 0.12 to 0.38pt low in Word — is the worst case
for that step: 0.2 rounds to nothing and 0.3 overshoots to 0.5, so both were left as Word set
them. Across the corpus the seat left a median 0.22pt in almost every document.
height over its em — Spectral 1.53, Poppins 1.49, Volkhov 1.31, Carlito 1.18 (measured) — so
Spectral's 24pt line, raised 4pt, stood 2.4pt high there.
Both editors keep a paragraph's space above as written — LibreOffice to the twip, Word on its
0.12pt grid — so a line moved up into it stands where the page sets it in both.
What changed
In
DocxSemanticBackend(and the measurement noted onDocxTextBands.BASELINE_SHARE):raiseIntoTheSpaceAbove: a line Word sets lower than the page by 0.1pt or more (Word's gridis 0.12pt; less moves a line a step or nothing) is moved up into its paragraph's
w:beforeby asmuch as that holds, written to the twip, and the same is owed below the paragraph
(
owePendingSpacingAfter), so what follows stays where it was.w:position, where the whole difference calls for one: aline 0.6pt low under 0.2pt of space is moved by its position too, not left 0.4pt low.
line is written; a line Word sets high keeps
w:position.ownLine— a line pair's, a container'sstacked lines, a text box's — or one holding other runs first), or a line cut to its pictures;
the flow — whose writers measure what follows them from the page;
paragraphOpeningItsPage), whose space above Worddrops, or breaks over one (
onOnePage), whose lines on the next page the space above does notmove;
(
panelsFirstLineKeeps, fromwritePanelPiece's border reach less what the padding takes):raised into it, the line gave it back and the panel grew by what it owed.
page; the raise the line above it still owes is added to that gap (
raiseOwedByTheLastLine), asit stood that much higher. A raise paid since — at a cell's end, before a table — is not
(
flushSpacingAfter), and a later layer that writes nothing leaves the raise owed in its cell.Docs: the recipe's Line height row, the backend capability matrix's paragraph row,
CHANGELOG.md(v2.5.0). The committed preview
assets/readme/examples/word-export-companion.docxisre-rendered: its spaces above and below raised lines move by the raise, nothing else.
Verification
./mvnw -B -ntp clean verify→ BUILD SUCCESS: render-docx 1332 run, 0 failures, 1 skipped(+14), qa 1820, examples 93, core 818.
DocxBaselineSeatTest,DocxLayerColumnsTest): a low line is raised into the spaceabove and owes it below; what the space cannot give goes to the position; a raise under a tenth
is left; a high line keeps its space; a line pair's first, taller half keeps the shared line; a
line in a layer stack keeps its space; a paragraph broken over a page keeps its space; what a
space leaves of a shift over half a point keeps its position; a cell's last line owes its raise
inside the cell and nothing of it below the row; a panel's first line leaves its top border the
space above it, the note unchanged; a later layer takes back the raise of the line above, not one
a card's cell already paid, and a later layer that writes nothing leaves it owed in the cell; and
the default text's raise (6 twips, Helvetica 14pt; 3 at 7pt) is derived from the layout's seat
and Word's four fifths.
(
DocxExports.DEFAULT_LINE_RAISE,SMALL_LINE_RAISE), and the space below it as that much morewhere it is flushed: 78 expectations across 21 classes, each the same 6 (or 3) twips.
lowering through the space above too, nothing owed below, a line pair moving its line, raising
inside overlays, raising a page-opening paragraph, raising one broken over a page, no floor, the
resume dropping the raise, a raise paid at a cell's end carried again, the position's threshold
read off what is left, the panel's border space raised into, an empty later layer dropping the
raise.
word-windows*.tsv, viascripts/docx-visual/word-fidelity.ps1 -Update): 27 documents nearer, the sum of the medians 1.65pt nearer —letter-blue_banner0.39 → 0.18pt,
letter-mint-editorial-letter0.18 → 0.03pt,cv-modern_professional0.32 →0.18pt; two a grid step further (below). LibreOffice on Windows (
libreoffice-windows*.tsv):21 nearer —
letter-mint-editorial-letter0.29 → 0.01pt,cv-modern_professional0.28 →0.04pt — two medians 0.01pt further. LibreOffice on Linux (
libreoffice-linux*.tsv, from CI'sdocx-fidelityartifact): the same 21 nearer and the same two 0.01pt further. The report isunchanged: the same 969 notes.
Notes for review
invoice-luma_studio: its company lines, raised to where the page sets them, now show theirblock standing 0.2 to 0.3pt high in both editors (median 0.13 → 0.14pt);
letter-panel's medianmoves 0.14 → 0.16pt, one line a grid step. Splitting each line's drift into the seat and the
block's position (Word's line tops against the page's) shows such block offsets of 0.3 to 0.7pt
in about fifteen documents, the same in Word and LibreOffice, which the old seat error partly
cancelled; they are their own change.
scaling still applies to it and to what a space above cannot give.
whether Word drops a paragraph's space above where a row's cell continues on a new page is not
measured. No corpus line moves further for it.
Lane: shared-engine (DOCX backend) —
render-docxonly, no public API.