diff --git a/CHANGELOG.md b/CHANGELOG.md index d28df7c99..20de0d8a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4313,6 +4313,14 @@ follow semantic versioning; release dates are ISO 8601. ### Tests +- **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 + stand nearer the page, and the card would not grow when text is added to it in an editor. + `DocxBodyShapePanelTest` now holds a card whose text and padding fill it to within a quarter of a + point, under its default clip, which cuts nothing: its row is held at least its outline's height, + as is the row of a card with room to spare. + - **The DOCX fidelity corpus holds its three body shape containers where the export now sets them.** With the boxes written as panels, Word and LibreOffice set every line of `narrow_container` and `rounded_card` at the page's words, and Word the paragraph under each box diff --git a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxBodyShapePanelTest.java b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxBodyShapePanelTest.java index 65ce5663c..b521da205 100644 --- a/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxBodyShapePanelTest.java +++ b/render-docx/src/test/java/com/demcha/compose/document/backend/semantic/docx/DocxBodyShapePanelTest.java @@ -17,6 +17,7 @@ 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.DocumentTextStyle; import com.demcha.compose.document.style.DocumentTransform; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; @@ -360,6 +361,30 @@ void aBoxWithNoClipOrNothingToCutGrowsWithItsText() throws Exception { + "them, and what follows stands up to that much lower"); } + @Test + void aCardItsTextFillsGrowsWithItsTextAsOneWithRoomToSpare() throws Exception { + // Its default clip has nothing to cut, though its text and padding fill it, so its row is held + // at least its outline's height and grows with text added to it. Held exactly at that height, + // it would set its lines where they stand in Word, and the fidelity gates, which measure + // lines, would not see that it no longer grows. Here it is a quarter of a point taller than + // its text and padding, as the page lays them out in a taller card. + double outline = export(page -> page.add(pageWideCard(200).build())).placed("Text").placementHeight() + 0.25; + assertThat(outline).as("the text laid out at its own height, not the card's").isLessThan(200); + Exported exported = export(page -> page + .addParagraph("Above") + .add(pageWideCard(outline).build()) + .addParagraph("Below")); + + PlacedNode box = exported.placed("Box"); + PlacedNode text = exported.placed("Text"); + // The page leaves less than half a point between the foot of its text's padding and the outline's. + assertThat(text.placementY() - box.placementY()).isBetween(0.0, 0.5); + XWPFTableRow row = onlyTable(exported.document()).getRow(0); + assertThat(ruleOf(row)).isEqualTo(STHeightRule.AT_LEAST); + assertThat(heightOf(row)).isEqualTo(Math.round(outline * 20) - 2 * 20); + assertThat(exported.report().bySubject()).doesNotContainKeys("clipped shape container", "shape container"); + } + @Test void itsPaddingStandsRoundTheOutline() throws Exception { Exported exported = export(page -> page @@ -436,6 +461,18 @@ private static ShapeContainerBuilder card(ClipPolicy clip) { .build()); } + /** A card as wide as the page between its margins, holding a heading and a paragraph. */ + private static ShapeContainerBuilder pageWideCard(double height) { + return new ShapeContainerBuilder().name("Box").roundedRect(328, height, 12).fillColor(SURFACE).stroke(BORDER) + .topLeft(new SectionBuilder().name("Text").padding(DocumentInsets.of(12)).spacing(4) + .addParagraph(p -> p.text("A rounded card").textStyle(DocumentTextStyle.builder().size(12).build())) + .addParagraph(p -> p.text("Its text runs over three lines from the card's top left corner, " + + "and the card is just tall enough to hold them with its padding " + + "round them: a line more, and the card needs to be taller.") + .textStyle(DocumentTextStyle.builder().size(10).build())) + .build()); + } + private static DocumentNode note() { return new ParagraphBuilder().name("Note").text(NOTE).build(); }