Skip to content

GraphCompose 2.0

v1.7.0 Breaking

This release includes breaking changes for platform teams planning a safe upgrade.

Published 1mo Build & Package
✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

business-documents cv-templates declarative-api document-generation invoice-template java
+12 more
java-17 layout-engine maven pagination pdf pdf-generator pdf-lib pdf-library pdfbox3 report-generation snapshot-testing template-engine

Summary

AI summary

Broad release touches Public API, Build, sides, and multi-layer.

Full changelog

v1.7.0 — 2026-06-07

Canonical DSL primitives — additive only, zero breaking changes. Adding public
API turns the open cycle into a minor.

Public API

  • Inline shape runs — geometry-based dots, diamonds, stars and bullets. New
    com.demcha.compose.document.node.InlineShapeRun (@since 1.7.0) joins the
    sealed InlineRun hierarchy alongside text and image runs. It draws any
    ShapeOutline figure on the paragraph baseline directly from geometry — no
    raster payload, no font glyph — so skill rating dots (Java ●●●●○), custom
    bullets and inline status markers no longer depend on a font shipping
    U+25CF and friends. Authored through ParagraphBuilder / RichText
    dot(...), ellipse(...), diamond(...), triangle(...), star(...) and
    the generic shape(ShapeOutline, ...); measured into line width and height
    like inline images. A null fill paints an outlined figure, a null stroke
    a filled one; at least one must be present.
  • New polygon shape geometry, usable block-level and inline. ShapeOutline
    (com.demcha.compose.document.style) gains a Polygon kind plus a family of
    factories built from normalized ShapePoint vertices (@since 1.7.0):
    diamond, triangle, star, polygon, arrow / arrowRight / arrowLeft
    (4-way Direction), chevron, checkmark, plus and regularPolygon(sides).
    Arrows and chevrons read as directional list bullets or inline markers
    between text ("Step 1 → Step 2", "Home › Docs"). ParagraphBuilder /
    RichText add arrow(size, Direction, fill) and chevron(...) shortcuts
    (every other kind is reachable through shape(ShapeOutline, ...));
    ShapeContainerBuilder exposes matching block outlines. Rectangle,
    rounded-rectangle and ellipse shape containers are unchanged.
  • Inline checkboxes + composite (multi-layer) inline figures. An inline
    shape run is now a stack of paint layers
    (com.demcha.compose.document.node.ShapeLayer, @since 1.7.0) drawn overlaid
    and centred, so a figure can compose several outlines — each with its own
    fill/stroke — and still measure and place as one unit on the baseline.
    ParagraphBuilder / RichText gain checkbox(size, checked, color) /
    checkbox(size, checked, boxColor, checkColor) (@since 1.7.0): a rounded
    frame plus, in the checked state, a centred tick — the todo / checklist marker
    for "some items done, some not". The single-outline InlineShapeRun
    convenience constructors are unchanged; every other kind still renders as one
    layer.
  • Swappable tick and arrow designs (the "pick your figure" seam).
    ShapeOutline adds CheckmarkStyle (CLASSIC, HEAVY) and ArrowStyle
    (BLOCK, TRIANGLE) enums plus the overloads
    checkmark(w, h, CheckmarkStyle) and arrow(w, h, Direction, ArrowStyle)
    (@since 1.7.0); the no-style factories delegate to CLASSIC / BLOCK, so
    the default look is unchanged. checkbox(...), RichText.arrow(...) and
    ParagraphBuilder.arrow(...) gain matching style overloads, and checkbox
    also accepts a raw ShapeOutline mark for fully custom ticks. Adding a new
    design is one enum constant plus its vertex ring — the foundation for letting
    a caller choose which tick or arrow to render.
  • softPanel(...) gains stroke overloads — rounded fill + outline in one node.
    AbstractFlowBuilder.softPanel(color, radius, padding, stroke) and
    softPanel(color, cornerRadius, padding, stroke) (@since 1.7.0) apply a
    border stroke alongside the panel fill on the same flow node (section, module,
    page flow), so a rounded, padded background with a thin outline no longer needs
    a separate wrapping node. Equivalent to the always-available
    softPanel(...).stroke(...) chain — these overloads just make the one-node
    form discoverable.
  • Per-corner radius on shape containers. ShapeContainerBuilder.roundedRect(width, height, DocumentCornerRadius) plus the new ShapeOutline.RoundedRectanglePerCorner
    (@since 1.7.0) round a container's four corners independently — a card "rounded on
    the left, square on the right" no longer needs a CLIP_PATH-parent workaround, since
    both the outline fill/stroke and the child clip now follow the per-corner geometry.
    DocumentCornerRadius.left/right/top/bottom(...) give the common asymmetric presets.
    The single-radius roundedRect(w, h, double) overload is unchanged, and uniform
    rounded rectangles render byte-for-byte identically (the clip and fill now share one
    per-corner path implementation, called with four equal radii).
  • Vertical text alignment for shape-container labels.
    ParagraphBuilder.verticalAlign(TextVerticalAlign) (new enum
    com.demcha.compose.document.node.TextVerticalAlign, @since 1.7.0) seats a
    single line by its cap band within its line box — TOP (cap top to the box top),
    CENTER (cap band centred) or BOTTOM (baseline to the box bottom). Combined
    with a vertically-centred layer placement (.center(...) / .centerLeft(...)), a
    label dropped into a taller ShapeContainer / LayerStack "pill" sits where you
    ask instead of always on the font baseline — no compensating offset hacks.
    TextVerticalAlign.DEFAULT keeps the pre-1.7.0 baseline seating;
    the correction is derived from font metrics (ascent, descent, leading, cap
    height), not a magic number. Render-only and opt-in — existing layouts are
    byte-for-byte unchanged.
  • Bundled font: JetBrains Mono. New FontName.JETBRAINS_MONO (@since 1.7.0)
    joins the built-in DefaultFonts catalog with its Regular / Bold / Italic /
    Bold-Italic faces (bundled from the OFL-1.1 release), so monospaced code and
    data blocks render without a system-font install. Usable through any
    DocumentTextStyle.fontName(...) and listed in the font showcase.
  • Dashed and dotted lines. LineBuilder.dashed(double... pattern) /
    dashed() / dashed(DocumentDashPattern) plus the new value type
    com.demcha.compose.document.style.DocumentDashPattern (@since 1.7.0) make a
    line(...) paint an on/off dash instead of a solid stroke — section and résumé
    dividers, timeline connectors, cut-here rules. The pattern alternates paint-on
    and paint-off lengths in points (dashed(8, 5); dashed() is a balanced 3pt-on
    / 2pt-off; dashed(1, 4) reads as dotted). Carried on the line independently of
    DocumentStroke, so the stroke value stays a stable two-component record; lines
    are solid by default and the PDF backend honours the dash (other backends fall
    back to a solid stroke).
  • Semantic timelines. addTimeline(timeline -> ...) on every flow / section /
    module, plus TimelineBuilder, TimelineMarker and TimelineEntryBuilder
    (com.demcha.compose.document.dsl, @since 1.7.0), lay out a vertical timeline
    where each entry(marker, e -> e.title(...).meta(...).body(...)) pairs a marker
    with its content along a continuous connector rail — work history, project
    milestones, numbered process steps. Markers are TimelineMarker.dot, circle,
    numbered or square; the rail colour/width, gutter, entry spacing and default
    title / meta / body styles are all tunable. Declaring the marker-to-content
    relationship replaces the hand-placed bullet-plus-margin pattern; the timeline
    paginates between entries and a tall entry splits within itself, the rail
    continuing across the page break.
  • DocumentSession.availableHeight(). A one-call alias for
    canvas().innerHeight() (@since 1.7.0) — the usable page content height
    (page height minus top and bottom margins), the value a composition reads to
    decide how much vertical room a section, sidebar or spacer may fill.
    Previously reachable only through the layout-inspection facade.
  • headingBar(text, bar -> ...) — one-call filled title band. Every flow,
    section and module gains headingBar(String) and headingBar(String, Consumer<HeadingBarStyle>) (@since 1.7.0) on AbstractFlowBuilder: a
    filled, rounded heading band with a single label, added as a child above the
    body. HeadingBarStyle (com.demcha.compose.document.dsl) tunes fill, corner
    radius, padding, margin, label text style, alignment and an optional outline
    stroke, each with a sensible default (a light-grey band with a centred bold
    label), so bar -> bar.fill(brand).textStyle(white) is enough. Sugar over the
    softPanel(...).addParagraph(...) recipe — no new node type, just discoverable.

Fixed

  • position(node, dx, dy, align) offsets are now honored for stacks nested
    inside a fixed slot.
    A LayerStack / ShapeContainer placed inside a row
    column or another layer compiled through the fixed-slot stack path, which
    silently dropped the per-layer offsets (anchoring on alignment only) — so a
    positioned badge or cap could not be nudged from its anchor once nested, even
    though the same call worked at the document root. The nested path now feeds
    the same PreparedStackLayout offsets as the root path. Layout for documents
    that did not use position(...) inside a nested stack is unchanged.

Documentation

  • New recipe pages for page composition and font coverage (closing the
    docs/recipes/ discoverability gaps G2 / G6):
    page-backgrounds.md (PageBackgroundFill
    columns / bands / point-based fills / layering),
    layered-page-design.md (choosing
    between page backgrounds, rows, layer stacks and canvases),
    absolute-placement.md (addCanvas +
    position(x, y)), and font-coverage.md (WinAnsi
    limits, vs , and the inline-shape / bundled-font alternatives). Linked
    from the README recipes index, docs/README.md, and docs/recipes.md.

Build

  • Showcase website separated from documentation (docs/web/), now deployed
    via GitHub Actions.
    The static GitHub Pages site (index.html, styles.css,
    examples.js, the generated examples.json + showcase/ gallery assets,
    robots.txt, sitemap.xml, logo) moved out of docs/ — which previously had to
    host it because branch-based Pages can only serve repo-root or /docs — into a new
    top-level web/ folder, so docs/ now holds only documentation. A new
    deploy-web.yml publishes web/ to Pages from
    the "GitHub Actions" source; the old branch-/docs deploy-site.yml was
    removed. ShowcaseSync now writes web/showcase + web/examples.json,
    VersionConsistencyGuardTest reads web/index.html, and cut-release.ps1
    bumps / commits web/. The unused Next.js rebuild under site/ (added in v1.6.8
    but never deployed) was removed. Also renamed docs/SHOWCASE.mdweb/README.md.
    ⚠️ Action required before the next release reaches main: set
    Settings → Pages → Source = "GitHub Actions" — once the move lands on main,
    /docs no longer holds index.html, so a branch-/docs Pages source would 404.
    The live site is unaffected until then.

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track GraphCompose 2.0

Get notified when new releases ship.

Sign up free

About GraphCompose 2.0

All releases →

Related context

Related tools

Earlier breaking changes

  • v2.0.0 Package rename: layered template packages dropped `.v2` suffix; update imports.
  • v2.0.0 Removed dormant ECS engine internals (EntityManager, SystemECS, Entity components, render pipeline).
  • v2.0.0 Removed linkOptions() accessor; use linkTarget() and ExternalLinkTarget.options().
  • v2.0.0 Removed PDF‑typed document‑chrome overloads on DocumentSession; use backend‑neutral metadata/watermark/protect/header/footer.
  • v2.0.0 Removed DSL name-aliases DocumentSession.builder() and DocumentDsl.text(); use builder() and paragraph().

Beta — feedback welcome: [email protected]