Repository navigation
Conversation
…ck paints it A panel is written as a shaded cell, and a cell's fill belongs to the text layer, which Word and LibreOffice both paint over everything drawn behind that layer. A shape standing beside such a panel — on a layer above it, not inside it — was drawn behind the text, and so was covered wherever the two boxes met, the stack's z-index lifting it clear of nothing. Measured at 300 dpi on the one document of the fidelity corpus that holds the case, an 18pt icon laid over a card's corner: the engine's PDF draws 4308 pixels of the icon's own ink, and Word 16.0.20527 and LibreOffice 24.2 drew none at all — not one pixel anywhere on the page. The order is now taken from the tree. A shape a node paints itself, standing on a layer the page paints after a painted layer of an overlay it stands in, goes in front of the text, where no shading reaches it; by z-index, the layers sharing one in the order they were given, which is the order the page paints them in. The order is the tree's and not the geometry's, so such a shape stands in front whether the two boxes meet or not — where they do not, the reader sees the same page either way. A neighbour counts as painted where it, or anything it holds, carries a fill in the tree. That is wider than what the export actually shades: whether a node that paints becomes a cell with shading is decided as it is written, and the tree cannot be asked that. Wider is the safe way round here, a neighbour counted that writes no shading only lifting a shape nothing would have covered. A node bearing borders alone carries no fill and is not counted; a shape container's own fill is not either, being a drawing rather than a cell, though a fill inside it is. Three things leave such a drawing behind the text, and the export goes on naming those as it named this case before. A painted layer the page lays over the drawing in turn, whatever lies below: there the page covers the drawing, and in front it would cover that layer's shading instead, which is the same loss the other way round. A box holding a line of the flow or a picture, which in front it would cover. And a drawing that is no shape of a node's own: a picture drawn where the page puts it and a pass of the layout's own, a timeline's rail, are not asked the question, and over a painted neighbour the naming pass names them where they stand. The corpus holds no instance of either. In Word the icon now draws 4314 pixels against the engine's 4308, in a box the same to the pixel, and the page differs from the page before it only inside that box. The anchor differs from the one written before by behindDoc alone, which the two files' unzipped parts show. Of the corpus's 71 documents two change: the icon's, and cv-sidebar_portrait, where 37 of 39 anchors turn from behind the text to in front. Word and LibreOffice draw that preset's page exactly as they drew it before — 0 of 3,868,706 pixels differ at 200 dpi in each — since nothing was painted over those 37 to begin with. The other 69 documents are byte for byte the ones the export wrote before, and the report's notes go 1498 to 1497. Re-measured over the changed corpus in Word 16.0.20527: 309 of 325 drawings follow their text, 95.08%, the same two counts as before. The two tests of a shape inside a painted panel are given the sign of an overlapping fill. As they stood they asserted the export's decision, for which behindDoc is the sign; what they could not tell was whether the reader sees the shape, the same fixture with its panel unpainted keeping behindDoc="1" and being seen in full. Each now also asks for the note that says the panel's shading hides the shape, and runs that unpainted control, which must not have it. The corpus guard's ledger of drawings left under a neighbour's fill goes to empty, and it gains the check the other way round: a drawing lifted in front must not lie in the box of a painted panel the page paints after it, judged on the page's own paint order. None of the 38 lifted does. LibreOffice draws the icon whole but 14.16pt right of where Word and the page put it. That is its own reading of a cell-anchored shape and not this change: with the card bordered instead of filled, where the icon is behind the text and seen anyway, LibreOffice puts it 14.40pt right and 13.92pt low while Word is within 0.24pt. It is recorded as a thing to measure, not fixed here.
… judge Whether a shape goes in front was decided shape by shape, so over one painted neighbour a shape clear of the text went in front while a shape above it that framed a line stayed behind. Word draws every shape in front over every shape behind, so the file then drew the lower of the two over the higher, which is the loss this was fixing, the other way round. The order is now taken whole, or not taken at all: where any drawing over that painted layer has to stay behind, none of them is lifted. The test of whether a shape frames a line of the flow moved with it, asked once of the stack instead of again of each shape — the second asking became unreachable, and breaking it reddened no test, so it is gone. That has a price, and it falls on the one live preset of the corpus that could have used the fix. cv-sidebar_portrait lays thirty-seven drawings over its painted sidebar; one of them frames the photo it rings, so none is lifted and the document is byte for byte what the export wrote before. Word and LibreOffice draw that page identically either way — 0 of 3,868,706 pixels differ at 200 dpi in each — but that is a measurement of this preset, not a property of the rule. So one document of the seventy-one changes now, by one anchor: the one the case was measured on. Over the changed corpus drawings-follow-text re-measures at 309 of 325 drawings following their text in Word 16.0.20527, the same two counts as before. Both documents whose bytes this change has touched at any point were run in both exports and contribute the same either way. The guard gains the check the other way round — a drawing lifted in front must lie under neither a painted panel nor a drawing left behind that the page paints after it, judged on the page's own paint order — and, with it, the numbers that say what this corpus can be asked. It lifts one drawing and offers no pair at all for those two lists, both asserted exactly. The first version of the check reported "0 of 38" and that was vacuous; a count of the pairs says so at once. A neighbour is counted as painted from the tree, which is wider than what the export shades in one place and narrower in another. Narrower for a shape container written as a shaded cell rather than drawn: a shape above one of those is still covered, and the naming pass names it. Said as the known loss it is, rather than as a safe side. The note no longer asserts shading that may not have been written: a layer counted from the tree carries a fill, and what a cell written for it would do with that fill is what the note now says. LibreOffice's placing of a cell-anchored shape, 14.16pt right of Word and the page, goes into the CHANGELOG and the capability matrix: before this the icon was drawn nowhere at all, and now it is drawn whole and that far across.
…n from The test of whether a drawing has to stay behind the text was asked of every fragment of a layer, and a paragraph's own fragment is in the list that test reads from — so it found its own centre in its own box and answered yes. Every layer bearing text or a picture above a painted one therefore refused the lift for its whole stack, whatever the geometry, and [painted background, text, icons] is the ordinary layer stack: the rule fired almost nowhere. Only a fragment a shape is drawn from is asked now, which is the only kind a lift could put over anything. A badge whose glyph is drawn over it keeps its exception, and the glyph is reached by the walk and asked nothing, a picture being drawn from no fragment of its own. That moves the numbers back. Two documents of the seventy-one change, by 38 anchors: the one the case was measured on, and 37 of cv-sidebar_portrait's over its painted sidebar. Word and LibreOffice draw that preset's page exactly as they drew it before — 0 of 3,868,706 pixels differ at 200 dpi in each — nothing having been painted over those 37 to begin with. The export after the whole-or-nothing rule writes the same 71 documents as the export before it, byte for byte, so what the rule costs is visible only on a built case, and the text that had it costing cv-sidebar_portrait its lift is gone: that was this defect, not a price. Three more answers the tree does not give are now refused rather than guessed. A node whose layer is not among its parent's answers false, as the javadoc already said it did, instead of leaving a lift an inner stack had granted. A node one instance of which stands at more than one place is refused outright: it has one box and one path among those places, so an answer taken from whichever the index kept would be given for all of them. Four fixtures for what the corpus cannot ask: a layer of words over the painted one, which must not keep the shape behind; two painted layers under one shape; a shape in a stack nested over a painted layer; and a node standing twice. The picture fixture now reads the frame's own anchor as well, so a badge quietly gaining a covered-note cannot pass it.
…shapes A badge whose glyph is drawn over it came back in front of the photo it frames, its own fill painted over that picture: the badge's outline is a shape and was lifted, while the picture was not — a picture is drawn where the page puts it and asks the surface it stands on, not its neighbours. That is the inversion the whole-stack rule exists to stop, done by the rule itself, and the fixture written for the case asserted it as correct. The test is now asked of the nodes the export draws as shapes — a drawing, or a shape container that is not a badge holding its initials — and of the fragments it draws them from. Two things were refusing the lift for stacks they have no business in: the decoration fragment the layout gives a painted or bordered wrapper, which is written as a cell and never as a drawing, and the box of a badge holding its initials, which is handed over in front wherever it stands and so cannot be put under anything. Both were measured; the badge left the stack's pin behind a shape already in front of it, the inversion again. The badge's outline frames its glyph, so this is what keeps that stack behind, and the fixture now reads both anchors rather than the report alone — which the last commit's message claimed it did and it did not. A rule of its own for pictures was written for this and then taken out: sabotaged it reddened no test, the badge's outline being what answers for the only shape a picture takes above a painted layer. A picture beside its label in a layer of a stack is written in the flow instead and covered by nothing, so no case drives the rule, and the javadoc says that rather than leaving conservatism nothing holds. Three more fixtures for what does hold: a badge with its initials, a bordered neighbour bearing text, and a shape container that paints nothing. The corpus is what it was: two of the seventy-one documents change, by 38 anchors, and the export writes the same bytes as before this commit.
…only drawing there Whether a shape could be lifted was decided by asking why each of its neighbours stays behind the text, and that question was answered wrongly three times, each time by letting the lower of two shapes be drawn over the higher. A picture drawn where the page puts it, in a nested layer: never lifted, and nothing asked about it. A badge handed over already in front: counted as a thing that stays behind, so the shape beside it was left under a shape standing over it. A node one instance of which stands at more than one place: refused for itself, and not for its neighbours. The question is now the other one. A shape is lifted where it is the only drawing over that painted layer, and otherwise none of the stack is. Being alone needs nothing remembered about the kinds of drawing there are, and the inversion inside a stack is closed by construction rather than by a list that has been short three times. Two predicates go with the enumeration. What counts as drawn is one test — the layout gives the node a fragment a shape is drawn from — which covers a node kind the export does not know as well as a shape of its own, and counts a painted wrapper's decoration too, which can only refuse a lift. A picture counts by being a picture, since it is drawn from no fragment of its own. The price is real and it falls on real documents: a stack that draws twice over a painted layer carries no order at all, even where lifting both would have been safe. cv-sidebar_portrait draws thirty-seven times over its painted sidebar, so none of those is lifted and that preset is byte for byte what the export wrote before. One document of the seventy-one changes now, by one anchor: the synthetic one the case was measured on. The recipe, the capability matrix and the CHANGELOG say that, rather than leaving a reader to think a sidebar CV gets its z-order carried. Over the changed corpus drawings-follow-text re-measures at 309 of 325 drawings following their text in Word 16.0.20527, the same two counts as before. Three fixtures for what the rule now does and two for what it costs: a stack drawing a picture beside its label, a lone shape beside a node standing twice, a stack drawing a badge, a stack drawing a bordered neighbour, and a shape container that paints nothing and so keeps nothing behind. The picture one was twice found passing for another reason — first because the picture never reached the path at all, then because the pin's box framed its centre — and is built so that neither holds: the icon's centre stands past the pin's edge while their boxes still overlap.
The rule was applied only at the overlay that holds the painted layer. Once an inner stack had granted a lift, every outer overlay without a fill of its own was skipped, so a drawing that is a layer of the OUTER stack — painted after the inner stack whole — was never asked about. Measured on a document built from the public DSL: the shape came back behindDoc="0" with a lower relativeHeight than the hood over it, so Word drew the shape over a hood the page draws over the shape. The refusal now runs at every overlay of the climb, painted or not, and asks one thing: is anything drawn after this shape. That also fixes the range the other way. It was refusing because of siblings painted between the painted layer and the shape — which stand UNDER the shape, where the file keeps them under it just as the page does — so the rule is both safer and less conservative than before. Two of its own checks went when breaking them reddened no test. Skipping the shape itself while walking its neighbours: the range starts above the shape, so the shape is never in it, and the parameter went with the check. And a clause for a painted layer above the shape: a layer that paints is drawn, so the one question catches it. Two checks that stay cannot be driven by a fixture, and the javadoc says so rather than leaving them looking held. A node one instance of which stands at more than one place is refused because its box and its path are one among those places — but which place the index keeps is not something a test chooses, and a stack built to need the refusal is as likely to be answered from the place that refuses anyway. A layer not found among its parent's is refused, and no shape of this repository's templates reaches that. Both can only refuse a lift. Two sentences of javadoc were false and are now limits rather than claims. The walk is not "as wide as the export draws": a pass of the layout's own, a timeline's rail, is drawn from a fragment no node owns, so neither walk sees it — no case where that bites could be built, every rail tried bringing a marker or a line of text that refused the lift anyway. And the walk is over children(), which a table, a chart, a list and a paragraph do not implement, so a fill or a drawing composed inside one of them is reached through the node's own fragments instead. Fixtures for what the corpus cannot ask: a shape of a nested stack under an outer stack's drawing, one standing in two stacks at once, and a layer of words over the shape that draws nothing and so keeps nothing behind. Two fixtures turned the other way with the range: a badge and a bordered neighbour stand UNDER the shape, so lifting it is right, and they are renamed for what they now hold. The corpus is unchanged by all of this: one document of the seventy-one, by one anchor, the synthetic one the case was measured on.
…uded A canvas lays its children over one another as a stack does, but says so with their order rather than with z-indices, so the climb — which looked for layers — went past it. What a canvas lays over the shape was therefore never asked about, and the lift was granted: measured on a document built from the public DSL, a canvas holding a shape and a picture over a painted band gave the shape behindDoc="0" and the picture behindDoc="1", their boxes overlapping by 4pt, so Word drew the shape over a picture the page draws over it. The band stopped short of the shape, so no note was written either. The climb now reads any overlay through the one function that already knew both forms — a stack's and a container's layers by z-index, a canvas's children in the order it was given them — which also takes two helpers out of this method. The javadoc said a canvas "is climbed past rather than read", as though that were a neutral fact. It is not: climbing past is safe for finding a painted layer below, where it only costs a lift, and unsafe for finding what is drawn after the shape, where it grants one. That sentence is why the hole survived six rounds of review, and it now says which direction it was unsafe in. A fixture of that shape, and a sabotage that climbs past a canvas again, which reddens it. The corpus does not move: one document of the seventy-one, by one anchor, as before.
This branch has not been deployed
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 panel is written as a shaded cell, and a cell's fill belongs to the text layer, which Word and
LibreOffice both paint over everything drawn behind that layer. A shape standing beside such a
panel — on a layer above it, not inside it — was drawn behind the text, and so was covered wherever
the two boxes met: a layer stack's z-index lifted it clear of nothing.
Measured at 300 dpi on the one document of the 71-document fidelity corpus that holds the case, an
18pt icon laid over a card's corner through a layer stack: the engine's PDF draws 4308 pixels of the
icon's own ink, and Word 16.0.20527 and LibreOffice 24.2 drew none at all — not one pixel anywhere
on the page, the icon's whole box coming back the card's colour in both. With the card unpainted,
the one difference, Word drew every pixel the engine does. #989 made the export name that loss; this
carries the order instead, so there is no loss to name.
What changed
A shape a node paints itself, standing on a layer the page paints after a painted layer of a stack
or container it stands in, goes in front of the text, where no shading reaches it
(
laidOverAPaintedNeighbour). The order is the tree's: layers by z-index, the ones sharing one inthe order they were given, which is the order the page paints them in — and not the geometry's, so
such a shape stands in front whether the two boxes meet or not.
Two things leave the drawing behind instead. A box of its own holding a line of the flow or a
picture, which in front it would cover. And — the rule that carries the weight — anything drawn
after it, in any overlay it stands in: Word draws every shape in front over every shape behind, so
lifting a shape the page covers with another drawing puts the lower of the two over the higher. A
drawing the page paints before the shape is not asked about: the file keeps it below, as the page
does.
That rule is deliberately blunt. It asks whether something is drawn over the shape, not whether that
other drawing would have stayed behind. The second question is the one this PR got wrong three times,
each time by letting the lower of two shapes be drawn over the higher — at a picture drawn where the
page puts it, at a badge handed over already in front, and at a shape whose node stands at more than
one place. Each was a kind of drawing that stays behind for a reason of its own and had to be
remembered in a second place; there is nothing to remember about being alone above. Asking it at
every overlay of the climb rather than only at the one holding the painted layer is what closes the
last of them: a sibling in an outer stack is painted after the inner stack whole.
Four predicates went this way — two of them this PR's own, removed when breaking them reddened no
test: skipping the shape itself while walking its neighbours (the range starts above the shape, so it
is never in it) and a clause for a painted layer above the shape (a layer that paints is drawn, so
the one question catches it).
A neighbour counts as painted where it, or anything it holds, carries a fill in the tree
(
paintsAFill) — wider than what the export shades in one direction and narrower in another, bothdeliberately. Wider, because whether a section, a container or a row that paints becomes a cell with
shading is decided as it is written, and the tree cannot be asked that;
rememberShadedPanel, whichthe naming pass uses, is the side that knows, and a neighbour counted that writes no shading only
lifts a shape nothing would have covered. Narrower, because a shape container's own fill is not
counted, being usually drawn rather than written as a cell: where one is written as a cell, holding
text, a shape above it is still covered, and the naming pass names it. That one is a known loss, not
a safe side.
The price, and where it falls
A shape with anything drawn over it carries no order at all, even where lifting it would have
been safe — over a badge the file already holds in front, for one. That is not a corner:
cv-sidebar_portraitdraws 37 times over its painted sidebar, so nothing of it is lifted and thatpreset is byte for byte what the export wrote before. The z-order is not carried for a sidebar CV
of this shape, and this PR should not be read as fixing one. What it fixes is one measured form — a
drawing with a painted layer under it and nothing drawn over it — and in the corpus that form occurs
in exactly one document, a synthetic one.
Earlier rounds of this change lifted those 37, and twice claimed a reason for not lifting them that
turned out to be a defect of mine rather than a rule. Both are gone; what stands is measured.
Tests
DocxDrawingsTest's two tests of a shape inside a painted panel are given the sign of anoverlapping fill. As they stood they asserted the export's decision, for which
behindDocis thesign; what they could not tell was whether the reader sees the shape, since the same fixture with its
panel unpainted keeps
behindDoc="1"and is seen in full. Each now also asks for the note that saysthe panel's shading hides the shape, and runs that unpainted control, which must not have it.
Sabotaging the control into a painted one turns the test red, which is what makes it a control.
Two checks that stay cannot be driven by a fixture, and the javadoc says so rather than leaving them
looking held: a node one instance of which stands at more than one place is refused, because its box
and its path are one among those places — but which place the index keeps is not something a test
chooses; and a layer not found among its parent's is refused, which no shape of this repository's
templates reaches. Both can only refuse a lift. Two javadoc sentences that claimed more than the code
does are now limits instead: the walk is not as wide as the export draws (a timeline's rail is drawn
from a fragment no node owns, and no biting case could be built), and it is over
children(), whicha table, a chart, a list and a paragraph do not implement.
DocxNeighbourFillGuardTest's ledger of drawings left under a neighbour's fill goes to empty, and itgains the check the other way round: a drawing lifted in front must lie under neither a painted panel
nor a drawing left behind that the page paints after it, judged on the engine's own paint order
(
LayoutGraph.fragments()) and the guard's own geometry.What that corpus can and cannot say is stated in numbers rather than left to a reader of empty
lists. It lifts one drawing, and offers no pair at all for those two lists to be judged over —
both asserted exactly. An earlier version of the check reported "0 of 38" and that was vacuous; a
count of the pairs, added on review, said so at once. The rules themselves are held by the fixtures
written for them, and what the guard holds over the corpus is that the corpus has not started asking
something new.
Verification
DocxNeighbourFillGuardTestnamed1 drawing over the corpus and now names 0; its other assertions — nothing named that its
geometry does not find, the four panels the layout breaks across pages, every note classified, 503
drawings read — hold unchanged.
the pixel (x 341.76..359.52, y 80.16..97.92). The whole page differs from the page before it only
inside that box: 2058 of 1,543,179 pixels at 200 dpi, bounded by x 341.6..359.6pt, y
79.9..98.3pt. The anchor differs from the one written before by
behindDocalone, which the twofiles' unzipped
word/document.xmlshow. Every file measured in either editor is byte for bytethe one this export writes, which
md5sumsays rather than I.byte the ones the export wrote before,
cv-sidebar_portraitamong them. The report's notes go1498 → 1497, the one that is gone being the loss that is gone, with 56 reworded.
drawings-follow-textre-measured in Word 16.0.20527, the version the figure wastaken with: 309 of 325 drawings follow their text, 95.08% — the same two counts as before, over
38 documents judged and 8 of them failing. The threshold is 95%, so the margin is still one
drawing. Both documents whose bytes this change touched at any point were run through the protocol
and contribute the same either way:
cv-sidebar_portraitPASS with 0 of its 37 drawings besidetext that moved, so none of the ratio is its, and the icon's PASS with 1 of 1 following. The icon's
document passes with the fix as it did without — the protocol reads lines of text, not where a
drawing stands, which is why the loss went unseen there in the first place.
render-docx2413,qa1874. Eleven sabotages, each caught by exactly theset of tests named for it and by no other, every touched file byte for byte afterwards. Seven found
something a reading would not have: a restored file keeping its old timestamp let an incremental
build hold the previous sabotage's class; two rules were found covering for each other and each
given a fixture; three rules reddened nothing and were taken out as subsumed or unreachable; and a
fixture written for the picture path was twice found passing for another reason and rebuilt both
times.
GATE PASS: 823 / 339 / 2413 / 5 / 142 / 127 / 1874 over the reactor, core javadoc 0warnings, the four knowledge checks clean, examples 93 with no committed-asset drift.
laidOverAPaintedNeighbouris asked once per node that draws something, and walks thatnode's ancestors and the layers above its painted neighbour. The corpus exports in the same time as
before, within the noise of a single pair of runs.
Not fixed here, measured
LibreOffice 24.2 now draws the icon whole — 4333 pixels against the engine's 4308 — but puts it
14.16pt right of where Word and the page put it. That is its own reading of a cell-anchored shape and
not this change: with the card bordered instead of filled, where the shape is behind the text and
seen anyway, LibreOffice puts it 14.40pt right and 13.92pt low while Word is within 0.24pt of the
engine. The shift is about the width of the card's left padding; the fidelity gate reads lines of
text, so it never saw it, and it is written into the CHANGELOG and the capability matrix rather than
left in a PR.