diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3cabb7df7..ebcf46633 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -274,7 +274,7 @@ jobs:
cache: maven
- name: Restore benchmark history cache
- uses: actions/cache@v5
+ uses: actions/cache@v6
with:
path: benchmarks/target/benchmarks/current-speed
key: benchmark-current-speed-${{ github.ref_name }}-${{ github.run_id }}
diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml
index 37c5dc1f8..5335b4ff7 100644
--- a/.github/workflows/deploy-web.yml
+++ b/.github/workflows/deploy-web.yml
@@ -10,19 +10,19 @@ name: Deploy web showcase to GitHub Pages
# (one-time switch from "Deploy from a branch"). Until that switch is made,
# this workflow builds the artifact but Pages keeps serving the old source.
#
-# Triggers: every push to `main` AND every `v*` release tag. The tag trigger
-# is the reliable one for releases — the v1.8.0 release fast-forwarded `main`
-# with 67 changed `web/` files yet the old `paths: web/**` filter silently
-# skipped the deploy, so the live site kept serving the previous release. We
-# dropped the paths filter rather than depend on it: `main` is only pushed at
-# releases / hotfixes, so deploying unconditionally is cheap and correct, and
-# the `v*` tag is a second guaranteed trigger. `concurrency` de-dupes the two
-# triggers a release fires.
+# Triggers: every push to `main` (no `paths` filter). `main` is only pushed at
+# releases / hotfixes, so deploying unconditionally is cheap and correct — the
+# v1.8.0 release fast-forwarded `main` with 67 changed `web/` files yet the old
+# `paths: web/**` filter silently skipped the deploy, leaving the live site on
+# the previous release. A `v*` tag trigger was tried as a backup but removed:
+# GitHub Pages refuses to deploy from a tag ref (the `github-pages` environment
+# only allows `main`), so it failed every release without ever deploying — and
+# via `cancel-in-progress` could even cancel the good `main` run. The
+# unconditional `main` trigger covers releases reliably on its own.
on:
push:
branches: [main]
- tags: ["v*"]
workflow_dispatch:
permissions:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 782755d47..9a7cddcfe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,7 @@
All notable changes to GraphCompose are documented here. Versions
follow semantic versioning; release dates are ISO 8601.
-## v1.9.0 — unreleased
+## v1.9.0 — 2026-06-29
In-document navigation. Rendered PDFs can now declare named **anchors** and
**internal links** that jump to them — clickable tables of contents,
@@ -305,6 +305,18 @@ PDF `GoTo` actions. External links are unchanged.
— a paginated catalogue of the entire bundled emoji set (every indexed glyph,
drawn inline).
+### Build
+
+- **The README hero banner is now version-stamped and re-rendered on release.**
+ `EngineDeckExample` reads its version and codename from a filtered
+ `banner.properties` (`@project.version@`) instead of hardcoded constants, and
+ the new `ReadmeBannerRenderer` writes
+ `assets/readme/repository_showcase_render.png` straight from the engine via
+ `DocumentSession.toImage(...)` — no PDF-rasterize round-trip.
+ `cut-release.ps1` re-renders and stages the hero on every tag, and
+ `VersionConsistencyGuardTest` fails the build if the banner version is ever
+ hardcoded again.
+
### Tests
- `InternalLinkAnchorTest` (PDFBox assertions): forward and backward references
diff --git a/README.md b/README.md
index 66e97b8ce..fb40339be 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
> **Release status** —
-> 🟢 **Latest stable**: [v1.8.0](https://github.com/DemchaAV/GraphCompose/releases/tag/v1.8.0) — codenamed **"illustrative"**: native vector charts, SVG & gradient graphics, free-form clipping, and a leaner engine artifact. **[What's new in v1.8 ↓](#whats-new-in-v18)**
+> 🟢 **Latest stable**: [v1.9.0](https://github.com/DemchaAV/GraphCompose/releases/tag/v1.9.0) — codenamed **"navigable"**: in-document navigation (anchors, internal links, a clickable TOC, page references & bookmarks), multi-section documents, and inline chips / SVG icons / colour emoji. **[What's new in v1.9 ↓](#whats-new-in-v19)**
> · 🟡 **In develop**: next cycle — open (see [CHANGELOG](./CHANGELOG.md))
> · See [API stability policy](./docs/api-stability.md) for tier definitions.
@@ -51,22 +51,19 @@
Sits between **iText** (low-level page primitives) and **JasperReports** (XML-template-driven layout): a Java DSL describes the document semantically, the engine renders.
-## What's new in v1.8
+## What's new in v1.9
-The **"illustrative"** release — the engine gains a vector-graphics dimension.
+The **"navigable"** release — a rendered PDF becomes a document you can move through.
-
-
-
-
-- **Native vector charts** — bar / line / pie, inline sparklines, and `MONOTONE` / `SMOOTH` line interpolation, drawn as native PDF Béziers (no rasterization): `section.chart(ChartSpec.line()…, style)`.
-- **SVG path & icon import** — `SvgIcon.parse(svg)` turns SVG into native vector geometry; recolour per use and place with `addSvgIcon(icon, width, align)`.
-- **Gradients & free-form clipping** — linear / radial `DocumentPaint` fills and arbitrary `ShapeOutline.Path` clip regions.
-- **Block-level alignment** — `addAligned(HorizontalAlign.CENTER, node)` centres or right-aligns any fixed node without a wrapper container.
-- **`keepTogether()` pagination** — keep a section from splitting across a page break.
-- **Leaner publication** — the bundled Google fonts moved to the independently-versioned `graph-compose-fonts` artifact, so the engine jar dropped from ~20 MB to ~2 MB. Pure-text / standard-14 documents need nothing extra; add `graph-compose-fonts` (or `graph-compose-bundle`) to keep the bundled families — see the [migration note](./docs/migration/v1.8.0-fonts.md).
+- **In-document navigation** — named `anchor(...)` targets and internal `link(...)` jumps emitted as native PDF `GoTo` actions: clickable cross-references, `[text](#heading)`-style links, and bidirectional footnotes (`DocumentLinkTarget` unifies internal and external links).
+- **Native table of contents & page references** — `addTableOfContents(toc -> toc.entry(label, anchor))` builds a clickable TOC with dot leaders and auto-resolved page numbers; `addPageReference(anchor)` prints a native "see page N" cross-reference; `DocumentSession.pageIndex()` resolves any anchor to its page.
+- **Bookmarks & viewer preferences** — `section.bookmark(...)` makes any section or container a PDF outline (bookmark-panel) target, and `chrome().viewerPreferences(...)` opens the reader on the outline panel, a chosen page layout, or the doc title in the window.
+- **Multi-section documents** — `GraphCompose.documents()` concatenates independently authored sections — each with its own page size, margins, and footer numbering — into one PDF, with anchors, links, and the outline resolving across section boundaries.
+- **Richer row & page layout** — `row.columns(auto(), weight(1), fixed(80))`, main-axis `flexSpacer()` / `arrangement(...)`, cross-axis `verticalAlign(...)`, per-page `pageMargins(...)`, and full-bleed `bleed(...)`.
+- **Inline chips, SVG icons & colour emoji** — text on a rounded highlight chip, a parsed `SvgIcon` on the text baseline, and `RichText.emoji(":star:", size)` colour emoji via the new independently-versioned `graph-compose-emoji` module.
+- **Render straight to images** — `DocumentSession` renders directly to `BufferedImage`s with no PDF round-trip; plus page-number offset / restart / style and round / dotted line caps.
-Core document APIs stay source- and binary-compatible with v1.7 (`ConfigLoader` is the one removal). Full notes in [`CHANGELOG.md`](./CHANGELOG.md).
+Core document APIs stay source- and binary-compatible with v1.8 — v1.9 is purely additive (two cover-letter / CV shim types are newly `@Deprecated` for 2.0). Full notes in [`CHANGELOG.md`](./CHANGELOG.md).
## Installation
@@ -76,12 +73,12 @@ Core document APIs stay source- and binary-compatible with v1.7 (`ConfigLoader`
io.github.demchaav
graph-compose
- 1.8.0
+ 1.9.0
```
```kotlin
-dependencies { implementation("io.github.demchaav:graph-compose:1.8.0") }
+dependencies { implementation("io.github.demchaav:graph-compose:1.9.0") }
```
> **Bundled fonts (from v1.8.0).** The curated Google fonts no longer ship
@@ -103,6 +100,23 @@ dependencies { implementation("io.github.demchaav:graph-compose:1.8.0") }
> above) to pull the engine + fonts together. Full details and upgrade steps:
> the [v1.8.0 fonts migration note](./docs/migration/v1.8.0-fonts.md).
+> **Colour emoji (from v1.9.0).** `RichText.emoji(":star:", size)` resolves
+> GitHub-style shortcodes to inline vector glyphs from an independently-versioned
+> companion artifact (the same split model as the fonts above). Text without
+> emoji needs nothing extra; to render colour emoji, add:
+>
+> ```xml
+>
+> io.github.demchaav
+> graph-compose-emoji
+> 1.0.0
+>
+> ```
+>
+> An unknown shortcode falls back to its literal text, so a document that uses no
+> emoji — or runs without the artifact — renders unchanged. The
+> `graph-compose-bundle` stays fonts-only; emoji is opt-in.
+
> **Distribution** — Maven Central is the canonical channel from **v1.6.6** onwards
> (`io.github.demchaav:graph-compose:`). Hosted Javadocs auto-publish to
> [javadoc.io/doc/io.github.demchaav/graph-compose](https://javadoc.io/doc/io.github.demchaav/graph-compose)
diff --git a/aggregator/pom.xml b/aggregator/pom.xml
index 2f7fbcc42..45bc81a67 100644
--- a/aggregator/pom.xml
+++ b/aggregator/pom.xml
@@ -6,7 +6,7 @@
io.github.demchaav
graph-compose-build
- 1.8.0
+ 1.9.0
pom
GraphCompose Build Aggregator
diff --git a/assets/readme/examples/emoji-shortcodes.pdf b/assets/readme/examples/emoji-shortcodes.pdf
new file mode 100644
index 000000000..32910dedb
Binary files /dev/null and b/assets/readme/examples/emoji-shortcodes.pdf differ
diff --git a/assets/readme/examples/in-pdf-navigation.pdf b/assets/readme/examples/in-pdf-navigation.pdf
new file mode 100644
index 000000000..5a9363dd2
Binary files /dev/null and b/assets/readme/examples/in-pdf-navigation.pdf differ
diff --git a/assets/readme/examples/inline-highlight-chips.pdf b/assets/readme/examples/inline-highlight-chips.pdf
new file mode 100644
index 000000000..51018b0be
Binary files /dev/null and b/assets/readme/examples/inline-highlight-chips.pdf differ
diff --git a/assets/readme/examples/inline-svg-icons.pdf b/assets/readme/examples/inline-svg-icons.pdf
new file mode 100644
index 000000000..546f65db4
Binary files /dev/null and b/assets/readme/examples/inline-svg-icons.pdf differ
diff --git a/assets/readme/repository_showcase_render.png b/assets/readme/repository_showcase_render.png
index 7617ab077..1a4a62fa3 100644
Binary files a/assets/readme/repository_showcase_render.png and b/assets/readme/repository_showcase_render.png differ
diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml
index 8248f7ad8..6f9cf6ef7 100644
--- a/benchmarks/pom.xml
+++ b/benchmarks/pom.xml
@@ -7,7 +7,7 @@
io.github.demchaav
graph-compose-build
- 1.8.0
+ 1.9.0
../aggregator/pom.xml
@@ -25,9 +25,9 @@
17
1.37
- 6.1.0
+ 6.1.1
3.27.7
- 1.5.34
+ 1.5.37
1.0.10
9.6.0
diff --git a/bundle/pom.xml b/bundle/pom.xml
index c33c291e0..6ddb9496e 100644
--- a/bundle/pom.xml
+++ b/bundle/pom.xml
@@ -25,7 +25,7 @@
line. The graph-compose dependency below uses ${project.version}, so it
follows automatically.
-->
- 1.8.0
+ 1.9.0
pom
GraphCompose Bundle
diff --git a/docs/README.md b/docs/README.md
index 44c722e6c..56b023b30 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -13,7 +13,8 @@ back here.
| You are… | Read |
|---|---|
-| **New to GraphCompose** — what is it, how do I render my first PDF | [Getting started](getting-started.md) → [Hello world in root README](../README.md#hello-world) |
+| **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** | [Built-in business templates](templates/business-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) |
| **Author using legacy v1.6 templates** (CV / cover-letter / invoice / proposal still using `*Spec` + builders) | [Templates v1-classic — landing](templates/v1-classic/README.md) |
@@ -26,10 +27,14 @@ back here.
## 📁 By category
### Getting started
+- **[first-document.md](first-document.md)** — the five-minute path from an empty project to a rendered PDF.
- **[getting-started.md](getting-started.md)** — DSL vs templates, first-render walk-through, decision tree.
+- **[capabilities.md](capabilities.md)** — one-glance map of every feature with its stability tier and guide link.
+- **[diagrams.md](diagrams.md)** — visual decision diagrams (authoring path, layout, output, lifecycle).
- **[troubleshooting.md](troubleshooting.md)** — symptom-first fixes for common gotchas: stray `?` glyphs, silent DOCX drops, optional-dependency `NoClassDefFoundError`, running the bundled examples.
### Templates
+- **[templates/business-templates.md](templates/business-templates.md)** — built-in invoice & proposal templates: the compose-first contract, end to end.
- **[templates/v2-layered/](templates/v2-layered/)** — 🆕 canonical going-forward pattern (CV is the reference implementation): `data` / `theme` / `components` / `widgets` / `presets`.
- **[templates/v1-classic/](templates/v1-classic/)** — the spec/builder/presets surface used by v1.6 CV, cover-letter, invoice, proposal templates. Still ships, still supported.
@@ -54,6 +59,8 @@ back here.
### Roadmaps & migrations
- **[roadmaps/v1.6-roadmap.md](roadmaps/v1.6-roadmap.md)** — current development roadmap.
+- **[roadmaps/migration-v1-8-to-v1-9.md](roadmaps/migration-v1-8-to-v1-9.md)** — upgrade guide for v1.8 → v1.9.
+- **[roadmaps/migration-v1-7-to-v1-8.md](roadmaps/migration-v1-7-to-v1-8.md)** — upgrade guide for v1.7 → v1.8.
- **[roadmaps/migration-v1-6-to-v1-7.md](roadmaps/migration-v1-6-to-v1-7.md)** — upgrade guide for v1.6 → v1.7.
- **[roadmaps/migration-v1-5-to-v1-6.md](roadmaps/migration-v1-5-to-v1-6.md)** — upgrade guide for v1.5 → v1.6.
- **[roadmaps/migration-v1-4-to-v1-5.md](roadmaps/migration-v1-4-to-v1-5.md)** — upgrade guide for v1.4 → v1.5.
diff --git a/docs/capabilities.md b/docs/capabilities.md
new file mode 100644
index 000000000..0aa109414
--- /dev/null
+++ b/docs/capabilities.md
@@ -0,0 +1,89 @@
+# Capabilities
+
+A one-glance map of what GraphCompose can do, the main API for each,
+and its stability tier. "Stability" rows use the tiers defined in the
+[API stability policy](api-stability.md); "Guide" links to the page that
+shows it in context.
+
+This is a feature catalogue, not the contract — the
+[API stability policy](api-stability.md) is authoritative for what each
+tier promises, and [canonical ⇄ legacy parity](architecture/canonical-legacy-parity.md)
+tracks what is `Partial` or `Planned`.
+
+---
+
+## Authoring
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| 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) |
+| Maintained document templates | `InvoiceTemplateV2`, `ProposalTemplateV2`, `cv.v2.*`, `coverletter.v2.*` | 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) |
+
+## Content
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| Paragraphs & rich inline text | `addParagraph(...)`, `addRich(...)`, `RichText` | Stable | [Recipes — rich text](recipes.md) |
+| 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) |
+| Vector shapes, dividers, lines | `addShape(...)`, `addLine(...)`, `addEllipse(...)` | Stable | [Shapes](recipes/shapes.md) |
+| Charts (bar / line / pie) | `chart(ChartSpec...)`, `ChartData` | Stable | [Charts](recipes/charts.md) |
+| Barcodes & QR | `addBarcode(...)` | Stable | [Recipes](recipes.md) |
+
+## Layout
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| Columns that still flow | `addRow(row -> row.weights(...))` | Stable | [Layered page design](recipes/layered-page-design.md) |
+| Page-wide fills / bands | `pageBackground(...)`, `pageBackgrounds(...)` | Stable | [Page backgrounds](recipes/page-backgrounds.md) |
+| 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) |
+| Bleed to page edge | `bleedToEdge(...)` | Stable | [Page backgrounds](recipes/page-backgrounds.md) |
+| Transforms (rotate / scale) | `DocumentTransform` | Stable | [Transforms](recipes/transforms.md) |
+
+## Output & testing
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| 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) |
+| 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) |
+| 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) |
+
+## Navigation
+
+| Capability | Main API | Stability | Guide |
+|---|---|---|---|
+| 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 1.9.0
+
+These ship from 1.9.0 onward — confirm your dependency version before relying on them:
+
+| Capability | Main API |
+|---|---|
+| Printed page references | `addPageReference("anchor")` |
+| Generated Table of Contents | `addTableOfContents(toc -> toc.entry(...))` |
+| Page preview images | `toImage(pageIndex, dpi)`, `toImages(dpi)` |
+
+---
+
+## See also
+
+- [API stability policy](api-stability.md) — what each tier promises.
+- [Decision diagrams](diagrams.md) — visual "which API do I use?".
+- [Recipes](recipes.md) — the full cookbook.
+- [Which template system should I use?](templates/which-template-system.md) — template-surface decision.
diff --git a/docs/diagrams.md b/docs/diagrams.md
new file mode 100644
index 000000000..927339dd2
--- /dev/null
+++ b/docs/diagrams.md
@@ -0,0 +1,89 @@
+# Decision Diagrams
+
+Visual versions of the "which API do I reach for?" decisions. Each
+diagram renders on GitHub (Mermaid). The prose walkthroughs live in
+[Getting started](getting-started.md) and
+[Your first document](first-document.md).
+
+---
+
+## Choose your authoring path
+
+Start from intent. Most documents are either a maintained template or a
+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}
+ B -- Yes --> T[Use a maintained template]
+ B -- No --> C{Mostly reads top to bottom?}
+ C -- Yes --> F["GraphCompose.document(...) + pageFlow(...)"]
+ C -- Needs specific placement --> L["A layout primitive
(row / canvas / shape container)"]
+ F --> D{Same shape repeats across your app?}
+ D -- Yes --> H[Extract a helper / widget over the DSL]
+ D -- No --> Done1[Author the page flow]
+ H --> Done1
+ L --> Done2[See the layout diagram below]
+ T --> Done3[Supply the data spec, then render]
+```
+
+---
+
+## Where does content go on the page?
+
+Flow is the default. Reach for a stronger primitive only when the
+content has a specific placement relationship.
+
+```mermaid
+flowchart TD
+ A[Place content on the page] --> B{Top-to-bottom reading order?}
+ B -- Yes --> Flow[pageFlow / sections / modules]
+ B -- No --> C{Side by side, still part of the flow?}
+ C -- Yes --> Row["addRow(row -> row.weights(...))"]
+ C -- No --> D{A fill behind every page?}
+ D -- Yes --> BG["pageBackground(s)(...)"]
+ D -- No --> E{Overlap or a framed block?}
+ E -- Yes --> Layer[Layer stack / shape container]
+ E -- No --> Canvas["addCanvas(w, h, ...) — exact x/y"]
+```
+
+---
+
+## Where does the output go?
+
+Choose the output method by destination. Create one `DocumentSession`
+per render request.
+
+```mermaid
+flowchart TD
+ A[The document is built] --> B{Where does it go?}
+ B -- A file --> F["buildPdf() / buildPdf(path)"]
+ B -- HTTP / cloud stream --> S["writePdf(OutputStream)"]
+ B -- A byte array --> Y["toPdfBytes()"]
+ B -- A preview image --> I["toImage(...) / toImages(...)"]
+ B -- Editable Word --> D["export(new DocxSemanticBackend())"]
+```
+
+---
+
+## Document lifecycle
+
+What happens between your code and the PDF.
+
+```mermaid
+flowchart LR
+ A["GraphCompose.document(...)"] --> B[DocumentSession]
+ B --> C["pageFlow(...) → semantic nodes"]
+ C --> D[Layout + pagination]
+ D --> E[PDF output]
+ D -. inspect .-> S["layoutSnapshot() (testing)"]
+```
+
+---
+
+## See also
+
+- [Getting started](getting-started.md) — the prose decision tree and first render.
+- [Your first document](first-document.md) — a five-minute walk-through.
+- [Which template system should I use?](templates/which-template-system.md) — the template-surface decision.
+- [Capabilities](capabilities.md) — what GraphCompose can do, with stability tiers.
diff --git a/docs/first-document.md b/docs/first-document.md
new file mode 100644
index 000000000..56666d72e
--- /dev/null
+++ b/docs/first-document.md
@@ -0,0 +1,121 @@
+# 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.
+
+> **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.
+
+```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)
+ .create()) {
+
+ document.pageFlow(page -> page
+ .module("Summary", module -> module.paragraph("Hello GraphCompose")));
+
+ document.buildPdf();
+}
+```
+
+`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.
+
+## A real custom document
+
+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.
+
+```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("profile.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .margin(24, 24, 24, 24)
+ .create()) {
+
+ document.pageFlow()
+ .name("CandidateProfile")
+ .spacing(12)
+ .module("Professional Summary", module -> module.paragraph(
+ "Backend engineer focused on clean Java APIs, stable document "
+ + "output, and reusable template architecture."))
+ .module("Technical Skills", module -> module.bullets(
+ "Java 21 and Spring Boot",
+ "PDF document generation with GraphCompose",
+ "Layout snapshot testing and render regression checks"))
+ .module("Projects", module -> module.rows(
+ "GraphCompose - declarative document layout engine.",
+ "CVRewriter - profile-aware CV tailoring platform."))
+ .build();
+
+ document.buildPdf();
+}
+```
+
+The callback form (`pageFlow(page -> ...)`) builds and attaches the root for you.
+The builder form (`pageFlow().…build()`) gives you the fluent chain but you must
+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:
+
+```java
+import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+InvoiceTemplateV2 template = new InvoiceTemplateV2(BusinessTheme.modern());
+
+try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf")).create()) {
+ template.compose(document, invoice); // invoice = your InvoiceDocumentSpec
+ document.buildPdf();
+}
+```
+
+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).
+
+## Rendering on a server
+
+When the caller already owns the output stream — an HTTP response, a cloud
+upload — create the session *without* a default path and stream the PDF with
+`writePdf(OutputStream)` instead of `buildPdf()`. GraphCompose writes the
+stream but does not close it. For the full server snippet, see
+[Getting started — Streaming output](getting-started.md#streaming-output).
+
+Create one `DocumentSession` per render request; it is mutable and not
+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.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 22de720cd..ded4d78ec 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -7,6 +7,8 @@ with `writePdf(...)`, `buildPdf()`, or `toPdfBytes()`.
> **Prerequisites:** Java 17+ and the `io.github.demchaav:graph-compose` dependency — see the [README install snippet](../README.md#installation).
+> **New to GraphCompose?** Start with [Your First Document](first-document.md) — a five-minute, copy-paste path from an empty project to a rendered PDF, then come back here for themes, layer stacks, and built-in templates.
+
## Templates vs DSL — pick the right starting point
GraphCompose has two layers a caller can target. Use this decision
diff --git a/docs/roadmaps/migration-v1-8-to-v1-9.md b/docs/roadmaps/migration-v1-8-to-v1-9.md
new file mode 100644
index 000000000..d12a8a3d1
--- /dev/null
+++ b/docs/roadmaps/migration-v1-8-to-v1-9.md
@@ -0,0 +1,84 @@
+# Migration: v1.8 → v1.9
+
+v1.9 — codenamed **"navigable"** — is **additive only**. Every public
+type, method, and behaviour from v1.8 is unchanged: bump the dependency
+to `1.9.0` and rebuild with **no source changes required**. Adding public
+API is what turns the open cycle into a minor release.
+
+The theme of the release is **in-document navigation** — a rendered PDF
+stops being a flat sequence of pages and becomes a document you can move
+through: named anchors, internal links, a native clickable table of
+contents, "see page N" cross-references, and a bookmark outline, all
+emitted as native PDF `GoTo` actions and resolved in a single authoring
+pass.
+
+If your application targets v1.8 today, there is nothing to do but
+upgrade. The rest of this guide is a tour of what you can now reach for.
+
+## TL;DR
+
+Everything below is `@since 1.9.0` and purely additive — no v1.8 API is
+replaced or removed.
+
+| Area | v1.9 addition | Reach for it when |
+| --- | --- | --- |
+| **In-PDF navigation** | `anchor(...)` targets + internal `link(...)`; sealed `DocumentLinkTarget` (`InternalLinkTarget` / `ExternalLinkTarget`) | clickable cross-references and `[text](#heading)`-style jumps emitted as native PDF `GoTo` actions |
+| **Table of contents** | `addTableOfContents(toc -> toc.entry(label, anchor))` + `TocBuilder` / `DocumentLeader` | a native clickable TOC with dot leaders and auto-resolved page numbers |
+| **Page references** | `addPageReference(anchor)` + `PageReferenceNode`; `DocumentSession.pageIndex()` → `PageIndex` / `PageReference` | a "see page N" cross-reference, or resolving any anchor to its page from code |
+| **Bookmarks & viewer prefs** | container `bookmark(DocumentBookmarkOptions)`; `chrome().viewerPreferences(...)` + `DocumentViewerPreferences` / `DocumentPageMode` / `DocumentPageLayout` | a PDF outline (bookmark panel), and controlling how a reader opens the document |
+| **Multi-section documents** | `GraphCompose.documents()` + `MultiSectionDocumentBuilder` / `MultiSectionDocument` | concatenating independently authored sections (own page size / margins / numbering) into one PDF, with links resolving across sections |
+| **Per-page margins & bleed** | `pageMargins(List)`; `bleed(...)` + `DocumentBleed` / `DocumentEdge` | mixing a full-bleed cover with book-margin body pages, or running content to the trimmed page edge |
+| **Row layout** | `row.columns(auto() / weight() / fixed())` + `DocumentRowColumn`; `flexSpacer()` / `pushRight()` / `arrangement(...)` + `RowArrangement`; `verticalAlign(...)` + `RowVerticalAlign` | column tracks, main-axis justify (push a badge flush right), and cross-axis seating within a row |
+| **Inline chips / SVG / emoji** | inline highlight chips, inline `SvgIcon` runs, `RichText.emoji(":star:", size)` via the new `graph-compose-emoji` module | a code/badge chip, an icon on the text baseline, or colour emoji by shortcode |
+| **Page numbering** | `DocumentPageNumbering` / `DocumentPageNumberStyle` | page-number offset / restart / numeral style in headers and footers |
+| **Lines** | `LineBuilder.fill()`; `LineBuilder.lineCap(DocumentLineCap)` | a line that stretches to its slot (dot leaders), and round / square caps or dotted strokes |
+| **Render to images** | render a `DocumentSession` straight to `BufferedImage` | a raster preview / thumbnail with no PDF round-trip |
+
+Runnable code for each lives in the
+[examples gallery](../../examples/README.md); the exact public-API list
+is in [`CHANGELOG.md`](../../CHANGELOG.md) under **v1.9.0**.
+
+## One behaviour to know about
+
+- **A negative page margin is now rejected; a negative bottom *content*
+ margin is honoured.** A negative `margin(...)` on the page fails fast at
+ construction instead of silently mis-laying-out the page, while an
+ intentional negative bottom margin on a block now pulls the following
+ content up as written. This only affects code that passed a negative
+ *page* margin — pass a non-negative one.
+
+## Things that did NOT break
+
+- Every entry point on `GraphCompose`, the full `DocumentSession`
+ authoring lifecycle (`compose`, `pageFlow`, add, `buildPdf`, export,
+ close, `layoutGraph`, `layoutSnapshot`).
+- `DocumentDsl`, `BusinessTheme`, `DocumentPalette`, and the invoice /
+ proposal / CV / cover-letter / weekly-schedule template entry points
+ (V1 and V2).
+- The fixed-layout and semantic backend SPIs and every public render
+ handler.
+- Layout snapshots and visual-regression baselines — navigation markers
+ are non-visual, so a document that adds no anchors, links, or bookmarks
+ renders byte-for-byte as before.
+
+> **Deprecations (informational, not breaking).**
+> `templates.api.CoverLetterTemplate` and the `cv.v2.components`
+> `HeadlineRenderer` / `ContactRenderer` / `BannerRenderer` shims are now
+> `@Deprecated(forRemoval = true, since = "1.9.0")` — superseded by the
+> generic `DocumentTemplate` seam and the `cv.v2.widgets` widgets
+> respectively. They still compile and behave exactly as before in 1.x;
+> they are slated for removal in 2.0. See
+> [`docs/api-stability.md`](../api-stability.md) §3 for the ledger.
+
+## Upgrading
+
+```xml
+
+ io.github.demchaav
+ graph-compose
+ 1.9.0
+
+```
+
+That is the entire migration. Pull in any of the primitives above as you
+need them.
diff --git a/docs/templates/business-templates.md b/docs/templates/business-templates.md
new file mode 100644
index 000000000..29c843ace
--- /dev/null
+++ b/docs/templates/business-templates.md
@@ -0,0 +1,179 @@
+# Built-In Business Templates — Invoice & Proposal
+
+GraphCompose ships maintained templates for two common business
+documents: **invoices** and **proposals**. You supply a typed data
+spec, pick a `BusinessTheme`, and the template renders a consistent,
+branded document. You never position anything by hand.
+
+> For **CVs and cover letters**, use the layered `cv.v2` / `coverletter.v2`
+> model instead — see the [Templates v2 (layered) quickstart](v2-layered/quickstart.md).
+> Not sure which surface to target? See
+> [Which template system should I use?](which-template-system.md).
+
+## The compose-first contract
+
+Every built-in template follows the same five steps. The template owns
+the document structure; your application owns the data and the output
+destination.
+
+1. Build a data spec — `InvoiceDocumentSpec` or `ProposalDocumentSpec`.
+2. Choose a `BusinessTheme` (commonly `BusinessTheme.modern()`).
+3. Create the template with the theme.
+4. Open a `DocumentSession`.
+5. `template.compose(document, spec)`, then render.
+
+The template composes into an **open** `DocumentSession` — it never
+decides file vs stream vs bytes. The caller does.
+
+## Invoice
+
+```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.templates.builtins.InvoiceTemplateV2;
+import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+import java.nio.file.Path;
+
+InvoiceDocumentSpec invoice = InvoiceDocumentSpec.builder()
+ .title("Invoice")
+ .invoiceNumber("GC-2026-041")
+ .issueDate("25 Jun 2026")
+ .dueDate("25 Jul 2026")
+ .reference("GraphCompose implementation")
+ .status("Due")
+ .fromParty(party -> party
+ .name("GraphCompose Studio")
+ .addressLines("10 Example Street", "London")
+ .email("billing@example.com")
+ .phone("+44 20 0000 0000")
+ .taxId("VAT GB000000000"))
+ .billToParty(party -> party
+ .name("Client Ltd")
+ .addressLines("22 Client Road", "Manchester")
+ .email("accounts@client.example"))
+ .lineItem("Document engine integration", "Implementation and support",
+ "1", "4,800.00", "4,800.00")
+ .summaryRow("Subtotal", "4,800.00")
+ .summaryRow("VAT", "960.00")
+ .totalRow("Total", "5,760.00")
+ .paymentTerm("Payment due within 30 days.")
+ .footerNote("Thank you for your business.")
+ .build();
+
+BusinessTheme theme = BusinessTheme.modern();
+InvoiceTemplateV2 template = new InvoiceTemplateV2(theme);
+
+try (DocumentSession document = GraphCompose.document(Path.of("invoice.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .pageBackground(theme.pageBackground())
+ .margin(28, 28, 28, 28)
+ .create()) {
+ template.compose(document, invoice);
+ document.buildPdf();
+}
+```
+
+The template renders a masthead with the invoice metadata, two-column
+seller / buyer blocks, a zebra-striped line-item table with summary and
+total rows, and a notes / payment-terms footer.
+
+## Proposal
+
+Same shape, different spec. Use a proposal when the artifact is sales or
+project scope rather than billing. The timeline takes a three-argument
+`timelineItem(phase, duration, details)`.
+
+```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.templates.builtins.ProposalTemplateV2;
+import com.demcha.compose.document.templates.data.proposal.ProposalDocumentSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+import java.nio.file.Path;
+
+ProposalDocumentSpec proposal = ProposalDocumentSpec.builder()
+ .title("Proposal")
+ .proposalNumber("PR-2026-014")
+ .preparedDate("25 Jun 2026")
+ .validUntil("25 Jul 2026")
+ .projectTitle("Document Automation Platform")
+ .executiveSummary("A proposal for building reliable PDF generation into the product workflow.")
+ .sender(party -> party
+ .name("GraphCompose Studio")
+ .addressLines("10 Example Street", "London")
+ .email("hello@example.com")
+ .website("graphcompose.example"))
+ .recipient(party -> party
+ .name("Client Ltd")
+ .addressLines("22 Client Road", "Manchester")
+ .email("product@client.example"))
+ .section("Scope", "Backend integration, template setup, and regression checks.")
+ .timelineItem("Week 1", "1 week", "Data model and rendering endpoint.")
+ .pricingRow("Implementation", "Fixed scope", "4,800.00")
+ .emphasizedPricingRow("Total", "Excluding tax", "4,800.00")
+ .acceptanceTerm("Proposal valid for 30 days.")
+ .footerNote("Prepared with GraphCompose.")
+ .build();
+
+BusinessTheme theme = BusinessTheme.modern();
+ProposalTemplateV2 template = new ProposalTemplateV2(theme);
+
+try (DocumentSession document = GraphCompose.document(Path.of("proposal.pdf"))
+ .pageSize(DocumentPageSize.A4)
+ .pageBackground(theme.pageBackground())
+ .margin(28, 28, 28, 28)
+ .create()) {
+ template.compose(document, proposal);
+ document.buildPdf();
+}
+```
+
+## Rendering on a server
+
+In production the spec usually comes from application data and the
+document is streamed to the caller's stream. The template composes the
+same way before any output method; create one session per request.
+
+```java
+import com.demcha.compose.GraphCompose;
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.templates.builtins.InvoiceTemplateV2;
+import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+
+import java.io.OutputStream;
+
+void streamInvoice(InvoiceDocumentSpec invoice, OutputStream out) throws Exception {
+ InvoiceTemplateV2 template = new InvoiceTemplateV2(BusinessTheme.modern());
+
+ try (DocumentSession document = GraphCompose.document().create()) {
+ template.compose(document, invoice);
+ document.writePdf(out);
+ }
+}
+```
+
+## Customizing
+
+If the built-in structure is close but not exact, prefer these moves in
+order:
+
+1. Check whether the spec already has the field you need.
+2. Change the `BusinessTheme` (or its tokens) for branding.
+3. Wrap the template call with session-level PDF chrome — a footer,
+ metadata, or protection — see [Getting started](../getting-started.md).
+4. Only fork or write a new template when the document *structure* itself
+ differs. A custom template implements the same `DocumentTemplate`
+ contract and composes into the same session.
+
+## See also
+
+- [Which template system should I use?](which-template-system.md) — the full decision tree.
+- [Templates v2 (layered) quickstart](v2-layered/quickstart.md) — CVs and cover letters.
+- [Recipes — themes](../recipes/themes.md) — customizing `BusinessTheme`.
+- [Streaming](../recipes/streaming.md) — server output patterns.
diff --git a/examples/README.md b/examples/README.md
index 0a51f6c65..e87e62dbf 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -69,6 +69,9 @@ are with the canonical DSL, then jump to its detailed section below.
|---|---|---|
| [Rich text](#rich-text) | Every `RichText` method (bold / italic / underline / link / colour / accent / size / append) | [PDF](../assets/readme/examples/rich-text-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/text/RichTextShowcaseExample.java) |
| [Inline shapes](#inline-shapes) | `InlineShapeRun` — dots, arrows, chevrons, diamonds, stars, checkmarks and checkboxes drawn as geometry on the text baseline | [PDF](../assets/readme/examples/inline-shapes.pdf) · [Source](src/main/java/com/demcha/examples/features/text/InlineShapesExample.java) |
+| [Inline highlight chips](#inline-highlight-chips) | `RichText.code(text)` / `chip(text, fg, bg)` / `highlight(text, style, bg, radius, padding)` — text on a rounded padded fill (inline code + status badges), wrapping across lines | [PDF](../assets/readme/examples/inline-highlight-chips.pdf) · [Source](src/main/java/com/demcha/examples/features/text/InlineHighlightExample.java) |
+| [Inline SVG icons](#inline-svg-icons) | `RichText.svgIcon(icon, size)` — a parsed multi-colour `SvgIcon` on the text baseline, crisp at any zoom and carrying its own colours | [PDF](../assets/readme/examples/inline-svg-icons.pdf) · [Source](src/main/java/com/demcha/examples/features/text/InlineSvgIconExample.java) |
+| [Colour emoji](#colour-emoji) | `RichText.emoji(":star:", size)` — GitHub-style shortcodes resolve to inline vector glyphs via the `graph-compose-emoji` artifact; unknown codes fall back to literal text | [PDF](../assets/readme/examples/emoji-shortcodes.pdf) · [Source](src/main/java/com/demcha/examples/features/text/EmojiShortcodeExample.java) |
| [Section presets](#section-presets) | `pageBackground`, `band`, `softPanel`, `accentLeft / Right / Top / Bottom`, per-corner `DocumentCornerRadius` | [PDF](../assets/readme/examples/section-presets.pdf) · [Source](src/main/java/com/demcha/examples/features/text/SectionPresetsExample.java) |
| [Nested lists](#nested-lists-v16) | `ListBuilder.addItem(label, Consumer)` — depth cascade, per-depth markers, mixed flat / nested authoring | [PDF](../assets/readme/examples/nested-list-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/lists/NestedListExample.java) |
| [Composed table cells](#composed-table-cells-v16) | `DocumentTableCell.node(DocumentNode)` — paragraphs, lists, sub-tables inside cells with two-pass measurement | [PDF](../assets/readme/examples/composed-table-cell-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/tables/ComposedTableCellExample.java) |
@@ -105,6 +108,7 @@ are with the canonical DSL, then jump to its detailed section below.
| [PDF chrome](#pdf-chrome) | `DocumentMetadata`, `DocumentWatermark`, `DocumentHeaderFooter`, `DocumentBookmarkOptions` | [PDF](../assets/readme/examples/pdf-chrome.pdf) · [Source](src/main/java/com/demcha/examples/features/chrome/PdfChromeExample.java) |
| [Page numbering](#page-numbering) | `DocumentPageNumbering` — offset / restart / roman / suppress-on-first-page for `{page}` / `{pages}` footer tokens | [PDF](../assets/readme/examples/page-numbering.pdf) · [Source](src/main/java/com/demcha/examples/features/chrome/PageNumberingExample.java) |
| [Viewer preferences](#viewer-preferences) | `chrome().viewerPreferences(...)` — open with the bookmark panel (`USE_OUTLINES`), set page layout, or show the doc title in the window | [PDF](../assets/readme/examples/viewer-preferences.pdf) · [Source](src/main/java/com/demcha/examples/features/chrome/ViewerPreferencesExample.java) |
+| [In-PDF navigation](#in-pdf-navigation) | `anchor(...)` destinations + internal `linkTo(...)` / `inlineLinkTo(...)` / `shapeLinkTo(...)` — clickable cross-references and footnotes as native PDF GoTo actions; forward references resolve in a deferred pass | [PDF](../assets/readme/examples/in-pdf-navigation.pdf) · [Source](src/main/java/com/demcha/examples/features/navigation/InPdfNavigationExample.java) |
| [Page references](#page-references) | `addPageReference(anchor)` — print the page an `anchor(...)` lands on (a native "see page N" cross-reference), resolved in one authoring pass | [PDF](../assets/readme/examples/page-reference.pdf) · [Source](src/main/java/com/demcha/examples/features/navigation/PageReferenceExample.java) |
| [Table of contents](#table-of-contents) | `addTableOfContents(toc -> toc.entry(label, anchor))` — a native clickable TOC with dot leaders and auto-resolved page numbers | [PDF](../assets/readme/examples/table-of-contents.pdf) · [Source](src/main/java/com/demcha/examples/features/navigation/TocExample.java) |
| [Container bookmarks](#container-bookmarks) | `section.bookmark(new DocumentBookmarkOptions(title))` — make a section / container a PDF outline (bookmark-panel) target | [PDF](../assets/readme/examples/container-bookmark.pdf) · [Source](src/main/java/com/demcha/examples/features/navigation/ContainerBookmarkExample.java) |
@@ -642,6 +646,62 @@ via `CheckmarkStyle` / `ArrowStyle`.
[📄 View PDF](../assets/readme/examples/inline-shapes.pdf) ·
[📜 Full source](src/main/java/com/demcha/examples/features/text/InlineShapesExample.java)
+### Inline highlight chips
+
+`RichText.code(text)` / `chip(text, fg, bg)` / `highlight(text, style, bg,
+radius, padding)` (`@since 1.9.0`) draw text on a rounded, padded background
+fill on the baseline — the GitHub inline `code` look and inline status badges.
+`code` ships engine defaults (monospace + a light chip), `chip` colours a
+badge, and `highlight` is the full primitive. A multi-word highlight wraps
+across lines, painting one continuous rounded fill per visual fragment. On
+`ParagraphBuilder` the calls are `inlineCode` / `inlineChip` / `inlineHighlight`.
+
+```java
+.addRich(rich -> rich
+ .plain("Run ").code("./mvnw verify").plain(" — status ")
+ .chip(" Paid ", paidFg, paidBg)
+ .highlight("rounded", style, fill, 8.0, DocumentInsets.symmetric(2, 8)))
+```
+
+[📄 View PDF](../assets/readme/examples/inline-highlight-chips.pdf) ·
+[📜 Full source](src/main/java/com/demcha/examples/features/text/InlineHighlightExample.java)
+
+### Inline SVG icons
+
+`RichText.svgIcon(icon, size)` / `ParagraphBuilder.inlineSvgIcon(...)`
+(`@since 1.9.0`) place a parsed `SvgIcon` on the text baseline, so multi-colour
+vector glyphs flow inside a line of text — crisp at any zoom, carrying their own
+colours, with no dependence on the active font's glyph coverage. `size` is the
+glyph height in points; width follows the icon's aspect ratio. This is the
+engine path behind vector colour emoji.
+
+```java
+.addRich(rich -> rich
+ .svgIcon(check, 10).plain(" Deploy succeeded ")
+ .svgIcon(warn, 10).plain(" Disk almost full"))
+```
+
+[📄 View PDF](../assets/readme/examples/inline-svg-icons.pdf) ·
+[📜 Full source](src/main/java/com/demcha/examples/features/text/InlineSvgIconExample.java)
+
+### Colour emoji
+
+`RichText.emoji(":star:", size)` / `ParagraphBuilder.inlineEmoji(...)`
+(`@since 1.9.0`) resolve a GitHub-style shortcode to an inline vector colour
+glyph on the baseline — crisp at any zoom, no emoji font required. Glyphs come
+from the `graph-compose-emoji` companion artifact on the classpath. Resolution
+is lenient: an unknown shortcode falls back to its literal text, exactly the way
+GitHub renders an unrecognised `:code:`.
+
+```java
+.addRich(rich -> rich
+ .plain("Ship it ").emoji(":rocket:", 11).plain(" ")
+ .emoji(":white_check_mark:", 11).plain(" ").emoji(":tada:", 11))
+```
+
+[📄 View PDF](../assets/readme/examples/emoji-shortcodes.pdf) ·
+[📜 Full source](src/main/java/com/demcha/examples/features/text/EmojiShortcodeExample.java)
+
### Section presets
`pageBackground`, `band`, `softPanel`, the four
@@ -780,6 +840,25 @@ document.chrome().viewerPreferences(DocumentViewerPreferences.builder()
[📄 View PDF](../assets/readme/examples/viewer-preferences.pdf) ·
[📜 Full source](src/main/java/com/demcha/examples/features/chrome/ViewerPreferencesExample.java)
+### In-PDF navigation
+
+`anchor(name)` marks a destination on a section, paragraph, or inline run;
+`linkTo(label, style, anchor)` / `inlineLinkTo(text, anchor)` /
+`shapeLinkTo(shape, color, anchor)` (`@since 1.9.0`) jump to it as native PDF
+`GoTo` actions — a clickable table of contents, `[text](#heading)`-style links,
+and bidirectional footnotes. Anchors resolve in a deferred pass, so a link may
+target an anchor that appears later in the document (a forward reference).
+External `link(label, new DocumentLinkOptions(url))` is unchanged.
+
+```java
+.addRich(RichText.text("See the ").linkTo("overview", linkStyle, "overview"))
+// …further down…
+.addSection("Overview", s -> s.anchor("overview") /* … */)
+```
+
+[📄 View PDF](../assets/readme/examples/in-pdf-navigation.pdf) ·
+[📜 Full source](src/main/java/com/demcha/examples/features/navigation/InPdfNavigationExample.java)
+
### Page references
`addPageReference(anchor)` prints the page a declared `anchor(...)` lands on — a
diff --git a/examples/pom.xml b/examples/pom.xml
index 9efb1d2d4..cbff15b74 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -7,7 +7,7 @@
io.github.demchaav
graph-compose-build
- 1.8.0
+ 1.9.0
../aggregator/pom.xml
@@ -18,10 +18,10 @@
${project.version}
- 1.5.34
+ 1.5.37
17
- 6.1.0
+ 6.1.1
3.27.7
@@ -98,6 +98,28 @@
+
+
+
+ src/main/resources
+ true
+
+ banner.properties
+
+
+
+ src/main/resources
+ false
+
+ banner.properties
+
+
+
org.apache.maven.plugins
diff --git a/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java b/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java
index 48346b2a5..c73a92547 100644
--- a/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java
+++ b/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java
@@ -48,11 +48,14 @@
import com.demcha.compose.font.FontName;
import com.demcha.examples.support.ExampleOutputPaths;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
+import java.util.Properties;
/**
* Flagship "what is GraphCompose" capability deck — a multi-page landscape
@@ -100,8 +103,35 @@
*/
public final class EngineDeckExample {
- private static final String VERSION = "1.8.0";
- private static final String CODENAME = "illustrative";
+ /**
+ * Release version + codename shown on the banner. Sourced from the filtered
+ * {@code banner.properties} ({@code version} = Maven {@code @project.version@},
+ * {@code codename} = the per-minor release name), so the hero is always
+ * current with the release the build was cut from rather than a hand-bumped
+ * literal. Falls back to {@code "dev"} when run without Maven resource
+ * filtering (e.g. straight from an IDE), so the banner never prints the raw
+ * {@code @…@} token.
+ */
+ private static final String VERSION;
+ private static final String CODENAME;
+
+ static {
+ Properties banner = new Properties();
+ try (InputStream in = EngineDeckExample.class.getResourceAsStream("/banner.properties")) {
+ if (in != null) {
+ banner.load(in);
+ }
+ } catch (IOException ignored) {
+ // Missing/unreadable metadata falls through to the defaults below.
+ }
+ VERSION = resolved(banner.getProperty("version"), "dev");
+ CODENAME = resolved(banner.getProperty("codename"), "");
+ }
+
+ /** Returns {@code value} unless it is blank or an unfiltered {@code @…@} token. */
+ private static String resolved(String value, String fallback) {
+ return value == null || value.isBlank() || value.startsWith("@") ? fallback : value.trim();
+ }
private EngineDeckExample() {
}
@@ -125,22 +155,24 @@ public static Path generate() throws Exception {
}
/**
- * Renders page 1's banner as a standalone, full-bleed hero. The dark
- * violet field is painted as the canonical {@code pageBackground}, so it
- * fills the whole landscape page — margins and corners included — and the
- * rasterised image carries no white frame, only the banner itself. This is
- * the source of the repository README hero
- * ({@code assets/readme/repository_showcase_render.png}, produced by
- * {@link com.demcha.examples.support.PdfPageRasterizer}); re-render it after a
- * version bump — the banner reads {@link #VERSION} / {@link #CODENAME}, so
- * the hero stays current with one rebuild.
+ * Renders the standalone hero banner to a raster image straight from the
+ * engine via {@link DocumentSession#toImage(int, int)} ({@code @since 1.9.0})
+ * — no intermediate PDF and no external rasterizer. This is the source of the
+ * repository README hero ({@code assets/readme/repository_showcase_render.png},
+ * written by {@link com.demcha.examples.support.ReadmeBannerRenderer}). The
+ * dark violet field is the canonical {@code pageBackground} on a page cropped
+ * to wrap the content, so the image is all banner and no white frame. The
+ * version pill reads {@link #VERSION} / {@link #CODENAME} from the filtered
+ * {@code banner.properties}, so the hero stays current with the release the
+ * build was cut from; {@code cut-release.ps1} re-renders it on every tag.
*
- * @return the generated single-page banner PDF path
+ * @param dpi raster resolution in dots per inch; 200 matches the committed asset
+ * @return the rendered banner image
* @throws Exception when rendering or icon IO fails
+ * @since 1.9.0
*/
- public static Path generateBanner() throws Exception {
- Path outputFile = ExampleOutputPaths.prepare("flagships", "engine-banner.pdf");
- try (DocumentSession document = GraphCompose.document(outputFile)
+ public static BufferedImage renderBannerImage(int dpi) throws Exception {
+ try (DocumentSession document = GraphCompose.document()
// The banner content is a top-anchored stack; the default
// A4-landscape page left a thick dead dark border around it.
// Crop the page to wrap the content tightly — width fits the
@@ -150,21 +182,46 @@ public static Path generateBanner() throws Exception {
.pageBackground(HERO_BG)
.margin(8, 8, 8, 8)
.create()) {
- document.metadata(DocumentMetadata.builder()
- .title("GraphCompose v" + VERSION + " — " + CODENAME)
- .author("GraphCompose")
- .subject("GraphCompose banner — the engine's own brand hero")
- .producer("GraphCompose (PDFBox 3.0)")
- .build());
- document.pageFlow()
- .name("EngineBanner")
- .addSection("Banner", EngineDeckExample::banner)
- .build();
+ composeBannerInto(document);
+ return document.toImage(0, dpi);
+ }
+ }
+
+ /**
+ * Renders the same standalone banner to a PDF — kept for a vector/print copy
+ * of the hero and as the layout the snapshot test guards. The committed
+ * README image comes from {@link #renderBannerImage(int)}, not this file.
+ *
+ * @return the generated single-page banner PDF path
+ * @throws Exception when rendering or icon IO fails
+ */
+ public static Path generateBanner() throws Exception {
+ Path outputFile = ExampleOutputPaths.prepare("flagships", "engine-banner.pdf");
+ try (DocumentSession document = GraphCompose.document(outputFile)
+ .pageSize(801, 525)
+ .pageBackground(HERO_BG)
+ .margin(8, 8, 8, 8)
+ .create()) {
+ composeBannerInto(document);
document.buildPdf();
}
return outputFile;
}
+ /** Banner metadata + the single banner section — shared by the image and PDF renders. */
+ private static void composeBannerInto(DocumentSession document) {
+ document.metadata(DocumentMetadata.builder()
+ .title("GraphCompose v" + VERSION + " — " + CODENAME)
+ .author("GraphCompose")
+ .subject("GraphCompose banner — the engine's own brand hero")
+ .producer("GraphCompose (PDFBox 3.0)")
+ .build());
+ document.pageFlow()
+ .name("EngineBanner")
+ .addSection("Banner", EngineDeckExample::banner)
+ .build();
+ }
+
/**
* Composes the four deck pages onto a session — shared by {@link #generate()}
* and the layout snapshot test, so the test guards the very layout we ship.
diff --git a/examples/src/main/java/com/demcha/examples/support/ReadmeBannerRenderer.java b/examples/src/main/java/com/demcha/examples/support/ReadmeBannerRenderer.java
new file mode 100644
index 000000000..f95d11c53
--- /dev/null
+++ b/examples/src/main/java/com/demcha/examples/support/ReadmeBannerRenderer.java
@@ -0,0 +1,75 @@
+package com.demcha.examples.support;
+
+import com.demcha.examples.flagships.EngineDeckExample;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Renders the README hero banner straight to its committed PNG via the engine's
+ * {@code DocumentSession.toImage(...)} path ({@code @since 1.9.0}) — the
+ * replacement for the old render-to-PDF-then-{@link PdfPageRasterizer}
+ * round-trip. The banner's version pill is sourced from the filtered
+ * {@code banner.properties}, so running this after a version bump stamps the
+ * hero with the current release. {@code cut-release.ps1} invokes it during the
+ * release commit so {@code assets/readme/repository_showcase_render.png} ships
+ * in lockstep with every tag, instead of drifting until someone re-renders by
+ * hand.
+ *
+ * Usage — pass an explicit output path (exec:java runs with the examples
+ * module as its working directory, so a relative default would land in the
+ * wrong place); DPI defaults to 200, matching the committed asset:
+ *
+ * ./mvnw -B -ntp -f examples/pom.xml -DskipTests exec:java \
+ * -Dexec.mainClass=com.demcha.examples.support.ReadmeBannerRenderer \
+ * -Dexec.args="<outputPng> [dpi=200]"
+ *
+ *
+ * @author Artem Demchyshyn
+ * @since 1.9.0
+ */
+public final class ReadmeBannerRenderer {
+
+ /** Resolution of the committed hero asset; high enough for a crisp README image. */
+ private static final int DEFAULT_DPI = 200;
+
+ private ReadmeBannerRenderer() {
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length < 1 || args[0].isBlank()) {
+ System.err.println("Usage: ReadmeBannerRenderer [dpi=200]");
+ System.exit(2);
+ }
+ Path outputPng = Paths.get(args[0]).toAbsolutePath().normalize();
+ int dpi = args.length >= 2 ? Integer.parseInt(args[1]) : DEFAULT_DPI;
+
+ Path written = render(outputPng, dpi);
+ System.out.println("Rendered README banner -> " + written + " (" + dpi + " DPI)");
+ }
+
+ /**
+ * Renders the hero banner and writes it as a PNG, creating parent
+ * directories as needed.
+ *
+ * @param outputPng destination file
+ * @param dpi raster resolution in dots per inch
+ * @return the written path
+ * @throws Exception if rendering fails
+ * @throws IllegalStateException if the PNG encoder rejects the image
+ */
+ public static Path render(Path outputPng, int dpi) throws Exception {
+ BufferedImage image = EngineDeckExample.renderBannerImage(dpi);
+ Path parent = outputPng.toAbsolutePath().getParent();
+ if (parent != null) {
+ Files.createDirectories(parent);
+ }
+ if (!ImageIO.write(image, "png", outputPng.toFile())) {
+ throw new IllegalStateException("No PNG ImageIO writer accepted the banner: " + outputPng);
+ }
+ return outputPng;
+ }
+}
diff --git a/examples/src/main/resources/banner.properties b/examples/src/main/resources/banner.properties
new file mode 100644
index 000000000..d3161d75e
--- /dev/null
+++ b/examples/src/main/resources/banner.properties
@@ -0,0 +1,10 @@
+# README hero banner metadata.
+#
+# `version` is filled by Maven resource filtering from the reactor
+# ${project.version} at build time (see examples/pom.xml ), so the
+# rendered hero is never a hand-bumped literal and can never drift from the
+# release it was cut from. `codename` is the per-minor release name, set during
+# release prep. cut-release.ps1 re-renders the banner on every tag, stamping the
+# committed assets/readme/repository_showcase_render.png with both values.
+version=@project.version@
+codename=navigable
diff --git a/examples/src/test/java/com/demcha/examples/support/ReadmeBannerRendererTest.java b/examples/src/test/java/com/demcha/examples/support/ReadmeBannerRendererTest.java
new file mode 100644
index 000000000..3fd64800e
--- /dev/null
+++ b/examples/src/test/java/com/demcha/examples/support/ReadmeBannerRendererTest.java
@@ -0,0 +1,59 @@
+package com.demcha.examples.support;
+
+import com.demcha.examples.flagships.EngineDeckExample;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Verifies the README hero renders straight to an image through the engine's
+ * {@code toImage} path — no intermediate PDF, no external rasterizer — and that
+ * the writer lands a non-trivial PNG.
+ */
+class ReadmeBannerRendererTest {
+
+ @Test
+ void rendersANonEmptyBannerImageWithoutAPdfRoundTrip() throws Exception {
+ BufferedImage image = EngineDeckExample.renderBannerImage(96);
+
+ // The banner page is 801x525pt; at 96 DPI that is ~1068x700px. Assert
+ // generous lower bounds so the test guards "rendered something real"
+ // without pinning exact pixels (DPI/metric drift would make that brittle).
+ assertThat(image.getWidth()).isGreaterThan(600);
+ assertThat(image.getHeight()).isGreaterThan(300);
+ }
+
+ @Test
+ void writesThePngToTheGivenPath(@TempDir Path tmp) throws Exception {
+ Path png = tmp.resolve("nested").resolve("banner.png");
+
+ Path written = ReadmeBannerRenderer.render(png, 72);
+
+ assertThat(written).isEqualTo(png);
+ assertThat(png).exists();
+ assertThat(Files.size(png)).isGreaterThan(2_000L);
+ }
+
+ @Test
+ void bannerPropertiesVersionIsFilledByMavenResourceFiltering() throws Exception {
+ // The on-classpath copy is the build's filtered output: if examples/pom.xml
+ // ever loses the filtering, version stays the raw @project.version@ token
+ // and the hero silently stops carrying the real release version.
+ Properties props = new Properties();
+ try (InputStream in = getClass().getResourceAsStream("/banner.properties")) {
+ assertThat(in).describedAs("banner.properties must be on the classpath").isNotNull();
+ props.load(in);
+ }
+ assertThat(props.getProperty("version"))
+ .describedAs("banner version must be filtered to the project version, not the raw @project.version@ token")
+ .doesNotContain("@")
+ .matches("\\d.*");
+ }
+}
diff --git a/pom.xml b/pom.xml
index b24ed21d2..75661f348 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.github.demchaav
graph-compose
- 1.8.0
+ 1.9.0
GraphCompose
A declarative layout engine for programmatic document generation, implemented primarily in Java.
@@ -48,7 +48,7 @@
0.64.8
2.21.4
- 1.5.34
+ 1.5.37
1.18.46
3.0.7
5.5.1
@@ -58,7 +58,7 @@
3.27.7
- 6.1.0
+ 6.1.1
5.23.0
1.18.10
diff --git a/scripts/cut-release.ps1 b/scripts/cut-release.ps1
index c08f22e8c..0e4d7f502 100644
--- a/scripts/cut-release.ps1
+++ b/scripts/cut-release.ps1
@@ -340,6 +340,39 @@ function Run-ShowcaseSync {
}
}
+function Render-ReadmeBanner {
+ # Re-renders assets/readme/repository_showcase_render.png straight from the
+ # engine (DocumentSession.toImage, @since 1.9.0) so the hero's version pill
+ # carries the just-bumped ${project.version} (read from the filtered
+ # banner.properties). The `compile` is REQUIRED: banner.properties is filtered
+ # at examples-compile time, so the examples module must be recompiled AFTER the
+ # Step-1 version bump — otherwise the banner would carry the previous release
+ # version. Runs after Run-ShowcaseSync, which already installed the bumped root
+ # artifact into the local m2 cache so the examples module resolves it.
+ Write-Host " > Re-render the version-stamped README hero banner" -ForegroundColor Cyan
+ $banner = Join-Path $repoRoot 'assets/readme/repository_showcase_render.png'
+ $execProp = '"-Dexec.mainClass=com.demcha.examples.support.ReadmeBannerRenderer"'
+ $execArgs = "`"-Dexec.args=$banner`""
+ if ($DryRun) {
+ Write-Host " [DRY RUN] $mvnw -f examples/pom.xml -DskipTests compile exec:java $execProp $execArgs" -ForegroundColor Yellow
+ return
+ }
+ Push-Location $repoRoot
+ try {
+ & $mvnw -B -ntp -f examples/pom.xml -DskipTests compile exec:java $execProp $execArgs 2>&1 | ForEach-Object {
+ if ($_ -match 'Rendered README banner|BUILD SUCCESS|BUILD FAILURE|ERROR') {
+ Write-Host " $_" -ForegroundColor DarkGray
+ }
+ }
+ if ($LASTEXITCODE -ne 0) {
+ throw "README banner render failed (exit $LASTEXITCODE)"
+ }
+ } finally {
+ Pop-Location
+ }
+ Note "banner: assets/readme/repository_showcase_render.png re-rendered"
+}
+
# ============================================================
# Mode: -PostReleaseOnly
# ============================================================
@@ -483,8 +516,9 @@ try {
Step 4 "Regenerate web/examples.json with $tag links"
Run-ShowcaseSync
+ Render-ReadmeBanner
} else {
- Step 3 "Skipped showcase GH_BASE flip + regen (-SkipShowcase)"
+ Step 3 "Skipped showcase GH_BASE flip + regen + banner (-SkipShowcase)"
}
if (-not $SkipVerify) {
@@ -523,7 +557,8 @@ try {
$commitFiles += @(
'examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java',
'web/examples.json',
- 'web/showcase'
+ 'web/showcase',
+ 'assets/readme/repository_showcase_render.png'
)
}
if ($DryRun) {
diff --git a/src/main/java/com/demcha/compose/document/api/DocumentSession.java b/src/main/java/com/demcha/compose/document/api/DocumentSession.java
index 5f0586f73..8ae52b00f 100644
--- a/src/main/java/com/demcha/compose/document/api/DocumentSession.java
+++ b/src/main/java/com/demcha/compose/document/api/DocumentSession.java
@@ -475,6 +475,7 @@ public DocumentSession pageBackgrounds(List fills) {
* @param rules ordered list of per-page margin overrides, or {@code null}/empty to clear
* @return this session
* @throws IllegalStateException if this session has already been closed
+ * @since 1.9.0
*/
public DocumentSession pageMargins(List rules) {
ensureOpen();
diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/components/BannerRenderer.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/components/BannerRenderer.java
index 4a2e6204a..0d1c4beca 100644
--- a/src/main/java/com/demcha/compose/document/templates/cv/v2/components/BannerRenderer.java
+++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/components/BannerRenderer.java
@@ -5,7 +5,7 @@
import com.demcha.compose.document.templates.cv.v2.widgets.SectionHeader;
/**
- * @deprecated Use
+ * @deprecated since 1.9.0; removed in 2.0. Use
* {@link com.demcha.compose.document.templates.cv.v2.widgets.SectionHeader#banner}
* instead — the widget groups the banner alongside its sibling
* variants ({@code underlined}, {@code flat}) so picking a section-
@@ -13,7 +13,7 @@
* delegating shim so v2 code written before the widgets layer keeps
* compiling unchanged.
*/
-@Deprecated(forRemoval = true)
+@Deprecated(since = "1.9.0", forRemoval = true)
public final class BannerRenderer {
private BannerRenderer() {
diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/components/ContactRenderer.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/components/ContactRenderer.java
index 88ec45955..4e74b7a3f 100644
--- a/src/main/java/com/demcha/compose/document/templates/cv/v2/components/ContactRenderer.java
+++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/components/ContactRenderer.java
@@ -6,14 +6,14 @@
import com.demcha.compose.document.templates.core.identity.ContactLine;
/**
- * @deprecated Use
+ * @deprecated since 1.9.0; removed in 2.0. Use
* {@link com.demcha.compose.document.templates.core.identity.ContactLine#centered}
* instead — the widget gives you named centred/right-aligned
* variants plus a configurable field order. Kept as a thin
* delegating shim so v2 code written before the widgets layer keeps
* compiling unchanged.
*/
-@Deprecated(forRemoval = true)
+@Deprecated(since = "1.9.0", forRemoval = true)
public final class ContactRenderer {
private ContactRenderer() {
diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/components/HeadlineRenderer.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/components/HeadlineRenderer.java
index c724c8b56..a25dc85a6 100644
--- a/src/main/java/com/demcha/compose/document/templates/cv/v2/components/HeadlineRenderer.java
+++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/components/HeadlineRenderer.java
@@ -6,14 +6,14 @@
import com.demcha.compose.document.templates.core.identity.Headline;
/**
- * @deprecated Use
+ * @deprecated since 1.9.0; removed in 2.0. Use
* {@link com.demcha.compose.document.templates.core.identity.Headline#spacedCentered}
* instead — the widget gives you a named API plus alignment +
* spaced-caps variants, while this class only ever did the
* centred-spaced-caps form. Kept as a thin delegating shim so v2
* code written before the widgets layer keeps compiling unchanged.
*/
-@Deprecated(forRemoval = true)
+@Deprecated(since = "1.9.0", forRemoval = true)
public final class HeadlineRenderer {
private HeadlineRenderer() {
diff --git a/src/test/java/com/demcha/documentation/PublicApiNoEngineLeakTest.java b/src/test/java/com/demcha/documentation/PublicApiNoEngineLeakTest.java
index 946c17468..0f20abbe2 100644
--- a/src/test/java/com/demcha/documentation/PublicApiNoEngineLeakTest.java
+++ b/src/test/java/com/demcha/documentation/PublicApiNoEngineLeakTest.java
@@ -41,6 +41,11 @@ class PublicApiNoEngineLeakTest {
PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/style"),
PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/table"),
PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/image"),
+ PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/output"),
+ PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/snapshot"),
+ PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/theme"),
+ PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/exceptions"),
+ PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/document/emoji"),
PROJECT_ROOT.resolve("src/main/java/com/demcha/compose/font"));
/**
diff --git a/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java b/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java
index 0d2648020..5a4fd50d1 100644
--- a/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java
+++ b/src/test/java/com/demcha/documentation/VersionConsistencyGuardTest.java
@@ -106,6 +106,28 @@ void benchmarksDependencyDerivesVersionAndIsNotHardcoded() throws IOException {
.isFalse();
}
+ /**
+ * The README hero banner reads its version pill from the filtered examples
+ * {@code banner.properties}; its {@code version} line must be the Maven
+ * {@code @project.version@} token so the rendered hero always carries the
+ * release version instead of a hand-bumped literal (the drift that left the
+ * banner reading v1.8.0 while the repo was on v1.9.0). {@code cut-release.ps1}
+ * re-renders the banner on release, and this fails the verify gate the moment
+ * the source is hardcoded again.
+ */
+ @Test
+ void readmeBannerVersionDerivesFromProjectVersion() throws Exception {
+ String props = Files.readString(
+ PROJECT_ROOT.resolve("examples/src/main/resources/banner.properties"));
+
+ assertThat(Pattern.compile("(?m)^\\s*version\\s*=\\s*@project\\.version@\\s*$").matcher(props).find())
+ .describedAs("examples/src/main/resources/banner.properties must source the hero version from @project.version@, not a literal")
+ .isTrue();
+ assertThat(Pattern.compile("(?m)^\\s*version\\s*=\\s*v?\\d").matcher(props).find())
+ .describedAs("banner.properties must not hardcode a numeric version — that reintroduces the stale-hero drift")
+ .isFalse();
+ }
+
@Test
void readmeInstallSnippetsMatchTheProjectVersion() throws Exception {
Set targets = acceptableTargets();
diff --git a/web/examples.json b/web/examples.json
index 4b4b9aad8..9d581aeea 100644
--- a/web/examples.json
+++ b/web/examples.json
@@ -15,7 +15,7 @@
"tags": ["cv", "banner", "blue"],
"pdf": "showcase/pdf/templates/cv/cv-blue-banner-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-blue-banner-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvBlueBannerExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvBlueBannerExample.java"
},
{
"id": "cv-boxed-sections-v2",
@@ -24,7 +24,7 @@
"tags": ["cv", "structured"],
"pdf": "showcase/pdf/templates/cv/cv-boxed-sections-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-boxed-sections-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvBoxedV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvBoxedV2Example.java"
},
{
"id": "cv-centered-headline-v2",
@@ -33,7 +33,7 @@
"tags": ["cv", "centered"],
"pdf": "showcase/pdf/templates/cv/cv-centered-headline-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-centered-headline-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvCenteredHeadlineExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvCenteredHeadlineExample.java"
},
{
"id": "cv-classic-serif-v2",
@@ -42,7 +42,7 @@
"tags": ["cv", "serif", "two-page"],
"pdf": "showcase/pdf/templates/cv/cv-classic-serif-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-classic-serif-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvClassicSerifExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvClassicSerifExample.java"
},
{
"id": "cv-compact-mono-v2",
@@ -51,7 +51,7 @@
"tags": ["cv", "compact", "mono"],
"pdf": "showcase/pdf/templates/cv/cv-compact-mono-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-compact-mono-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvCompactMonoExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvCompactMonoExample.java"
},
{
"id": "cv-editorial-blue-v2",
@@ -60,7 +60,7 @@
"tags": ["cv", "editorial", "blue"],
"pdf": "showcase/pdf/templates/cv/cv-editorial-blue-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-editorial-blue-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvEditorialBlueExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvEditorialBlueExample.java"
},
{
"id": "cv-engineering-resume-v2",
@@ -69,7 +69,7 @@
"tags": ["cv", "tech"],
"pdf": "showcase/pdf/templates/cv/cv-engineering-resume-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-engineering-resume-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvEngineeringResumeExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvEngineeringResumeExample.java"
},
{
"id": "cv-executive-v2",
@@ -78,7 +78,7 @@
"tags": ["cv", "executive"],
"pdf": "showcase/pdf/templates/cv/cv-executive-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-executive-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvExecutiveExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvExecutiveExample.java"
},
{
"id": "cv-minimal-underlined-v2",
@@ -87,7 +87,7 @@
"tags": ["cv", "minimal"],
"pdf": "showcase/pdf/templates/cv/cv-minimal-underlined-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-minimal-underlined-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMinimalUnderlinedExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMinimalUnderlinedExample.java"
},
{
"id": "cv-mint-editorial-v2",
@@ -96,7 +96,7 @@
"tags": ["cv", "editorial", "mint"],
"pdf": "showcase/pdf/templates/cv/cv-mint-editorial-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-mint-editorial-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMintEditorialExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMintEditorialExample.java"
},
{
"id": "cv-modern-professional-v2",
@@ -105,7 +105,7 @@
"tags": ["cv", "minimal"],
"pdf": "showcase/pdf/templates/cv/cv-modern-professional-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-modern-professional-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvModernV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvModernV2Example.java"
},
{
"id": "cv-monogram-sidebar-v2",
@@ -114,7 +114,7 @@
"tags": ["cv", "sidebar", "monogram"],
"pdf": "showcase/pdf/templates/cv/cv-monogram-sidebar-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-monogram-sidebar-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMonogramSidebarExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMonogramSidebarExample.java"
},
{
"id": "cv-nordic-clean-v2",
@@ -123,7 +123,7 @@
"tags": ["cv", "sidebar"],
"pdf": "showcase/pdf/templates/cv/cv-nordic-clean-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-nordic-clean-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvNordicCleanExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvNordicCleanExample.java"
},
{
"id": "cv-panel-v2",
@@ -132,7 +132,7 @@
"tags": ["cv", "panel"],
"pdf": "showcase/pdf/templates/cv/cv-panel-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-panel-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvPanelExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvPanelExample.java"
},
{
"id": "cv-sidebar-portrait-v2",
@@ -141,7 +141,7 @@
"tags": ["cv", "sidebar", "portrait"],
"pdf": "showcase/pdf/templates/cv/cv-sidebar-portrait-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-sidebar-portrait-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvSidebarPortraitExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvSidebarPortraitExample.java"
},
{
"id": "cv-timeline-minimal-v2",
@@ -150,7 +150,7 @@
"tags": ["cv", "timeline"],
"pdf": "showcase/pdf/templates/cv/cv-timeline-minimal-v2.pdf",
"screenshot": "showcase/screenshots/templates/cv/cv-timeline-minimal-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvTimelineMinimalExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvTimelineMinimalExample.java"
}
]
},
@@ -165,7 +165,7 @@
"tags": ["proposal", "proposal", "cinematic"],
"pdf": "showcase/pdf/templates/proposal/project-proposal-cinematic.pdf",
"screenshot": "showcase/screenshots/templates/proposal/project-proposal-cinematic.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/proposal/CinematicProposalFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/proposal/CinematicProposalFileExample.java"
},
{
"id": "proposal",
@@ -174,7 +174,7 @@
"tags": ["proposal", "proposal"],
"pdf": "showcase/pdf/templates/proposal/proposal.pdf",
"screenshot": "showcase/screenshots/templates/proposal/proposal.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/proposal/ProposalFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/proposal/ProposalFileExample.java"
},
{
"id": "proposal-cinematic",
@@ -183,7 +183,7 @@
"tags": ["proposal", "proposal", "cinematic"],
"pdf": "showcase/pdf/templates/proposal/proposal-cinematic.pdf",
"screenshot": "showcase/screenshots/templates/proposal/proposal-cinematic.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/proposal/ProposalCinematicFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/proposal/ProposalCinematicFileExample.java"
}
]
},
@@ -198,7 +198,7 @@
"tags": ["invoice", "invoice"],
"pdf": "showcase/pdf/templates/invoice/invoice.pdf",
"screenshot": "showcase/screenshots/templates/invoice/invoice.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java"
},
{
"id": "invoice-cinematic",
@@ -207,7 +207,7 @@
"tags": ["invoice", "invoice", "cinematic"],
"pdf": "showcase/pdf/templates/invoice/invoice-cinematic.pdf",
"screenshot": "showcase/screenshots/templates/invoice/invoice-cinematic.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceCinematicFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceCinematicFileExample.java"
}
]
},
@@ -222,7 +222,7 @@
"tags": ["schedule", "schedule", "table"],
"pdf": "showcase/pdf/templates/schedule/weekly-schedule.pdf",
"screenshot": "showcase/screenshots/templates/schedule/weekly-schedule.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/schedule/WeeklyScheduleFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/schedule/WeeklyScheduleFileExample.java"
}
]
},
@@ -237,7 +237,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-blue-banner-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-blue-banner-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvBlueBannerLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvBlueBannerLetterV2Example.java"
},
{
"id": "cover-letter-boxed-sections-v2",
@@ -246,7 +246,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-boxed-sections-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-boxed-sections-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvBoxedSectionsLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvBoxedSectionsLetterV2Example.java"
},
{
"id": "cover-letter-centered-headline-v2",
@@ -255,7 +255,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-centered-headline-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-centered-headline-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvCenteredHeadlineLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvCenteredHeadlineLetterV2Example.java"
},
{
"id": "cover-letter-classic-serif-v2",
@@ -264,7 +264,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-classic-serif-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-classic-serif-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvClassicSerifLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvClassicSerifLetterV2Example.java"
},
{
"id": "cover-letter-compact-mono-v2",
@@ -273,7 +273,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-compact-mono-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-compact-mono-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvCompactMonoLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvCompactMonoLetterV2Example.java"
},
{
"id": "cover-letter-editorial-blue-v2",
@@ -282,7 +282,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-editorial-blue-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-editorial-blue-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvEditorialBlueLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvEditorialBlueLetterV2Example.java"
},
{
"id": "cover-letter-engineering-resume-v2",
@@ -291,7 +291,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-engineering-resume-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-engineering-resume-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvEngineeringResumeLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvEngineeringResumeLetterV2Example.java"
},
{
"id": "cover-letter-executive-v2",
@@ -300,7 +300,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-executive-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-executive-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvExecutiveLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvExecutiveLetterV2Example.java"
},
{
"id": "cover-letter-mint-editorial-v2",
@@ -309,7 +309,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-mint-editorial-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-mint-editorial-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvMintEditorialLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvMintEditorialLetterV2Example.java"
},
{
"id": "cover-letter-modern-professional-v2",
@@ -318,7 +318,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-modern-professional-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-modern-professional-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvModernProfessionalLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvModernProfessionalLetterV2Example.java"
},
{
"id": "cover-letter-monogram-sidebar-v2",
@@ -327,7 +327,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-monogram-sidebar-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-monogram-sidebar-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvMonogramSidebarLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvMonogramSidebarLetterV2Example.java"
},
{
"id": "cover-letter-nordic-clean-v2",
@@ -336,7 +336,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-nordic-clean-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-nordic-clean-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvNordicCleanLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvNordicCleanLetterV2Example.java"
},
{
"id": "cover-letter-panel-v2",
@@ -345,7 +345,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-panel-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-panel-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvPanelLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvPanelLetterV2Example.java"
},
{
"id": "cover-letter-sidebar-portrait-v2",
@@ -354,7 +354,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-sidebar-portrait-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-sidebar-portrait-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvSidebarPortraitLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvSidebarPortraitLetterV2Example.java"
},
{
"id": "cover-letter-timeline-minimal-v2",
@@ -363,7 +363,7 @@
"tags": ["letter"],
"pdf": "showcase/pdf/templates/coverletter/cover-letter-timeline-minimal-v2.pdf",
"screenshot": "showcase/screenshots/templates/coverletter/cover-letter-timeline-minimal-v2.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvTimelineMinimalLetterV2Example.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/templates/coverletter/v2/CvTimelineMinimalLetterV2Example.java"
}
]
}
@@ -384,7 +384,7 @@
"tags": ["canvas", "canvas", "v1.6", "absolute"],
"pdf": "showcase/pdf/features/canvas/canvas-layer-showcase.pdf",
"screenshot": "showcase/screenshots/features/canvas/canvas-layer-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/canvas/CanvasLayerExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/canvas/CanvasLayerExample.java"
}
]
},
@@ -399,7 +399,7 @@
"tags": ["tables", "tables", "v1.6"],
"pdf": "showcase/pdf/features/tables/composed-table-cell-showcase.pdf",
"screenshot": "showcase/screenshots/features/tables/composed-table-cell-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/tables/ComposedTableCellExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/tables/ComposedTableCellExample.java"
},
{
"id": "table-advanced",
@@ -408,7 +408,7 @@
"tags": ["tables", "tables", "pagination"],
"pdf": "showcase/pdf/features/tables/table-advanced.pdf",
"screenshot": "showcase/screenshots/features/tables/table-advanced.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/tables/TableAdvancedExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/tables/TableAdvancedExample.java"
}
]
},
@@ -423,7 +423,7 @@
"tags": ["lists", "lists", "v1.6"],
"pdf": "showcase/pdf/features/lists/nested-list-showcase.pdf",
"screenshot": "showcase/screenshots/features/lists/nested-list-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/lists/NestedListExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/lists/NestedListExample.java"
}
]
},
@@ -431,6 +431,24 @@
"id": "shapes",
"label": "Shapes & Containers",
"examples": [
+ {
+ "id": "line-cap",
+ "title": "Line Cap",
+ "description": "Generated showcase for features / shapes.",
+ "tags": ["features", "shapes"],
+ "pdf": "showcase/pdf/features/shapes/line-cap.pdf",
+ "screenshot": "showcase/screenshots/features/shapes/line-cap.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "line-fill",
+ "title": "Line Fill",
+ "description": "Generated showcase for features / shapes.",
+ "tags": ["features", "shapes"],
+ "pdf": "showcase/pdf/features/shapes/line-fill.pdf",
+ "screenshot": "showcase/screenshots/features/shapes/line-fill.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
{
"id": "photo-clip",
"title": "Photo Clip (silhouette)",
@@ -438,7 +456,7 @@
"tags": ["shapes", "shapes", "clip", "v1.8"],
"pdf": "showcase/pdf/features/shapes/photo-clip.pdf",
"screenshot": "showcase/screenshots/features/shapes/photo-clip.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/shapes/PhotoClipExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/shapes/PhotoClipExample.java"
},
{
"id": "shape-container",
@@ -447,7 +465,7 @@
"tags": ["shapes", "shapes", "clip"],
"pdf": "showcase/pdf/features/shapes/shape-container.pdf",
"screenshot": "showcase/screenshots/features/shapes/shape-container.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/shapes/ShapeContainerExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/shapes/ShapeContainerExample.java"
},
{
"id": "vector-path",
@@ -456,7 +474,7 @@
"tags": ["shapes", "shapes", "bezier", "v1.8"],
"pdf": "showcase/pdf/features/shapes/vector-path.pdf",
"screenshot": "showcase/screenshots/features/shapes/vector-path.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/shapes/ShapeContainerExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/shapes/ShapeContainerExample.java"
}
]
},
@@ -471,7 +489,7 @@
"tags": ["transforms", "transforms", "layers"],
"pdf": "showcase/pdf/features/transforms/transforms.pdf",
"screenshot": "showcase/screenshots/features/transforms/transforms.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/transforms/TransformsExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/transforms/TransformsExample.java"
}
]
},
@@ -479,6 +497,51 @@
"id": "text",
"label": "Rich Text",
"examples": [
+ {
+ "id": "emoji-clip-path",
+ "title": "Emoji Clip Path",
+ "description": "Generated showcase for features / text.",
+ "tags": ["features", "text"],
+ "pdf": "showcase/pdf/features/text/emoji-clip-path.pdf",
+ "screenshot": "showcase/screenshots/features/text/emoji-clip-path.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "emoji-gallery",
+ "title": "Emoji Gallery",
+ "description": "Generated showcase for features / text.",
+ "tags": ["features", "text"],
+ "pdf": "showcase/pdf/features/text/emoji-gallery.pdf",
+ "screenshot": "showcase/screenshots/features/text/emoji-gallery.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "emoji-shortcodes",
+ "title": "Emoji Shortcodes",
+ "description": "Generated showcase for features / text.",
+ "tags": ["features", "text"],
+ "pdf": "showcase/pdf/features/text/emoji-shortcodes.pdf",
+ "screenshot": "showcase/screenshots/features/text/emoji-shortcodes.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "emoji-svg-vs-png",
+ "title": "Emoji Svg Vs Png",
+ "description": "Generated showcase for features / text.",
+ "tags": ["features", "text"],
+ "pdf": "showcase/pdf/features/text/emoji-svg-vs-png.pdf",
+ "screenshot": "showcase/screenshots/features/text/emoji-svg-vs-png.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "inline-highlight-chips",
+ "title": "Inline Highlight Chips",
+ "description": "Generated showcase for features / text.",
+ "tags": ["features", "text"],
+ "pdf": "showcase/pdf/features/text/inline-highlight-chips.pdf",
+ "screenshot": "showcase/screenshots/features/text/inline-highlight-chips.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
{
"id": "inline-shapes",
"title": "Inline Shapes",
@@ -486,7 +549,16 @@
"tags": ["features", "text"],
"pdf": "showcase/pdf/features/text/inline-shapes.pdf",
"screenshot": "showcase/screenshots/features/text/inline-shapes.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "inline-svg-icons",
+ "title": "Inline Svg Icons",
+ "description": "Generated showcase for features / text.",
+ "tags": ["features", "text"],
+ "pdf": "showcase/pdf/features/text/inline-svg-icons.pdf",
+ "screenshot": "showcase/screenshots/features/text/inline-svg-icons.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
},
{
"id": "rich-text-showcase",
@@ -495,7 +567,7 @@
"tags": ["text", "text", "rich"],
"pdf": "showcase/pdf/features/text/rich-text-showcase.pdf",
"screenshot": "showcase/screenshots/features/text/rich-text-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/text/RichTextShowcaseExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/text/RichTextShowcaseExample.java"
},
{
"id": "section-presets",
@@ -504,7 +576,7 @@
"tags": ["text", "text", "sections"],
"pdf": "showcase/pdf/features/text/section-presets.pdf",
"screenshot": "showcase/screenshots/features/text/section-presets.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/text/SectionPresetsExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/text/SectionPresetsExample.java"
}
]
},
@@ -519,7 +591,7 @@
"tags": ["themes", "themes"],
"pdf": "showcase/pdf/features/themes/invoice-custom-theme.pdf",
"screenshot": "showcase/screenshots/features/themes/invoice-custom-theme.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/themes/CustomBusinessThemeExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/themes/CustomBusinessThemeExample.java"
}
]
},
@@ -534,7 +606,7 @@
"tags": ["barcodes", "barcodes", "qr"],
"pdf": "showcase/pdf/features/barcodes/barcode-showcase.pdf",
"screenshot": "showcase/screenshots/features/barcodes/barcode-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/barcodes/BarcodeShowcaseExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/barcodes/BarcodeShowcaseExample.java"
}
]
},
@@ -542,6 +614,15 @@
"id": "chrome",
"label": "PDF Chrome (header / footer / watermark)",
"examples": [
+ {
+ "id": "page-numbering",
+ "title": "Page Numbering",
+ "description": "Generated showcase for features / chrome.",
+ "tags": ["features", "chrome"],
+ "pdf": "showcase/pdf/features/chrome/page-numbering.pdf",
+ "screenshot": "showcase/screenshots/features/chrome/page-numbering.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
{
"id": "pdf-chrome",
"title": "PDF Chrome",
@@ -549,7 +630,16 @@
"tags": ["chrome", "chrome", "metadata", "watermark"],
"pdf": "showcase/pdf/features/chrome/pdf-chrome.pdf",
"screenshot": "showcase/screenshots/features/chrome/pdf-chrome.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/chrome/PdfChromeExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/chrome/PdfChromeExample.java"
+ },
+ {
+ "id": "viewer-preferences",
+ "title": "Viewer Preferences",
+ "description": "Generated showcase for features / chrome.",
+ "tags": ["features", "chrome"],
+ "pdf": "showcase/pdf/features/chrome/viewer-preferences.pdf",
+ "screenshot": "showcase/screenshots/features/chrome/viewer-preferences.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
}
]
},
@@ -564,7 +654,7 @@
"tags": ["streaming", "streaming", "http"],
"pdf": "showcase/pdf/features/streaming/invoice-http-stream.pdf",
"screenshot": "showcase/screenshots/features/streaming/invoice-http-stream.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java"
}
]
},
@@ -579,7 +669,7 @@
"tags": ["snapshots", "snapshots", "testing"],
"pdf": "showcase/pdf/features/snapshots/invoice-snapshot-regression.pdf",
"screenshot": "showcase/screenshots/features/snapshots/invoice-snapshot-regression.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java"
}
]
},
@@ -594,7 +684,7 @@
"tags": ["features", "charts"],
"pdf": "showcase/pdf/features/charts/chart-showcase.pdf",
"screenshot": "showcase/screenshots/features/charts/chart-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
}
]
},
@@ -609,7 +699,7 @@
"tags": ["debug", "debug", "labels", "v1.8"],
"pdf": "showcase/pdf/features/debug/debug-overlay.pdf",
"screenshot": "showcase/screenshots/features/debug/debug-overlay.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/debug/Debug Overlay"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/debug/Debug Overlay"
}
]
},
@@ -624,7 +714,7 @@
"tags": ["docx", "docx", "word", "export"],
"pdf": "showcase/pdf/features/docx/word-export-companion.pdf",
"screenshot": "showcase/screenshots/features/docx/word-export-companion.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/docx/WordExportExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/docx/WordExportExample.java"
}
]
},
@@ -639,7 +729,109 @@
"tags": ["layout", "layout", "align", "v1.8"],
"pdf": "showcase/pdf/features/layout/block-align.pdf",
"screenshot": "showcase/screenshots/features/layout/block-align.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/layout/Block Align"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/layout/Block Align"
+ },
+ {
+ "id": "content-bleed",
+ "title": "Content Bleed",
+ "description": "Generated showcase for features / layout.",
+ "tags": ["features", "layout"],
+ "pdf": "showcase/pdf/features/layout/content-bleed.pdf",
+ "screenshot": "showcase/screenshots/features/layout/content-bleed.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "per-page-margin",
+ "title": "Per Page Margin",
+ "description": "Generated showcase for features / layout.",
+ "tags": ["features", "layout"],
+ "pdf": "showcase/pdf/features/layout/per-page-margin.pdf",
+ "screenshot": "showcase/screenshots/features/layout/per-page-margin.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "row-columns",
+ "title": "Row Columns",
+ "description": "Generated showcase for features / layout.",
+ "tags": ["features", "layout"],
+ "pdf": "showcase/pdf/features/layout/row-columns.pdf",
+ "screenshot": "showcase/screenshots/features/layout/row-columns.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "row-flex",
+ "title": "Row Flex",
+ "description": "Generated showcase for features / layout.",
+ "tags": ["features", "layout"],
+ "pdf": "showcase/pdf/features/layout/row-flex.pdf",
+ "screenshot": "showcase/screenshots/features/layout/row-flex.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "row-vertical-align",
+ "title": "Row Vertical Align",
+ "description": "Generated showcase for features / layout.",
+ "tags": ["features", "layout"],
+ "pdf": "showcase/pdf/features/layout/row-vertical-align.pdf",
+ "screenshot": "showcase/screenshots/features/layout/row-vertical-align.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ }
+ ]
+ },
+ {
+ "id": "navigation",
+ "label": "Navigation",
+ "examples": [
+ {
+ "id": "container-bookmark",
+ "title": "Container Bookmark",
+ "description": "Generated showcase for features / navigation.",
+ "tags": ["features", "navigation"],
+ "pdf": "showcase/pdf/features/navigation/container-bookmark.pdf",
+ "screenshot": "showcase/screenshots/features/navigation/container-bookmark.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "in-pdf-navigation",
+ "title": "In Pdf Navigation",
+ "description": "Generated showcase for features / navigation.",
+ "tags": ["features", "navigation"],
+ "pdf": "showcase/pdf/features/navigation/in-pdf-navigation.pdf",
+ "screenshot": "showcase/screenshots/features/navigation/in-pdf-navigation.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "page-reference",
+ "title": "Page Reference",
+ "description": "Generated showcase for features / navigation.",
+ "tags": ["features", "navigation"],
+ "pdf": "showcase/pdf/features/navigation/page-reference.pdf",
+ "screenshot": "showcase/screenshots/features/navigation/page-reference.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ },
+ {
+ "id": "table-of-contents",
+ "title": "Table Of Contents",
+ "description": "Generated showcase for features / navigation.",
+ "tags": ["features", "navigation"],
+ "pdf": "showcase/pdf/features/navigation/table-of-contents.pdf",
+ "screenshot": "showcase/screenshots/features/navigation/table-of-contents.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
+ }
+ ]
+ },
+ {
+ "id": "structure",
+ "label": "Structure",
+ "examples": [
+ {
+ "id": "multi-section-document",
+ "title": "Multi Section Document",
+ "description": "Generated showcase for features / structure.",
+ "tags": ["features", "structure"],
+ "pdf": "showcase/pdf/features/structure/multi-section-document.pdf",
+ "screenshot": "showcase/screenshots/features/structure/multi-section-document.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
}
]
},
@@ -654,7 +846,22 @@
"tags": ["svg", "svg", "icons", "v1.8"],
"pdf": "showcase/pdf/features/svg/svg-icon-gallery.pdf",
"screenshot": "showcase/screenshots/features/svg/svg-icon-gallery.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/features/svg/Svg Icon Gallery"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/features/svg/Svg Icon Gallery"
+ }
+ ]
+ },
+ {
+ "id": "title",
+ "label": "Title",
+ "examples": [
+ {
+ "id": "book-template",
+ "title": "Book Template",
+ "description": "Generated showcase for features / title.",
+ "tags": ["features", "title"],
+ "pdf": "showcase/pdf/features/title/book-template.pdf",
+ "screenshot": "showcase/screenshots/features/title/book-template.png",
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
}
]
}
@@ -675,7 +882,7 @@
"tags": ["flagship", "showcase", "cover"],
"pdf": "showcase/pdf/flagships/default/business-report.pdf",
"screenshot": "showcase/screenshots/flagships/default/business-report.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/flagships/BusinessReportExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/flagships/BusinessReportExample.java"
},
{
"id": "engine-deck",
@@ -684,7 +891,7 @@
"tags": ["flagships", "default"],
"pdf": "showcase/pdf/flagships/default/engine-deck.pdf",
"screenshot": "showcase/screenshots/flagships/default/engine-deck.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
},
{
"id": "feature-catalog",
@@ -693,7 +900,7 @@
"tags": ["flagships", "default"],
"pdf": "showcase/pdf/flagships/default/feature-catalog.pdf",
"screenshot": "showcase/screenshots/flagships/default/feature-catalog.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
},
{
"id": "financial-report",
@@ -702,7 +909,7 @@
"tags": ["flagships", "default"],
"pdf": "showcase/pdf/flagships/default/financial-report.pdf",
"screenshot": "showcase/screenshots/flagships/default/financial-report.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples"
},
{
"id": "master-showcase",
@@ -711,7 +918,7 @@
"tags": ["flagship", "showcase"],
"pdf": "showcase/pdf/flagships/default/master-showcase.pdf",
"screenshot": "showcase/screenshots/flagships/default/master-showcase.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/flagships/MasterShowcaseExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/flagships/MasterShowcaseExample.java"
},
{
"id": "module-first-profile",
@@ -720,7 +927,7 @@
"tags": ["flagship", "authoring"],
"pdf": "showcase/pdf/flagships/default/module-first-profile.pdf",
"screenshot": "showcase/screenshots/flagships/default/module-first-profile.png",
- "code": "https://github.com/DemchaAV/GraphCompose/blob/develop/examples/src/main/java/com/demcha/examples/flagships/ModuleFirstFileExample.java"
+ "code": "https://github.com/DemchaAV/GraphCompose/blob/v1.9.0/examples/src/main/java/com/demcha/examples/flagships/ModuleFirstFileExample.java"
}
]
}
diff --git a/web/index.html b/web/index.html
index 3844365c4..acfb32427 100644
--- a/web/index.html
+++ b/web/index.html
@@ -57,9 +57,9 @@
"applicationSubCategory": "Library",
"operatingSystem": "Cross-platform (JVM 21+)",
"programmingLanguage": "Java",
- "softwareVersion": "1.8.0",
+ "softwareVersion": "1.9.0",
"url": "https://demchaav.github.io/GraphCompose/",
- "downloadUrl": "https://central.sonatype.com/artifact/io.github.demchaav/graph-compose/1.8.0",
+ "downloadUrl": "https://central.sonatype.com/artifact/io.github.demchaav/graph-compose/1.9.0",
"image": "https://demchaav.github.io/GraphCompose/assets/logo/graphcompose-logo.png",
"license": "https://github.com/DemchaAV/GraphCompose/blob/main/LICENSE",
"author": {
@@ -168,7 +168,7 @@
-
Java · v1.8.0 · MIT
+
Java · v1.9.0 · MIT
Java PDF layout engine for structured business documents.
Describe documents. Render polished PDFs. A semantic layout engine for Java services that need structured, paginated, theme-driven output — CVs, invoices, proposals, reports.
No drawing API. No pixel arithmetic. You compose ParagraphNode, TableNode, SectionNode; GraphCompose handles measurement, pagination, fonts, and PDFBox rendering.
@@ -233,14 +233,14 @@
Maven
<dependency>
<groupId>io.github.demchaav</groupId>
<artifactId>graph-compose</artifactId>
- <version>1.8.0</version>
+ <version>1.9.0</version>
</dependency>
Gradle
dependencies {
implementation(
- 'io.github.demchaav:graph-compose:1.8.0'
+ 'io.github.demchaav:graph-compose:1.9.0'
)
}
diff --git a/web/showcase/pdf/features/barcodes/barcode-showcase.pdf b/web/showcase/pdf/features/barcodes/barcode-showcase.pdf
index 02e5d26f4..cdd035316 100644
Binary files a/web/showcase/pdf/features/barcodes/barcode-showcase.pdf and b/web/showcase/pdf/features/barcodes/barcode-showcase.pdf differ
diff --git a/web/showcase/pdf/features/canvas/canvas-layer-showcase.pdf b/web/showcase/pdf/features/canvas/canvas-layer-showcase.pdf
index ad7a855c1..3e4896a29 100644
Binary files a/web/showcase/pdf/features/canvas/canvas-layer-showcase.pdf and b/web/showcase/pdf/features/canvas/canvas-layer-showcase.pdf differ
diff --git a/web/showcase/pdf/features/chrome/pdf-chrome.pdf b/web/showcase/pdf/features/chrome/pdf-chrome.pdf
index 8da3ab702..6ec0f64d5 100644
Binary files a/web/showcase/pdf/features/chrome/pdf-chrome.pdf and b/web/showcase/pdf/features/chrome/pdf-chrome.pdf differ
diff --git a/web/showcase/pdf/features/lists/nested-list-showcase.pdf b/web/showcase/pdf/features/lists/nested-list-showcase.pdf
index c759ea3d1..019357cb1 100644
Binary files a/web/showcase/pdf/features/lists/nested-list-showcase.pdf and b/web/showcase/pdf/features/lists/nested-list-showcase.pdf differ
diff --git a/web/showcase/pdf/features/shapes/shape-container.pdf b/web/showcase/pdf/features/shapes/shape-container.pdf
index d815aad34..c3b00965d 100644
Binary files a/web/showcase/pdf/features/shapes/shape-container.pdf and b/web/showcase/pdf/features/shapes/shape-container.pdf differ
diff --git a/web/showcase/pdf/features/snapshots/invoice-snapshot-regression.pdf b/web/showcase/pdf/features/snapshots/invoice-snapshot-regression.pdf
index 749a55bf7..9beb876ea 100644
Binary files a/web/showcase/pdf/features/snapshots/invoice-snapshot-regression.pdf and b/web/showcase/pdf/features/snapshots/invoice-snapshot-regression.pdf differ
diff --git a/web/showcase/pdf/features/streaming/invoice-http-stream.pdf b/web/showcase/pdf/features/streaming/invoice-http-stream.pdf
index 737fa9f3e..3fc309b69 100644
Binary files a/web/showcase/pdf/features/streaming/invoice-http-stream.pdf and b/web/showcase/pdf/features/streaming/invoice-http-stream.pdf differ
diff --git a/web/showcase/pdf/features/tables/composed-table-cell-showcase.pdf b/web/showcase/pdf/features/tables/composed-table-cell-showcase.pdf
index cebfd68e0..97d31e47c 100644
Binary files a/web/showcase/pdf/features/tables/composed-table-cell-showcase.pdf and b/web/showcase/pdf/features/tables/composed-table-cell-showcase.pdf differ
diff --git a/web/showcase/pdf/features/tables/table-advanced.pdf b/web/showcase/pdf/features/tables/table-advanced.pdf
index c6051047d..8c233fa6c 100644
Binary files a/web/showcase/pdf/features/tables/table-advanced.pdf and b/web/showcase/pdf/features/tables/table-advanced.pdf differ
diff --git a/web/showcase/pdf/features/text/rich-text-showcase.pdf b/web/showcase/pdf/features/text/rich-text-showcase.pdf
index 6a4e91552..151c761af 100644
Binary files a/web/showcase/pdf/features/text/rich-text-showcase.pdf and b/web/showcase/pdf/features/text/rich-text-showcase.pdf differ
diff --git a/web/showcase/pdf/features/text/section-presets.pdf b/web/showcase/pdf/features/text/section-presets.pdf
index 8eda09571..bf1930848 100644
Binary files a/web/showcase/pdf/features/text/section-presets.pdf and b/web/showcase/pdf/features/text/section-presets.pdf differ
diff --git a/web/showcase/pdf/features/themes/invoice-custom-theme.pdf b/web/showcase/pdf/features/themes/invoice-custom-theme.pdf
index b18faf470..b8526be12 100644
Binary files a/web/showcase/pdf/features/themes/invoice-custom-theme.pdf and b/web/showcase/pdf/features/themes/invoice-custom-theme.pdf differ
diff --git a/web/showcase/pdf/features/transforms/transforms.pdf b/web/showcase/pdf/features/transforms/transforms.pdf
index 7d7f13e9d..26d459c18 100644
Binary files a/web/showcase/pdf/features/transforms/transforms.pdf and b/web/showcase/pdf/features/transforms/transforms.pdf differ
diff --git a/web/showcase/pdf/flagships/default/business-report.pdf b/web/showcase/pdf/flagships/default/business-report.pdf
index 29377b16a..08ea8c7ab 100644
Binary files a/web/showcase/pdf/flagships/default/business-report.pdf and b/web/showcase/pdf/flagships/default/business-report.pdf differ
diff --git a/web/showcase/pdf/flagships/default/master-showcase.pdf b/web/showcase/pdf/flagships/default/master-showcase.pdf
index 040d8d197..6cf9d1617 100644
Binary files a/web/showcase/pdf/flagships/default/master-showcase.pdf and b/web/showcase/pdf/flagships/default/master-showcase.pdf differ
diff --git a/web/showcase/pdf/flagships/default/module-first-profile.pdf b/web/showcase/pdf/flagships/default/module-first-profile.pdf
index 292f6f50b..6f91fd167 100644
Binary files a/web/showcase/pdf/flagships/default/module-first-profile.pdf and b/web/showcase/pdf/flagships/default/module-first-profile.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-blue-banner-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-blue-banner-v2.pdf
index 8dbefa552..2354064a9 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-blue-banner-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-blue-banner-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-boxed-sections-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-boxed-sections-v2.pdf
index 2dd4c4fc0..a5d6847e4 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-boxed-sections-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-boxed-sections-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-centered-headline-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-centered-headline-v2.pdf
index 0b17d8a70..b3cf2d728 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-centered-headline-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-centered-headline-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-classic-serif-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-classic-serif-v2.pdf
index 7815ec101..6fec12e89 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-classic-serif-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-classic-serif-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-compact-mono-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-compact-mono-v2.pdf
index 0092ee1ef..681e0465c 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-compact-mono-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-compact-mono-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-editorial-blue-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-editorial-blue-v2.pdf
index 27112c2fe..550a6e068 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-editorial-blue-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-editorial-blue-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-engineering-resume-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-engineering-resume-v2.pdf
index ee1c5c8c4..f8082f661 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-engineering-resume-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-engineering-resume-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-executive-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-executive-v2.pdf
index 49c89d354..14dd114be 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-executive-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-executive-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-mint-editorial-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-mint-editorial-v2.pdf
index 3441a2b30..86057767e 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-mint-editorial-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-mint-editorial-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-modern-professional-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-modern-professional-v2.pdf
index 8a4453962..8c15b7ad4 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-modern-professional-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-modern-professional-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-monogram-sidebar-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-monogram-sidebar-v2.pdf
index 7ebeac915..6ac32d7b8 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-monogram-sidebar-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-monogram-sidebar-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-nordic-clean-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-nordic-clean-v2.pdf
index fe240c5ed..fc21513a1 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-nordic-clean-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-nordic-clean-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-panel-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-panel-v2.pdf
index d19272d72..7b650fbdd 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-panel-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-panel-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-sidebar-portrait-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-sidebar-portrait-v2.pdf
index e5584fbd9..c1d89a7f5 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-sidebar-portrait-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-sidebar-portrait-v2.pdf differ
diff --git a/web/showcase/pdf/templates/coverletter/cover-letter-timeline-minimal-v2.pdf b/web/showcase/pdf/templates/coverletter/cover-letter-timeline-minimal-v2.pdf
index 81802b954..c3e62dbb8 100644
Binary files a/web/showcase/pdf/templates/coverletter/cover-letter-timeline-minimal-v2.pdf and b/web/showcase/pdf/templates/coverletter/cover-letter-timeline-minimal-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-blue-banner-v2.pdf b/web/showcase/pdf/templates/cv/cv-blue-banner-v2.pdf
index b5404102f..100bf00d5 100644
Binary files a/web/showcase/pdf/templates/cv/cv-blue-banner-v2.pdf and b/web/showcase/pdf/templates/cv/cv-blue-banner-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-boxed-sections-v2.pdf b/web/showcase/pdf/templates/cv/cv-boxed-sections-v2.pdf
index 6e51afe98..7a5eae616 100644
Binary files a/web/showcase/pdf/templates/cv/cv-boxed-sections-v2.pdf and b/web/showcase/pdf/templates/cv/cv-boxed-sections-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-centered-headline-v2.pdf b/web/showcase/pdf/templates/cv/cv-centered-headline-v2.pdf
index 4e1914144..a30263c10 100644
Binary files a/web/showcase/pdf/templates/cv/cv-centered-headline-v2.pdf and b/web/showcase/pdf/templates/cv/cv-centered-headline-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-classic-serif-v2.pdf b/web/showcase/pdf/templates/cv/cv-classic-serif-v2.pdf
index b35986c87..c3e111e8e 100644
Binary files a/web/showcase/pdf/templates/cv/cv-classic-serif-v2.pdf and b/web/showcase/pdf/templates/cv/cv-classic-serif-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-compact-mono-v2.pdf b/web/showcase/pdf/templates/cv/cv-compact-mono-v2.pdf
index 580a7928f..81ed90bf1 100644
Binary files a/web/showcase/pdf/templates/cv/cv-compact-mono-v2.pdf and b/web/showcase/pdf/templates/cv/cv-compact-mono-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-editorial-blue-v2.pdf b/web/showcase/pdf/templates/cv/cv-editorial-blue-v2.pdf
index 3cab2f0b0..676d101bd 100644
Binary files a/web/showcase/pdf/templates/cv/cv-editorial-blue-v2.pdf and b/web/showcase/pdf/templates/cv/cv-editorial-blue-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-engineering-resume-v2.pdf b/web/showcase/pdf/templates/cv/cv-engineering-resume-v2.pdf
index 34fc9f487..f83e49343 100644
Binary files a/web/showcase/pdf/templates/cv/cv-engineering-resume-v2.pdf and b/web/showcase/pdf/templates/cv/cv-engineering-resume-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-executive-v2.pdf b/web/showcase/pdf/templates/cv/cv-executive-v2.pdf
index f819d65b0..272fffc8f 100644
Binary files a/web/showcase/pdf/templates/cv/cv-executive-v2.pdf and b/web/showcase/pdf/templates/cv/cv-executive-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-minimal-underlined-v2.pdf b/web/showcase/pdf/templates/cv/cv-minimal-underlined-v2.pdf
index acc1ee1eb..9f15b6553 100644
Binary files a/web/showcase/pdf/templates/cv/cv-minimal-underlined-v2.pdf and b/web/showcase/pdf/templates/cv/cv-minimal-underlined-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-mint-editorial-v2.pdf b/web/showcase/pdf/templates/cv/cv-mint-editorial-v2.pdf
index f7b6a19f7..d8e43db23 100644
Binary files a/web/showcase/pdf/templates/cv/cv-mint-editorial-v2.pdf and b/web/showcase/pdf/templates/cv/cv-mint-editorial-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-modern-professional-v2.pdf b/web/showcase/pdf/templates/cv/cv-modern-professional-v2.pdf
index 7d8e5a086..6228c9972 100644
Binary files a/web/showcase/pdf/templates/cv/cv-modern-professional-v2.pdf and b/web/showcase/pdf/templates/cv/cv-modern-professional-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-monogram-sidebar-v2.pdf b/web/showcase/pdf/templates/cv/cv-monogram-sidebar-v2.pdf
index 8c8acf756..07c7c2b30 100644
Binary files a/web/showcase/pdf/templates/cv/cv-monogram-sidebar-v2.pdf and b/web/showcase/pdf/templates/cv/cv-monogram-sidebar-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-nordic-clean-v2.pdf b/web/showcase/pdf/templates/cv/cv-nordic-clean-v2.pdf
index 791743aee..a664be6cd 100644
Binary files a/web/showcase/pdf/templates/cv/cv-nordic-clean-v2.pdf and b/web/showcase/pdf/templates/cv/cv-nordic-clean-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-panel-v2.pdf b/web/showcase/pdf/templates/cv/cv-panel-v2.pdf
index 02a494309..f7b582bb1 100644
Binary files a/web/showcase/pdf/templates/cv/cv-panel-v2.pdf and b/web/showcase/pdf/templates/cv/cv-panel-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-sidebar-portrait-v2.pdf b/web/showcase/pdf/templates/cv/cv-sidebar-portrait-v2.pdf
index ed36f04f2..693ad4b58 100644
Binary files a/web/showcase/pdf/templates/cv/cv-sidebar-portrait-v2.pdf and b/web/showcase/pdf/templates/cv/cv-sidebar-portrait-v2.pdf differ
diff --git a/web/showcase/pdf/templates/cv/cv-timeline-minimal-v2.pdf b/web/showcase/pdf/templates/cv/cv-timeline-minimal-v2.pdf
index 8e3dcf227..eb864108b 100644
Binary files a/web/showcase/pdf/templates/cv/cv-timeline-minimal-v2.pdf and b/web/showcase/pdf/templates/cv/cv-timeline-minimal-v2.pdf differ
diff --git a/web/showcase/pdf/templates/invoice/invoice-cinematic.pdf b/web/showcase/pdf/templates/invoice/invoice-cinematic.pdf
index 04aa5b871..80696f00c 100644
Binary files a/web/showcase/pdf/templates/invoice/invoice-cinematic.pdf and b/web/showcase/pdf/templates/invoice/invoice-cinematic.pdf differ
diff --git a/web/showcase/pdf/templates/invoice/invoice.pdf b/web/showcase/pdf/templates/invoice/invoice.pdf
index 937326769..91cb652c6 100644
Binary files a/web/showcase/pdf/templates/invoice/invoice.pdf and b/web/showcase/pdf/templates/invoice/invoice.pdf differ
diff --git a/web/showcase/pdf/templates/proposal/project-proposal-cinematic.pdf b/web/showcase/pdf/templates/proposal/project-proposal-cinematic.pdf
index c8aeaf8d5..7cc887b1f 100644
Binary files a/web/showcase/pdf/templates/proposal/project-proposal-cinematic.pdf and b/web/showcase/pdf/templates/proposal/project-proposal-cinematic.pdf differ
diff --git a/web/showcase/pdf/templates/proposal/proposal-cinematic.pdf b/web/showcase/pdf/templates/proposal/proposal-cinematic.pdf
index 4e7bae40e..0d6fdce13 100644
Binary files a/web/showcase/pdf/templates/proposal/proposal-cinematic.pdf and b/web/showcase/pdf/templates/proposal/proposal-cinematic.pdf differ
diff --git a/web/showcase/pdf/templates/proposal/proposal.pdf b/web/showcase/pdf/templates/proposal/proposal.pdf
index 7f7b5279f..3d18c951b 100644
Binary files a/web/showcase/pdf/templates/proposal/proposal.pdf and b/web/showcase/pdf/templates/proposal/proposal.pdf differ
diff --git a/web/showcase/pdf/templates/schedule/weekly-schedule.pdf b/web/showcase/pdf/templates/schedule/weekly-schedule.pdf
index a2c160256..4a23dc2db 100644
Binary files a/web/showcase/pdf/templates/schedule/weekly-schedule.pdf and b/web/showcase/pdf/templates/schedule/weekly-schedule.pdf differ
diff --git a/web/showcase/screenshots/features/chrome/page-numbering.png b/web/showcase/screenshots/features/chrome/page-numbering.png
new file mode 100644
index 000000000..c76bbaa6f
Binary files /dev/null and b/web/showcase/screenshots/features/chrome/page-numbering.png differ
diff --git a/web/showcase/screenshots/features/chrome/pdf-chrome.png b/web/showcase/screenshots/features/chrome/pdf-chrome.png
index a02a23ddd..040257062 100644
Binary files a/web/showcase/screenshots/features/chrome/pdf-chrome.png and b/web/showcase/screenshots/features/chrome/pdf-chrome.png differ
diff --git a/web/showcase/screenshots/features/chrome/viewer-preferences.png b/web/showcase/screenshots/features/chrome/viewer-preferences.png
new file mode 100644
index 000000000..e234b7ace
Binary files /dev/null and b/web/showcase/screenshots/features/chrome/viewer-preferences.png differ
diff --git a/web/showcase/screenshots/features/layout/content-bleed.png b/web/showcase/screenshots/features/layout/content-bleed.png
new file mode 100644
index 000000000..40cc45af7
Binary files /dev/null and b/web/showcase/screenshots/features/layout/content-bleed.png differ
diff --git a/web/showcase/screenshots/features/layout/per-page-margin.png b/web/showcase/screenshots/features/layout/per-page-margin.png
new file mode 100644
index 000000000..ceaccd30c
Binary files /dev/null and b/web/showcase/screenshots/features/layout/per-page-margin.png differ
diff --git a/web/showcase/screenshots/features/layout/row-columns.png b/web/showcase/screenshots/features/layout/row-columns.png
new file mode 100644
index 000000000..7a083d082
Binary files /dev/null and b/web/showcase/screenshots/features/layout/row-columns.png differ
diff --git a/web/showcase/screenshots/features/layout/row-flex.png b/web/showcase/screenshots/features/layout/row-flex.png
new file mode 100644
index 000000000..8ffc8849a
Binary files /dev/null and b/web/showcase/screenshots/features/layout/row-flex.png differ
diff --git a/web/showcase/screenshots/features/layout/row-vertical-align.png b/web/showcase/screenshots/features/layout/row-vertical-align.png
new file mode 100644
index 000000000..7939646f0
Binary files /dev/null and b/web/showcase/screenshots/features/layout/row-vertical-align.png differ
diff --git a/web/showcase/screenshots/features/navigation/container-bookmark.png b/web/showcase/screenshots/features/navigation/container-bookmark.png
new file mode 100644
index 000000000..476925f73
Binary files /dev/null and b/web/showcase/screenshots/features/navigation/container-bookmark.png differ
diff --git a/web/showcase/screenshots/features/navigation/in-pdf-navigation.png b/web/showcase/screenshots/features/navigation/in-pdf-navigation.png
new file mode 100644
index 000000000..c4450d41a
Binary files /dev/null and b/web/showcase/screenshots/features/navigation/in-pdf-navigation.png differ
diff --git a/web/showcase/screenshots/features/navigation/page-reference.png b/web/showcase/screenshots/features/navigation/page-reference.png
new file mode 100644
index 000000000..fda011548
Binary files /dev/null and b/web/showcase/screenshots/features/navigation/page-reference.png differ
diff --git a/web/showcase/screenshots/features/navigation/table-of-contents.png b/web/showcase/screenshots/features/navigation/table-of-contents.png
new file mode 100644
index 000000000..c06b650b8
Binary files /dev/null and b/web/showcase/screenshots/features/navigation/table-of-contents.png differ
diff --git a/web/showcase/screenshots/features/shapes/line-cap.png b/web/showcase/screenshots/features/shapes/line-cap.png
new file mode 100644
index 000000000..d4728b10e
Binary files /dev/null and b/web/showcase/screenshots/features/shapes/line-cap.png differ
diff --git a/web/showcase/screenshots/features/shapes/line-fill.png b/web/showcase/screenshots/features/shapes/line-fill.png
new file mode 100644
index 000000000..ecf1976ec
Binary files /dev/null and b/web/showcase/screenshots/features/shapes/line-fill.png differ
diff --git a/web/showcase/screenshots/features/structure/multi-section-document.png b/web/showcase/screenshots/features/structure/multi-section-document.png
new file mode 100644
index 000000000..e8dd29024
Binary files /dev/null and b/web/showcase/screenshots/features/structure/multi-section-document.png differ
diff --git a/web/showcase/screenshots/features/svg/svg-icon-gallery.png b/web/showcase/screenshots/features/svg/svg-icon-gallery.png
index dd33ae42a..35a2622d5 100644
Binary files a/web/showcase/screenshots/features/svg/svg-icon-gallery.png and b/web/showcase/screenshots/features/svg/svg-icon-gallery.png differ
diff --git a/web/showcase/screenshots/features/text/emoji-clip-path.png b/web/showcase/screenshots/features/text/emoji-clip-path.png
new file mode 100644
index 000000000..c48688d66
Binary files /dev/null and b/web/showcase/screenshots/features/text/emoji-clip-path.png differ
diff --git a/web/showcase/screenshots/features/text/emoji-gallery.png b/web/showcase/screenshots/features/text/emoji-gallery.png
new file mode 100644
index 000000000..2551d08eb
Binary files /dev/null and b/web/showcase/screenshots/features/text/emoji-gallery.png differ
diff --git a/web/showcase/screenshots/features/text/emoji-shortcodes.png b/web/showcase/screenshots/features/text/emoji-shortcodes.png
new file mode 100644
index 000000000..34e5562a2
Binary files /dev/null and b/web/showcase/screenshots/features/text/emoji-shortcodes.png differ
diff --git a/web/showcase/screenshots/features/text/emoji-svg-vs-png.png b/web/showcase/screenshots/features/text/emoji-svg-vs-png.png
new file mode 100644
index 000000000..98ef37129
Binary files /dev/null and b/web/showcase/screenshots/features/text/emoji-svg-vs-png.png differ
diff --git a/web/showcase/screenshots/features/text/inline-highlight-chips.png b/web/showcase/screenshots/features/text/inline-highlight-chips.png
new file mode 100644
index 000000000..2e2303813
Binary files /dev/null and b/web/showcase/screenshots/features/text/inline-highlight-chips.png differ
diff --git a/web/showcase/screenshots/features/text/inline-svg-icons.png b/web/showcase/screenshots/features/text/inline-svg-icons.png
new file mode 100644
index 000000000..ba655ec81
Binary files /dev/null and b/web/showcase/screenshots/features/text/inline-svg-icons.png differ
diff --git a/web/showcase/screenshots/features/title/book-template.png b/web/showcase/screenshots/features/title/book-template.png
new file mode 100644
index 000000000..8d271ecc6
Binary files /dev/null and b/web/showcase/screenshots/features/title/book-template.png differ
diff --git a/web/showcase/screenshots/flagships/default/engine-deck.png b/web/showcase/screenshots/flagships/default/engine-deck.png
index 0471d773f..f493cbc5f 100644
Binary files a/web/showcase/screenshots/flagships/default/engine-deck.png and b/web/showcase/screenshots/flagships/default/engine-deck.png differ
diff --git a/web/showcase/screenshots/flagships/default/feature-catalog.png b/web/showcase/screenshots/flagships/default/feature-catalog.png
index 96ad05696..432417ba4 100644
Binary files a/web/showcase/screenshots/flagships/default/feature-catalog.png and b/web/showcase/screenshots/flagships/default/feature-catalog.png differ
diff --git a/web/showcase/screenshots/flagships/default/master-showcase.png b/web/showcase/screenshots/flagships/default/master-showcase.png
index c365454ed..f5ac16919 100644
Binary files a/web/showcase/screenshots/flagships/default/master-showcase.png and b/web/showcase/screenshots/flagships/default/master-showcase.png differ