diff --git a/CHANGELOG.md b/CHANGELOG.md index ee4324903..67a250795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,9 +119,11 @@ Entries land here as they merge. lands in the PDF outline (the bookmarks panel works as a clickable index), a code panel shows the exact API call, and the live result renders right under it — rich text, sparklines, nested lists, timelines, tables, every - chart kind, gradients, translucency, polygons, clipped containers, canvas, - transforms, barcodes, and the document's own chrome. Blocks use - `keepTogether()`, so a snippet is never orphaned from its result. + chart kind, images (COVER vs CONTAIN fit), gradients, translucency, + polygons, shape basics (dividers, ellipses, soft cards), clipped + containers, canvas, transforms, barcodes, and the document's own chrome — + 19 blocks across 6 pages. Blocks use `keepTogether()`, so a snippet is + never orphaned from its result. - **Recipe coverage is complete.** Nine new cookbook pages close every gap the recipe index tracked: rich text, lists, timelines, barcodes, images, PDF chrome (metadata / watermark / running header-footer / protection / diff --git a/assets/readme/examples/feature-catalog.pdf b/assets/readme/examples/feature-catalog.pdf index 276b4b149..c7c7bad70 100644 Binary files a/assets/readme/examples/feature-catalog.pdf and b/assets/readme/examples/feature-catalog.pdf differ diff --git a/assets/readme/feature-catalog.png b/assets/readme/feature-catalog.png index 9b5590b41..35ac71127 100644 Binary files a/assets/readme/feature-catalog.png and b/assets/readme/feature-catalog.png differ diff --git a/examples/src/main/java/com/demcha/examples/flagships/FeatureCatalogExample.java b/examples/src/main/java/com/demcha/examples/flagships/FeatureCatalogExample.java index 986935d8c..bd5a91b65 100644 --- a/examples/src/main/java/com/demcha/examples/flagships/FeatureCatalogExample.java +++ b/examples/src/main/java/com/demcha/examples/flagships/FeatureCatalogExample.java @@ -98,7 +98,6 @@ public static Path generate() throws Exception { .centerText("Page {page} of {pages}") .fontSize(8f).textColor(MUTED) .build()); - var flow = document.pageFlow().name("FeatureCatalog").spacing(12); flow.addSection("Intro", s -> s @@ -235,6 +234,33 @@ public static Path generate() throws Exception { .valueLabels(ValueLabelMode.OUTSIDE) .size(ChartSize.fixedHeight(140)).build())); + feature(flow, "Images — classpath bytes, explicit size, fit modes", """ + DocumentImageData photo = DocumentImageData.fromBytes( + getClass().getResourceAsStream("/engine-hero.png").readAllBytes()); + section.addImage(i -> i.source(photo).size(150, 84) + .fitMode(DocumentImageFitMode.COVER)) // vs CONTAIN on the right""", + demo -> demo.addRow(r -> r.spacing(14).weights(1, 1) + .addSection("Cover", a -> a.addImage(i -> i + .source(catalogImage()).size(150, 84) + .fitMode(com.demcha.compose.document.image.DocumentImageFitMode.COVER))) + .addSection("Contain", b -> b.addImage(i -> i + .source(catalogImage()).size(150, 84) + .fitMode(com.demcha.compose.document.image.DocumentImageFitMode.CONTAIN))))); + + feature(flow, "Shapes — dividers, ellipses, soft cards", """ + section.addDivider(d -> d.width(420).thickness(2).color(GOLD)); + section.addEllipse(96, 44, TEAL); + section.addShape(s -> s.size(150, 40).cornerRadius(10) + .fillColor(rgb(248, 246, 240)).stroke(DocumentStroke.of(GOLD, 0.8)))""", + demo -> demo + .addDivider(d -> d.width(420).thickness(2).color(GOLD)) + .addRow(r -> r.spacing(14).weights(1, 1) + .addSection("El", a -> a.addEllipse(96, 44, TEAL)) + .addSection("Card", b -> b.addShape(s -> s + .size(150, 40).cornerRadius(10) + .fillColor(DocumentColor.rgb(248, 246, 240)) + .stroke(DocumentStroke.of(GOLD, 0.8)))))); + feature(flow, "Gradient fills — native linear and radial shadings", """ section.addShape(s -> s.size(420, 40).cornerRadius(8) .fill(DocumentPaint.linear(TEAL, GOLD))); // 0 deg = left -> right @@ -325,9 +351,10 @@ public static Path generate() throws Exception { document.footer(…centerText("Page {page} of {pages}")…); paragraph.bookmark(new DocumentBookmarkOptions("Feature catalog", 0))""", demo -> demo.addParagraph(p -> p - .text("Look up: the running header and the page-X-of-Y footer on every " - + "page of this catalog come from the calls above, and every block " - + "heading is a clickable entry in the PDF bookmarks panel.") + .text("Look around: the running header and the page-X-of-Y footer on every " + + "page come from the calls above, and every block heading is a " + + "clickable entry in the PDF bookmarks panel. A document.watermark(…) " + + "call stamps text or an image behind or above the content the same way.") .textStyle(THEME.text().body()) .lineSpacing(1.35) .margin(DocumentInsets.zero()))); @@ -374,6 +401,18 @@ private static void feature(com.demcha.compose.document.dsl.PageFlowBuilder flow }); } + /** Classpath demo photo shared by the image fit-mode pair. */ + private static com.demcha.compose.document.image.DocumentImageData catalogImage() { + try (java.io.InputStream stream = java.util.Objects.requireNonNull( + FeatureCatalogExample.class.getResourceAsStream("/engine-hero.png"), + "engine-hero.png missing from examples/src/main/resources/")) { + return com.demcha.compose.document.image.DocumentImageData.fromBytes( + stream.readAllBytes()); + } catch (java.io.IOException e) { + throw new IllegalStateException("failed to load catalog demo image", e); + } + } + private static com.demcha.compose.document.node.DocumentNode badge(String text) { return new com.demcha.compose.document.dsl.ShapeContainerBuilder() .roundedRect(64, 22, 11)