Repository navigation
fix(docx): let a list's items and a shared line carry their block's drawings - #978
Merged
Merged
Conversation
…rawings A paragraph was offered to the drawings standing beside it only while a paragraph node was being written, so a block whose text another writer writes had no paragraph to offer at all: a card whose only text is a list's items, a CV entry's head band written as one Word line. Their drawings fell back to the text nearest them — in the body, the paragraph above the card, and a drawing is placed from that paragraph's top. Lengthen that paragraph by a line and the card moves while the drawing stays: measured in Word 16.0.20527 and LibreOffice 24.2 on a probe of each case, the card's own text moved 9.72pt and 19.40pt and the drawing 0.00 in both. offerASeat now takes the piece of text, the path of its node and that text's box, and three writers call it: a paragraph node's own paragraph, as before; each item of a list, with that item's box, which DocxLayoutMetrics.ItemText now carries (built by the same reckoning as firstTextBox, extracted as textBoxOf); and the one line two halves of a band share, offered as the half that starts it, since both are written in that one paragraph and a seat stands for a paragraph. An item offers its place where the export matched the layout's items to the list's one by one, which their line gap and their own seating need as well. Carried so, the drawing moves with its card — 9.70pt in Word and 19.35pt in LibreOffice — and stands on its corner, 0.09pt from where the page draws it against 13.76pt before. A table's own cell text offers none, and the drawings whose block's text is written only so keep the nearest-text rule: a table's cells state no width of their own, their table's grid holding it, and a drawing anchored in a cell is placed from the cell's text column, which cannot be read without that width; a text cell is set to the middle by default besides, and such a cell carries no drawing at all. The corpus has 1811 cells stating no width of their own, 1713 of them set to the middle, so a place offered for them could be taken in no document of it. A test holds that limit, and the recipe, the capability matrix and the CHANGELOG name it. Corpus of 71 documents: drawings placed from the page's edges 39 to 32 — all six of invoice-luma_studio's are anchored in their holders, and the hairline paragraph its section opened to carry them is no longer written. Six documents changed, 135 paragraphs; notes 1497 to 1497. The Word fidelity baseline passes unchanged, and across the six changed documents every drawing stands within 0.30pt of its previous place in Word, 2821 of 2833 of them identical: the largest move is six dots in two CVs 0.25 to 0.30pt higher, as far as Word sets the shared line they now stand by below the page. DocxTextWriterAnchorTest holds the rule from both sides, and a sabotage of each of its five parts was caught.
A drawing nothing holds is taken in by the text it stands nearest, counted from that text's edges, and the line two halves of a band share was offered as the box of the half that starts it — a title laid out 60pt wide on a line 360pt wide. A drawing beside the dates at the line's right end was reckoned 250pt from it, and a paragraph of body text above took the drawing instead: the very placement this is meant to end. The place is offered for the whole line now (offerTheSharedLine): its left edge from the half that starts the line, whose text begins where the line begins and whose raise Word reads back from the line's first run, and its right edge from the far end of the other. A seat's right edge is read nowhere else than in that distance, so what a block's own paragraph carries is unchanged. No byte of the fidelity corpus moves: no document of it has a drawing whose nearest text the line's right edge decided. A test holds the rule — the shared line takes a drawing beside the dates although a paragraph of body text stands just above it — and a sabotage of it was caught.
… is set in A drawing anchored in a table cell is placed from the cell's text column, and a place states that column by its text's left edge less the indent written on its paragraph (Seat#textLeft). writeLinePair writes that indent from the inked start of the half that starts the line, while the place was offered with the edge of the box that half is laid out in — a box as wide as the band where the half is set in it by its alignment. A band in a card whose starting half is centred in its box had its column read short by the band's width, and the cell was given no drawing to carry at all. The place now states that inked start, which is where the line's own indent puts its text; its right edge still reaches to the end of the other half's box, which is the line's own reach. The two edges answer different questions, and the Javadoc says which. No byte of the fidelity corpus moves: every band in it starts with a half laid out at its own text's left, so the two edges agree there. A test holds the rule — a band whose starting half is centred in a box as wide as the band, in a card, places its drawing from the cell's text column — and a sabotage of it was caught. Also: the recipe now describes both of the line's edges, as the CHANGELOG and the capability matrix already did; the test that claimed to hold the nested item writer held the top-level one, so it is a test of the writer that was really uncovered, a list whose marker the export writes itself; and offerASeatFor, left with one caller, is folded into it.
Word draws no text past a cell's left edge, so a line pulled out past its card's padding is
written with no indent at all (leftIndentTwips) and its text does not start where the layout puts
it. A place states the cell's text column by its text's left edge less the indent written on the
paragraph, so such a line cannot state it: the column came out as far left as the line is hung,
and the drawing was placed from there or handed to a neighbouring cell. Such a line now offers no
place, and the drawings beside it keep to the text nearest them, as they did before a shared line
offered one. A test holds it and a sabotage of it was caught.
The sentence saying a cell was given no drawing to carry at all was wrong, in the Javadoc, the
CHANGELOG and the recipe alike: the cell does carry it, from the wrong column — 292pt across its
cell where the page draws it at 150, measured on the test's card. All three now say that.
The test that claimed to hold the item writer with a marker the export writes itself held neither:
marker("*") is a marker Word draws, so it went through the writer three other tests already
cover. It is a drawn marker now, which has no Word list definition to be given, and the test says
the document was laid out — a drawn marker in a stack's panel is not laid out through items(...),
and without a layout nothing is drawn and the rest of the test proves nothing.
No byte of the fidelity corpus moves.
…sides The rule is that a line the layout starts left of its cell's text column offers no place for the drawings beside it — a band pulled out by a margin below zero, into the cell's padding or past it. The Javadoc, the CHANGELOG, the recipe and the test's name all said "past its cell's padding", which is narrower than the code: Word holds the indent at nought for a line hung into the padding as well. The capability matrix, which said a shared line is offered from the start its indent is written from, now names the case where that is not so. The test comment that still carried the sentence the commit before last refuted — that such a cell is given no shape to carry — says what was measured instead: the cell carries it, from the wrong column. The hung-line test now also says where the drawing goes, as its neighbour does, so losing it to the page would redden it; and the shared line's place is held from above as well as across, by the offset down from the line's own top. Tests 10, and each of the eight rules broken once and caught.
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 paragraph was offered to the drawings standing beside it only while a paragraph node was being
written (
offerASeat), so a block whose text another writer writes had no paragraph to offer atall: a card whose only text is a list's items, and a CV entry's head band written as one Word line.
A drawing such a block holds found no holder of its own and fell back to the text nearest it — in
the body, the paragraph above the card, which is placed from that paragraph's top. Lengthen that
paragraph by a line and the card moves while the drawing does not.
Measured on a probe of each, in Word 16.0.20527 and LibreOffice 24.2, by lengthening the paragraph
above by one line in Word and re-exporting the PDF:
In the unedited document the card's dot stood 13.76pt off its corner in Word, where the page draws
it on the corner: the card's own text starts its padding below the dot's top, and the paragraph
above does not.
What changed
A paragraph written for any piece of text is offered to the drawings beside it.
offerASeattook the node being written and only acted on a
ParagraphNode; it now takes the piece of text,its node's path and that text's box, and three writers call it — a paragraph node's own paragraph,
as before; each item of a list; and the one line two halves of a band share.
Each item of a list is offered with its own text's box.
DocxLayoutMetrics.ItemText, which thelist writer already matches item by item to seat their lines, now carries that item's
TextBox,built by the same reckoning as
firstTextBox(extracted astextBoxOf), andseatTheItemoffersit with the list's path (
listPath, set bywriteList). So a drawing the list or a block round itholds takes the first item written on its page, and a drawing nothing holds that stands beside the
third item takes that item. An item offers its place only where the export matched the layout's
items to the list's one by one, which the items' line gap and their own seating need as well: a
list with an item split across a page is laid out as more pieces of text than it has items, and
none of its items offers a place. The recipe says so.
The line two halves share is offered as the half that starts it (
offerTheSharedLine), fromits text's own start, reaching across to the end of the other's box. Both halves are written in
one Word paragraph and a place stands for a paragraph, so one place is enough; the half that starts
the line is the one whose text begins where the line begins, and whose raise Word reads back from
the line's first run. The line itself is raised by the lesser of what its two halves call for, so a
drawing placed from it stands as far below the page's place as the line does — 0.2 to 0.33pt,
measured where that raise is decided.
Its two edges are not the same thing, and both matter:
wide as the band where the half is set in it by its alignment. A drawing anchored in a table cell
is placed from the cell's text column, and a place states that column by its text's left edge
less the indent written on the paragraph (
Seat.textLeft) — andwriteLinePairwrites thatindent from the inked start. Stated as the box, the column came out the band's width to the left
and the cell carried the drawing from there: 292pt across its cell where the page draws it at 150.
pulled out by a margin below zero, into the cell's padding or past it. Word draws no text past a
cell's left edge, so the indent written for such a line is held at nought (
leftIndentTwips) andits text does not start where the layout puts it; the column cannot be read back from the place,
so the drawings beside it keep to the text nearest them, as they did before any of this.
out in, so that a drawing beside the dates at the line's right end, or in the gap between the two
texts, stands beside the line. The text nearest a drawing is reckoned from its edges, and the
half that starts the line is laid out in a box of its own width — the title of the test's band is
60pt of a line 360pt wide. Offered as that box, the line was reckoned 250pt from a drawing beside
the dates, and a paragraph of body text above it took the drawing instead.
A table's own cell text offers none, and the drawings whose block's text is written only so keep
the nearest-text rule. Not an omission but a limit with two causes outside the writer, both
measured: a table's cells state no width of their own — their table's grid holds it,
writeGrid—and a drawing anchored in a cell is placed from the cell's text column, which
Seat.widthOfcannotread without that width; and a text cell is set to the middle by default, which carries no drawing
at all (LibreOffice 24.2 sets such a cell's text from its top, measured for #977). The corpus has
1811 cells stating no width of their own, 1713 of them set to the middle, so a seat offered for
them could be taken in no document of it.
aCardWhoseTextATablesCellsHoldLeavesItWhereItWasholdsthat: the cell takes nothing and the paragraph above the card keeps the drawing, as before this
change. The probe for the case moved its text 9.72pt in Word and 19.40pt in LibreOffice with the
drawing standing still. Lifting it needs two repairs of their own — reading a cell's width from its
table's grid, and carrying the "page fills the cell" exception onto the holder's path — and is its
own step. The recipe, the capability matrix and the CHANGELOG name the limit.
Verification
The corpus (71 documents). Drawings placed from the page's edges: 39 → 32. All six of
invoice-luma_studio's are now anchored in their holders, and the hairline paragraph its sectionopened to carry them is no longer written (111 paragraphs → 110). Six documents changed, 135
paragraphs:
cv-navy_sidebar,cv-serif_headline,invoice-consulting, its longer form,invoice-luma_studioand its longer form. Notes 1497 → 1497, none added or removed. The 32 leftare
invoice-payments' 13,invoice-workspace' 8,invoice-luma_studio_long's 6,invoice-merchant's 4 andinvoice-obsidian_long's 1.Word 16.0.20527 and LibreOffice 24.2, the probes above: the drawing moves with its card
(+9.70pt in Word, +19.35pt in LibreOffice against the text's +9.72 and +19.40) and stands on the
card's corner, 0.09pt from where the page draws it against 13.76pt before.
Both fidelity baselines pass unchanged, Word over all 71 documents
(
scripts/docx-visual/word-fidelity.ps1, no-Update) and the Linux LibreOffice gate green in CI.Neither of the shared line's two edges, nor the guard on a line hung past its cell, moves a byte of
the corpus. No document of it has a drawing
whose nearest text the line's right edge decided, and every band in it starts with a half laid out
at its own text's left, so the inked start and the box agree there. Both are cases the corpus does
not hold, found by reading rather than measured in it, and each is held by a test and a sabotage
rather than claimed as corpus-proven.
Tests.
DocxTextWriterAnchorTest— ten: a card whose text is a list's items carries thedrawing laid over it, placed from the cell's text column; the first item carries one standing
beside the last; the item whose text a drawing stands beside carries it; an item of a list whose
marker is drawn — written by the other item writer — carries them too, from its own column; the shared
line carries its band's drawings; it carries one no block holds although a paragraph of body text
stands just above it; it states its text's own start rather than the box its half is set in, so a
cell carries the drawing from the right column; a shared line in a cell places it from that
column, and down from the line's own top; a line the layout starts left of its cell's text column
offers no place, and the paragraph above the card keeps the drawing; and a card whose only text a
table's cells hold leaves the drawing in the paragraph above it.
A sabotage script broke each of eight rules once — a list item offering no place, a list left
without a path of its own, a shared line offering none, a shared line stating the box its half is
set in rather than its text, a shared line reaching no further than the half that starts it, a
line hung past its cell offering a place all the same, a paragraph node offering none, and an item
taking the list's first box instead of its own — and all eight were caught, the paragraph-node one
by
DocxDrawingHolderTest, which holds that older rule.Not pinned by a test, and said rather than papered over: a list with an item split across a page
offers no place (the sentence above);
ItemText.boxis null only for an item the page laid no lineof, which the matched-items condition already rules out; the place's path is the list's own, which
no test distinguishes from an ancestor's — holders are matched by path prefix, so a drawing held by
anything round the list matches either way; the guard on a hung line declines a place for a hang of
under a twip as well, where holding the indent at nought is what the layout asked for anyway; and a
shared line states the top of the half that starts it, while Word sets the line by the higher half's
top, so a band whose second half is set larger is ranked a little off against other text — no band
of the corpus has one.
The gate measures where the editors set the text, not where they draw: the drawings this change
moves are measured by hand in both editors above, and the corpus has no assertion on a drawing's
position — recorded as a gap, not claimed as covered.