diff --git a/CHANGELOG.md b/CHANGELOG.md index 729ab7bb..21ca2621 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,21 +104,28 @@ Entries land here as they merge. Consumers who relied on the helper can copy the former ~100-line class into their own codebase or load configs directly with Jackson (`new ObjectMapper(new YAMLFactory()).readValue(...)`). -- **PDF debug node labels** (`@since 1.8.0`). The debug overlay grew a second - layer: `PdfDebugOptions` (guides + node labels + label-text mode) configures - the canonical PDF backend via `GraphCompose.document(...).debug(...)`, - `DocumentSession.debug(...)`, or `PdfFixedLayoutBackend.builder().debug(...)`. - With `nodeLabels()` enabled, every rendered node prints its stable semantic - path — the same path `layoutSnapshot()` reports — once per node and page at - the node's top-left corner (5pt Helvetica on a pale halo), so a misplaced - block on the sheet reads straight back to the builder call that authored it. - `LabelText.NAME` (default) prints the compact own segment - (`PriceSummaryTitle[0]`); `FULL_PATH` prints the whole ancestry. The overlay - uses the base-14 Helvetica font (non-WinAnsi name characters degrade to - `?`), draws strictly on top of content, and never touches measurement or - pagination. `guideLines(boolean)` everywhere became sugar over the new - options — node-label settings survive the toggle — and disabled debug - output stays byte-identical. +- **Debug node labels** (`@since 1.8.0`). The debug overlay grew a second + layer: backend-neutral `DocumentDebugOptions` (guides + node labels + + label-text mode, in `document.output` next to the other neutral output + options) configures fixed-layout rendering via + `GraphCompose.document(...).debug(...)`, `DocumentSession.debug(...)`, or + `PdfFixedLayoutBackend.builder().debug(...)`. With `nodeLabels()` enabled, + every rendered node prints its stable semantic path — the same path + `layoutSnapshot()` reports — once per node and page, as a small corner + badge straddling the top edge of the node's bounds (right-aligned 5pt + Helvetica on a pale halo), so a misplaced block on the sheet reads straight + back to the builder call that authored it. Labels paint as a single + deterministic post-pass after all content, so badges always sit on top — + a container's children or a higher layer can never overdraw the label that + annotates them. `LabelText.NAME` (default) prints the compact own segment + (`PriceSummaryTitle[0]`); `FULL_PATH` prints the whole ancestry. Label text + degrades through the shared WinAnsi fallback (accents like `é` survive, + anything outside WinAnsi becomes `?` with a `glyph.missing` log). The + overlay draws strictly on top of content and never touches measurement or + pagination. `guideLines(boolean)` everywhere became sugar over the options + object with uniform last-write-wins semantics on all three surfaces — + node-label settings survive the toggle, `debug(none())` reliably disables + everything — and disabled debug output stays byte-identical. ### Bug fixes diff --git a/assets/readme/examples/debug-overlay.pdf b/assets/readme/examples/debug-overlay.pdf index 9872ee1c..70ed58f3 100644 Binary files a/assets/readme/examples/debug-overlay.pdf and b/assets/readme/examples/debug-overlay.pdf differ diff --git a/examples/README.md b/examples/README.md index 25ecf362..f7b7893f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -95,7 +95,7 @@ are with the canonical DSL, then jump to its detailed section below. | [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) | | [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) | | [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) | -| [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) | +| [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) | | [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) | | [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) | | 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. ```java try (DocumentSession document = GraphCompose.document(outputFile) - .debug(PdfDebugOptions.guidesAndNodeLabels()) + .debug(DocumentDebugOptions.guidesAndNodeLabels()) .create()) { document.pageFlow(page -> page .module("InvoiceHeader", m -> m.paragraph("ACME Corp — Invoice 2026-104"))); @@ -662,7 +662,7 @@ try (DocumentSession document = GraphCompose.document(outputFile) ``` Labels default to the compact own segment (`InvoiceHeaderTitle[0]`); -`PdfDebugOptions.LabelText.FULL_PATH` prints the whole ancestor chain +`DocumentDebugOptions.LabelText.FULL_PATH` prints the whole ancestor chain instead. Debug overlays draw strictly on top of content and never affect measurement or pagination — disabling them returns the exact production bytes. diff --git a/examples/src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java b/examples/src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java index 18eb6d46..e057a03f 100644 --- a/examples/src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java +++ b/examples/src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java @@ -3,7 +3,7 @@ import com.demcha.compose.GraphCompose; import com.demcha.compose.document.api.DocumentPageSize; import com.demcha.compose.document.api.DocumentSession; -import com.demcha.compose.document.backend.fixed.pdf.options.PdfDebugOptions; +import com.demcha.compose.document.output.DocumentDebugOptions; import com.demcha.compose.document.style.DocumentInsets; import com.demcha.examples.support.ExampleOutputPaths; @@ -18,16 +18,17 @@ * *
{@code
* GraphCompose.document(out)
- * .debug(PdfDebugOptions.guidesAndNodeLabels())
+ * .debug(DocumentDebugOptions.guidesAndNodeLabels())
* .create()
* }
*
* Every rendered node then prints its stable semantic path — the same * path {@code DocumentSession.layoutSnapshot()} reports — once per node - * and page at the node's top-left corner, next to the familiar fragment - * boxes and dashed margin/padding guides. Spot a misplaced block on + * and page as a corner badge straddling the top edge of the node's bounds + * (right-aligned), next to the familiar fragment boxes and dashed + * margin/padding guides. Spot a misplaced block on * paper, read its label, and grep that name straight in your builder - * code. {@code PdfDebugOptions.LabelText.FULL_PATH} switches the labels + * code. {@code DocumentDebugOptions.LabelText.FULL_PATH} switches the labels * from the compact own segment to the whole ancestor chain.
* *Debug overlays draw strictly on top of regular content and never
@@ -53,7 +54,7 @@ public static Path generate() throws Exception {
try (DocumentSession document = GraphCompose.document(pdfFile)
.pageSize(DocumentPageSize.A4)
.margin(DocumentInsets.of(28))
- .debug(PdfDebugOptions.guidesAndNodeLabels())
+ .debug(DocumentDebugOptions.guidesAndNodeLabels())
.create()) {
document.pageFlow(page -> page
.module("HowToReadThisSheet", module -> module
diff --git a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java
index 3f23df31..45f2c60c 100644
--- a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java
+++ b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java
@@ -104,7 +104,7 @@ record Entry(String title, String description, List
Shorthand for toggling only the guide overlay on the current + * {@link #debug(DocumentDebugOptions) debug} configuration — node-label + * settings are preserved, and the call order with {@code debug(...)} + * follows last-write-wins, exactly like the equivalent switches on + * {@code DocumentSession} and the PDF backend builder.
+ * * @param enabled {@code true} to draw debug guide-line overlays * @return this builder */ public DocumentBuilder guideLines(boolean enabled) { - this.guideLines = enabled; + this.debug = this.debug.withGuides(enabled); return this; } /** - * Configures PDF debug overlays (guide lines and semantic node labels) + * Configures debug overlays (guide lines and semantic node labels) * for the session's convenience PDF output. * - *Combines with {@link #guideLines(boolean)}: when both switches are - * used, the guide overlay is enabled if either of them requests it. - * Like guide lines, debug overlays draw on top of regular content and - * never alter semantic layout geometry or layout snapshots.
+ *Replaces the whole debug configuration; {@code null} resets to + * {@link DocumentDebugOptions#none()}. Debug overlays draw on top of + * regular content and never alter semantic layout geometry or layout + * snapshots.
* * @param options debug overlay options, or {@code null} for none * @return this builder * @since 1.8.0 */ - public DocumentBuilder debug(PdfDebugOptions options) { - this.debug = options; + public DocumentBuilder debug(DocumentDebugOptions options) { + this.debug = options == null ? DocumentDebugOptions.none() : options; return this; } @@ -410,10 +415,8 @@ public DocumentSession create() { margin, List.copyOf(customFontFamilies), markdown, - guideLines); - if (debug != null) { - session.debug(debug.withGuides(debug.showGuides() || guideLines)); - } + debug.showGuides()); + session.debug(debug); if (pageBackgrounds != null) { // Explicit pageBackgrounds() call wins over a prior // pageBackground(color). Empty list = clear; see builder Javadoc. diff --git a/src/main/java/com/demcha/compose/document/api/DocumentChromeOptions.java b/src/main/java/com/demcha/compose/document/api/DocumentChromeOptions.java index 24b00198..af483d66 100644 --- a/src/main/java/com/demcha/compose/document/api/DocumentChromeOptions.java +++ b/src/main/java/com/demcha/compose/document/api/DocumentChromeOptions.java @@ -102,7 +102,7 @@ DocumentOutputOptions snapshot() { * never {@code null} * @return ready-to-use PDF backend */ - PdfFixedLayoutBackend toConveniencePdfBackend(PdfDebugOptions debug) { + PdfFixedLayoutBackend toConveniencePdfBackend(DocumentDebugOptions debug) { if (!debug.enabled() && isEmpty()) { return new PdfFixedLayoutBackend(); } diff --git a/src/main/java/com/demcha/compose/document/api/DocumentSession.java b/src/main/java/com/demcha/compose/document/api/DocumentSession.java index ee2d2dae..d166274e 100644 --- a/src/main/java/com/demcha/compose/document/api/DocumentSession.java +++ b/src/main/java/com/demcha/compose/document/api/DocumentSession.java @@ -4,7 +4,6 @@ import com.demcha.compose.document.backend.fixed.FixedLayoutBackend; import com.demcha.compose.document.backend.fixed.pdf.PdfFixedLayoutBackend; import com.demcha.compose.document.backend.fixed.pdf.PdfMeasurementResources; -import com.demcha.compose.document.backend.fixed.pdf.options.PdfDebugOptions; import com.demcha.compose.document.backend.fixed.pdf.options.PdfHeaderFooterOptions; import com.demcha.compose.document.backend.fixed.pdf.options.PdfMetadataOptions; import com.demcha.compose.document.backend.fixed.pdf.options.PdfProtectionOptions; @@ -74,7 +73,7 @@ public final class DocumentSession implements AutoCloseable { private DocumentInsets margin; private LayoutCanvas canvas; private boolean markdown; - private PdfDebugOptions debug = PdfDebugOptions.none(); + private DocumentDebugOptions debug = DocumentDebugOptions.none(); private ListShorthand for toggling only the guide overlay on the current - * {@link #debug(PdfDebugOptions) debug} configuration; node-label + * {@link #debug(DocumentDebugOptions) debug} configuration; node-label * settings are preserved.
* * @param enabled {@code true} to draw debug guide-line overlays @@ -340,9 +339,9 @@ public DocumentSession guideLines(boolean enabled) { * @return this session * @since 1.8.0 */ - public DocumentSession debug(PdfDebugOptions options) { + public DocumentSession debug(DocumentDebugOptions options) { ensureOpen(); - this.debug = options == null ? PdfDebugOptions.none() : options; + this.debug = options == null ? DocumentDebugOptions.none() : options; return this; } diff --git a/src/main/java/com/demcha/compose/document/backend/fixed/pdf/PdfFixedLayoutBackend.java b/src/main/java/com/demcha/compose/document/backend/fixed/pdf/PdfFixedLayoutBackend.java index 36049cc6..6d2d7925 100644 --- a/src/main/java/com/demcha/compose/document/backend/fixed/pdf/PdfFixedLayoutBackend.java +++ b/src/main/java/com/demcha/compose/document/backend/fixed/pdf/PdfFixedLayoutBackend.java @@ -8,6 +8,7 @@ import com.demcha.compose.document.layout.LayoutGraph; import com.demcha.compose.document.layout.PlacedFragment; import com.demcha.compose.document.layout.payloads.*; +import com.demcha.compose.document.output.DocumentDebugOptions; import com.demcha.compose.font.FontLibrary; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; @@ -40,7 +41,7 @@ public final class PdfFixedLayoutBackend implements FixedLayoutBackend