Skip to content

Commit be076b8

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 be076b8

6 files changed

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

0 commit comments

Comments
 (0)