diff --git a/CHANGELOG.md b/CHANGELOG.md index d4401594d..9d195bfae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ PDF `GoTo` actions. External links are unchanged. ### Public API +- **`RowBuilder.verticalAlign(...)` + `RowVerticalAlign`** (`@since 1.9.0`). Seats a + row's children on the cross axis within the row band, whose height is that of the + tallest child: `TOP` (the default), `CENTER`, or `BOTTOM` — the `align-items` + analogue for a horizontal row, without manual coordinates. The measure phase is + unchanged and `TOP` rows render byte-for-byte as before, so existing documents are + unaffected. + - **`GraphCompose.documents()` + `MultiSectionDocumentBuilder` / `MultiSectionDocument`** (`@since 1.9.0`). Concatenates several independently authored `DocumentSession` sections — each with its own page size, margins, fonts, and footer numbering — diff --git a/assets/readme/examples/row-vertical-align.pdf b/assets/readme/examples/row-vertical-align.pdf new file mode 100644 index 000000000..ff57810af Binary files /dev/null and b/assets/readme/examples/row-vertical-align.pdf differ diff --git a/examples/README.md b/examples/README.md index 3d8deb3d1..0a0cfd787 100644 --- a/examples/README.md +++ b/examples/README.md @@ -77,6 +77,7 @@ are with the canonical DSL, then jump to its detailed section below. | [Block alignment](#block-alignment) | `addAligned(align, node)` / `addSvgIcon(icon, w, align)` — seat any fixed-size node left / centre / right across the content width | [PDF](../assets/readme/examples/block-align.pdf) · [Source](src/main/java/com/demcha/examples/features/layout/BlockAlignExample.java) | | [Content bleed](#content-bleed) | `band.bleedToEdge(TOP, LEFT, RIGHT)` / `bleed(DocumentBleed.of(...))` — a section's fill reaches the trimmed page edge while its children stay in the content margin | [PDF](../assets/readme/examples/content-bleed.pdf) · [Source](src/main/java/com/demcha/examples/features/layout/BleedExample.java) | | [Row columns & TOC](#row-columns--toc) | `row.columns(auto(), weight(1), auto())` — size columns by content / fixed points / weight; with `line().fill()` it builds a dot-leader table of contents | [PDF](../assets/readme/examples/row-columns.pdf) · [Source](src/main/java/com/demcha/examples/features/layout/RowColumnsExample.java) | +| [Row vertical align](#row-vertical-align) | `row.verticalAlign(TOP / CENTER / BOTTOM)` — seat a row's children on the cross axis within the band set by the tallest child | [PDF](../assets/readme/examples/row-vertical-align.pdf) · [Source](src/main/java/com/demcha/examples/features/layout/RowVerticalAlignExample.java) | ### 📋 Templates recommended @@ -494,6 +495,23 @@ flow.addRow(r -> r.columns(auto(), weight(1), auto()) [📄 View PDF](../assets/readme/examples/row-columns.pdf) · [📜 Full source](src/main/java/com/demcha/examples/features/layout/RowColumnsExample.java) +### Row vertical align + +`RowBuilder.verticalAlign(...)` seats a row's children on the cross axis within +the row band, whose height is that of the tallest child. A short label beside a +large price moves from the top to the middle to the bottom of the band as the +alignment changes — the `align-items` analogue for a horizontal row, no manual +coordinates. `TOP` is the default, so existing rows are unchanged. + +```java +flow.addRow(r -> r.verticalAlign(RowVerticalAlign.BOTTOM) + .addParagraph(bigPrice) // tallest child sets the band height + .addParagraph(smallLabel)); // seated on the band bottom +``` + +[📄 View PDF](../assets/readme/examples/row-vertical-align.pdf) · +[📜 Full source](src/main/java/com/demcha/examples/features/layout/RowVerticalAlignExample.java) + ### Advanced tables `DocumentTableCell.rowSpan(int)` mirrors `colSpan(int)`. diff --git a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java index 8148a5087..4f4af036d 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -9,6 +9,7 @@ import com.demcha.examples.features.chrome.PdfChromeExample; import com.demcha.examples.features.layout.BleedExample; import com.demcha.examples.features.layout.RowColumnsExample; +import com.demcha.examples.features.layout.RowVerticalAlignExample; import com.demcha.examples.features.layout.BlockAlignExample; import com.demcha.examples.features.lists.NestedListExample; import com.demcha.examples.features.shapes.LineCapExample; @@ -157,6 +158,7 @@ public static void main(String[] args) throws Exception { System.out.println("Generated: " + BlockAlignExample.generate()); System.out.println("Generated: " + BleedExample.generate()); System.out.println("Generated: " + RowColumnsExample.generate()); + System.out.println("Generated: " + RowVerticalAlignExample.generate()); System.out.println("Generated: " + TransformsExample.generate()); System.out.println("Generated: " + TableAdvancedExample.generate()); diff --git a/examples/src/main/java/com/demcha/examples/features/layout/RowVerticalAlignExample.java b/examples/src/main/java/com/demcha/examples/features/layout/RowVerticalAlignExample.java new file mode 100644 index 000000000..ccb2735dd --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/features/layout/RowVerticalAlignExample.java @@ -0,0 +1,86 @@ +package com.demcha.examples.features.layout; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.node.RowVerticalAlign; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.examples.support.ExampleOutputPaths; + +import java.nio.file.Path; + +/** + * Runnable showcase for v1.9 {@code RowBuilder.verticalAlign(...)}: cross-axis + * placement of a row's children within the band set by the tallest child. A + * short {@code / month} label seated beside a large price moves from the top to + * the middle to the bottom of the band as the alignment changes — no manual + * coordinates. + * + *
{@code
+ * flow.addRow(r -> r.verticalAlign(RowVerticalAlign.BOTTOM)
+ * .addParagraph(bigPrice) // tallest child -> sets the band height
+ * .addParagraph(smallLabel)); // seated on the band bottom
+ * }
+ *
+ * @author Artem Demchyshyn
+ */
+public final class RowVerticalAlignExample {
+
+ private static final DocumentColor INK = DocumentColor.rgb(24, 28, 38);
+ private static final DocumentColor MUTED = DocumentColor.rgb(120, 126, 135);
+ private static final DocumentColor BAND = DocumentColor.rgb(238, 241, 246);
+
+ private RowVerticalAlignExample() {
+ }
+
+ /**
+ * Renders the same price row at {@code TOP}, {@code CENTER}, and {@code BOTTOM}
+ * vertical alignment.
+ *
+ * @return path to the generated PDF
+ * @throws Exception if rendering or file IO fails
+ */
+ public static Path generate() throws Exception {
+ Path pdfFile = ExampleOutputPaths.prepare("features/layout", "row-vertical-align.pdf");
+
+ try (DocumentSession document = GraphCompose.document(pdfFile)
+ .pageSize(380, 300)
+ .margin(DocumentInsets.of(34))
+ .create()) {
+ document.pageFlow(page -> {
+ page.addParagraph(p -> p.text("Row verticalAlign")
+ .textStyle(DocumentTextStyle.DEFAULT.withSize(18).withColor(INK)));
+ page.addParagraph(p -> p.text("the short label is seated against the band set by the price")
+ .textStyle(DocumentTextStyle.DEFAULT.withSize(9).withColor(MUTED))
+ .padding(DocumentInsets.bottom(10)));
+
+ priceRow(page, RowVerticalAlign.TOP);
+ priceRow(page, RowVerticalAlign.CENTER);
+ priceRow(page, RowVerticalAlign.BOTTOM);
+ });
+
+ document.buildPdf();
+ }
+
+ return pdfFile;
+ }
+
+ private static void priceRow(PageFlowBuilder page, RowVerticalAlign align) {
+ page.addRow(r -> r.verticalAlign(align)
+ .fillColor(BAND)
+ .cornerRadius(8)
+ .padding(DocumentInsets.of(12))
+ .gap(10)
+ .addParagraph(p -> p.text("$49")
+ .textStyle(DocumentTextStyle.DEFAULT.withSize(30).withColor(INK)))
+ .addParagraph(p -> p.text("/ month · verticalAlign(" + align + ")")
+ .textStyle(DocumentTextStyle.DEFAULT.withSize(11).withColor(MUTED))));
+ page.addSpacer(s -> s.height(10));
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("Generated: " + generate());
+ }
+}
diff --git a/src/main/java/com/demcha/compose/document/dsl/RowBuilder.java b/src/main/java/com/demcha/compose/document/dsl/RowBuilder.java
index cc699b753..d590c8138 100644
--- a/src/main/java/com/demcha/compose/document/dsl/RowBuilder.java
+++ b/src/main/java/com/demcha/compose/document/dsl/RowBuilder.java
@@ -42,6 +42,7 @@ public final class RowBuilder {
private DocumentStroke stroke;
private DocumentCornerRadius cornerRadius = DocumentCornerRadius.ZERO;
private DocumentBorders borders = DocumentBorders.NONE;
+ private RowVerticalAlign verticalAlign = RowVerticalAlign.TOP;
/**
* Creates a row builder.
@@ -179,6 +180,21 @@ public RowBuilder borders(DocumentBorders borders) {
return this;
}
+ /**
+ * Sets the cross-axis (vertical) placement of the row's children within the
+ * row band, whose height is that of the tallest child. Shorter children align
+ * to the top, middle, or bottom without manual coordinates.
+ *
+ * @param verticalAlign cross-axis alignment; {@code null} resets to
+ * {@link RowVerticalAlign#TOP}
+ * @return this builder
+ * @since 1.9.0
+ */
+ public RowBuilder verticalAlign(RowVerticalAlign verticalAlign) {
+ this.verticalAlign = verticalAlign == null ? RowVerticalAlign.TOP : verticalAlign;
+ return this;
+ }
+
/**
* Replaces the per-child weights used to distribute the row's inner width.
*
@@ -473,7 +489,8 @@ public RowNode build() {
stroke,
cornerRadius,
borders,
- List.copyOf(columns));
+ List.copyOf(columns),
+ verticalAlign);
}
private void validate() {
diff --git a/src/main/java/com/demcha/compose/document/layout/LayoutCompiler.java b/src/main/java/com/demcha/compose/document/layout/LayoutCompiler.java
index 69b7ab6ff..d0511124c 100644
--- a/src/main/java/com/demcha/compose/document/layout/LayoutCompiler.java
+++ b/src/main/java/com/demcha/compose/document/layout/LayoutCompiler.java
@@ -6,6 +6,7 @@
import com.demcha.compose.document.node.LayerStackNode;
import com.demcha.compose.document.node.PageBreakNode;
import com.demcha.compose.document.node.RowNode;
+import com.demcha.compose.document.node.RowVerticalAlign;
import com.demcha.compose.document.style.DocumentBleed;
import com.demcha.compose.document.style.DocumentEdge;
import com.demcha.compose.document.style.DocumentRowColumn;
@@ -518,6 +519,9 @@ private void compileHorizontalRow(PreparedNode