diff --git a/CHANGELOG.md b/CHANGELOG.md index 95689af3c..723b7f002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,29 @@ for this cycle. (`graph-compose-render-docx` / `graph-compose-render-pptx`) stay opt-in and are not bundled. +## v1.9.1 — 2026-07-06 + +Table columns now contain long inline-code content instead of letting it spill +over the next column. + +### Bug fixes + +- **Long inline-code chips no longer overflow their column.** A long + `inlineCode(...)` / `inlineHighlight(...)` token with no spaces — a package + coordinate, fully-qualified class name or URL — now breaks *within* its + paragraph or table cell instead of drawing over the neighbouring content. It + breaks at `.` `:` `/` `-` seams and char-splits only when a segment is still + too wide, keeping the rounded chip fill intact on every wrapped fragment. + Applies to all three paragraph wrap paths (plain, inline, markdown); text that + already fits is laid out exactly as before. + +- **`auto()` table columns grow to fit composed cell content.** A composed cell + (`DocumentTableCell.node(...)`) in an `auto()` column now contributes its + intrinsic content width, so the column sizes to the content (e.g. an + inline-code chip) instead of collapsing toward zero. `fixed(...)` columns are + unchanged. As with plain-text auto columns, a table too narrow for the summed + intrinsic width of its auto columns reports `exceeds available width`. + ## v1.9.0 — 2026-06-29 In-document navigation. Rendered PDFs can now declare named **anchors** and diff --git a/assets/readme/examples/inline-code-column-wrap.pdf b/assets/readme/examples/inline-code-column-wrap.pdf new file mode 100644 index 000000000..c4f1ee395 Binary files /dev/null and b/assets/readme/examples/inline-code-column-wrap.pdf differ diff --git a/examples/README.md b/examples/README.md index 4d222bb27..1aadb5f4b 100644 --- a/examples/README.md +++ b/examples/README.md @@ -75,6 +75,7 @@ are with the canonical DSL, then jump to its detailed section below. | [Section presets](#section-presets) | `pageBackground`, `band`, `softPanel`, `accentLeft / Right / Top / Bottom`, per-corner `DocumentCornerRadius` | [PDF](../assets/readme/examples/section-presets.pdf) · [Source](src/main/java/com/demcha/examples/features/text/SectionPresetsExample.java) | | [Nested lists](#nested-lists-v16) | `ListBuilder.addItem(label, Consumer)` — depth cascade, per-depth markers, mixed flat / nested authoring | [PDF](../assets/readme/examples/nested-list-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/lists/NestedListExample.java) | | [Composed table cells](#composed-table-cells-v16) | `DocumentTableCell.node(DocumentNode)` — paragraphs, lists, sub-tables inside cells with two-pass measurement | [PDF](../assets/readme/examples/composed-table-cell-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/tables/ComposedTableCellExample.java) | +| [Inline-code column wrap](#inline-code-column-wrap) | A long `inlineCode(...)` coordinate breaks at its `. : / -` seams inside a narrow **fixed** column and an **auto** column grows to fit it on one line | [PDF](../assets/readme/examples/inline-code-column-wrap.pdf) · [Source](src/main/java/com/demcha/examples/features/tables/InlineCodeColumnWrapExample.java) | | [Canvas layer (free placement)](#canvas-layer-v16) | `CanvasLayerNode` — pixel-precise `(x, y)` placement of children inside a fixed bounding box, with `ClipPolicy` clipping | [PDF](../assets/readme/examples/canvas-layer-showcase.pdf) · [Source](src/main/java/com/demcha/examples/features/canvas/CanvasLayerExample.java) | | [Transforms](#transforms) | `rotate`, `scale`, and per-layer `zIndex` swap | [PDF](../assets/readme/examples/transforms.pdf) · [Source](src/main/java/com/demcha/examples/features/transforms/TransformsExample.java) | | [Block alignment](#block-alignment) | `addAligned(align, node)` / `addSvgIcon(icon, w, align)` — seat any fixed-size node left / centre / right across the content width | [PDF](../assets/readme/examples/block-align.pdf) · [Source](src/main/java/com/demcha/examples/features/layout/BlockAlignExample.java) | @@ -535,6 +536,44 @@ table.columns(...) [📄 View PDF](../assets/readme/examples/table-advanced.pdf) · [📜 Full source](src/main/java/com/demcha/examples/features/tables/TableAdvancedExample.java) +### Inline-code column wrap + +A composed cell holding one long `inlineCode(...)` token — a Maven coordinate, +fully-qualified class name or URL — stays inside its column. In a narrow +**fixed** column the chip breaks at its `. : / -` seams (char-splitting only +when a segment is still too wide), with the rounded fill intact on every +fragment; in an **auto** column the column grows to fit the coordinate on one +line instead of collapsing. + +```java +DocumentTableCell.node(document.dsl().paragraph() + .inlineCode("org.junit.jupiter:junit-jupiter:5.10.2").build()) +// fixed(104) column -> wraps at seams; auto() column -> grows to one line +``` + +[📄 View PDF](../assets/readme/examples/inline-code-column-wrap.pdf) · +[📜 Full source](src/main/java/com/demcha/examples/features/tables/InlineCodeColumnWrapExample.java) + +### Custom Business Theme + +Build a `BusinessTheme` from raw `DocumentPalette` / `SpacingScale` / +`TextScale` / `TablePreset` records — no factory shortcut. Plug it +straight into `InvoiceTemplateV2` to retheme the whole template +without touching any code that uses it. + +```java +BusinessTheme studioEmerald = new BusinessTheme( + new DocumentPalette(/* page, surface, surfaceMuted, ink, accent, … */), + SpacingScale.cinematic(), + new TextScale(/* h1, h2, body, caption fonts … */), + TablePreset.cinematic()); + +new InvoiceTemplateV2(studioEmerald).compose(document, invoice); +``` + +[📄 View PDF](../assets/readme/examples/invoice-custom-theme.pdf) · +[📜 Full source](src/main/java/com/demcha/examples/features/themes/CustomBusinessThemeExample.java) + --- ## Public-API surface diff --git a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java index b7fad09d7..67ae395c6 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -24,6 +24,7 @@ import com.demcha.examples.features.streaming.HttpStreamingExample; import com.demcha.examples.features.svg.SvgIconGalleryExample; import com.demcha.examples.features.tables.ComposedTableCellExample; +import com.demcha.examples.features.tables.InlineCodeColumnWrapExample; import com.demcha.examples.features.tables.TableAdvancedExample; import com.demcha.examples.features.text.EmojiGalleryExample; import com.demcha.examples.features.text.EmojiShortcodeExample; @@ -151,6 +152,7 @@ public static void main(String[] args) throws Exception { // v1.6 layout primitives System.out.println("Generated: " + NestedListExample.generate()); System.out.println("Generated: " + ComposedTableCellExample.generate()); + System.out.println("Generated: " + InlineCodeColumnWrapExample.generate()); System.out.println("Generated: " + CanvasLayerExample.generate()); // v1.5 visual primitives diff --git a/examples/src/main/java/com/demcha/examples/features/tables/InlineCodeColumnWrapExample.java b/examples/src/main/java/com/demcha/examples/features/tables/InlineCodeColumnWrapExample.java new file mode 100644 index 000000000..37df23ee9 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/features/tables/InlineCodeColumnWrapExample.java @@ -0,0 +1,121 @@ +package com.demcha.examples.features.tables; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.node.TableNode; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.table.DocumentTableCell; +import com.demcha.compose.document.table.DocumentTableColumn; +import com.demcha.compose.document.table.DocumentTableStyle; +import com.demcha.compose.font.FontName; +import com.demcha.examples.support.ExampleOutputPaths; + +import java.nio.file.Path; +import java.util.List; + +/** + * Runnable showcase for long inline-code coordinates inside table columns. + * + *

A composed cell holding a single long {@code inlineCode(...)} token — a + * Maven coordinate, fully-qualified class name or URL — no longer overflows its + * column. In a narrow fixed column the chip breaks inside the + * cell, preferring its {@code . : / -} seams and char-splitting only as a last + * resort, with the rounded fill intact on every fragment. In an auto + * column the column grows to fit the coordinate on one line instead of + * collapsing.

+ * + * @author Artem Demchyshyn + */ +public final class InlineCodeColumnWrapExample { + + private static final DocumentColor INK = DocumentColor.rgb(34, 38, 50); + private static final DocumentColor MUTED = DocumentColor.rgb(102, 106, 118); + private static final DocumentColor RULE = DocumentColor.rgb(180, 188, 200); + private static final DocumentColor HEADER_FILL = DocumentColor.rgb(20, 60, 75); + private static final DocumentColor ROW_TINT = DocumentColor.rgb(244, 247, 252); + + private static final List COORDINATES = List.of( + "org.junit.jupiter:junit-jupiter:5.10.2", + "io.github.demchaav:graph-compose:1.9.0", + "https://repo1.maven.org/maven2/"); + + private InlineCodeColumnWrapExample() { + } + + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare("features/tables", "inline-code-column-wrap.pdf"); + + DocumentTextStyle title = DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD).size(20).color(INK) + .decoration(DocumentTextDecoration.BOLD).build(); + DocumentTextStyle caption = DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_OBLIQUE).size(10).color(MUTED).build(); + DocumentTextStyle body = DocumentTextStyle.builder() + .fontName(FontName.HELVETICA).size(10.5).color(INK).build(); + DocumentTextStyle headerText = DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD).size(11).color(DocumentColor.WHITE) + .decoration(DocumentTextDecoration.BOLD).build(); + + DocumentTableStyle headerStyle = DocumentTableStyle.builder() + .fillColor(HEADER_FILL).stroke(DocumentStroke.of(RULE, 0.6)) + .padding(DocumentInsets.of(8)).textStyle(headerText).build(); + DocumentTableStyle bodyCellStyle = DocumentTableStyle.builder() + .stroke(DocumentStroke.of(RULE, 0.6)).padding(DocumentInsets.of(8)).textStyle(body).build(); + DocumentTableStyle tintedCellStyle = DocumentTableStyle.builder() + .fillColor(ROW_TINT).stroke(DocumentStroke.of(RULE, 0.6)) + .padding(DocumentInsets.of(8)).textStyle(body).build(); + + try (DocumentSession document = GraphCompose.document(outputFile) + .pageSize(DocumentPageSize.A4) + .margin(36, 36, 36, 36) + .create()) { + + document.pageFlow() + .name("CodeWrapShowcase") + .spacing(8) + .addParagraph("Long inline-code coordinates stay inside the column", title) + .addParagraph( + "A composed cell with one long inlineCode(...) token used to spill over the " + + "next column. It now breaks at its . : / - seams inside a narrow FIXED " + + "column (char-splitting only when a segment is still too wide), and an " + + "AUTO column grows to fit it on one line.", caption) + .build(); + + List> rows = new java.util.ArrayList<>(); + rows.add(List.of( + DocumentTableCell.text("Fixed 104pt").withStyle(headerStyle), + DocumentTableCell.text("Auto").withStyle(headerStyle))); + for (int i = 0; i < COORDINATES.size(); i++) { + String coordinate = COORDINATES.get(i); + DocumentTableStyle cellStyle = i % 2 == 0 ? bodyCellStyle : tintedCellStyle; + rows.add(List.of( + DocumentTableCell.node(document.dsl().paragraph().inlineCode(coordinate).build()) + .withStyle(cellStyle), + DocumentTableCell.node(document.dsl().paragraph().inlineCode(coordinate).build()) + .withStyle(cellStyle))); + } + + document.add(new TableNode( + "CodeWrapTable", + List.of(DocumentTableColumn.fixed(104), + DocumentTableColumn.auto()), + rows, + bodyCellStyle, + null, + DocumentInsets.zero(), + DocumentInsets.zero())); + + document.buildPdf(); + } + return outputFile; + } + + public static void main(String[] args) throws Exception { + System.out.println("Generated: " + generate()); + } +} diff --git a/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java b/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java index c73a92547..112d91490 100644 --- a/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java +++ b/examples/src/main/java/com/demcha/examples/flagships/EngineDeckExample.java @@ -411,9 +411,13 @@ private static DocumentNode brandLine() { /** Version pill ("v1.8.0") with the codename centred beside it as a tag. */ private static DocumentNode versionBlock() { + // Show the base version only: a dev/pre-release qualifier ("2.0.0-SNAPSHOT", + // "2.0.0-beta.1") is wider than the 96pt pill, and the engine wraps long + // tokens at their "." / "-" seams — the pill would break onto two lines. + String pillVersion = VERSION.replaceFirst("-.*$", ""); DocumentNode pill = new ShapeContainerBuilder().name("VerPill") .roundedRect(96, 30, 8).fillColor(VIOLET_DEEP) - .center(new ParagraphBuilder().text("v" + VERSION) + .center(new ParagraphBuilder().text("v" + pillVersion) .textStyle(DocumentTextStyle.builder().fontName(FontName.HELVETICA_BOLD) .size(14).color(DocumentColor.WHITE).build()) .align(TextAlign.CENTER).margin(DocumentInsets.zero()).build()) diff --git a/pom.xml b/pom.xml index c22dff16f..060771842 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 3.27.7 6.1.1 5.23.0 - 1.18.10 + 1.18.11