Skip to content

DemchaAV/GraphCompose

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,294 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GraphCompose

GraphCompose logo

Declarative Java DSL for structured business documents.
Describe what the document says; the engine resolves layout, pagination, themes, and backend rendering — print-ready PDF first, an editable PowerPoint deck from the same source. Cinematic by default.

CI Latest release Maven Central Java 17+ PDFBox 3.0 — PDF backend Apache POI 5.5 — PPTX and DOCX backends MIT License

Release status — 🟢 Latest stable: v2.1.0 — the PowerPoint release: graph-compose-render-pptx turns the same resolved layout into an editable deck — one page per slide, geometry-identical to the PDF, text and panels as native shapes. Ships as @Beta. What each backend supports ↓

 ·  ⬆️ 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.

One source → a PDF and an editable PowerPoint deck

The same DocumentSession emits both. The PDF backend prints the resolved layout; the PPTX backend (beta) rebuilds it as slides. Both consume the same resolved layout graph, so page and slide frames and every positioned element share the same geometry — text, panels, tables, and vectors arrive in PowerPoint as native, editable shapes, not screenshots (the page below lands as 69 native shapes; only its clip-masked logo art is a picture). Glyphs are rasterised by the viewer, so the exact text rendering depends on the fonts installed on the viewing machine; see the backend capability matrix for per-feature fidelity.

Path deck = Path.of("twin-output.pptx");
try (DocumentSession doc = GraphCompose.document(Path.of("twin-output.pdf"))
        .pageSize(DocumentPageSize.SLIDE_16_9)
        .create()) {
    compose(doc);        // one description
    doc.buildPdf();      // print-ready PDF
    doc.buildPptx(deck); // editable PowerPoint
}
twin-output.pdf — rendered by the PDF backend twin-output.pptx — the same page, as PowerPoint itself renders it
The twin-output page rendered from the PDF The same page exported as a PNG by PowerPoint from the generated .pptx

The generated deck open in PowerPoint with the headline text frame selected for editing

☝ The generated deck open in PowerPoint — the headline is a selected, editable text frame, and the ribbon is live because the slide is built from native shapes. Artifacts: PDF · PPTX · source (TwinOutputExample, ~370 lines, page included).

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-pptx (Apache POI, geometry-identical PowerPoint decks from the same resolved layout — one page per editable slide; clipped regions land as pixel-exact pictures; ships as beta in its first release), graph-compose-render-docx (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.1.0</version>
</dependency>
dependencies { implementation("io.github.demchaav:graph-compose:2.1.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
PowerPoint deck, geometry-identical to the PDF add graph-compose-render-pptx
DOCX export (semantic) add graph-compose-render-docx

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 — paragraphs, lists, block images, tables and metadata. Word owns the flow, so drawing nodes (shape, line, ellipse, barcode) are dropped, one logged warning per kind. Hyperlinks, bookmarks and headers/footers are not implemented, table colSpan/rowSpan is not applied, and image fit modes are ignored — see render-docx.
PPTX Beta Fixed-layout export via Apache POI from the same resolved layout — one page per editable slide with native shapes and text frames; clipped regions land as pixel-exact pictures. First shipped in 2.1, marked @Beta while the API shape settles.

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. The fixed-layout backends (PDF, PPTX) register through a ServiceLoader seam and consume the same resolved LayoutGraph, which is why a deck matches the PDF geometrically. The semantic DOCX exporter registers nothing — you name it directly, and it walks the node tree without a layout pass. 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-pptx| G["PptxFixedLayoutBackend<br/>POI · same LayoutGraph as the PDF"]
    B -.->|render-docx · named directly| I["DocxSemanticBackend<br/>POI · no layout pass"]
    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.

About

Declarative Java engine for structured business documents — one session renders a print-ready PDF and a geometry-identical, editable PowerPoint deck. Semantic layout, atomic pagination, theme tokens, native charts, SVG & gradients, deterministic, PDFBox-backed

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages