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
14 changes: 7 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ not need any of them.
`com.demcha.compose.document.backend.fixed.pdf`, not in
`components/renderable`.
- Builders and layout code get text width and line metrics from
`TextMeasurementSystem`, not from
`LayoutSystem -> RenderingSystem`, `PdfFont`, or PDFBox objects.
`TextMeasurementSystem`, not from the active renderer, `PdfFont`,
or PDFBox objects.
- Keep `src/main/java/com/demcha/compose/engine/components/*` free of
`org.apache.pdfbox` and `com.demcha.compose.engine.render.pdf`
imports.
Expand All @@ -207,8 +207,8 @@ Keep the entity core thin:
page-shift propagation live in `ParentContainerUpdater`.
- `Entity.bounding*` and `Entity.updateParentContainer*` are
deprecated compatibility wrappers; do not copy them into new code.
- Render-order optimizations live in rendering helpers such as
`EntityRenderOrder`, not in `Entity`.
- Render-order optimizations live in the render layer (the PDF
fragment handlers), not in `Entity`.

### Guard rails

Expand Down Expand Up @@ -311,11 +311,11 @@ marker, a new layout system, a new render-pass session):
layout component plus a backend-neutral renderable marker plus a
backend-owned render handler.
- Marker rule of thumb:
- add `Expendable` only to parent-like boxes that should grow
because of child content
- add the container-growth marker only to parent-like boxes that
should grow because of child content
- add `Breakable` only to entities whose own content may continue
across pages
- do not treat `Expendable` as a pagination flag
- do not treat the container-growth marker as a pagination flag

For text-heavy primitives, also read:

Expand Down
10 changes: 3 additions & 7 deletions docs/architecture/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ These rules apply to engine and backend contributors. Application
code should not need any of them.

- engine builders and layout helpers consume an engine-level
`TextMeasurementSystem` instead of reaching through `LayoutSystem`
into the active renderer
`TextMeasurementSystem` instead of reaching through the active
renderer
- render marker components identify *what* needs to be rendered;
*how* it is drawn lives in renderer-owned handler packages such as
the `PdfFragmentRenderHandler` implementations under
Expand All @@ -233,12 +233,8 @@ code should not need any of them.
package imports
- the PDF entity path dispatches through registered render handlers;
there is no backend-specific render fallback path
- `EntityRenderOrder` is the shared render-order helper for resolved
entities. It precomputes lightweight sort entries per layer before
sorting so render ordering stays deterministic without repeated
component lookups inside the comparator hot path

Fixed leaf primitives (`Rectangle`, `Circle`, `Image`, `Line`)
Fixed leaf primitives (such as `TextComponent` and `BlockText`)
follow the same engine contract: they materialize as regular
entities with render/content/layout components, rely on normal
`ContentSize` / `Padding` / `Margin` / `Placement`, and do not
Expand Down
6 changes: 2 additions & 4 deletions docs/architecture/package-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,9 @@ per-package artifact.
| --- | --- | --- |
| `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. |
| `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. |
| `com.demcha.compose.engine.layout` | Low-level entity layout systems. | Preserve deterministic traversal and container sizing semantics. |
| `com.demcha.compose.engine.layout.container` | Container alignment, expansion, and module width helpers. | Helpers should operate on existing ECS state and avoid renderer dependencies. |
| `com.demcha.compose.engine.pagination` | Page-breaking helpers and pagination fallback systems. | Maintain child-first ordering and page-shift propagation rules. |
| `com.demcha.compose.engine.pagination` | Pagination markers and helpers (`Breakable`, `ParentContainerUpdater`, `Offset`). | Maintain child-first ordering and page-shift propagation rules. |
| `com.demcha.compose.engine.measurement` | Text measurement contracts and font-backed implementations. | Builders/layout helpers depend on this seam instead of reaching into renderers. |
| `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. |
| `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. |
| `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`. |
| `com.demcha.compose.engine.render.word` | Experimental Word backend internals. | Treat as research until a supported public surface is designed. |
| `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. |
Expand Down
16 changes: 8 additions & 8 deletions docs/architecture/pagination-ordering.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ If the parent container is processed before the child shift happens, the parent
will still be based on the old value. In practice, this looks like a container guide or span box
that does not extend to cover the final child position.

## The Circle vs Image confusion
## The leaf-type ordering confusion

This issue can be easy to misdiagnose because it does not belong to the leaf renderable itself.

`Circle` and `ImageComponent` are both fixed leaf renderables:
Fixed leaf renderables such as `TextComponent` are all the same shape here:

- they render a single box
- they are not `Breakable`
- when they do not fit, they are moved as a whole

So if a circle container fails to expand while an image container appears correct, the first thing
So if one leaf container fails to expand while another appears correct, the first thing
to check is not the render implementation. The first thing to check is pagination order.

In one scenario, the image child may happen to be processed before its parent container, so the
In one scenario, one child may happen to be processed before its parent container, so the
container placement is computed with the final child-driven size and the result looks correct.
In another scenario, a circle child may be processed after its parent container, which exposes the
In another scenario, a child may be processed after its parent container, which exposes the
bug immediately.

The difference is therefore often accidental ordering, not a semantic difference between image and
circle rendering.
The difference is therefore often accidental ordering, not a semantic difference between the
leaf types.

## Correct rule

Expand Down Expand Up @@ -77,7 +77,7 @@ If you see one of these behaviors, check pagination ordering first:

When debugging a pagination tree:

1. log the `PageBreaker` processing order
1. log the page-breaker processing order
2. compare parent and child `ComputedPosition.y`
3. check whether a child shift updates parent `ContentSize`
4. verify that the parent `Placement` is created after that update, not before it
Expand Down
40 changes: 16 additions & 24 deletions docs/contributing/implementation-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Use these ownership rules when adding or refactoring engine code:

- put geometry reads in `EntityBounds`
- put parent container size propagation and page-shift updates in `ParentContainerUpdater`
- keep render-order policy in rendering helpers such as `EntityRenderOrder`
- keep render-order policy in the render layer (the `engine.render` contracts and the PDF fragment handlers)
- treat `Entity.bounding*` and `Entity.updateParentContainer*` as deprecated compatibility wrappers

Rule of thumb:
Expand All @@ -46,8 +46,7 @@ If the object should render something visible, the entity needs a renderable mar
Examples:

- [TextComponent.java](../../src/main/java/com/demcha/compose/engine/components/renderable/TextComponent.java)
- [Rectangle.java](../../src/main/java/com/demcha/compose/engine/components/renderable/Rectangle.java)
- [ImageComponent.java](../../src/main/java/com/demcha/compose/engine/components/renderable/ImageComponent.java)
- [BlockText.java](../../src/main/java/com/demcha/compose/engine/components/renderable/BlockText.java)

Those renderable components are render markers. Prefer keeping them backend-neutral and let renderer-owned handlers perform format-specific drawing.

Expand All @@ -56,32 +55,30 @@ Those renderable components are render markers. Prefer keeping them backend-neut
There are three different ideas in the engine that are easy to mix up:

- render marker: tells the active renderer how to draw the entity
- `Expendable`: tells the container expansion phase that the parent box may grow to fit children
- container-growth marker: tells the container expansion phase that the parent box may grow to fit children
- `Breakable`: tells the page breaker that the entity's own content may continue across pages

Use them for different reasons:

- add a render marker when the object is visible
- add `Expendable` only when the entity is a true parent-like box that should resize because of child content
- add the container-growth marker only when the entity is a true parent-like box that should resize because of child content
- add `Breakable` only when the entity itself can be split or continued during pagination

Examples:

- `Container` is both `Expendable` and `Breakable` because it owns children and may span pages
- a container marker is both parent-growable and `Breakable` because it owns children and may span pages
- `BlockText` is `Breakable` because its content can flow across pages
- `ImageComponent` is neither `Expendable` nor `Breakable`; it is a fixed leaf renderable
- `Circle` is a fixed leaf renderable like `ImageComponent`; it renders, but it should not be marked `Expendable`
- `Line` is also a fixed leaf renderable; it draws inside its resolved box and should stay non-breakable unless the engine gets a true multi-page line contract later
- `TextComponent` is a fixed leaf renderable; it renders a single resolved box and stays non-breakable

Important:

- `Expendable` is not a pagination flag
- the container-growth marker is not a pagination flag
- `Breakable` is not a child-sizing flag
- if a long leaf object is not `Breakable`, the engine treats it as a single block and moves it to the next page when needed

Leaf parity rule:

- if two objects are conceptually fixed leaf renderables, such as `ImageComponent`, `Circle`, and `Line`, they should use the same layout contract
- if two objects are conceptually fixed leaf renderables, they should use the same layout contract
- that usually means the same kind of `ContentSize`, the same padding-aware inner draw area, and the same non-breakable pagination behavior
- if one of them behaves differently in containers or multi-page flow, first check the render/layout contract before changing pagination rules

Expand All @@ -92,7 +89,7 @@ Attach the components that describe what the object is and how it should look.
Examples:

- `Text`, `TextStyle`
- `FillColor`, `Stroke`, `CornerRadius`
- `Stroke`
- `ImageData`

The renderer reads those components later during the render pass.
Expand All @@ -116,7 +113,6 @@ The layout engine expects the usual layout metadata to be present when needed:
- `Anchor`
- `Margin`
- `Padding`
- `Align` for containers
- `ParentComponent` for child entities

You normally do not add `Placement` yourself. The layout system calculates placement later.
Expand All @@ -138,7 +134,7 @@ Steps:

Leaf rule of thumb:

- most leaf renderables should not implement `Expendable`
- most leaf renderables should not grow to fit children
- only implement `Breakable` if the leaf's content can really continue across pages

### Case 2: add a new container-like object
Expand All @@ -155,7 +151,7 @@ Steps:

Container rule of thumb:

- containers that must resize around children usually need `Expendable`
- containers that must resize around children usually need the container-growth marker
- containers whose content may continue on another page usually need `Breakable`
- some containers need both markers
- if the container models semantic section behavior, decide whether width should be inherited from the parent before letting child layout run
Expand Down Expand Up @@ -184,7 +180,6 @@ Why the table uses this contract:

Relevant files:

- [TableRow.java](../../src/main/java/com/demcha/compose/engine/components/renderable/TableRow.java)
- [TableResolvedCell.java](../../src/main/java/com/demcha/compose/engine/components/content/table/TableResolvedCell.java)

Rule of thumb:
Expand Down Expand Up @@ -250,7 +245,7 @@ Preferred extension pattern for new backends:
> `com.demcha.compose.document.backend.fixed.pdf`: `PdfFixedLayoutBackend`
> dispatches each layout fragment to a `PdfFragmentRenderHandler` implementation
> under `document.backend.fixed.pdf.handlers`. The backend-neutral `engine.render`
> *contracts* (`Render`, `RenderPassSession`, `RenderStream`, `EntityRenderOrder`)
> *contracts* (`Render`, `RenderPassSession`, `RenderStream`)
> remain the shared render seam — extend PDF drawing by adding or updating a
> fragment handler, not by touching those contracts.

Expand All @@ -261,16 +256,15 @@ Important files:
- [RenderStream.java](../../src/main/java/com/demcha/compose/engine/render/RenderStream.java)
- [PdfFixedLayoutBackend.java](../../render-pdf/src/main/java/com/demcha/compose/document/backend/fixed/pdf/PdfFixedLayoutBackend.java)
- [PdfFragmentRenderHandler.java](../../render-pdf/src/main/java/com/demcha/compose/document/backend/fixed/pdf/PdfFragmentRenderHandler.java)
- [EntityRenderOrder.java](../../src/main/java/com/demcha/compose/engine/render/EntityRenderOrder.java)

Migration rule for new engine components:

- implement backend-neutral `Render`, not backend-specific render interfaces
- move PDF drawing into a `PdfFragmentRenderHandler` under `...document.backend.fixed.pdf.handlers`
- use `TextMeasurementSystem` for text width and line metrics instead of reaching through `LayoutSystem`
- use `TextMeasurementSystem` for text width and line metrics instead of reaching through the active renderer
- place PDF-only helper objects alongside the backend in `...document.backend.fixed.pdf`
- keep page-surface lifetime in a backend-specific `RenderPassSession`, not in engine builders or render markers
- keep resolved draw ordering in renderer-owned or renderer-neutral rendering helpers such as `EntityRenderOrder`
- keep resolved draw ordering in the render layer (the PDF fragment handlers), not in pagination utilities
- register a render handler for every engine render marker because the PDF entity path no longer supports a backend-specific render fallback

### Render-pass session rules
Expand Down Expand Up @@ -315,9 +309,7 @@ The layout side uses entity components, not builder classes directly.
Important files:

- [LayoutTraversalContext.java](../../src/main/java/com/demcha/compose/engine/core/LayoutTraversalContext.java)
- [LayoutSystem.java](../../src/main/java/com/demcha/compose/engine/layout/LayoutSystem.java)
- [ComputedPosition.java](../../src/main/java/com/demcha/compose/engine/components/layout/coordinator/ComputedPosition.java)
- [PageBreaker.java](../../src/main/java/com/demcha/compose/engine/pagination/PageBreaker.java)
- [EntityBounds.java](../../src/main/java/com/demcha/compose/engine/components/geometry/EntityBounds.java)
- [ParentContainerUpdater.java](../../src/main/java/com/demcha/compose/engine/pagination/ParentContainerUpdater.java)

Expand All @@ -335,15 +327,15 @@ Use the helpers directly when that intent is what you need:
- read bounds and edges through `EntityBounds` instead of adding more bound helpers to `Entity`
- update parent container size or shifted positions through `ParentContainerUpdater` instead of growing the `Entity` API further

See [pagination-ordering.md](../architecture/pagination-ordering.md) for a focused explanation of this rule, including why a `Circle` case can fail while an `Image` case appears to work.
See [pagination-ordering.md](../architecture/pagination-ordering.md) for a focused explanation of this rule, including why one leaf type can fail while another appears to work.

If those components are missing or inconsistent, the renderer cannot save you later.

## Practical checklist for a new object

- choose the correct builder base class
- add the render marker in `initialize()` if the object is drawable
- add `Expendable` only for parent-like boxes that should grow because of children
- add the container-growth marker only for parent-like boxes that should grow because of children
- add `Breakable` only for entities whose own content can span pages
- attach content/style components through fluent methods
- provide `ContentSize` directly or calculate it in `build()`
Expand Down
2 changes: 1 addition & 1 deletion docs/operations/layout-snapshot-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Use them when you want to know that:

That means the coordinates in the snapshot are:

- after `LayoutSystem`
- after layout resolution
- after page-breaking decisions have been applied
- before any PDFBox drawing happens

Expand Down
7 changes: 0 additions & 7 deletions qa/src/test/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,6 @@
</encoder>
</appender>

<!-- Console for LayoutSystem (DEBUG) -->
<appender name="CONSOLE_LAYOUT_SYSTEM" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>
</encoder>
</appender>

<!-- Console for global WARN+ERROR -->
<appender name="CONSOLE_WARN_ERROR" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
Expand Down

This file was deleted.

This file was deleted.

Loading