Skip to content

Commit 6bb3f04

Browse files
committed
Merge remote-tracking branch 'origin/develop' into chore/sweep-aftermath
2 parents 695964b + 5aa754a commit 6bb3f04

15 files changed

Lines changed: 343 additions & 249 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,28 @@ Entries land here as they merge.
104104
Consumers who relied on the helper can copy the former ~100-line class into
105105
their own codebase or load configs directly with Jackson
106106
(`new ObjectMapper(new YAMLFactory()).readValue(...)`).
107-
- **PDF debug node labels** (`@since 1.8.0`). The debug overlay grew a second
108-
layer: `PdfDebugOptions` (guides + node labels + label-text mode) configures
109-
the canonical PDF backend via `GraphCompose.document(...).debug(...)`,
110-
`DocumentSession.debug(...)`, or `PdfFixedLayoutBackend.builder().debug(...)`.
111-
With `nodeLabels()` enabled, every rendered node prints its stable semantic
112-
path — the same path `layoutSnapshot()` reports — once per node and page at
113-
the node's top-left corner (5pt Helvetica on a pale halo), so a misplaced
114-
block on the sheet reads straight back to the builder call that authored it.
115-
`LabelText.NAME` (default) prints the compact own segment
116-
(`PriceSummaryTitle[0]`); `FULL_PATH` prints the whole ancestry. The overlay
117-
uses the base-14 Helvetica font (non-WinAnsi name characters degrade to
118-
`?`), draws strictly on top of content, and never touches measurement or
119-
pagination. `guideLines(boolean)` everywhere became sugar over the new
120-
options — node-label settings survive the toggle — and disabled debug
121-
output stays byte-identical.
107+
- **Debug node labels** (`@since 1.8.0`). The debug overlay grew a second
108+
layer: backend-neutral `DocumentDebugOptions` (guides + node labels +
109+
label-text mode, in `document.output` next to the other neutral output
110+
options) configures fixed-layout rendering via
111+
`GraphCompose.document(...).debug(...)`, `DocumentSession.debug(...)`, or
112+
`PdfFixedLayoutBackend.builder().debug(...)`. With `nodeLabels()` enabled,
113+
every rendered node prints its stable semantic path — the same path
114+
`layoutSnapshot()` reports — once per node and page, as a small corner
115+
badge straddling the top edge of the node's bounds (right-aligned 5pt
116+
Helvetica on a pale halo), so a misplaced block on the sheet reads straight
117+
back to the builder call that authored it. Labels paint as a single
118+
deterministic post-pass after all content, so badges always sit on top —
119+
a container's children or a higher layer can never overdraw the label that
120+
annotates them. `LabelText.NAME` (default) prints the compact own segment
121+
(`PriceSummaryTitle[0]`); `FULL_PATH` prints the whole ancestry. Label text
122+
degrades through the shared WinAnsi fallback (accents like `é` survive,
123+
anything outside WinAnsi becomes `?` with a `glyph.missing` log). The
124+
overlay draws strictly on top of content and never touches measurement or
125+
pagination. `guideLines(boolean)` everywhere became sugar over the options
126+
object with uniform last-write-wins semantics on all three surfaces —
127+
node-label settings survive the toggle, `debug(none())` reliably disables
128+
everything — and disabled debug output stays byte-identical.
122129

123130
### Bug fixes
124131

-2 Bytes
Binary file not shown.

examples/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ are with the canonical DSL, then jump to its detailed section below.
9595
| [HTTP streaming](#http-streaming) | `writePdf(OutputStream)` for Servlet / S3 / GCS — caller's stream is not closed | [PDF](../assets/readme/examples/invoice-http-stream.pdf) · [Source](src/main/java/com/demcha/examples/features/streaming/HttpStreamingExample.java) |
9696
| [Word export (DOCX)](#word-export-docx) | `DocxSemanticBackend` — the same session renders a fixed-layout PDF and an editable Word file; paragraphs / lists / tables / images map 1:1, charts fall back to their data table | [PDF](../assets/readme/examples/word-export-companion.pdf) · [DOCX](../assets/readme/examples/word-export-companion.docx) · [Source](src/main/java/com/demcha/examples/features/docx/WordExportExample.java) |
9797
| [Layout snapshot regression](#layout-snapshot-regression) | Deterministic `layoutSnapshot()` workflow with baseline + drift report — production regression-testing pattern | [PDF](../assets/readme/examples/invoice-snapshot-regression.pdf) · [Source](src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java) |
98-
| [Debug overlay](#debug-overlay) | `PdfDebugOptions` — guide lines + semantic node-path labels on the sheet; trace any misplaced block back to the builder call that authored it | [PDF](../assets/readme/examples/debug-overlay.pdf) · [Source](src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java) |
98+
| [Debug overlay](#debug-overlay) | `DocumentDebugOptions` — guide lines + semantic node-path labels on the sheet; trace any misplaced block back to the builder call that authored it | [PDF](../assets/readme/examples/debug-overlay.pdf) · [Source](src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java) |
9999
| [Business report cover](#business-report-cover) | Single-page Q1 investor brief — hero image, KPI cards, bar chart, metrics table | [PDF](../assets/readme/examples/business-report.pdf) · [Source](src/main/java/com/demcha/examples/flagships/BusinessReportExample.java) |
100100
| [Master showcase](#master-showcase) | Kitchen-sink "Q2 sample report" combining the canonical surface end-to-end | [PDF](../assets/readme/examples/master-showcase.pdf) · [Source](src/main/java/com/demcha/examples/flagships/MasterShowcaseExample.java) |
101101
| Feature catalog | Browsable reference PDF: every shipped capability as a block — outline-clickable heading, the exact API call, the rendered result right under it | [PDF](../assets/readme/examples/feature-catalog.pdf) · [Source](src/main/java/com/demcha/examples/flagships/FeatureCatalogExample.java) |
@@ -653,7 +653,7 @@ label, then search that name in your builder code.
653653

654654
```java
655655
try (DocumentSession document = GraphCompose.document(outputFile)
656-
.debug(PdfDebugOptions.guidesAndNodeLabels())
656+
.debug(DocumentDebugOptions.guidesAndNodeLabels())
657657
.create()) {
658658
document.pageFlow(page -> page
659659
.module("InvoiceHeader", m -> m.paragraph("ACME Corp — Invoice 2026-104")));
@@ -662,7 +662,7 @@ try (DocumentSession document = GraphCompose.document(outputFile)
662662
```
663663

664664
Labels default to the compact own segment (`InvoiceHeaderTitle[0]`);
665-
`PdfDebugOptions.LabelText.FULL_PATH` prints the whole ancestor chain
665+
`DocumentDebugOptions.LabelText.FULL_PATH` prints the whole ancestor chain
666666
instead. Debug overlays draw strictly on top of content and never
667667
affect measurement or pagination — disabling them returns the exact
668668
production bytes.

examples/src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.demcha.compose.GraphCompose;
44
import com.demcha.compose.document.api.DocumentPageSize;
55
import com.demcha.compose.document.api.DocumentSession;
6-
import com.demcha.compose.document.backend.fixed.pdf.options.PdfDebugOptions;
6+
import com.demcha.compose.document.output.DocumentDebugOptions;
77
import com.demcha.compose.document.style.DocumentInsets;
88
import com.demcha.examples.support.ExampleOutputPaths;
99

@@ -18,16 +18,17 @@
1818
*
1919
* <pre>{@code
2020
* GraphCompose.document(out)
21-
* .debug(PdfDebugOptions.guidesAndNodeLabels())
21+
* .debug(DocumentDebugOptions.guidesAndNodeLabels())
2222
* .create()
2323
* }</pre>
2424
*
2525
* <p>Every rendered node then prints its stable semantic path — the same
2626
* path {@code DocumentSession.layoutSnapshot()} reports — once per node
27-
* and page at the node's top-left corner, next to the familiar fragment
28-
* boxes and dashed margin/padding guides. Spot a misplaced block on
27+
* and page as a corner badge straddling the top edge of the node's bounds
28+
* (right-aligned), next to the familiar fragment boxes and dashed
29+
* margin/padding guides. Spot a misplaced block on
2930
* paper, read its label, and grep that name straight in your builder
30-
* code. {@code PdfDebugOptions.LabelText.FULL_PATH} switches the labels
31+
* code. {@code DocumentDebugOptions.LabelText.FULL_PATH} switches the labels
3132
* from the compact own segment to the whole ancestor chain.</p>
3233
*
3334
* <p>Debug overlays draw strictly on top of regular content and never
@@ -53,7 +54,7 @@ public static Path generate() throws Exception {
5354
try (DocumentSession document = GraphCompose.document(pdfFile)
5455
.pageSize(DocumentPageSize.A4)
5556
.margin(DocumentInsets.of(28))
56-
.debug(PdfDebugOptions.guidesAndNodeLabels())
57+
.debug(DocumentDebugOptions.guidesAndNodeLabels())
5758
.create()) {
5859
document.pageFlow(page -> page
5960
.module("HowToReadThisSheet", module -> module

examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ record Entry(String title, String description, List<String> tags, String codeUrl
104104
feature("streaming", "invoice-http-stream", "HTTP Streaming", "Stream PDF directly to a Servlet response with no buffering.", "streaming", "http");
105105
feature("snapshots", "invoice-snapshot-regression", "Layout Snapshots", "How LayoutSnapshotAssertions captures the resolved layout graph for regression testing.", "snapshots", "testing");
106106
feature("docx", "word-export-companion", "Word Export (DOCX)", "DocxSemanticBackend — the same document as a fixed-layout PDF and an editable Word file; charts fall back to their data table.", "docx", "word", "export");
107-
feature("debug", "debug-overlay", "Debug Overlay", "PdfDebugOptions — guide lines plus semantic node-path labels on the rendered sheet; trace any misplaced block back to the builder call that authored it.", "debug", "labels", "v1.8");
107+
feature("debug", "debug-overlay", "Debug Overlay", "DocumentDebugOptions — guide lines plus semantic node-path labels on the rendered sheet; trace any misplaced block back to the builder call that authored it.", "debug", "labels", "v1.8");
108108

109109
// ===== Flagships =====
110110
flagship("master-showcase", "Master Showcase", "Kitchen-sink demo combining every primitive into a single document — the full GraphCompose surface.", "showcase");

src/main/java/com/demcha/compose/GraphCompose.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import com.demcha.compose.font.DefaultFonts;
77
import com.demcha.compose.document.api.DocumentPageSize;
88
import com.demcha.compose.document.api.DocumentSession;
9-
import com.demcha.compose.document.backend.fixed.pdf.options.PdfDebugOptions;
9+
import com.demcha.compose.document.output.DocumentDebugOptions;
1010
import com.demcha.compose.document.style.DocumentInsets;
1111

1212
import java.nio.file.Path;
@@ -140,8 +140,7 @@ public static final class DocumentBuilder {
140140
private DocumentPageSize pageSize = DocumentPageSize.A4;
141141
private DocumentInsets margin = DocumentInsets.zero();
142142
private boolean markdown = true;
143-
private boolean guideLines;
144-
private PdfDebugOptions debug;
143+
private DocumentDebugOptions debug = DocumentDebugOptions.none();
145144
private com.demcha.compose.document.style.DocumentColor pageBackground;
146145
private java.util.List<com.demcha.compose.document.api.PageBackgroundFill> pageBackgrounds;
147146
private final List<FontFamilyDefinition> customFontFamilies = new ArrayList<>();
@@ -220,29 +219,35 @@ public DocumentBuilder markdown(boolean enabled) {
220219
* {@link DocumentSession#toPdfBytes()}. It does not alter semantic layout
221220
* geometry or layout snapshots.</p>
222221
*
222+
* <p>Shorthand for toggling only the guide overlay on the current
223+
* {@link #debug(DocumentDebugOptions) debug} configuration — node-label
224+
* settings are preserved, and the call order with {@code debug(...)}
225+
* follows last-write-wins, exactly like the equivalent switches on
226+
* {@code DocumentSession} and the PDF backend builder.</p>
227+
*
223228
* @param enabled {@code true} to draw debug guide-line overlays
224229
* @return this builder
225230
*/
226231
public DocumentBuilder guideLines(boolean enabled) {
227-
this.guideLines = enabled;
232+
this.debug = this.debug.withGuides(enabled);
228233
return this;
229234
}
230235

231236
/**
232-
* Configures PDF debug overlays (guide lines and semantic node labels)
237+
* Configures debug overlays (guide lines and semantic node labels)
233238
* for the session's convenience PDF output.
234239
*
235-
* <p>Combines with {@link #guideLines(boolean)}: when both switches are
236-
* used, the guide overlay is enabled if either of them requests it.
237-
* Like guide lines, debug overlays draw on top of regular content and
238-
* never alter semantic layout geometry or layout snapshots.</p>
240+
* <p>Replaces the whole debug configuration; {@code null} resets to
241+
* {@link DocumentDebugOptions#none()}. Debug overlays draw on top of
242+
* regular content and never alter semantic layout geometry or layout
243+
* snapshots.</p>
239244
*
240245
* @param options debug overlay options, or {@code null} for none
241246
* @return this builder
242247
* @since 1.8.0
243248
*/
244-
public DocumentBuilder debug(PdfDebugOptions options) {
245-
this.debug = options;
249+
public DocumentBuilder debug(DocumentDebugOptions options) {
250+
this.debug = options == null ? DocumentDebugOptions.none() : options;
246251
return this;
247252
}
248253

@@ -410,10 +415,8 @@ public DocumentSession create() {
410415
margin,
411416
List.copyOf(customFontFamilies),
412417
markdown,
413-
guideLines);
414-
if (debug != null) {
415-
session.debug(debug.withGuides(debug.showGuides() || guideLines));
416-
}
418+
debug.showGuides());
419+
session.debug(debug);
417420
if (pageBackgrounds != null) {
418421
// Explicit pageBackgrounds() call wins over a prior
419422
// pageBackground(color). Empty list = clear; see builder Javadoc.

src/main/java/com/demcha/compose/document/api/DocumentChromeOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ DocumentOutputOptions snapshot() {
102102
* never {@code null}
103103
* @return ready-to-use PDF backend
104104
*/
105-
PdfFixedLayoutBackend toConveniencePdfBackend(PdfDebugOptions debug) {
105+
PdfFixedLayoutBackend toConveniencePdfBackend(DocumentDebugOptions debug) {
106106
if (!debug.enabled() && isEmpty()) {
107107
return new PdfFixedLayoutBackend();
108108
}

src/main/java/com/demcha/compose/document/api/DocumentSession.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.demcha.compose.document.backend.fixed.FixedLayoutBackend;
55
import com.demcha.compose.document.backend.fixed.pdf.PdfFixedLayoutBackend;
66
import com.demcha.compose.document.backend.fixed.pdf.PdfMeasurementResources;
7-
import com.demcha.compose.document.backend.fixed.pdf.options.PdfDebugOptions;
87
import com.demcha.compose.document.backend.fixed.pdf.options.PdfHeaderFooterOptions;
98
import com.demcha.compose.document.backend.fixed.pdf.options.PdfMetadataOptions;
109
import com.demcha.compose.document.backend.fixed.pdf.options.PdfProtectionOptions;
@@ -74,7 +73,7 @@ public final class DocumentSession implements AutoCloseable {
7473
private DocumentInsets margin;
7574
private LayoutCanvas canvas;
7675
private boolean markdown;
77-
private PdfDebugOptions debug = PdfDebugOptions.none();
76+
private DocumentDebugOptions debug = DocumentDebugOptions.none();
7877
private List<PageBackgroundFill> pageBackgrounds = List.of();
7978
private PdfMeasurementResources measurementResources;
8079
private boolean closed;
@@ -101,7 +100,7 @@ public DocumentSession(Path defaultOutputFile,
101100
this.margin = margin == null ? DocumentInsets.zero() : margin;
102101
this.canvas = LayoutCanvas.from(pageSize.width(), pageSize.height(), toEngineMargin(this.margin));
103102
this.markdown = markdown;
104-
this.debug = PdfDebugOptions.none().withGuides(guideLines);
103+
this.debug = DocumentDebugOptions.none().withGuides(guideLines);
105104
this.registry = BuiltInNodeDefinitions.registerDefaults(new InvalidatingNodeRegistry());
106105
this.compiler = new LayoutCompiler(registry);
107106
this.customFontFamilies.addAll(List.copyOf(customFontFamilies));
@@ -310,7 +309,7 @@ public DocumentSession markdown(boolean enabled) {
310309
* so existing layout cache entries remain valid.</p>
311310
*
312311
* <p>Shorthand for toggling only the guide overlay on the current
313-
* {@link #debug(PdfDebugOptions) debug} configuration; node-label
312+
* {@link #debug(DocumentDebugOptions) debug} configuration; node-label
314313
* settings are preserved.</p>
315314
*
316315
* @param enabled {@code true} to draw debug guide-line overlays
@@ -340,9 +339,9 @@ public DocumentSession guideLines(boolean enabled) {
340339
* @return this session
341340
* @since 1.8.0
342341
*/
343-
public DocumentSession debug(PdfDebugOptions options) {
342+
public DocumentSession debug(DocumentDebugOptions options) {
344343
ensureOpen();
345-
this.debug = options == null ? PdfDebugOptions.none() : options;
344+
this.debug = options == null ? DocumentDebugOptions.none() : options;
346345
return this;
347346
}
348347

0 commit comments

Comments
 (0)