Skip to content

Releases: DemchaAV/GraphCompose

GraphCompose v1.6.1 — Java 17 baseline + dependency refresh

09 May 09:09

Choose a tag to compare

For users

GraphCompose v1.6.1 is a maintenance + compatibility patch. Drops the Java 21 source/target baseline to Java 17+ so the library can ship into older enterprise stacks without a fork, and refreshes test/build dependencies (incl. CVE pass + ByteBuddy/Mockito update so Mockito works on JDK 25).

No public API change — engine, DSL, themes, templates, and backend records all stay source-compatible with v1.6.0; existing v1.6.0 callers compile and behave unchanged.

Co-developed with external contributor @jottinger (#8, #10).

Toolchain

  • Java 17 baseline. <maven.compiler.release> flips from 21 to 17 across pom.xml, examples/pom.xml, and benchmarks/pom.xml. Engine source loses the Java 21–only constructs (switch-with-type-patterns, switch-with-deconstruction, List.getFirst(), Thread.threadId()) in favour of Java 17–compatible forms. CI runs against Temurin JDK 17 / 21 / 25 in matrix.
  • Dependency refresh + CVE pass. Bumps Jackson 2.20.1 → 2.21.3, Logback 1.5.18 → 1.5.32, Lombok 1.18.38 → 1.18.46, POI 5.4.0 → 5.5.1, SnakeYAML 2.4 → 2.6, AssertJ 3.27.3 → 3.27.6, JUnit 5.12.2 → 5.14.4, Mockito 5.20.0 → 5.23.0. Adds explicit ByteBuddy 1.18.7 so Mockito works on the Java 25+ access rules. Maven plugin bumps: maven-compiler-plugin 3.13 → 3.15, maven-surefire-plugin 3.2.5 → 3.5.5, exec-maven-plugin 3.5 → 3.6.2.

Verified

  • CI green on Temurin JDK 17 / 21 / 25 in parallel matrix.
  • 819 / 0 / 0 / 0 canonical test suite green on every JDK.
  • 26 runnable examples regenerate cleanly.
  • Engine source has zero remaining Java 21–only constructs (getFirst, getLast, threadId, switch type patterns, switch deconstruction).

Quick start

Maven:

<repositories>
    <repository><id>jitpack.io</id><url>https://jitpack.io</url></repository>
</repositories>

<dependency>
    <groupId>com.github.DemchaAV</groupId>
    <artifactId>GraphCompose</artifactId>
    <version>v1.6.1</version>
</dependency>

Gradle (Kotlin DSL):

repositories { maven("https://jitpack.io") }
dependencies { implementation("com.github.demchaav:GraphCompose:v1.6.1") }

Looking ahead

Maven Central distribution (#7) remains queued for the upcoming v1.7.0, alongside the JMH benchmark migration. v1.6.1 stays on JitPack.

Links


Author intent, not coordinates.

GraphCompose v1.6.0 — expressive release

07 May 22:08

Choose a tag to compare

For users

v1.6.0 — "expressive" — makes GraphCompose more expressive without breaking your engine code:

  • Nested lists are now easier to compose — ListBuilder.addItem(label, Consumer) opens a child scope, and a per-depth marker cascade (·) handles the visual hierarchy. Mixed flat / nested authoring preserves source order.
  • Table cells can hold any composable node — DocumentTableCell.node(...) accepts paragraphs, nested lists, sub-tables, layer stacks. Two-pass measurement preserves the row-by-row pagination contract.
  • Canvas layers support pixel-precise (x, y) placement — CanvasLayerNode is the controlled free-canvas primitive for when you do need coordinates, with ClipPolicy.CLIP_BOUNDS clipping and atomic pagination.
  • Templates v2 ships 14 CV presets and 14 paired cover-letter presets, theme-driven via BusinessTheme, with one-liner create(theme) factories. Inline markdown, active hyperlinks, slot-based multi-column layouts. The Templates v2 visual pipeline is hardened with pixel-diff parity checks against v1 reference renders to catch visual regressions on every build.
  • Architecture hardening@Internal API stability marker on engine internals, public PdfFragmentRenderHandler SPI for custom render handlers, DocumentRenderingException wrapping the convenience render path so buildPdf / writePdf / toPdfBytes no longer declare throws Exception, documented thread-safety contract.

Engine source-compatibility with v1.5 is preserved for unmodified callers. Every public record that grew a new field ships back-compat constructors that default the new value, so v1.5 callers compile and behave unchanged.

Breaking change

Templates v2 replaces the legacy CV / cover-letter template classes. Legacy classes (CvTemplateV1, NordicCleanCvTemplate, MonogramSidebarCvTemplate, …) are deleted, not deprecated. Anyone constructing those classes must switch to the matching v2 preset's create(BusinessTheme) factory. The full v1 → v2 mapping (every old class → its v2 replacement, with before/after code) is documented in docs/migration-v1-5-to-v1-6.md.

Quick start

Maven:

<repositories>
    <repository><id>jitpack.io</id><url>https://jitpack.io</url></repository>
</repositories>

<dependency>
    <groupId>com.github.DemchaAV</groupId>
    <artifactId>GraphCompose</artifactId>
    <version>v1.6.0</version>
</dependency>

Gradle (Kotlin DSL):

repositories { maven("https://jitpack.io") }
dependencies { implementation("com.github.demchaav:GraphCompose:v1.6.0") }

Distribution status — currently published via JitPack. Maven Central is planned for v1.7 (tracking issue #7).

What's verified

  • 819 / 0 / 0 / 0 — full canonical test suite green via mvnw verify.
  • 26 runnable examples regenerate cleanly via GenerateAllExamples and ship as a CI artifact on every build.
  • 28 layout-snapshot baselines + 29 pixel-diff visual baselines cover every CV / cover-letter preset. The parity gate runs on every CI build with a calibrated mismatchedPixelBudget for cross-platform PDFBox font drift.

Links

  • Live showcase — install snippet, every example with a generated PDF preview, template gallery, feature examples.
  • Templates v2 landing — preset gallery, four-layer architecture explanation, quick-start.
  • Migration v1.5 → v1.6 — v1 → v2 template upgrade table.
  • CHANGELOG.md — full technical changelog, including architecture-hardening details, package-level changes, and ADRs 0011-0014.

Author intent, not coordinates.

GraphCompose v1.5.1

05 May 21:06

Choose a tag to compare

v1.5.1 — 2026-05-05

Dependencies

  • PDFBox 3.0.7. Bumped from 3.0.5 to 3.0.7 (Apache PDFBox patch
    release with upstream rendering and security fixes). No
    public-API impact for GraphCompose consumers.

Tooling

  • ShapeContainerVisualRegressionTest tolerates the cross-platform
    PDF font-rendering drift that surfaces between Windows-rendered
    baselines and the Linux CI runner (~1-2% pixel diff), via a
    calibrated mismatchedPixelBudget instead of bit-exact comparison.
  • DocumentationCoverageTest no longer pins to the structural
    section anchors that the v1.5.0 README slim removed; the guard now
    scans the whole README for canonical-DSL coverage and
    legacy-API leakage in one whole-file pass.

This is a maintenance patch release. There are no public API
changes; v1.5.0 consumers can upgrade with no code changes.

GraphCompose v1.5.0 — the intuitive release

04 May 10:51

Choose a tag to compare

Headline — "intuitive"

v1.5 keeps every v1.4 cinematic primitive and turns the canonical
authoring surface into a polished, theme-driven experience. Three new
visual feature pillars — shape-as-container with clip path,
transforms (rotate / scale) + per-layer z-index, and advanced
tables
— combine with two new cinematic templates
(InvoiceTemplateV2, ProposalTemplateV2), a CvTheme
BusinessTheme bridge
(ADR 0002), six modernised CV templates,
and a documentation pass that covers every new primitive with a recipe
and a runnable example. Test count grew from 525 (v1.4.1) to 675 — an
extra +150 tests across the cinematic, transform, table, theme-bridge,
streaming, snapshot, CV-render, and Transformable-leaf-builder surfaces.

v1.5 is fully source-compatible with v1.4. Every public record
that grew a new field ships back-compat constructors that default the
new value, so v1.4 callers compile and behave unchanged. See
docs/migration-v1-4-to-v1-5.md.

Public API — visual primitives

  • Shape-as-container. New addCircle(diameter, fill, inside),
    addEllipse(w, h, fill, inside), and addContainer(...) shortcuts
    on AbstractFlowBuilder build a ShapeContainerNode whose bounding
    box is dictated by a ShapeOutline (Rectangle,
    RoundedRectangle, Ellipse, plus a circle(diameter) factory).
    Children are clipped via the new ClipPolicy enum
    (CLIP_PATH — default — / CLIP_BOUNDS / OVERFLOW_VISIBLE). The
    PDF backend honours every clip policy via graphics-state
    saveGraphicsState() + clip(path) markers; the DOCX backend renders
    layers inline without the outline frame and logs a one-time
    docx.export.shape-container-fallback capability warning.
    ShapeContainerBuilder exposes the same nine-point alignment
    vocabulary as LayerStackBuilder plus position(node, dx, dy, anchor) for screen-space nudges.
  • Transforms (rotate / scale). New
    com.demcha.compose.document.style.DocumentTransform value type
    with rotate(deg), scale(uniform), scale(sx, sy) factories
    plus withRotation(...) / withScale(...) axis-preserving copies
    and an isIdentity() helper. New
    com.demcha.compose.document.dsl.Transformable<T> mixin exposes
    transform(...), rotate(...), scale(...) as default methods.
    Every shape-shaped builder opts in: ShapeContainerBuilder,
    ShapeBuilder, LineBuilder, EllipseBuilder, ImageBuilder,
    BarcodeBuilder. rotate(...).scale(...) chain naturally and pivot
    around the placement centre. The PDF backend issues
    saveGraphicsState() + cm(matrix) around each transformed leaf
    (rotation is negated on the way out so the engine's clockwise
    convention matches PDF native counter-clockwise). Identity
    transforms short-circuit and emit no markers, so layout snapshots
    for default-configured nodes are byte-identical to v1.4.
  • Per-layer z-index. LayerStackNode.Layer and shape-container
    layers gain int zIndex (default 0).
    LayerStackBuilder.layer(node, align, zIndex) /
    position(node, dx, dy, align, zIndex) and the matching
    ShapeContainerBuilder overloads let a layer declared earlier draw
    on top of layers declared later. The layout compiler stable-sorts
    layers before render; equal zIndex keeps source order.

Public API — advanced tables

  • DocumentTableCell.rowSpan(int) mirrors the existing
    colSpan(int). Cells compose freely:
    DocumentTableCell.text("Tall").colSpan(2).rowSpan(3). The layout
    layer skips occupied grid positions when interpreting subsequent
    source rows; misalignments (missing cell, extra source cell,
    overlapping span, span exceeding remaining rows) raise precise
    diagnostics.
  • TableBuilder.zebra(odd, even) paints alternating row fills.
    Available as (DocumentTableStyle, DocumentTableStyle) and as a
    (DocumentColor, DocumentColor) overload. Either argument may be
    null to skip painting that parity. Existing entries in the
    rowStyles map (headerStyle(...), rowStyle(idx, ...),
    totalRow(...)) always win over zebra alternation.
  • TableBuilder.totalRow(values) adds a totals row with a default
    bold-on-grey-blue style; totalRow(style, values) is the
    customisable form.
  • TableBuilder.repeatHeader() / repeatHeader(rowCount) re-emits
    the configured leading rows at the top of every continuation page
    when a table paginates. Default is 0 so existing tables paginate
    exactly as before.
  • TableBuilder.headerRow(values) is a naming alias for
    header(...) so authors writing
    headerRow(...).row(...).totalRow(...) keep a parallel vocabulary.

Public API — templates and themes

  • InvoiceTemplateV2 is the cinematic invoice counterpart to
    InvoiceTemplateV1. Two constructors: the no-arg form picks
    BusinessTheme.modern(), the one-arg
    InvoiceTemplateV2(BusinessTheme) accepts any theme. Hero
    softPanel carrying invoice number / dates / inline rich-text
    status, a two-column row with From / Bill to parties, themed
    line-items table with headerStyle / zebra / totals /
    repeatHeader(), and a footer row with accentLeft strips on the
    notes / payment-terms columns.
  • ProposalTemplateV2 is the proposal counterpart, sharing the
    same BusinessTheme-driven composition: hero panel rounded only on
    the right (via the new DocumentCornerRadius.right(...) form),
    themed executive-summary panel, sender / recipient parties row,
    sections rendered through theme.text().h2() headings, a timeline
    table (Phase / Duration / Details), and a pricing table (Item /
    Description / Amount) with repeatHeader(), zebra rows, and a
    total-pricing row anchored at the bottom via totalRow(...).
  • CvTheme.fromBusinessTheme(BusinessTheme) static factory
    derives a CV theme from a business theme (ADR 0002). The bridge
    maps palette / text-scale slots into primaryColor /
    secondaryColor / bodyColor / accentColor / headerFont /
    bodyFont / font sizes; CV-specific layout tokens (spacing,
    moduleMargin, spacingModuleName) keep the existing CV defaults.
    The ten existing CV templates and CvTemplateV1 continue to work
    unchanged.
  • Six CV templates modernised to v1.5 idioms:
    BlueBannerCvTemplate, BoxedSectionsCvTemplate,
    CenteredHeadlineCvTemplate, MonogramSidebarCvTemplate,
    SidebarPortraitCvTemplate, TimelineMinimalCvTemplate. Each
    gains a (CvTheme) constructor and keeps a no-arg one whose
    default theme matches the legacy palette/font choices, so default-
    constructed instances render identical-page-count PDFs to v1.4.
    accentTop / accentBottom replace the old
    addLine(horizontal=innerWidth) separators around section banners,
    and softPanel(...) collapses the
    padding(asymmetric) + fillColor(...) cascade.
  • InvoiceTemplateV1 and ProposalTemplateV1 continue to ship
    side-by-side. Authors who want the cinematic look opt in by
    switching the type.

Public API — DSL ergonomics (Phase A)

  • LayerStackBuilder exposes nine alignment shortcuts (topLeft,
    topCenter, topRight, centerLeft, center, centerRight,
    bottomLeft, bottomCenter, bottomRight) on top of back /
    center so authors do not need to remember the full LayerAlign
    enum.
  • LayerStackBuilder.position(node, offsetX, offsetY, anchor) nudges
    a layer from its anchor by an on-screen offset (positive offsetX
    = right, positive offsetY = down).
  • AbstractFlowBuilder gains five convenience overloads on top of
    the v1.4 surface: addShape(w, h, fill),
    addEllipse(diameter, fill), addEllipse(w, h, fill),
    addCircle(diameter, fill), addImage(data, w, h).
  • RowBuilder.spacing(double) is the canonical name for horizontal
    child spacing; RowBuilder.gap(double) becomes a deprecated alias
    (@Deprecated(since = "1.5.0")) that delegates to spacing(...).
  • RowBuilder.add(node) validates the child type eagerly and
    raises IllegalArgumentException from the offending call site
    instead of deferring to build() and raising
    IllegalStateException later.
  • DocumentDsl.richText(Consumer<RichText>) is a new callback entry
    point that builds a RichText run sequence in one fluent call.

Architecture

  • New NodeDefinition.emitOverlayFragments(...) hook complements the
    existing emitFragments(...). It exists for paired begin/end
    marker pairs (clip-begin/end, transform-begin/end) so the layout
    compiler can emit a single flat fragment sequence
    [transform-begin → outline → clip-begin → … layers … → clip-end → transform-end] in one pass. Most node types inherit the empty
    default and need no changes.
  • New marker payloads on BuiltInNodeDefinitions:
    ShapeClipBeginPayload / ShapeClipEndPayload (carry outline +
    policy + owner path), TransformBeginPayload /
    TransformEndPayload. PDF render handlers ship alongside:
    PdfShapeClipBeginRenderHandler, PdfShapeClipEndRenderHandler,
    PdfTransformBeginRenderHandler, PdfTransformEndRenderHandler,
    registered in PdfFixedLayoutBackend.defaultHandlers().
  • New PaginationPolicy.SHAPE_ATOMIC distinguishes shape-clipped
    atomicity from bbox-only ATOMIC for snapshots and render
    handlers. Oversized containers raise the existing
    AtomicNodeTooLargeException with the offending semantic name.
  • TableLayoutSupport replaces the per-row colSpan-sum check with
    a unified cell-grid pre-pass driven by an occupancy mask. The new
    buildLogicalRows(node, columnCount) walks columns left-to-right,
    skipping positions covered by a prior row's spanning cell.
    LogicalCell carries the cell's full
    (startRow, startColumn, colSpan, rowSpan, content) extent.
    Row-height resolution is two-pass: single-row first, then spanning
    cells distribute deficit equally across covered rows.
  • TableResolvedCell gains double yOffset (eighth field). Spanning
    cells use a NEGATIVE offset equal to the...
Read more

GraphCompose v1.4.1 — README guards hotfix

27 Apr 16:26

Choose a tag to compare

Documentation

  • README rewrite for v1.4.0 dropped three structural sections (## Table component, ## Line primitive, ## Architecture at a glance) that the DocumentationCoverageTest guards baseline. CI flagged the regression on the main branch; v1.4.1 restores the sections (the table snippet now also points readers to the new column-span feature), keeps the canonical-DSL anti-patterns out of the snippets, and moves the architecture mermaid diagram back into its dedicated section.

Tooling

  • examples/src/main/java/com/demcha/examples/GenerateAllExamples.java now wires CinematicProposalFileExample.generate() into the orchestrator, so the runnable examples module produces all seven fixtures (including project-proposal-cinematic.pdf) used by the README visual previews.

This is a documentation-only patch release. There are no public API changes; v1.4.0 consumers can upgrade with no code changes.


GraphCompose v1.4.0 — cinematic document engine

27 Apr 15:48

Choose a tag to compare

Headline — "cinematic document engine"

v1.4 closes the visual-design gap that the previous releases left open. Tables can now span columns, layers can stack on top of each other, sections and pages carry semantic backgrounds, paragraphs accept fluent rich text, and the whole look-and-feel can be parametrised through a single BusinessTheme. The release also lands the visual-regression scaffolding required to keep README screenshots stable across refactors.

Public API — semantic primitives

  • DocumentTableCell is now a 3-field record (lines, style, colSpan). The new colSpan(int) factory plus withColSpan(...) on TableCellContent let one cell occupy several columns; sum-of-spans-per-row is validated by TableLayoutSupport. Border ownership and natural-width distribution understand spans (extra width is shared across auto columns inside the span; an all-fixed span throws when it cannot fit). Renderer code is unchanged — spanned cells emit a single TableResolvedCell with the merged width.
  • new LayerStackNode + LayerAlign primitive composes children inside the same bounding box, in source order (first behind, last in front). Each layer carries one of nine alignments (TOP_LEFT … BOTTOM_RIGHT). Pagination is atomic. Backed by a new Axis.STACK in CompositeLayoutSpec and a compileStackedLayer branch in LayoutCompiler. DSL surface: LayerStackBuilder with back(...), center(...), layer(node, align).
  • DocumentSession.pageBackground(DocumentColor | Color) (and the matching GraphCompose.DocumentBuilder setter) injects a full-canvas ShapeFragmentPayload at the start of every page. Combine with LayerStackNode for cinematic hero pages without any backend changes.
  • AbstractFlowBuilder gains semantic shortcuts on every flow / section / module: band(color), softPanel(color) / softPanel(color, radius, padding), and accentLeft / accentRight / accentTop / accentBottom(color, width). They reuse the existing fillColor, cornerRadius, padding, and DocumentBorders plumbing — the new methods are sugar for designer-style flows.
  • RichText fluent builder (document.dsl.RichText) plus ParagraphBuilder.rich(...) / AbstractFlowBuilder.addRich(...) cover the Status: Pending label/value pattern in one expression: RichText.text("Status: ").bold("Pending").color("…", red).accent("…", brand). Includes plain / bold / italic / boldItalic / underline / strikethrough / color / accent / size / style / link / append.

Public API — design tokens

  • new com.demcha.compose.document.theme package — entirely on top of public document-level types, no engine leaks.
    • DocumentPalette — primary / accent / surface / surfaceMuted / textPrimary / textMuted / rule
    • SpacingScale — five-step xs / sm / md / lg / xl with monotonicity validation and insetsXs() … insetsXl() helpers
    • TextScaleh1 / h2 / h3 / body / caption / label / accent resolved styles
    • TablePresetdefaultCellStyle / headerStyle / totalRowStyle / zebraStyle
    • BusinessTheme — composes the four scales plus an optional page background, with three built-in presets (classic(), modern() cream paper + teal, executive() slate panels with Times-Roman headings) and immutable withName / withPageBackground forks

Testing infrastructure

  • com.demcha.testing.visual.ImageDiff — pixel-by-pixel comparison with per-channel tolerance and a red/grey diff image.
  • com.demcha.testing.visual.PdfVisualRegression — renders PDF bytes to one PNG per page via PdfRenderBridge and compares against baselines under src/test/resources/visual-baselines. Approve mode (-Dgraphcompose.visual.approve=true or GRAPHCOMPOSE_VISUAL_APPROVE=true) writes new baselines; comparison failures drop actual.png and diff.png next to the baseline for inspection.
  • 41 new tests across the cinematic surfaces (TableColSpanIntegrationTest, TableBuilderColSpanTest, LayerStackBuilderTest, PageBackgroundTest, SectionPresetTest, RichTextTest, BusinessThemeTest, PdfVisualRegressionTest). Total green count: 525.

Architecture

  • CompositeLayoutSpec.Axis.STACK joins VERTICAL and HORIZONTAL. The compiler dispatches STACK to compileStackedLayer, which positions each child inside the stack box via per-layer alignment offsets and shares the same compileNodeInFixedSlot plumbing rows already use.
  • table layout (TableLayoutSupport, test-side TableBuilder) was rewritten around a "logical cell" model: each authored cell is one LogicalCell(startColumn, colSpan, content) resolved against a stylesGrid[row][col] — the grid keeps existing border-ownership logic intact while letting render code keep emitting one TableResolvedCell per logical cell.
  • DocumentSession.layoutGraph() now wraps compiler.compile(...) with withPageBackgrounds(...) so backends never need to know about the page-background option — they just iterate fragments as usual.

Performance

  • Cinematic features have negligible overhead: page-background injection is a single fragment per page; column spans, layer stacks, and themes do not change the number of emitted fragments. End-to-end template latency stays in the same envelope as v1.3 once JIT is warm.
  • Full benchmark surface is now published in the README: current-speed (full profile) latency + per-stage breakdown, parallel throughput on the invoice template (1→8 threads), scalability suite (1→16 threads, 13.8× speedup at 16), 50-thread stress test (5,000 docs, 0 errors), and the comparative table against iText 5 and JasperReports.

Documentation

  • README rewritten around the cinematic v1.4 narrative: new sections for column spans, layer stacks, page background + section presets, rich text DSL, business themes, the visual-regression workflow, "Extending GraphCompose" guidance, and a refreshed Performance section sourced from scripts/run-benchmarks.ps1.

GraphCompose v1.1.0

13 Apr 19:50

Choose a tag to compare

Highlights

  • compose-first built-in template usage is now the documented default
  • backend-neutral DocumentComposer and handler-driven rendering make the engine less PDF-centric internally
  • layout snapshot testing is now part of the regression workflow for pagination and geometry changes
  • runnable examples now cover CV, cover letter, invoice, proposal, and weekly schedule generation
  • document-level PDF features now include QR/barcodes, watermarks, headers/footers, bookmarks, metadata, protection, explicit page breaks, and dividers
  • visual showcase tests make pagination, document chrome, and barcode output easier to inspect
  • benchmark tooling now includes current-speed, comparative, and diffable JSON/CSV reports
  • an experimental live preview dev tool is available in test scope for fast template iteration

Added

  • barcode support with QR, Code 128, and EAN-13 builders
  • watermark support
  • configurable headers and footers with page numbers and separators
  • PDF bookmarks / outline generation
  • document metadata support
  • PDF protection hooks
  • explicit page-break and divider builders
  • visual showcase render tests
  • benchmark export and diff tooling
  • one-command benchmark runner

Compatibility

  • older tagged JitPack releases such as v1.0.3 remain usable
  • deprecated render(...) template adapters are still available for compatibility
  • new docs and examples now prefer compose(...)

v1.0.3 — First Public Release

27 Mar 15:25

Choose a tag to compare

Highlights:

  • Declarative document composition with reusable builders and layout systems
  • Automatic multi-page layout and pagination
  • Markdown support via Flexmark
  • Shared font registration and reusable text styles
  • Template layer with CvTheme and TemplateBuilder
  • JitPack distribution for easy integration