Declarative Java DSL for structured business PDFs.
Describe what the document says; the engine resolves layout, pagination, themes, and PDFBox rendering. Cinematic by default.
Live Showcase · Examples Gallery · Changelog
- Author intent, not coordinates. Fluent DSL for sections, paragraphs, tables, lists, layer stacks, themes — the engine handles measurement, pagination, and rendering.
- Deterministic by design. Two-pass layout. Snapshots are stable across machines, so layout regressions are catchable in tests before any byte ships.
- Cinematic-by-default.
BusinessTheme+ soft panels + accent strips + transforms + advanced tables are first-class primitives, not workarounds. - PDFBox isolated, DOCX optional. Single backend interface. Apache POI–backed DOCX export is available for compatible semantic content — see support matrix for limitations.
Sits between iText (low-level page primitives) and JasperReports (XML-template-driven layout): a Java DSL describes the document semantically, the engine renders.
| Format | Status | Notes |
|---|---|---|
| Production | Fixed-layout backend on PDFBox 3.0. Full DSL coverage. | |
| DOCX | Partial | Semantic export via Apache POI. Unsupported nodes (shape, line, ellipse, barcode) are dropped silently — layout fidelity is best-effort for paragraph / list / table content. |
| PPTX | Skeleton | Validates supported node types and emits a manifest. Not a real PowerPoint export yet — planned only if there is demand. |
- Server-side PDF generation in Java — invoices, CVs, reports, proposals, statements, schedules.
- Templated documents from data — themed presets (
ModernProfessional,InvoiceTemplateV2, …) you parameterise instead of re-styling every time. - Regression-tested layouts —
DocumentSession#layoutSnapshot()makes layout changes visible in PRs before any byte ships. - Streaming PDFs from web backends — Spring Boot
@RestControllerwriting straight to the response (HttpStreamingExample). - Higher-level than PDFBox, lighter than JasperReports — Java DSL describes semantics; no XML templates, no manual coordinates.
- Not a hosted PDF rendering service — it is a library you embed.
- Not a WYSIWYG editor — the DSL is code, not drag-and-drop.
- Not a reporting engine like JasperReports — no datasource bindings, no XML templates, no compiled
.jasperfiles. - Not a browser / HTML-to-PDF renderer — the engine has its own layout pipeline; HTML/CSS input is not supported.
| Library | API style | Layout | License | Best for |
|---|---|---|---|---|
| GraphCompose | Java DSL, semantic nodes | Two-pass, deterministic, snapshot-testable | MIT | Code-first business documents with layout regression tests |
| PDFBox | Low-level text / path primitives | Manual coordinates | Apache 2.0 | Direct PDF manipulation, parsing, extraction |
| iText 7 | Low-level page primitives + high-level helpers | Manual + helpers | AGPL / commercial | When AGPL is acceptable or you have a commercial licence |
| OpenPDF | iText 4 fork | Manual + helpers | LGPL / MPL | Legacy iText 4 codebases |
| JasperReports | XML templates compiled to .jasper |
Template-driven | LGPL | Tabular reports with datasource bindings |
GraphCompose uses PDFBox under the hood as the rendering backend — the comparison is about authoring surface, not the renderer.
| You want to… | Surface | Entry point |
|---|---|---|
| Generate a one-off PDF programmatically | DSL | GraphCompose.document(...).pageFlow(...) — see Hello world below |
| Generate a CV / cover letter / invoice / proposal from data | Templates v2 | ModernProfessional.create(BusinessTheme.modern()).compose(session, spec) — see templates v2 |
| Add a custom visual primitive | Engine extension | NodeDefinition + PdfFragmentRenderHandler — see extension guide |
| Regression-test generated layouts | Layout snapshots | DocumentSession#layoutSnapshot() — see snapshot testing |
<repositories>
<repository><id>jitpack.io</id><url>https://jitpack.io</url></repository>
</repositories>
<dependency>
<groupId>com.github.DemchaAV</groupId>
<artifactId>GraphCompose</artifactId>
<version>v1.6.1</version>
</dependency>repositories { maven("https://jitpack.io") }
dependencies { implementation("com.github.demchaav:GraphCompose:v1.6.1") }Distribution status — currently JitPack. Maven Central is planned for v1.7 (tracking issue).
Upgrading from v1.5? Core document authoring stays source-compatible — engine, DSL, themes, and backend-neutral records carry v1.5 callers unchanged. Templates v2 replaces the legacy CV / cover-letter template classes; legacy classes were deleted, not deprecated. Read the migration guide before upgrading template-heavy code.
import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentPageSize;
import com.demcha.compose.document.api.DocumentSession;
import com.demcha.compose.document.theme.BusinessTheme;
import java.nio.file.Path;
class Hello {
public static void main(String[] args) throws Exception {
BusinessTheme theme = BusinessTheme.modern();
try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
.pageSize(DocumentPageSize.A4)
.pageBackground(theme.pageBackground())
.margin(28, 28, 28, 28)
.create()) {
document.pageFlow(page -> page
.addSection("Hero", section -> section
.softPanel(theme.palette().surfaceMuted(), 10, 14)
.accentLeft(theme.palette().accent(), 4)
.addParagraph(p -> p.text("GraphCompose").textStyle(theme.text().h1()))
.addParagraph(p -> p.text("A theme-driven hero, no manual coordinates.")
.textStyle(theme.text().body()))));
document.buildPdf();
}
}
}For a Spring Boot @RestController streaming the PDF straight to the response, see HttpStreamingExample.
- Templates v2 — 14 CV and 14 paired cover-letter presets, theme-driven via
BusinessTheme, one-linercreate(theme)factories. Inline markdown, slot-based multi-column layouts. Seedocs/templates-v2.md. - Composed primitives —
ListBuilder.addItem(label, Consumer)(nested lists),DocumentTableCell.node(...)(any node inside a cell),CanvasLayerNode(pixel-precise free-canvas placement). - Architecture hardening —
@InternalAPI stability marker, publicPdfFragmentRenderHandlerSPI,DocumentRenderingExceptionon the convenience render path, documented thread-safety contract.
Full notes in CHANGELOG.md. Upgrade guide: docs/migration-v1-5-to-v1-6.md.
Three snippets, one per new primitive. Full runnable versions live in the examples gallery.
Nested list — builder-callback child scopes with a per-depth marker cascade.
document.pageFlow().addList(list -> list
.addItem("Backend platform", row -> row
.addItem("Java 21, Spring Boot, PostgreSQL")
.addItem("REST APIs and event-driven services"))
.addItem("Document generation", row -> row
.addItem("PDF rendering pipeline")
.addItem("Layout snapshot tests")));Composed table cell — any composable node inside a cell, two-pass row measurement.
DocumentTableCell richSummary = DocumentTableCell.node(
new ParagraphNode("Summary",
"**Q3 results** were *strong* — revenue grew 18% YoY.",
bodyStyle, TextAlign.LEFT, 1.0,
DocumentInsets.zero(), DocumentInsets.zero()));Canvas layer — pixel-precise (x, y) placement inside a fixed bounding box.
document.pageFlow().addCanvas(523, 360, canvas -> canvas
.clipPolicy(ClipPolicy.CLIP_BOUNDS)
.position(headline, 0, 60)
.position(rule(503, 1.4, accent), 10, 32));- Template authoring cheatsheet — read this once before writing your own template
- Templates v2 landing — CV / cover-letter / invoice / proposal preset library
- Examples gallery — every runnable example with PDF preview
- Architecture · Lifecycle · Production rendering
- Recipes: shape-as-container · transforms · tables · themes · streaming · extending
- Migration v1.5 → v1.6 · Release process · Contributing
MIT — see LICENSE.

