GraphCompose v1.5 uses the canonical session-first API. Application
code starts with GraphCompose.document(...), creates one
DocumentSession, describes content with DocumentDsl, and finishes
with writePdf(...), buildPdf(), or toPdfBytes().
GraphCompose has two layers a caller can target. Use this decision tree to choose the right one for the document you're rendering.
| Question | Answer | Pick this layer |
|---|---|---|
| Is your document one of the built-in shapes (CV, invoice, proposal, weekly schedule, cover letter)? | Yes | Built-in template. Skip ahead to "Built-in templates". |
| Do you need pixel-level control over a one-off PDF? | Yes | Raw DSL (DocumentSession.pageFlow(...)). |
| Do you need a re-usable scene for a new business document type? | Yes | Custom template that wraps the DSL. Implement the *Template interface and re-use BusinessTheme for visual coherence. |
The DSL and the templates compose against the SAME DocumentSession
— a template can also live alongside hand-written DSL inside one
session, so you don't have to commit to one layer per document.
The shortest path to a real PDF: open a session, drop a soft-panel
hero on the page, render. The BusinessTheme keeps the look
consistent with the rest of your branded documents.
import com.demcha.compose.GraphCompose;
import com.demcha.compose.document.api.DocumentSession;
import com.demcha.compose.document.theme.BusinessTheme;
import java.nio.file.Path;
BusinessTheme theme = BusinessTheme.modern(); // cream paper + teal/gold
try (DocumentSession document = GraphCompose.document(Path.of("output.pdf"))
.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("Quick-start hero block."))
.module("Summary", module -> module.paragraph(
"GraphCompose composes a document graph and renders it twice — "
+ "once as a deterministic layout snapshot, once as the final PDF.")));
document.buildPdf();
}Use writePdf(OutputStream) for web APIs, cloud storage uploads, and
other server paths where the caller already owns an output stream.
GraphCompose writes the PDF but does not close the stream.
void writeResponse(OutputStream responseOutputStream) throws Exception {
try (DocumentSession document = GraphCompose.document().create()) {
document.pageFlow(page -> page
.module("Summary", module -> module.paragraph("Generated for an HTTP response.")));
document.writePdf(responseOutputStream);
}
}byte[] pdfBytes;
try (DocumentSession document = GraphCompose.document().create()) {
document.pageFlow(page -> page
.module("Summary", module -> module.paragraph("Generated for an HTTP response.")));
pdfBytes = document.toPdfBytes();
}toPdfBytes() is a convenience wrapper around the streaming path.
Prefer writePdf(...) when the next step is already a stream.
Guide lines are a render-only diagnostic overlay for checking page margins, padding, and resolved boxes. They do not change layout geometry or layout snapshots.
try (DocumentSession document = GraphCompose.document(Path.of("debug.pdf"))
.guideLines(true)
.create()) {
document.pageFlow(page -> page
.module("Summary", module -> module.paragraph("Guide-line preview")));
document.buildPdf();
}You can also toggle the same option on an open session before convenience PDF output:
document.guideLines(true);
byte[] debugPdf = document.toPdfBytes();Use modules when you are building a normal document section. A module is a titled full-width block with a body made from semantic content.
document.pageFlow(page -> page
.module("Professional Summary", module -> module.paragraph(summary))
.module("Technical Skills", module -> module.bullets(skills))
.module("Projects", module -> module.rows(projectRows)));The common body calls are paragraph, bullets, dashList, rows,
table, image, divider, and pageBreak.
addLayerStack(...) composes children inside the same bounding box,
in source order — first child behind, last in front. Each layer
carries one of nine LayerAlign values. The builder also exposes
nine alignment-named shortcuts (topLeft, topCenter, …,
bottomRight) plus position(node, offsetX, offsetY, anchor) for
screen-space nudges. Pagination is atomic: the entire stack moves to
the next page if it does not fit.
import com.demcha.compose.document.dsl.ParagraphBuilder;
import com.demcha.compose.document.node.ShapeNode;
document.pageFlow(page -> page
.addLayerStack(stack -> stack
.name("HeroBadge")
.back(new ShapeNode(...)) // background
.center(new ParagraphBuilder().text("M&A").build()) // foreground
.topRight(badge) // overlay anchor
.position(stamp, -8, 4, LayerAlign.BOTTOM_LEFT))); // anchor + offsetaddCircle(diameter, fill, inside) /
addEllipse(w, h, fill, inside) / addContainer(...) build a
ShapeContainerNode whose bounding box is dictated by the outline
(rectangle, rounded rectangle, ellipse, or circle). Children are
clipped to the outline path (ClipPolicy.CLIP_PATH is the default),
to the bounding box (CLIP_BOUNDS), or render unclipped
(OVERFLOW_VISIBLE).
document.pageFlow(page -> page
.addCircle(80, brand, circle -> circle
.name("BrandSeal")
.center(new ParagraphBuilder().text("M&A").build())));The PDF backend honours every clip policy via graphics-state
saveGraphicsState() / clip(path) / restoreGraphicsState() markers
emitted by the layout layer; the DOCX backend renders layers inline
without the outline frame and logs a one-time capability warning. See
docs/recipes/shape-as-container.md
for the full recipe.
Built-ins compose into the same DocumentSession. Template data
lives under com.demcha.compose.document.templates.data.*, and
templates live under
com.demcha.compose.document.templates.builtins.
InvoiceTemplate template = new InvoiceTemplateV1();
try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).create()) {
template.compose(document, invoice);
document.buildPdf();
}- Recipes — themes, shapes, transforms, tables, layout snapshots splits the long-form copy-paste catalogue into focused pages.
- Shape-as-container — circles, ellipses, rounded cards with clipped layers.
- Transforms and z-index — rotate, scale, per-layer z-index for overlays.
- Advanced tables — row span, zebra rows, totals row, repeating header on page break.
- Canonical Legacy-Parity Matrix — what
works today, what is
Partial, what isPlanned. - Lifecycle — the session, layout, and render flow.
- Production Rendering — server-side lifecycle, privacy, and load guidance.
- Package Map — read this before adding new public APIs or engine internals.