Skip to content

DemchaAV/GraphCompose

GraphCompose

GraphCompose logo

Declarative Java DSL for structured business PDFs.
Describe what the document says; the engine resolves layout, pagination, themes, and PDFBox rendering. Cinematic by default.

CI Latest release JitPack Java 17+ PDFBox 3.0 MIT License

Live Showcase  ·  Examples Gallery  ·  Changelog

GraphCompose render preview

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 + 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.

Scope and comparison

Output support

Format Status Notes
PDF 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 layoutsDocumentSession#layoutSnapshot() makes layout changes visible in PRs before any byte ships.
  • Streaming PDFs from web backends — Spring Boot @RestController writing 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 .jasper files.
  • 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 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.

Which API should I use?

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

Installation

<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.

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.

What's in v1.6 — "expressive"

  • Templates v2 — 14 CV and 14 paired cover-letter presets, theme-driven via BusinessTheme, one-liner create(theme) factories. Inline markdown, slot-based multi-column layouts. See docs/templates-v2.md.
  • Composed primitivesListBuilder.addItem(label, Consumer) (nested lists), DocumentTableCell.node(...) (any node inside a cell), CanvasLayerNode (pixel-precise free-canvas placement).
  • Architecture hardening@Internal API stability marker, public PdfFragmentRenderHandler SPI, DocumentRenderingException on the convenience render path, documented thread-safety contract.

Full notes in CHANGELOG.md. Upgrade guide: docs/migration-v1-5-to-v1-6.md.

v1.6 primitives in 30 lines

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));

Documentation

License

MIT — see LICENSE.