diff --git a/CHANGELOG.md b/CHANGELOG.md index 20de0d8a9..5746c8590 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9db84b2da..921837f32 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/docs/recipes/docx-export.md b/docs/recipes/docx-export.md index d69e33431..32515cfa7 100644 --- a/docs/recipes/docx-export.md +++ b/docs/recipes/docx-export.md @@ -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, @@ -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 diff --git a/qa/src/test/java/com/demcha/compose/document/templates/fidelity/SyntheticDocxCorpus.java b/qa/src/test/java/com/demcha/compose/document/templates/fidelity/SyntheticDocxCorpus.java index a6574b138..1daf6b254 100644 --- a/qa/src/test/java/com/demcha/compose/document/templates/fidelity/SyntheticDocxCorpus.java +++ b/qa/src/test/java/com/demcha/compose/document/templates/fidelity/SyntheticDocxCorpus.java @@ -4,6 +4,7 @@ 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; @@ -11,6 +12,7 @@ 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; @@ -26,9 +28,9 @@ * 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.
+ * 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. * *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 @@ -36,6 +38,9 @@ * 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.
+ * + *The card under the icon is held as the editors set it today, off the page: see + * {@link #iconOverCard}.
*/ final class SyntheticDocxCorpus { @@ -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(""); private SyntheticDocxCorpus() { } @@ -59,7 +66,36 @@ static ListIts 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.
+ */ + 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."))); } /** diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv index c6f584269..2714dd32e 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux-lines.tsv @@ -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 @@ -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 diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv index 530d7f99f..cf14305c8 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-linux.tsv @@ -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 @@ -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 diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv index 4d2de2c02..fd8f7c5be 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows-lines.tsv @@ -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 @@ -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 diff --git a/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv b/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv index ecb44428a..d79c451a5 100644 --- a/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv +++ b/qa/src/test/resources/docx-fidelity/libreoffice-windows.tsv @@ -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 @@ -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 diff --git a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv index ce1bfa028..8380a86c2 100644 --- a/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv +++ b/qa/src/test/resources/docx-fidelity/word-windows-lines.tsv @@ -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 @@ -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 diff --git a/qa/src/test/resources/docx-fidelity/word-windows.tsv b/qa/src/test/resources/docx-fidelity/word-windows.tsv index a7110acb5..67760767c 100644 --- a/qa/src/test/resources/docx-fidelity/word-windows.tsv +++ b/qa/src/test/resources/docx-fidelity/word-windows.tsv @@ -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 @@ -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