Releases: DemchaAV/GraphCompose
GraphCompose v1.6.1 — Java 17 baseline + dependency refresh
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 from21to17acrosspom.xml,examples/pom.xml, andbenchmarks/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, Logback1.5.18 → 1.5.32, Lombok1.18.38 → 1.18.46, POI5.4.0 → 5.5.1, SnakeYAML2.4 → 2.6, AssertJ3.27.3 → 3.27.6, JUnit5.12.2 → 5.14.4, Mockito5.20.0 → 5.23.0. Adds explicit ByteBuddy1.18.7so 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
- Live showcase — install snippet, every example with a generated PDF preview, template gallery, feature examples.
- Templates v2 landing — 14 CV + 14 paired cover-letter presets.
- Migration v1.5 → v1.6 — for callers still on v1.5 (engine source-compatible to v1.6.x).
- CHANGELOG.md — full technical changelog.
Author intent, not coordinates.
GraphCompose v1.6.0 — expressive release
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 —CanvasLayerNodeis the controlled free-canvas primitive for when you do need coordinates, withClipPolicy.CLIP_BOUNDSclipping and atomic pagination. - Templates v2 ships 14 CV presets and 14 paired cover-letter presets, theme-driven via
BusinessTheme, with one-linercreate(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 —
@InternalAPI stability marker on engine internals, publicPdfFragmentRenderHandlerSPI for custom render handlers,DocumentRenderingExceptionwrapping the convenience render path sobuildPdf/writePdf/toPdfBytesno longer declarethrows 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
GenerateAllExamplesand 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
mismatchedPixelBudgetfor 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
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
ShapeContainerVisualRegressionTesttolerates the cross-platform
PDF font-rendering drift that surfaces between Windows-rendered
baselines and the Linux CI runner (~1-2% pixel diff), via a
calibratedmismatchedPixelBudgetinstead of bit-exact comparison.DocumentationCoverageTestno 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
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), andaddContainer(...)shortcuts
onAbstractFlowBuilderbuild aShapeContainerNodewhose bounding
box is dictated by aShapeOutline(Rectangle,
RoundedRectangle,Ellipse, plus acircle(diameter)factory).
Children are clipped via the newClipPolicyenum
(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-fallbackcapability warning.
ShapeContainerBuilderexposes the same nine-point alignment
vocabulary asLayerStackBuilderplusposition(node, dx, dy, anchor)for screen-space nudges. - Transforms (rotate / scale). New
com.demcha.compose.document.style.DocumentTransformvalue type
withrotate(deg),scale(uniform),scale(sx, sy)factories
pluswithRotation(...)/withScale(...)axis-preserving copies
and anisIdentity()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.Layerand shape-container
layers gainint zIndex(default0).
LayerStackBuilder.layer(node, align, zIndex)/
position(node, dx, dy, align, zIndex)and the matching
ShapeContainerBuilderoverloads let a layer declared earlier draw
on top of layers declared later. The layout compiler stable-sorts
layers before render; equalzIndexkeeps 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
nullto skip painting that parity. Existing entries in the
rowStylesmap (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 is0so 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
InvoiceTemplateV2is the cinematic invoice counterpart to
InvoiceTemplateV1. Two constructors: the no-arg form picks
BusinessTheme.modern(), the one-arg
InvoiceTemplateV2(BusinessTheme)accepts any theme. Hero
softPanelcarrying invoice number / dates / inline rich-text
status, a two-column row withFrom/Bill toparties, themed
line-items table withheaderStyle/ zebra / totals /
repeatHeader(), and a footer row withaccentLeftstrips on the
notes / payment-terms columns.ProposalTemplateV2is the proposal counterpart, sharing the
sameBusinessTheme-driven composition: hero panel rounded only on
the right (via the newDocumentCornerRadius.right(...)form),
themed executive-summary panel, sender / recipient parties row,
sections rendered throughtheme.text().h2()headings, a timeline
table (Phase / Duration / Details), and a pricing table (Item /
Description / Amount) withrepeatHeader(), zebra rows, and a
total-pricing row anchored at the bottom viatotalRow(...).CvTheme.fromBusinessTheme(BusinessTheme)static factory
derives a CV theme from a business theme (ADR 0002). The bridge
maps palette / text-scale slots intoprimaryColor/
secondaryColor/bodyColor/accentColor/headerFont/
bodyFont/ font sizes; CV-specific layout tokens (spacing,
moduleMargin,spacingModuleName) keep the existing CV defaults.
The ten existing CV templates andCvTemplateV1continue 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/accentBottomreplace the old
addLine(horizontal=innerWidth)separators around section banners,
andsoftPanel(...)collapses the
padding(asymmetric) + fillColor(...)cascade. InvoiceTemplateV1andProposalTemplateV1continue to ship
side-by-side. Authors who want the cinematic look opt in by
switching the type.
Public API — DSL ergonomics (Phase A)
LayerStackBuilderexposes nine alignment shortcuts (topLeft,
topCenter,topRight,centerLeft,center,centerRight,
bottomLeft,bottomCenter,bottomRight) on top ofback/
centerso authors do not need to remember the fullLayerAlign
enum.LayerStackBuilder.position(node, offsetX, offsetY, anchor)nudges
a layer from its anchor by an on-screen offset (positiveoffsetX
= right, positiveoffsetY= down).AbstractFlowBuildergains 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 tospacing(...).RowBuilder.add(node)validates the child type eagerly and
raisesIllegalArgumentExceptionfrom the offending call site
instead of deferring tobuild()and raising
IllegalStateExceptionlater.DocumentDsl.richText(Consumer<RichText>)is a new callback entry
point that builds aRichTextrun sequence in one fluent call.
Architecture
- New
NodeDefinition.emitOverlayFragments(...)hook complements the
existingemitFragments(...). 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 inPdfFixedLayoutBackend.defaultHandlers(). - New
PaginationPolicy.SHAPE_ATOMICdistinguishes shape-clipped
atomicity from bbox-onlyATOMICfor snapshots and render
handlers. Oversized containers raise the existing
AtomicNodeTooLargeExceptionwith the offending semantic name. TableLayoutSupportreplaces the per-rowcolSpan-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.
LogicalCellcarries 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.TableResolvedCellgainsdouble yOffset(eighth field). Spanning
cells use a NEGATIVE offset equal to the...
GraphCompose v1.4.1 — README guards hotfix
Documentation
- README rewrite for v1.4.0 dropped three structural sections (
## Table component,## Line primitive,## Architecture at a glance) that theDocumentationCoverageTestguards baseline. CI flagged the regression on themainbranch; 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.javanow wiresCinematicProposalFileExample.generate()into the orchestrator, so the runnable examples module produces all seven fixtures (includingproject-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
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
DocumentTableCellis now a 3-field record (lines,style,colSpan). The newcolSpan(int)factory pluswithColSpan(...)onTableCellContentlet one cell occupy several columns; sum-of-spans-per-row is validated byTableLayoutSupport. Border ownership and natural-width distribution understand spans (extra width is shared acrossautocolumns inside the span; an all-fixed span throws when it cannot fit). Renderer code is unchanged — spanned cells emit a singleTableResolvedCellwith the merged width.- new
LayerStackNode+LayerAlignprimitive 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 newAxis.STACKinCompositeLayoutSpecand acompileStackedLayerbranch inLayoutCompiler. DSL surface:LayerStackBuilderwithback(...),center(...),layer(node, align). DocumentSession.pageBackground(DocumentColor | Color)(and the matchingGraphCompose.DocumentBuildersetter) injects a full-canvasShapeFragmentPayloadat the start of every page. Combine withLayerStackNodefor cinematic hero pages without any backend changes.AbstractFlowBuildergains semantic shortcuts on every flow / section / module:band(color),softPanel(color)/softPanel(color, radius, padding), andaccentLeft / accentRight / accentTop / accentBottom(color, width). They reuse the existingfillColor,cornerRadius,padding, andDocumentBordersplumbing — the new methods are sugar for designer-style flows.RichTextfluent builder (document.dsl.RichText) plusParagraphBuilder.rich(...)/AbstractFlowBuilder.addRich(...)cover theStatus: Pendinglabel/value pattern in one expression:RichText.text("Status: ").bold("Pending").color("…", red).accent("…", brand). Includesplain / bold / italic / boldItalic / underline / strikethrough / color / accent / size / style / link / append.
Public API — design tokens
- new
com.demcha.compose.document.themepackage — entirely on top of public document-level types, no engine leaks.DocumentPalette— primary / accent / surface / surfaceMuted / textPrimary / textMuted / ruleSpacingScale— five-stepxs / sm / md / lg / xlwith monotonicity validation andinsetsXs() … insetsXl()helpersTextScale—h1 / h2 / h3 / body / caption / label / accentresolved stylesTablePreset—defaultCellStyle / headerStyle / totalRowStyle / zebraStyleBusinessTheme— 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 immutablewithName / withPageBackgroundforks
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 viaPdfRenderBridgeand compares against baselines undersrc/test/resources/visual-baselines. Approve mode (-Dgraphcompose.visual.approve=trueorGRAPHCOMPOSE_VISUAL_APPROVE=true) writes new baselines; comparison failures dropactual.pnganddiff.pngnext 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.STACKjoinsVERTICALandHORIZONTAL. The compiler dispatchesSTACKtocompileStackedLayer, which positions each child inside the stack box via per-layer alignment offsets and shares the samecompileNodeInFixedSlotplumbing rows already use.- table layout (
TableLayoutSupport, test-sideTableBuilder) was rewritten around a "logical cell" model: each authored cell is oneLogicalCell(startColumn, colSpan, content)resolved against astylesGrid[row][col]— the grid keeps existing border-ownership logic intact while letting render code keep emitting oneTableResolvedCellper logical cell. DocumentSession.layoutGraph()now wrapscompiler.compile(...)withwithPageBackgrounds(...)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),scalabilitysuite (1→16 threads, 13.8× speedup at 16), 50-threadstresstest (5,000 docs, 0 errors), and thecomparativetable 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
Highlights
- compose-first built-in template usage is now the documented default
- backend-neutral
DocumentComposerand 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.3remain usable - deprecated
render(...)template adapters are still available for compatibility - new docs and examples now prefer
compose(...)
v1.0.3 — First Public Release
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