From 3f639f68e0d7554f57ce218282ee08ba92767098 Mon Sep 17 00:00:00 2001
From: DemchaAV
@@ -56,7 +56,7 @@
public class ColdStartJmhBenchmark {
private InvoiceDocumentSpec invoice;
- private InvoiceTemplateV1 invoiceTemplate;
+ private DocumentTemplate invoiceTemplate;
private CvSpec cv;
private DocumentTemplate cvTemplate;
@@ -64,7 +64,7 @@ public class ColdStartJmhBenchmark {
@Setup
public void setUp() {
invoice = CanonicalBenchmarkSupport.canonicalInvoice();
- invoiceTemplate = new InvoiceTemplateV1();
+ invoiceTemplate = ModernInvoice.create();
cv = CanonicalBenchmarkSupport.canonicalCv();
cvTemplate = ModernProfessional.create(BusinessTheme.modern());
}
@@ -96,7 +96,7 @@ public byte[] coldEngineSimple() throws Exception {
}
/**
- * Cold first render of the canonical invoice through {@code InvoiceTemplateV1}.
+ * Cold first render of the canonical invoice through {@code ModernInvoice}.
*
* @return the rendered PDF bytes (consumed by JMH)
* @throws Exception if rendering fails
diff --git a/examples/README.md b/examples/README.md
index e87e62dbf..ef5a56285 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -127,10 +127,8 @@ are with the canonical DSL, then jump to its detailed section below.
| Example | What it shows | Preview · Source |
|---|---|---|
-| [Invoice (V1)](#invoice-v1) | `InvoiceTemplateV1` driven from `InvoiceDocumentSpec` — pre-rebuild surface, supported only | [PDF](../assets/readme/examples/invoice.pdf) · [Source](src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java) |
-| [Proposal (V1)](#proposal-v1) | `ProposalTemplateV1` driven from `ProposalDocumentSpec` — pre-rebuild surface, supported only | [PDF](../assets/readme/examples/proposal.pdf) · [Source](src/main/java/com/demcha/examples/templates/proposal/ProposalFileExample.java) |
| [Handcrafted Proposal](#handcrafted-proposal) | v1.4-style cinematic proposal composed by hand — pre-template authoring; kept for parity reference | [PDF](../assets/readme/examples/project-proposal-cinematic.pdf) · [Source](src/main/java/com/demcha/examples/templates/proposal/CinematicProposalFileExample.java) |
-| [Weekly schedule](#weekly-schedule) | Bar / restaurant shift schedule via `WeeklyScheduleRenderer` (`WeeklyScheduleTemplateV1` — no V2 yet; will be re-shaped before 2.0) | [PDF](../assets/readme/examples/weekly-schedule.pdf) · [Source](src/main/java/com/demcha/examples/templates/schedule/WeeklyScheduleFileExample.java) |
+| [Weekly schedule](#weekly-schedule) | Bar / restaurant shift schedule via `WeeklyScheduleRenderer` | [PDF](../assets/readme/examples/weekly-schedule.pdf) · [Source](src/main/java/com/demcha/examples/templates/schedule/WeeklyScheduleFileExample.java) |
---
@@ -177,45 +175,6 @@ try (DocumentSession document = GraphCompose.document(outputFile)
[📄 View PDF](../assets/readme/examples/cover-letter.pdf) ·
[📜 Full source](src/main/java/com/demcha/examples/templates/coverletter/CoverLetterFileExample.java)
-### Invoice (V1)
-
-`InvoiceTemplateV1.compose(document, spec)` handles the full layout —
-header band, parties row, line-items table, totals row, payment-terms
-footer — driven from a `InvoiceDocumentSpec`. Use this when you want
-the legacy hard-coded theme; for V2 cinematic, see below.
-
-```java
-InvoiceDocumentSpec spec = InvoiceDocumentSpec.builder()
- .invoiceNumber("GC-2026-041")
- .issueDate("02 Apr 2026")
- .dueDate("16 Apr 2026")
- .fromParty(p -> p.name("GraphCompose Studio"))
- .billToParty(p -> p.name("Northwind Systems"))
- .lineItem("Template architecture", "Reusable invoice flow", "2", "GBP 980", "GBP 1,960")
- .totalRow("Total", "GBP 1,960")
- .build();
-
-try (DocumentSession document = GraphCompose.document(outputFile)
- .pageSize(DocumentPageSize.A4)
- .margin(28, 28, 28, 28)
- .create()) {
- new InvoiceTemplateV1().compose(document, spec);
- document.buildPdf();
-}
-```
-
-[📄 View PDF](../assets/readme/examples/invoice.pdf) ·
-[📜 Full source](src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java)
-
-### Proposal (V1)
-
-`ProposalTemplateV1` rendered against a `ProposalDocumentSpec` —
-sections, scope items, deliverables, sign-off. Pairs naturally with
-`InvoiceTemplateV1` for consistent "spec → PDF" pipelines.
-
-[📄 View PDF](../assets/readme/examples/proposal.pdf) ·
-[📜 Full source](src/main/java/com/demcha/examples/templates/proposal/ProposalFileExample.java)
-
### Module-first profile
Authoring against `DocumentSession.pageFlow().module(...)` — no
diff --git a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java
index e566e387a..32b9a2aa5 100644
--- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java
+++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java
@@ -80,12 +80,10 @@
import com.demcha.examples.templates.cv.v2.CvSidebarPortraitExample;
import com.demcha.examples.templates.cv.v2.CvTimelineMinimalExample;
import com.demcha.examples.templates.invoice.InvoiceCinematicFileExample;
-import com.demcha.examples.templates.invoice.InvoiceFileExample;
import com.demcha.examples.templates.invoice.v2.ModernInvoiceV2Example;
import com.demcha.examples.templates.proposal.CinematicProposalFileExample;
import com.demcha.examples.templates.proposal.ProposalCinematicFileExample;
import com.demcha.examples.templates.proposal.v2.ModernProposalV2Example;
-import com.demcha.examples.templates.proposal.ProposalFileExample;
import com.demcha.examples.templates.schedule.WeeklyScheduleFileExample;
/**
@@ -138,12 +136,10 @@ public static void main(String[] args) throws Exception {
System.out.println("Generated: " + CvTimelineMinimalLetterV2Example.generate());
// Invoices
- System.out.println("Generated: " + InvoiceFileExample.generate());
System.out.println("Generated: " + InvoiceCinematicFileExample.generate());
System.out.println("Generated: " + ModernInvoiceV2Example.generate());
// Proposals
- System.out.println("Generated: " + ProposalFileExample.generate());
System.out.println("Generated: " + ProposalCinematicFileExample.generate());
System.out.println("Generated: " + ModernProposalV2Example.generate());
System.out.println("Generated: " + CinematicProposalFileExample.generate());
diff --git a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java
index 0eb173e60..c7649b02c 100644
--- a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java
+++ b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java
@@ -78,11 +78,9 @@ record Entry(String title, String description, List tags, String codeUrl
letter("cover-letter-mint-editorial-v2", "CvMintEditorialLetterV2Example", "Mint Editorial letter", "Letter paired with Mint Editorial CV — magazine-style mint accent.");
// ===== Templates / Invoice =====
- invoice("invoice", "Invoice (canonical)", "Single-page invoice with line items, totals, and structured chrome — InvoiceTemplateV1.", "invoice");
invoice("invoice-cinematic", "Cinematic Invoice", "Polished V2 invoice template with theme-driven layout, advanced tables, and totals.", "invoice", "cinematic");
// ===== Templates / Proposal =====
- proposal("proposal", "Proposal (canonical)", "Multi-section proposal with cover, scope, deliverables, and pricing — ProposalTemplateV1.", "proposal");
proposal("proposal-cinematic", "Cinematic Proposal", "Cinematic V2 proposal layout with cover panel, hero spread, and rich typography.", "proposal", "cinematic");
proposal("project-proposal-cinematic", "Project Proposal (cinematic)", "End-to-end project proposal with mountain hero, scope panels, and pricing summary.", "proposal", "cinematic");
@@ -183,16 +181,13 @@ private static void letter(String id, String exampleClass, String title, String
}
private static void invoice(String id, String title, String desc, String... tags) {
- String file = id.contains("cinematic") ? "InvoiceCinematicFileExample" : "InvoiceFileExample";
ENTRIES.put(id, entry(title, desc, withCategory("invoice", tags),
- EX_BASE + "/templates/invoice/" + file + ".java"));
+ EX_BASE + "/templates/invoice/InvoiceCinematicFileExample.java"));
}
private static void proposal(String id, String title, String desc, String... tags) {
- String file;
- if (id.equals("project-proposal-cinematic")) file = "CinematicProposalFileExample";
- else if (id.contains("cinematic")) file = "ProposalCinematicFileExample";
- else file = "ProposalFileExample";
+ String file = id.equals("project-proposal-cinematic")
+ ? "CinematicProposalFileExample" : "ProposalCinematicFileExample";
ENTRIES.put(id, entry(title, desc, withCategory("proposal", tags),
EX_BASE + "/templates/proposal/" + file + ".java"));
}
diff --git a/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java b/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java
deleted file mode 100644
index 6035e5e9e..000000000
--- a/examples/src/main/java/com/demcha/examples/templates/invoice/InvoiceFileExample.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.demcha.examples.templates.invoice;
-
-import com.demcha.compose.GraphCompose;
-import com.demcha.compose.document.api.DocumentPageSize;
-import com.demcha.compose.document.api.DocumentSession;
-import com.demcha.compose.document.templates.builtins.InvoiceTemplateV1;
-import com.demcha.examples.support.ExampleDataFactory;
-import com.demcha.examples.support.ExampleOutputPaths;
-
-import java.nio.file.Path;
-
-public final class InvoiceFileExample {
-
- private InvoiceFileExample() {
- }
-
- public static Path generate() throws Exception {
- Path outputFile = ExampleOutputPaths.prepare("templates/invoice", "invoice.pdf");
- InvoiceTemplateV1 template = new InvoiceTemplateV1();
-
- try (DocumentSession document = GraphCompose.document(outputFile)
- .pageSize(DocumentPageSize.A4)
- .margin(22, 22, 22, 22)
- .create()) {
- template.compose(document, ExampleDataFactory.sampleInvoice());
- 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/templates/proposal/ProposalFileExample.java b/examples/src/main/java/com/demcha/examples/templates/proposal/ProposalFileExample.java
deleted file mode 100644
index a4708830b..000000000
--- a/examples/src/main/java/com/demcha/examples/templates/proposal/ProposalFileExample.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.demcha.examples.templates.proposal;
-
-import com.demcha.compose.GraphCompose;
-import com.demcha.compose.document.api.DocumentPageSize;
-import com.demcha.compose.document.api.DocumentSession;
-import com.demcha.compose.document.templates.builtins.ProposalTemplateV1;
-import com.demcha.examples.support.ExampleDataFactory;
-import com.demcha.examples.support.ExampleOutputPaths;
-
-import java.nio.file.Path;
-
-public final class ProposalFileExample {
-
- private ProposalFileExample() {
- }
-
- public static Path generate() throws Exception {
- Path outputFile = ExampleOutputPaths.prepare("templates/proposal", "proposal.pdf");
- ProposalTemplateV1 template = new ProposalTemplateV1();
-
- try (DocumentSession document = GraphCompose.document(outputFile)
- .pageSize(DocumentPageSize.A4)
- .margin(22, 22, 22, 22)
- .create()) {
- template.compose(document, ExampleDataFactory.sampleProposal());
- document.buildPdf();
- }
-
- return outputFile;
- }
-
- public static void main(String[] args) throws Exception {
- System.out.println("Generated: " + generate());
- }
-}
diff --git a/src/main/java/com/demcha/compose/document/templates/api/InvoiceTemplate.java b/src/main/java/com/demcha/compose/document/templates/api/InvoiceTemplate.java
index 3ec59a3fb..2ea737906 100644
--- a/src/main/java/com/demcha/compose/document/templates/api/InvoiceTemplate.java
+++ b/src/main/java/com/demcha/compose/document/templates/api/InvoiceTemplate.java
@@ -11,7 +11,7 @@
* {@link DocumentSession}.
*
* {@code
- * InvoiceTemplate template = new InvoiceTemplateV1();
+ * InvoiceTemplate template = new InvoiceTemplateV2();
* InvoiceDocumentSpec invoice = InvoiceDocumentSpec.builder()
* .invoiceNumber("GC-2026-041")
* .fromParty(party -> party.name("GraphCompose Studio"))
diff --git a/src/main/java/com/demcha/compose/document/templates/api/ProposalTemplate.java b/src/main/java/com/demcha/compose/document/templates/api/ProposalTemplate.java
index 44e45a514..92d153c5a 100644
--- a/src/main/java/com/demcha/compose/document/templates/api/ProposalTemplate.java
+++ b/src/main/java/com/demcha/compose/document/templates/api/ProposalTemplate.java
@@ -11,7 +11,7 @@
* {@link DocumentSession}.
*
* {@code
- * ProposalTemplate template = new ProposalTemplateV1();
+ * ProposalTemplate template = new ProposalTemplateV2();
* ProposalDocumentSpec proposal = ProposalDocumentSpec.builder()
* .projectTitle("GraphCompose rollout")
* .section("Scope", "Introduce reusable invoice and proposal templates.")
diff --git a/src/main/java/com/demcha/compose/document/templates/builtins/InvoiceTemplateV1.java b/src/main/java/com/demcha/compose/document/templates/builtins/InvoiceTemplateV1.java
deleted file mode 100644
index 2f399cfd9..000000000
--- a/src/main/java/com/demcha/compose/document/templates/builtins/InvoiceTemplateV1.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.demcha.compose.document.templates.builtins;
-
-import com.demcha.compose.document.api.DocumentSession;
-import com.demcha.compose.document.templates.api.InvoiceTemplate;
-import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec;
-import com.demcha.compose.document.templates.support.business.BusinessDocumentSceneStyles;
-import com.demcha.compose.document.templates.support.business.InvoiceTemplateComposer;
-import com.demcha.compose.document.templates.support.common.SessionTemplateComposeTarget;
-import com.demcha.compose.document.templates.support.common.TemplateLifecycleLog;
-
-/**
- * Canonical implementation of the invoice template.
- */
-public final class InvoiceTemplateV1 implements InvoiceTemplate {
- private final InvoiceTemplateComposer composer = new InvoiceTemplateComposer(new BusinessDocumentSceneStyles());
-
- /**
- * Creates the canonical invoice template.
- */
- public InvoiceTemplateV1() {
- }
-
- @Override
- public String getTemplateId() {
- return "invoice-v1";
- }
-
- @Override
- public String getTemplateName() {
- return "Invoice V1";
- }
-
- @Override
- public String getDescription() {
- return "A light business invoice template with metadata, billing parties, line items, and totals.";
- }
-
- @Override
- public void compose(DocumentSession document, InvoiceDocumentSpec spec) {
- long startNanos = TemplateLifecycleLog.start(getTemplateId(), spec);
- try {
- composer.compose(new SessionTemplateComposeTarget(document), spec);
- TemplateLifecycleLog.success(getTemplateId(), spec, startNanos);
- } catch (RuntimeException | Error ex) {
- TemplateLifecycleLog.failure(getTemplateId(), spec, startNanos, ex);
- throw ex;
- }
- }
-}
diff --git a/src/main/java/com/demcha/compose/document/templates/builtins/ProposalTemplateV1.java b/src/main/java/com/demcha/compose/document/templates/builtins/ProposalTemplateV1.java
deleted file mode 100644
index 9190bbb45..000000000
--- a/src/main/java/com/demcha/compose/document/templates/builtins/ProposalTemplateV1.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.demcha.compose.document.templates.builtins;
-
-import com.demcha.compose.document.api.DocumentSession;
-import com.demcha.compose.document.templates.api.ProposalTemplate;
-import com.demcha.compose.document.templates.data.proposal.ProposalDocumentSpec;
-import com.demcha.compose.document.templates.support.business.BusinessDocumentSceneStyles;
-import com.demcha.compose.document.templates.support.business.ProposalTemplateComposer;
-import com.demcha.compose.document.templates.support.common.SessionTemplateComposeTarget;
-import com.demcha.compose.document.templates.support.common.TemplateLifecycleLog;
-
-/**
- * Canonical implementation of the proposal template.
- */
-public final class ProposalTemplateV1 implements ProposalTemplate {
- private final ProposalTemplateComposer composer = new ProposalTemplateComposer(new BusinessDocumentSceneStyles());
-
- /**
- * Creates the canonical proposal template.
- */
- public ProposalTemplateV1() {
- }
-
- @Override
- public String getTemplateId() {
- return "proposal-v1";
- }
-
- @Override
- public String getTemplateName() {
- return "Proposal V1";
- }
-
- @Override
- public String getDescription() {
- return "A light business proposal template with executive summary, scope, timeline, pricing, and acceptance terms.";
- }
-
- @Override
- public void compose(DocumentSession document, ProposalDocumentSpec spec) {
- long startNanos = TemplateLifecycleLog.start(getTemplateId(), spec);
- try {
- composer.compose(new SessionTemplateComposeTarget(document), spec);
- TemplateLifecycleLog.success(getTemplateId(), spec, startNanos);
- } catch (RuntimeException | Error ex) {
- TemplateLifecycleLog.failure(getTemplateId(), spec, startNanos, ex);
- throw ex;
- }
- }
-}
diff --git a/src/test/java/com/demcha/documentation/DocumentationExamplesTest.java b/src/test/java/com/demcha/documentation/DocumentationExamplesTest.java
index cd130f84d..14e8c749f 100644
--- a/src/test/java/com/demcha/documentation/DocumentationExamplesTest.java
+++ b/src/test/java/com/demcha/documentation/DocumentationExamplesTest.java
@@ -4,9 +4,9 @@
import com.demcha.compose.document.api.DocumentPageSize;
import com.demcha.compose.document.api.DocumentSession;
import com.demcha.compose.document.image.DocumentImageFitMode;
-import com.demcha.compose.document.templates.api.InvoiceTemplate;
-import com.demcha.compose.document.templates.builtins.InvoiceTemplateV1;
+import com.demcha.compose.document.templates.api.DocumentTemplate;
import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec;
+import com.demcha.compose.document.templates.invoice.v2.presets.ModernInvoice;
import com.demcha.compose.document.style.DocumentColor;
import com.demcha.compose.document.style.DocumentInsets;
import com.demcha.compose.document.style.DocumentStroke;
@@ -163,7 +163,7 @@ void shouldRenderModuleFirstDslExampleToFile() throws Exception {
@Test
void shouldRenderComposeFirstBuiltInTemplateExampleToFile() throws Exception {
Path outputFile = VisualTestOutputs.preparePdf("compose-first-invoice-template", "clean", "documentation");
- InvoiceTemplate template = new InvoiceTemplateV1();
+ DocumentTemplate template = ModernInvoice.create();
try (DocumentSession document = GraphCompose.document(outputFile)
.pageSize(DocumentPageSize.A4)
From 515228b1a503d5db79973025572735945d98278a Mon Sep 17 00:00:00 2001
From: DemchaAV
Date: Mon, 29 Jun 2026 21:58:20 +0100
Subject: [PATCH 2/2] fix(benchmarks): trim canonical proposal table cells to
fit A4
ModernProposal sizes its timeline and pricing description columns to their
content with no wrap, so once the proposal benchmark moved onto the v2
preset the long-form fixture overflowed the page ("Table ProposalTimeline
width 826.5 exceeds available 551.3") and failed the perf-smoke run.
Shorten the timeline and pricing-row descriptions in the benchmark proposal
fixture so both tables fit A4 at the benchmark's 22pt margin. The long
section paragraphs that drive the multi-page pagination workload are
unchanged, so the benchmark still exercises long content. longProposal()
has no other live consumer (canonicalProposalData() is uncalled; the v2
parity test builds its own data).
Tests: ./mvnw -f benchmarks/pom.xml -DskipTests exec:java
-Dexec.mainClass=com.demcha.compose.CurrentSpeedBenchmark
-Dgraphcompose.benchmark.profile=smoke now renders invoice/cv/proposal
(BUILD SUCCESS); engine verify + benchmark gates green.
---
.../java/com/demcha/mock/ProposalDataFixtures.java | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/test/java/com/demcha/mock/ProposalDataFixtures.java b/src/test/java/com/demcha/mock/ProposalDataFixtures.java
index 0e8d06b3d..458dd3331 100644
--- a/src/test/java/com/demcha/mock/ProposalDataFixtures.java
+++ b/src/test/java/com/demcha/mock/ProposalDataFixtures.java
@@ -47,14 +47,14 @@ public static ProposalData longProposal() {
repeatSentence("Weekly checkpoints will focus on visual deltas, example ergonomics, and whether the generated outputs are understandable for both library consumers and internal maintainers.", 4),
repeatSentence("Each milestone includes a review of template polish, code readability, and whether the generated examples are strong enough to support README and demo usage.", 4)))),
List.of(
- new ProposalTimelineItem("Week 1", "5 days", "API alignment, DTO modeling, invoice template implementation, and review-ready render tests."),
- new ProposalTimelineItem("Week 2", "5 days", "Proposal template implementation, long-content pagination verification, and examples module setup."),
- new ProposalTimelineItem("Week 3", "3 days", "README refresh, final PDF review, and delivery handoff package.")),
+ new ProposalTimelineItem("Week 1", "5 days", "API alignment, DTO modeling, render tests."),
+ new ProposalTimelineItem("Week 2", "5 days", "Proposal template + pagination checks."),
+ new ProposalTimelineItem("Week 3", "3 days", "README refresh, final review, handoff.")),
List.of(
- new ProposalPricingRow("Foundation", "Public template APIs, DTOs, and cleanup of devtool production scope.", "GBP 3,200", false),
- new ProposalPricingRow("Template delivery", "Invoice and proposal layouts with render tests and polished defaults.", "GBP 4,450", false),
- new ProposalPricingRow("Examples package", "Runnable examples module, sample data, and usage documentation.", "GBP 1,850", false),
- new ProposalPricingRow("Total investment", "Fixed-price delivery for the agreed scope.", "GBP 9,500", true)),
+ new ProposalPricingRow("Foundation", "Public template APIs and cleanup.", "GBP 3,200", false),
+ new ProposalPricingRow("Template delivery", "Invoice and proposal layouts + tests.", "GBP 4,450", false),
+ new ProposalPricingRow("Examples package", "Runnable examples module + docs.", "GBP 1,850", false),
+ new ProposalPricingRow("Total investment", "Fixed-price delivery, agreed scope.", "GBP 9,500", true)),
List.of(
"Proposal pricing is valid until the stated expiration date.",
"Any additional template families beyond the agreed four examples are scoped separately.",