Skip to content

Commit ff05e74

Browse files
authored
docs(architecture): retarget the engine documentation to 2.x (#438)
The architecture pages still described the Entity/ECS engine removed in 2.0 and a PPTX backend that "is roadmap for v1.6+". A reader following them reached for `RenderStream`, `RenderPassSession`, `TextComponent`, `EntityManager`, `ParentComponent`, `EntityBounds`, `ParentContainerUpdater`, `LayoutTraversalContext`, `ComputedPosition`, `Breakable` and the packages `engine.core` / `engine.pagination` — none of which exist on any live branch. The release runbook called itself the 1.x checklist and pointed every command at `aggregator/pom.xml` and `-pl .`, neither of which is the 2.x layout. Rewrite the stale sections against the code as it is. `overview.md` drops the ECS responsibilities section, names both fixed-layout backends in the pipeline and the package list, and gains a PPTX leg in the diagram. That diagram also had the DOCX edge in the wrong place: `DocxSemanticBackend.export` takes a `DocumentGraph`, so it branches off the semantic tree, not off the resolved layout — which is exactly why it cannot reproduce fixed-layout geometry. `package-map.md` rebuilds the engine table from the live package list (`components` / `debug` / `font` / `measurement` / `render` / `text`), splits the render-docx and render-pptx row that claimed both were semantic exporters, and adds the `document.backend.fixed.pptx` row. `lifecycle.md` loses its v1.2 title, gains the PPTX render path, and states the chrome coverage per backend instead of calling metadata and watermarks PDF-only. `pagination-ordering.md` describes the mechanism that actually runs — a single sequential compile pass whose emission order is render order — and its debugging steps now name the live pagination log events and `layoutSnapshot()`. The runbook becomes the 2.x checklist: the whole-reactor gate, the 13-pom lockstep list taken from the script's own array, the real staging allow-list including the per-module READMEs, and a branch-flow section describing the standing roles rather than the completed 2.0 transition. It also records two traps that cost time this cycle: a gate command ending in a pipe reports the pipe's status and hides `BUILD FAILURE`, and after a version bump every goal resolving train modules from `~/.m2` needs an `install` first. Verified: full reactor `clean verify` green (1508 tests, 0 failures); no dead engine identifier remains anywhere under docs/architecture.
1 parent 3e28724 commit ff05e74

6 files changed

Lines changed: 170 additions & 128 deletions

File tree

docs/architecture/canonical-legacy-parity.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ Public application code should start with:
88

99
`GraphCompose.document(...) -> DocumentSession -> DocumentDsl -> layout graph -> PdfFixedLayoutBackend`
1010

11-
`EntityManager` and raw engine builders remain internal, test-support, or
12-
compatibility concerns. New authoring features should be added through
13-
`com.demcha.compose.document.*`, not through low-level entity assembly.
11+
The `com.demcha.compose.engine.*` foundation remains internal. New authoring
12+
features should be added through `com.demcha.compose.document.*`, not by
13+
reaching into engine internals.
1414

1515
## Status Legend
1616

docs/architecture/lifecycle.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Canonical Document Lifecycle
22

3-
GraphCompose v1.2 follows the canonical session-first lifecycle:
3+
GraphCompose follows a session-first lifecycle:
44

55
```text
66
GraphCompose.document(...)
@@ -9,7 +9,7 @@ GraphCompose.document(...)
99
-> semantic nodes
1010
-> layout graph
1111
-> layout snapshot or fixed backend render
12-
-> PDF stream/bytes/file
12+
-> PDF or PPTX stream/bytes/file
1313
```
1414

1515
```mermaid
@@ -41,10 +41,12 @@ for layout, pagination, diagnostics, and rendering.
4141

4242
`DocumentSession` is mutable and not thread-safe. Create one session per document/request.
4343

44-
Advanced PDF-only options such as metadata, protection, watermark, headers,
45-
and footers are configured through `PdfFixedLayoutBackend.builder()`. The
46-
session convenience PDF methods expose only the common document-level options,
47-
including guide-line overlays.
44+
Backend-level tuning is configured through the backend builder —
45+
`PdfFixedLayoutBackend.builder()` for PDF-specific options such as protection,
46+
and `PptxFixedLayoutBackend.builder()` for PPTX-specific ones such as
47+
deterministic output and the clip raster fallback. The backend-neutral chrome
48+
(metadata, watermark, headers/footers) is set on the session itself and reaches
49+
whichever backend renders, subject to the coverage noted in §5.
4850

4951
## 2. Authoring
5052

@@ -78,9 +80,9 @@ snapshot.
7880

7981
Pagination happens during layout. Semantic nodes define whether they are atomic or splittable. Long paragraphs/lists can split into fragments; atomic blocks move to the next page when needed.
8082

81-
The lower-level ECS engine still has pagination helpers under `com.demcha.compose.engine.pagination` for internal tests, diagnostics, and backend/tooling work.
83+
Pagination lives entirely in `LayoutCompiler` and the `NodeDefinition` split contracts under `com.demcha.compose.document.layout`. A node opts into keep-together or keep-with-next behaviour through its semantic flags; the compiler resolves the page break and emits the resulting `PlacedFragment` records.
8284

83-
## 5. Render
85+
## 5. Render (PDF / PPTX)
8486

8587
`DocumentSession.writePdf(OutputStream)` renders the resolved layout graph through `PdfFixedLayoutBackend` and writes the PDF to a caller-owned stream without closing it. This is the preferred server path because the session does not keep a PDF byte-array cache.
8688

@@ -94,6 +96,10 @@ The canonical PDF backend:
9496
- dispatches placed fragments to payload handlers
9597
- applies bookmarks, links, guide lines, metadata, watermarks, headers/footers, and protection
9698

99+
`writePptx(OutputStream)`, `toPptxBytes()` and `buildPptx(...)` are the PowerPoint counterparts, resolved through the `"pptx"` provider when `graph-compose-render-pptx` is on the classpath. `PptxFixedLayoutBackend` consumes the **same** resolved layout graph — one page becomes one identically-sized slide, and fragments land at the same coordinates — then emits native POI XSLF shapes rather than a rasterised page.
100+
101+
The chrome options are backend-neutral, but coverage differs. Watermarks and repeating headers/footers apply to both backends; metadata applies to both, mapping onto OPC core properties for PPTX where the producer field has no equivalent. Protection, viewer preferences and the debug guide-line overlays are PDF concepts that the PPTX backend ignores with a one-time warning. See the [backend capability matrix](./backend-capability-matrix.md) for the per-capability breakdown.
102+
97103
## 6. Close
98104

99105
Always close the session, normally with try-with-resources. Closing releases measurement resources and clears request-local text measurement caches. It does not require consumers to manage PDFBox objects directly.

docs/architecture/overview.md

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@ flowchart TD
2020
A["Application code — GraphCompose.document(...)"] --> B["DocumentSession + DocumentDsl<br/>(document.api · document.dsl)"]
2121
B --> C["Semantic DocumentNode tree<br/>(document.node) — renderer-neutral"]
2222
C --> D["LayoutCompiler + NodeRegistry<br/>(document.layout) → LayoutFragments"]
23+
C -->|"export(...)"| H["DocxSemanticBackend — Apache POI<br/>(document.backend.semantic) — reads DocumentGraph"]
2324
D --> E["Shared engine foundation — @Internal<br/>(engine.*): measure → paginate → place → order"]
24-
E --> F{"Active backend"}
25+
E --> F{"Fixed-layout backend"}
2526
F -->|PDF| G["PdfFixedLayoutBackend<br/>(document.backend.fixed.pdf + engine.render.pdf)"]
26-
F -->|DOCX| H["DocxSemanticBackendApache POI<br/>(document.backend.semantic)"]
27+
F -->|PPTX| P["PptxFixedLayoutBackend — POI XSLF<br/>(document.backend.fixed.pptx) — @Beta"]
2728
E -.->|"layoutSnapshot()"| I["Deterministic layout snapshot<br/>(regression tests — no bytes rendered)"]
2829
```
2930

31+
The two fixed-layout backends branch from the **same** resolved graph, which
32+
is why page and slide geometry match by construction. The semantic DOCX
33+
exporter branches earlier, straight off the `DocumentGraph`: it never sees a
34+
`LayoutGraph`, which is why it cannot reproduce fixed-layout geometry.
35+
3036
The PDF path deliberately spans **two** packages: the canonical backend
3137
`document.backend.fixed.pdf` owns PDFBox lifecycle and option translation,
3238
then dispatches resolved fragments to the engine render handlers under
@@ -48,8 +54,10 @@ Concretely:
4854
placement, and render ordering against those prepared fragments.
4955
5. the active backend turns the resolved `LayoutGraph` /
5056
`PlacedFragment` stream into output bytes — `PdfFixedLayoutBackend`
51-
for PDF, `DocxSemanticBackend` for DOCX, future PPTX backend
52-
skeleton in place.
57+
for PDF and `PptxFixedLayoutBackend` for PowerPoint, both consuming
58+
the same resolved graph, so page and slide geometry match by
59+
construction. `DocxSemanticBackend` takes the other route: it
60+
consumes the semantic node tree directly, without the layout graph.
5361

5462
That separation is the core project concept. Public code describes
5563
document intent, layout resolves geometry, renderers only draw already
@@ -105,11 +113,19 @@ need to reach below it.
105113
does not need to touch it.
106114
- **`document.backend.fixed.pdf`** — the canonical PDF backend
107115
(`PdfFixedLayoutBackend`, fragment render handlers, option
108-
translators). The only place PDFBox imports are allowed outside the
109-
engine foundation.
110-
- **`document.backend.semantic`** — semantic exporters
111-
(`DocxSemanticBackend` based on Apache POI; `PptxSemanticBackend`
112-
manifest skeleton).
116+
translators), shipped in **graph-compose-render-pdf**. The only place
117+
PDFBox imports are allowed outside the engine foundation.
118+
- **`document.backend.fixed.pptx`** — the fixed-layout PowerPoint
119+
backend (`PptxFixedLayoutBackend`, `PptxFragmentRenderHandler` and its
120+
handler set), shipped in **graph-compose-render-pptx**. Consumes the
121+
same resolved `LayoutGraph` as the PDF backend — one page becomes one
122+
identically-sized slide. Marked `@Beta` at the package level; see the
123+
[backend capability matrix](./backend-capability-matrix.md) for
124+
per-capability fidelity.
125+
- **`document.backend.semantic`** — semantic exporters that bypass the
126+
layout graph (`DocxSemanticBackend`, Apache POI; and `PptxSemanticBackend`,
127+
a slide-safe node-graph manifest that predates the fixed-layout backend
128+
and is not what `buildPptx(...)` uses).
113129

114130
## Template layer (`com.demcha.compose.document.templates.*`)
115131

@@ -165,26 +181,11 @@ objects are resolved before their parent containers so parent
165181
finalized. See [pagination-ordering.md](./pagination-ordering.md) for
166182
the detailed rationale and the failure modes that motivated it.
167183

168-
The engine materializes one deterministic hierarchy snapshot per
169-
layout pass: parent links from `ParentComponent`, sibling order from
170-
`Entity.children`, roots / layers / depth metadata rebuilt every pass.
171-
Layout, pagination, snapshot extraction, and render backends all
172-
agree on the same tree semantics.
173-
174-
### Entity / ECS responsibilities (engine-internal)
175-
176-
`Entity` is intentionally a thin ECS-style identity object. It owns:
177-
178-
- stable identity
179-
- the component map
180-
- canonical child order through `Entity.children`
181-
- a cached render marker reference for fast `hasRender()` checks
182-
183-
Layout-specific math and pagination mutation live in dedicated
184-
helpers — `EntityBounds` for geometry reads,
185-
`ParentContainerUpdater` for parent-container size and page-shift
186-
propagation. Deprecated helper methods on `Entity` are migration
187-
shims, not extension points.
184+
The compiler materializes one deterministic result per layout pass:
185+
`LayoutCompiler` prepares each semantic node into a `PreparedNode`,
186+
paginates it, and emits `PlacedFragment` records into a `LayoutGraph`.
187+
Layout, pagination, snapshot extraction, and render backends all read
188+
that one resolved graph, so they cannot disagree about geometry.
188189

189190
### Semantic modules
190191

@@ -219,26 +220,22 @@ rows it merges.
219220
These rules apply to engine and backend contributors. Application
220221
code should not need any of them.
221222

222-
- engine builders and layout helpers consume an engine-level
223-
`TextMeasurementSystem` instead of reaching through the active
224-
renderer
225-
- render marker components identify *what* needs to be rendered;
226-
*how* it is drawn lives in renderer-owned handler packages such as
227-
the `PdfFragmentRenderHandler` implementations under
228-
`document.backend.fixed.pdf.handlers`
229-
- `RenderStream` acts as a session factory, not as a per-entity
230-
content-stream opener
231-
- `RenderPassSession` is the shared seam for page lifetime and
232-
page-surface reuse — it must stay free of PDFBox and backend
233-
package imports
234-
- the PDF entity path dispatches through registered render handlers;
235-
there is no backend-specific render fallback path
236-
237-
Fixed leaf primitives (such as `TextComponent` and `BlockText`)
238-
follow the same engine contract: they materialize as regular
239-
entities with render/content/layout components, rely on normal
240-
`ContentSize` / `Padding` / `Margin` / `Placement`, and do not
241-
introduce a separate layout subsystem.
223+
- layout helpers consume an engine-level `TextMeasurementSystem`
224+
instead of reaching through the active renderer, so measurement is
225+
backend-neutral and the same widths produce the layout graph that
226+
every backend then draws
227+
- a `PlacedFragment`'s payload identifies *what* needs to be rendered;
228+
*how* it is drawn lives in renderer-owned handler packages — the
229+
`PdfFragmentRenderHandler` implementations under
230+
`document.backend.fixed.pdf.handlers` and the
231+
`PptxFragmentRenderHandler` set under
232+
`document.backend.fixed.pptx.handlers`
233+
- each fixed-layout backend owns its own render-pass session and page
234+
surface lifetime; that seam stays free of backend-library imports so a
235+
new backend does not have to touch engine code
236+
- fragment dispatch goes through registered handlers only; there is no
237+
backend-specific render fallback path, and an unhandled payload fails
238+
with `UnsupportedNodeCapabilityException` rather than drawing nothing
242239

243240
## Current package roots
244241

@@ -276,8 +273,16 @@ last:
276273
`ShapeContainerNode` clip and `DocumentTransform` rotation/scale
277274
fall back to inline content with a one-time capability warning.
278275
Authors who need clipped or rotated output must export to PDF.
279-
- The PPTX skeleton lives behind `PptxSemanticBackend`; richer slide
280-
layout is roadmap for v1.6+.
276+
- The PPTX backend (`PptxFixedLayoutBackend`, Apache POI XSLF) renders
277+
the same resolved `LayoutGraph` as PDF into an editable deck — one
278+
page per identically-sized slide, native shapes rather than pictures.
279+
It ships `@Beta` in 2.1.0: usable for production decks, with the API
280+
shape still open to change in a minor. Clipped composites fall back to
281+
a rasterised island (switchable via `clipRasterFallback(false)`), and
282+
`renderToImages` is unsupported — the per-capability breakdown is the
283+
[backend capability matrix](./backend-capability-matrix.md).
284+
The older `PptxSemanticBackend` manifest remains in the module but is
285+
not on the `buildPptx(...)` path.
281286
- New backends should add their own rendering system, render-pass
282287
session, text measurement system, and handler set without changing
283288
engine builders such as tables or template data models. The shared

docs/architecture/package-map.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ ships them differs.
2323
| --- | --- |
2424
| `graph-compose-core` | The lean engine: `com.demcha.compose`, the canonical `document.*` authoring surface (`api` / `dsl` / `node` / `style` / `table` / `snapshot`), `document.showcase` (`FontShowcase`), the `document.backend.fixed` SPI seam, the public `document.backend.fixed.pdf.options` records, `document.layout`, `font.*`, and the internal `engine.*` foundation. |
2525
| `graph-compose-render-pdf` | The PDFBox backend: `document.backend.fixed.pdf.**` (the `PdfFixedLayoutBackend` impl + handlers) and the `engine.render.pdf.**` render tree. Registers the PDF `FixedLayoutBackendProvider` / `FontMetricsProvider`. |
26-
| `graph-compose-render-docx` / `graph-compose-render-pptx` | The POI semantic exporters — `document.backend.semantic.docx` / `.pptx`. |
26+
| `graph-compose-render-pptx` | The POI XSLF backend: `document.backend.fixed.pptx.**` (the `PptxFixedLayoutBackend` impl + handlers), registering the `"pptx"` `FixedLayoutBackendProvider`. Also carries the older `document.backend.semantic.pptx` manifest exporter. Depends on `graph-compose-render-pdf` for shared font measurement and the clip raster pass. |
27+
| `graph-compose-render-docx` | The POI semantic exporter — `document.backend.semantic.docx`. |
2728
| `graph-compose-templates` | The built-in preset families — `document.templates.**`. |
2829
| `graph-compose-testing` | Consumer test support — `com.demcha.compose.testing.**`. |
2930
| `graph-compose` | Back-compat wrapper: an empty jar over `graph-compose-core` + `graph-compose-render-pdf`. |
@@ -54,6 +55,7 @@ per-package artifact.
5455
| `com.demcha.compose.document.layout` (`@Internal` at package level) | Semantic layout compiler, node definitions, fragments, split/measure contracts, and layout graph. | `NodeDefinition` is `@Beta` — Extension SPI for custom node types. New node behavior must be deterministic and covered by layout or render tests. |
5556
| `com.demcha.compose.document.backend.fixed` | Backend-neutral fixed-layout rendering contract. | Keep it independent from PDFBox and semantic template data. |
5657
| `com.demcha.compose.document.backend.fixed.pdf` | Canonical fixed-layout PDF backend, fragment handlers, PDF-specific options, and PDF-backed measurement resources. | Keep PDFBox lifecycle internal; normal callers should use `DocumentSession` and default PDF convenience methods. |
58+
| `com.demcha.compose.document.backend.fixed.pptx` (`@Beta` at package level) | Fixed-layout PowerPoint backend — `PptxFixedLayoutBackend`, its provider, deterministic writer, embedded-font and clip support, plus the `.handlers` fragment handler set. Consumes the same resolved `LayoutGraph` as the PDF backend. | Keep POI XSLF lifecycle internal. Any capability change must update [backend-capability-matrix.md](./backend-capability-matrix.md) in the same commit. |
5759
| `com.demcha.compose.document.dsl.internal` | Internal helpers for public DSL builders such as semantic name normalization and builder callback application. | Do not expose these helpers in examples; move reusable authoring concepts to public builder classes instead. |
5860
| `com.demcha.compose.document.backend.semantic` | Experimental semantic export contracts for non-fixed outputs. | Keep exporters separate from PDF fixed-layout rendering. |
5961
| `com.demcha.compose.document.debug` | Snapshot/debug adapters for canonical layout graph inspection. | Debug output should be deterministic and safe for tests. |
@@ -62,9 +64,9 @@ per-package artifact.
6264

6365
| Package | Responsibility | Extension rule |
6466
| --- | --- | --- |
65-
| `com.demcha.compose.engine.components.*` | Low-level ECS components, content payloads, style values, geometry, layout, and render markers. | Use only for engine primitives; public document authoring should go through `DocumentDsl` and semantic nodes. |
66-
| `com.demcha.compose.engine.core` | Entity manager, canvas, traversal context, and base ECS system contracts. | Keep core thin; put stage-specific logic in layout, pagination, measurement, or render packages. |
67-
| `com.demcha.compose.engine.pagination` | Pagination markers and helpers (`Breakable`, `ParentContainerUpdater`, `Offset`). | Maintain child-first ordering and page-shift propagation rules. |
67+
| `com.demcha.compose.engine.components.*` | Engine content payloads and their caches — decoded image data and intrinsic sizes, barcode payloads, and the shared bounded LRU cache behind them. | Use only for engine primitives; public document authoring should go through `DocumentDsl` and semantic nodes. |
68+
| `com.demcha.compose.engine.debug` | Layout snapshot value types (`LayoutSnapshot`, `LayoutNodeSnapshot`, `LayoutCanvasSnapshot`, `LayoutInsetsSnapshot`) behind `DocumentSession.layoutSnapshot()`. | Snapshot output must stay deterministic — it is a regression-test contract. |
69+
| `com.demcha.compose.engine.font` | Backend-neutral font abstraction: `Font`, `FontBase`, `FontLineMetrics`. | Backends implement the seam; the catalogue and lookup live in the public `com.demcha.compose.font`. |
6870
| `com.demcha.compose.engine.measurement` | Text measurement contracts and font-backed implementations. | Builders/layout helpers depend on this seam instead of reaching into renderers. |
6971
| `com.demcha.compose.engine.render` | Backend-neutral render contracts, handler registry, and render-pass session lifetime. | Add backend-neutral contracts here, backend-specific drawing elsewhere. |
7072
| `com.demcha.compose.engine.render.pdf` | Shared PDFBox primitives for the canonical fixed-layout backend: `PdfFont`, `GlyphFallbackLogger`, and the header/footer + watermark renderers under `helpers`. | Add canonical-shared PDF support here; per-fragment PDF drawing lives in the `PdfFragmentRenderHandler` implementations under `document.backend.fixed.pdf.handlers`. |

0 commit comments

Comments
 (0)