A runnable, single-file Java example for every public surface in
GraphCompose — built-in templates, v1.5 cinematic features, public-API
showcases, and a kitchen-sink master demo. Each example writes a PDF
to examples/target/generated-pdfs/; the same PDFs are committed to
assets/readme/examples/ so you can
preview every render straight from GitHub without running anything.
Install the library artifact once from the repository root:
./mvnw -DskipTests installThen run all 26 examples in one shot:
./mvnw -f examples/pom.xml exec:java \
-Dexec.mainClass=com.demcha.examples.GenerateAllExamples…or run a single example by passing its main class:
./mvnw -f examples/pom.xml exec:java \
-Dexec.mainClass=com.demcha.examples.MasterShowcaseExampleGenerated PDFs land in examples/target/generated-pdfs/. The same
mvnw.cmd form works on Windows PowerShell with backslash paths.
Examples are categorised by maturity / intent, not by the GraphCompose release that introduced them. Pick the row that matches how far along you are with the canonical DSL, then jump to its detailed section below.
Maturity legend. 🚀 Start here — minimum-friction entry points; pick one if you've never used the canonical DSL before. 🧱 Core DSL — features you'll author against every day once the basics click. 📋 Templates recommended — the v2 / layered template surfaces and how to drive them; pick when you want a one-line CV / invoice / proposal / cover-letter. 🔧 Advanced SPI — production-deployment patterns, specialty SPIs (shapes, transforms, barcodes), engine-level tools (snapshots). 🗄️ Legacy — pre-rebuild examples kept for downstream callers still on V1; do not start new code here. See
docs/templates/which-template-system.mdfor the V1 → V2 migration path.
| Example | What it shows | Preview · Source |
|---|---|---|
| CV — single template | One CV via ModernProfessional.create(BusinessTheme.modern()) (Templates v2) |
PDF · Source |
| Invoice — cinematic V2 | InvoiceTemplateV2 + BusinessTheme.modern() — the recommended invoice path |
PDF · Source |
| Cover Letter | One-page BusinessTheme.modern() cover letter with section presets |
PDF · Source |
| Module-first Profile | Authoring directly against DocumentSession.module(...).paragraph(...) — DSL-direct, no template |
PDF · Source |
| 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 |
| Example | What it shows | Preview · Source |
|---|---|---|
| Rich text | Every RichText method (bold / italic / underline / link / colour / accent / size / append) |
PDF · Source |
| Section presets | pageBackground, band, softPanel, accentLeft / Right / Top / Bottom, per-corner DocumentCornerRadius |
PDF · Source |
| Nested lists | ListBuilder.addItem(label, Consumer) — depth cascade, per-depth markers, mixed flat / nested authoring |
PDF · Source |
| Composed table cells | DocumentTableCell.node(DocumentNode) — paragraphs, lists, sub-tables inside cells with two-pass measurement |
PDF · Source |
| Canvas layer (free placement) | CanvasLayerNode — pixel-precise (x, y) placement of children inside a fixed bounding box, with ClipPolicy clipping |
PDF · Source |
| Transforms | rotate, scale, and per-layer zIndex swap |
PDF · Source |
| Example | What it shows | Preview · Source |
|---|---|---|
| CV — template gallery | All 14 v2 CV presets in one orchestrated run | Source |
| Cover letter — template gallery | All 14 paired v2 cover-letter presets in one orchestrated run | Source |
| Proposal — cinematic V2 | ProposalTemplateV2 + BusinessTheme.modern() |
PDF · Source |
| Custom Business Theme | Hand-built BusinessTheme driving InvoiceTemplateV2 — theme-token customisation entry point |
PDF · Source |
| Example | What it shows | Preview · Source |
|---|---|---|
| Shape containers | Circles, ellipses, rounded cards with ClipPolicy.CLIP_PATH |
PDF · Source |
| Advanced tables | Row span, zebra rows, totals, repeating header on page break | PDF · Source |
| Barcodes | QR, Code 128, Code 39, EAN-13, EAN-8, branded QR with theme colours | PDF · Source |
| PDF chrome | DocumentMetadata, DocumentWatermark, DocumentHeaderFooter, DocumentBookmarkOptions |
PDF · Source |
| HTTP streaming | writePdf(OutputStream) for Servlet / S3 / GCS — caller's stream is not closed |
PDF · Source |
| Layout snapshot regression | Deterministic layoutSnapshot() workflow with baseline + drift report — production regression-testing pattern |
PDF · Source |
| Business report cover | Single-page Q1 investor brief — hero image, KPI cards, bar chart, metrics table | PDF · Source |
| Master showcase | Kitchen-sink "Q2 sample report" combining the canonical surface end-to-end | PDF · Source |
| Example | What it shows | Preview · Source |
|---|---|---|
| Invoice (V1) | InvoiceTemplateV1 driven from InvoiceDocumentSpec — pre-rebuild surface, supported only |
PDF · Source |
| Proposal (V1) | ProposalTemplateV1 driven from ProposalDocumentSpec — pre-rebuild surface, supported only |
PDF · Source |
| Handcrafted Proposal | v1.4-style cinematic proposal composed by hand — pre-template authoring; kept for parity reference | PDF · Source |
| Weekly schedule | Bar / restaurant shift schedule via WeeklyScheduleRenderer (WeeklyScheduleTemplateV1 — no V2 yet; will be re-shaped before 2.0) |
PDF · Source |
A one-page modern cover letter — BusinessTheme.modern() drives every
colour and font choice; section presets (softPanel, accentLeft,
accentTop) carry the visual hierarchy; opening rich-text strip
highlights the candidate's headline.
try (DocumentSession document = GraphCompose.document(outputFile)
.pageSize(DocumentPageSize.A4)
.pageBackground(THEME.pageBackground())
.margin(56, 48, 56, 48)
.create()) {
document.pageFlow()
.name("CoverLetter")
.spacing(18)
.addRow("Header", row -> row
.weights(3, 1)
.addSection("Identity", section -> section
.addParagraph(p -> p.text("Mariia Demchyshyn")
.textStyle(THEME.text().h1())))
.addSection("Date", section -> section
.addParagraph(p -> p.text("15 May 2026"))))
.addSection("Headline", section -> section
.softPanel(THEME.palette().surfaceMuted(), 10, 18)
.accentLeft(ACCENT, 4)
.addRich(rich -> rich
.plain("I help teams ship ")
.style("designed PDFs as code", BOLD_BRAND)
.plain(" — semantic Java DSL, deterministic layout.")))
// … recipient block + body paragraphs + highlights row + closing …
.build();
document.buildPdf();
}InvoiceTemplateV1.compose(document, spec) handles the full layout —
header band, parties row, line-items table, totals row, payment-terms
footer — driven from a InvoiceDocumentSpec. Use this when you want
the legacy hard-coded theme; for V2 cinematic, see below.
InvoiceDocumentSpec spec = InvoiceDocumentSpec.builder()
.invoiceNumber("GC-2026-041")
.issueDate("02 Apr 2026")
.dueDate("16 Apr 2026")
.fromParty(p -> p.name("GraphCompose Studio"))
.billToParty(p -> p.name("Northwind Systems"))
.lineItem("Template architecture", "Reusable invoice flow", "2", "GBP 980", "GBP 1,960")
.totalRow("Total", "GBP 1,960")
.build();
try (DocumentSession document = GraphCompose.document(outputFile)
.pageSize(DocumentPageSize.A4)
.margin(28, 28, 28, 28)
.create()) {
new InvoiceTemplateV1().compose(document, spec);
document.buildPdf();
}ProposalTemplateV1 rendered against a ProposalDocumentSpec —
sections, scope items, deliverables, sign-off. Pairs naturally with
InvoiceTemplateV1 for consistent "spec → PDF" pipelines.
Authoring against DocumentSession.pageFlow().module(...) — no
template, no theme, just the canonical DSL. Smallest possible footprint
for "I just need a one-page PDF from data".
document.pageFlow()
.module("Profile", module -> module
.heading("Mariia Demchyshyn")
.paragraph("Senior Backend Engineer")
.paragraph("mariia@example.com · +44 20 7946 0234"));
document.buildPdf();One CV rendered through the Templates v2 surface:
ModernProfessional.create(BusinessTheme.modern()) paired with a
CvSpec data shape. The preset is one final class with one
create(BusinessTheme) factory — copy-and-tweak rather than
fork-a-monolith.
Generates every v2 CV preset in one orchestrated run — 14 presets
covering single-column, two-column-sidebar, and three-column-magazine
layouts. Use this as the side-by-side catalogue when picking a base
preset for your own CV product. Each preset is a one-liner factory
(ModernProfessional.create(), NordicClean.create(),
…); see cv/v2/presets/ for the full list.
| Variant | |
|---|---|
| Modern professional | |
| Nordic clean | |
| Classic serif | |
| Compact mono | |
| Timeline minimal | |
| Engineering resume | |
| Panel | |
| Executive · BoxedSections · CenteredHeadline · BlueBanner · EditorialBlue · SidebarPortrait · MonogramSidebar | run the gallery to render |
Generates all 14 paired v2 cover-letter presets in one run — one
letter style per CV preset so a candidate's CV and cover letter
share the same visual language end-to-end. Each preset is a
one-liner factory (ModernProfessionalLetter.create(),
NordicCleanLetter.create(), …) under
coverletter/v2/presets/.
InvoiceTemplateV2(BusinessTheme.modern()) — the cinematic invoice.
Hero softPanel with invoice number / dates / inline rich-text status,
two-column parties row, themed line-items table with headerStyle +
zebra + totals + repeatHeader(), footer row with accentLeft strips.
BusinessTheme theme = BusinessTheme.modern();
InvoiceTemplateV2 template = new InvoiceTemplateV2(theme);
try (DocumentSession document = GraphCompose.document(outputFile)
.pageSize(DocumentPageSize.A4)
.pageBackground(theme.pageBackground())
.margin(28, 28, 28, 28)
.create()) {
template.compose(document, invoice);
document.buildPdf();
}ProposalTemplateV2 — same BusinessTheme-driven pattern as the
invoice. Hero rounded only on the right (DocumentCornerRadius.right(...)),
themed executive-summary panel, sender / recipient parties row,
theme.text().h2() headings, timeline + pricing tables with
repeatHeader(), zebra rows, and a totalRow(...).
A v1.4-style cinematic proposal composed by hand — no template — to
show how the same primitives compose without a XxxTemplateV2
wrapper. Useful starting point when your domain doesn't fit any
built-in template.
addContainer(...), addCircle(...), addEllipse(...) build a
ShapeContainerNode whose bounding box is dictated by a
ShapeOutline. Children are clipped via ClipPolicy.CLIP_PATH
(default), CLIP_BOUNDS, or OVERFLOW_VISIBLE. The
ShapeContainerBuilder exposes the same nine-point alignment
vocabulary as LayerStackBuilder plus position(node, dx, dy, anchor)
for screen-space nudges.
.addContainer(
ShapeOutline.RoundedRectangle.of(220, 140, 14),
ClipPolicy.CLIP_PATH,
DocumentColor.rgb(28, 31, 38), // background
DocumentStroke.of(DocumentColor.rgb(196, 153, 76), 1.0),
container -> container
.center(centeredImage) // 9-point alignment
.position(badge, 120, 18, Anchor.TOP_LEFT)) // pixel-preciseDocumentTransform.rotate(deg) and .scale(uniform | sx, sy) —
attached to any Transformable<T> builder
(ShapeContainerBuilder, ShapeBuilder, LineBuilder,
EllipseBuilder, ImageBuilder, BarcodeBuilder). Identity
transforms emit no markers, so layout snapshots stay byte-identical to
v1.4. Per-layer zIndex lets a layer declared earlier draw on top of
layers declared later — LayerStackNode.Layer and shape-container
layers both gain int zIndex (default 0).
.addCircle(60, ROYAL_BLUE, container -> container
.rotate(15)
.center(label))
.layer(redSquare, Anchor.CENTER, /* zIndex */ 10) // declared first, drawn on top
.layer(tealSquare, Anchor.CENTER) // declared second, drawn beneathDocumentTableCell.rowSpan(int) mirrors colSpan(int).
TableBuilder.zebra(odd, even) paints alternating rows.
totalRow(...) adds a bold-on-grey-blue totals row.
repeatHeader() re-emits the leading rows on every continuation page
when the table paginates.
table.columns(...)
.headerRow("Item", "Description", "Qty", "Unit", "Amount")
.repeatHeader() // pinned on every page
.zebra(zebraOdd, zebraEven)
.row("Tall", "Spans 3 rows", "—", "—", "—") // colSpan(2).rowSpan(3) cell
.row("…", …)
.totalRow("", "", "", "Total", "GBP 1,960");Build a BusinessTheme from raw DocumentPalette / SpacingScale /
TextScale / TablePreset records — no factory shortcut. Plug it
straight into InvoiceTemplateV2 to retheme the whole template
without touching any code that uses it.
BusinessTheme studioEmerald = new BusinessTheme(
new DocumentPalette(/* page, surface, surfaceMuted, ink, accent, … */),
SpacingScale.cinematic(),
new TextScale(/* h1, h2, body, caption fonts … */),
TablePreset.cinematic());
new InvoiceTemplateV2(studioEmerald).compose(document, invoice);Every RichText method laid out as labelled rows on a single A4 page:
plain, bold, italic, boldItalic, underline, strikethrough,
color, accent, size, style, link, append. Use this as the
visual reference when picking which call to make for inline text.
.addRich(rich -> rich
.plain("Customer ")
.bold("Northwind Systems")
.plain(" placed order ")
.accent("#GC-2026-041", BRAND_GOLD)
.plain(" — see ")
.link("invoice", "https://example.com/invoice/41")
.plain("."))pageBackground, band, softPanel, the four
accentLeft / accentRight / accentTop / accentBottom strips, and
per-corner DocumentCornerRadius (top, bottom, left, right,
only(...)) rendered side-by-side as recipe cards.
.addSection("Hero", section -> section
.softPanel(theme.palette().surfaceMuted(), 10, 18)
.accentLeft(theme.palette().accent(), 4)
.cornerRadius(DocumentCornerRadius.right(12))
.addParagraph(p -> p.text("Hero block").textStyle(theme.text().h1())))BarcodeBuilder with five symbology types — QR, Code 128,
Code 39, EAN-13, EAN-8 — plus a branded QR using the active
theme's foreground / background colours. ZXing is the encoder; the
PDF backend rasterises and embeds.
.addBarcode(b -> b
.symbology(BarcodeSymbology.QR_CODE)
.data("https://github.com/DemchaAV/GraphCompose")
.size(140, 140)
.foreground(theme.palette().ink())
.background(theme.palette().surface()))Backend-neutral DocumentMetadata, DocumentWatermark,
DocumentHeaderFooter (header + footer with {page} / {pages} / {date} tokens), and paragraph-level DocumentBookmarkOptions
materialising as PDF outline entries.
GraphCompose.document(outputFile)
.metadata(DocumentMetadata.builder()
.title("Q1 2026 Investor Brief")
.author("Mariia Demchyshyn")
.build())
.watermark(DocumentWatermark.draftStamp(theme.palette().muted()))
.headerFooter(DocumentHeaderFooter.tokens("Q1 Brief", "Page {page} of {pages}"))
.create();writePdf(OutputStream) for Servlet / S3 / GCS adopters. The caller's
stream is not closed by GraphCompose — pinned by
HttpStreamingDemoTest. A Spring Boot @RestController snippet in the
example javadoc shows the canonical wiring.
@GetMapping(value = "/invoice/{id}", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<StreamingResponseBody> invoice(@PathVariable Long id) {
InvoiceDocumentSpec spec = invoiceService.loadInvoice(id);
StreamingResponseBody body = response -> {
try (DocumentSession document = GraphCompose.document()
.pageSize(DocumentPageSize.A4)
.pageBackground(BusinessTheme.modern().pageBackground())
.margin(28, 28, 28, 28)
.create()) {
new InvoiceTemplateV2(BusinessTheme.modern()).compose(document, spec);
document.writePdf(response); // streams directly, no in-memory PDF
}
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=invoice.pdf")
.body(body);
}The full compose → layoutSnapshot() → LayoutSnapshotJson.toJson(...)
workflow with a copy-and-paste baseline / drift report pattern, plus a
pointer to the production
LayoutSnapshotAssertions.assertMatches(document, "...") helper for
in-test usage.
DocumentSession document = GraphCompose.document(outputFile)…create();
new InvoiceTemplateV2(theme).compose(document, spec);
String snapshot = LayoutSnapshotJson.toJson(document.layoutSnapshot());
String baseline = Files.readString(baselinePath);
if (!baseline.equals(snapshot)) {
Files.writeString(driftPath, snapshot);
throw new AssertionError("Layout drift detected — diff " + driftPath);
}
document.buildPdf();Bar / restaurant weekly shift schedule rendered through the canonical
DSL via a reusable WeeklyScheduleRenderer. The renderer's typed API
lets you express any combination of full-day status fills, half-day
shifts (lunch / dinner), and cross-meal shifts without parsing strings:
import com.demcha.examples.support.WeeklyScheduleRenderer;
import com.demcha.examples.support.WeeklyScheduleRenderer.*;
private static final List<StaffMember> STAFF = List.of(
new StaffMember("AARON PARK", JobTitle.MANAGER),
new StaffMember("DIANA COLE", JobTitle.BARTENDER),
new StaffMember("JASPER LIN", JobTitle.BAR_BACK)
);
private static final Map<String, DayShift[]> SHIFTS = Map.ofEntries(
Map.entry("AARON PARK", new DayShift[] {
DayShift.acrossDay("09:00", "18:00", ShiftType.STOCK), // Mon — stock recon all day
DayShift.OFF, // Tue
DayShift.OFF, // Wed
DayShift.dinnerOnly("16:00", "00:00"), // Thu — dinner only
DayShift.OFF, // Fri
DayShift.shifts("11:00", "16:00", "16:00", "22:00"), // Sat — both halves
DayShift.shifts("08:00", "13:00", "13:00", "18:00") // Sun
}),
Map.entry("DIANA COLE", new DayShift[] {
DayShift.halves(Half.shift("12:00", "20:00"), Half.STANDBY),
// …
})
);
WeeklyScheduleRenderer.renderTo(outputFile, "AURORA",
LocalDate.of(2026, 5, 4), STAFF, WEEK, SHIFTS);The renderer auto-fills the seven date labels from weekStart
(Monday 4th / Tuesday 5th / …), sorts staff by JobTitle.ordinal()
so groups appear in declared order, and emits a separator row at every
job-title boundary so adding or removing a StaffMember never
requires updating positional indices. The colour palette and column
widths live behind Theme.aurora() and Layout.landscape() records,
so reskinning is one parameter swap.
📄 View PDF · 📜 Example source · 📜 Renderer source
Single-page Q1 investor brief — top band identifier, serif headline,
procedurally rendered hero image (Java Graphics2D PNG embedded via
DocumentImageData.fromBytes(...)), three gold-ringed KPI cards,
strategic-highlights bullet list paired with a five-quarter Revenue /
Profit bar chart, YoY metrics table, and a confidential / page-number
footer. Use this as the visual reference for landing-page hero shots.
Fictional "Q2 sample report" combining the canonical surface
end-to-end: BusinessTheme + page background + hero with rotated
shape container + branded QR + executive summary + zebra-striped
totals table + accent-bordered highlight cards + Code 128 footer
barcode. Reference it when composing your own multi-page documents.
Every example file follows the same shape:
public final class FooExample {
private static final BusinessTheme THEME = BusinessTheme.modern();
// … more constants if useful …
private FooExample() {
}
public static Path generate() throws Exception {
Path outputFile = ExampleOutputPaths.prepare("foo.pdf");
try (DocumentSession document = GraphCompose.document(outputFile)
.pageSize(DocumentPageSize.A4)
.pageBackground(THEME.pageBackground())
.margin(28, 28, 28, 28)
.create()) {
document.pageFlow()
.name("Foo")
.spacing(12)
// … your composition …
.build();
document.buildPdf();
}
return outputFile;
}
public static void main(String[] args) throws Exception {
System.out.println("Generated: " + generate());
}
}This makes every example runnable on its own (main), callable
from GenerateAllExamples (generate() returns the output path),
and deterministic — the same data + same code always produces the
same bytes (verified by LayoutSnapshotRegressionExample).
For the canonical authoring patterns — builder hierarchy, theme
tokens, table presets, golden / anti-patterns, and a 40-line skeleton
for new templates — read
docs/templates/v1-classic/authoring.md
once before writing your own.
| Path | What's there |
|---|---|
examples/src/main/java/com/demcha/examples/ |
One file per example, runnable via main |
examples/src/main/java/com/demcha/examples/support/ |
Reusable helpers (ExampleOutputPaths, WeeklyScheduleRenderer) |
examples/target/generated-pdfs/ |
Output of running the examples (gitignored) |
assets/readme/examples/ |
Committed PDF previews linked from this gallery |
docs/templates/v1-classic/authoring.md |
Template authoring cheatsheet |
CHANGELOG.md |
Per-version surface changes (every example link is current to v1.6) |
