Skip to content

Commit ef6ebed

Browse files
committed
Merge remote-tracking branch 'origin/develop' into refactor/font-vertical-metrics-contract
# Conflicts: # CHANGELOG.md
2 parents 0cda576 + 587cc8c commit ef6ebed

57 files changed

Lines changed: 363 additions & 146 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,44 @@ Open cycle — bug-fix / housekeeping. Entries land here as they merge.
124124
(`GraphCompose.document() → DocumentSession → LayoutCompiler`) never runs; it
125125
imports nothing from them directly, and the former `GraphCompose.pdf(...)`
126126
entry point has already been removed. The ECS execution engine runs only under
127-
the legacy engine regression tests. (One vestigial coupling remains, tracked as
128-
a follow-up: the canonical `TextMeasurementSystem` still `extends
129-
engine.core.SystemECS` via a no-op `process(...)`.) The
130-
packages are now `@Deprecated` (package level, so no deprecation-warning cascade)
127+
the legacy engine regression tests. The packages are now `@Deprecated` (package
128+
level, so no deprecation-warning cascade)
131129
with corrected package docs, to stop misdirecting contributors into optimizing a
132130
dead engine. The genuinely shared engine packages (`engine.components`,
133131
`engine.measurement`, `engine.font`, `engine.render`) are **not** deprecated.
134132
No public API or behaviour change.
135133

134+
- **`TextMeasurementSystem` decoupled from `engine.core.SystemECS`.** The shared
135+
text-measurement contract (`engine.measurement.TextMeasurementSystem`) dropped
136+
its vestigial `extends SystemECS` and the no-op `process(EntityManager)` default
137+
it carried — it was never consumed as an ECS system. The legacy ECS engine now
138+
obtains the measurement service via `SystemRegistry.registerTextMeasurement(...)`
139+
/ `textMeasurement()` instead of enrolling it as a `process()`-driven system,
140+
completing the isolation of the deprecated `engine.core` from live and shared
141+
code (only the legacy engine regression tests still reference it). Dropping the
142+
super-interface is binary-incompatible on paper, so
143+
`engine.measurement.TextMeasurementSystem` is excluded from the japicmp gate
144+
until the baseline advances past this release. No canonical API or behaviour
145+
change.
146+
147+
- **The legacy ECS PDF render pipeline is deprecated.** Follow-up to the ECS
148+
engine deprecation above. The `Entity`-based PDFBox renderer
149+
(`PdfRenderingSystemECS` and its collaborators — `PdfRenderSession`, `PdfCanvas`,
150+
`PdfStream`, `PdfImageCache`, `PdfFileManagerSystem`, `PdfGuidesRenderer`, the
151+
render-marker handlers, and the `TableCellBox` / `PdfBookmarkBuilder` helpers) is
152+
the renderer for the removed `GraphCompose.pdf(...)` surface and now runs only
153+
under the legacy engine regression tests; canonical PDF output goes through
154+
`com.demcha.compose.document.backend.fixed.pdf`. Because `engine.render.pdf` is a
155+
*mixed* package — it also holds the canonical-shared `PdfFont`,
156+
`GlyphFallbackLogger`, and the header/footer + watermark post-processors — the
157+
legacy classes were physically moved into a new `engine.render.pdf.ecs`
158+
(with `.handlers` / `.helpers` sub-packages), which is then `@Deprecated` at
159+
package level (so no deprecation-warning cascade, same pattern as the ECS engine
160+
packages). The four genuinely shared `engine.render.pdf` types are **not**
161+
deprecated and stay put. No behaviour change. The relocated renderer has no
162+
public entry point and carries no binary-compatibility promise, so the move is
163+
excluded from the japicmp gate rather than treated as a breaking removal.
164+
136165
### Internal
137166

138167
- **Text-measurement line metrics resolve through the `Font` contract instead of a

CONTRIBUTING.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,11 @@ not need any of them.
168168
- Engine render markers implement backend-neutral `Render`. Do not
169169
add backend-specific render interfaces back into
170170
`engine/components`.
171-
- PDF rendering logic lives in
172-
`src/main/java/com/demcha/compose/engine/render/pdf/handlers/`.
171+
- PDF rendering logic for the legacy ECS renderer (deprecated; canonical PDF
172+
output goes through `com.demcha.compose.document.backend.fixed.pdf`) lives in
173+
`src/main/java/com/demcha/compose/engine/render/pdf/ecs/handlers/`.
173174
Backend-only helper objects live in
174-
`com.demcha.compose.engine.render.pdf.helpers`, not in
175+
`com.demcha.compose.engine.render.pdf.ecs.helpers`, not in
175176
`components/renderable`.
176177
- Builders and layout code get text width and line metrics from
177178
`TextMeasurementSystem`, not from
@@ -208,7 +209,7 @@ The rules above are enforced by tests:
208209
[EnginePdfBoundaryTest.java](./src/test/java/com/demcha/compose/engine/architecture/EnginePdfBoundaryTest.java),
209210
[CanonicalTemplateComposerPdfBoundaryTest.java](./src/test/java/com/demcha/compose/document/templates/architecture/CanonicalTemplateComposerPdfBoundaryTest.java),
210211
[PdfRenderInterfaceGuardTest.java](./src/test/java/com/demcha/compose/engine/render/pdf/PdfRenderInterfaceGuardTest.java),
211-
[PdfRenderingSystemECSDispatchTest.java](./src/test/java/com/demcha/compose/engine/render/pdf/PdfRenderingSystemECSDispatchTest.java)
212+
[PdfRenderingSystemECSDispatchTest.java](./src/test/java/com/demcha/compose/engine/render/pdf/ecs/PdfRenderingSystemECSDispatchTest.java)
212213

213214
## Adding a new feature
214215

@@ -325,7 +326,7 @@ Choose the smallest tests that match the change:
325326
- For low-level test harness changes:
326327
[ComponentBuilderTest.java](./src/test/java/com/demcha/compose/engine/components/ComponentBuilderTest.java)
327328
- For render-marker dispatch changes:
328-
[PdfRenderingSystemECSDispatchTest.java](./src/test/java/com/demcha/compose/engine/render/pdf/PdfRenderingSystemECSDispatchTest.java)
329+
[PdfRenderingSystemECSDispatchTest.java](./src/test/java/com/demcha/compose/engine/render/pdf/ecs/PdfRenderingSystemECSDispatchTest.java)
329330
- For layout/positioning behavior:
330331
[ComputedPositionTest.java](./src/test/java/com/demcha/compose/engine/components/layout/ComputedPositionTest.java)
331332
- For pagination and multi-page behavior:

docs/architecture/overview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ code should not need any of them.
231231
into the active renderer
232232
- render marker components identify *what* needs to be rendered;
233233
*how* it is drawn lives in renderer-owned handler packages such as
234-
`...render.pdf.handlers` (with helper objects under
235-
`...render.pdf.helpers`)
234+
`...render.pdf.ecs.handlers` (with helper objects under
235+
`...render.pdf.ecs.helpers`)
236236
- `RenderStream` acts as a session factory, not as a per-entity
237237
content-stream opener
238238
- `RenderPassSession` is the shared seam for page lifetime and

docs/architecture/package-map.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ intended.
4949
| `com.demcha.compose.engine.pagination` | Page-breaking helpers and pagination fallback systems. | Maintain child-first ordering and page-shift propagation rules. |
5050
| `com.demcha.compose.engine.measurement` | Text measurement contracts and font-backed implementations. | Builders/layout helpers depend on this seam instead of reaching into renderers. |
5151
| `com.demcha.compose.engine.render` | Backend-neutral render contracts, handler registry, render ordering, and render-pass session lifetime. | Add backend-neutral contracts here, backend-specific drawing elsewhere. |
52-
| `com.demcha.compose.engine.render.pdf` | Low-level PDF backend internals for ECS rendering. | PDFBox state stays here and in child handler/helper packages. |
52+
| `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; the legacy ECS renderer lives in the deprecated `ecs` sub-package. |
53+
| `com.demcha.compose.engine.render.pdf.ecs` | **Deprecated.** Legacy `Entity`-based ECS PDF renderer (`PdfRenderingSystemECS` and collaborators, plus the `ecs.handlers` / `ecs.helpers` sub-packages). | Dead renderer kept only for legacy regression tests; do not extend or optimize. |
5354
| `com.demcha.compose.engine.render.word` | Experimental Word backend internals. | Treat as research until a supported public surface is designed. |
5455
| `com.demcha.compose.engine.text` | Internal text hot-path utilities shared by layout and render stages. | Keep helpers backend-neutral and avoid public authoring concepts here. |
5556
| `com.demcha.compose.engine.text.markdown` | Markdown token parsing used by text preparation. | Keep output backend-neutral. |

docs/contributing/implementation-guide.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ Relevant files:
259259

260260
- [TableBuilder.java](./../src/test/java/com/demcha/compose/testsupport/engine/assembly/TableBuilder.java)
261261
- [TableRow.java](./../src/main/java/com/demcha/compose/engine/components/renderable/TableRow.java)
262-
- [TableCellBox.java](./../src/main/java/com/demcha/compose/engine/render/pdf/helpers/TableCellBox.java)
262+
- [TableCellBox.java](./../src/main/java/com/demcha/compose/engine/render/pdf/ecs/helpers/TableCellBox.java)
263263
- [TableResolvedCell.java](./../src/main/java/com/demcha/compose/engine/components/content/table/TableResolvedCell.java)
264264

265265
Rule of thumb:
@@ -321,21 +321,29 @@ Preferred extension pattern for new backends:
321321
4. keep backend-only helper drawing in renderer-owned helper packages when the code is not an entity render marker
322322
5. keep renderer ordering policy in the rendering layer rather than in pagination utilities
323323

324+
> **Deprecated reference implementation.** The PDF renderer named below
325+
> (`PdfRenderingSystemECS` and the `engine.render.pdf.ecs` handlers/helpers) is the
326+
> legacy `Entity`-based ECS renderer, kept only for regression tests; canonical PDF
327+
> output is produced by `com.demcha.compose.document.backend.fixed.pdf`. The
328+
> backend-neutral `engine.render` *contracts* (`Render`, `RenderPassSession`,
329+
> `RenderStream`, `EntityRenderOrder`) are **not** deprecated — only the ECS PDF
330+
> implementation is. Do not extend the `ecs` renderer.
331+
324332
Important files:
325333

326334
- [Render.java](./../src/main/java/com/demcha/compose/engine/render/Render.java)
327335
- [RenderPassSession.java](./../src/main/java/com/demcha/compose/engine/render/RenderPassSession.java)
328336
- [RenderStream.java](./../src/main/java/com/demcha/compose/engine/render/RenderStream.java)
329-
- [PdfRenderingSystemECS.java](./../src/main/java/com/demcha/compose/engine/render/pdf/PdfRenderingSystemECS.java)
330-
- [PdfRenderSession.java](./../src/main/java/com/demcha/compose/engine/render/pdf/PdfRenderSession.java)
337+
- [PdfRenderingSystemECS.java](./../src/main/java/com/demcha/compose/engine/render/pdf/ecs/PdfRenderingSystemECS.java)
338+
- [PdfRenderSession.java](./../src/main/java/com/demcha/compose/engine/render/pdf/ecs/PdfRenderSession.java)
331339
- [EntityRenderOrder.java](./../src/main/java/com/demcha/compose/engine/render/EntityRenderOrder.java)
332340

333341
Migration rule for new engine components:
334342

335343
- implement backend-neutral `Render`, not backend-specific render interfaces
336-
- move PDF drawing into `...render.pdf.handlers`
344+
- move PDF drawing into `...render.pdf.ecs.handlers`
337345
- use `TextMeasurementSystem` for text width and line metrics instead of reaching through `LayoutSystem`
338-
- place PDF-only helper objects in `...render.pdf.helpers`
346+
- place PDF-only helper objects in `...render.pdf.ecs.helpers`
339347
- keep page-surface lifetime in a backend-specific `RenderPassSession`, not in engine builders or render markers
340348
- keep resolved draw ordering in renderer-owned or renderer-neutral rendering helpers such as `EntityRenderOrder`
341349
- register a render handler for every engine render marker because the PDF entity path no longer supports a backend-specific render fallback

pom.xml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,42 @@
692692
-->
693693
<excludes>
694694
<exclude>com.demcha.compose.document.layout.payloads</exclude>
695+
<!--
696+
Legacy ECS PDF renderer (PdfRenderingSystemECS and its
697+
collaborators), relocated to
698+
com.demcha.compose.engine.render.pdf.ecs[.handlers|.helpers]
699+
and @Deprecated at package level. This is the dead
700+
Entity-based renderer that no public entry point reaches
701+
(canonical PDF output goes through
702+
document.backend.fixed.pdf); it carries no
703+
binary-compatibility promise. Both the relocated packages
704+
and the vacated original fully-qualified names are excluded
705+
so the package split is not flagged as a breaking removal.
706+
-->
707+
<exclude>com.demcha.compose.engine.render.pdf.ecs</exclude>
708+
<exclude>com.demcha.compose.engine.render.pdf.ecs.handlers</exclude>
709+
<exclude>com.demcha.compose.engine.render.pdf.ecs.helpers</exclude>
710+
<exclude>com.demcha.compose.engine.render.pdf.handlers</exclude>
711+
<exclude>com.demcha.compose.engine.render.pdf.PdfRenderingSystemECS</exclude>
712+
<exclude>com.demcha.compose.engine.render.pdf.PdfCanvas</exclude>
713+
<exclude>com.demcha.compose.engine.render.pdf.PdfStream</exclude>
714+
<exclude>com.demcha.compose.engine.render.pdf.PdfFileManagerSystem</exclude>
715+
<exclude>com.demcha.compose.engine.render.pdf.PdfGuidesRenderer</exclude>
716+
<exclude>com.demcha.compose.engine.render.pdf.helpers.TableCellBox</exclude>
717+
<exclude>com.demcha.compose.engine.render.pdf.helpers.PdfBookmarkBuilder</exclude>
718+
<!--
719+
engine.measurement.TextMeasurementSystem dropped its vestigial
720+
SystemECS super-interface (and the no-op process(EntityManager)
721+
default) — see CHANGELOG v1.7.1. japicmp reports this as
722+
INTERFACE_REMOVED, but it is safe: nothing consumes a
723+
TextMeasurementSystem as a SystemECS. The legacy ECS engine now
724+
obtains the measurement service via
725+
SystemRegistry.registerTextMeasurement(...) / textMeasurement()
726+
instead of enrolling it as a process()-driven system, and the
727+
canonical pipeline injects it directly. Drop this exclude once the
728+
japicmp baseline advances to the release that lands the change.
729+
-->
730+
<exclude>com.demcha.compose.engine.measurement.TextMeasurementSystem</exclude>
695731
</excludes>
696732
</parameter>
697733
</configuration>

src/main/java/com/demcha/compose/engine/components/content/text/BlockTextLineMetrics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public static double interLineGap(TextMeasurementSystem.LineMetrics previous,
143143

144144
private static TextMeasurementSystem measurementSystem(EntityManager entityManager) {
145145
return entityManager.getSystems()
146-
.getSystem(TextMeasurementSystem.class)
146+
.textMeasurement()
147147
.orElseThrow(() -> new IllegalStateException("TextMeasurementSystem is required to resolve block text metrics."));
148148
}
149149
}

src/main/java/com/demcha/compose/engine/components/renderable/TextComponent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static ContentSize autoMeasureText(Entity entity, EntityManager entityMan
2424
Text text = entity.getComponent(Text.class).orElseThrow();
2525
TextStyle style = entity.getComponent(TextStyle.class).orElseThrow();
2626
TextMeasurementSystem measurementSystem = entityManager.getSystems()
27-
.getSystem(TextMeasurementSystem.class)
27+
.textMeasurement()
2828
.orElseThrow(() -> new IllegalStateException("TextMeasurementSystem is required to auto-measure text."));
2929
ContentSize measured = measurementSystem.measure(style, text.value());
3030
double width = measured.width();

src/main/java/com/demcha/compose/engine/core/SystemRegistry.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.demcha.compose.engine.core;
22

33
import com.demcha.compose.engine.core.SystemECS;
4+
import com.demcha.compose.engine.measurement.TextMeasurementSystem;
5+
import lombok.AccessLevel;
46
import lombok.Getter;
57
import lombok.experimental.Accessors;
68
import lombok.extern.slf4j.Slf4j;
@@ -23,6 +25,41 @@ public class SystemRegistry {
2325
*/
2426
private final Map<Class<? extends SystemECS>, SystemECS> systems = new LinkedHashMap<>();
2527

28+
/**
29+
* The text-measurement service provider, held separately from the
30+
* process()-driven {@link #systems} map. Measurement exposes font metrics to
31+
* builders and layout helpers on demand; it is not a {@link SystemECS} and
32+
* never participates in the {@code processSystems()} loop, so it is registered
33+
* out-of-band via {@link #registerTextMeasurement(TextMeasurementSystem)}
34+
* rather than {@link #addSystem(SystemECS)}.
35+
*/
36+
@Getter(AccessLevel.NONE)
37+
private TextMeasurementSystem textMeasurement;
38+
39+
/**
40+
* Registers the text-measurement service exposed to the legacy engine's text
41+
* components and layout alignment. Unlike {@link #addSystem(SystemECS)} this
42+
* does not enroll the measurement system in the {@code process()} loop — it is
43+
* a service provider, not an ECS system.
44+
*
45+
* @param textMeasurement the measurement service to expose
46+
* @since 1.7.1
47+
*/
48+
public void registerTextMeasurement(TextMeasurementSystem textMeasurement) {
49+
this.textMeasurement = Objects.requireNonNull(textMeasurement, "textMeasurement");
50+
}
51+
52+
/**
53+
* Retrieves the registered text-measurement service, if any.
54+
*
55+
* @return an Optional containing the measurement service, or empty if none has
56+
* been registered
57+
* @since 1.7.1
58+
*/
59+
public Optional<TextMeasurementSystem> textMeasurement() {
60+
return Optional.ofNullable(textMeasurement);
61+
}
62+
2663
/**
2764
* Adds a single system to the registry.
2865
*

src/main/java/com/demcha/compose/engine/core/package-info.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@
1212
* and the layout / pagination / render systems it drives — is dead: it runs only
1313
* under the legacy engine regression tests.</p>
1414
*
15-
* <p>One vestigial holdover keeps {@code SystemECS} and {@code EntityManager}
16-
* referenced from live code: the canonical
17-
* {@code engine.measurement.TextMeasurementSystem} still
18-
* {@code extends SystemECS} with a no-op {@code process(EntityManager)}.
19-
* Decoupling that base — so {@code engine.core} becomes genuinely unreferenced by
20-
* the canonical pipeline — is a tracked follow-up.</p>
21-
*
2215
* <p>The genuinely shared engine packages are elsewhere and are <em>not</em>
2316
* deprecated: {@code engine.components} (value types), {@code engine.measurement}
2417
* (text-measurement contracts), {@code engine.font}, and
@@ -28,9 +21,8 @@
2821
* @deprecated Legacy ECS engine, superseded by the canonical
2922
* {@code com.demcha.compose.document.layout} pipeline. No public entry point
3023
* runs it and it is not on the canonical hot path; it is retained only for the
31-
* legacy engine regression tests (aside from the vestigial {@code SystemECS}
32-
* base of {@code TextMeasurementSystem}, a tracked cleanup) — a candidate for
33-
* removal. Do not extend it or spend optimization effort here.
24+
* legacy engine regression tests — a candidate for removal. Do not extend it or
25+
* spend optimization effort here.
3426
*/
3527
@Deprecated
3628
package com.demcha.compose.engine.core;

0 commit comments

Comments
 (0)