Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/deploy-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</p>

> **Release status** &mdash;
> 🟢 **Latest stable**: [v1.8.0](https://github.com/DemchaAV/GraphCompose/releases/tag/v1.8.0) &mdash; codenamed **"illustrative"**: native vector charts, SVG &amp; gradient graphics, free-form clipping, and a leaner engine artifact. **[What's new in v1.8 &darr;](#whats-new-in-v18)**
> 🟢 **Latest stable**: [v1.9.0](https://github.com/DemchaAV/GraphCompose/releases/tag/v1.9.0) &mdash; codenamed **"navigable"**: in-document navigation (anchors, internal links, a clickable TOC, page references &amp; bookmarks), multi-section documents, and inline chips / SVG icons / colour emoji. **[What's new in v1.9 &darr;](#whats-new-in-v19)**

> &nbsp;·&nbsp; 🟡 **In develop**: next cycle — open (see [CHANGELOG](./CHANGELOG.md))
> &nbsp;·&nbsp; See [API stability policy](./docs/api-stability.md) for tier definitions.
Expand Down Expand Up @@ -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 &mdash; the engine gains a vector-graphics dimension.
The **"navigable"** release &mdash; a rendered PDF becomes a document you can move through.

<p align="center">
<img src="./assets/readme/chart-showcase.png" alt="GraphCompose native vector charts" width="720"/>
</p>

- **Native vector charts** &mdash; bar / line / pie, inline sparklines, and `MONOTONE` / `SMOOTH` line interpolation, drawn as native PDF Béziers (no rasterization): `section.chart(ChartSpec.line()&hellip;, style)`.
- **SVG path &amp; icon import** &mdash; `SvgIcon.parse(svg)` turns SVG into native vector geometry; recolour per use and place with `addSvgIcon(icon, width, align)`.
- **Gradients &amp; free-form clipping** &mdash; linear / radial `DocumentPaint` fills and arbitrary `ShapeOutline.Path` clip regions.
- **Block-level alignment** &mdash; `addAligned(HorizontalAlign.CENTER, node)` centres or right-aligns any fixed node without a wrapper container.
- **`keepTogether()` pagination** &mdash; keep a section from splitting across a page break.
- **Leaner publication** &mdash; the bundled Google fonts moved to the independently-versioned `graph-compose-fonts` artifact, so the engine jar dropped from ~20&nbsp;MB to ~2&nbsp;MB. Pure-text / standard-14 documents need nothing extra; add `graph-compose-fonts` (or `graph-compose-bundle`) to keep the bundled families &mdash; see the [migration note](./docs/migration/v1.8.0-fonts.md).
- **In-document navigation** &mdash; 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 &amp; page references** &mdash; `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 &amp; viewer preferences** &mdash; `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** &mdash; `GraphCompose.documents()` concatenates independently authored sections &mdash; each with its own page size, margins, and footer numbering &mdash; into one PDF, with anchors, links, and the outline resolving across section boundaries.
- **Richer row &amp; page layout** &mdash; `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 &amp; colour emoji** &mdash; 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** &mdash; `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 &mdash; 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

Expand All @@ -76,12 +73,12 @@ Core document APIs stay source- and binary-compatible with v1.7 (`ConfigLoader`
<dependency>
<groupId>io.github.demchaav</groupId>
<artifactId>graph-compose</artifactId>
<version>1.8.0</version>
<version>1.9.0</version>
</dependency>
```

```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
Expand All @@ -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
> <dependency>
> <groupId>io.github.demchaav</groupId>
> <artifactId>graph-compose-emoji</artifactId>
> <version>1.0.0</version>
> </dependency>
> ```
>
> An unknown shortcode falls back to its literal text, so a document that uses no
> emoji &mdash; or runs without the artifact &mdash; renders unchanged. The
> `graph-compose-bundle` stays fonts-only; emoji is opt-in.

> **Distribution** &mdash; Maven Central is the canonical channel from **v1.6.6** onwards
> (`io.github.demchaav:graph-compose:<version>`). Hosted Javadocs auto-publish to
> [javadoc.io/doc/io.github.demchaav/graph-compose](https://javadoc.io/doc/io.github.demchaav/graph-compose)
Expand Down
2 changes: 1 addition & 1 deletion aggregator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.demchaav</groupId>
<artifactId>graph-compose-build</artifactId>
<version>1.8.0</version>
<version>1.9.0</version>
<packaging>pom</packaging>

<name>GraphCompose Build Aggregator</name>
Expand Down
Binary file added assets/readme/examples/emoji-shortcodes.pdf
Binary file not shown.
Binary file added assets/readme/examples/in-pdf-navigation.pdf
Binary file not shown.
Binary file not shown.
Binary file added assets/readme/examples/inline-svg-icons.pdf
Binary file not shown.
Binary file modified assets/readme/repository_showcase_render.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>io.github.demchaav</groupId>
<artifactId>graph-compose-build</artifactId>
<version>1.8.0</version>
<version>1.9.0</version>
<relativePath>../aggregator/pom.xml</relativePath>
</parent>

Expand All @@ -25,9 +25,9 @@
<maven.compiler.release>17</maven.compiler.release>

<jmh.version>1.37</jmh.version>
<junit.bom.version>6.1.0</junit.bom.version>
<junit.bom.version>6.1.1</junit.bom.version>
<assertj.version>3.27.7</assertj.version>
<logback.version>1.5.34</logback.version>
<logback.version>1.5.37</logback.version>

<openhtmltopdf.version>1.0.10</openhtmltopdf.version>
<itext.version>9.6.0</itext.version>
Expand Down
2 changes: 1 addition & 1 deletion bundle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
line. The graph-compose dependency below uses ${project.version}, so it
follows automatically.
-->
<version>1.8.0</version>
<version>1.9.0</version>
<packaging>pom</packaging>

<name>GraphCompose Bundle</name>
Expand Down
9 changes: 8 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand All @@ -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.

Expand All @@ -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.
Expand Down
89 changes: 89 additions & 0 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading