Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4313,6 +4313,31 @@ follow semantic versioning; release dates are ISO 8601.

### Tests

- **The DOCX fidelity corpus holds an icon laid over a card's corner, as the export writes it
today.**
- `SyntheticDocxCorpus` takes `icon_over_card`: a layer stack laying an 18pt icon over the top
right corner of a card, a section painted as a panel with 14pt padding, between the
document's longest paragraph above it and a paragraph under it.
- Measured in Word 16 and in LibreOffice 24.2 and 26.8, which set it alike down the page;
LibreOffice 24.2 also sets the card's text 14pt right of the page, as it sets a card with the
same padding and no icon over it.
- The stack is written as a band, and a band measures the space above and below it to its
first and last block written, through the card's padding, which the card writes again inside
its cell: the card's text stands 14.0pt lower than the page sets it, and the paragraph under
the card 28.0pt lower.
- The icon is drawn from the page's edges, where the page draws it, as the paragraph nearest
it, the card's, starts 14pt below its top, further than a shape may rise above a paragraph in
a table cell. It stands 14pt above the card's top as the editors set it, and stays there when
text above the card is edited: Word's editing protocol (`edit-protocol-corpus.ps1`),
lengthening the paragraph above the card, finds the card moved 36pt and the icon not
(`drawings-follow-text` fails).
- The baselines record the document as it stands: its rows hold the card's lines and the
paragraph under it that far from the page. A change that writes the band's space from the
card's edges moves them nearer and rewrites the rows; a change that moves them further fails
the gate. The gate measures lines of text: where the icon stands, it does not see.
- Each baseline takes the document's rows alone, from the run of its editor — Linux's from
CI's.

- **A body card whose text fills its outline is held to grow with that text.** The fidelity gates
measure where lines stand. Were such a card's row written exactly as tall as its outline, every
line of it in Word would stand where it stood, the paragraph under it in LibreOffice 24.2 would
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,9 @@ Choose the smallest tests that match the change:
which documents moved. A new preset joins the corpus in its family's `*DocxCorpus` class,
and so does a preset on a longer fixture; a shape no preset has — a page break inside a
panel, a break in a row's column, a shape container narrower than the block it stands in
or clipped short of its text — goes in `SyntheticDocxCorpus`. Its baseline holds it as
the editors set it, a defect of the export included, and the change fixing that rewrites
its rows nearer the page.
or clipped short of its text, an icon a layer stack lays over a card's corner — goes in
`SyntheticDocxCorpus`. Its baseline holds it as the editors set it, a defect of the export
included, and the change fixing that rewrites its rows nearer the page.
CI runs it on Linux (the `DOCX Fidelity` job) against `libreoffice-linux.tsv`, and uploads
what it measured: LibreOffice sets text a little differently per platform, so a change that
moves documents nearer the page takes that artifact's files as the Linux baseline, and its own
Expand Down
17 changes: 13 additions & 4 deletions docs/recipes/docx-export.md
Original file line number Diff line number Diff line change
Expand Up @@ -1092,9 +1092,16 @@ recolour what is under it in Word and it keeps the colour it was flattened to.
spacer level with another layer's content only keeps its place and is not
written, the space above the first block written and below the last is the
page's distance from the stack's edges, and a later layer resumes the page's
distance below the blocks above it, to its first block's content. What that block
holds above its content past that distance — a margin, a padding, a contained
picture's room reaching above where the layers before it end — comes out of its own
distance below the blocks above it, to its first block's content. The space above
and below is measured to the band's first and last block written, through a panel
among the layers: a panel opening the band writes its top padding again inside its
cell, and one closing it its bottom padding, so the band stands that much too tall
there. A card with 14pt padding under an icon laid over its corner stood 14pt low in
Word and LibreOffice, and the paragraph under it 28pt (`icon_over_card` in the
fidelity corpus holds its lines as they stand, not where the icon stands). What a
later layer's first block holds above its content past the page's distance — a
margin, a padding, a contained picture's room reaching above where the layers before
it end — comes out of its own
edge, in a band as in a column's cell, where the block is a paragraph, a picture, a
list or a table in its layer's own flow — under plain sections and containers, and
alignment and anchor wrappers — its edge above at zero or more; not in a row,
Expand Down Expand Up @@ -1490,7 +1497,9 @@ from the top, and give every cell of a row the row's largest top margin, so the
first paragraph starts where the text's does; one with a border above it is not used; a row whose top is not known that way, a line not set
from the left and a repeated header row carry none. When the nearest paragraph cannot carry
the shape, it stays on the page rather than going to a paragraph further off, which is not
the text it stands by. The paragraph's top is where Word places from:
the text it stands by: an icon laid over a panel's top corner whose nearest paragraph is
the panel's first, more than a stroke's width below the icon's top, stays where the page
draws it when the text above the panel moves. The paragraph's top is where Word places from:
the space written above its first line included, and the line where Word stands it — an
exact line's baseline four fifths of the way down it, raised by the run's position —
measured in Word 16.0.20430 and LibreOffice to a tenth of a point. Only the first paragraph
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import com.demcha.compose.document.api.PageMarginRule;
import com.demcha.compose.document.dsl.ParagraphBuilder;
import com.demcha.compose.document.dsl.SectionBuilder;
import com.demcha.compose.document.node.LayerAlign;
import com.demcha.compose.document.node.ParagraphNode;
import com.demcha.compose.document.style.ClipPolicy;
import com.demcha.compose.document.style.DocumentColor;
import com.demcha.compose.document.style.DocumentInsets;
import com.demcha.compose.document.style.DocumentStroke;
import com.demcha.compose.document.style.DocumentTextDecoration;
import com.demcha.compose.document.style.DocumentTextStyle;
import com.demcha.compose.document.svg.SvgIcon;
import com.demcha.compose.document.table.DocumentTableColumn;
import com.demcha.compose.document.table.DocumentTableStyle;
import com.demcha.compose.font.FontName;
Expand All @@ -26,16 +28,19 @@
* children and inside a panel nested in it, page breaks the page sets as boxes of no height in a
* row's column and in a stack's layer, and a card whose table the layout runs over three pages,
* with other margins from the second. Three others set a shape container holding text in the
* body: a box narrower than the page, a box clipped to its outline, and a card. Their text is
* set in a face the export embeds, so both editors break at the page's words the lines they
* set at the page's width.</p>
* body: a box narrower than the page, a box clipped to its outline, and a card. One more lays an
* icon over a card's top right corner, in a layer stack. Their text is set in a face the export
* embeds, so both editors break at the page's words the lines they set at the page's width.</p>
*
* <p>The export writes the three boxes as panels, their text inside the outline: Word sets the
* narrow box's note and the card's text at the page's words, and the paragraph under each box
* where the page sets it; of the clipped box, whose clip is written as the panel's exact height,
* both editors show the lines the page shows. The gate reads lines of text only: whether the
* text stays inside the outline, and whether the card grows when text is added to it in an
* editor, it does not see.</p>
*
* <p>The card under the icon is held as the editors set it today, off the page: see
* {@link #iconOverCard}.</p>
*/
final class SyntheticDocxCorpus {

Expand All @@ -47,6 +52,8 @@ final class SyntheticDocxCorpus {
private static final DocumentColor INNER = DocumentColor.rgb(224, 233, 244);
private static final DocumentColor INK = DocumentColor.rgb(26, 86, 148);
private static final DocumentInsets MARGIN = DocumentInsets.of(36);
private static final SvgIcon ICON = SvgIcon.parse("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'>"
+ "<circle cx='5' cy='5' r='5' fill='#1a5694'/></svg>");

private SyntheticDocxCorpus() {
}
Expand All @@ -59,7 +66,36 @@ static List<DocxCorpusDocument> documents() {
synthetic("card_over_pages", SyntheticDocxCorpus::cardOverPages),
synthetic("narrow_container", SyntheticDocxCorpus::narrowContainer),
synthetic("clipped_container", SyntheticDocxCorpus::clippedContainer),
synthetic("rounded_card", SyntheticDocxCorpus::roundedCard));
synthetic("rounded_card", SyntheticDocxCorpus::roundedCard),
synthetic("icon_over_card", SyntheticDocxCorpus::iconOverCard));
}

/**
* A layer stack laying an icon over the top right corner of a card, between a paragraph above
* it and one under it. The paragraph above is the document's longest, so an edit that
* lengthens the longest paragraph moves the card, and the icon has to move with it.
*
* <p>Its baseline holds the export as Word and LibreOffice set it today, off the page: the
* stack writes the card's padding as space above and below the card as well as inside it, so
* both editors set the card's text about 14pt lower than the page, and the paragraph under it
* about 28pt lower. The icon is placed from the page's edges, so it stands
* where the page draws it, above the card the editors moved down, and stays there when text
* above the card is edited. The gate reads lines of text only: where the icon stands, it
* does not see.</p>
*/
private static void iconOverCard(DocumentSession session) {
session.pageSize(400, 500).margin(MARGIN);
session.pageFlow(page -> page.spacing(8)
.addParagraph(p -> text(p, "Before the card: this paragraph is the longest in the document, so "
+ "an edit that lengthens the longest paragraph lengthens it and moves "
+ "the card and the icon over its corner down the page together."))
.addLayerStack(stack -> stack
.layer(new SectionBuilder().softPanel(SURFACE, 0, 14)
.addParagraph(p -> text(p, "A card under an icon laid over its top right "
+ "corner, its text held in by its padding."))
.build())
.layer(ICON.node(18), LayerAlign.TOP_RIGHT))
.addParagraph(p -> text(p, "After the card: this paragraph stands under it on the page.")));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# libreoffice build 420(Build:2) on linux — document, line, drift below the page (pt), drift right of the page (pt), letters
# rows of invoice-metered_long, invoice-obsidian_long, synthetic-break_in_column, synthetic-break_in_panel, synthetic-card_over_pages: libreoffice build 420(Build:2) on linux
# rows of synthetic-clipped_container, synthetic-narrow_container, synthetic-rounded_card: libreoffice build 420(Build:2) on linux
# rows of synthetic-icon_over_card: libreoffice build 420(Build:2) on linux
cv-blue_banner 0:8f1010120ed6 0.02 1.90 jordanrivera
cv-blue_banner 0:8d7a2cca3060 0.00 0.14 +442055551000|jordan.rivera@exam
cv-blue_banner 0:b944906ed84c -0.20 -0.38 professionalsummary
Expand Down Expand Up @@ -5131,6 +5132,12 @@ synthetic-clipped_container 0:1d37c4498256 0.98 0.60 thatrunpastthefootoftheboxa
synthetic-clipped_container 0:e7653895ac38 0.98 0.60 seen.onlythelinestheboxholdsarer
synthetic-clipped_container 0:2633ce73dc24 -0.02 0.10 aftertheclippedbox:thisparagraph
synthetic-clipped_container 0:667f8b3b3eff -0.02 0.10 noneofitslinesrunsoverthebox.
synthetic-icon_over_card 0:22d08874d8c4 -0.02 0.10 beforethecard:thisparagraphisthe
synthetic-icon_over_card 0:ea7f305a7f76 -0.02 0.10 thatlengthensthelongestparagraph
synthetic-icon_over_card 0:e3ac6d2ba79c -0.02 0.10 theiconoveritscornerdownthepaget
synthetic-icon_over_card 0:887d93e46549 13.98 14.10 acardunderaniconlaidoveritstopri
synthetic-icon_over_card 0:abfed2fbe03e 13.98 14.10 itspadding.
synthetic-icon_over_card 0:573902639706 27.98 0.10 afterthecard:thisparagraphstands
synthetic-narrow_container 0:fa13cab3fe73 -0.02 0.10 beforethebox,onalineofitsown.
synthetic-narrow_container 0:ed60c8a4c178 -0.52 0.59 anotecentredinaboxnarrowerthanth
synthetic-narrow_container 0:d7899490a8cd -0.52 0.59 thepagewrapsitoverthreelinesinsi
Expand Down
2 changes: 2 additions & 0 deletions qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
document engine_pages editor_pages lines matched median p90 over2
# rows of invoice-metered_long, invoice-obsidian_long, synthetic-break_in_column, synthetic-break_in_panel, synthetic-card_over_pages: libreoffice build 420(Build:2) on linux
# rows of synthetic-clipped_container, synthetic-narrow_container, synthetic-rounded_card: libreoffice build 420(Build:2) on linux
# rows of synthetic-icon_over_card: libreoffice build 420(Build:2) on linux
cv-blue_banner 2 2 54 52 0.16 0.25 0
cv-boxed_sections 2 2 58 58 0.06 0.11 0
cv-centered_headline 2 2 58 56 0.12 0.29 0
Expand Down Expand Up @@ -70,5 +71,6 @@ synthetic-break_in_column 1 1 7 7 0.02 0.02 0
synthetic-break_in_panel 3 3 8 8 3.02 5.02 5
synthetic-card_over_pages 3 3 114 114 0.98 1.08 0
synthetic-clipped_container 1 1 10 8 0.98 0.98 0
synthetic-icon_over_card 1 1 6 6 13.98 27.98 3
synthetic-narrow_container 1 1 6 6 0.52 1.02 0
synthetic-rounded_card 1 1 7 7 0.02 0.62 0
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# libreoffice build bf6ef2662401 on windows — document, line, drift below the page (pt), drift right of the page (pt), letters
# rows of invoice-metered_long, invoice-obsidian_long, synthetic-break_in_column, synthetic-break_in_panel, synthetic-card_over_pages: libreoffice build bf6ef2662401 on windows
# rows of synthetic-clipped_container, synthetic-narrow_container, synthetic-rounded_card: libreoffice build bf6ef2662401 on windows
# rows of synthetic-icon_over_card: libreoffice build bf6ef2662401 on windows
cv-blue_banner 0:8f1010120ed6 0.02 1.95 jordanrivera
cv-blue_banner 0:8d7a2cca3060 0.00 0.24 +442055551000|jordan.rivera@exam
cv-blue_banner 0:b944906ed84c -0.20 -0.38 professionalsummary
Expand Down Expand Up @@ -5025,6 +5026,12 @@ synthetic-clipped_container 0:1d37c4498256 0.98 -0.40 thatrunpastthefootofthebox
synthetic-clipped_container 0:e7653895ac38 0.98 -0.40 seen.onlythelinestheboxholdsarer
synthetic-clipped_container 0:2633ce73dc24 -0.02 0.10 aftertheclippedbox:thisparagraph
synthetic-clipped_container 0:667f8b3b3eff -0.02 0.10 noneofitslinesrunsoverthebox.
synthetic-icon_over_card 0:22d08874d8c4 -0.02 0.10 beforethecard:thisparagraphisthe
synthetic-icon_over_card 0:ea7f305a7f76 -0.02 0.10 thatlengthensthelongestparagraph
synthetic-icon_over_card 0:e3ac6d2ba79c -0.02 0.10 theiconoveritscornerdownthepaget
synthetic-icon_over_card 0:887d93e46549 13.98 0.10 acardunderaniconlaidoveritstopri
synthetic-icon_over_card 0:abfed2fbe03e 13.98 0.10 itspadding.
synthetic-icon_over_card 0:573902639706 27.98 0.10 afterthecard:thisparagraphstands
synthetic-narrow_container 0:fa13cab3fe73 -0.02 0.10 beforethebox,onalineofitsown.
synthetic-narrow_container 0:ed60c8a4c178 -0.02 -0.41 anotecentredinaboxnarrowerthanth
synthetic-narrow_container 0:d7899490a8cd -0.02 -0.41 thepagewrapsitoverthreelinesinsi
Expand Down
2 changes: 2 additions & 0 deletions qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
document engine_pages editor_pages lines matched median p90 over2
# rows of invoice-metered_long, invoice-obsidian_long, synthetic-break_in_column, synthetic-break_in_panel, synthetic-card_over_pages: libreoffice build bf6ef2662401 on windows
# rows of synthetic-clipped_container, synthetic-narrow_container, synthetic-rounded_card: libreoffice build bf6ef2662401 on windows
# rows of synthetic-icon_over_card: libreoffice build bf6ef2662401 on windows
cv-blue_banner 2 2 54 52 0.16 0.25 0
cv-boxed_sections 2 2 58 58 0.06 0.11 0
cv-centered_headline 2 2 58 56 0.12 0.29 0
Expand Down Expand Up @@ -70,5 +71,6 @@ synthetic-break_in_column 1 1 7 7 0.02 0.02 0
synthetic-break_in_panel 3 3 8 8 3.02 5.02 5
synthetic-card_over_pages 3 3 114 114 0.98 1.08 0
synthetic-clipped_container 1 1 10 8 0.98 0.98 0
synthetic-icon_over_card 1 1 6 6 13.98 27.98 3
synthetic-narrow_container 1 1 6 6 0.02 0.02 0
synthetic-rounded_card 1 1 7 7 0.02 0.02 0
7 changes: 7 additions & 0 deletions qa/src/test/resources/docx-fidelity/word-windows-lines.tsv
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# word 16.0 (16.0.20430) on windows — document, line, drift below the page (pt), drift right of the page (pt), letters
# rows of invoice-metered_long, invoice-obsidian_long, synthetic-break_in_column, synthetic-break_in_panel, synthetic-card_over_pages: word 16.0 (16.0.20527) on windows
# rows of synthetic-clipped_container, synthetic-narrow_container, synthetic-rounded_card: word 16.0 (16.0.20527) on windows
# rows of synthetic-icon_over_card: word 16.0 (16.0.20527) on windows
cv-blue_banner 0:8f1010120ed6 -0.02 -0.08 jordanrivera
cv-blue_banner 0:8d7a2cca3060 0.00 -0.07 +442055551000|jordan.rivera@exam
cv-blue_banner 0:b944906ed84c -0.21 -1.21 professionalsummary
Expand Down Expand Up @@ -5147,6 +5148,12 @@ synthetic-clipped_container 0:7365672653fc 0.99 0.02 andtheboxstaysastallasitwas
synthetic-clipped_container 0:f0da0f0af799 1.02 0.02 whateveritisgiventohold.
synthetic-clipped_container 0:2633ce73dc24 -0.02 0.02 aftertheclippedbox:thisparagraph
synthetic-clipped_container 0:667f8b3b3eff -0.02 0.02 noneofitslinesrunsoverthebox.
synthetic-icon_over_card 0:22d08874d8c4 -0.01 0.02 beforethecard:thisparagraphisthe
synthetic-icon_over_card 0:ea7f305a7f76 -0.01 0.02 thatlengthensthelongestparagraph
synthetic-icon_over_card 0:e3ac6d2ba79c -0.01 0.02 theiconoveritscornerdownthepaget
synthetic-icon_over_card 0:887d93e46549 13.99 0.06 acardunderaniconlaidoveritstopri
synthetic-icon_over_card 0:abfed2fbe03e 13.99 0.06 itspadding.
synthetic-icon_over_card 0:573902639706 28.02 0.02 afterthecard:thisparagraphstands
synthetic-narrow_container 0:fa13cab3fe73 -0.01 0.02 beforethebox,onalineofitsown.
synthetic-narrow_container 0:ed60c8a4c178 0.03 0.03 anotecentredinaboxnarrowerthanth
synthetic-narrow_container 0:d7899490a8cd 0.03 0.03 thepagewrapsitoverthreelinesinsi
Expand Down
2 changes: 2 additions & 0 deletions qa/src/test/resources/docx-fidelity/word-windows.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
document engine_pages editor_pages lines matched median p90 over2
# rows of invoice-metered_long, invoice-obsidian_long, synthetic-break_in_column, synthetic-break_in_panel, synthetic-card_over_pages: word 16.0 (16.0.20527) on windows
# rows of synthetic-clipped_container, synthetic-narrow_container, synthetic-rounded_card: word 16.0 (16.0.20527) on windows
# rows of synthetic-icon_over_card: word 16.0 (16.0.20527) on windows
cv-blue_banner 2 2 54 52 0.10 0.23 0
cv-boxed_sections 2 2 58 58 0.12 0.21 0
cv-centered_headline 2 2 58 56 0.12 0.25 0
Expand Down Expand Up @@ -70,5 +71,6 @@ synthetic-break_in_column 1 1 7 7 0.03 0.03 0
synthetic-break_in_panel 3 3 8 8 0.95 0.99 0
synthetic-card_over_pages 3 3 114 114 1.03 1.15 0
synthetic-clipped_container 1 1 10 10 0.99 0.99 0
synthetic-icon_over_card 1 1 6 6 13.99 28.02 3
synthetic-narrow_container 1 1 6 6 0.03 0.03 0
synthetic-rounded_card 1 1 7 7 0.01 0.02 0
Loading