Skip to content

Commit 1c0f33a

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 1c0f33a

6 files changed

Lines changed: 435 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.64 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: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
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 with cinematic engine image ────
109+
.addContainer(frame -> frame
110+
.name("HeroFrame")
111+
.roundedRect(516, 280, 14)
112+
.fillColor(NAVY_DARK)
113+
.stroke(DocumentStroke.of(ACCENT, 2.5))
114+
.clipPolicy(ClipPolicy.CLIP_PATH)
115+
.center(new com.demcha.compose.document.dsl.ImageBuilder()
116+
.name("HeroImage")
117+
.source(hero)
118+
.size(512, 276)
119+
.fitMode(DocumentImageFitMode.COVER)
120+
.build()))
121+
122+
// ── Magazine-headline lockup ──────────────────
123+
.addSection("Lockup", section -> section
124+
.padding(new DocumentInsets(8, 4, 0, 4))
125+
.spacing(2)
126+
.addParagraph(p -> p
127+
.text("One engine.")
128+
.textStyle(headline())
129+
.margin(DocumentInsets.zero()))
130+
.addParagraph(p -> p
131+
.text("Every PDF you ship.")
132+
.textStyle(headlineAccent())
133+
.margin(DocumentInsets.zero()))
134+
.addParagraph(p -> p
135+
.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.")
136+
.textStyle(tagline())
137+
.lineSpacing(1.35)
138+
.margin(new DocumentInsets(6, 0, 0, 0))))
139+
140+
// ── Three KPI cards — engine capability counts ─
141+
.addRow("KpiRow", row -> row
142+
.spacing(12)
143+
.weights(1, 1, 1)
144+
.addSection("KpiPresets", section -> kpiCard(section,
145+
"31", "V2 Presets",
146+
"Layered CV + cover-letter preset architecture — data · theme · components · widgets, swap one without rewriting the others."))
147+
.addSection("KpiTests", section -> kpiCard(section,
148+
"1,033", "Tests · 0 failures",
149+
"Snapshot baselines, japicmp binary-compat gate, parallel-session stress harness, JMH benchmark suite."))
150+
.addSection("KpiNodes", section -> kpiCard(section,
151+
"17", "Semantic Primitives",
152+
"Modules, sections, paragraphs, lists, tables, rows, shapes, images — every node is themable and snapshot-tested.")))
153+
154+
// ── Capability columns ────────────────────────
155+
.addRow("Capabilities", row -> row
156+
.spacing(14)
157+
.weights(1, 1, 1)
158+
.addSection("CapDsl", section -> capabilityColumn(section,
159+
"DECLARATIVE BY DESIGN",
160+
"Compose by intent — modules, sections, rows. Zero PDFBox imports in your application code."))
161+
.addSection("CapDeterm", section -> capabilityColumn(section,
162+
"DETERMINISTIC RENDER",
163+
"Identical input, identical PDF — byte-comparable. Layout snapshots, JMH-benchmarked hot paths."))
164+
.addSection("CapTheme", section -> capabilityColumn(section,
165+
"CINEMATIC THEMING",
166+
"BusinessTheme · CvTheme palettes, component-level tokens, v2 layered preset architecture.")))
167+
168+
// ── Footer brand stripe ───────────────────────
169+
.addShape(s -> s.size(516, 0.6).fillColor(RULE)
170+
.margin(new DocumentInsets(8, 0, 0, 0)))
171+
.addRow("Footer", row -> row
172+
.spacing(0)
173+
.weights(2, 1)
174+
.addSection("FooterLeft", section -> section
175+
.addParagraph(p -> p
176+
.text("graphcompose.dev · github.com/DemchaAV/GraphCompose")
177+
.textStyle(footer())
178+
.margin(DocumentInsets.zero())))
179+
.addSection("FooterRight", section -> section
180+
.addParagraph(p -> p
181+
.text("Java · PDFBox · MIT")
182+
.textStyle(footer())
183+
.align(TextAlign.RIGHT)
184+
.margin(DocumentInsets.zero()))))
185+
186+
.build();
187+
188+
document.buildPdf();
189+
}
190+
return output;
191+
}
192+
193+
private static DocumentImageData loadHeroImage() throws Exception {
194+
try (InputStream is = Objects.requireNonNull(
195+
EngineShowcase.class.getResourceAsStream("/engine-hero.png"),
196+
"engine-hero.png missing from examples/src/main/resources/")) {
197+
return DocumentImageData.fromBytes(is.readAllBytes());
198+
}
199+
}
200+
201+
// ── Helpers ──────────────────────────────────────────────────
202+
203+
private static void kpiCard(com.demcha.compose.document.dsl.SectionBuilder section,
204+
String value, String label, String detail) {
205+
section
206+
.softPanel(DocumentColor.WHITE, 8, 12)
207+
.stroke(DocumentStroke.of(CARD_RING, 0.5))
208+
.spacing(2)
209+
.addParagraph(p -> p
210+
.text(value)
211+
.textStyle(kpiValue())
212+
.margin(DocumentInsets.zero()))
213+
.addParagraph(p -> p
214+
.text(label)
215+
.textStyle(kpiLabel())
216+
.margin(new DocumentInsets(0, 0, 0, 0)))
217+
.addParagraph(p -> p
218+
.text(detail)
219+
.textStyle(kpiDetail())
220+
.lineSpacing(1.25)
221+
.margin(new DocumentInsets(4, 0, 0, 0)));
222+
}
223+
224+
private static void capabilityColumn(com.demcha.compose.document.dsl.SectionBuilder section,
225+
String label, String prose) {
226+
section
227+
.softPanel(PANEL_TINT, 6, 0)
228+
.stroke(DocumentStroke.of(PANEL_RING, 0.5))
229+
.accentLeft(ACCENT, 3.0f)
230+
.padding(new DocumentInsets(12, 12, 12, 16))
231+
.spacing(3)
232+
.addParagraph(p -> p
233+
.text(label)
234+
.textStyle(capLabel())
235+
.margin(DocumentInsets.zero()))
236+
.addParagraph(p -> p
237+
.text(prose)
238+
.textStyle(capProse())
239+
.lineSpacing(1.3)
240+
.margin(new DocumentInsets(2, 0, 0, 0)));
241+
}
242+
243+
// ── Text styles ──────────────────────────────────────────────
244+
245+
private static DocumentTextStyle bandLeft() {
246+
return DocumentTextStyle.builder()
247+
.fontName(FontName.HELVETICA_BOLD)
248+
.size(10)
249+
.color(NAVY)
250+
.build();
251+
}
252+
253+
private static DocumentTextStyle bandRight() {
254+
return DocumentTextStyle.builder()
255+
.fontName(FontName.HELVETICA)
256+
.size(9)
257+
.color(MUTED)
258+
.build();
259+
}
260+
261+
private static DocumentTextStyle headline() {
262+
return DocumentTextStyle.builder()
263+
.fontName(FontName.TIMES_BOLD)
264+
.size(38)
265+
.color(INK)
266+
.build();
267+
}
268+
269+
private static DocumentTextStyle headlineAccent() {
270+
return DocumentTextStyle.builder()
271+
.fontName(FontName.TIMES_BOLD_ITALIC)
272+
.size(38)
273+
.color(ACCENT)
274+
.build();
275+
}
276+
277+
private static DocumentTextStyle tagline() {
278+
return DocumentTextStyle.builder()
279+
.fontName(FontName.HELVETICA)
280+
.size(11)
281+
.color(INK)
282+
.build();
283+
}
284+
285+
private static DocumentTextStyle kpiValue() {
286+
return DocumentTextStyle.builder()
287+
.fontName(FontName.TIMES_BOLD)
288+
.size(26)
289+
.color(NAVY)
290+
.build();
291+
}
292+
293+
private static DocumentTextStyle kpiLabel() {
294+
return DocumentTextStyle.builder()
295+
.fontName(FontName.HELVETICA_BOLD)
296+
.size(9)
297+
.color(ACCENT)
298+
.build();
299+
}
300+
301+
private static DocumentTextStyle kpiDetail() {
302+
return DocumentTextStyle.builder()
303+
.fontName(FontName.HELVETICA)
304+
.size(8)
305+
.color(MUTED)
306+
.build();
307+
}
308+
309+
private static DocumentTextStyle capLabel() {
310+
return DocumentTextStyle.builder()
311+
.fontName(FontName.HELVETICA_BOLD)
312+
.size(9)
313+
.color(NAVY)
314+
.build();
315+
}
316+
317+
private static DocumentTextStyle capProse() {
318+
return DocumentTextStyle.builder()
319+
.fontName(FontName.HELVETICA)
320+
.size(9)
321+
.color(INK)
322+
.build();
323+
}
324+
325+
private static DocumentTextStyle footer() {
326+
return DocumentTextStyle.builder()
327+
.fontName(FontName.HELVETICA)
328+
.size(8)
329+
.color(MUTED)
330+
.build();
331+
}
332+
333+
public static void main(String[] args) throws Exception {
334+
System.out.println("Generated: " + generate());
335+
}
336+
}

0 commit comments

Comments
 (0)