Declarative Java DSL for structured business PDFs.
Describe what the document says; the engine resolves layout, pagination, themes, and PDFBox rendering. Cinematic by default.
Release status β π’ Latest stable: v1.9.0 β codenamed "navigable": in-document navigation (anchors, internal links, a clickable TOC, page references & bookmarks), multi-section documents, and inline chips / SVG icons / colour emoji. What's new in v1.9 β
Β· π‘ In develop: next cycle β open (see CHANGELOG) Β· See API stability policy for tier definitions.
Live Showcase Β· Examples Gallery Β· Docs Β· Changelog
β This banner is itself a GraphCompose document β view the full capability deck (PDF), rendered by EngineDeckExample: native vector charts and real comparative benchmarks, all drawn by the engine. It renders its own marketing.
Why GraphCompose
- 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, native vector charts, and gradients 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.
What's new in v1.9
The "navigable" release β a rendered PDF becomes a document you can move through.
- In-document navigation β named
anchor(...)targets and internallink(...)jumps emitted as native PDFGoToactions: clickable cross-references,[text](#heading)-style links, and bidirectional footnotes (DocumentLinkTargetunifies internal and external links). - Native table of contents & page references β
addTableOfContents(toc -> toc.entry(label, anchor))builds a clickable TOC with dot leaders and auto-resolved page numbers;addPageReference(anchor)prints a native "see page N" cross-reference;DocumentSession.pageIndex()resolves any anchor to its page. - Bookmarks & viewer preferences β
section.bookmark(...)makes any section or container a PDF outline (bookmark-panel) target, andchrome().viewerPreferences(...)opens the reader on the outline panel, a chosen page layout, or the doc title in the window. - Multi-section documents β
GraphCompose.documents()concatenates independently authored sections β each with its own page size, margins, and footer numbering β into one PDF, with anchors, links, and the outline resolving across section boundaries. - Richer row & page layout β
row.columns(auto(), weight(1), fixed(80)), main-axisflexSpacer()/arrangement(...), cross-axisverticalAlign(...), per-pagepageMargins(...), and full-bleedbleed(...). - Inline chips, SVG icons & colour emoji β text on a rounded highlight chip, a parsed
SvgIconon the text baseline, andRichText.emoji(":star:", size)colour emoji via the new independently-versionedgraph-compose-emojimodule. - Render straight to images β
DocumentSessionrenders directly toBufferedImages with no PDF round-trip; plus page-number offset / restart / style and round / dotted line caps.
Core document APIs stay source- and binary-compatible with v1.8 β v1.9 is purely additive (two cover-letter / CV shim types are newly @Deprecated for 2.0). Full notes in CHANGELOG.md.
Installation
Requires Java 17+ (enforced by the build).
<dependency> <groupId>io.github.demchaav</groupId> <artifactId>graph-compose</artifactId> <version>1.9.0</version> </dependency>
dependencies { implementation("io.github.demchaav:graph-compose:1.9.0") }Bundled fonts (from v1.8.0). The curated Google fonts no longer ship inside the engine jar β they live in an independently-versioned companion artifact so an engine upgrade never re-downloads ~18 MB of fonts. Pure-text and standard-14 documents need nothing extra; to use the bundled families, add:
<dependency> <groupId>io.github.demchaav</groupId> <artifactId>graph-compose-fonts</artifactId> <version>1.0.0</version> </dependency>Prefer a single "batteries-included" coordinate? Depend on
io.github.demchaav:graph-compose-bundle(same version asgraph-composeabove) to pull the engine + fonts together. Full details and upgrade steps: the v1.8.0 fonts migration note.
Colour emoji (from v1.9.0).
RichText.emoji(":star:", size)resolves GitHub-style shortcodes to inline vector glyphs from an independently-versioned companion artifact (the same split model as the fonts above). Text without emoji needs nothing extra; to render colour emoji, add:<dependency> <groupId>io.github.demchaav</groupId> <artifactId>graph-compose-emoji</artifactId> <version>1.0.0</version> </dependency>An unknown shortcode falls back to its literal text, so a document that uses no emoji β or runs without the artifact β renders unchanged. The
graph-compose-bundlestays fonts-only; emoji is opt-in.
Distribution β Maven Central is the canonical channel from v1.6.6 onwards (
io.github.demchaav:graph-compose:<version>). Hosted Javadocs auto-publish to javadoc.io/doc/io.github.demchaav/graph-compose shortly after each Central release. The legacy JitPack URL (com.github.DemchaAV:GraphCompose:v<version>) remains resolvable for callers pinned to v1.6.5 and earlier but is no longer the documented install option.
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.
Hello world
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.
Scope and comparison
Output support
| 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. |
When to use GraphCompose
- 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;PdfVisualRegressionadds a pixel-level gate for font and colour fidelity. - 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.
What GraphCompose is not
- 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.
Compared with similar Java libraries
| 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 | Object/layout API + low-level canvas | Automatic layout with direct-positioning options | 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.
Which API should I use?
| You want to⦠| Surface | Entry point |
|---|---|---|
| Generate a one-off PDF programmatically | DSL | GraphCompose.document(...).pageFlow(...) β see Hello world above |
| Generate a CV / cover letter from data | Layered templates | ModernProfessional.create().compose(session, cvDocument) β see layered templates |
| Add a custom visual primitive | Engine extension | NodeDefinition + PdfFragmentRenderHandler β see extension guide |
| Regression-test generated layouts | Layout snapshots | DocumentSession#layoutSnapshot() β quickstart at Testing your document; full reference at snapshot testing |
| Pixel-test the rendered PDF (fonts, colours, anti-aliasing) | Visual regression | PdfVisualRegression.standard()…assertMatchesBaseline(...) β see visual regression testing |
| See the live gallery | Static showcase site | Showcase β source under web/, deployed to GitHub Pages via the Pages workflow |
Choosing a template surface β layered (
cv.v2), classic (cv.presets), or the built-in*TemplateV2family? See Which template system should I use? for the status matrix, decision tree, andclassic β layeredmigration map.
v1.8 primitives in 30 lines
Three snippets from the new vector surfaces. Full runnable versions live in the examples gallery.
Native chart β categories + series in, native vector bars out (no rasterization).
ChartData revenue = ChartData.builder() .categories("Q1", "Q2", "Q3", "Q4") .series("2024", 12.4, 15.1, 9.8, 14.2) .series("2025", 14.0, 18.2, 11.3, 16.9) .build(); section.chart(ChartSpec.bar().data(revenue) .legend(LegendPosition.BOTTOM) .size(ChartSize.aspectRatio(16, 7)) .build());
Overshoot-free line β a smooth curve constrained to never overshoot the data range.
section.chart(ChartSpec.line().data(series) .interpolation(LineInterpolation.MONOTONE) .build());
SVG import + alignment β parse SVG to native geometry, seat any fixed node across the width.
SvgIcon globe = SvgIcon.parse(svgMarkup); flow.addSvgIcon(globe, 48, HorizontalAlign.CENTER); flow.addAligned(HorizontalAlign.RIGHT, anyFixedNode);
Architecture
GraphCompose splits into a public canonical surface you author against (com.demcha.compose.document.*) and an internal shared engine foundation (com.demcha.compose.engine.*, marked @Internal) that resolves geometry, pagination, and rendering behind it. You author intent; the engine resolves the rest.
flowchart LR
A["GraphCompose.document(...)<br/>DocumentSession Β· DocumentDsl"] --> B["DocumentNode tree<br/>document.node"]
B --> C["LayoutCompiler<br/>document.layout"]
C --> D["Engine foundation @Internal<br/>measure β paginate β place"]
D --> E{Backend}
E -->|PDF| F["PdfFixedLayoutBackend"]
E -->|DOCX| G["DocxSemanticBackend Β· POI"]
D -.->|layoutSnapshot| H["Deterministic snapshot<br/>(regression tests)"]
Full detail: architecture overview Β· package map Β· lifecycle.
Documentation
π Full docs index β categorised map of every doc, ADR, and recipe. Start there to navigate the documentation.
Templates
- π Templates β v2 layered architecture β the canonical going-forward pattern for new template families (CV v2 is the reference implementation). Personas: quickstart Β· using templates Β· authoring presets Β· contributing a new family.
- Templates v1-classic landing β the older
BusinessTheme/CvSpecCV / cover-letter / invoice / proposal preset library (deprecated β CV + cover letter are superseded by v2-layered; invoice / proposal / schedule are not yet ported). Cheat sheet: authoring.
Architecture & operations
- Architecture overview Β· Lifecycle Β· Production rendering Β· Layout snapshot testing Β· Troubleshooting
Recipes & examples
- Recipes index β shape-as-container Β· shapes Β· transforms Β· page-backgrounds Β· layered-page-design Β· absolute-placement Β· tables Β· themes Β· streaming Β· extending Β· font-coverage
- Examples gallery β every runnable example with PDF preview
Contributing & releases
- Contributing Β· Code of conduct Β· Security policy Β· Release process
- API stability policy Β· Which template system? Β· Migration v1.6 β v1.7 Β· Migration v1.5 β v1.6
Companion projects
- graphcompose-ai-flow β experimental sister project exploring an AI-assisted authoring flow on top of GraphCompose. Independent codebase, separate lifecycle β nothing in this repo depends on it. Track it if you are interested in agentic document composition driven by the same semantic node model.
License
MIT β see LICENSE.

