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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ Entries land here as they merge.
zoom level instead of being tessellated into straight pieces. Atomic
pagination, deterministic layout snapshots, fill (non-zero winding rule)
and/or stroke. This is the leaf vehicle for smooth chart lines, decorative
design shapes, and future SVG path import.
design shapes, and future SVG path import. DSL:
`addPath(p -> p.moveTo(...).curveTo(...).closePath().fillColor(...))` on
every flow builder authors design shapes directly.
- **Inline sparklines** (`@since 1.8.0`). `RichText.sparkline(w, h, color,
values...)` draws a filled mini-area silhouette on the text baseline, and
`sparklineLine(w, h, thickness, color, values...)` a constant-thickness line
Expand Down
Binary file added assets/readme/examples/vector-path.pdf
Binary file not shown.
22 changes: 22 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ are with the canonical DSL, then jump to its detailed section below.
| Example | What it shows | Preview · Source |
|---|---|---|
| [Shape containers](#shape-containers) | Circles, ellipses, rounded cards with `ClipPolicy.CLIP_PATH` | [PDF](../assets/readme/examples/shape-container.pdf) · [Source](src/main/java/com/demcha/examples/features/shapes/ShapeContainerExample.java) |
| [Vector paths (Bézier)](#vector-paths-bézier) | `addPath(...)` — design shapes with native cubic curves: waves, blobs, ribbons; zero tessellation | [PDF](../assets/readme/examples/vector-path.pdf) · [Source](src/main/java/com/demcha/examples/features/shapes/VectorPathExample.java) |
| [Advanced tables](#advanced-tables) | Row span, zebra rows, totals, repeating header on page break | [PDF](../assets/readme/examples/table-advanced.pdf) · [Source](src/main/java/com/demcha/examples/features/tables/TableAdvancedExample.java) |
| [Barcodes](#barcodes) | QR, Code 128, Code 39, EAN-13, EAN-8, branded QR with theme colours | [PDF](../assets/readme/examples/barcode-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/barcodes/BarcodeShowcaseExample.java) |
| [Charts](#charts) | Native vector bar, line, and pie/donut charts — data/spec/style layers, axis & grid toggles, point markers, value labels, legend | [PDF](../assets/readme/examples/chart-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/charts/ChartShowcaseExample.java) |
Expand Down Expand Up @@ -354,6 +355,27 @@ layers both gain `int zIndex` (default `0`).
[📄 View PDF](../assets/readme/examples/transforms.pdf) ·
[📜 Full source](src/main/java/com/demcha/examples/features/transforms/TransformsExample.java)

### Vector paths (Bézier)

Free-form design shapes with native cubic Bézier curves through
`addPath(...)`: stroked waves, filled blobs, and mixed line/curve
ribbons in one closed subpath. Curves render as native PDF `curveTo`
operators — perfectly smooth at any zoom, no tessellation. Coordinates
are normalized to the shape's box (`(0,0)` bottom-left, `y` up) and
control points may overshoot it.

```java
flow.addPath(path -> path
.size(320, 60)
.moveTo(0.0, 0.5)
.curveTo(0.25, 1.0, 0.25, 0.0, 0.5, 0.5)
.curveTo(0.75, 1.0, 0.75, 0.0, 1.0, 0.5)
.stroke(DocumentStroke.of(accent, 2.4)));
```

[📄 View PDF](../assets/readme/examples/vector-path.pdf) ·
[📜 Full source](src/main/java/com/demcha/examples/features/shapes/VectorPathExample.java)

### Advanced tables

`DocumentTableCell.rowSpan(int)` mirrors `colSpan(int)`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.demcha.examples.features.chrome.PdfChromeExample;
import com.demcha.examples.features.lists.NestedListExample;
import com.demcha.examples.features.shapes.ShapeContainerExample;
import com.demcha.examples.features.shapes.VectorPathExample;
import com.demcha.examples.features.snapshots.LayoutSnapshotRegressionExample;
import com.demcha.examples.features.streaming.HttpStreamingExample;
import com.demcha.examples.features.tables.ComposedTableCellExample;
Expand Down Expand Up @@ -128,6 +129,7 @@ public static void main(String[] args) throws Exception {

// v1.5 visual primitives
System.out.println("Generated: " + ShapeContainerExample.generate());
System.out.println("Generated: " + VectorPathExample.generate());
System.out.println("Generated: " + TransformsExample.generate());
System.out.println("Generated: " + TableAdvancedExample.generate());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.demcha.examples.features.shapes;

import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentSession;
import com.demcha.compose.document.style.DocumentColor;
import com.demcha.compose.document.style.DocumentInsets;
import com.demcha.compose.document.style.DocumentStroke;
import com.demcha.examples.support.ExampleOutputPaths;

import java.nio.file.Path;

/**
* Runnable showcase for the v1.8 vector-path primitive: free-form design
* shapes with native cubic Bézier curves, authored through the
* {@code addPath(...)} DSL.
*
* <pre>{@code
* flow.addPath(path -> path
* .size(320, 60)
* .moveTo(0.0, 0.5)
* .curveTo(0.25, 1.0, 0.25, 0.0, 0.5, 0.5)
* .curveTo(0.75, 1.0, 0.75, 0.0, 1.0, 0.5)
* .stroke(DocumentStroke.of(accent, 2.4)));
* }</pre>
*
* <p>Curves render as native PDF {@code curveTo} operators — perfectly
* smooth at any zoom, no tessellation. Coordinates are normalized to the
* shape's box ({@code (0,0)} bottom-left, {@code y} up) and Bézier control
* points may overshoot it, which is what gives the blob its bulge.</p>
*
* @author Artem Demchyshyn
*/
public final class VectorPathExample {

private static final DocumentColor INK = DocumentColor.rgb(20, 60, 120);
private static final DocumentColor SAND = DocumentColor.rgb(235, 205, 160);
private static final DocumentColor SAND_EDGE = DocumentColor.rgb(140, 90, 30);
private static final DocumentColor MOSS = DocumentColor.rgb(208, 226, 213);
private static final DocumentColor MOSS_EDGE = DocumentColor.rgb(60, 110, 80);

private VectorPathExample() {
}

/**
* Renders the three-shape sheet: a stroked Bézier wave, a filled blob
* with overshooting control points, and a mixed line/curve ribbon.
*
* @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/shapes", "vector-path.pdf");

try (DocumentSession document = GraphCompose.document(pdfFile)
.pageSize(420, 420)
.margin(DocumentInsets.of(28))
.create()) {
document.pageFlow(page -> page
.addParagraph("Stroked Bézier wave — two cubic spans, zero tessellation")
.addPath(path -> path
.name("Wave")
.size(364, 64)
.moveTo(0.0, 0.5)
.curveTo(0.25, 1.1, 0.25, -0.1, 0.5, 0.5)
.curveTo(0.75, 1.1, 0.75, -0.1, 1.0, 0.5)
.stroke(DocumentStroke.of(INK, 2.4))
.margin(DocumentInsets.bottom(16)))
.addParagraph("Filled blob — closed curves, control points overshoot the box")
.addPath(path -> path
.name("Blob")
.size(120, 104)
.moveTo(0.5, 1.0)
.curveTo(1.12, 0.94, 0.96, 0.08, 0.5, 0.0)
.curveTo(0.04, 0.08, -0.12, 0.94, 0.5, 1.0)
.closePath()
.fillColor(SAND)
.stroke(DocumentStroke.of(SAND_EDGE, 1.4))
.margin(DocumentInsets.bottom(16)))
.addParagraph("Mixed ribbon — lines and curves in one closed, filled subpath")
.addPath(path -> path
.name("Ribbon")
.size(364, 70)
.moveTo(0.0, 0.85)
.lineTo(0.18, 0.85)
.curveTo(0.42, 1.05, 0.58, 0.55, 0.82, 0.75)
.lineTo(1.0, 0.75)
.lineTo(1.0, 0.15)
.curveTo(0.6, -0.05, 0.4, 0.45, 0.0, 0.25)
.closePath()
.fillColor(MOSS)
.stroke(DocumentStroke.of(MOSS_EDGE, 1.2))));

document.buildPdf();
}

return pdfFile;
}

public static void main(String[] args) throws Exception {
System.out.println("Generated: " + generate());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ record Entry(String title, String description, List<String> tags, String codeUrl
feature("tables", "composed-table-cell-showcase", "Composed Table Cells", "DocumentTableCell.node(DocumentNode) — paragraphs, lists, sub-tables inside cells with two-pass measurement.", "tables", "v1.6");
feature("canvas", "canvas-layer-showcase", "Canvas Layer (free-canvas)", "CanvasLayerNode — pixel-precise (x,y) placement of children inside a fixed bounding box.", "canvas", "v1.6", "absolute");
feature("shapes", "shape-container", "Shape-as-Container", "Rounded rect, ellipse, circle containers with ClipPolicy and layered children.", "shapes", "clip");
feature("shapes", "vector-path", "Vector Paths (Bézier)", "addPath(...) — free-form design shapes with native cubic Bézier curves: stroked waves, filled blobs, mixed line/curve ribbons. No tessellation.", "shapes", "bezier", "v1.8");
feature("transforms", "transforms", "Layers + Transforms", "rotate / scale on every leaf builder + LayerStack with explicit z-index.", "transforms", "layers");
feature("text", "rich-text-showcase", "Rich Text", "Inline runs with bold / italic / colour / link options, markdown parsing.", "text", "rich");
feature("text", "section-presets", "Section Presets", "Pre-baked section bands, accent strips, soft panels for templates.", "text", "sections");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,18 @@ public T addEllipse(Consumer<EllipseBuilder> spec) {
return add(BuilderSupport.configure(new EllipseBuilder(), spec).build());
}

/**
* Adds a free-form vector path configured through a nested builder —
* design shapes with native cubic Bézier curves.
*
* @param spec path builder callback
* @return this builder
* @since 1.8.0
*/
public T addPath(Consumer<PathBuilder> spec) {
return add(BuilderSupport.configure(new PathBuilder(), spec).build());
}

/**
* Adds a filled circle ellipse — shortcut for
* {@code addEllipse(e -> e.circle(diameter).fillColor(fillColor))}.
Expand Down
Loading