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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ follow semantic versioning; release dates are ISO 8601.

### Documentation

- **The template-authoring guide describes the packages that exist.** It told a
contributor to put a new family under `templates.<family>.v2` in exactly five
sub-packages, one of them a per-family `theme/` — a layout 2.0 replaced. A family has
`data` / `components` / `widgets` / `presets`, the cosmetic tokens are the shared
`BrandTheme`, and the rule about not editing the v1 surface outlived that surface. The
contributing guide had the same problem in one paragraph, routing new template code
into `templates.builtins` and `templates.support`; both are gone, so the instruction
produced code that does not compile. Package names join the retired-surface guard,
which until now could only see types.
- **Three documents stop pointing at things that are not there.** The extension guide
sent a reader to a snapshot-test directory that does not exist, the layout-snapshot
page listed two tests among six that are not in the repository, and the docs index
still carried a rework warning for a page reworked a major release ago. The roadmap
asked for a warning on dropped DOCX content that the backend already logs; what it is
really asking for — a mode that refuses instead of dropping — now says so.
- **The example catalogue stops offering a type that ships nowhere.** Its entry-point
table introduced the cover letter as a `BusinessTheme.modern()` document, and
`BusinessTheme` is a record local to the examples module — a reader adding the
Expand Down
21 changes: 11 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ The 2.0 GA shipped, so the branches now hold their long-term roles:
- `render-docx/` — module **graph-compose-render-docx**
Semantic exporter `DocxSemanticBackend` (Apache POI based), under `com.demcha.compose.document.backend.semantic.docx`
- `templates/src/main/java/com/demcha/compose/document/templates/*` — module **graph-compose-templates**
Built-in templates (CV, cover letter, invoice, proposal, weekly schedule), DTOs, themes, registries, and scene composition helpers
Template families (CV, cover letter, invoice, proposal), their data records, the shared `BrandTheme`, and the widgets they compose from. Schedule data records live here too; the schedule document is composed by an example rather than by a template.
- `core/src/main/java/com/demcha/compose/document/showcase`
`FontShowcase` (bundled-font preview renderer) — stays in the core engine
- `core/src/main/java/com/demcha/compose/engine/*`
Expand Down Expand Up @@ -176,12 +176,13 @@ template feature. The rules:
- Layout integration for a new node is a `NodeDefinition<MyNode>`
registered with `NodeRegistry`. See `BuiltInNodeDefinitions` for
the established pattern.
- Built-in templates in `...document.templates.builtins` stay thin
public facades over reusable scene composers in
`...document.templates.support`. Keep PDF-only setup in the document
session/backend layer rather than inside template composers, and do
not import `PDDocument`, `PDPage`, `PDRectangle`, or low-level PDF
composer types into scene composer classes.
- A template family lives under `...document.templates.<family>` with
`data` / `components` / `widgets` / `presets`; cosmetic tokens are the
shared `BrandTheme` in `...document.templates.core.theme`, never a
package of the family's own. Keep PDF-only setup in the document
session/backend layer rather than inside a preset, and do not import
`PDDocument`, `PDPage`, `PDRectangle` or other low-level PDF types into
template code.
- Public template contracts are compose-first: prefer
`compose(DocumentSession, ...)`. New README snippets, runnable
examples, and integration docs must show `compose(...)` rather than
Expand Down Expand Up @@ -275,9 +276,9 @@ There is one template authoring pattern, whether you are adding a
new family or a new preset inside an existing one: the layered
architecture documented in
[**docs/templates/v2-layered/contributor-guide.md**](./docs/templates/v2-layered/contributor-guide.md).
Five sub-packages (`data/` / `theme/` / `components/` / `widgets/`
/ `presets/`), each with a clear contract, over the shared
`templates.core` layer.
A family has `presets/` and adds `data/`, `components/` and `widgets/`
as it needs them, each with a clear contract, over the shared
`templates.core` layer that owns the theme.

- Every preset is a `public final class` — no inheritance — with a
`create(BrandTheme)` factory returning `DocumentTemplate<S>`, plus a
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Committed internal direction for the post-2.0 line: refactors, scale work, and t
Not committed. Reflects current thinking; priorities may shift based on user feedback and adoption signals.

- **PPTX beyond beta.** Graduate the fixed-layout PPTX backend from `@Beta` to stable and close the remaining fidelity gaps &mdash; true vector clipping instead of the raster fallback ([#413](https://github.com/DemchaAV/GraphCompose/issues/413)), exact numeric dash arrays, and distinct per-corner radii.
- **DOCX visibility for unsupported nodes.** Make currently-silent skips (`shape`, `line`, `ellipse`, `barcode`) loud &mdash; minimum a warn log, ideally a strict-mode flag that fails instead of dropping content silently.
- **A strict mode for DOCX.** Dropping a node the format cannot carry (`shape`, `line`, `ellipse`, `barcode`) now warns, so the loss is at least visible in the log. What is missing is the option to refuse: a flag that fails the export instead of silently producing a document with content gone.
- **Block-level alignment for fixed-size flow children.** Paths, images, layer stacks, shape containers and barcodes currently left-align in a flow; centring one means wrapping it in a full-width `ShapeContainer` just to use its CENTER anchor. Add a per-node horizontal align (left / centre / right &mdash; the `margin: auto` / `align(center)` analogue) so a fixed box can place itself in the flow directly. Surfaced by the v1.8 SVG icon-gallery and feature-catalog work.
- **Backend-neutral layout measurement.** Decouple measurement from PDFBox-specific resources so non-PDF backends do not pull PDFBox into the dependency graph.
- **DOCX maturity.** Either expand DOCX coverage toward PDF parity, or move DOCX behind an explicitly experimental flag.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ class CanonicalSurfaceGuardTest {
"ModuleSummary");

/**
* Types the 2.0 line removed. Every one is absent from every {@code src/main}
* tree, so a contributor-facing document naming one is teaching code that cannot
* compile — which is how the template-authoring and engine-primitive sections of
* CONTRIBUTING went stale for a full release cycle.
* Names the 2.0 line removed — types, and the packages that held them. Every one is
* absent from every {@code src/main} tree, so a contributor-facing document naming
* one is teaching code that cannot compile — which is how the template-authoring
* and engine-primitive sections of CONTRIBUTING went stale for a full release
* cycle, and how two dead package names outlived the types that lived in them.
*
* <p>Deliberately absent: {@code BusinessTheme} survives as an examples-local
* theme helper used by twenty-one example sources, and {@code PptxSemanticBackend}
* theme helper the example catalogue still uses, and {@code PptxSemanticBackend}
* still ships beside the fixed-layout PPTX backend. Forbidding either would fail
* the build on text that is correct.</p>
*/
Expand All @@ -49,7 +50,19 @@ class CanonicalSurfaceGuardTest {
"Breakable",
"hasRender(",
"CvSpec",
"CvBuilder");
"CvBuilder",
// Package names, not types. The retired-type list could not catch a
// contributing guide that routed new template code into
// `templates.builtins` and `templates.support` — two packages the 2.0
// split left behind and nothing has occupied since, so the instruction
// read as current and produced code that does not compile. Both spellings
// are listed: a doc names a package either as a dotted coordinate or as the
// path it lives at, and the sentence that carried this one for a release
// used the path.
"templates.builtins",
"templates.support",
"templates/builtins/",
"templates/support/");

/**
* Types a contributor must not be pointed at when told how to build against the
Expand Down Expand Up @@ -211,9 +224,9 @@ void contributorFacingDocsShouldNotNameSurfaceRetiredIn2_0() throws IOException
}

assertThat(violations)
.describedAs("these documents name a type 2.0 removed, so anyone following "
+ "them writes code that does not compile. A document whose purpose "
+ "is to record the removal belongs under one of %s.",
.describedAs("these documents name a type or package 2.0 removed, so anyone "
+ "following them writes code that does not compile. A document whose "
+ "purpose is to record the removal belongs under one of %s.",
HISTORICAL_RECORD_PREFIXES)
.isEmpty();
}
Expand Down
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ back here.
- **[troubleshooting.md](troubleshooting.md)** — symptom-first fixes for common gotchas: stray `?` glyphs, silent DOCX drops, optional-dependency `NoClassDefFoundError`, running the bundled examples.

### Templates
- **[templates/business-templates.md](templates/business-templates.md)** — invoice & proposal templates: the compose-first contract, end to end. ⚠️ Being reworked for the layered `ModernInvoice` / `ModernProposal` surface.
- **[templates/v2-layered/](templates/v2-layered/)** — the template surface (CV is the reference implementation): `data` / `theme` / `components` / `widgets` / `presets`.
- **[templates/business-templates.md](templates/business-templates.md)** — invoice & proposal templates: the compose-first contract, end to end, on the layered `ModernInvoice` / `ModernProposal` surface.
- **[templates/v2-layered/](templates/v2-layered/)** — the template surface (CV is the reference implementation): `data` / `components` / `widgets` / `presets` per family, over the shared `templates.core.theme`.
- **[templates/v1-classic/](templates/v1-classic/)** — 🗄️ archived: the classic spec/builder/presets surface removed in 2.0; kept for pre-2.0 callers.

### Output backends
Expand Down Expand Up @@ -107,7 +107,7 @@ it does.
- **[adr/0012-nested-list-evolution.md](adr/0012-nested-list-evolution.md)** — nested list rendering evolution.
- **[adr/0013-composed-table-cell.md](adr/0013-composed-table-cell.md)** — composed table cell model.
- **[adr/0014-controlled-absolute-placement.md](adr/0014-controlled-absolute-placement.md)** — controlled absolute placement strategy.
- **[adr/0015-layered-template-architecture.md](adr/0015-layered-template-architecture.md)** — the layered `cv.v2` / `coverletter.v2` authoring model (current standard); supersedes the preset/builder portion of 0011.
- **[adr/0015-layered-template-architecture.md](adr/0015-layered-template-architecture.md)** — the layered `templates.cv` / `templates.coverletter` authoring model (current standard); supersedes the preset/builder portion of 0011.

> **ADR numbering gap (0005–0010)** is intentional — those numbers
> were reserved during a v1.5 restructure that landed under ADR 0011
Expand Down
6 changes: 3 additions & 3 deletions docs/contributing/extension-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ Set-up per node:

The
[`LayoutSnapshotAssertions`](../../testing/src/main/java/com/demcha/compose/testing/layout/LayoutSnapshotAssertions.java)
helper class wraps the diff machinery; existing snapshot tests under
`core/src/test/java/com/demcha/compose/document/templates/builtins/`
(e.g. `BuiltInTemplateLayoutSnapshotTest`) show the call site shape.
helper class wraps the diff machinery; the snapshot tests in the `qa`
module (`ChartLayoutSnapshotTest`, `ShapeContainerLayoutSnapshotTest`)
show the call site shape.

## Reading the source by responsibility

Expand Down
2 changes: 0 additions & 2 deletions docs/operations/layout-snapshot-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,6 @@ Prioritize documents that are most sensitive to layout regressions:

## Examples in this repository

- `RepositoryShowcaseRenderTest`
- `TablePaginationIntegrationTest`
- `FontShowcaseLayoutSnapshotTest`
- `ChartLayoutSnapshotTest`
- `ShapeContainerLayoutSnapshotTest`
Expand Down
37 changes: 19 additions & 18 deletions docs/templates/v2-layered/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,19 @@ inline DSL, and how to ship a new preset as ~150 lines that anyone can
read end-to-end.

### 🛠 You're adding a new template family to the library
You're a GraphCompose maintainer or contributor. You want to bring
invoice-v2, cover-letter-v2, or a new document type onto the same
5-layer pattern that CV uses.
You're a GraphCompose maintainer or contributor. You want to bring a
new document type onto the same layering the shipped families use.

→ **[contributor-guide.md](contributor-guide.md)**

You'll get the package convention (`<name>/v2/data` /
`theme` / `components` / `widgets` / `presets`), naming rules, test
expectations, doc expectations, and a worked checklist for a new
template family from empty folder to merged PR.
You'll get the package convention (`<family>/data` / `components` /
`widgets` / `presets` over the shared `templates.core.theme`), naming
rules, test expectations, doc expectations, and a worked checklist for a
new template family from empty folder to merged PR.

---

## The 5-layer pattern at a glance
## The layering at a glance

```
presets/ composition: data + theme + widgets → DocumentTemplate
Expand All @@ -74,10 +73,11 @@ presets/ composition: data + theme + widgets → DocumentTemplate
widgets/ LEGO bricks: Headline, Subheadline, ContactLine, SectionHeader, …
│ delegate to read tokens from
▼ ▼
components/ internal renderers + primitives theme/ palette
typography
spacing
decoration
components/ internal renderers + primitives templates.core.theme
BrandTheme: palette
typography
spacing
decoration
│ render
data/ records describing what to render (no styling)
Expand All @@ -95,16 +95,17 @@ The detailed contract for each layer is in

## What this pattern is *not*

- ❌ **Not a migration mandate.** Existing v1 templates
(`cv/spec`, `cv/builder`, `cv/presets`) continue to work and
ship. The layered pattern is for **new** templates and major
rewrites.
- ❌ **Not one shape for every family.** `presets/` is the only
package a family always has. CV carries all four; cover letter has no
`widgets/`; invoice and proposal are presets alone, reading data
records from the shared `templates.data.<family>`. Add a layer when
the family needs one.
- ❌ **Not a framework with magic.** Every file is plain
Java records + static helpers. No reflection, no annotations,
no codegen.
- ❌ **Not coupled to CV.** The pattern is domain-agnostic; CV is
just the first family migrated. Invoice or cover-letter would
use the same five folders with their own data shapes inside.
just the family that uses every layer, which is why it is the
reference implementation.
- ❌ **Not a UI framework.** No state, no events, no lifecycle.
Templates render static PDFs from immutable data.

Expand Down
2 changes: 1 addition & 1 deletion docs/templates/v2-layered/authoring-presets.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ When you do add a new widget:

A new preset needs at least:

1. **Smoke test** in `core/src/test/.../cv/presets/MyPresetSmokeTest.java`:
1. **Smoke test** in `qa/src/test/.../cv/presets/MyPresetSmokeTest.java`:
- `exposes_stable_identity` — checks `id()` and `displayName()`
- `default_factory_renders` — calls `create().compose(...)` with
a full sample document, asserts `session.roots()` is non-empty
Expand Down
Loading
Loading