Skip to content

Commit 8e1b840

Browse files
committed
feat(examples): add RepositoryHeroShowcase 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/RepositoryHeroShowcase.java (200 LOC) renders a two-page editorial CV via the MintEditorial v2 preset, using a meta-narrative dataset (Mariia Demchyshyn - Senior Software Engineer - Document Engine Author). Substantive sample content - no Lorem ipsum, no repeated DEGREE/BACHELOR rows. Shows multiple v1.6 features in one glance: themed masthead band, two-column page geometry, mixed section kinds (Paragraph, Entries, Skills, Rows), proficiency bars, cross-page Experience continuation, icon-augmented contact list. 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 the new hero (358 KB, down from 478 KB; 1818x2572 px at 220 DPI). Visual verification: rendered PDF then rasterised page 1 to PNG and inspected the image directly through Claude. First iteration had three sidebar labels wrapping mid-letter (DETERMINISTIC RENDERIN/G, OPEN-SOURCE STEWARDSH/IP, M.SC. COMPUTER SCIENC/E). Second iteration shortened labels (DETERMINISM, OSS STEWARDSHIP, MSC COMPUTER SCIENCE) and the layout now fits cleanly with no mid-word breaks. CHANGELOG entry added to v1.6.6 Planned ### Documentation subsection.
1 parent 7cfa21b commit 8e1b840

5 files changed

Lines changed: 284 additions & 0 deletions

File tree

CHANGELOG.md

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

203203
### Documentation
204204

205+
- **New flagship example: `RepositoryHeroShowcase`** + **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/RepositoryHeroShowcase.java` renders a
214+
two-page editorial CV via the MintEditorial preset with substantive
215+
fictional data ("Mariia Demchyshyn — Senior Software Engineer ·
216+
Document Engine Author"); (c) page 1 rasterised to
217+
`assets/readme/repository_showcase_render.png` via the new persistent
218+
helper `com.demcha.examples.support.PdfPageRasterizer` (PDFBox-based,
219+
no external Ghostscript / ImageMagick dependency). The hero now shows
220+
multiple v1.6 features in one glance — themed masthead band,
221+
two-column page geometry, mixed section kinds, proficiency bars,
222+
cross-page experience continuation, icon-augmented contact list —
223+
and lands as a real engineering artefact rather than a Lorem-ipsum
224+
template render.
205225
- **`docs/architecture/package-map.md` updated** alongside H2. A new
206226
intro paragraph documents the stability-marker convention (Stable
207227
default; engine packages are package-level `@Internal`; individual
-120 KB
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+
| **Repository Hero Showcase** | Two-page editorial CV ("the engine renders its own author's CV") — MintEditorial preset with substantive sample data; source of the README hero image | [Source](src/main/java/com/demcha/examples/flagships/RepositoryHeroShowcase.java) |
5657

5758
### 🧱 Core DSL
5859

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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.templates.api.DocumentTemplate;
7+
import com.demcha.compose.document.templates.cv.v2.data.CvDocument;
8+
import com.demcha.compose.document.templates.cv.v2.data.CvEntry;
9+
import com.demcha.compose.document.templates.cv.v2.data.CvIdentity;
10+
import com.demcha.compose.document.templates.cv.v2.data.CvSkill;
11+
import com.demcha.compose.document.templates.cv.v2.data.EntriesSection;
12+
import com.demcha.compose.document.templates.cv.v2.data.ParagraphSection;
13+
import com.demcha.compose.document.templates.cv.v2.data.RowStyle;
14+
import com.demcha.compose.document.templates.cv.v2.data.RowsSection;
15+
import com.demcha.compose.document.templates.cv.v2.data.SkillsSection;
16+
import com.demcha.compose.document.templates.cv.v2.presets.MintEditorial;
17+
import com.demcha.examples.support.ExampleOutputPaths;
18+
19+
import java.nio.file.Path;
20+
import java.util.List;
21+
22+
/**
23+
* Flagship "wow" hero used as the {@code assets/readme/repository_showcase_render.png}
24+
* source. Renders a polished two-page editorial CV (Mint Editorial preset) for a
25+
* meta narrative: the engine renders its own author's CV.
26+
*
27+
* <p><b>What it shows in one glance:</b></p>
28+
* <ul>
29+
* <li>Centered spaced-caps masthead with full-width mint accent rule (theme tokens
30+
* + canonical {@code pageBackgrounds} band).</li>
31+
* <li>Two-column page geometry — contact / skill / education sidebar against
32+
* profile / experience body (layered template slots).</li>
33+
* <li>Mixed content sections — paragraph (Profile), entries (Experience, Education),
34+
* skills with proficiency bars, rows (Awards), reference cards.</li>
35+
* <li>Deterministic pagination — two experience entries on page 1, continuation
36+
* onto page 2 with sidebar refresh; same {@code DocumentSession} cycle.</li>
37+
* <li>Substantive sample data — no Lorem ipsum, no placeholder reuse, so a
38+
* README hero feels like a real engineering artefact, not a template demo.</li>
39+
* </ul>
40+
*
41+
* <p>Output:
42+
* {@code examples/target/generated-pdfs/flagships/repository-hero-showcase.pdf}
43+
* — page 1 is rasterised by {@link com.demcha.examples.support.PdfPageRasterizer}
44+
* into {@code assets/readme/repository_showcase_render.png} during release prep.</p>
45+
*
46+
* @author Artem Demchyshyn
47+
* @since 1.6.6
48+
*/
49+
public final class RepositoryHeroShowcase {
50+
51+
private RepositoryHeroShowcase() {
52+
}
53+
54+
public static Path generate() throws Exception {
55+
CvDocument doc = heroCv();
56+
Path output = ExampleOutputPaths.prepare(
57+
"flagships", "repository-hero-showcase.pdf");
58+
DocumentTemplate<CvDocument> template = MintEditorial.create();
59+
float m = (float) MintEditorial.RECOMMENDED_MARGIN;
60+
try (DocumentSession document = GraphCompose.document(output)
61+
.pageSize(DocumentPageSize.A4)
62+
.margin(m, m, m, m)
63+
.create()) {
64+
template.compose(document, doc);
65+
document.buildPdf();
66+
}
67+
return output;
68+
}
69+
70+
private static CvDocument heroCv() {
71+
CvIdentity identity = CvIdentity.builder()
72+
.name("Mariia", "Demchyshyn")
73+
.jobTitle("Senior Software Engineer · Document Engine Author")
74+
.contact("+48 600 000 000", "hello@graphcompose.dev", "Kraków, Poland")
75+
.link("graphcompose.dev", "https://demchaav.github.io/GraphCompose/")
76+
.link("GitHub", "https://github.com/DemchaAV")
77+
.link("LinkedIn", "https://linkedin.com/in/demchaav")
78+
.link("Twitter", "https://twitter.com/demchaav")
79+
.build();
80+
81+
ParagraphSection profile = new ParagraphSection("Profile",
82+
"Senior engineer focused on declarative document composition for "
83+
+ "business systems. Author and maintainer of GraphCompose — "
84+
+ "a semantic Java DSL that lets services describe what a "
85+
+ "document says, then resolves layout, pagination, and "
86+
+ "PDFBox rendering deterministically. Eight years across "
87+
+ "reporting platforms, fintech back-ends, and developer-"
88+
+ "facing tooling. Believes business documents deserve the "
89+
+ "same engineering rigour as the systems that produce them.");
90+
91+
RowsSection interests = RowsSection.builder("Interests", RowStyle.PLAIN)
92+
.row("Layout Engines", "")
93+
.row("Type Design", "")
94+
.row("Determinism", "")
95+
.row("OSS Stewardship", "")
96+
.build();
97+
98+
EntriesSection education = EntriesSection.builder("Education")
99+
.entry("MSc Computer Science",
100+
"AGH University, Kraków", "2015 – 2017", "")
101+
.entry("BSc Software Eng.",
102+
"Lviv Polytechnic", "2011 – 2015", "")
103+
.build();
104+
105+
SkillsSection skills = SkillsSection.builder("Technical Skills")
106+
.leveledGroup("Layout Engines",
107+
List.of(CvSkill.of("Java + PDFBox", 0.92)))
108+
.leveledGroup("API Design",
109+
List.of(CvSkill.of("Sealed types", 0.86)))
110+
.leveledGroup("PDF Rendering",
111+
List.of(CvSkill.of("Glyph fallback", 0.81)))
112+
.leveledGroup("Test Engineering",
113+
List.of(CvSkill.of("Snapshot + japicmp", 0.78)))
114+
.leveledGroup("Release Engineering",
115+
List.of(CvSkill.of("Maven Central", 0.74)))
116+
.build();
117+
118+
String openSourceBody =
119+
"Authored and shipped GraphCompose v1.4 through v1.6 — declarative "
120+
+ "Java DSL for structured business PDFs. ~1000 tests, no "
121+
+ "breaking changes across the 1.x line, Maven Central debut "
122+
+ "in v1.6.6 with signed artefacts and a japicmp binary-"
123+
+ "compatibility gate.\n"
124+
+ "- Designed Templates v2 layered architecture (ADR-0015) "
125+
+ "and migrated 14 CV presets through it without a baseline "
126+
+ "regression.\n"
127+
+ "- Built the release runbook + automation that takes "
128+
+ "tag-to-Central deploy under five minutes.\n"
129+
+ "- Active maintainer; release cadence quarterly with "
130+
+ "ADR-driven decisions visible in the repo.";
131+
132+
String fintechBody =
133+
"Owned the PDF-rendering pipeline for invoice, account-statement, "
134+
+ "and proposal generation across twelve markets. Replaced "
135+
+ "the legacy HTML-to-PDF tooling with deterministic "
136+
+ "semantic templates rendered through an early prototype "
137+
+ "of GraphCompose.\n"
138+
+ "- Measurable layout regressions cut by 80% through "
139+
+ "snapshot-based regression tests.\n"
140+
+ "- Designer-engineer feedback loop shortened from days "
141+
+ "to hours via reviewable layout snapshots in CI.\n"
142+
+ "- Mentored four engineers on declarative-layout patterns.";
143+
144+
String reportingBody =
145+
"Java services for analytics dashboards backed by Postgres + Kafka. "
146+
+ "Owned a custom PDF report exporter built on PDFBox — and "
147+
+ "first ran into the layout-by-coordinates problem that "
148+
+ "motivated GraphCompose's existence two years later.";
149+
150+
EntriesSection experience = EntriesSection.builder("Experience")
151+
.entry("Author / Maintainer",
152+
"GraphCompose · Open Source",
153+
"2024 — Present",
154+
openSourceBody)
155+
.entry("Backend Engineer · Documents",
156+
"Iglika Fintech · Krakow, PL",
157+
"2020 — 2024",
158+
fintechBody)
159+
.entry("Software Engineer",
160+
"Northwind Reporting · Lviv, UA",
161+
"2017 — 2020",
162+
reportingBody)
163+
.build();
164+
165+
RowsSection awards = RowsSection.builder("Highlights", RowStyle.PLAIN)
166+
.row("GraphCompose v1.6 — Maven Central debut",
167+
"Cross-platform, signed artefacts · 2026")
168+
.row("Templates v2 — layered architecture (ADR-0015)",
169+
"14 CV presets migrated · 2025")
170+
.row("1,000+ test coverage maintained",
171+
"Snapshot + japicmp binary-compat gates · 2025")
172+
.row("Conference talk — \"Documents as Code\"",
173+
"Code Europe · 2024")
174+
.build();
175+
176+
RowsSection references = RowsSection.builder("References", RowStyle.PLAIN)
177+
.row("Available on request", "Shared during interview process.")
178+
.build();
179+
180+
return CvDocument.builder()
181+
.identity(identity)
182+
.sections(profile, interests, education, skills, experience,
183+
awards, references)
184+
.build();
185+
}
186+
187+
public static void main(String[] args) throws Exception {
188+
System.out.println("Generated: " + generate());
189+
}
190+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.demcha.examples.support;
2+
3+
import org.apache.pdfbox.Loader;
4+
import org.apache.pdfbox.pdmodel.PDDocument;
5+
import org.apache.pdfbox.rendering.ImageType;
6+
import org.apache.pdfbox.rendering.PDFRenderer;
7+
8+
import javax.imageio.ImageIO;
9+
import java.awt.image.BufferedImage;
10+
import java.io.File;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
13+
import java.nio.file.Paths;
14+
15+
/**
16+
* Rasterises one page of a PDF into a PNG at a chosen DPI. Tiny CLI built
17+
* on top of PDFBox so this repo never has to depend on Ghostscript /
18+
* pdftoppm / ImageMagick for README hero asset regeneration. Used during
19+
* v1.6.6 release prep to produce the new {@code repository_showcase_render.png}
20+
* (CV mint-editorial page 1 hero) without an external rasterizer.
21+
*
22+
* <p>Usage:</p>
23+
* <pre>
24+
* ./mvnw -B -ntp -f examples/pom.xml -DskipTests exec:java \
25+
* -Dexec.mainClass=com.demcha.examples.support.PdfPageRasterizer \
26+
* -Dexec.args="&lt;inputPdf&gt; &lt;outputPng&gt; [pageIndex0=0] [dpi=200]"
27+
* </pre>
28+
*
29+
* <p>Defaults: page index 0 (first page), 200 DPI (~1654 × 2339 for A4 —
30+
* comfortable for README hero quality).</p>
31+
*
32+
* @author Artem Demchyshyn
33+
* @since 1.6.6
34+
*/
35+
public final class PdfPageRasterizer {
36+
37+
private PdfPageRasterizer() {
38+
}
39+
40+
public static void main(String[] args) throws Exception {
41+
if (args.length < 2) {
42+
System.err.println("Usage: PdfPageRasterizer <inputPdf> <outputPng> [pageIndex0=0] [dpi=200]");
43+
System.exit(2);
44+
}
45+
Path inputPdf = Paths.get(args[0]).toAbsolutePath();
46+
Path outputPng = Paths.get(args[1]).toAbsolutePath();
47+
int pageIndex = args.length >= 3 ? Integer.parseInt(args[2]) : 0;
48+
float dpi = args.length >= 4 ? Float.parseFloat(args[3]) : 200f;
49+
50+
if (!Files.isRegularFile(inputPdf)) {
51+
System.err.println("Input PDF not found: " + inputPdf);
52+
System.exit(3);
53+
}
54+
Files.createDirectories(outputPng.getParent());
55+
56+
try (PDDocument doc = Loader.loadPDF(inputPdf.toFile())) {
57+
int totalPages = doc.getNumberOfPages();
58+
if (pageIndex < 0 || pageIndex >= totalPages) {
59+
System.err.println("Page index " + pageIndex + " out of range (0.." + (totalPages - 1) + ")");
60+
System.exit(4);
61+
}
62+
PDFRenderer renderer = new PDFRenderer(doc);
63+
BufferedImage image = renderer.renderImageWithDPI(pageIndex, dpi, ImageType.RGB);
64+
if (!ImageIO.write(image, "png", outputPng.toFile())) {
65+
System.err.println("Failed to write PNG to " + outputPng);
66+
System.exit(5);
67+
}
68+
System.out.println("Rasterised page " + pageIndex + " of " + inputPdf
69+
+ " at " + dpi + " DPI -> " + outputPng
70+
+ " (" + image.getWidth() + " x " + image.getHeight() + " px)");
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)