byPage =
- ownerBoundsByPath == null ? null : ownerBoundsByPath.get(fragment.path());
- PdfGuideLinesRenderer.Bounds bounds = byPage == null ? null : byPage.get(fragment.pageIndex());
- return bounds == null ? PdfGuideLinesRenderer.Bounds.from(fragment) : bounds;
- }
-
- /**
- * Replaces every character outside printable ASCII with {@code ?} so the
- * base-14 Helvetica encoder never rejects a label. Semantic names accept
- * any Unicode letter (the DSL normalizer keeps letters and digits), while
- * WinAnsi covers only a Latin subset — a Cyrillic or CJK node name must
- * degrade gracefully instead of failing the debug render.
- */
- private static String sanitize(String text) {
- StringBuilder safe = new StringBuilder(text.length());
- for (int index = 0; index < text.length(); index++) {
- char current = text.charAt(index);
- safe.append(current >= 0x20 && current <= 0x7E ? current : '?');
- }
- return safe.toString();
- }
}
diff --git a/src/main/java/com/demcha/compose/document/backend/fixed/pdf/options/PdfDebugOptions.java b/src/main/java/com/demcha/compose/document/output/DocumentDebugOptions.java
similarity index 61%
rename from src/main/java/com/demcha/compose/document/backend/fixed/pdf/options/PdfDebugOptions.java
rename to src/main/java/com/demcha/compose/document/output/DocumentDebugOptions.java
index 64f5f461..465ec1b2 100644
--- a/src/main/java/com/demcha/compose/document/backend/fixed/pdf/options/PdfDebugOptions.java
+++ b/src/main/java/com/demcha/compose/document/output/DocumentDebugOptions.java
@@ -1,33 +1,38 @@
-package com.demcha.compose.document.backend.fixed.pdf.options;
+package com.demcha.compose.document.output;
import java.util.Objects;
/**
- * Debug-overlay configuration for the canonical PDF backend.
+ * Debug-overlay configuration for fixed-layout document rendering.
*
* Debug overlays are development aids drawn on top of the regular page
* content. They never participate in measurement or pagination, so enabling
* them does not change the layout graph — and leaving them disabled (the
- * default) keeps rendered documents byte-identical to previous releases.
+ * default) keeps rendered documents byte-identical to previous releases.
+ * Like the other backend-neutral output options in this package
+ * ({@link DocumentWatermark}, {@link DocumentMetadata}, …), the carrier is
+ * format-agnostic; the canonical PDF backend implements both overlays today,
+ * and future fixed-layout backends can honour the same options.
*
* Two overlays are available:
*
* - Guides — fragment boxes plus dashed margin/padding rectangles,
* the overlay previously toggled by the {@code guideLines(boolean)}
* convenience switch.
- * - Node labels — the stable semantic path of the owning node
- * printed once per node and page at the top-left corner of the node's
- * bounds. Labels make a misplaced block traceable back to the exact
- * builder call that authored it: name nodes via the DSL (for example
- * {@code pageFlow().name("InvoiceSheet")}; module titles auto-name
- * their blocks) and the same name appears on the sheet and in
- * {@code DocumentSession.layoutSnapshot()}.
+ * - Node labels — the stable semantic path of the owning node,
+ * printed once per node and page as a small corner badge straddling
+ * the top edge of the node's bounds (right-aligned, so it rarely
+ * covers the node's own text). Labels make a misplaced block traceable
+ * back to the exact builder call that authored it: name nodes via the
+ * DSL (for example {@code pageFlow().name("InvoiceSheet")}; module
+ * titles auto-name their blocks) and the same name appears on the
+ * sheet and in {@code DocumentSession.layoutSnapshot()}.
*
*
* Typical usage through the session convenience API:
* {@code
* try (DocumentSession document = GraphCompose.document(out)
- * .debug(PdfDebugOptions.guidesAndNodeLabels())
+ * .debug(DocumentDebugOptions.guidesAndNodeLabels())
* .create()) {
* // author content ...
* document.buildPdf();
@@ -42,14 +47,14 @@
* @author Artem Demchyshyn
* @since 1.8.0
*/
-public record PdfDebugOptions(boolean showGuides, boolean showNodeLabels, LabelText labelText) {
+public record DocumentDebugOptions(boolean showGuides, boolean showNodeLabels, LabelText labelText) {
- private static final PdfDebugOptions NONE = new PdfDebugOptions(false, false, LabelText.NAME);
+ private static final DocumentDebugOptions NONE = new DocumentDebugOptions(false, false, LabelText.NAME);
/**
* Validates record invariants.
*/
- public PdfDebugOptions {
+ public DocumentDebugOptions {
Objects.requireNonNull(labelText, "labelText");
}
@@ -58,7 +63,7 @@ public record PdfDebugOptions(boolean showGuides, boolean showNodeLabels, LabelT
*
* @return options with all debug overlays off
*/
- public static PdfDebugOptions none() {
+ public static DocumentDebugOptions none() {
return NONE;
}
@@ -68,8 +73,8 @@ public static PdfDebugOptions none() {
*
* @return options drawing fragment boxes and margin/padding guides
*/
- public static PdfDebugOptions guides() {
- return new PdfDebugOptions(true, false, LabelText.NAME);
+ public static DocumentDebugOptions guides() {
+ return new DocumentDebugOptions(true, false, LabelText.NAME);
}
/**
@@ -77,8 +82,8 @@ public static PdfDebugOptions guides() {
*
* @return options drawing semantic node labels
*/
- public static PdfDebugOptions nodeLabels() {
- return new PdfDebugOptions(false, true, LabelText.NAME);
+ public static DocumentDebugOptions nodeLabels() {
+ return new DocumentDebugOptions(false, true, LabelText.NAME);
}
/**
@@ -87,8 +92,8 @@ public static PdfDebugOptions nodeLabels() {
*
* @return options drawing guides and semantic node labels
*/
- public static PdfDebugOptions guidesAndNodeLabels() {
- return new PdfDebugOptions(true, true, LabelText.NAME);
+ public static DocumentDebugOptions guidesAndNodeLabels() {
+ return new DocumentDebugOptions(true, true, LabelText.NAME);
}
/**
@@ -97,8 +102,8 @@ public static PdfDebugOptions guidesAndNodeLabels() {
* @param enabled {@code true} to draw guide lines
* @return new options instance with the requested guide state
*/
- public PdfDebugOptions withGuides(boolean enabled) {
- return enabled == showGuides ? this : new PdfDebugOptions(enabled, showNodeLabels, labelText);
+ public DocumentDebugOptions withGuides(boolean enabled) {
+ return enabled == showGuides ? this : new DocumentDebugOptions(enabled, showNodeLabels, labelText);
}
/**
@@ -107,8 +112,8 @@ public PdfDebugOptions withGuides(boolean enabled) {
* @param enabled {@code true} to draw semantic node labels
* @return new options instance with the requested label state
*/
- public PdfDebugOptions withNodeLabels(boolean enabled) {
- return enabled == showNodeLabels ? this : new PdfDebugOptions(showGuides, enabled, labelText);
+ public DocumentDebugOptions withNodeLabels(boolean enabled) {
+ return enabled == showNodeLabels ? this : new DocumentDebugOptions(showGuides, enabled, labelText);
}
/**
@@ -117,9 +122,9 @@ public PdfDebugOptions withNodeLabels(boolean enabled) {
* @param text label text mode; must not be {@code null}
* @return new options instance with the requested label text mode
*/
- public PdfDebugOptions withLabelText(LabelText text) {
+ public DocumentDebugOptions withLabelText(LabelText text) {
Objects.requireNonNull(text, "text");
- return text == labelText ? this : new PdfDebugOptions(showGuides, showNodeLabels, text);
+ return text == labelText ? this : new DocumentDebugOptions(showGuides, showNodeLabels, text);
}
/**
diff --git a/src/test/java/com/demcha/compose/document/backend/fixed/pdf/PdfDebugNodeLabelsTest.java b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/PdfDebugNodeLabelsTest.java
index 4ebec7a6..fcd246e3 100644
--- a/src/test/java/com/demcha/compose/document/backend/fixed/pdf/PdfDebugNodeLabelsTest.java
+++ b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/PdfDebugNodeLabelsTest.java
@@ -2,7 +2,7 @@
import com.demcha.compose.GraphCompose;
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 org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
@@ -24,7 +24,7 @@ class PdfDebugNodeLabelsTest {
@Test
void nodeLabelsPrintLeafSegmentByDefault() throws Exception {
- String text = extractText(render(PdfDebugOptions.nodeLabels(), "PriceSummary"));
+ String text = extractText(render(DocumentDebugOptions.nodeLabels(), "PriceSummary"));
// The module title is an auto-named paragraph at child index 0; the
// body paragraph follows at index 1.
@@ -37,7 +37,7 @@ void nodeLabelsPrintLeafSegmentByDefault() throws Exception {
@Test
void fullPathLabelsIncludeAncestry() throws Exception {
String text = extractText(render(
- PdfDebugOptions.nodeLabels().withLabelText(PdfDebugOptions.LabelText.FULL_PATH),
+ DocumentDebugOptions.nodeLabels().withLabelText(DocumentDebugOptions.LabelText.FULL_PATH),
"PriceSummary"));
assertThat(text).contains("PriceSummary[0]");
@@ -54,7 +54,7 @@ void labelsStayOffByDefault() throws Exception {
@Test
void guideOverlayAloneDrawsNoLabels() throws Exception {
- String text = extractText(render(PdfDebugOptions.guides(), "PriceSummary"));
+ String text = extractText(render(DocumentDebugOptions.guides(), "PriceSummary"));
assertThat(text).doesNotContain("ParagraphNode[1]");
assertThat(text).doesNotContain("PriceSummaryTitle[0]");
@@ -66,7 +66,7 @@ void guideLinesToggleAfterDebugKeepsLabelSettings() throws Exception {
.pageSize(340, 260)
.margin(DocumentInsets.of(18))
.create()) {
- document.debug(PdfDebugOptions.nodeLabels());
+ document.debug(DocumentDebugOptions.nodeLabels());
document.guideLines(true);
document.pageFlow(page -> page.module("PriceSummary",
module -> module.paragraph("Body copy")));
@@ -76,10 +76,84 @@ void guideLinesToggleAfterDebugKeepsLabelSettings() throws Exception {
}
}
+ @Test
+ void winAnsiEncodableAccentsSurviveInLabels() throws Exception {
+ String text = extractText(render(DocumentDebugOptions.nodeLabels(), "Résumé"));
+
+ // é is WinAnsi-encodable, so the shared GlyphFallbackLogger
+ // degradation keeps it intact instead of mangling it to '?'.
+ assertThat(text).contains("RésuméTitle[0]");
+ assertThat(text).doesNotContain("R?sum?Title[0]");
+ }
+
+ @Test
+ void builderDebugAfterGuideLinesReplacesTheWholeConfig() throws Exception {
+ // Last-write-wins on the GraphCompose builder, same as the session:
+ // debug(none()) after guideLines(true) disables everything, so the
+ // bytes match a render that never enabled debug at all.
+ byte[] disabled;
+ try (DocumentSession document = GraphCompose.document()
+ .pageSize(340, 260)
+ .margin(DocumentInsets.of(18))
+ .guideLines(true)
+ .debug(DocumentDebugOptions.none())
+ .create()) {
+ document.pageFlow(page -> page.module("PriceSummary",
+ module -> module.paragraph("Body copy for the overlay test")));
+ disabled = document.toPdfBytes();
+ }
+ byte[] plain = render(null, "PriceSummary");
+
+ assertThat(disabled).hasSameSizeAs(plain);
+ }
+
+ @Test
+ void builderGuideLinesAfterDebugPreservesLabelSettings() throws Exception {
+ try (DocumentSession document = GraphCompose.document()
+ .pageSize(340, 260)
+ .margin(DocumentInsets.of(18))
+ .debug(DocumentDebugOptions.nodeLabels())
+ .guideLines(true)
+ .create()) {
+ document.pageFlow(page -> page.module("PriceSummary",
+ module -> module.paragraph("Body copy")));
+
+ String text = extractText(document.toPdfBytes());
+ assertThat(text).contains("ParagraphNode[1]");
+ }
+ }
+
+ @Test
+ void splitOwnersAreLabelledOnEveryPageTheyTouch() throws Exception {
+ try (DocumentSession document = GraphCompose.document()
+ .pageSize(220, 120)
+ .margin(DocumentInsets.of(16))
+ .debug(DocumentDebugOptions.nodeLabels())
+ .create()) {
+ document.pageFlow(page -> page.module("LongStory",
+ module -> module.paragraph("flow ".repeat(160))));
+
+ byte[] pdf = document.toPdfBytes();
+ try (PDDocument loaded = Loader.loadPDF(pdf)) {
+ assertThat(loaded.getNumberOfPages()).isGreaterThan(1);
+ PDFTextStripper firstPage = new PDFTextStripper();
+ firstPage.setStartPage(1);
+ firstPage.setEndPage(1);
+ PDFTextStripper secondPage = new PDFTextStripper();
+ secondPage.setStartPage(2);
+ secondPage.setEndPage(2);
+ // The split paragraph's owner gets one badge on each page it
+ // touches, not just on the page of its first fragment.
+ assertThat(firstPage.getText(loaded)).contains("ParagraphNode[1]");
+ assertThat(secondPage.getText(loaded)).contains("ParagraphNode[1]");
+ }
+ }
+ }
+
@Test
void nonWinAnsiNamesDegradeToPlaceholders() throws Exception {
String text = extractText(render(
- PdfDebugOptions.nodeLabels().withLabelText(PdfDebugOptions.LabelText.FULL_PATH),
+ DocumentDebugOptions.nodeLabels().withLabelText(DocumentDebugOptions.LabelText.FULL_PATH),
"Шапка"));
// The five Cyrillic letters survive name normalization but exceed the
@@ -92,7 +166,7 @@ void nonWinAnsiNamesDegradeToPlaceholders() throws Exception {
@Test
void disabledDebugOptionsMatchTheDefaultRender() throws Exception {
byte[] plain = render(null, "PriceSummary");
- byte[] explicitNone = render(PdfDebugOptions.none(), "PriceSummary");
+ byte[] explicitNone = render(DocumentDebugOptions.none(), "PriceSummary");
assertThat(new String(plain, 0, 5, StandardCharsets.US_ASCII)).isEqualTo("%PDF-");
assertThat(new String(explicitNone, 0, 5, StandardCharsets.US_ASCII)).isEqualTo("%PDF-");
@@ -101,7 +175,7 @@ void disabledDebugOptionsMatchTheDefaultRender() throws Exception {
assertThat(explicitNone).hasSameSizeAs(plain);
}
- private static byte[] render(PdfDebugOptions debug, String moduleName) throws Exception {
+ private static byte[] render(DocumentDebugOptions debug, String moduleName) throws Exception {
try (DocumentSession document = GraphCompose.document()
.pageSize(340, 260)
.margin(DocumentInsets.of(18))
diff --git a/src/test/java/com/demcha/compose/document/backend/fixed/pdf/options/PdfDebugOptionsTest.java b/src/test/java/com/demcha/compose/document/backend/fixed/pdf/options/PdfDebugOptionsTest.java
deleted file mode 100644
index e3213a9f..00000000
--- a/src/test/java/com/demcha/compose/document/backend/fixed/pdf/options/PdfDebugOptionsTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.demcha.compose.document.backend.fixed.pdf.options;
-
-import org.junit.jupiter.api.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatNullPointerException;
-
-/**
- * Unit coverage for the {@link PdfDebugOptions} value semantics: factory
- * presets, wither transitions, and the enabled() aggregate.
- */
-class PdfDebugOptionsTest {
-
- @Test
- void noneDisablesEveryOverlay() {
- PdfDebugOptions options = PdfDebugOptions.none();
-
- assertThat(options.showGuides()).isFalse();
- assertThat(options.showNodeLabels()).isFalse();
- assertThat(options.labelText()).isEqualTo(PdfDebugOptions.LabelText.NAME);
- assertThat(options.enabled()).isFalse();
- }
-
- @Test
- void factoriesEnableTheRequestedOverlays() {
- assertThat(PdfDebugOptions.guides().showGuides()).isTrue();
- assertThat(PdfDebugOptions.guides().showNodeLabels()).isFalse();
- assertThat(PdfDebugOptions.guides().enabled()).isTrue();
-
- assertThat(PdfDebugOptions.nodeLabels().showGuides()).isFalse();
- assertThat(PdfDebugOptions.nodeLabels().showNodeLabels()).isTrue();
- assertThat(PdfDebugOptions.nodeLabels().enabled()).isTrue();
-
- PdfDebugOptions both = PdfDebugOptions.guidesAndNodeLabels();
- assertThat(both.showGuides()).isTrue();
- assertThat(both.showNodeLabels()).isTrue();
- assertThat(both.enabled()).isTrue();
- }
-
- @Test
- void withersToggleSingleAspectsAndPreserveTheRest() {
- PdfDebugOptions labelsWithGuides = PdfDebugOptions.nodeLabels().withGuides(true);
- assertThat(labelsWithGuides.showGuides()).isTrue();
- assertThat(labelsWithGuides.showNodeLabels()).isTrue();
-
- PdfDebugOptions guidesOnlyAgain = labelsWithGuides.withNodeLabels(false);
- assertThat(guidesOnlyAgain.showGuides()).isTrue();
- assertThat(guidesOnlyAgain.showNodeLabels()).isFalse();
-
- PdfDebugOptions fullPath = PdfDebugOptions.nodeLabels()
- .withLabelText(PdfDebugOptions.LabelText.FULL_PATH);
- assertThat(fullPath.labelText()).isEqualTo(PdfDebugOptions.LabelText.FULL_PATH);
- assertThat(fullPath.showNodeLabels()).isTrue();
- }
-
- @Test
- void noOpWithersReturnTheSameInstance() {
- PdfDebugOptions options = PdfDebugOptions.guides();
-
- assertThat(options.withGuides(true)).isSameAs(options);
- assertThat(options.withNodeLabels(false)).isSameAs(options);
- assertThat(options.withLabelText(PdfDebugOptions.LabelText.NAME)).isSameAs(options);
- }
-
- @Test
- void nullLabelTextIsRejected() {
- assertThatNullPointerException()
- .isThrownBy(() -> new PdfDebugOptions(false, false, null));
- assertThatNullPointerException()
- .isThrownBy(() -> PdfDebugOptions.none().withLabelText(null));
- }
-}
diff --git a/src/test/java/com/demcha/compose/document/output/DocumentDebugOptionsTest.java b/src/test/java/com/demcha/compose/document/output/DocumentDebugOptionsTest.java
new file mode 100644
index 00000000..271a4e78
--- /dev/null
+++ b/src/test/java/com/demcha/compose/document/output/DocumentDebugOptionsTest.java
@@ -0,0 +1,72 @@
+package com.demcha.compose.document.output;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatNullPointerException;
+
+/**
+ * Unit coverage for the {@link DocumentDebugOptions} value semantics: factory
+ * presets, wither transitions, and the enabled() aggregate.
+ */
+class DocumentDebugOptionsTest {
+
+ @Test
+ void noneDisablesEveryOverlay() {
+ DocumentDebugOptions options = DocumentDebugOptions.none();
+
+ assertThat(options.showGuides()).isFalse();
+ assertThat(options.showNodeLabels()).isFalse();
+ assertThat(options.labelText()).isEqualTo(DocumentDebugOptions.LabelText.NAME);
+ assertThat(options.enabled()).isFalse();
+ }
+
+ @Test
+ void factoriesEnableTheRequestedOverlays() {
+ assertThat(DocumentDebugOptions.guides().showGuides()).isTrue();
+ assertThat(DocumentDebugOptions.guides().showNodeLabels()).isFalse();
+ assertThat(DocumentDebugOptions.guides().enabled()).isTrue();
+
+ assertThat(DocumentDebugOptions.nodeLabels().showGuides()).isFalse();
+ assertThat(DocumentDebugOptions.nodeLabels().showNodeLabels()).isTrue();
+ assertThat(DocumentDebugOptions.nodeLabels().enabled()).isTrue();
+
+ DocumentDebugOptions both = DocumentDebugOptions.guidesAndNodeLabels();
+ assertThat(both.showGuides()).isTrue();
+ assertThat(both.showNodeLabels()).isTrue();
+ assertThat(both.enabled()).isTrue();
+ }
+
+ @Test
+ void withersToggleSingleAspectsAndPreserveTheRest() {
+ DocumentDebugOptions labelsWithGuides = DocumentDebugOptions.nodeLabels().withGuides(true);
+ assertThat(labelsWithGuides.showGuides()).isTrue();
+ assertThat(labelsWithGuides.showNodeLabels()).isTrue();
+
+ DocumentDebugOptions guidesOnlyAgain = labelsWithGuides.withNodeLabels(false);
+ assertThat(guidesOnlyAgain.showGuides()).isTrue();
+ assertThat(guidesOnlyAgain.showNodeLabels()).isFalse();
+
+ DocumentDebugOptions fullPath = DocumentDebugOptions.nodeLabels()
+ .withLabelText(DocumentDebugOptions.LabelText.FULL_PATH);
+ assertThat(fullPath.labelText()).isEqualTo(DocumentDebugOptions.LabelText.FULL_PATH);
+ assertThat(fullPath.showNodeLabels()).isTrue();
+ }
+
+ @Test
+ void noOpWithersReturnTheSameInstance() {
+ DocumentDebugOptions options = DocumentDebugOptions.guides();
+
+ assertThat(options.withGuides(true)).isSameAs(options);
+ assertThat(options.withNodeLabels(false)).isSameAs(options);
+ assertThat(options.withLabelText(DocumentDebugOptions.LabelText.NAME)).isSameAs(options);
+ }
+
+ @Test
+ void nullLabelTextIsRejected() {
+ assertThatNullPointerException()
+ .isThrownBy(() -> new DocumentDebugOptions(false, false, null));
+ assertThatNullPointerException()
+ .isThrownBy(() -> DocumentDebugOptions.none().withLabelText(null));
+ }
+}
diff --git a/src/test/java/com/demcha/testing/visual/DebugNodeLabelsDemoTest.java b/src/test/java/com/demcha/testing/visual/DebugNodeLabelsDemoTest.java
index 7e26e7d6..c1af0a25 100644
--- a/src/test/java/com/demcha/testing/visual/DebugNodeLabelsDemoTest.java
+++ b/src/test/java/com/demcha/testing/visual/DebugNodeLabelsDemoTest.java
@@ -2,7 +2,7 @@
import com.demcha.compose.GraphCompose;
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.compose.testing.visual.ImageDiff;
import com.demcha.compose.testing.visual.PdfVisualRegression;
@@ -26,8 +26,8 @@ class DebugNodeLabelsDemoTest {
@Test
void debugOverlayPaintsGuidesAndNodeLabels() throws Exception {
- byte[] plain = sheet(PdfDebugOptions.none());
- byte[] debug = sheet(PdfDebugOptions.guidesAndNodeLabels());
+ byte[] plain = sheet(DocumentDebugOptions.none());
+ byte[] debug = sheet(DocumentDebugOptions.guidesAndNodeLabels());
assertThat(debug).isNotEmpty();
assertThat(new String(debug, 0, 5, StandardCharsets.US_ASCII)).isEqualTo("%PDF-");
@@ -46,7 +46,7 @@ void debugOverlayPaintsGuidesAndNodeLabels() throws Exception {
out.resolveSibling("debug_node_labels.png").toFile());
}
- private static byte[] sheet(PdfDebugOptions options) throws Exception {
+ private static byte[] sheet(DocumentDebugOptions options) throws Exception {
try (DocumentSession document = GraphCompose.document()
.pageSize(360, 320)
.margin(DocumentInsets.of(22))
From d1db433527c24b450ce1abafeb5278bfefaf9a32 Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Fri, 12 Jun 2026 00:50:20 +0100
Subject: [PATCH 2/2] docs(examples): regenerate debug-overlay preview after
label post-pass
---
assets/readme/examples/debug-overlay.pdf | Bin 1789 -> 1787 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/assets/readme/examples/debug-overlay.pdf b/assets/readme/examples/debug-overlay.pdf
index 9872ee1c1b31a88c194b5f37fdfc3ea9a538cbaa..70ed58f383a7963866b146c158d4b17233b5c4cf 100644
GIT binary patch
delta 1170
zcmey%`ye{*}6-HgBa{mr*}=`&r)
z&mXEhK2xD@YP;XBZRUdGw(RGcxXwK)IJf6W=%$1foi~>pz8hT1sK2-QW@Lyv|Hhja
zb3)JmUs!q1BsFK{oh>0pZk9Qk-dp$S?)ohmpJeZv{(NK?COzw_ebVoFU3+4WHNE?s
z78v%W)F3+d@%8d4;YZIGRC}Fk+*xcDD7B{k^CFGatZ!bctrt6)yivECg~R{U%Dv~q
zwWIQ9f7QCAHUFrn?X=mdxzi@TIZ*b7k-M%B`Bu&-ZPf#hRk<<#_J2H%u?f
z!;IcFe@+oezZRkAQTfcD)xf}I%Re1PyH^WE!t6p8%u|{_Bj1Vm`)zDuNm5W*4@lEI9gI)1-sn9$0^({4MJghg##M
zLHgc_GaPpvsW0AsPZ3B~SAN)e@bbxTa>9k>oU`JLTUo5Rnk@f2)vay5dUN;7#ewG{
zzSO-c|MFen7N@04V8zj=97`LScDcD-_;%>Ux&o0?&wiXOj^}-U?-5gK>fD4~w~LJ;
z(yn;k@K+XY;J9&~@p{QbO|OHj#+O?Qp@DGtz^A_?EGow32z$pCfMIz+0;AfpW};L
z%v!&%PjY5_7wFS}$4)3nK-#PJDV69nVPs-8d$mj)w(&k8k#s88JJl*+9}vjVgxhLjL9)<
bMeGI)48{^nx|6T6DR7u_sj9mAyKw;kv7#kD
delta 1228
zcmey(`aqZ5I`E~n~y3K@j{@!ve`?#=f#?|>rac9C(tz(bg*qgX{|K^W6zqNnQx?59!
zaMPoWA2Vig=Iv2GJ1>6uVxLXzd-M6HYK6g?&YN3~*S_jyacQ{Ec)eud
zlbIruq>U5}^jX906qH`?NYXoa$L^Dp#rmrUxo0Q|P8Uv6JF3enn~>4qm=xDh9ai?b
zLV2G}U;X?b@dwk(Ii9bW()T`jbMoiXy+7J@Kh-bOOg?>TU8?Jx{ON0sT`%4Dd#dj2
zn&Wx1YbwQ8_nm#6zp?go*Phs;O?RK3-L&S$^X{Kj()RlkD?|SjWEY&YOu1osmwofe
z?nP^+MZ}v|ZuJp6og1m*5V2{_*7BcKXU%S>ZSKpNd;ak(+bMUuN>`tF)vk3jtW>Z*
zGgA8fr&ZeG=j*p^mJ%(}$>uZyQq+Hx_nT+PCTx*Jt
zC3hb=a(M>xS=oj)5)J>(%2d^NTDDkRxp?W5dujaRzw9r@$+>!s^byh}sxizVF`
zvD&(DtMHb^UT>FqEZx5}yYoIDU4zrTKR
zMK|9sMwcQ6tDuht4?TIAA9CE=_Es#pm*r;pp?#BuCg({{wmPuxMEP6E9Xza$y_Q~6
z_F-u*Iwf4>&2h%jaiUP-&ez(dL3tbhGM?afIG)+`l~2U7Y*tK7&WEyHdk@b{EblX^
zvXz<4#PnhLhs)pAI5}SO+#(*j;mUrq#();v`Yy?UUvF%=zU_Z{!0JfB7s(%c1J6aA
zIcCCd_}}e?kEimMJ>gBqGHOm77X8t(QKR==s-vTbTzzHU>O|W$7R?zE>g}I7SWov_
zE!B9Uy4~WWkk*{O%_|Qr7rcF$v4d^F{O)7rcQpS;@N%W*r4*MGr6%TrSouj=T$Tz3
z3i(N!>sX=~>rEZqT+9p&9gR$l4NOcdon0)PEzC`wP0cJ_&5T@KP0cM#TntRdw}u;{pIsQ7={i