Repository navigation
fix(docx): write a list composed in a table cell on the lines its table laid out - #884
Merged
Merged
Conversation
…le laid out A table lays a composed cell's content out under its own path, so a list in DocumentTableCell.node(...) had no lines to read: its items were written with no line height, and Word set each at the face's own line. SlateOrange's certifications stood 0.3pt lower with every item, the fourth 1.18pt low in Word and 1.05pt in LibreOffice. matchComposedText now walks a table's composed paragraphs and lists in layout order. A list takes the run of its table's lines that holds its items in turn, opening at a line no node before it took: each the line the page writes for the item, its marker and indent in the text, and in a hangingIndent list the item's own marker, level, before it. A list holding an item of runs, one the page reads as markdown, or a hanging list with a blank item is left as before. itemLines, markerToText and markerWidth read through textFragmentsOf, so a matched list takes what a list in the flow takes from its lines. An item in a row Word keeps whole rises into the gap above it, as a paragraph composed there does. The certifications now stand 0.26pt low at most in Word and within 0.1pt in LibreOffice; the other 61 corpus documents are byte-identical, and the Word and LibreOffice-on-Windows baselines change only there.
…hange's CI run - Taken from the DOCX Fidelity artifact of this change's CI run. Against the run before it, only cv-slate_orange moved: its certifications, 0.45 to 1.05pt low, now stand 0.10pt low to 0.10pt high; 52 of 62 documents still pass. - Without it, the gate's half point a line would let those lines slide back to where they stood.
…ake a list's marker only where it is visible - A list of one item could open its run at the line of a paragraph the page reads as markdown, of the same text, waiting for the markdown pass after the walk: **0.00** beside a list of 0.00 lost its exact line, and the list took the paragraph's height. The paragraphs left before a list are now matched by their markdown before it is; a table with no composed list matches its paragraphs as before. - A hanging list's marker line is taken only for a visible marker: one that draws nothing lays out no line, and the line level before its item is another node's. The check of the marker's letters goes: the page sets a visible marker's line just before its item's text. - TableLines.take polls the head of its queue where it can, so pairing a table's paragraphs stays linear. - Tests: a markdown paragraph before a list of its text, a drawn marker, a tree of items flat and hanging; the test of a list matched to none no longer asserts a paragraph that could not collide with it. - The CHANGELOG, recipe and capability matrix say which composed lists are not matched, an item run onto the next page among them.
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 table lays a composed cell's content out under its own path, so a list in
DocumentTableCell.node(...)has no fragments at a path of its own.DocxLayoutMetricsfound none for it. Its items were written with now:lineRuleat all, and Word and LibreOffice set each at the face's own line, taller than the page's. A paragraph composed in a cell already had a way out:matchComposedTextpairs it with its table's fragments by text. A list had none.SlateOrange's certifications are a bullet list beside the education column. They stood 0.26, 0.48, 0.84 and 1.18pt low in Word, and 0.15, 0.45, 0.75 and 1.05pt low in LibreOffice: 0.3pt lower with every item.What changed
matchComposedTextpairs lists as well as paragraphs. It walks a table's composed paragraphs and lists in layout order, overTableLines: the table's lines, each line's text key computed once, and a queue per text of the lines not taken yet.itemFragments: the run that holds its items in turn, opening at a waiting line of its first item's text.DocxMarkdown.items): the marker and indent set in the text, then the item.endsWithwould let a one-item list take another paragraph's line that merely ends with its text.hangingIndentlist, the line just before an item's is its marker's, set in a column of its own: level with the item's text, only a drawing for a drawn marker, and only for a visible marker. A marker that draws nothing lays out no line, so a line level before such an item's is another node's: a paragraph's in the cell before.**0.00**sets its line as0.00, and a list item of that text would otherwise take the line first. Tables with no composed list match their paragraphs exactly as before.The match fails closed. No run, and the list is written as before, in these cases:
hangingIndentlist with a blank item, which the page draws as a marker alone, as in the flow;The list readers go through
textFragmentsOf. These areitemLines,markerToTextandmarkerWidth, next tolineHeightandlineGap, which already did. A matched composed list then takes from its lines what a list in the flow takes:lineSpacingbetween a wrapped item's lines;hangingIndentlist's marker column;With no place of its own, a Word list whose marker the page sets in its text keeps its stated column (
prefixColumnneeds a placement).seatTheItemraises an item into the gap above it in a row Word keeps whole (inARowKeptWhole()), as a composed paragraph has been since fix(docx): raise a cell's first line into its padding, and a row's out of the space above it #882. A composed list has no placement, solistOnOnePageis false for it. A list set over two pages in a panel still keeps its space above: its row is neithercantSplitnor placed whole.Corpus
Only
cv-slate_orange.docxchanges. The other 61 DOCX files are byte-identical with this PR's base, and the report's 969 notes are unchanged.The four certifications, in pt below the page, measured against the committed baselines:
The first item opens its cell, with no space above it to rise into. The other three rise 0.2pt into their gaps. Matched lines alone, without the raise, left all four 0.22–0.26pt low in Word and 0.10pt low in LibreOffice.
The Word and LibreOffice-on-Windows baselines are rewritten from this measurement; only
cv-slate_orangerows change. The Linux LibreOffice baseline is rewritten from this PR's CI artifact, in a commit of its own. Against the run before it, onlycv-slate_orangemoved, 3 lines nearer and none further, and 52 of 62 documents still pass.Verification
./mvnw -B -ntp clean verify→ BUILD SUCCESS: render-docx 1435 (+12), qa 1820.DocxComposedListTest(new):lineSpacinginto its line, off its space above;DocxListItemSeatTest: an item in a composed cell rises into the gap above it and owes it below; the first item takes its position.DocxListReportTest: a matched composed list'slineSpacingis no longer named; an unmatched one still is.Lane: shared-engine — DOCX semantic backend (
render-docx), no public API change.