feat(templates): add the ClassicInvoice layered invoice preset - #608
Merged
DemchaAV merged 2 commits intoAug 30, 2026
Merged
Conversation
The invoice family had a single preset. ClassicInvoice brings the letterhead-style look - header band, TOTAL DUE hero strip, BILL TO / FROM party columns, a dedicated Summary table after the line items, and a notes / payment-terms footer - onto the layered stack with the same create() / create(BrandTheme) contract as ModernInvoice, porting the rendered layout of the published standalone invoice-classic template. The preset reads its surfaces from BrandTheme.invoiceModern() and skips the Summary section when the spec carries no summary rows (the engine rejects zero-row tables). qa: a shared InvoicePresetFixtures feeds both the pixel gate and the new exact layout-snapshot gate (canonical single page plus a forty-line-item overflow freezing the two-page table continuation); ClassicInvoiceSmokeTest drives full layout and render, including the empty invoice. Examples gain ClassicInvoiceV2Example (invoice-classic-v2 in the showcase).
Every showcase document is either a committed preview under assets/readme/examples or deliberately unpublished; invoice-classic-v2 now ships its preview alongside the other invoice renders.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The layered invoice family ships a single preset —
ModernInvoice. The published standaloneinvoice-classictemplate (the letterhead-style invoice look) targets the removed 1.6.x surface (InvoiceTemplate,BusinessTheme), so on 2.x that look is unreachable through the built-in templates API: using it means vendoring two source files pinned tograph-compose:1.6.7.What changed
invoice/presets/ClassicInvoice— aDocumentTemplate<InvoiceDocumentSpec>viacreate()/create(BrandTheme), mirroring theModernInvoiceshape: header band (company name + 28pt INVOICE title), TOTAL DUE hero strip, BILL TO / FROM party columns, the line-items table, a dedicated Summary table composed after the line items (last row emphasized viatotalRow), and the notes / payment-terms footer. Long invoices paginate naturally with the table header repeated.BrandTheme.invoiceModern(), whose palette and Helvetica scale are value-identical to the ported template's theme. The deep-teal / gold inks and the geometry constants with no theme slot (17pt company name, 16pt flow spacing, 24pt page padding) stay preset-local: a newPaletteslot would change the record's public constructor, so promoting the shared colours waits for a compatible opening.InvoicePresetFixtures(canonical invoice + forty-line-item overflow) is the single fixture source for both gates.InvoiceV2VisualParityTestgains theclassic_invoicerow; the newClassicInvoiceLayoutSnapshotTestfreezes exact geometry for the canonical single page and the two-page overflow (page count asserted explicitly);ClassicInvoiceSmokeTestdrives full layout + render throughtoPdfBytes(), including the empty invoice. The overflow contract is guarded at snapshot level, where the comparison is exact on every platform — not with full-page pixel baselines, whose cross-platform drift exceeds what they would guard.ClassicInvoiceV2Examplerendersinvoice-classic-v2, wired intoGenerateAllExamplesand the showcase metadata. CHANGELOG entry underv2.2.3 — Planned / Templates.Verification
Full reactor gate (the eight CI modules,
./mvnw -B -ntp verify -pl :graph-compose-core,:graph-compose-render-pdf,:graph-compose-render-docx,:graph-compose-render-pptx,:graph-compose-templates,:graph-compose-testing,:graph-compose-qa,:graph-compose-coverage -am) → BUILD SUCCESS;javadoc:javadoc -pl :graph-compose-templates→ 0 warnings. Invoice qa suite 14/14 green (-f qa/pom.xml: 5 smoke ClassicInvoice, 5 smoke ModernInvoice, 2 snapshot, 2 pixel rows).Port parity, verified on one machine against the published template itself: the preset rendered with the template's own example fixture is pixel-identical to the template's approved preview — 0 / 2,173,720 differing pixels @150 DPI on the canonical page, and 0 on both pages of the forty-item overflow render; the resolved layout snapshots are node-identical (33/33 paths, zero geometry deltas > 0.01pt).
Notes
Four traits of the ported render are preserved deliberately and documented in the class Javadoc: the dark body-bold header ink on the deep-teal table-header fill; the TOTAL DUE strip carrying the invoice metadata (number, dates, status) rather than an amount;
InvoiceData.title()not being rendered (the header always reads INVOICE); and the Summary table shrink-wrapping to its own rows instead of stretching to the line-items width. Changing any of these is a redesign of the ported look, not part of this port, and belongs in its own PR.Lane: canonical (templates.invoice + qa + examples) — no engine changes.