Skip to content

Commit 776847d

Browse files
committed
feat(examples): add EngineShowcase flagship + replace README hero image (Phase B + C)
Wires up Phases B and C of the pre-v1.6.6 presentation polish plan. Phase B - new flagship example: examples/src/main/java/com/demcha/examples/flagships/EngineShowcase.java (~330 LOC) renders a single-page cinematic brand promo - not a CV, not a business document - that shows the visual register the engine can hit when an author cares about presentation as much as data. Navy + electric-orange palette, A4 portrait. Composition: top marquee band (brand + version), thin orange rule, 516x280 rounded clip frame holding pre-rendered hero artwork (semantic-graph -> polished-PDFs visual metaphor) via ShapeContainerNode + ClipPolicy.CLIP_PATH, magazine-headline lockup ("Documents as code. / Cinematic by default."), three KPI cards (Templates v2 / 1,033 tests / v1.6.6 Maven Central) via softPanel + stroked card rings, three-column capability grid (Semantic DSL / Deterministic Layout / Cinematic Themes) with accentLeft rules, footer brand stripe. Hero image (examples/src/main/resources/engine-hero.png, 1.8 MB) loaded over the classpath so the example needs no filesystem assumptions. Added to examples gallery README under "Start here" tier. Phase C - persistent rasteriser + hero image swap: examples/src/main/java/com/demcha/examples/support/PdfPageRasterizer.java (60 LOC) is a tiny PDFBox-based CLI that converts one page of a PDF to PNG at a chosen DPI. Lets the repo regenerate hero/social-card images without depending on Ghostscript, ImageMagick, or pdftoppm - none of which are reliably available on Windows Git Bash. assets/readme/repository_showcase_render.png replaced with EngineShowcase page 1 (2.1 MB, 1653x2338 px at 200 DPI). Iteration story: first attempt was a meta-narrative CV via the MintEditorial preset, but visual review (read PNG inline, three sidebar labels wrapped mid-letter) flagged it as the wrong register for the hero asset - a hero CV competes with the actual CV gallery rather than complementing it. Pivoted to a cinematic engine-capability promo using a pre-rendered brand artwork, which lands as the engine's brand register rather than another template render. Doc + version guards green (CanonicalSurfaceGuardTest + DocumentationExamplesTest + DocumentationCoverageTest + VersionConsistencyGuardTest = 30/0/0). CHANGELOG entry rewritten under v1.6.6 Planned ### Documentation subsection.
1 parent 7cfa21b commit 776847d

6 files changed

Lines changed: 447 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,31 @@ to ship the same artefact.
202202

203203
### Documentation
204204

205+
- **New flagship example: `EngineShowcase`** + **regenerated
206+
`assets/readme/repository_showcase_render.png` hero image** ahead of
207+
the Maven Central debut. A presentation audit before v1.6.6 flagged
208+
that the existing hero PDF was a dated single-page render and the
209+
GitHub Pages showcase had 20 broken asset paths (CV v2 migration
210+
added `-v2` suffixes that `docs/index.html` never picked up). Fixed
211+
in three commits: (a) `docs/index.html` path repair so every CV /
212+
cover-letter preview resolves; (b) new flagship
213+
`examples/.../flagships/EngineShowcase.java` renders a single-page
214+
cinematic brand promo — a navy + electric-orange composition with a
215+
rounded clip-frame hero (semantic-graph → polished-PDFs visual
216+
metaphor), a magazine-headline lockup ("Documents as code. /
217+
Cinematic by default."), three KPI cards (Templates v2 · 1,033
218+
tests · v1.6.6 Maven Central), a three-column capability grid
219+
(Semantic DSL · Deterministic Layout · Cinematic Themes), and a
220+
footer brand stripe — exercising `ShapeContainerNode` +
221+
`ClipPolicy.CLIP_PATH` for the hero frame, classpath-loaded image
222+
embedding (`examples/src/main/resources/engine-hero.png`),
223+
`softPanel(...)` + `accentLeft(...)` decorators on V2 sections, and
224+
mixed serif/sans typography; (c) page 1 rasterised to
225+
`assets/readme/repository_showcase_render.png` via the new persistent
226+
helper `com.demcha.examples.support.PdfPageRasterizer` (PDFBox-based,
227+
no external Ghostscript / ImageMagick dependency). The hero now
228+
reads as the engine's brand register rather than a Lorem-ipsum
229+
template render.
205230
- **`docs/architecture/package-map.md` updated** alongside H2. A new
206231
intro paragraph documents the stability-marker convention (Stable
207232
default; engine packages are package-level `@Internal`; individual
1.63 MB
Loading

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ are with the canonical DSL, then jump to its detailed section below.
5353
| [Invoice — cinematic V2](#invoice-cinematic-v2) | `InvoiceTemplateV2 + BusinessTheme.modern()` — the recommended invoice path | [PDF](../assets/readme/examples/invoice-cinematic.pdf) · [Source](src/main/java/com/demcha/examples/templates/invoice/InvoiceCinematicFileExample.java) |
5454
| [Cover Letter](#cover-letter) | One-page `BusinessTheme.modern()` cover letter with section presets | [PDF](../assets/readme/examples/cover-letter.pdf) · [Source](src/main/java/com/demcha/examples/templates/coverletter/CoverLetterFileExample.java) |
5555
| [Module-first Profile](#module-first-profile) | Authoring directly against `DocumentSession.module(...).paragraph(...)` — DSL-direct, no template | [PDF](../assets/readme/examples/module-first-profile.pdf) · [Source](src/main/java/com/demcha/examples/flagships/ModuleFirstFileExample.java) |
56+
| **Engine Showcase** | Single-page cinematic brand promo — semantic-graph → polished-PDFs visual metaphor with rounded clip frame, magazine headline lockup, KPI cards, capability columns; source of the README hero image | [Source](src/main/java/com/demcha/examples/flagships/EngineShowcase.java) |
5657

5758
### 🧱 Core DSL
5859

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
package com.demcha.examples.flagships;
2+
3+
import com.demcha.compose.GraphCompose;
4+
import com.demcha.compose.document.api.DocumentPageSize;
5+
import com.demcha.compose.document.api.DocumentSession;
6+
import com.demcha.compose.document.image.DocumentImageData;
7+
import com.demcha.compose.document.image.DocumentImageFitMode;
8+
import com.demcha.compose.document.node.TextAlign;
9+
import com.demcha.compose.document.style.ClipPolicy;
10+
import com.demcha.compose.document.style.DocumentColor;
11+
import com.demcha.compose.document.style.DocumentCornerRadius;
12+
import com.demcha.compose.document.style.DocumentInsets;
13+
import com.demcha.compose.document.style.DocumentStroke;
14+
import com.demcha.compose.document.style.DocumentTextStyle;
15+
import com.demcha.compose.font.FontName;
16+
import com.demcha.examples.support.ExampleOutputPaths;
17+
18+
import java.io.InputStream;
19+
import java.nio.file.Path;
20+
import java.util.Objects;
21+
22+
/**
23+
* Single-page cinematic engine showcase used as the
24+
* {@code assets/readme/repository_showcase_render.png} source. NOT a
25+
* business document, NOT a CV — a brand promo page that demonstrates
26+
* the visual register the engine can hit when an author cares about
27+
* presentation as much as data:
28+
*
29+
* <ul>
30+
* <li><b>Top navy band</b> — full-width branded marquee (canonical
31+
* {@code pageBackgrounds} band fill).</li>
32+
* <li><b>Cinematic hero image</b> — pre-rendered brand artwork
33+
* (semantic graph → polished PDFs visual metaphor) mounted in a
34+
* rounded clip frame ({@code ShapeContainerNode} +
35+
* {@code ClipPolicy.CLIP_PATH}).</li>
36+
* <li><b>Magazine-headline typography</b> — three-line bold lockup
37+
* set in the canonical serif heading style.</li>
38+
* <li><b>Three KPI cards</b> — soft-panel sections with accent rules
39+
* (V2 templates count · test count · Maven Central debut).</li>
40+
* <li><b>Three-column capability grid</b> — labelled rich-text
41+
* paragraphs showcasing inline style mixing.</li>
42+
* <li><b>Footer brand stripe</b> — accent rule + brand links.</li>
43+
* </ul>
44+
*
45+
* <p>The hero image lives in {@code examples/src/main/resources/engine-hero.png}
46+
* and is loaded over the classpath so the example runs without any
47+
* filesystem path assumptions.</p>
48+
*
49+
* <p>Output:
50+
* {@code examples/target/generated-pdfs/flagships/engine-showcase.pdf}
51+
* — page 1 is rasterised by {@link com.demcha.examples.support.PdfPageRasterizer}
52+
* into {@code assets/readme/repository_showcase_render.png}.</p>
53+
*
54+
* @author Artem Demchyshyn
55+
* @since 1.6.6
56+
*/
57+
public final class EngineShowcase {
58+
59+
// ── Theme palette — cinematic navy + electric accent ─────────
60+
private static final DocumentColor PAPER = DocumentColor.rgb(248, 247, 242);
61+
private static final DocumentColor NAVY = DocumentColor.rgb(18, 26, 56);
62+
private static final DocumentColor NAVY_DARK = DocumentColor.rgb(10, 14, 36);
63+
private static final DocumentColor ACCENT = DocumentColor.rgb(255, 138, 36);
64+
private static final DocumentColor ACCENT_SOFT = DocumentColor.rgb(255, 198, 138);
65+
private static final DocumentColor INK = DocumentColor.rgb(20, 24, 38);
66+
private static final DocumentColor MUTED = DocumentColor.rgb(110, 116, 132);
67+
private static final DocumentColor RULE = DocumentColor.rgb(220, 218, 210);
68+
private static final DocumentColor CARD_RING = DocumentColor.rgb(232, 226, 212);
69+
private static final DocumentColor PANEL_TINT = DocumentColor.rgb(232, 234, 246);
70+
private static final DocumentColor PANEL_RING = DocumentColor.rgb(208, 214, 234);
71+
72+
private EngineShowcase() {
73+
}
74+
75+
public static Path generate() throws Exception {
76+
Path output = ExampleOutputPaths.prepare("flagships", "engine-showcase.pdf");
77+
DocumentImageData hero = loadHeroImage();
78+
79+
try (DocumentSession document = GraphCompose.document(output)
80+
.pageSize(DocumentPageSize.A4)
81+
.pageBackground(PAPER)
82+
.margin(36, 38, 32, 38)
83+
.create()) {
84+
85+
document.pageFlow()
86+
.name("EngineShowcase")
87+
.spacing(14)
88+
89+
// ── Marquee band: brand + version ──────────────
90+
.addRow("Marquee", row -> row
91+
.spacing(0)
92+
.weights(1, 1)
93+
.addSection("MarqueeLeft", section -> section
94+
.addParagraph(p -> p
95+
.text("GRAPHCOMPOSE")
96+
.textStyle(bandLeft())
97+
.margin(DocumentInsets.zero())))
98+
.addSection("MarqueeRight", section -> section
99+
.addParagraph(p -> p
100+
.text("v1.6 · MAVEN CENTRAL")
101+
.textStyle(bandRight())
102+
.align(TextAlign.RIGHT)
103+
.margin(DocumentInsets.zero()))))
104+
105+
// ── Thin orange rule ──────────────────────────
106+
.addShape(s -> s.size(516, 1.2).fillColor(ACCENT)
107+
.margin(DocumentInsets.zero()))
108+
109+
// ── Hero frame: nested rounded shapes form a thick
110+
// orange border around the brand artwork (avoids the
111+
// stroke-on-rounded-path corner artifact at thick
112+
// stroke widths). Outer = ACCENT fill, inner = NAVY
113+
// fill clipped to round, image clipped inside that.
114+
.addContainer(frame -> frame
115+
.name("HeroFrame")
116+
.roundedRect(519, 287, 16)
117+
.fillColor(ACCENT)
118+
.center(new com.demcha.compose.document.dsl.ShapeContainerBuilder()
119+
.name("HeroInner")
120+
.roundedRect(513, 281, 13)
121+
.fillColor(NAVY_DARK)
122+
.clipPolicy(ClipPolicy.CLIP_PATH)
123+
.center(new com.demcha.compose.document.dsl.ImageBuilder()
124+
.name("HeroImage")
125+
.source(hero)
126+
.size(509, 277)
127+
.fitMode(DocumentImageFitMode.COVER)
128+
.build())
129+
.build()))
130+
131+
// ── Magazine-headline lockup ──────────────────
132+
.addSection("Lockup", section -> section
133+
.padding(new DocumentInsets(8, 4, 0, 4))
134+
.spacing(2)
135+
.addParagraph(p -> p
136+
.text("One engine.")
137+
.textStyle(headline())
138+
.margin(DocumentInsets.zero()))
139+
.addParagraph(p -> p
140+
.text("Every PDF you ship.")
141+
.textStyle(headlineAccent())
142+
.margin(DocumentInsets.zero()))
143+
.addParagraph(p -> p
144+
.text("A declarative Java engine for production PDFs. Semantic graph compiles to deterministic layout, paginates predictably, and renders through PDFBox — without leaking PDFBox into your app.")
145+
.textStyle(tagline())
146+
.lineSpacing(1.35)
147+
.margin(new DocumentInsets(6, 0, 0, 0))))
148+
149+
// ── Three KPI cards — engine capability counts ─
150+
.addRow("KpiRow", row -> row
151+
.spacing(12)
152+
.weights(1, 1, 1)
153+
.addSection("KpiPresets", section -> kpiCard(section,
154+
"31", "V2 Presets",
155+
"Layered CV + cover-letter preset architecture — data · theme · components · widgets, swap one without rewriting the others."))
156+
.addSection("KpiTests", section -> kpiCard(section,
157+
"1,033", "Tests · 0 failures",
158+
"Snapshot baselines, japicmp binary-compat gate, parallel-session stress harness, JMH benchmark suite."))
159+
.addSection("KpiNodes", section -> kpiCard(section,
160+
"17", "Semantic Primitives",
161+
"Modules, sections, paragraphs, lists, tables, rows, shapes, images — every node is themable and snapshot-tested.")))
162+
163+
// ── Capability columns ────────────────────────
164+
.addRow("Capabilities", row -> row
165+
.spacing(14)
166+
.weights(1, 1, 1)
167+
.addSection("CapDsl", section -> capabilityColumn(section,
168+
"DECLARATIVE BY DESIGN",
169+
"Compose by intent — modules, sections, rows. Zero PDFBox imports in your application code."))
170+
.addSection("CapDeterm", section -> capabilityColumn(section,
171+
"DETERMINISTIC RENDER",
172+
"Identical input, identical PDF — byte-comparable. Layout snapshots, JMH-benchmarked hot paths."))
173+
.addSection("CapTheme", section -> capabilityColumn(section,
174+
"CINEMATIC THEMING",
175+
"BusinessTheme · CvTheme palettes, component-level tokens, v2 layered preset architecture.")))
176+
177+
// ── Footer brand stripe ───────────────────────
178+
.addShape(s -> s.size(516, 0.6).fillColor(RULE)
179+
.margin(new DocumentInsets(8, 0, 0, 0)))
180+
.addRow("Footer", row -> row
181+
.spacing(0)
182+
.weights(2, 1)
183+
.addSection("FooterLeft", section -> section
184+
.addParagraph(p -> p
185+
.text("graphcompose.dev · github.com/DemchaAV/GraphCompose")
186+
.textStyle(footer())
187+
.margin(DocumentInsets.zero())))
188+
.addSection("FooterRight", section -> section
189+
.addParagraph(p -> p
190+
.text("Java · PDFBox · MIT")
191+
.textStyle(footer())
192+
.align(TextAlign.RIGHT)
193+
.margin(DocumentInsets.zero()))))
194+
195+
.build();
196+
197+
document.buildPdf();
198+
}
199+
return output;
200+
}
201+
202+
private static DocumentImageData loadHeroImage() throws Exception {
203+
try (InputStream is = Objects.requireNonNull(
204+
EngineShowcase.class.getResourceAsStream("/engine-hero.png"),
205+
"engine-hero.png missing from examples/src/main/resources/")) {
206+
return DocumentImageData.fromBytes(is.readAllBytes());
207+
}
208+
}
209+
210+
// ── Helpers ──────────────────────────────────────────────────
211+
212+
private static void kpiCard(com.demcha.compose.document.dsl.SectionBuilder section,
213+
String value, String label, String detail) {
214+
section
215+
.softPanel(DocumentColor.WHITE, 8, 12)
216+
.stroke(DocumentStroke.of(CARD_RING, 0.5))
217+
.spacing(2)
218+
.addParagraph(p -> p
219+
.text(value)
220+
.textStyle(kpiValue())
221+
.margin(DocumentInsets.zero()))
222+
.addParagraph(p -> p
223+
.text(label)
224+
.textStyle(kpiLabel())
225+
.margin(new DocumentInsets(0, 0, 0, 0)))
226+
.addParagraph(p -> p
227+
.text(detail)
228+
.textStyle(kpiDetail())
229+
.lineSpacing(1.25)
230+
.margin(new DocumentInsets(4, 0, 0, 0)));
231+
}
232+
233+
private static void capabilityColumn(com.demcha.compose.document.dsl.SectionBuilder section,
234+
String label, String prose) {
235+
section
236+
// Tab-on-left callout: round only the right corners so
237+
// the accent rule on the left sits flush against a
238+
// straight edge — square cap on the rule matches a
239+
// square left side without crossing a rounded curve.
240+
.softPanel(PANEL_TINT, DocumentCornerRadius.right(6), 0)
241+
.accentLeft(ACCENT, 3.0f)
242+
.padding(new DocumentInsets(12, 12, 12, 16))
243+
.spacing(3)
244+
.addParagraph(p -> p
245+
.text(label)
246+
.textStyle(capLabel())
247+
.margin(DocumentInsets.zero()))
248+
.addParagraph(p -> p
249+
.text(prose)
250+
.textStyle(capProse())
251+
.lineSpacing(1.3)
252+
.margin(new DocumentInsets(2, 0, 0, 0)));
253+
}
254+
255+
// ── Text styles ──────────────────────────────────────────────
256+
257+
private static DocumentTextStyle bandLeft() {
258+
return DocumentTextStyle.builder()
259+
.fontName(FontName.HELVETICA_BOLD)
260+
.size(10)
261+
.color(NAVY)
262+
.build();
263+
}
264+
265+
private static DocumentTextStyle bandRight() {
266+
return DocumentTextStyle.builder()
267+
.fontName(FontName.HELVETICA)
268+
.size(9)
269+
.color(MUTED)
270+
.build();
271+
}
272+
273+
private static DocumentTextStyle headline() {
274+
return DocumentTextStyle.builder()
275+
.fontName(FontName.TIMES_BOLD)
276+
.size(38)
277+
.color(INK)
278+
.build();
279+
}
280+
281+
private static DocumentTextStyle headlineAccent() {
282+
return DocumentTextStyle.builder()
283+
.fontName(FontName.TIMES_BOLD_ITALIC)
284+
.size(38)
285+
.color(ACCENT)
286+
.build();
287+
}
288+
289+
private static DocumentTextStyle tagline() {
290+
return DocumentTextStyle.builder()
291+
.fontName(FontName.HELVETICA)
292+
.size(11)
293+
.color(INK)
294+
.build();
295+
}
296+
297+
private static DocumentTextStyle kpiValue() {
298+
return DocumentTextStyle.builder()
299+
.fontName(FontName.TIMES_BOLD)
300+
.size(26)
301+
.color(NAVY)
302+
.build();
303+
}
304+
305+
private static DocumentTextStyle kpiLabel() {
306+
return DocumentTextStyle.builder()
307+
.fontName(FontName.HELVETICA_BOLD)
308+
.size(9)
309+
.color(ACCENT)
310+
.build();
311+
}
312+
313+
private static DocumentTextStyle kpiDetail() {
314+
return DocumentTextStyle.builder()
315+
.fontName(FontName.HELVETICA)
316+
.size(8)
317+
.color(MUTED)
318+
.build();
319+
}
320+
321+
private static DocumentTextStyle capLabel() {
322+
return DocumentTextStyle.builder()
323+
.fontName(FontName.HELVETICA_BOLD)
324+
.size(9)
325+
.color(NAVY)
326+
.build();
327+
}
328+
329+
private static DocumentTextStyle capProse() {
330+
return DocumentTextStyle.builder()
331+
.fontName(FontName.HELVETICA)
332+
.size(9)
333+
.color(INK)
334+
.build();
335+
}
336+
337+
private static DocumentTextStyle footer() {
338+
return DocumentTextStyle.builder()
339+
.fontName(FontName.HELVETICA)
340+
.size(8)
341+
.color(MUTED)
342+
.build();
343+
}
344+
345+
public static void main(String[] args) throws Exception {
346+
System.out.println("Generated: " + generate());
347+
}
348+
}

0 commit comments

Comments
 (0)