Skip to content

Latest commit

 

History

History
326 lines (252 loc) · 22.8 KB

File metadata and controls

326 lines (252 loc) · 22.8 KB

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 Maven Central Java 17+ PDFBox 3.0 MIT License

Release status — 🟢 Latest stable: v2.0.0 — the module-first release: the engine is now a lean graph-compose-core with opt-in render-pdf / render-docx backends, while graph-compose stays a drop-in for PDF. Migrating to 2.0 ↓

 ·  ⬆️ Upgrading from 1.x? graph-compose stays a drop-in for PDF with no code change; see the 2.0 modules migration guide  ·  See API stability policy for tier definitions.

Live Showcase  ·  Examples Gallery  ·  Docs  ·  Changelog

GraphCompose render preview

☝ This banner is itself a GraphCompose document — view the full module-first deck (PDF), rendered by EngineDeckV2Example: the 2.0 module graph, 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. Soft panels, accent strips, transforms, native vector charts, and gradients are first-class primitives, not workarounds.
  • Lean core, pluggable backends. The graph-compose-core engine carries no PDFBox or POI; render backends are separate modules discovered via ServiceLoader — PDF is one dependency away (or already included in graph-compose), DOCX/PPTX are opt-in — see support matrix.

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 2.0

The module-first release — the single jar becomes a family of per-concern artifacts, so you install exactly what you render.

  • Lean enginegraph-compose-core is the document model, DSL, themes, and deterministic layout with no PDFBox, POI, or template code on its dependency tree. Backends plug in through a ServiceLoader seam; a core-only classpath asked to render throws MissingBackendException naming the artifact to add.
  • Opt-in render backendsgraph-compose-render-pdf (PDFBox 3.0, full DSL coverage), graph-compose-render-docx and graph-compose-render-pptx (Apache POI, semantic export).
  • graph-compose stays a drop-in — the 1.x coordinate is now a thin wrapper over core + the PDF backend, so existing callers upgrade with no code and no dependency change.
  • Templates are their own artifact — the CV / cover-letter / invoice / proposal preset families moved to graph-compose-templates (imports unchanged). This is the one dependency-level break of the split.
  • graph-compose-bundle — one batteries-included coordinate: PDF stack + templates + fonts + colour emoji.
  • Retired surface — the APIs deprecated across 1.6–1.9 are removed, the layered template packages dropped their .v2 suffix, and BusinessTheme plus the classic pre-layered presets are gone — each removal has a named replacement in the migration guide.

Everything the 1.9 line added — in-document navigation, native TOC and page references, bookmarks, multi-section documents, inline chips / SVG icons / colour emoji, render-to-image — ships unchanged in 2.0. Full history in CHANGELOG.md.

Installation

Requires Java 17+ (enforced by the build).

<dependency>
    <groupId>io.github.demchaav</groupId>
    <artifactId>graph-compose</artifactId>
    <version>2.0.0</version>
</dependency>
dependencies { implementation("io.github.demchaav:graph-compose:2.0.0") }

Which artifact? (2.0 module split). graph-compose above is the drop-in default — it renders PDF out of the box because it aggregates the lean graph-compose-core engine plus the graph-compose-render-pdf backend, so existing 1.x callers upgrade with no code change. Reach for a different coordinate only to take less or more:

Goal Depend on
PDF — the 1.x default graph-compose
Batteries-included (PDF + templates + fonts + emoji) graph-compose-bundle
Lean core, bring your own backend graph-compose-core
Built-in CV / cover-letter / invoice / proposal templates add graph-compose-templates
DOCX / PPTX export add graph-compose-render-docx / graph-compose-render-pptx

Every 2.0 coordinate shares the graph-compose version (the fonts and emoji companions keep their own lines). A bare graph-compose-core renders nothing until a backend is on the classpath — asking it to build a PDF throws MissingBackendException, which names the artifact to add (graph-compose-render-pdf, already included in graph-compose).

Companion artifacts: fonts & colour emoji. Two opt-in companions carry their own version lines (they change on their own cadence, so an engine upgrade never re-downloads them): graph-compose-fonts:1.0.0 — the curated Google font families (~18 MB; pure-text and standard-14 documents need nothing extra; details in the fonts migration note) — and graph-compose-emoji:1.0.0 — inline colour emoji for RichText.emoji(":star:", size) (an unknown shortcode falls back to its literal text, so documents without emoji render unchanged). Both are already included in graph-compose-bundle.

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 1.x? Rendering PDF through graph-compose needs no change at all. If you reached the built-in templates through the single 1.x jar, add graph-compose-templates (imports are unchanged) — the 2.0 migration guide walks every case, including the removed deprecated APIs and their replacements.

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.style.DocumentColor;
import com.demcha.compose.document.style.DocumentTextDecoration;
import com.demcha.compose.document.style.DocumentTextStyle;
import com.demcha.compose.font.FontName;

import java.nio.file.Path;

class Hello {
    public static void main(String[] args) throws Exception {
        // A small inline palette — swap these for your own brand colours.
        DocumentColor cream = DocumentColor.rgb(252, 248, 240);
        DocumentColor panel = DocumentColor.rgb(244, 238, 228);
        DocumentColor accent = DocumentColor.rgb(196, 153, 76);
        DocumentTextStyle h1 = DocumentTextStyle.builder()
                .fontName(FontName.HELVETICA_BOLD).size(28)
                .decoration(DocumentTextDecoration.BOLD)
                .color(DocumentColor.rgb(20, 60, 75)).build();
        DocumentTextStyle body = DocumentTextStyle.builder()
                .fontName(FontName.HELVETICA).size(11)
                .color(DocumentColor.rgb(34, 38, 50)).build();

        try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
                .pageSize(DocumentPageSize.A4)
                .pageBackground(cream)
                .margin(28, 28, 28, 28)
                .create()) {

            document.pageFlow(page -> page
                    .addSection("Hero", section -> section
                            .softPanel(panel, 10, 14)
                            .accentLeft(accent, 4)
                            .addParagraph(p -> p.text("GraphCompose").textStyle(h1))
                            .addParagraph(p -> p.text("A cinematic hero, no manual coordinates.")
                                    .textStyle(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
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.

Text & internationalization

  • Text is laid out left-to-right. Bidirectional (RTL) reordering and complex-script shaping — Arabic contextual joining, Indic reordering — are not performed, so Arabic / Hebrew text renders in logical order without correct visual ordering. Full RTL / bidi support is tracked in #140.
  • A glyph the active font does not cover renders as ? (with a warning logged); load a font that covers the script you need.

When to use GraphCompose

  • Server-side PDF generation in Java — invoices, CVs, reports, proposals, statements, schedules.
  • Templated documents from data — themed presets (ModernProfessional, ModernInvoice, …) you parameterise instead of re-styling every time.
  • Regression-tested layoutsDocumentSession#layoutSnapshot() makes layout changes visible in PRs before any byte ships; PdfVisualRegression adds a pixel-level gate for font and colour fidelity.
  • 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 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()&hellip;assertMatchesBaseline(...) — see visual regression testing
See the live gallery Static showcase site Showcase — source under web/, deployed to GitHub Pages via the Pages workflow

Templates in 2.0 — there is one template surface: the layered preset families in graph-compose-templates, themed through BrandTheme. Arriving from a pre-2.0 surface (classic presets, the built-in *Template classes)? Which template system should I use? maps every retired name to its layered replacement.

Vector primitives in 30 lines

Three snippets from the 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. Since 2.0 that boundary is also a packaging boundary: the surface and engine ship in graph-compose-core, and each render backend is a separate module that registers through a ServiceLoader seam. 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{ServiceLoader}
    E -->|render-pdf| F["PdfFixedLayoutBackend<br/>PDFBox"]
    E -->|render-docx| G["DocxSemanticBackend<br/>POI"]
    D -.->|layoutSnapshot| H["Deterministic snapshot<br/>(regression tests)"]
Loading

Full detail: architecture overview · package map · lifecycle.

Modules

The repository is a Maven multi-module reactor: the root pom.xml is the build aggregator, so ./mvnw clean verify at the root builds and tests every module (scope to one with -pl :<artifactId>; the lean engine lives in core/).

  • Published to Maven Central
    • graph-compose-core (core/) — the lean document engine
    • graph-compose-render-pdf · -render-docx · -render-pptx — render backends
    • graph-compose-templates — built-in CV / cover-letter / invoice / proposal presets
    • graph-compose-testing — snapshot & visual-regression test helpers
    • graph-compose — the drop-in wrapper (core + PDF); graph-compose-bundle — batteries-included (adds templates + fonts + emoji)
  • Companion artifacts (independent version lines) — graph-compose-fonts, graph-compose-emoji
  • Development only (never published) — qa (architecture guards + visual regression), coverage (aggregate JaCoCo), examples, benchmarks

See CONTRIBUTING for the branch-routing table and the full build / verify flow.

Documentation

📚 Full docs index — categorised map of every doc, ADR, and recipe. Start there to navigate the documentation.

Templates

Architecture & operations

Recipes & examples

Contributing & releases

Companion projects

  • graph-compose-markdown — a Markdown → PDF path built on the GraphCompose engine. Hand it a Markdown document and it renders through the same layout, theme, and PDFBox pipeline as the Java DSL — a companion input surface for teams who would rather author in Markdown than call the DSL directly. Published on Maven Central as io.github.demchaav:graph-compose-markdown; independent lifecycle, consumes the engine as a dependency.
  • 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.

Star History

Star History Chart