|
| 1 | +# Your First Document |
| 2 | + |
| 3 | +A five-minute path from an empty project to a real PDF. GraphCompose is |
| 4 | +session-first: you open a `DocumentSession`, describe content in reading order |
| 5 | +with a page flow, and render. No coordinates, no manual page breaks. |
| 6 | + |
| 7 | +> **Prerequisites:** Java 17+ and the `io.github.demchaav:graph-compose` |
| 8 | +> dependency — see the [README install snippet](../README.md#installation). |
| 9 | +
|
| 10 | +## The smallest document |
| 11 | + |
| 12 | +Open a session for a file path, add one page flow, render. The engine handles |
| 13 | +placement and pagination. |
| 14 | + |
| 15 | +```java |
| 16 | +import com.demcha.compose.GraphCompose; |
| 17 | +import com.demcha.compose.document.api.DocumentPageSize; |
| 18 | +import com.demcha.compose.document.api.DocumentSession; |
| 19 | + |
| 20 | +import java.nio.file.Path; |
| 21 | + |
| 22 | +try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf")) |
| 23 | + .pageSize(DocumentPageSize.A4) |
| 24 | + .margin(24, 24, 24, 24) |
| 25 | + .create()) { |
| 26 | + |
| 27 | + document.pageFlow(page -> page |
| 28 | + .module("Summary", module -> module.paragraph("Hello GraphCompose"))); |
| 29 | + |
| 30 | + document.buildPdf(); |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +`GraphCompose.document(path)` configures the output; `create()` returns the |
| 35 | +`DocumentSession`. Use try-with-resources so the session is always released, even |
| 36 | +if rendering fails. Inside the session, `pageFlow(...)` is the document body: |
| 37 | +modules, sections, paragraphs, lists, tables, and rows are added top to bottom. |
| 38 | + |
| 39 | +## A real custom document |
| 40 | + |
| 41 | +The same Flow model scales to a multi-section document. There are still no |
| 42 | +coordinates and no manual page breaks — just structure in reading order. |
| 43 | + |
| 44 | +```java |
| 45 | +import com.demcha.compose.GraphCompose; |
| 46 | +import com.demcha.compose.document.api.DocumentPageSize; |
| 47 | +import com.demcha.compose.document.api.DocumentSession; |
| 48 | + |
| 49 | +import java.nio.file.Path; |
| 50 | + |
| 51 | +try (DocumentSession document = GraphCompose.document(Path.of("profile.pdf")) |
| 52 | + .pageSize(DocumentPageSize.A4) |
| 53 | + .margin(24, 24, 24, 24) |
| 54 | + .create()) { |
| 55 | + |
| 56 | + document.pageFlow() |
| 57 | + .name("CandidateProfile") |
| 58 | + .spacing(12) |
| 59 | + .module("Professional Summary", module -> module.paragraph( |
| 60 | + "Backend engineer focused on clean Java APIs, stable document " |
| 61 | + + "output, and reusable template architecture.")) |
| 62 | + .module("Technical Skills", module -> module.bullets( |
| 63 | + "Java 21 and Spring Boot", |
| 64 | + "PDF document generation with GraphCompose", |
| 65 | + "Layout snapshot testing and render regression checks")) |
| 66 | + .module("Projects", module -> module.rows( |
| 67 | + "GraphCompose - declarative document layout engine.", |
| 68 | + "CVRewriter - profile-aware CV tailoring platform.")) |
| 69 | + .build(); |
| 70 | + |
| 71 | + document.buildPdf(); |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +The callback form (`pageFlow(page -> ...)`) builds and attaches the root for you. |
| 76 | +The builder form (`pageFlow().…build()`) gives you the fluent chain but you must |
| 77 | +call `.build()` yourself. |
| 78 | + |
| 79 | +## Already a known document? Use a template |
| 80 | + |
| 81 | +If your document is a known family — invoice, proposal, CV, cover letter — do not |
| 82 | +hand-build it. A maintained template maps a typed data object into the same |
| 83 | +session, then you render as usual: |
| 84 | + |
| 85 | +```java |
| 86 | +import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2; |
| 87 | +import com.demcha.compose.document.theme.BusinessTheme; |
| 88 | + |
| 89 | +InvoiceTemplateV2 template = new InvoiceTemplateV2(BusinessTheme.modern()); |
| 90 | + |
| 91 | +try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).create()) { |
| 92 | + template.compose(document, invoice); // invoice = your InvoiceDocumentSpec |
| 93 | + document.buildPdf(); |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +Templates and hand-written Flow compose into the *same* `DocumentSession`, so you |
| 98 | +can mix them. To choose a template surface, see |
| 99 | +[Which template system should I use?](templates/which-template-system.md). |
| 100 | + |
| 101 | +## Rendering on a server |
| 102 | + |
| 103 | +When the caller already owns the output stream — an HTTP response, a cloud |
| 104 | +upload — create the session *without* a default path and stream the PDF with |
| 105 | +`writePdf(OutputStream)` instead of `buildPdf()`. GraphCompose writes the |
| 106 | +stream but does not close it. For the full server snippet, see |
| 107 | +[Getting started — Streaming output](getting-started.md#streaming-output). |
| 108 | + |
| 109 | +Create one `DocumentSession` per render request; it is mutable and not |
| 110 | +thread-safe. Use `toPdfBytes()` only when the caller truly needs a byte array. |
| 111 | + |
| 112 | +## Where to go next |
| 113 | + |
| 114 | +- [Getting Started](getting-started.md) — themes, hero blocks, layer stacks, |
| 115 | + shape-as-container, and built-in templates. |
| 116 | +- [Recipes](recipes.md) — themes, shapes, transforms, tables, and layout |
| 117 | + snapshots. |
| 118 | +- [Which template system should I use?](templates/which-template-system.md) — |
| 119 | + the decision tree for CV / invoice / proposal surfaces. |
| 120 | +- [Production Rendering](operations/production-rendering.md) — server-side |
| 121 | + lifecycle, streaming, and load guidance. |
0 commit comments