From 3259658afe51e582fc8c73d6c6395a325a5ca840 Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Thu, 10 Sep 2026 12:17:35 +0100
Subject: [PATCH 1/5] docs: simplify onboarding and feature discovery
---
README.md | 502 ++++++++----------
docs/README.md | 62 ++-
docs/capabilities.md | 39 +-
docs/first-document.md | 57 +-
docs/operations/test-your-document.md | 4 +-
docs/recipes.md | 70 ++-
docs/recipes/charts.md | 85 ++-
docs/recipes/tables.md | 52 +-
.../templates/v2-layered/authoring-presets.md | 18 +-
9 files changed, 501 insertions(+), 388 deletions(-)
diff --git a/README.md b/README.md
index b30d4193f..c89bc261c 100644
--- a/README.md
+++ b/README.md
@@ -5,8 +5,9 @@
- 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.
+ Build production PDF documents in Java without calculating coordinates.
+ Compose content in reading order, let GraphCompose handle layout and pagination,
+ then protect important templates with layout snapshots and visual diffs.
@@ -14,8 +15,6 @@
-
-
@@ -24,36 +23,38 @@
> Β· π‘ **In development**: v2.4.0 on `develop` — see [CHANGELOG.md](./CHANGELOG.md).
- Live Showcase
- Β·
- Examples Gallery
- Β·
- Docs
- Β·
- Changelog
+
-
+ Live showcase
+ Β·
+ Examples gallery
-
- β This banner is itself a GraphCompose document β read all five pages (PDF), rendered by MavenBannerPptxExample: how a document is authored, how it measures against the field, how it scales, and Hebrew and Arabic running right to left. The charts are native vector output, and the same file exports as an editable PowerPoint deck. It renders its own marketing.
-
+## What it is
-## Why GraphCompose
+GraphCompose is a code-first document layout library for Java. You describe the
+document β paragraphs, sections, lists, tables, images β and the engine measures,
+wraps, places, and paginates it.
-- **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](#output-support).
-- **Writes in more than one direction.** Hebrew and Arabic lay out through the Unicode Bidirectional Algorithm, Arabic is shaped into its joined forms, and a paragraph or a table cell says which way it runs with `direction(RTL)` — or `AUTO`, read off the text. The same document does it in **all three formats**: the PDF is painted, so the engine resolves the line itself; Word and PowerPoint have bidirectional engines of their own and are told what each needs instead. Five script families ship in `graph-compose-fonts` — Arabic, Hebrew, Georgian, Armenian, Korean — so a mixed page renders with no font hunting ([preview](assets/readme/examples/world-scripts.pdf)).
+- **Higher-level than PDFBox:** no manual `x`/`y` calculations for normal document flow.
+- **Lighter than a reporting platform:** typed Java, no XML templates or datasource language.
+- **Built for change:** deterministic geometry snapshots and pixel-level PDF diffs can
+ catch an invoice, CV, or report drifting before the change reaches production.
-Sits between **iText** (low-level page primitives) and **JasperReports** (XML-template-driven layout): a Java DSL describes the document semantically, the engine renders.
+Use it for server-side invoices, proposals, CVs, reports, statements, schedules,
+and other documents assembled from application data.
-## Installation
+
+## Create your first PDF
-**Requires Java 17+** (enforced by the build).
+You need one dependency and one Java file. You do not need to understand the engine,
+backends, templates, or repository modules first.
+
+### 1. Add GraphCompose
+
+Maven:
```xml
@@ -63,74 +64,20 @@ Sits between **iText** (low-level page primitives) and **JasperReports** (XML-te
```
+Gradle:
+
```kotlin
dependencies { implementation("io.github.demchaav:graph-compose:2.3.0") }
```
-That coordinate renders PDF out of the box: 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**.
+The `graph-compose` artifact already includes PDF output.
-> β¬οΈ **Upgrading from 1.x?** `graph-compose` stays a drop-in for PDF with no code change; see the [2.0 modules migration guide](./docs/migration/v2.0.0-modules.md).
-> Β· See [API stability policy](./docs/api-stability.md) for tier definitions.
-
-
-Which artifact? — the 2.0 module split, when you want 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 β opening a session (`create()`) throws `MissingBackendException`, which
-names the artifact to add (`graph-compose-render-pdf`, already included in
-`graph-compose`).
-
-
-
-
-Bundled fonts & colour emoji — optional companions
-
-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.1.0` — the curated Google font families (~20 MB).
- Pure-text and standard-14 documents need nothing extra; details in the
- [fonts migration note](./docs/migration/v1.8.0-fonts.md).
-- `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, hosted Javadocs, legacy JitPack
-
-Maven Central is the canonical channel from **v1.6.6** onwards
-(`io.github.demchaav:graph-compose:`). Hosted Javadocs for the engine API
-publish to
-[javadoc.io/doc/io.github.demchaav/graph-compose](https://javadoc.io/doc/io.github.demchaav/graph-compose)
-shortly after each Central release, from **v2.1.1** onwards β the 2.0 and 2.1.0
-releases of that coordinate shipped no javadoc artifact, so the page there still
-renders the 1.9.1 API until the next release lands. The legacy JitPack URL
-(`com.github.DemchaAV:GraphCompose:v`) remains resolvable for callers
-pinned to v1.6.5 and earlier but is no longer the documented install option.
-
-
-
-## Hello world
+
+### 2. Copy this complete file
```java
import com.demcha.compose.GraphCompose;
-import com.demcha.compose.document.api.DocumentPageSize;
import com.demcha.compose.document.api.DocumentSession;
import java.nio.file.Path;
@@ -138,12 +85,12 @@ import java.nio.file.Path;
class Hello {
public static void main(String[] args) throws Exception {
try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
- .pageSize(DocumentPageSize.A4)
- .margin(24, 24, 24, 24)
+ .margin(48, 48, 48, 48)
.create()) {
document.pageFlow(page -> page
- .module("Summary", module -> module.paragraph("Hello GraphCompose")));
+ .addParagraph("Hello GraphCompose")
+ .addParagraph("This PDF was created without manual coordinates."));
document.buildPdf();
}
@@ -151,272 +98,251 @@ class Hello {
}
```
-Save it as `Hello.java` and run it β that is the whole file. `create()` opens the session,
-`pageFlow` describes the page, `buildPdf()` writes it: no coordinates, no page-break
-arithmetic.
-
-### Make it cinematic
+Run `Hello.main()`. The file `hello.pdf` appears in the working directory.
-The same page with the engine's visual primitives: a page background, a soft panel, an
-accent strip and two text styles. Nothing here is a workaround β panels and accents are
-nodes, and the engine places them. [`SectionPresetsExample`](./examples/src/main/java/com/demcha/examples/features/text/SectionPresetsExample.java)
-renders the whole family; its output is committed as
-[section-presets.pdf](./assets/readme/examples/section-presets.pdf).
+That program contains the whole basic model:
-
-```java
-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;
+```text
+GraphCompose.document(...) β pageFlow(...) β content blocks β buildPdf()
+```
-import java.nio.file.Path;
+- `GraphCompose.document(...)` chooses the output and page settings.
+- `pageFlow(...)` is ordinary top-to-bottom content.
+- Block methods add content in reading order; the engine handles wrapping and page breaks.
+- `buildPdf()` writes the result.
-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).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();
+Styling is optional. Introduce `DocumentTextStyle` after the document structure says
+what it needs to say.
- try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
- .pageSize(DocumentPageSize.A4)
- .pageBackground(cream)
- .margin(28, 28, 28, 28)
- .create()) {
+## Grow it into a real document
- 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))));
+A larger document uses the same flow. Group related content into named modules and
+feed them your application data; coordinates still do not enter the authoring code.
- document.buildPdf();
- }
- }
-}
+
+```java
+document.pageFlow(page -> page
+ .module("Summary", module -> module.paragraph(summary))
+ .module("Skills", module -> module.bullets(skills))
+ .module("Projects", module -> module.rows(projects)));
```
-For a Spring Boot `@RestController` streaming the PDF straight to the response, see [`HttpStreamingExample`](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java).
+### Pick the next thing you need
-### Next steps
+The same flow accepts the common building blocks directly. You do not need a new
+document model for each feature.
-- [**Your first document**](./docs/first-document.md) — the five-minute path from an empty project to a rendered PDF.
-- [**Getting started**](./docs/getting-started.md) — DSL or templates, and how to choose; the first-render walk-through.
-- [**Examples gallery**](./examples/README.md) — every runnable example, with a PDF you can preview without building anything.
-
-## One source β a PDF and an editable PowerPoint deck
+| Area | Includes | Open |
+|---|---|---|
+| **Content and data** | rich text, links, lists, tables, charts, timelines, images, SVG icons, emoji, QR and barcodes | [Find a content block](./docs/recipes.md#content-and-data) |
+| **Layout and visual composition** | sections, cards, rows, layers, clipping, shapes, backgrounds, transforms, canvases, pagination rules | [Choose a layout primitive](./docs/recipes.md#layout-and-visual-composition) |
+| **Page behaviour and output** | headers, footers, page numbers, watermarks, metadata, navigation, previews, streaming, DOCX | [Configure the document](./docs/recipes.md#page-behaviour-output-and-development) |
+| **Development confidence** | guide lines, node labels, layout snapshots, PDF visual diffs | [Test and debug it](./docs/operations/test-your-document.md) |
-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](docs/architecture/backend-capability-matrix.md) for per-feature fidelity.
+The [recipe index](./docs/recipes.md) is the complete task map. Start from its
+smallest entry point, then open one focused guide for the styling, layout,
+pagination, backend, and testing details that matter to that feature.
-PowerPoint output needs `graph-compose-render-pptx` on the classpath in addition to `graph-compose`; without it `buildPptx` fails with a `MissingBackendException` naming the artifact. See [Which artifact?](#installation) above.
+For example, a labelled bar chart is one more block in `pageFlow`:
-
+
```java
-Path deck = Path.of("twin-output.pptx");
-try (DocumentSession doc = GraphCompose.document(Path.of("twin-output.pdf"))
- .pageSize(DocumentPageSize.SLIDE_16_9)
- .create()) {
- // β¦ describe the page (see Hello world above)
- doc.buildPdf(); // print-ready PDF
- doc.buildPptx(deck); // editable PowerPoint
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.chart.ChartData;
+import com.demcha.compose.document.chart.ChartSpec;
+import com.demcha.compose.document.chart.ValueLabelMode;
+
+import java.nio.file.Path;
+
+ChartData revenue = ChartData.builder()
+ .categories("Q1", "Q2", "Q3", "Q4")
+ .series("Revenue", 12.4, 15.1, 9.8, 14.2)
+ .build();
+
+try (DocumentSession document = GraphCompose.document(Path.of("revenue.pdf")).create()) {
+ document.pageFlow(page -> page
+ .addParagraph("Quarterly revenue")
+ .chart(ChartSpec.bar()
+ .data(revenue)
+ .valueLabels(ValueLabelMode.OUTSIDE)
+ .build()));
+ document.buildPdf();
}
```
-
-
-| twin-output.pdf β rendered by the PDF backend |
-twin-output.pptx β the same page, as PowerPoint itself renders it |
-
-
- |
- |
-
-
+From there, the [chart recipe](./docs/recipes/charts.md) shows the next questions in
+order: value formatting, legends, bar/line shape, colours, grid, labels, background,
+and snapshot coverage.
-
-
-
-
- β 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, one page, source included).
-
-
-## What's new in 2.0
+### Choose a longer path only when you need it
-The **module-first** release: the single jar became a family of per-concern artifacts, so you install exactly what you render, and `graph-compose` stayed a drop-in for PDF callers. Everything the 1.9 line added ships unchanged.
+1. Follow [Your first document](./docs/first-document.md) for a guided five-minute build.
+2. Open [Recipes](./docs/recipes.md) only when you need a table, image, header,
+ barcode, chart, page-break rule, or another specific feature.
+3. Start from a [built-in template](./docs/templates/v2-layered/using-templates.md)
+ when the document is an invoice, proposal, CV, or cover letter.
-Full detail in [`CHANGELOG.md`](./CHANGELOG.md); every removed API and its replacement in the [2.0 modules migration guide](./docs/migration/v2.0.0-modules.md).
+For a backend endpoint, use the
+[Spring Boot streaming example](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java).
-## Vector primitives in 30 lines
+## Keep production documents from drifting
-Three snippets from the vector surfaces. Full runnable versions live in the [examples gallery](./examples/README.md).
+Rendering successfully is the first check. Important documents should also prove that
+their layout did not change unexpectedly after a library upgrade or a template edit.
-**Native chart** — categories + series in, native vector bars out (no rasterization).
+Add the testing artifact at test scope:
-
-```java
-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());
+```xml
+
+ io.github.demchaav
+ graph-compose-testing
+ 2.3.0
+ test
+
```
-**Overshoot-free line** — a smooth curve constrained to never overshoot the data range.
+Then pin the resolved geometry in a normal JUnit test:
-
+
```java
-section.chart(ChartSpec.line().data(series)
- .interpolation(LineInterpolation.MONOTONE)
- .build());
-```
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.testing.layout.LayoutSnapshotAssertions;
+import org.junit.jupiter.api.Test;
-**SVG import + alignment** — parse SVG to native geometry, seat any fixed node across the width.
+class InvoiceLayoutTest {
+ @Test
+ void layoutDoesNotDrift() throws Exception {
+ try (DocumentSession document = GraphCompose.document().create()) {
+ document.pageFlow(page -> page
+ .addParagraph("Invoice")
+ .addParagraph("Total: Β£125.00"));
-
-```java
-SvgIcon globe = SvgIcon.parse(svgMarkup);
-flow.addSvgIcon(globe, 48, HorizontalAlign.CENTER);
-flow.addAligned(HorizontalAlign.RIGHT, anyFixedNode);
+ LayoutSnapshotAssertions.assertMatches(document, "invoices/standard");
+ }
+ }
+}
```
-## Architecture
+Create the first JSON baseline intentionally, commit it with the test, and run the test
+normally in CI. If a node moves, a page break changes, or content order drifts, the test
+fails with a reviewable geometry diff. CI never needs to update the baseline itself.
-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.
+For flagship templates, add a second, pixel-level gate with `PdfVisualRegression`.
+It renders the PDF pages to images and writes `.actual.png` and `.diff.png` artifacts on
+mismatch, catching font, colour, glyph, and renderer changes that geometry alone cannot.
-```mermaid
-flowchart LR
- A["GraphCompose.document(...)
DocumentSession Β· DocumentDsl"] --> B["DocumentNode tree
document.node"]
- B --> C["LayoutCompiler
document.layout"]
- C --> D["Engine foundation @Internal
measure β paginate β place"]
- D --> E{ServiceLoader}
- E -->|render-pdf| F["PdfFixedLayoutBackend
PDFBox"]
- E -->|render-pptx| G["PptxFixedLayoutBackend
POI Β· same LayoutGraph as the PDF"]
- B -.->|render-docx Β· named directly| I["DocxSemanticBackend
POI Β· no layout pass"]
- D -.->|layoutSnapshot| H["Deterministic snapshot
(regression tests)"]
-```
+Start with [Testing your document](./docs/operations/test-your-document.md). The
+[layout snapshot](./docs/operations/layout-snapshot-testing.md) and
+[visual regression](./docs/operations/visual-regression-testing.md) pages are the deeper
+references when you need custom baseline paths, approval flow, or cross-platform tolerance.
-Full detail: [architecture overview](./docs/architecture/overview.md) · [package map](./docs/architecture/package-map.md) · [lifecycle](./docs/architecture/lifecycle.md).
+## Learn only what you need
-### Modules
+There is no requirement to read the repository from top to bottom. Stop at the level
+that solves your problem:
-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 :`; the lean engine lives in `core/`).
+1. **First PDF** β finish the example above or read [Your first document](./docs/first-document.md).
+2. **Document features** β use the [recipe index](./docs/recipes.md) as a lookup table.
+3. **Reusable business documents** β use [templates](./docs/templates/v2-layered/using-templates.md).
+4. **Production confidence** β add [snapshot and visual tests](./docs/operations/test-your-document.md),
+ then review [production rendering](./docs/operations/production-rendering.md).
+5. **Library internals** β only contributors need [Contributing](./CONTRIBUTING.md),
+ the [architecture overview](./docs/architecture/overview.md), package map, and ADRs.
-- **Published to Maven Central** — each links to its own README (what it is, when to depend on it, smallest complete example)
- - [`graph-compose-core`](./core/README.md) (`core/`) — the lean document engine
- - [`graph-compose-render-pdf`](./render-pdf/README.md) Β· [`-render-docx`](./render-docx/README.md) Β· [`-render-pptx`](./render-pptx/README.md) — render backends
- - [`graph-compose-templates`](./templates/README.md) — built-in CV / cover-letter / invoice / proposal presets
- - [`graph-compose-testing`](./testing/README.md) — snapshot & visual-regression test helpers
- - [`graph-compose`](./wrapper/README.md) (`wrapper/`) — the drop-in wrapper (core + PDF); [`graph-compose-bundle`](./bundle/README.md) — batteries-included (adds templates + fonts + emoji)
-- **Companion artifacts** (independent version lines) — [`graph-compose-fonts`](./fonts/README.md), [`graph-compose-emoji`](./emoji/README.md)
-- **Development only** (never published) — `qa` (architecture guards + visual regression), `coverage` (aggregate JaCoCo), `examples`, `benchmarks`
+The [full documentation index](./docs/README.md) is a reference catalogue, not a
+prerequisite for creating a document.
-See [CONTRIBUTING](./CONTRIBUTING.md) for the branch-routing table and the full build / verify flow.
-
-## Scope and comparison
+
+Output formats and optional modules
-### Output support
+PDF is the production, fixed-layout output included by `graph-compose`.
-| 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. Tables keep their `colSpan`/`rowSpan`, their fill and their borders, and images their fit mode; **hyperlinks, bookmarks and headers/footers are not implemented** — see [render-docx](./render-docx/README.md#what-it-maps-and-what-it-does-not). |
-| 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
+| Need | Add |
+|---|---|
+| Built-in templates, bundled fonts, and emoji together | `graph-compose-bundle` |
+| Built-in invoice, proposal, CV, or cover-letter templates | `graph-compose-templates` |
+| Editable PowerPoint output (**beta**) | `graph-compose-render-pptx` |
+| Semantic DOCX output (**partial**) | `graph-compose-render-docx` |
+| Layout snapshots and PDF visual diffs in tests | `graph-compose-testing` |
+| Only the authoring surface and engine | `graph-compose-core` |
-- Paragraphs and table cells support **right-to-left text**: `ParagraphBuilder.direction(RTL)` and `DocumentTableStyle.direction(RTL)` (or `AUTO`) reorder lines with the Unicode Bidirectional Algorithm, and Arabic is shaped into its joined contextual forms — Amiri and David Libre ship in `graph-compose-fonts`. One limit remains: **Indic reordering** is not performed.
-- **Five bundled script families**, each carrying its own script plus Latin: Arabic (`AMIRI`), Hebrew (`DAVID_LIBRE`), Georgian (`NOTO_SANS_GEORGIAN`), Armenian (`NOTO_SANS_ARMENIAN`), Korean (`GOTHIC_A1`). Chinese and Japanese have none: the official static Noto CJK faces use CFF outlines a PDF cannot embed, and the variable ones draw at their default weight, which is Thin — register your own with `FontFamilyDefinition`. See the [world-scripts example](examples/src/main/java/com/demcha/examples/features/text/WorldScriptsExample.java) ([preview](assets/readme/examples/world-scripts.pdf)).
-- **The direction reaches every backend, each on its own terms.** A PDF is painted, so the engine resolves the line and draws it reordered. Word and PowerPoint order the text themselves, so they are told what they need instead — `w:bidi` plus `w:rtl` for Word, a declared direction per frame for PowerPoint — and receive the text as written. What each one does, and where it deviates, is in the [backend capability matrix](docs/architecture/backend-capability-matrix.md).
-- A glyph the active font does not cover renders as `?` (with a warning logged); load a font that covers the script you need.
+Modules use the same GraphCompose version. Bundled fonts and colour emoji have their
+own release lines. Consult the [module guide](./docs/migration/v2.0.0-modules.md) only
+when you need to split dependencies, and the
+[backend capability matrix](./docs/architecture/backend-capability-matrix.md) before
+relying on a feature outside PDF.
-### When to use GraphCompose
+> **Upgrading from 1.x?** Read the [2.0 migration guide](./docs/migration/v2.0.0-modules.md).
-- **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 layouts** — `DocumentSession#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`](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java)).
-- **Higher-level than PDFBox, lighter than JasperReports** — Java DSL describes semantics; no XML templates, no manual coordinates.
+
-### What GraphCompose is not
+
+Scope and comparison
-- 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.
+GraphCompose uses PDFBox as its PDF renderer. The difference is the authoring layer:
+GraphCompose provides semantic document flow and automatic pagination, while PDFBox
+provides low-level PDF primitives and direct document manipulation.
-### Compared with similar Java libraries
+GraphCompose is an embedded Java library, not a hosted rendering service, WYSIWYG
+editor, HTML/CSS renderer, or datasource-driven reporting engine.
-| 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 |
+| Library | Authoring model | Best fit |
+|---|---|---|
+| **GraphCompose** | Typed Java document flow with deterministic layout tests | Code-first business documents |
+| **PDFBox** | Low-level text and path primitives | Direct PDF manipulation, parsing, extraction |
+| **iText 7** | Object layout API plus low-level canvas | Teams comfortable with AGPL or a commercial licence |
+| **JasperReports** | XML templates and datasource bindings | Traditional tabular reporting |
-GraphCompose uses PDFBox under the hood as the rendering backend — the comparison is about authoring surface, not the renderer.
+
-### Which API should I use?
+
+
+Architecture for contributors
-| You want to… | Surface | Entry point |
-|---|---|---|
-| Generate a one-off PDF programmatically | DSL | `GraphCompose.document(...).pageFlow(...)` — see [Hello world](#hello-world) above |
-| Generate a CV / cover letter from data | Layered templates | `ModernProfessional.create().compose(session, cvDocument)` — see [layered templates](./docs/templates/v2-layered/README.md) |
-| Add a custom visual primitive | Engine extension | `NodeDefinition` + a fragment handler per fixed-layout backend (`PdfFragmentRenderHandler`, `PptxFragmentRenderHandler`) — see [extension guide](./docs/contributing/extension-guide.md) |
-| Regression-test generated layouts | Layout snapshots | `DocumentSession#layoutSnapshot()` — quickstart at [Testing your document](./docs/operations/test-your-document.md); full reference at [snapshot testing](./docs/operations/layout-snapshot-testing.md) |
-| Pixel-test the rendered PDF (fonts, colours, anti-aliasing) | Visual regression | `PdfVisualRegression.standard()…assertMatchesBaseline(...)` — see [visual regression testing](./docs/operations/visual-regression-testing.md) |
-| See the live gallery | Static showcase site | [Showcase](https://DemchaAV.github.io/GraphCompose/) — source under [`web/`](./web), deployed to GitHub Pages via the [Pages workflow](./.github/workflows/deploy-web.yml) |
+Application code authors against `GraphCompose.document(...)`, `DocumentSession`, and
+the semantic document DSL. Internally, GraphCompose compiles that node tree into a
+deterministic layout, paginates it, and passes the result to a render backend.
-> **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?](./docs/templates/which-template-system.md)** maps every retired name to its layered replacement.
+```mermaid
+flowchart LR
+ A["Java application"] --> B["DocumentSession + semantic DSL"]
+ B --> C["Document node tree"]
+ C --> D["measure β paginate β place"]
+ D --> E["PDF / PPTX backend"]
+ C -.-> F["semantic DOCX backend"]
+ D -.-> G["layout snapshot"]
+```
-## Documentation
+Read the [architecture overview](./docs/architecture/overview.md),
+[package map](./docs/architecture/package-map.md), and
+[extension guide](./docs/contributing/extension-guide.md) before changing engine or
+backend code. The repository module map and build workflow live in
+[Contributing](./CONTRIBUTING.md).
-π **[Full docs index](./docs/README.md)** — categorised map of every doc, ADR, and recipe. Start there to navigate the documentation.
+
-The index routes by what you are doing β first document, using or authoring a
-template, extending the engine, running in production. The entry points most
-people want directly:
+## Project links
-- **Capabilities** β [the feature map](./docs/capabilities.md): every capability with its stability tier and the guide that covers it
-- **Templates** β [layered architecture](./docs/templates/v2-layered/README.md) (CV, cover letter, invoice, proposal on `BrandTheme`) Β· [which template system?](./docs/templates/which-template-system.md) for callers arriving from a pre-2.0 surface
-- **Recipes** β [the cookbook](./docs/recipes.md): tables, themes, shapes, transforms, page backgrounds, streaming, extending
-- **Operations** β [production rendering](./docs/operations/production-rendering.md) Β· [layout snapshot testing](./docs/operations/layout-snapshot-testing.md) Β· [troubleshooting](./docs/troubleshooting.md)
-- **Project** β [Contributing](./CONTRIBUTING.md) Β· [Roadmap](./ROADMAP.md) Β· [Support](./SUPPORT.md) Β· [Security policy](./SECURITY.md) Β· [API stability](./docs/api-stability.md) Β· [Migration to 2.0](./docs/migration/v2.0.0-modules.md)
+[Documentation](./docs/README.md) Β· [Examples](./examples/README.md) Β·
+[Roadmap](./ROADMAP.md) Β· [Changelog](./CHANGELOG.md) Β· [Support](./SUPPORT.md) Β·
+[Security](./SECURITY.md) Β· [API stability](./docs/api-stability.md)
## Companion projects
-- [**graph-compose-markdown**](https://central.sonatype.com/artifact/io.github.demchaav/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**](https://github.com/DemchaAV/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.
+- [**graph-compose-markdown**](https://central.sonatype.com/artifact/io.github.demchaav/graph-compose-markdown)
+ adds a Markdown input path over the same layout and PDF pipeline.
+- [**graphcompose-ai-flow**](https://github.com/DemchaAV/graphcompose-ai-flow)
+ is an experimental, independently released AI-assisted authoring project.
## Sponsorship
GraphCompose is MIT-licensed and solo-maintained. If it saves your team work,
-[GitHub Sponsors](https://github.com/sponsors/DemchaAV) funds the unglamorous
-half of keeping it alive — release engineering, dependency upgrades, the
-visual-regression suite, and issue triage.
-
-Recurring or one-off. No tier gates a feature, nothing here is or will be
-paywalled, and sponsorship buys no queue position: issues stay best-effort for
-everyone alike — see [SUPPORT.md](./SUPPORT.md) for which channel fits
-which question.
+[GitHub Sponsors](https://github.com/sponsors/DemchaAV) helps fund releases,
+dependency updates, visual-regression coverage, and issue triage. Sponsorship does
+not gate features or buy support priority; see [Support](./SUPPORT.md).
## License
-MIT — see [`LICENSE`](./LICENSE).
+MIT β see [LICENSE](./LICENSE).
diff --git a/docs/README.md b/docs/README.md
index a51471f4b..c2f50b6e3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,26 +1,45 @@
# GraphCompose Documentation
-Comprehensive docs for the engine, the templates, and the
-contribution workflow. Below is the map β pick your path.
+Use these docs as a path while learning and as a catalogue afterwards. You do not
+need to understand the engine, module layout, or ADRs to generate documents.
-If you're new to GraphCompose entirely, start at the
-[**root README**](../README.md) for the project overview, then come
-back here.
+If you have not rendered anything yet, begin with the [root README](../README.md).
----
+## The learning path
+
+Follow the path only as far as your current task requires:
+
+1. **Render one PDF:** [Your first document](first-document.md).
+2. **Add the blocks you need:** [Recipes](recipes.md) explains where text, tables,
+ charts, icons, images, cards, rows, backgrounds, and canvases fit.
+3. **Reuse a business-document design:** [Use a built-in template](templates/v2-layered/using-templates.md)
+ for an invoice, proposal, CV, or cover letter.
+4. **Protect the result:** [Testing your document](operations/test-your-document.md)
+ adds deterministic layout snapshots and pixel-level visual diffs.
+5. **Run it in a backend:** [Production rendering](operations/production-rendering.md)
+ covers streams, concurrency, failure handling, and operations.
+
+Stop there if you are a library user. Continue to
+[Contributing](../CONTRIBUTING.md), the [architecture overview](architecture/overview.md),
+and [extension guide](contributing/extension-guide.md) only when you are changing
+GraphCompose itself.
-## π§ By persona β start here
+## Go directly to a task
-| You are⦠| Read |
+| I need to⦠| Read |
|---|---|
-| **New to GraphCompose** β what is it, how do I render my first PDF | [Your first document](first-document.md) β [Getting started](getting-started.md) |
-| **Author rendering an invoice or proposal** | [Templates v2 (layered) β using templates](templates/v2-layered/using-templates.md) |
-| **Author rendering a CV** with your own data | [Templates v2 (layered) β quickstart](templates/v2-layered/quickstart.md) |
-| **Designer / author** wanting a custom visual style for CVs | [Templates v2 (layered) β authoring presets](templates/v2-layered/authoring-presets.md) |
-| **Maintainer of a pre-2.0 caller** (classic `*Spec` + builder templates, removed in 2.0) | [Which template system? β migration map](templates/which-template-system.md) |
-| **Contributor adding a new template family** to the library | [Templates v2 (layered) β contributor guide](templates/v2-layered/contributor-guide.md) |
-| **Contributor extending the engine** (new node type, new backend handler) | [Extension guide](contributing/extension-guide.md) β [Package map](architecture/package-map.md) |
-| **Operator** running GraphCompose in production | [Production rendering](operations/production-rendering.md) β [Performance](operations/performance.md) β [Logging](operations/logging.md) |
+| Add text, a list, table, chart, timeline, image, icon, emoji, or barcode | [Content and data recipes](recipes.md#content-and-data) |
+| Build columns, cards, clipping, overlapping layers, backgrounds, or a canvas | [Layout and visual recipes](recipes.md#layout-and-visual-composition) |
+| Add a header, footer, page number, watermark, link, bookmark, or contents page | [Page behaviour recipes](recipes.md#page-behaviour-output-and-development) |
+| Inspect layout boxes or create a page preview while developing | [Developer tools and output](recipes.md#page-behaviour-output-and-development) |
+| Protect a document with snapshots and visual diffs | [Testing your document](operations/test-your-document.md) |
+| Render an invoice or proposal from data | [Using templates](templates/v2-layered/using-templates.md) |
+| Render a CV with my own data | [CV template quickstart](templates/v2-layered/quickstart.md) |
+| Design a custom CV style | [Authoring presets](templates/v2-layered/authoring-presets.md) |
+| Upgrade a pre-2.0 caller | [2.0 migration guide](migration/v2.0.0-modules.md) |
+| Add a new template family | [Template contributor guide](templates/v2-layered/contributor-guide.md) |
+| Add a node or backend handler | [Extension guide](contributing/extension-guide.md) β [Package map](architecture/package-map.md) |
+| Operate GraphCompose in production | [Production rendering](operations/production-rendering.md) β [Performance](operations/performance.md) β [Logging](operations/logging.md) |
---
@@ -80,14 +99,23 @@ Kept for anyone stepping through the 1.x line one minor at a time. Nothing here
### Recipes (cookbook-style howtos)
- **[recipes.md](recipes.md)** β the catalogue: every recipe page and what it covers.
+- **[recipes/rich-text.md](recipes/rich-text.md)** β styled runs, links, inline images, SVG icons, emoji, shapes, and checkboxes.
+- **[recipes/lists.md](recipes/lists.md)** β flat and nested lists with custom markers and spacing.
- **[recipes/charts.md](recipes/charts.md)** β native vector bar/line/area/pie charts.
+- **[recipes/tables.md](recipes/tables.md)** β columns, structured cells, spans, zebra rows, totals, and repeated headers.
+- **[recipes/timelines.md](recipes/timelines.md)** β timelines, marker/rail geometry, dated entries, and pagination controls.
+- **[recipes/images.md](recipes/images.md)** β image sources, sizing, fit modes, rows, and cards.
+- **[recipes/barcodes.md](recipes/barcodes.md)** β QR and common barcode formats, tinting, quiet zones, and placement.
- **[recipes/keep-together.md](recipes/keep-together.md)** β blocks that never split at a page break.
- **[recipes/shapes.md](recipes/shapes.md)** β cards, dividers, lines, ellipses, images.
- **[recipes/shape-as-container.md](recipes/shape-as-container.md)** β shapes that hold child content.
- **[recipes/transforms.md](recipes/transforms.md)** β rotation, scaling, skewing.
-- **[recipes/tables.md](recipes/tables.md)** β tabular layouts.
- **[recipes/themes.md](recipes/themes.md)** β custom themes.
+- **[recipes/pdf-chrome.md](recipes/pdf-chrome.md)** β metadata, watermarks, headers, footers, page zones, and protection.
+- **[recipes/in-pdf-navigation.md](recipes/in-pdf-navigation.md)** β anchors, links, page references, bookmarks, and tables of contents.
- **[recipes/streaming.md](recipes/streaming.md)** β streaming PDFs to HTTP responses.
+- **[recipes/snapshot-testing.md](recipes/snapshot-testing.md)** β layout regression baselines in consumer projects.
+- **[recipes/docx-export.md](recipes/docx-export.md)** β semantic DOCX output and capability fallbacks.
- **[recipes/extending.md](recipes/extending.md)** β extension patterns by example.
- **[recipes/page-backgrounds.md](recipes/page-backgrounds.md)** β per-page fills: sidebars, bands, layered tints.
- **[recipes/layered-page-design.md](recipes/layered-page-design.md)** β page background vs. row vs. layer stack vs. canvas.
diff --git a/docs/capabilities.md b/docs/capabilities.md
index 32697c491..bd8f1dba4 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -18,6 +18,7 @@ tracks what is `Partial` or `Planned`.
|---|---|---|---|
| Open a document session | `GraphCompose.document(...)` β `DocumentSession` | Stable | [Your first document](first-document.md) |
| Describe content in reading order | `pageFlow(...)`, `module(...)`, `addSection(...)` | Stable | [Getting started](getting-started.md) |
+| Combine sections with different page sizes and chrome | `GraphCompose.documents(...)`, `MultiSectionDocument` | Stable | [Runnable example](../examples/src/main/java/com/demcha/examples/features/structure/MultiSectionExample.java) |
| Maintained document templates | `ModernInvoice`, `ModernProposal`, the `templates.cv.*` / `templates.coverletter.*` preset galleries | Stable | [Templates](templates/which-template-system.md) |
| Reusable building blocks (helpers) | helper methods / widgets over the DSL | Stable | [Diagrams](diagrams.md#choose-your-authoring-path) |
| Custom node / backend | `NodeDefinition`, render-handler SPI, `FixedLayoutBackend` | Extension SPI (`@Beta`) | [Extending](recipes/extending.md) |
@@ -30,8 +31,11 @@ tracks what is `Partial` or `Planned`.
| Lists (flat & nested) | `addList(...)`, `ListBuilder` | Stable | [Recipes](recipes.md) |
| Tables (spans, zebra, totals, repeat header) | `addTable(...)`, `DocumentTableCell` | Stable | [Advanced tables](recipes/tables.md) |
| Raster images | `addImage(...)`, fit modes | Stable | [Shapes & images](recipes/shapes.md) |
+| Inline SVG icons & colour emoji | `addSvgIcon(...)`, `RichText.svgIcon(...)`, `RichText.emoji(...)` | Stable (emoji requires its companion artifact or bundle) | [Rich text](recipes/rich-text.md) |
| Vector shapes, dividers, lines | `addShape(...)`, `addLine(...)`, `addEllipse(...)` | Stable | [Shapes](recipes/shapes.md) |
-| Charts (bar / line / pie) | `chart(ChartSpec...)`, `ChartData` | Stable | [Charts](recipes/charts.md) |
+| Vector paths, SVG path data & gradients | `addPath(...)`, `SvgPath.parse(...)`, `DocumentPaint` | Stable | [Runnable example](../examples/src/main/java/com/demcha/examples/features/shapes/VectorPathExample.java) |
+| Charts & inline sparklines | `chart(ChartSpec...)`, `RichText.sparkline(...)` | Stable | [Charts](recipes/charts.md) |
+| Timelines & process steps | `addTimeline(...)`, `TimelineBuilder` | Stable | [Timelines](recipes/timelines.md) |
| Barcodes & QR | `addBarcode(...)` | Stable | [Recipes](recipes.md) |
## Layout
@@ -43,8 +47,12 @@ tracks what is `Partial` or `Planned`.
| Overlap & alignment | `addLayerStack(...)` | Stable | [Layered page design](recipes/layered-page-design.md) |
| Shape-as-container | `addContainer(...)`, `addCircle(...)`, `addEllipse(...)` | Stable | [Shape as container](recipes/shape-as-container.md) |
| Fixed (x, y) placement | `addCanvas(w, h, canvas -> canvas.position(...))` | Stable | [Absolute placement](recipes/absolute-placement.md) |
+| Content-driven block with a fixed width | `fixedWidth(points)` | Stable | [Fixed-width flows](recipes/fixed-width-flows.md) |
+| Different margins by page range | `pageMargins(List)` | Stable | [Runnable example](../examples/src/main/java/com/demcha/examples/features/layout/PerPageMarginExample.java) |
| Bleed to page edge | `bleedToEdge(...)` | Stable | [Page backgrounds](recipes/page-backgrounds.md) |
| Transforms (rotate / scale) | `DocumentTransform` | Stable | [Transforms](recipes/transforms.md) |
+| Translucent colours | `DocumentColor.rgba(...)`, `withOpacity(...)` | Stable | [Translucency](recipes/translucency.md) |
+| Keep headings or composites together | `keepWithNext()`, `keepTogether()` | Stable | [Keep-together pagination](recipes/keep-together.md) |
## Output & testing
@@ -53,9 +61,11 @@ tracks what is `Partial` or `Planned`.
| Write a PDF file | `buildPdf()`, `buildPdf(Path)` | Stable | [Getting started](getting-started.md) |
| Stream to a caller-owned stream | `writePdf(OutputStream)` | Stable | [Streaming](recipes/streaming.md) |
| In-memory bytes | `toPdfBytes()` | Stable | [Getting started](getting-started.md) |
+| Page preview images | `toImage(pageIndex, dpi)`, `toImages(dpi)` | Stable | [Streaming and output](recipes/streaming.md) |
| Geometry-identical PowerPoint deck | `buildPptx()`, `buildPptx(Path)`, `writePptx(OutputStream)`, `toPptxBytes()` β needs `graph-compose-render-pptx` on the classpath | Experimental (`@Beta`, first shipped in 2.1.0) | [Backend capability matrix](architecture/backend-capability-matrix.md) |
| Editable Word (semantic) | `export(new DocxSemanticBackend())` | Stable (semantic, not PDF parity) | [Troubleshooting](troubleshooting.md) |
-| PDF chrome (metadata / watermark / header / footer / protection) | `metadata(...)`, `watermark(...)`, `header(...)`, `footer(...)`, `protect(...)` | Stable | [Getting started](getting-started.md) |
+| PDF chrome (metadata / watermark / header / footer / protection) | `metadata(...)`, `watermark(...)`, `header(...)`, `footer(...)`, `protect(...)` | Stable | [PDF chrome](recipes/pdf-chrome.md) |
+| Repeating node-based page zones | `chrome().zone(DocumentPageZone.header(...) / footer(...))` | Stable | [PDF chrome](recipes/pdf-chrome.md) |
| Layout snapshot regression | `LayoutSnapshotAssertions.assertMatches(...)` | Stable | [Layout snapshot testing](operations/layout-snapshot-testing.md) |
| Visual (pixel) regression | `PdfVisualRegression` | Stable | [Layout snapshot testing](operations/layout-snapshot-testing.md) |
| Render-only debug overlays | `guideLines(...)`, `debug(...)` | Stable | [Getting started](getting-started.md#debug-guide-lines) |
@@ -67,29 +77,8 @@ tracks what is `Partial` or `Planned`.
| External links | `addLink(...)`, `inlineLink(...)` | Stable | [Getting started](getting-started.md) |
| Internal jumps | `anchor("x")` + `linkTo("x")` | Stable | [Getting started](getting-started.md) |
| PDF outline bookmarks | `bookmark(new DocumentBookmarkOptions(...))` | Stable | [Getting started](getting-started.md) |
-
----
-
-## New in 2.1.0
-
-These ship from 2.1.0 onward β confirm your dependency version before relying on them:
-
-| Capability | Main API |
-|---|---|
-| Editable PowerPoint deck (`@Beta`) | `buildPptx(Path)`, `writePptx(OutputStream)`, `toPptxBytes()` β add `graph-compose-render-pptx` |
-| Slide page-size presets | `DocumentPageSize.SLIDE_16_9`, `DocumentPageSize.SLIDE_4_3` |
-| Select a render backend by format | `BackendProviders.fixedLayout("pptx")` |
-| Keep a heading with its content | `SectionBuilder.keepWithNext()`, `LineBuilder.keepWithNext()` |
-
-## New in 1.9.0
-
-These ship from 1.9.0 onward:
-
-| Capability | Main API |
-|---|---|
-| Printed page references | `addPageReference("anchor")` |
-| Generated Table of Contents | `addTableOfContents(toc -> toc.entry(...))` |
-| Page preview images | `toImage(pageIndex, dpi)`, `toImages(dpi)` |
+| Printed page references | `addPageReference("anchor")` | Stable | [In-PDF navigation](recipes/in-pdf-navigation.md) |
+| Generated table of contents | `addTableOfContents(toc -> toc.entry(...))` | Stable | [In-PDF navigation](recipes/in-pdf-navigation.md) |
---
diff --git a/docs/first-document.md b/docs/first-document.md
index 5c6463c06..cac6ca808 100644
--- a/docs/first-document.md
+++ b/docs/first-document.md
@@ -1,32 +1,31 @@
# Your First Document
-A five-minute path from an empty project to a real PDF. GraphCompose is
-session-first: you open a `DocumentSession`, describe content in reading order
-with a page flow, and render. No coordinates, no manual page breaks.
+A five-minute path from an empty project to a real PDF. Add the dependency,
+copy one complete file, and run it. You do not need to learn the layout engine
+or choose a template first.
> **Prerequisites:** Java 17+ and the `io.github.demchaav:graph-compose`
> dependency β see the [README install snippet](../README.md#installation).
## The smallest document
-Open a session for a file path, add one page flow, render. The engine handles
-placement and pagination.
+Open a document for a file path, add content from top to bottom, and render.
+The engine handles placement and pagination.
```java
import com.demcha.compose.GraphCompose;
-import com.demcha.compose.document.api.DocumentPageSize;
import com.demcha.compose.document.api.DocumentSession;
import java.nio.file.Path;
try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
- .pageSize(DocumentPageSize.A4)
- .margin(24, 24, 24, 24)
+ .margin(48, 48, 48, 48)
.create()) {
document.pageFlow(page -> page
- .module("Summary", module -> module.paragraph("Hello GraphCompose")));
+ .addParagraph("Hello GraphCompose")
+ .addParagraph("This PDF was created without manual coordinates."));
document.buildPdf();
}
@@ -37,14 +36,20 @@ Those are statements, not a file: they go inside a method β the
`Hello.java`. Every snippet on this page is written the same way, so the shape you are
reading is the GraphCompose part and nothing else.
-`GraphCompose.document(path)` configures the output; `create()` returns the
-`DocumentSession`. Use try-with-resources so the session is always released, even
-if rendering fails. Inside the session, `pageFlow(...)` is the document body:
-modules, sections, paragraphs, lists, tables, and rows are added top to bottom.
+There are only four ideas here:
+
+1. `GraphCompose.document(path)` chooses the output file.
+2. `create()` opens a `DocumentSession`.
+3. `pageFlow(...)` receives content in top-to-bottom reading order.
+4. `buildPdf()` writes the file.
+
+Use try-with-resources so the session is released even if rendering fails. The
+page flow can contain paragraphs directly; introduce modules, sections, tables,
+and styling only when the document actually needs them.
## A real custom document
-The same Flow model scales to a multi-section document. There are still no
+The same flow model scales to a multi-section document. There are still no
coordinates and no manual page breaks β just structure in reading order.
@@ -102,9 +107,8 @@ try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).cr
}
```
-Templates and hand-written Flow compose into the *same* `DocumentSession`, so you
-can mix them. To choose a template surface, see
-[Which template system should I use?](templates/which-template-system.md).
+Templates and hand-written flow compose into the *same* `DocumentSession`, so you
+can mix them. Continue with [Using built-in templates](templates/v2-layered/using-templates.md).
## Rendering on a server
@@ -119,11 +123,14 @@ thread-safe. Use `toPdfBytes()` only when the caller truly needs a byte array.
## Where to go next
-- [Getting Started](getting-started.md) β themes, hero blocks, layer stacks,
- shape-as-container, and built-in templates.
-- [Recipes](recipes.md) β themes, shapes, transforms, tables, and layout
- snapshots.
-- [Which template system should I use?](templates/which-template-system.md) β
- the decision tree for CV / invoice / proposal surfaces.
-- [Production Rendering](operations/production-rendering.md) β server-side
- lifecycle, streaming, and load guidance.
+Choose the one line that matches your next task:
+
+| Next task | Continue with |
+| --- | --- |
+| Add content such as a table, timeline, chart, image, icon, emoji, or barcode | [Content and data recipes](recipes.md#content-and-data) |
+| Build cards, columns, clipping, layers, backgrounds, or a canvas | [Layout and visual recipes](recipes.md#layout-and-visual-composition) |
+| Add headers, footers, navigation, previews, or debug overlays | [Page behaviour and development](recipes.md#page-behaviour-output-and-development) |
+| Render an invoice, proposal, CV, or cover letter | [Using built-in templates](templates/v2-layered/using-templates.md) |
+| Protect this document from layout drift | [Testing your document](operations/test-your-document.md) |
+| Stream it from a backend | [Production rendering](operations/production-rendering.md) |
+| Learn rows, layers, backgrounds, and canvases | [Layered page design](recipes/layered-page-design.md) |
diff --git a/docs/operations/test-your-document.md b/docs/operations/test-your-document.md
index 2f614db95..2136343d0 100644
--- a/docs/operations/test-your-document.md
+++ b/docs/operations/test-your-document.md
@@ -259,5 +259,5 @@ about, at near-zero cost per run.
β a working integration test that drives the snapshot API
entirely through the published surface. Copyable starting point.
- [`CvV2VisualParityTest`](../../qa/src/test/java/com/demcha/compose/document/templates/cv/presets/CvV2VisualParityTest.java)
- β example of the pixel-level pattern (currently test-only;
- becoming public via Track N).
+ β a repository example built with the same public `PdfVisualRegression`
+ helper available to downstream projects in `graph-compose-testing`.
diff --git a/docs/recipes.md b/docs/recipes.md
index e9aca5f69..75b611f19 100644
--- a/docs/recipes.md
+++ b/docs/recipes.md
@@ -5,7 +5,75 @@ one topic end-to-end. All recipes use only the canonical session-first
authoring API; public application code should not import
`com.demcha.compose.engine.*`.
-## Topic-focused recipe pages
+## Where content goes
+
+Most document features are blocks in the same `pageFlow`. A section, module, row,
+or card is just a container that groups those blocks; it does not introduce a
+different authoring API.
+
+```text
+DocumentSession
+βββ pageBackground(...) / pageBackgrounds(...) behind every page
+βββ chrome() / header(...) / footer(...) repeating page chrome
+βββ pageFlow(...)
+ βββ addParagraph / addRich / addList text
+ βββ addImage / addSvgIcon / addBarcode media
+ βββ addTable / chart / addTimeline data and stories
+ βββ addSection / module grouped content
+ βββ addRow side-by-side content
+ βββ addContainer / addLayerStack clipped or overlapping content
+ βββ addCanvas absolute placement, only when needed
+```
+
+Calls such as `chart(...)`, `addTable(...)`, and `addImage(...)` are shared by
+the flow containers. Put them directly on the page, or call the same method inside
+a section/module when the element needs a title, panel, padding, or accent.
+
+## Find the feature, then refine it
+
+Start with the entry point in the middle column. The linked recipe answers the next
+questions: where the element can live, which controls matter most, and how
+pagination or backend limits behave when they apply.
+
+### Content and data
+
+| I want to add⦠| Smallest entry point | Recipe answers next |
+| --- | --- | --- |
+| [A paragraph with links, styles, icons, or emoji](recipes/rich-text.md) | `addRich(rich -> ...)` | runs, links, SVG icons, emoji, inline shapes |
+| [A list](recipes/lists.md) | `addList(list -> ...)` | markers, nesting, spacing, styled items |
+| [A table](recipes/tables.md) | `addTable(table -> ...)` | columns, cells, zebra rows, totals, repeated headers |
+| [A chart](recipes/charts.md) | `chart(ChartSpec...)` | labels, value formats, colours, bar/line shape, background, legend |
+| [A timeline](recipes/timelines.md) | `addTimeline(timeline -> ...)` | markers, rail geometry, dated entries, text styles, pagination |
+| [An image](recipes/images.md) | `addImage(image -> ...)` | path/bytes, size, contain/cover, links |
+| [A QR code or barcode](recipes/barcodes.md) | `addBarcode(barcode -> ...)` | format, colour, quiet zone, card alignment |
+
+### Layout and visual composition
+
+| I want to build⦠| Smallest entry point | Recipe answers next |
+| --- | --- | --- |
+| [A section, panel, divider, or visual shape](recipes/shapes.md) | `addSection(...)` / `softPanel(...)` | fill, border, radius, accent, spacing, primitive shapes |
+| [Side-by-side columns](recipes/layered-page-design.md) | `addRow(row -> ...)` | column weights, flow layout, and when a row is the right primitive |
+| [Overlapping content](recipes/layered-page-design.md) | `addLayerStack(stack -> ...)` | alignment, offsets, z-index, and when to use a container or canvas |
+| [A clipped circle, ellipse, or custom container](recipes/shape-as-container.md) | `addContainer(...)` | outline, child alignment, `CLIP_PATH`, bounds, visible overflow |
+| [A repeating page tint, sidebar, or band](recipes/page-backgrounds.md) | `pageBackgrounds(...)` | full fills, partial fills, bleed, and layering |
+| [Rotation, scale, or layer ordering](recipes/transforms.md) | `rotate(...)` / `scale(...)` / `zIndex(...)` | transform origin, clipped transforms, deterministic overlap |
+| [Pixel-precise placement](recipes/absolute-placement.md) | `addCanvas(...)` | fixed box size, `(x, y)` positions, clipping, appropriate use cases |
+| [A block that should not split badly](recipes/keep-together.md) | `keepTogether()` / `keepWithNext()` | sections, lines, timeline entries, relocation at page breaks |
+| [A theme shared across documents](recipes/themes.md) | `BrandTheme` | palette, typography, spacing, decoration, preset-level reuse |
+
+### Page behaviour, output, and development
+
+| I want to⦠| Smallest entry point | Recipe answers next |
+| --- | --- | --- |
+| [Add metadata, watermark, header, footer, or page numbers](recipes/pdf-chrome.md) | `metadata(...)` / `header(...)` / `footer(...)` | text chrome, node-based page zones, protection, viewer behaviour |
+| [Add links, bookmarks, or a clickable table of contents](recipes/in-pdf-navigation.md) | `anchor(...)` / `linkTo(...)` | internal destinations, page references, TOC entries, PDF actions |
+| [Preview, stream, or choose an output form](recipes/streaming.md) | `buildPdf()` / `writePdf(...)` / `toImage(...)` | files, streams, bytes, preview images, backend selection |
+| [See layout boxes and node names while developing](getting-started.md#debug-guide-lines) | `guideLines(true)` / `debug(...)` | margin guides, resolved boxes, stable node labels |
+| [Protect a document from regressions](operations/test-your-document.md) | `LayoutSnapshotAssertions` | smoke tests, geometry snapshots, pixel-level PDF diffs, CI flow |
+| [Export semantic DOCX](recipes/docx-export.md) | `export(new DocxSemanticBackend())` | semantic mapping and fixed-layout feature fallbacks |
+| [Add a new node or backend capability](recipes/extending.md) | `NodeDefinition` / render handler | extension path, fluent builder, rendering, snapshot coverage |
+
+## Full recipe catalogue
| Page | Covers |
| --- | --- |
diff --git a/docs/recipes/charts.md b/docs/recipes/charts.md
index 5d4800376..c9c975b16 100644
--- a/docs/recipes/charts.md
+++ b/docs/recipes/charts.md
@@ -1,10 +1,58 @@
-# Charts: native vector bar, line, area, and pie/donut
+# Charts: start with data, refine the look
-GraphCompose charts are **not rasterised images**. A `ChartNode` is compiled
-at layout time into the same primitives everything else uses (shapes, lines,
-polygons, paragraphs), so charts are deterministic, snapshot-testable,
-theme-stylable, and render as crisp vectors in every fixed-layout backend
-with zero chart-specific render code.
+GraphCompose supports native vector bar, line, area, pie, and donut charts. Start
+with one dataset and one `chart(...)` call; add labels or styling only when the
+document needs them.
+
+## Quick start: one labelled bar chart
+
+A chart is a normal flow block. Put it directly in `pageFlow`, or use the same
+`chart(...)` call inside a section, module, a column within a row, or a card.
+
+```java
+import com.demcha.compose.document.chart.ChartData;
+import com.demcha.compose.document.chart.ChartSpec;
+import com.demcha.compose.document.chart.ValueLabelMode;
+
+ChartData revenue = ChartData.builder()
+ .categories("Q1", "Q2", "Q3", "Q4")
+ .series("Revenue", 12.4, 15.1, 9.8, 14.2)
+ .build();
+
+document.pageFlow(page -> page
+ .addParagraph("Quarterly revenue")
+ .chart(ChartSpec.bar()
+ .data(revenue)
+ .valueLabels(ValueLabelMode.OUTSIDE)
+ .build()));
+```
+
+That is the complete structural path: data β chart spec β flow. GraphCompose derives
+the chart width from its container and uses the spec's size policy for height.
+
+## Common changes
+
+| You want to⦠| Change |
+| --- | --- |
+| Show numbers on bars | `.valueLabels(ValueLabelMode.OUTSIDE)` |
+| Format values as currency, percent, or `k` | `.valueAxis(AxisSpec.builder().format(...).build())` |
+| Move or hide the legend | `.legend(BOTTOM / TOP / RIGHT / NONE)` |
+| Use brand colours | pass a `ChartStyle` with `.seriesPaint(index, paint)` |
+| Round bar corners or change bar width | `.barCornerRadius(...)` / `.barWidthRatio(...)` on `ChartStyle` |
+| Add line dots and tune the stroke | `.pointMarker(...)` / `.lineWidth(...)` on `ChartStyle` |
+| Hide grid, tick labels, or category labels | use `AxisSpec` toggles and `.showCategoryLabels(false)` |
+| Put the chart on a coloured card | call `chart(...)` inside a `softPanel(...)` section and match `valueLabelHalo` to the panel |
+| Catch chart movement after an upgrade | snapshot the containing `DocumentSession` with `LayoutSnapshotAssertions` |
+
+The sections below follow that order: choose data and chart kind first, then add the
+specific presentation controls you need.
+
+## How the chart API is organised
+
+Charts are **not rasterised images**. A `ChartNode` is compiled at layout time into
+the same primitives everything else uses (shapes, lines, polygons, paragraphs), so
+charts remain deterministic, snapshot-testable, theme-stylable, and crisp in
+fixed-layout outputs.
The API is split into independent layers so nothing is baked in:
@@ -192,6 +240,31 @@ pass it to each one.
returns raw primitives rather than placing a chart in a document β that is the
geometry seam, useful for tooling and tests, not a second way to author.
+### Put the chart on a coloured card
+
+The card belongs to the surrounding flow section; the chart remains a normal child.
+Match the value-label halo to the card so labels do not paint white rectangles over
+the background.
+
+```java
+DocumentColor panel = DocumentColor.rgb(18, 24, 38);
+
+ChartStyle onPanel = ChartStyle.builder()
+ .seriesPaint(0, DocumentPaint.solid(DocumentColor.rgb(88, 166, 255)))
+ .barCornerRadius(DocumentCornerRadius.top(3))
+ .valueLabelHalo(DocumentPaint.solid(panel))
+ .build();
+
+document.pageFlow(page -> page
+ .addSection("Revenue card", section -> section
+ .softPanel(panel, 10, 14)
+ .chart(barSpec, onPanel)));
+```
+
+Change the panel with `softPanel(...)`, the data marks with `ChartStyle`, and axes,
+labels, legend, or chart kind with `ChartSpec`. Keeping those responsibilities separate
+makes it clear which object to reach for.
+
### Typography
Three text styles cover the chrome, all plain `DocumentTextStyle` (the fourth,
diff --git a/docs/recipes/tables.md b/docs/recipes/tables.md
index f0faf17db..5ce7e5783 100644
--- a/docs/recipes/tables.md
+++ b/docs/recipes/tables.md
@@ -1,24 +1,36 @@
-# Advanced tables
+# Tables: start with rows, add behaviour as needed
-Phase D of the v1.5 release lifts the canonical table from "fixed
-header + body rows" to a feature set that covers most rendered-report
-patterns:
+A table is a normal flow block. Define its columns, add a header and rows, and
+place it directly in `pageFlow` or inside a section/module.
-| Feature | DSL entry point |
+## Quick start: the smallest useful table
+
+```java
+document.pageFlow(page -> page
+ .addTable(table -> table
+ .autoColumns(3)
+ .headerRow("Item", "Qty", "Amount")
+ .row("API support", "2", "Β£240")
+ .row("Hosting", "1", "Β£80")
+ .totalRow("Total", "", "Β£320")));
+```
+
+Start there. Add style or pagination behaviour only when the document needs it.
+
+| You want to⦠| Add |
| --- | --- |
-| Column span | `DocumentTableCell.text(...).colSpan(int)` |
-| Row span | `DocumentTableCell.text(...).rowSpan(int)` |
-| Several lines in one cell | `DocumentTableCell.lines(String...)` |
-| Any node in one cell | `DocumentTableCell.node(DocumentNode)` |
-| Header row alias | `TableBuilder.headerRow(String...)` |
-| Totals row | `TableBuilder.totalRow(String...)` |
-| Zebra rows | `TableBuilder.zebra(odd, even)` |
-| Repeated header on page break | `TableBuilder.repeatHeader()` |
-
-All the new pieces compose. A table can have a row-spanning side cell,
-zebra striping on the data rows, a bold totals row at the bottom, and
-a header that re-emits at the top of every continuation page when the
-table paginates.
+| Control fixed or automatic column widths | `.columns(DocumentTableColumn...)` |
+| Style every cell | `.defaultCellStyle(...)` |
+| Add several lines to one cell | `DocumentTableCell.lines(String...)` |
+| Put any document node in a cell | `DocumentTableCell.node(DocumentNode)` |
+| Merge cells across columns or rows | `.colSpan(...)` / `.rowSpan(...)` |
+| Colour alternating rows | `.zebra(odd, even)` |
+| Give the total a branded style | `.totalRow(style, values...)` |
+| Repeat headings after a page break | `.repeatHeader()` |
+
+The rest of this page builds on that basic table. The features compose: one table
+can contain structured cells, spans, zebra rows, a branded total, and a header
+repeated on every continuation page.
## Cell content β one line, several lines, or a node
@@ -221,7 +233,7 @@ they explicitly call the method.
The runnable example
[`examples/.../TableAdvancedExample.java`](../../examples/src/main/java/com/demcha/examples/features/tables/TableAdvancedExample.java)
-combines every Phase D feature on one PDF: a 3-column invoice with a
+combines the advanced table features on one PDF: a 3-column invoice with a
row-spanning side note, zebra body rows, a totals row, and a
repeating "Item / Qty / Amount" header on every continuation page.
The output lands at `examples/target/generated-pdfs/table-advanced.pdf`
@@ -229,7 +241,7 @@ on every full example sweep.
## Layout invariants you can rely on
-The Phase D feature set pins five test invariants:
+The table regression suite pins five invariants:
1. **Row span placement** β `TableBuilderRowSpanTest` verifies the
spanning cell's height equals the SUM of its covered row heights
diff --git a/docs/templates/v2-layered/authoring-presets.md b/docs/templates/v2-layered/authoring-presets.md
index a54739acc..c5b4a843d 100644
--- a/docs/templates/v2-layered/authoring-presets.md
+++ b/docs/templates/v2-layered/authoring-presets.md
@@ -397,11 +397,21 @@ A new preset needs at least:
- Renders to `examples/target/generated-pdfs/templates/cv/cv-my-preset.pdf`
- Uses `ExampleDataFactory.sampleCvDocumentV2()` for content
-3. **Eyeball the rendered PDF** β does it match your design
- intent? Are sections in the right slots? Is page break sensible?
+3. **Layout snapshot** with `LayoutSnapshotAssertions.assertMatches(...)`:
+ - commits the resolved geometry as reviewable JSON
+ - fails when coordinates, ordering, or page breaks drift
-A future Phase will add PDF/PNG snapshot diffing so visual
-regressions break the build. Until then, render parity is by-hand.
+4. **Pixel-level visual parity** with `PdfVisualRegression`:
+ - commits one PNG baseline per page
+ - writes `.actual.png` and `.diff.png` artifacts on mismatch
+
+5. **Eyeball the rendered PDF** β does it match your design
+ intent? Are sections in the right slots? Is the page break sensible?
+
+Use [Testing your document](../../operations/test-your-document.md) for the short
+consumer workflow and [Visual regression testing](../../operations/visual-regression-testing.md)
+for approval flags, tolerance, and baseline paths. GraphCompose uses the same public
+`graph-compose-testing` helpers for its own preset parity suite.
---
From 4c79c68631d27868969063890cb76d2168b0039d Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Thu, 10 Sep 2026 12:57:03 +0100
Subject: [PATCH 2/5] docs: clarify onboarding routes and output modules
---
README.md | 34 ++++++++-
docs/README.md | 96 +++++++++++++------------
docs/first-document.md | 7 +-
docs/templates/business-templates.md | 4 ++
docs/templates/v2-layered/quickstart.md | 8 +--
5 files changed, 94 insertions(+), 55 deletions(-)
diff --git a/README.md b/README.md
index c89bc261c..6a1314076 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,26 @@ wraps, places, and paginates it.
Use it for server-side invoices, proposals, CVs, reports, statements, schedules,
and other documents assembled from application data.
+### Start here
+
+- [Start with a first PDF](#installation)
+- Use a built-in template: [invoice or proposal](./docs/templates/business-templates.md),
+ or [CV or cover letter](./docs/templates/v2-layered/quickstart.md)
+- Export to [PowerPoint](./render-pptx/README.md) or [DOCX](./render-docx/README.md)
+
+### Outputs and templates
+
+- **PDF** β the main production output, included in `graph-compose`.
+- **PPTX** β editable fixed-layout export, currently **beta**, provided by the
+ separate [`graph-compose-render-pptx`](./render-pptx/README.md) module.
+- **DOCX** β semantic export with partial capability coverage, not full PDF parity,
+ provided by the separate [`graph-compose-render-docx`](./render-docx/README.md)
+ module.
+- **Templates** β maintained invoice, proposal, CV, and cover-letter designs in the
+ separate `graph-compose-templates` module. Start with
+ [invoice and proposal templates](./docs/templates/business-templates.md) or the
+ [CV and cover-letter quickstart](./docs/templates/v2-layered/quickstart.md).
+
## Create your first PDF
@@ -180,8 +200,10 @@ and snapshot coverage.
1. Follow [Your first document](./docs/first-document.md) for a guided five-minute build.
2. Open [Recipes](./docs/recipes.md) only when you need a table, image, header,
barcode, chart, page-break rule, or another specific feature.
-3. Start from a [built-in template](./docs/templates/v2-layered/using-templates.md)
- when the document is an invoice, proposal, CV, or cover letter.
+3. Start from an [invoice or proposal template](./docs/templates/business-templates.md)
+ when the document is a standard business artifact.
+4. Use the [CV and cover-letter quickstart](./docs/templates/v2-layered/quickstart.md)
+ for resume-focused document families.
For a backend endpoint, use the
[Spring Boot streaming example](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java).
@@ -245,7 +267,9 @@ that solves your problem:
1. **First PDF** β finish the example above or read [Your first document](./docs/first-document.md).
2. **Document features** β use the [recipe index](./docs/recipes.md) as a lookup table.
-3. **Reusable business documents** β use [templates](./docs/templates/v2-layered/using-templates.md).
+3. **Reusable business documents** β use the
+ [invoice and proposal templates](./docs/templates/business-templates.md), or the
+ [CV and cover-letter quickstart](./docs/templates/v2-layered/quickstart.md).
4. **Production confidence** β add [snapshot and visual tests](./docs/operations/test-your-document.md),
then review [production rendering](./docs/operations/production-rendering.md).
5. **Library internals** β only contributors need [Contributing](./CONTRIBUTING.md),
@@ -268,6 +292,9 @@ PDF is the production, fixed-layout output included by `graph-compose`.
| Layout snapshots and PDF visual diffs in tests | `graph-compose-testing` |
| Only the authoring surface and engine | `graph-compose-core` |
+`graph-compose-bundle` includes PDF, templates, fonts, and emoji. PPTX and DOCX
+are not included in the bundle; add their render modules separately when needed.
+
Modules use the same GraphCompose version. Bundled fonts and colour emoji have their
own release lines. Consult the [module guide](./docs/migration/v2.0.0-modules.md) only
when you need to split dependencies, and the
@@ -278,6 +305,7 @@ relying on a feature outside PDF.
+
Scope and comparison
diff --git a/docs/README.md b/docs/README.md
index c2f50b6e3..13daa9474 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,8 +12,9 @@ Follow the path only as far as your current task requires:
1. **Render one PDF:** [Your first document](first-document.md).
2. **Add the blocks you need:** [Recipes](recipes.md) explains where text, tables,
charts, icons, images, cards, rows, backgrounds, and canvases fit.
-3. **Reuse a business-document design:** [Use a built-in template](templates/v2-layered/using-templates.md)
- for an invoice, proposal, CV, or cover letter.
+3. **Reuse a business-document design:** start with the
+ [invoice and proposal templates](templates/business-templates.md), or the
+ [CV and cover-letter quickstart](templates/v2-layered/quickstart.md).
4. **Protect the result:** [Testing your document](operations/test-your-document.md)
adds deterministic layout snapshots and pixel-level visual diffs.
5. **Run it in a backend:** [Production rendering](operations/production-rendering.md)
@@ -33,8 +34,8 @@ GraphCompose itself.
| Add a header, footer, page number, watermark, link, bookmark, or contents page | [Page behaviour recipes](recipes.md#page-behaviour-output-and-development) |
| Inspect layout boxes or create a page preview while developing | [Developer tools and output](recipes.md#page-behaviour-output-and-development) |
| Protect a document with snapshots and visual diffs | [Testing your document](operations/test-your-document.md) |
-| Render an invoice or proposal from data | [Using templates](templates/v2-layered/using-templates.md) |
-| Render a CV with my own data | [CV template quickstart](templates/v2-layered/quickstart.md) |
+| Render an invoice or proposal from data | [Business templates](templates/business-templates.md) |
+| Render a CV or cover letter with my own data | [CV and cover-letter quickstart](templates/v2-layered/quickstart.md) |
| Design a custom CV style | [Authoring presets](templates/v2-layered/authoring-presets.md) |
| Upgrade a pre-2.0 caller | [2.0 migration guide](migration/v2.0.0-modules.md) |
| Add a new template family | [Template contributor guide](templates/v2-layered/contributor-guide.md) |
@@ -57,47 +58,7 @@ GraphCompose itself.
- **[templates/v2-layered/](templates/v2-layered/)** β the template surface (CV is the reference implementation): `data` / `components` / `widgets` / `presets` per family, over the shared `templates.core.theme`.
- **[templates/v1-classic/](templates/v1-classic/)** β ποΈ archived: the classic spec/builder/presets surface removed in 2.0; kept for pre-2.0 callers.
-### Output backends
-- **[architecture/backend-capability-matrix.md](architecture/backend-capability-matrix.md)** β what each render backend supports, per capability. The source of truth for PDF vs PPTX fidelity.
-- **[../render-pptx/README.md](../render-pptx/README.md)** β `graph-compose-render-pptx`: editable PowerPoint decks from the same session that prints the PDF (`@Beta`, first shipped in 2.1.0).
-- **[api-stability.md](api-stability.md)** β stability tier per package, and what a tier promises.
-
-### Architecture
-- **[architecture/overview.md](architecture/overview.md)** β high-level system architecture (engine + DSL + templates + backends).
-- **[architecture/lifecycle.md](architecture/lifecycle.md)** β the document lifecycle from `GraphCompose.document(...)` through `buildPdf()`.
-- **[architecture/pagination-ordering.md](architecture/pagination-ordering.md)** β how nodes are paginated and ordered.
-- **[architecture/package-map.md](architecture/package-map.md)** β what's in which package.
-- **[architecture/canonical-legacy-parity.md](architecture/canonical-legacy-parity.md)** β per-feature authoring coverage of the canonical API, refreshed for the 2.1 line. The recipes, the capabilities catalogue and the troubleshooting guide all link into it.
-
-### Operations
-- **[operations/production-rendering.md](operations/production-rendering.md)** β server-side rendering, streaming, thread safety.
-- **[operations/performance.md](operations/performance.md)** β perf characteristics + tuning.
-- **[operations/benchmarks.md](operations/benchmarks.md)** β how to run benchmarks; reference numbers.
-- **[operations/logging.md](operations/logging.md)** β logger configuration, what each logger emits.
-- **[operations/layout-snapshot-testing.md](operations/layout-snapshot-testing.md)** β snapshot-based layout regression testing.
-
-### Contributing
-- **[contributing/extension-guide.md](contributing/extension-guide.md)** β add a new node type, backend handler, or theme primitive.
-- **[architecture/pagination-ordering.md](architecture/pagination-ordering.md)** β how nodes are paginated and ordered, for contributors working on layout / measurement.
-- **[contributing/release-process.md](contributing/release-process.md)** β versioning, tag procedure, Maven Central publication.
-
-### Migrations & roadmap
-- **[migration/v2.0.0-modules.md](migration/v2.0.0-modules.md)** β the current upgrade guide: the 1.x β 2.x module split, the one dependency-level break, and every removed API with its replacement.
-- **[migration/v1.8.0-fonts.md](migration/v1.8.0-fonts.md)** β the fonts artifact split, still relevant to anyone adding `graph-compose-fonts`.
-- **[../ROADMAP.md](../ROADMAP.md)** β direction for the line after 2.1.
-- **[roadmaps/post-2.0-engineering.md](roadmaps/post-2.0-engineering.md)** β internal refactors, scale, and tooling deferred past the 2.0 line.
-
-
-Historical documentation β shipped roadmaps and superseded minor-to-minor upgrade guides
-
-Kept for anyone stepping through the 1.x line one minor at a time. Nothing here describes the current API.
-
-- [roadmaps/v1.6-roadmap.md](roadmaps/v1.6-roadmap.md) β the v1.6 "expressive" roadmap (shipped).
-- [roadmaps/migration-v1-8-to-v1-9.md](roadmaps/migration-v1-8-to-v1-9.md) Β· [v1-7-to-v1-8](roadmaps/migration-v1-7-to-v1-8.md) Β· [v1-6-to-v1-7](roadmaps/migration-v1-6-to-v1-7.md) Β· [v1-5-to-v1-6](roadmaps/migration-v1-5-to-v1-6.md) Β· [v1-4-to-v1-5](roadmaps/migration-v1-4-to-v1-5.md)
-
-
-
-### Recipes (cookbook-style howtos)
+### Recipes
- **[recipes.md](recipes.md)** β the catalogue: every recipe page and what it covers.
- **[recipes/rich-text.md](recipes/rich-text.md)** β styled runs, links, inline images, SVG icons, emoji, shapes, and checkboxes.
- **[recipes/lists.md](recipes/lists.md)** β flat and nested lists with custom markers and spacing.
@@ -122,7 +83,50 @@ Kept for anyone stepping through the 1.x line one minor at a time. Nothing here
- **[recipes/absolute-placement.md](recipes/absolute-placement.md)** β pixel-precise canvas placement.
- **[font-coverage.md](font-coverage.md)** β WinAnsi limits, `β` vs `β’`, and glyph fallback.
-### Architecture Decision Records (ADRs)
+### Operations / Testing
+- **[operations/test-your-document.md](operations/test-your-document.md)** β consumer smoke tests, layout snapshots, and PDF visual diffs.
+- **[operations/visual-regression-testing.md](operations/visual-regression-testing.md)** β pixel-level PDF baselines, diffs, tolerances, and approval flow.
+- **[operations/layout-snapshot-testing.md](operations/layout-snapshot-testing.md)** β deterministic geometry regression testing.
+- **[operations/production-rendering.md](operations/production-rendering.md)** β server-side rendering, streaming, thread safety.
+- **[operations/performance.md](operations/performance.md)** β perf characteristics + tuning.
+- **[operations/benchmarks.md](operations/benchmarks.md)** β how to run benchmarks; reference numbers.
+- **[operations/logging.md](operations/logging.md)** β logger configuration, what each logger emits.
+
+### Output backends
+- **[architecture/backend-capability-matrix.md](architecture/backend-capability-matrix.md)** β what each render backend supports, per capability. The source of truth for PDF vs PPTX fidelity.
+- **[../render-pptx/README.md](../render-pptx/README.md)** β `graph-compose-render-pptx`: editable PowerPoint decks from the same session that prints the PDF (`@Beta`, first shipped in 2.1.0).
+- **[../render-docx/README.md](../render-docx/README.md)** β `graph-compose-render-docx`: semantic Word export with partial capability coverage and no full PDF parity.
+- **[api-stability.md](api-stability.md)** β stability tier per package, and what a tier promises.
+
+### Migrations
+- **[migration/v2.0.0-modules.md](migration/v2.0.0-modules.md)** β the current upgrade guide: the 1.x β 2.x module split, the one dependency-level break, and every removed API with its replacement.
+- **[migration/v1.8.0-fonts.md](migration/v1.8.0-fonts.md)** β the fonts artifact split, still relevant to anyone adding `graph-compose-fonts`.
+- **[../ROADMAP.md](../ROADMAP.md)** β direction for the line after 2.1.
+- **[roadmaps/post-2.0-engineering.md](roadmaps/post-2.0-engineering.md)** β internal refactors, scale, and tooling deferred past the 2.0 line.
+
+
+Historical documentation β shipped roadmaps and superseded minor-to-minor upgrade guides
+
+Kept for anyone stepping through the 1.x line one minor at a time. Nothing here describes the current API.
+
+- [roadmaps/v1.6-roadmap.md](roadmaps/v1.6-roadmap.md) β the v1.6 "expressive" roadmap (shipped).
+- [roadmaps/migration-v1-8-to-v1-9.md](roadmaps/migration-v1-8-to-v1-9.md) Β· [v1-7-to-v1-8](roadmaps/migration-v1-7-to-v1-8.md) Β· [v1-6-to-v1-7](roadmaps/migration-v1-6-to-v1-7.md) Β· [v1-5-to-v1-6](roadmaps/migration-v1-5-to-v1-6.md) Β· [v1-4-to-v1-5](roadmaps/migration-v1-4-to-v1-5.md)
+
+
+
+### Architecture
+- **[architecture/overview.md](architecture/overview.md)** β high-level system architecture (engine + DSL + templates + backends).
+- **[architecture/lifecycle.md](architecture/lifecycle.md)** β the document lifecycle from `GraphCompose.document(...)` through `buildPdf()`.
+- **[architecture/pagination-ordering.md](architecture/pagination-ordering.md)** β how nodes are paginated and ordered.
+- **[architecture/package-map.md](architecture/package-map.md)** β what's in which package.
+- **[architecture/canonical-legacy-parity.md](architecture/canonical-legacy-parity.md)** β per-feature authoring coverage of the canonical API, refreshed for the 2.1 line. The recipes, the capabilities catalogue and the troubleshooting guide all link into it.
+
+### Contributing
+- **[contributing/extension-guide.md](contributing/extension-guide.md)** β add a new node type, backend handler, or theme primitive.
+- **[architecture/pagination-ordering.md](architecture/pagination-ordering.md)** β how nodes are paginated and ordered, for contributors working on layout / measurement.
+- **[contributing/release-process.md](contributing/release-process.md)** β versioning, tag procedure, Maven Central publication.
+
+### ADRs
Numbered, dated decisions about non-trivial design choices. Read these
when you need to understand *why* a piece of the system looks the way
it does.
diff --git a/docs/first-document.md b/docs/first-document.md
index cac6ca808..4e7a31f24 100644
--- a/docs/first-document.md
+++ b/docs/first-document.md
@@ -108,7 +108,9 @@ try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).cr
```
Templates and hand-written flow compose into the *same* `DocumentSession`, so you
-can mix them. Continue with [Using built-in templates](templates/v2-layered/using-templates.md).
+can mix them. Continue with the
+[invoice and proposal guide](templates/business-templates.md), or use the
+[CV and cover-letter quickstart](templates/v2-layered/quickstart.md).
## Rendering on a server
@@ -130,7 +132,8 @@ Choose the one line that matches your next task:
| Add content such as a table, timeline, chart, image, icon, emoji, or barcode | [Content and data recipes](recipes.md#content-and-data) |
| Build cards, columns, clipping, layers, backgrounds, or a canvas | [Layout and visual recipes](recipes.md#layout-and-visual-composition) |
| Add headers, footers, navigation, previews, or debug overlays | [Page behaviour and development](recipes.md#page-behaviour-output-and-development) |
-| Render an invoice, proposal, CV, or cover letter | [Using built-in templates](templates/v2-layered/using-templates.md) |
+| Render an invoice or proposal | [Business templates](templates/business-templates.md) |
+| Render a CV or cover letter | [CV and cover-letter quickstart](templates/v2-layered/quickstart.md) |
| Protect this document from layout drift | [Testing your document](operations/test-your-document.md) |
| Stream it from a backend | [Production rendering](operations/production-rendering.md) |
| Learn rows, layers, backgrounds, and canvases | [Layered page design](recipes/layered-page-design.md) |
diff --git a/docs/templates/business-templates.md b/docs/templates/business-templates.md
index 27db24acb..f599a375e 100644
--- a/docs/templates/business-templates.md
+++ b/docs/templates/business-templates.md
@@ -28,6 +28,10 @@ destination.
The template composes into an **open** `DocumentSession` β it never
decides file vs stream vs bytes. The caller does.
+> **Dependency.** `graph-compose-templates` is not included in `graph-compose`.
+> Add it separately, or use `graph-compose-bundle`, which includes PDF,
+> templates, fonts, and emoji.
+
## Invoice
diff --git a/docs/templates/v2-layered/quickstart.md b/docs/templates/v2-layered/quickstart.md
index 6665150ab..dccdcdfbe 100644
--- a/docs/templates/v2-layered/quickstart.md
+++ b/docs/templates/v2-layered/quickstart.md
@@ -3,10 +3,10 @@
**5 minutes.** What it is, why it's structured this way, and a working
example that renders a CV PDF.
-> **Dependency.** The ready-made presets ship in the opt-in
-> `graph-compose-templates` artifact β they are **not** bundled in `graph-compose`. Add it
-> next to `graph-compose` (or depend on `graph-compose-bundle`, which includes both). See
-> the [README install matrix](../../../README.md#installation).
+> **Dependency.** CV and cover-letter presets ship in
+> `graph-compose-templates`, which is **not** included in `graph-compose`. Add it
+> separately, or use `graph-compose-bundle`, which includes PDF, templates, fonts,
+> and emoji. See the [README install matrix](../../../README.md#installation).
---
From 99fafd7142b1838ab134817af63d83dca6ab4bf1 Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Sun, 13 Sep 2026 01:55:51 +0100
Subject: [PATCH 3/5] docs(onboarding): one route from the README to every
shipped family
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The README carried three competing "what next" lists β "Start here" before
the install section, "Choose a longer path only when you need it", and
"Learn only what you need" β each routing to the same four destinations
with different wording. A reader had to pick a documentation structure
before picking a task.
Two of the six shipped template families had nowhere to be found from the
onboarding path: receipt appeared only on a contributor architecture page,
and rota appeared nowhere outside the generated knowledge pack, while
which-template-system.md still said no rota template existed β CobaltRota
has shipped since. There was no page that listed the families at all.
- Collapse the three README lists into one "What to read next" table whose
rows are the four destinations: Recipes, Templates, Testing, Production.
- Add docs/templates/README.md as the single Templates door: the six
families grouped as business (invoice, proposal, receipt, rota) and
profile (CV, cover letter), with preset counts and the data package each
takes. Presets within a family take different spec types, so the table
names the package and points at create() for the exact type.
- Define DSL / preset / template once, on that page, and stop calling
templates "presets built on the DSL" in getting-started.
- Name the DSL sense of module() where it is introduced; the README used
the same word for a Maven artifact eight lines earlier.
- Correct which-template-system.md: WeeklyScheduleTemplateV1 maps to
CobaltRota, which has create() only and takes StructuredRotaDocumentSpec.
- Fold architecture, contributing and ADRs into one collapsed block in the
docs index so the learning path and task table lead.
- Regenerate the claims and proofs indexes, stale since the develop merge.
Verified: core documentation guards 48 green (DocumentationLinkGuard,
RecipeCatalogueGuard, CanonicalSurfaceGuard, DocumentationCoverage,
VersionConsistencyGuard, PackageMapGuard), qa 18 green
(DocumentationExamplesTest, DocumentationSnippetCompileTest β which
compiles the README snippets). check-claims 46 claims / 10 proofs current,
check-routes and check-stability-doc green. Repo-wide relative-link audit
falls from 27 broken to 11, all pre-existing in CHANGELOG history and one
receipt AUTHORS.md path. Every anchor introduced resolves; every Java
identifier named resolves except the removed 1.x types the migration table
names on purpose.
---
README.md | 77 +++++++------------------
docs/README.md | 13 ++++-
docs/diagrams.md | 2 +-
docs/first-document.md | 14 ++---
docs/getting-started.md | 4 +-
docs/templates/README.md | 59 +++++++++++++++++++
docs/templates/business-templates.md | 7 +--
docs/templates/v2-layered/README.md | 27 +++++++--
docs/templates/which-template-system.md | 2 +-
knowledge/claims/index.json | 32 +++++-----
knowledge/proofs/index.json | 10 ++--
11 files changed, 146 insertions(+), 101 deletions(-)
create mode 100644 docs/templates/README.md
diff --git a/README.md b/README.md
index 6a1314076..cd9125956 100644
--- a/README.md
+++ b/README.md
@@ -46,13 +46,6 @@ wraps, places, and paginates it.
Use it for server-side invoices, proposals, CVs, reports, statements, schedules,
and other documents assembled from application data.
-### Start here
-
-- [Start with a first PDF](#installation)
-- Use a built-in template: [invoice or proposal](./docs/templates/business-templates.md),
- or [CV or cover letter](./docs/templates/v2-layered/quickstart.md)
-- Export to [PowerPoint](./render-pptx/README.md) or [DOCX](./render-docx/README.md)
-
### Outputs and templates
- **PDF** β the main production output, included in `graph-compose`.
@@ -61,10 +54,10 @@ and other documents assembled from application data.
- **DOCX** β semantic export with partial capability coverage, not full PDF parity,
provided by the separate [`graph-compose-render-docx`](./render-docx/README.md)
module.
-- **Templates** β maintained invoice, proposal, CV, and cover-letter designs in the
- separate `graph-compose-templates` module. Start with
- [invoice and proposal templates](./docs/templates/business-templates.md) or the
- [CV and cover-letter quickstart](./docs/templates/v2-layered/quickstart.md).
+- **Templates** β 58 maintained designs across six document families in the separate
+ `graph-compose-templates` module: invoice, proposal, receipt and rota for business
+ documents, CV and cover letter for profiles. See the
+ [templates overview](./docs/templates/README.md).
## Create your first PDF
@@ -136,7 +129,8 @@ what it needs to say.
## Grow it into a real document
-A larger document uses the same flow. Group related content into named modules and
+A larger document uses the same flow. Group related content into named **content
+modules** β `module(...)`, a titled block of the document, not a Maven module β and
feed them your application data; coordinates still do not enter the authoring code.
@@ -147,23 +141,8 @@ document.pageFlow(page -> page
.module("Projects", module -> module.rows(projects)));
```
-### Pick the next thing you need
-
The same flow accepts the common building blocks directly. You do not need a new
-document model for each feature.
-
-| Area | Includes | Open |
-|---|---|---|
-| **Content and data** | rich text, links, lists, tables, charts, timelines, images, SVG icons, emoji, QR and barcodes | [Find a content block](./docs/recipes.md#content-and-data) |
-| **Layout and visual composition** | sections, cards, rows, layers, clipping, shapes, backgrounds, transforms, canvases, pagination rules | [Choose a layout primitive](./docs/recipes.md#layout-and-visual-composition) |
-| **Page behaviour and output** | headers, footers, page numbers, watermarks, metadata, navigation, previews, streaming, DOCX | [Configure the document](./docs/recipes.md#page-behaviour-output-and-development) |
-| **Development confidence** | guide lines, node labels, layout snapshots, PDF visual diffs | [Test and debug it](./docs/operations/test-your-document.md) |
-
-The [recipe index](./docs/recipes.md) is the complete task map. Start from its
-smallest entry point, then open one focused guide for the styling, layout,
-pagination, backend, and testing details that matter to that feature.
-
-For example, a labelled bar chart is one more block in `pageFlow`:
+document model for each feature β a labelled bar chart is one more block in `pageFlow`:
```java
@@ -195,19 +174,6 @@ From there, the [chart recipe](./docs/recipes/charts.md) shows the next question
order: value formatting, legends, bar/line shape, colours, grid, labels, background,
and snapshot coverage.
-### Choose a longer path only when you need it
-
-1. Follow [Your first document](./docs/first-document.md) for a guided five-minute build.
-2. Open [Recipes](./docs/recipes.md) only when you need a table, image, header,
- barcode, chart, page-break rule, or another specific feature.
-3. Start from an [invoice or proposal template](./docs/templates/business-templates.md)
- when the document is a standard business artifact.
-4. Use the [CV and cover-letter quickstart](./docs/templates/v2-layered/quickstart.md)
- for resume-focused document families.
-
-For a backend endpoint, use the
-[Spring Boot streaming example](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java).
-
## Keep production documents from drifting
Rendering successfully is the first check. Important documents should also prove that
@@ -260,23 +226,22 @@ Start with [Testing your document](./docs/operations/test-your-document.md). The
[visual regression](./docs/operations/visual-regression-testing.md) pages are the deeper
references when you need custom baseline paths, approval flow, or cross-platform tolerance.
-## Learn only what you need
+## What to read next
-There is no requirement to read the repository from top to bottom. Stop at the level
-that solves your problem:
+Pick the one row that matches what you are doing. Each is a complete route β you do
+not need the others, and you never need the engine internals to author a document.
-1. **First PDF** β finish the example above or read [Your first document](./docs/first-document.md).
-2. **Document features** β use the [recipe index](./docs/recipes.md) as a lookup table.
-3. **Reusable business documents** β use the
- [invoice and proposal templates](./docs/templates/business-templates.md), or the
- [CV and cover-letter quickstart](./docs/templates/v2-layered/quickstart.md).
-4. **Production confidence** β add [snapshot and visual tests](./docs/operations/test-your-document.md),
- then review [production rendering](./docs/operations/production-rendering.md).
-5. **Library internals** β only contributors need [Contributing](./CONTRIBUTING.md),
- the [architecture overview](./docs/architecture/overview.md), package map, and ADRs.
+| I want to⦠| Go to |
+|---|---|
+| **Understand the model** behind the example above | [Your first document](./docs/first-document.md) β a guided five-minute build |
+| **Add a feature** β table, list, chart, timeline, image, header, footer, barcode, page-break rule | [Recipes](./docs/recipes.md) β the complete task index |
+| **Start from a ready-made design** β invoice, proposal, receipt, rota, CV, cover letter | [Templates](./docs/templates/README.md) β 58 presets across six families |
+| **Prove an upgrade did not move my document** | [Testing your document](./docs/operations/test-your-document.md) β layout snapshots and visual diffs |
+| **Render from a server** | [Production rendering](./docs/operations/production-rendering.md) β streaming, concurrency, failure handling; the [Spring Boot streaming example](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java) is a working endpoint |
-The [full documentation index](./docs/README.md) is a reference catalogue, not a
-prerequisite for creating a document.
+Contributing to GraphCompose itself is a separate path: start at
+[Contributing](./CONTRIBUTING.md). The [documentation index](./docs/README.md) is a
+reference catalogue β useful for finding a page, never required reading.
Output formats and optional modules
@@ -286,7 +251,7 @@ PDF is the production, fixed-layout output included by `graph-compose`.
| Need | Add |
|---|---|
| Built-in templates, bundled fonts, and emoji together | `graph-compose-bundle` |
-| Built-in invoice, proposal, CV, or cover-letter templates | `graph-compose-templates` |
+| Built-in document templates β invoice, proposal, receipt, rota, CV, cover letter | `graph-compose-templates` |
| Editable PowerPoint output (**beta**) | `graph-compose-render-pptx` |
| Semantic DOCX output (**partial**) | `graph-compose-render-docx` |
| Layout snapshots and PDF visual diffs in tests | `graph-compose-testing` |
diff --git a/docs/README.md b/docs/README.md
index 13daa9474..c589a53e3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,9 +12,8 @@ Follow the path only as far as your current task requires:
1. **Render one PDF:** [Your first document](first-document.md).
2. **Add the blocks you need:** [Recipes](recipes.md) explains where text, tables,
charts, icons, images, cards, rows, backgrounds, and canvases fit.
-3. **Reuse a business-document design:** start with the
- [invoice and proposal templates](templates/business-templates.md), or the
- [CV and cover-letter quickstart](templates/v2-layered/quickstart.md).
+3. **Reuse a ready-made design:** [Templates](templates/README.md) β invoice,
+ proposal, receipt and rota for business documents; CV and cover letter for profiles.
4. **Protect the result:** [Testing your document](operations/test-your-document.md)
adds deterministic layout snapshots and pixel-level visual diffs.
5. **Run it in a backend:** [Production rendering](operations/production-rendering.md)
@@ -34,7 +33,9 @@ GraphCompose itself.
| Add a header, footer, page number, watermark, link, bookmark, or contents page | [Page behaviour recipes](recipes.md#page-behaviour-output-and-development) |
| Inspect layout boxes or create a page preview while developing | [Developer tools and output](recipes.md#page-behaviour-output-and-development) |
| Protect a document with snapshots and visual diffs | [Testing your document](operations/test-your-document.md) |
+| Pick a ready-made document design | [Templates overview](templates/README.md) β all six families |
| Render an invoice or proposal from data | [Business templates](templates/business-templates.md) |
+| Render a receipt or a shift rota from data | [Templates overview](templates/README.md#business-documents) |
| Render a CV or cover letter with my own data | [CV and cover-letter quickstart](templates/v2-layered/quickstart.md) |
| Design a custom CV style | [Authoring presets](templates/v2-layered/authoring-presets.md) |
| Upgrade a pre-2.0 caller | [2.0 migration guide](migration/v2.0.0-modules.md) |
@@ -54,6 +55,7 @@ GraphCompose itself.
- **[troubleshooting.md](troubleshooting.md)** β symptom-first fixes for common gotchas: stray `?` glyphs, silent DOCX drops, optional-dependency `NoClassDefFoundError`, running the bundled examples.
### Templates
+- **[templates/README.md](templates/README.md)** β start here: all six shipped families, what data each takes, and which guide to open.
- **[templates/business-templates.md](templates/business-templates.md)** β invoice & proposal templates: the compose-first contract, end to end, on the layered `ModernInvoice` / `ModernProposal` surface.
- **[templates/v2-layered/](templates/v2-layered/)** β the template surface (CV is the reference implementation): `data` / `components` / `widgets` / `presets` per family, over the shared `templates.core.theme`.
- **[templates/v1-classic/](templates/v1-classic/)** β ποΈ archived: the classic spec/builder/presets surface removed in 2.0; kept for pre-2.0 callers.
@@ -114,6 +116,9 @@ Kept for anyone stepping through the 1.x line one minor at a time. Nothing here
+
+Library internals β architecture, contributing, and ADRs. Needed only when you change GraphCompose itself, never to author a document.
+
### Architecture
- **[architecture/overview.md](architecture/overview.md)** β high-level system architecture (engine + DSL + templates + backends).
- **[architecture/lifecycle.md](architecture/lifecycle.md)** β the document lifecycle from `GraphCompose.document(...)` through `buildPdf()`.
@@ -146,6 +151,8 @@ it does.
> were reserved during a v1.5 restructure that landed under ADR 0011
> instead of multiple smaller records. No deleted ADRs.
+
+
### Showcase website (separate from docs)
- The public showcase website is **not** documentation β it lives in
[`web/`](../web/) (static GitHub Pages site) and is documented by its own
diff --git a/docs/diagrams.md b/docs/diagrams.md
index 927339dd2..327adca25 100644
--- a/docs/diagrams.md
+++ b/docs/diagrams.md
@@ -14,7 +14,7 @@ custom page flow; helpers, layout primitives, and extensions come later.
```mermaid
flowchart TD
- A[I want to generate a document] --> B{Known family?
CV / invoice / proposal / cover letter}
+ A[I want to generate a document] --> B{Known family?
invoice / proposal / receipt / rota
CV / cover letter}
B -- Yes --> T[Use a maintained template]
B -- No --> C{Mostly reads top to bottom?}
C -- Yes --> F["GraphCompose.document(...) + pageFlow(...)"]
diff --git a/docs/first-document.md b/docs/first-document.md
index 4e7a31f24..3b20b8145 100644
--- a/docs/first-document.md
+++ b/docs/first-document.md
@@ -90,9 +90,9 @@ call `.build()` yourself.
## Already a known document? Use a template
-If your document is a known family β invoice, proposal, CV, cover letter β do not
-hand-build it. A maintained template maps a typed data object into the same
-session, then you render as usual:
+If your document is one of the six shipped families β invoice, proposal, receipt,
+rota, CV, cover letter β do not hand-build it. A maintained template maps a typed
+data object into the same session, then you render as usual:
```java
import com.demcha.compose.document.templates.api.DocumentTemplate;
@@ -108,9 +108,8 @@ try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).cr
```
Templates and hand-written flow compose into the *same* `DocumentSession`, so you
-can mix them. Continue with the
-[invoice and proposal guide](templates/business-templates.md), or use the
-[CV and cover-letter quickstart](templates/v2-layered/quickstart.md).
+can mix them. The [templates overview](templates/README.md) lists every family and
+points at its guide.
## Rendering on a server
@@ -132,8 +131,7 @@ Choose the one line that matches your next task:
| Add content such as a table, timeline, chart, image, icon, emoji, or barcode | [Content and data recipes](recipes.md#content-and-data) |
| Build cards, columns, clipping, layers, backgrounds, or a canvas | [Layout and visual recipes](recipes.md#layout-and-visual-composition) |
| Add headers, footers, navigation, previews, or debug overlays | [Page behaviour and development](recipes.md#page-behaviour-output-and-development) |
-| Render an invoice or proposal | [Business templates](templates/business-templates.md) |
-| Render a CV or cover letter | [CV and cover-letter quickstart](templates/v2-layered/quickstart.md) |
+| Start from a ready-made design β invoice, proposal, receipt, rota, CV, cover letter | [Templates overview](templates/README.md) |
| Protect this document from layout drift | [Testing your document](operations/test-your-document.md) |
| Stream it from a backend | [Production rendering](operations/production-rendering.md) |
| Learn rows, layers, backgrounds, and canvases | [Layered page design](recipes/layered-page-design.md) |
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 6dc7801ca..fd44cffeb 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -16,10 +16,10 @@ tree to choose the right one for the document you're rendering.
| Question | Answer | Pick this layer |
| --- | --- | --- |
-| Is your document one of the template families (CV, cover letter, invoice, proposal)? | Yes | **Layered template preset.** Skip ahead to "Templates". |
+| Is your document one of the six [shipped template families](templates/README.md) β invoice, proposal, receipt, rota, CV, cover letter? | Yes | **Layered template preset.** Skip ahead to "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 `DocumentTemplate` and take a `BrandTheme` for visual coherence. |
-| Anything else β a report, a statement, a brochure, one-off or generated from data | β | **Raw DSL.** This is the default answer, not a fallback: the DSL is the authoring surface, and templates are presets built on it. Continue with "Quick start" below. |
+| Anything else β a report, a statement, a brochure, one-off or generated from data | β | **Raw DSL.** This is the default answer, not a fallback: the DSL is the authoring surface, and every preset is built on it. Continue with "Quick start" below. |
The DSL and the templates compose against the SAME `DocumentSession`
β a template can also live alongside hand-written DSL inside one
diff --git a/docs/templates/README.md b/docs/templates/README.md
new file mode 100644
index 000000000..97f696ec4
--- /dev/null
+++ b/docs/templates/README.md
@@ -0,0 +1,59 @@
+# Templates
+
+A template is a finished document design you feed with typed data. You supply the
+data, pick a `BrandTheme` where the preset accepts one, and it renders a consistent,
+branded document β you never position anything by hand.
+
+Reach for a template when your document is a **known kind**. Everything else β a
+report, a statement, a one-off, anything generated from application data β is
+ordinary [DSL authoring](../first-document.md), which is the default, not a fallback.
+
+> **Dependency.** Templates ship in `graph-compose-templates`, which is **not**
+> part of `graph-compose`. Add it separately, or use `graph-compose-bundle`
+> (PDF + templates + fonts + emoji).
+
+### Three words used throughout
+
+| Term | Means | You use it when |
+|---|---|---|
+| **DSL** | Authoring the document yourself with `pageFlow(...)` and content blocks | The document is not a known kind β the default |
+| **Preset** | A shipped, named design such as `ModernInvoice` or `CharcoalGold` | Your document *is* a known kind and you just supply data |
+| **Template** | The `DocumentTemplate` contract every preset implements | You want your own reusable design for a new document kind |
+
+A preset is one template. Templates and hand-written DSL compose into the same
+`DocumentSession`, so you can mix them in one document.
+
+## Business documents
+
+| Family | Presets | Data records | Start here |
+|---|---|---|---|
+| **Invoice** | 11 | `templates.data.invoice` | [Business templates](business-templates.md) |
+| **Proposal** | 4 | `templates.data.proposal` | [Business templates](business-templates.md) |
+| **Receipt** | 1 β `ModernReceipt` | `templates.data.receipt` | [The shipped families](v2-layered/README.md#the-shipped-families) |
+| **Rota** (shift schedule) | 1 β `CobaltRota` | `templates.data.rota` | [The shipped families](v2-layered/README.md#the-shipped-families) |
+
+## Profile documents
+
+| Family | Presets | Data records | Start here |
+|---|---|---|---|
+| **CV** | 26 | `templates.cv.data` (`CvDocument`) | [CV and cover-letter quickstart](v2-layered/quickstart.md) |
+| **Cover letter** | 15 | `templates.coverletter.data` (`CoverLetterDocument`) | [CV and cover-letter quickstart](v2-layered/quickstart.md) |
+
+Every preset is a static factory: `SomePreset.create()`, and `create(BrandTheme)`
+where the design accepts your palette. The exact spec type a preset takes is the
+type parameter on its `create()` β within the invoice family, for example, older
+presets take `InvoiceDocumentSpec` while newer ones take `StructuredInvoiceData`.
+
+All six families are built the same way β family data, then components, widgets and
+presets over the shared `templates.core.theme`. Receipt and rota are the newest and
+smallest, which makes them the easiest two to read end to end.
+
+See the presets rendered in the [examples gallery](../../examples/README.md) and on
+the [live showcase](https://demchaav.github.io/GraphCompose/).
+
+## Going further
+
+- **Restyle a shipped preset** β pass your own `BrandTheme`; see the family guide above.
+- **Design a new preset in an existing family** β [Authoring presets](v2-layered/authoring-presets.md).
+- **Add a whole new family** β [Template contributor guide](v2-layered/contributor-guide.md).
+- **Upgrading a pre-2.0 caller** β [Which template system should I use?](which-template-system.md).
diff --git a/docs/templates/business-templates.md b/docs/templates/business-templates.md
index f599a375e..452aeb348 100644
--- a/docs/templates/business-templates.md
+++ b/docs/templates/business-templates.md
@@ -5,10 +5,9 @@ documents: **invoices** and **proposals**. You supply a typed data
spec, pick a `BrandTheme`, and the preset renders a consistent,
branded document. You never position anything by hand.
-> For **CVs and cover letters**, use the same layered model's CV and
-> cover-letter families β see the
-> [Templates v2 (layered) quickstart](v2-layered/quickstart.md).
-> Arriving from a pre-2.0 surface? See
+> This page covers two of the six shipped families. For **receipts** and
+> **shift rotas**, and for **CVs** and **cover letters**, start from the
+> [templates overview](README.md). Arriving from a pre-2.0 surface? See
> [Which template system should I use?](which-template-system.md).
## The compose-first contract
diff --git a/docs/templates/v2-layered/README.md b/docs/templates/v2-layered/README.md
index 9b82f6135..b1a63a46c 100644
--- a/docs/templates/v2-layered/README.md
+++ b/docs/templates/v2-layered/README.md
@@ -11,12 +11,29 @@
> archived at [templates/v1-classic/](../v1-classic/README.md).
The **template surface** for building business documents on
-GraphCompose. All five families ship on it β CV (the reference
-implementation, `com.demcha.compose.document.templates.cv`),
-cover-letter, invoice, proposal, and receipt β and any new template
-family follows the same shape.
+GraphCompose. All six shipped families are built on it β CV is the
+reference implementation (`com.demcha.compose.document.templates.cv`) β
+and any new template family follows the same shape.
-This is the entry point. Pick the doc that matches your goal.
+This page is the architecture entry point. If you only want to *render*
+one of the shipped designs, start from the
+[templates overview](../README.md) instead.
+
+## The shipped families
+
+| Family | Package | Presets | Data records |
+|---|---|---|---|
+| CV | `templates.cv` | 26 | `templates.cv.data` |
+| Cover letter | `templates.coverletter` | 15 | `templates.coverletter.data` |
+| Invoice | `templates.invoice` | 11 | `templates.data.invoice` |
+| Proposal | `templates.proposal` | 4 | `templates.data.proposal` |
+| Receipt | `templates.receipt` | 1 β `ModernReceipt` | `templates.data.receipt` |
+| Rota (shift schedule) | `templates.rota` | 1 β `CobaltRota` | `templates.data.rota` |
+
+Receipt and rota are the newest and smallest, which makes them the
+easiest two to read end to end.
+
+Pick the doc below that matches your goal.
---
diff --git a/docs/templates/which-template-system.md b/docs/templates/which-template-system.md
index 50ae32543..40bfeb325 100644
--- a/docs/templates/which-template-system.md
+++ b/docs/templates/which-template-system.md
@@ -88,7 +88,7 @@ migration is the theme + data-record swap:
|---|---|
| `InvoiceTemplateV1` / `InvoiceTemplateV2` | `templates.invoice.presets.ModernInvoice` β `create()` or `create(BrandTheme)`, data record `InvoiceDocumentSpec`. |
| `ProposalTemplateV1` / `ProposalTemplateV2` | `templates.proposal.presets.ModernProposal` β same shape, data record `ProposalDocumentSpec`. |
-| `WeeklyScheduleTemplateV1` | No template yet. Model the rota on `templates.data.rota` (`StructuredRotaDocumentSpec`) and author the rendering on the canonical DSL. The `templates.data.schedule` records still ship but are **deprecated since 2.4.0**: they hold their colours in the data, know nothing of staff bands, and nothing ever rendered them. |
+| `WeeklyScheduleTemplateV1` | `templates.rota.presets.CobaltRota` β `create()`, data record `StructuredRotaDocumentSpec` (which wraps `StructuredRotaData`). The `templates.data.schedule` records still ship but are **deprecated since 2.4.0**: they hold their colours in the data, know nothing of staff bands, and nothing ever rendered them. |
### Legacy PDF API β canonical DSL
diff --git a/knowledge/claims/index.json b/knowledge/claims/index.json
index 3de8dc743..c768d81c1 100644
--- a/knowledge/claims/index.json
+++ b/knowledge/claims/index.json
@@ -85,7 +85,7 @@
"DocumentTableCell.lines": [
{
"page": "docs/recipes/tables.md",
- "line": 27,
+ "line": 39,
"heading": "Cell content β one line, several lines, or a node",
"surface": "authoring",
"stability": "stable"
@@ -94,7 +94,7 @@
"DocumentTableCell.node": [
{
"page": "docs/recipes/tables.md",
- "line": 28,
+ "line": 40,
"heading": "Cell content β one line, several lines, or a node",
"surface": "authoring",
"stability": "stable"
@@ -103,7 +103,7 @@
"DocumentTableCell.text": [
{
"page": "docs/recipes/tables.md",
- "line": 26,
+ "line": 38,
"heading": "Cell content β one line, several lines, or a node",
"surface": "authoring",
"stability": "stable"
@@ -202,7 +202,7 @@
"TableBuilder.headerRow": [
{
"page": "docs/recipes/tables.md",
- "line": 191,
+ "line": 203,
"heading": "Repeated header on page break",
"surface": "authoring",
"stability": "stable"
@@ -211,7 +211,7 @@
"TableBuilder.repeatHeader": [
{
"page": "docs/recipes/tables.md",
- "line": 190,
+ "line": 202,
"heading": "Repeated header on page break",
"surface": "authoring",
"stability": "stable"
@@ -220,7 +220,7 @@
"TableBuilder.totalRow": [
{
"page": "docs/recipes/tables.md",
- "line": 153,
+ "line": 165,
"heading": "Totals row β bold + subtle fill",
"surface": "authoring",
"stability": "stable"
@@ -229,7 +229,7 @@
"TableBuilder.zebra": [
{
"page": "docs/recipes/tables.md",
- "line": 112,
+ "line": 124,
"heading": "Zebra β alternating row fills",
"surface": "authoring",
"stability": "stable"
@@ -268,28 +268,28 @@
"table.cell-content": [
{
"page": "docs/recipes/tables.md",
- "line": 25,
+ "line": 37,
"heading": "Cell content β one line, several lines, or a node"
}
],
"table.repeat-header-across-pages": [
{
"page": "docs/recipes/tables.md",
- "line": 189,
+ "line": 201,
"heading": "Repeated header on page break"
}
],
"table.row-span": [
{
"page": "docs/recipes/tables.md",
- "line": 71,
+ "line": 83,
"heading": "Row span β merge a cell vertically"
}
],
"table.zebra-striping": [
{
"page": "docs/recipes/tables.md",
- "line": 111,
+ "line": 123,
"heading": "Zebra β alternating row fills"
}
]
@@ -362,7 +362,7 @@
"table.composed-cell-paints-but-has-no-snapshot-node": [
{
"page": "docs/recipes/tables.md",
- "line": 29,
+ "line": 41,
"heading": "Cell content β one line, several lines, or a node",
"proof": "test:ComposedCellSnapshotContractTest"
}
@@ -370,7 +370,7 @@
"table.explicit-row-style-beats-zebra": [
{
"page": "docs/recipes/tables.md",
- "line": 113,
+ "line": 125,
"heading": "Zebra β alternating row fills",
"proof": "test:TableBuilderZebraAndTotalsTest"
}
@@ -378,7 +378,7 @@
"table.header-repeats-on-every-continuation-page": [
{
"page": "docs/recipes/tables.md",
- "line": 192,
+ "line": 204,
"heading": "Repeated header on page break",
"proof": "test:TableBuilderRepeatHeaderTest"
}
@@ -386,7 +386,7 @@
"table.repeat-header-defaults-to-zero": [
{
"page": "docs/recipes/tables.md",
- "line": 193,
+ "line": 205,
"heading": "Repeated header on page break",
"proof": "test:TableBuilderRepeatHeaderTest"
}
@@ -394,7 +394,7 @@
"table.spanning-cell-height-is-sum-of-covered-rows": [
{
"page": "docs/recipes/tables.md",
- "line": 72,
+ "line": 84,
"heading": "Row span β merge a cell vertically",
"proof": "test:TableBuilderRowSpanTest"
}
diff --git a/knowledge/proofs/index.json b/knowledge/proofs/index.json
index 00cf88f25..f13afa382 100644
--- a/knowledge/proofs/index.json
+++ b/knowledge/proofs/index.json
@@ -29,7 +29,7 @@
"kind": "behavior",
"value": "table.composed-cell-paints-but-has-no-snapshot-node",
"page": "docs/recipes/tables.md",
- "line": 29,
+ "line": 41,
"heading": "Cell content β one line, several lines, or a node"
}
]
@@ -121,14 +121,14 @@
"kind": "behavior",
"value": "table.header-repeats-on-every-continuation-page",
"page": "docs/recipes/tables.md",
- "line": 192,
+ "line": 204,
"heading": "Repeated header on page break"
},
{
"kind": "behavior",
"value": "table.repeat-header-defaults-to-zero",
"page": "docs/recipes/tables.md",
- "line": 193,
+ "line": 205,
"heading": "Repeated header on page break"
}
]
@@ -141,7 +141,7 @@
"kind": "behavior",
"value": "table.spanning-cell-height-is-sum-of-covered-rows",
"page": "docs/recipes/tables.md",
- "line": 72,
+ "line": 84,
"heading": "Row span β merge a cell vertically"
}
]
@@ -154,7 +154,7 @@
"kind": "behavior",
"value": "table.explicit-row-style-beats-zebra",
"page": "docs/recipes/tables.md",
- "line": 113,
+ "line": 125,
"heading": "Zebra β alternating row fills"
}
]
From 169d47cc6ef2875b8f0423b63e2205ced4dde3b4 Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Mon, 14 Sep 2026 14:43:05 +0100
Subject: [PATCH 4/5] docs(onboarding): restore direct starting routes
---
README.md | 15 ++++++++++++---
docs/first-document.md | 9 ++++++---
2 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index cd9125956..96a799f58 100644
--- a/README.md
+++ b/README.md
@@ -56,8 +56,17 @@ and other documents assembled from application data.
module.
- **Templates** β 58 maintained designs across six document families in the separate
`graph-compose-templates` module: invoice, proposal, receipt and rota for business
- documents, CV and cover letter for profiles. See the
- [templates overview](./docs/templates/README.md).
+ documents, CV and cover letter for profiles. Start with
+ [invoice and proposal](./docs/templates/business-templates.md),
+ [CV and cover letter](./docs/templates/v2-layered/quickstart.md), or the
+ [templates overview](./docs/templates/README.md) for all six families.
+
+### Choose a starting point
+
+- [Start with a first PDF](#create-your-first-pdf).
+- [Use a built-in template](./docs/templates/README.md).
+- Export to [PowerPoint](./render-pptx/README.md) or
+ [DOCX](./render-docx/README.md).
## Create your first PDF
@@ -235,7 +244,7 @@ not need the others, and you never need the engine internals to author a documen
|---|---|
| **Understand the model** behind the example above | [Your first document](./docs/first-document.md) β a guided five-minute build |
| **Add a feature** β table, list, chart, timeline, image, header, footer, barcode, page-break rule | [Recipes](./docs/recipes.md) β the complete task index |
-| **Start from a ready-made design** β invoice, proposal, receipt, rota, CV, cover letter | [Templates](./docs/templates/README.md) β 58 presets across six families |
+| **Start from a ready-made design** | [Invoice and proposal](./docs/templates/business-templates.md) Β· [CV and cover letter](./docs/templates/v2-layered/quickstart.md) Β· [receipt, rota, and all 58 presets](./docs/templates/README.md) |
| **Prove an upgrade did not move my document** | [Testing your document](./docs/operations/test-your-document.md) β layout snapshots and visual diffs |
| **Render from a server** | [Production rendering](./docs/operations/production-rendering.md) β streaming, concurrency, failure handling; the [Spring Boot streaming example](./examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java) is a working endpoint |
diff --git a/docs/first-document.md b/docs/first-document.md
index 3b20b8145..68e6fac1a 100644
--- a/docs/first-document.md
+++ b/docs/first-document.md
@@ -108,8 +108,9 @@ try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).cr
```
Templates and hand-written flow compose into the *same* `DocumentSession`, so you
-can mix them. The [templates overview](templates/README.md) lists every family and
-points at its guide.
+can mix them. Continue with the [invoice and proposal guide](templates/business-templates.md),
+the [CV and cover-letter quickstart](templates/v2-layered/quickstart.md), or the
+[templates overview](templates/README.md) for receipt, rota, and the complete catalogue.
## Rendering on a server
@@ -131,7 +132,9 @@ Choose the one line that matches your next task:
| Add content such as a table, timeline, chart, image, icon, emoji, or barcode | [Content and data recipes](recipes.md#content-and-data) |
| Build cards, columns, clipping, layers, backgrounds, or a canvas | [Layout and visual recipes](recipes.md#layout-and-visual-composition) |
| Add headers, footers, navigation, previews, or debug overlays | [Page behaviour and development](recipes.md#page-behaviour-output-and-development) |
-| Start from a ready-made design β invoice, proposal, receipt, rota, CV, cover letter | [Templates overview](templates/README.md) |
+| Start from an invoice or proposal design | [Business templates](templates/business-templates.md) |
+| Start from a CV or cover-letter design | [CV and cover-letter quickstart](templates/v2-layered/quickstart.md) |
+| Start from a receipt or rota design, or browse every preset | [Templates overview](templates/README.md) |
| Protect this document from layout drift | [Testing your document](operations/test-your-document.md) |
| Stream it from a backend | [Production rendering](operations/production-rendering.md) |
| Learn rows, layers, backgrounds, and canvases | [Layered page design](recipes/layered-page-design.md) |
From e82df9d18b6f275b95d3bc6cfd1d07c60fa77430 Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Mon, 14 Sep 2026 15:46:13 +0100
Subject: [PATCH 5/5] docs(onboarding): relink module READMEs and fix wrong API
names
The README rewrite moved the "which artifact?" table out of Installation into
the collapsed "Output formats and optional modules" block and dropped the module
list. Seven module READMEs (core, render-pdf, templates, testing, wrapper, bundle,
emoji) were left with no inbound link, and all ten, plus four docs pages, still
sent readers to a table that is no longer under #installation.
- README: anchor the module table as #which-artifact and link each artifact in
it, and the fonts and emoji companions, to its own README.
- Module READMEs and the quickstart, using-templates, which-template-system and
2.0 migration pages: point their "which artifact?" links at #which-artifact.
- examples/README.md: the Twin Output row no longer says the root README shows
the PowerPoint render beside the PDF.
- recipes.md: z-order is the zIndex argument of position(...), not a zIndex(...)
call, and a row takes a narrower set of blocks than a section does.
- recipes/tables.md: colSpan and rowSpan are DocumentTableCell methods.
- recipes/charts.md: showGridLines and showTickLabels are the AxisSpec toggles;
showCategoryLabels sits on the bar or line spec.
- capabilities.md: buildPptx takes a Path; there is no no-argument overload.
- templates/README.md: the invoice family takes three spec types, not two.
---
README.md | 23 +++++++++++---------
bundle/README.md | 2 +-
core/README.md | 2 +-
docs/capabilities.md | 2 +-
docs/migration/v2.0.0-modules.md | 2 +-
docs/recipes.md | 9 ++++----
docs/recipes/charts.md | 2 +-
docs/recipes/tables.md | 2 +-
docs/templates/README.md | 5 +++--
docs/templates/v2-layered/quickstart.md | 2 +-
docs/templates/v2-layered/using-templates.md | 2 +-
docs/templates/which-template-system.md | 2 +-
emoji/README.md | 2 +-
examples/README.md | 2 +-
fonts/README.md | 2 +-
render-docx/README.md | 2 +-
render-pdf/README.md | 2 +-
render-pptx/README.md | 2 +-
templates/README.md | 2 +-
testing/README.md | 2 +-
wrapper/README.md | 2 +-
21 files changed, 39 insertions(+), 34 deletions(-)
diff --git a/README.md b/README.md
index 96a799f58..f0383a133 100644
--- a/README.md
+++ b/README.md
@@ -252,26 +252,29 @@ Contributing to GraphCompose itself is a separate path: start at
[Contributing](./CONTRIBUTING.md). The [documentation index](./docs/README.md) is a
reference catalogue β useful for finding a page, never required reading.
+
Output formats and optional modules
-PDF is the production, fixed-layout output included by `graph-compose`.
+PDF is the production, fixed-layout output: [`graph-compose`](./wrapper/README.md)
+includes the [`graph-compose-render-pdf`](./render-pdf/README.md) backend.
| Need | Add |
|---|---|
-| Built-in templates, bundled fonts, and emoji together | `graph-compose-bundle` |
-| Built-in document templates β invoice, proposal, receipt, rota, CV, cover letter | `graph-compose-templates` |
-| Editable PowerPoint output (**beta**) | `graph-compose-render-pptx` |
-| Semantic DOCX output (**partial**) | `graph-compose-render-docx` |
-| Layout snapshots and PDF visual diffs in tests | `graph-compose-testing` |
-| Only the authoring surface and engine | `graph-compose-core` |
+| Built-in templates, bundled fonts, and emoji together | [`graph-compose-bundle`](./bundle/README.md) |
+| Built-in document templates β invoice, proposal, receipt, rota, CV, cover letter | [`graph-compose-templates`](./templates/README.md) |
+| Editable PowerPoint output (**beta**) | [`graph-compose-render-pptx`](./render-pptx/README.md) |
+| Semantic DOCX output (**partial**) | [`graph-compose-render-docx`](./render-docx/README.md) |
+| Layout snapshots and PDF visual diffs in tests | [`graph-compose-testing`](./testing/README.md) |
+| Only the authoring surface and engine | [`graph-compose-core`](./core/README.md) |
`graph-compose-bundle` includes PDF, templates, fonts, and emoji. PPTX and DOCX
are not included in the bundle; add their render modules separately when needed.
-Modules use the same GraphCompose version. Bundled fonts and colour emoji have their
-own release lines. Consult the [module guide](./docs/migration/v2.0.0-modules.md) only
-when you need to split dependencies, and the
+Modules use the same GraphCompose version. [Bundled fonts](./fonts/README.md) and
+[colour emoji](./emoji/README.md) have their own release lines. Consult the
+[module guide](./docs/migration/v2.0.0-modules.md) only when you need to split
+dependencies, and the
[backend capability matrix](./docs/architecture/backend-capability-matrix.md) before
relying on a feature outside PDF.
diff --git a/bundle/README.md b/bundle/README.md
index 533c8a14a..5725c1660 100644
--- a/bundle/README.md
+++ b/bundle/README.md
@@ -33,5 +33,5 @@ to compatible independent versions:
dependencies { implementation("io.github.demchaav:graph-compose-bundle:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
diff --git a/core/README.md b/core/README.md
index daad0524c..a82937d6f 100644
--- a/core/README.md
+++ b/core/README.md
@@ -53,5 +53,5 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { implementation("io.github.demchaav:graph-compose-core:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
diff --git a/docs/capabilities.md b/docs/capabilities.md
index bd8f1dba4..2d56ea1d3 100644
--- a/docs/capabilities.md
+++ b/docs/capabilities.md
@@ -62,7 +62,7 @@ tracks what is `Partial` or `Planned`.
| Stream to a caller-owned stream | `writePdf(OutputStream)` | Stable | [Streaming](recipes/streaming.md) |
| In-memory bytes | `toPdfBytes()` | Stable | [Getting started](getting-started.md) |
| Page preview images | `toImage(pageIndex, dpi)`, `toImages(dpi)` | Stable | [Streaming and output](recipes/streaming.md) |
-| Geometry-identical PowerPoint deck | `buildPptx()`, `buildPptx(Path)`, `writePptx(OutputStream)`, `toPptxBytes()` β needs `graph-compose-render-pptx` on the classpath | Experimental (`@Beta`, first shipped in 2.1.0) | [Backend capability matrix](architecture/backend-capability-matrix.md) |
+| Geometry-identical PowerPoint deck | `buildPptx(Path)`, `writePptx(OutputStream)`, `toPptxBytes()` β needs `graph-compose-render-pptx` on the classpath | Experimental (`@Beta`, first shipped in 2.1.0) | [Backend capability matrix](architecture/backend-capability-matrix.md) |
| Editable Word (semantic) | `export(new DocxSemanticBackend())` | Stable (semantic, not PDF parity) | [Troubleshooting](troubleshooting.md) |
| PDF chrome (metadata / watermark / header / footer / protection) | `metadata(...)`, `watermark(...)`, `header(...)`, `footer(...)`, `protect(...)` | Stable | [PDF chrome](recipes/pdf-chrome.md) |
| Repeating node-based page zones | `chrome().zone(DocumentPageZone.header(...) / footer(...))` | Stable | [PDF chrome](recipes/pdf-chrome.md) |
diff --git a/docs/migration/v2.0.0-modules.md b/docs/migration/v2.0.0-modules.md
index ce5d898ae..40afead54 100644
--- a/docs/migration/v2.0.0-modules.md
+++ b/docs/migration/v2.0.0-modules.md
@@ -115,7 +115,7 @@ plain PDF rendering; they touch template authoring and one package rename.
## Reference
-- [README install matrix](../../README.md#installation) β the same table with copy-paste snippets.
+- [README module table](../../README.md#which-artifact) β the artifact to add for each need; the [install snippet](../../README.md#installation) sits above it.
- [ADR 0016 β multi-module packaging](../adr/0016-multi-module-packaging.md) β why the split
is shaped this way, including the back-compat wrapper decision.
- [API stability policy Β§ 3](../api-stability.md#3-deprecation-window) β the packaging entry
diff --git a/docs/recipes.md b/docs/recipes.md
index 75b611f19..43b2f5bf5 100644
--- a/docs/recipes.md
+++ b/docs/recipes.md
@@ -7,9 +7,10 @@ authoring API; public application code should not import
## Where content goes
-Most document features are blocks in the same `pageFlow`. A section, module, row,
-or card is just a container that groups those blocks; it does not introduce a
-different authoring API.
+Most document features are blocks in the same `pageFlow`. A section, module, or card
+is a container that groups those blocks with the same calls. A row places blocks
+side by side and takes a narrower set β paragraphs, images, shapes, barcodes, and
+sections β so a chart or a table goes into a section inside the row.
```text
DocumentSession
@@ -56,7 +57,7 @@ pagination or backend limits behave when they apply.
| [Overlapping content](recipes/layered-page-design.md) | `addLayerStack(stack -> ...)` | alignment, offsets, z-index, and when to use a container or canvas |
| [A clipped circle, ellipse, or custom container](recipes/shape-as-container.md) | `addContainer(...)` | outline, child alignment, `CLIP_PATH`, bounds, visible overflow |
| [A repeating page tint, sidebar, or band](recipes/page-backgrounds.md) | `pageBackgrounds(...)` | full fills, partial fills, bleed, and layering |
-| [Rotation, scale, or layer ordering](recipes/transforms.md) | `rotate(...)` / `scale(...)` / `zIndex(...)` | transform origin, clipped transforms, deterministic overlap |
+| [Rotation, scale, or layer ordering](recipes/transforms.md) | `rotate(...)` / `scale(...)` / `position(node, x, y, align, zIndex)` | transform origin, clipped transforms, deterministic overlap |
| [Pixel-precise placement](recipes/absolute-placement.md) | `addCanvas(...)` | fixed box size, `(x, y)` positions, clipping, appropriate use cases |
| [A block that should not split badly](recipes/keep-together.md) | `keepTogether()` / `keepWithNext()` | sections, lines, timeline entries, relocation at page breaks |
| [A theme shared across documents](recipes/themes.md) | `BrandTheme` | palette, typography, spacing, decoration, preset-level reuse |
diff --git a/docs/recipes/charts.md b/docs/recipes/charts.md
index c9c975b16..bc90716b8 100644
--- a/docs/recipes/charts.md
+++ b/docs/recipes/charts.md
@@ -40,7 +40,7 @@ the chart width from its container and uses the spec's size policy for height.
| Use brand colours | pass a `ChartStyle` with `.seriesPaint(index, paint)` |
| Round bar corners or change bar width | `.barCornerRadius(...)` / `.barWidthRatio(...)` on `ChartStyle` |
| Add line dots and tune the stroke | `.pointMarker(...)` / `.lineWidth(...)` on `ChartStyle` |
-| Hide grid, tick labels, or category labels | use `AxisSpec` toggles and `.showCategoryLabels(false)` |
+| Hide grid, tick labels, or category labels | `.showGridLines(false)` / `.showTickLabels(false)` on `AxisSpec`; `.showCategoryLabels(false)` on a bar or line spec |
| Put the chart on a coloured card | call `chart(...)` inside a `softPanel(...)` section and match `valueLabelHalo` to the panel |
| Catch chart movement after an upgrade | snapshot the containing `DocumentSession` with `LayoutSnapshotAssertions` |
diff --git a/docs/recipes/tables.md b/docs/recipes/tables.md
index 5ce7e5783..f82b6993e 100644
--- a/docs/recipes/tables.md
+++ b/docs/recipes/tables.md
@@ -23,7 +23,7 @@ Start there. Add style or pagination behaviour only when the document needs it.
| Style every cell | `.defaultCellStyle(...)` |
| Add several lines to one cell | `DocumentTableCell.lines(String...)` |
| Put any document node in a cell | `DocumentTableCell.node(DocumentNode)` |
-| Merge cells across columns or rows | `.colSpan(...)` / `.rowSpan(...)` |
+| Merge cells across columns or rows | `DocumentTableCell.text(...).colSpan(...)` / `.rowSpan(...)` |
| Colour alternating rows | `.zebra(odd, even)` |
| Give the total a branded style | `.totalRow(style, values...)` |
| Repeat headings after a page break | `.repeatHeader()` |
diff --git a/docs/templates/README.md b/docs/templates/README.md
index 97f696ec4..37c4ec849 100644
--- a/docs/templates/README.md
+++ b/docs/templates/README.md
@@ -41,8 +41,9 @@ A preset is one template. Templates and hand-written DSL compose into the same
Every preset is a static factory: `SomePreset.create()`, and `create(BrandTheme)`
where the design accepts your palette. The exact spec type a preset takes is the
-type parameter on its `create()` β within the invoice family, for example, older
-presets take `InvoiceDocumentSpec` while newer ones take `StructuredInvoiceData`.
+type parameter on its `create()` β the invoice family alone uses three:
+`InvoiceDocumentSpec` (`ModernInvoice`, `ClassicInvoice`), `StructuredInvoiceDocumentSpec`
+(`ConsultingInvoice`, `LumaStudioInvoice`) and `StructuredInvoiceData` (the other seven).
All six families are built the same way β family data, then components, widgets and
presets over the shared `templates.core.theme`. Receipt and rota are the newest and
diff --git a/docs/templates/v2-layered/quickstart.md b/docs/templates/v2-layered/quickstart.md
index dccdcdfbe..f2fc5696b 100644
--- a/docs/templates/v2-layered/quickstart.md
+++ b/docs/templates/v2-layered/quickstart.md
@@ -6,7 +6,7 @@ example that renders a CV PDF.
> **Dependency.** CV and cover-letter presets ship in
> `graph-compose-templates`, which is **not** included in `graph-compose`. Add it
> separately, or use `graph-compose-bundle`, which includes PDF, templates, fonts,
-> and emoji. See the [README install matrix](../../../README.md#installation).
+> and emoji. See the [README module table](../../../README.md#which-artifact).
---
diff --git a/docs/templates/v2-layered/using-templates.md b/docs/templates/v2-layered/using-templates.md
index a85a50e95..0b6e34310 100644
--- a/docs/templates/v2-layered/using-templates.md
+++ b/docs/templates/v2-layered/using-templates.md
@@ -10,7 +10,7 @@ it sets up the conceptual model in 5 minutes.
> **Dependency.** These presets ship in the opt-in `graph-compose-templates` artifact (not
> bundled in `graph-compose`); add it, or use `graph-compose-bundle`. See the
-> [README install matrix](../../../README.md#installation).
+> [README module table](../../../README.md#which-artifact).
---
diff --git a/docs/templates/which-template-system.md b/docs/templates/which-template-system.md
index 40bfeb325..9fde5867f 100644
--- a/docs/templates/which-template-system.md
+++ b/docs/templates/which-template-system.md
@@ -8,7 +8,7 @@ invoice, proposal}`, every preset a final class with a
> **Dependency.** These presets ship in the opt-in `graph-compose-templates` artifact β not
> bundled in `graph-compose`. Add it, or depend on `graph-compose-bundle`. See the
-> [README install matrix](../../README.md#installation).
+> [README module table](../../README.md#which-artifact).
Through the 1.x line this page was a decision guide between two parallel
surfaces. On the 2.0 line the decision is gone; what remains here is the
diff --git a/emoji/README.md b/emoji/README.md
index 5df7af4ee..98b247510 100644
--- a/emoji/README.md
+++ b/emoji/README.md
@@ -48,4 +48,4 @@ Independent version line (`emoji-v*`) β pinned, not the engine version:
dependencies { implementation("io.github.demchaav:graph-compose-emoji:1.0.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
diff --git a/examples/README.md b/examples/README.md
index c932f5ed9..2bd6377f4 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -105,7 +105,7 @@ are with the canonical DSL, then jump to its detailed section below.
| [Module-first Profile](#module-first-profile) | Authoring directly against `DocumentSession.module(...).paragraph(...)` β DSL-direct, no template | [PDF](../assets/readme/examples/module-first-profile.pdf) Β· [Source](src/main/java/com/demcha/examples/flagships/ModuleFirstFileExample.java) |
| **Engine Showcase** | Single-page cinematic brand promo β semantic-graph β polished-PDFs visual metaphor with rounded clip frame, magazine headline lockup, KPI cards, capability columns; source of the README hero image | [Source](src/main/java/com/demcha/examples/flagships/EngineShowcase.java) |
| **Engine Deck** | Multi-page **landscape** capability deck β page 1 is a banner infographic (DSL code β engine β backends β **real rendered-document thumbnails**), then an authoring-pipeline walkthrough, and two pages of **real benchmark data** (GraphCompose vs iText 9 vs JasperReports) loaded from a bundled result file and drawn as tables + native charts; the landscape companion to Engine Showcase. The same composition also renders as a **geometry-identical PowerPoint deck** (one page = one editable slide) through `buildPptx(Path)` | [PDF](../assets/readme/examples/engine-deck.pdf) Β· [Source](src/main/java/com/demcha/examples/flagships/EngineDeckExample.java) Β· [PPTX source](src/main/java/com/demcha/examples/flagships/EngineDeckPptxExample.java) |
-| **Twin Output** | The dual-output hook stated by the artifact itself β a single 16:9 page written once and emitted **twice from the same session**: `buildPdf()` and `buildPptx(...)` produce a print-ready PDF and a PowerPoint slide with identical geometry where text, panels, and vectors stay native, editable shapes (only the clip-masked logo art lands as a picture); the root README shows PowerPoint's own render of the slide next to the PDF | [PDF](../assets/readme/examples/twin-output.pdf) Β· [PPTX](../assets/readme/examples/twin-output.pptx) Β· [Source](src/main/java/com/demcha/examples/flagships/TwinOutputExample.java) |
+| **Twin Output** | The dual-output hook stated by the artifact itself β a single 16:9 page written once and emitted **twice from the same session**: `buildPdf()` and `buildPptx(...)` produce a print-ready PDF and a PowerPoint slide with identical geometry where text, panels, and vectors stay native, editable shapes (only the clip-masked logo art lands as a picture) | [PDF](../assets/readme/examples/twin-output.pdf) Β· [PPTX](../assets/readme/examples/twin-output.pptx) Β· [Source](src/main/java/com/demcha/examples/flagships/TwinOutputExample.java) |
| **Maven Central Banner** | Five 16:9 slides opening on the "Available on Maven Central" brand banner β the `GraphCompose` wordmark, an `io.github.demchaav:graph-compose` coordinate card, `JAVA 17+ / PDF / PPTX / AUTO-PAGINATION` tags, and a `code β layout β PDF/PPTX` diagram whose amber connectors branch to both backends, composed as one full-bleed `CanvasLayerNode` β then how a document is authored, how it measures against the field, how it scales, and five bundled scripts with Hebrew and Arabic running right to left. **The README hero is page one.** Emitted as an editable PowerPoint deck via `buildPptx(Path)`; panels and text stay native shapes, only the badge checkmark rasterises | [PDF](../assets/readme/examples/maven-banner.pdf) Β· [PPTX](../assets/readme/examples/maven-banner.pptx) Β· [Source](src/main/java/com/demcha/examples/flagships/MavenBannerPptxExample.java) |
### π§± Core DSL
diff --git a/fonts/README.md b/fonts/README.md
index 1423cfef8..2c2937dd5 100644
--- a/fonts/README.md
+++ b/fonts/README.md
@@ -98,5 +98,5 @@ Independent version line (`fonts-v*`) β pinned, not the engine version:
dependencies { implementation("io.github.demchaav:graph-compose-fonts:1.1.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation);
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact);
background in the [v1.8.0 fonts migration note](../docs/migration/v1.8.0-fonts.md).
diff --git a/render-docx/README.md b/render-docx/README.md
index a47ccfaf6..0a4de55e3 100644
--- a/render-docx/README.md
+++ b/render-docx/README.md
@@ -102,5 +102,5 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { implementation("io.github.demchaav:graph-compose-render-docx:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
diff --git a/render-pdf/README.md b/render-pdf/README.md
index 984eced7c..76c611eed 100644
--- a/render-pdf/README.md
+++ b/render-pdf/README.md
@@ -52,5 +52,5 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { implementation("io.github.demchaav:graph-compose-render-pdf:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
diff --git a/render-pptx/README.md b/render-pptx/README.md
index 555981596..0075e202d 100644
--- a/render-pptx/README.md
+++ b/render-pptx/README.md
@@ -157,7 +157,7 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { implementation("io.github.demchaav:graph-compose-render-pptx:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
---
diff --git a/templates/README.md b/templates/README.md
index 182beb6cb..8eb5408d2 100644
--- a/templates/README.md
+++ b/templates/README.md
@@ -53,5 +53,5 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { implementation("io.github.demchaav:graph-compose-templates:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
diff --git a/testing/README.md b/testing/README.md
index 5178a58ea..977b31eb4 100644
--- a/testing/README.md
+++ b/testing/README.md
@@ -55,5 +55,5 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { testImplementation("io.github.demchaav:graph-compose-testing:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).
diff --git a/wrapper/README.md b/wrapper/README.md
index 3f8a94766..28b88cb8b 100644
--- a/wrapper/README.md
+++ b/wrapper/README.md
@@ -32,5 +32,5 @@ Same version as the rest of the GraphCompose train (lockstep):
dependencies { implementation("io.github.demchaav:graph-compose:2.3.0") }
```
-The full "which artifact?" table: [root README β Installation](../README.md#installation).
+The full "which artifact?" table: [root README β Output formats and optional modules](../README.md#which-artifact).
Upgrading from 1.x: [modules migration guide](../docs/migration/v2.0.0-modules.md).