diff --git a/README.md b/README.md
index b30d4193f..f0383a133 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,60 @@
> Β· π‘ **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
+
+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.
-## Why GraphCompose
+- **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.
-- **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)).
+Use it for server-side invoices, proposals, CVs, reports, statements, schedules,
+and other documents assembled from application data.
-Sits between **iText** (low-level page primitives) and **JasperReports** (XML-template-driven layout): a Java DSL describes the document semantically, the engine renders.
+### Outputs and templates
-## Installation
+- **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** β 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. 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.
-**Requires Java 17+** (enforced by the build).
+### 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
+
+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 +86,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 +107,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 +120,234 @@ 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
-
-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).
-
-
-```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;
-
-import java.nio.file.Path;
-
-class Hello {
- public static void main(String[] args) throws Exception {
- // A small inline palette β swap these for your own brand colours.
- DocumentColor cream = DocumentColor.rgb(252, 248, 240);
- DocumentColor panel = DocumentColor.rgb(244, 238, 228);
- DocumentColor accent = DocumentColor.rgb(196, 153, 76);
- DocumentTextStyle h1 = DocumentTextStyle.builder()
- .fontName(FontName.HELVETICA).size(28)
- .decoration(DocumentTextDecoration.BOLD)
- .color(DocumentColor.rgb(20, 60, 75)).build();
- DocumentTextStyle body = DocumentTextStyle.builder()
- .fontName(FontName.HELVETICA).size(11)
- .color(DocumentColor.rgb(34, 38, 50)).build();
-
- try (DocumentSession document = GraphCompose.document(Path.of("hello.pdf"))
- .pageSize(DocumentPageSize.A4)
- .pageBackground(cream)
- .margin(28, 28, 28, 28)
- .create()) {
+Run `Hello.main()`. The file `hello.pdf` appears in the working directory.
- 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))));
+That program contains the whole basic model:
- document.buildPdf();
- }
- }
-}
+```text
+GraphCompose.document(...) β pageFlow(...) β content blocks β buildPdf()
```
-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).
-
-### Next steps
+- `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.
-- [**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.
+Styling is optional. Introduce `DocumentTextStyle` after the document structure says
+what it needs to say.
-## One source β a PDF and an editable PowerPoint deck
+## Grow it into a real document
-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.
+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.
-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.
-
-
+
```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
-}
+document.pageFlow(page -> page
+ .module("Summary", module -> module.paragraph(summary))
+ .module("Skills", module -> module.bullets(skills))
+ .module("Projects", module -> module.rows(projects)));
```
-
-
-| twin-output.pdf β rendered by the PDF backend |
-twin-output.pptx β the same page, as PowerPoint itself renders it |
-
-
- |
- |
-
-
+The same flow accepts the common building blocks directly. You do not need a new
+document model for each feature β a labelled bar chart is one more block in `pageFlow`:
-
-
-
-
- β 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).
-
+
+```java
+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;
-## What's new in 2.0
+import java.nio.file.Path;
-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.
+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();
+}
+```
-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).
+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.
-## 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
-
-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.
+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.
-```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)"]
-```
+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.
-Full detail: [architecture overview](./docs/architecture/overview.md) · [package map](./docs/architecture/package-map.md) · [lifecycle](./docs/architecture/lifecycle.md).
+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.
-### Modules
+## What to read next
-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/`).
+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.
-- **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`
+| 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 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 |
-See [CONTRIBUTING](./CONTRIBUTING.md) for the branch-routing table and the full build / verify flow.
+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.
-## Scope and comparison
+
+
+Output formats and optional modules
-### Output support
+PDF is the production, fixed-layout output: [`graph-compose`](./wrapper/README.md)
+includes the [`graph-compose-render-pdf`](./render-pdf/README.md) backend.
-| 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. |
+| Need | Add |
+|---|---|
+| 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) |
-### Text & internationalization
+`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.
-- 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](./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.
-### 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/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/README.md b/docs/README.md
index a51471f4b..c589a53e3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,26 +1,47 @@
# 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 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)
+ 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) |
+| 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) |
+| 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) |
---
@@ -34,35 +55,52 @@ back here.
- **[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.
-### 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.
+### 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.
+- **[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/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.
+- **[recipes/absolute-placement.md](recipes/absolute-placement.md)** β pixel-precise canvas placement.
+- **[font-coverage.md](font-coverage.md)** β WinAnsi limits, `β` vs `β’`, and glyph fallback.
-### Operations
+### 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.
-- **[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.
+### 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 & roadmap
+### 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.
@@ -78,23 +116,22 @@ 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/charts.md](recipes/charts.md)** β native vector bar/line/area/pie charts.
-- **[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/streaming.md](recipes/streaming.md)** β streaming PDFs to HTTP responses.
-- **[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.
-- **[recipes/absolute-placement.md](recipes/absolute-placement.md)** β pixel-precise canvas placement.
-- **[font-coverage.md](font-coverage.md)** β WinAnsi limits, `β` vs `β’`, and glyph fallback.
+
+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()`.
+- **[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.
-### Architecture Decision Records (ADRs)
+### 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.
@@ -114,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/capabilities.md b/docs/capabilities.md
index 32697c491..2d56ea1d3 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) |
-| 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) |
+| Page preview images | `toImage(pageIndex, dpi)`, `toImages(dpi)` | Stable | [Streaming and output](recipes/streaming.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 | [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/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 5c6463c06..68e6fac1a 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.
@@ -85,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;
@@ -102,9 +107,10 @@ 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 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
@@ -119,11 +125,16 @@ 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) |
+| 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) |
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/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/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..43b2f5bf5 100644
--- a/docs/recipes.md
+++ b/docs/recipes.md
@@ -5,7 +5,76 @@ 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, 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
+βββ 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(...)` / `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 |
+
+### 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..bc90716b8 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 | `.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` |
+
+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..f82b6993e 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 | `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()` |
+
+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/README.md b/docs/templates/README.md
new file mode 100644
index 000000000..37c4ec849
--- /dev/null
+++ b/docs/templates/README.md
@@ -0,0 +1,60 @@
+# 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()` β 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
+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 27db24acb..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
@@ -28,6 +27,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/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/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.
---
diff --git a/docs/templates/v2-layered/quickstart.md b/docs/templates/v2-layered/quickstart.md
index 6665150ab..f2fc5696b 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 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 50ae32543..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
@@ -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/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/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"
}
]
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).