diff --git a/CHANGELOG.md b/CHANGELOG.md index 693f4b206..984bb6b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,35 @@ follow semantic versioning; release dates are ISO 8601. ### Templates +- **A paginating invoice preset: `PaymentsInvoice`.** A lavender-and-navy sheet with a + diagonal band crossing the masthead, a half-split issuer and metadata header, two + addressed parties on discs, a marked service-line table, a settlement row pairing bank + details against the totals, a note block and a two-cell document footer. Ships as + `invoice.presets.PaymentsInvoice` on the existing `StructuredInvoiceData` model, + porting the rendered layout of a published standalone template. It is the first preset + in the family built to **flow**: the design shows six service lines and a real billing + month brings dozens, so the table's header repeats on every page it reaches, a + continuation page reserves a deeper bottom margin than page one, and every page + carries its number — a financial record that runs over has to make a missing page + detectable. The table is one column, not five: the design shows an outer box and a + rule between rows with no interior verticals, which is exactly what a single-column + table draws, and the five columns are a row inside each cell. Building it the other + way round would leave every continuation page ending in an empty bordered strip, + because a section's box fills its page fragment rather than hugging its rows. The + lockup beside the title is the caller's: the design's mark occupies a measured + 136 × 55.3 px box, a document that brings a logo has it drawn to that height, and one + that brings only a name has the name set as a wordmark — the templates artifact + carries no mark of its own. Figures are written with the locale stated rather than + inherited, because both the grouping of a number and the symbol for a currency code + change with it and a preset that let the JVM decide would render a different sheet on + a different machine. Guarded by a smoke test (including the unknown-mark data error, a + line with no mark, the wordmark fallback, the currency named once and carried, the + uppercased due line, a trunk prefix printed but not dialled, the support contacts as + annotations, a missing ship-to and a missing note block, an empty document, and a + thirty-line invoice that runs on, repeats its column names and numbers its pages), an + exact layout snapshot and a pixel-parity gate; the examples showcase gains + `invoice-payments-v2`. + - **A navy-plate CV preset: `MidnightNavy`.** A one-page sheet on a full-height navy plate: an outlined monogram over a two-weight name and a tracked role line, then contact, education, metered skills and dotted languages down the plate, beside a diff --git a/assets/readme/examples/invoice-payments-v2.pdf b/assets/readme/examples/invoice-payments-v2.pdf new file mode 100644 index 000000000..ef189d1d9 Binary files /dev/null and b/assets/readme/examples/invoice-payments-v2.pdf differ diff --git a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java index 9a7049ba2..54f713e62 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -109,6 +109,7 @@ import com.demcha.examples.templates.invoice.ClassicInvoiceV2Example; import com.demcha.examples.templates.invoice.ConsultingInvoiceV2Example; import com.demcha.examples.templates.invoice.LumaStudioInvoiceV2Example; +import com.demcha.examples.templates.invoice.PaymentsInvoiceV2Example; import com.demcha.examples.templates.invoice.InvoiceCinematicFileExample; import com.demcha.examples.templates.invoice.ModernInvoiceV2Example; import com.demcha.examples.templates.proposal.CinematicProposalFileExample; @@ -190,6 +191,7 @@ public static void main(String[] args) throws Exception { System.out.println("Generated: " + ClassicInvoiceV2Example.generate()); System.out.println("Generated: " + ConsultingInvoiceV2Example.generate()); System.out.println("Generated: " + LumaStudioInvoiceV2Example.generate()); + System.out.println("Generated: " + PaymentsInvoiceV2Example.generate()); // Proposals System.out.println("Generated: " + ProposalCinematicFileExample.generate()); diff --git a/examples/src/main/java/com/demcha/examples/support/PaymentsInvoiceSampleData.java b/examples/src/main/java/com/demcha/examples/support/PaymentsInvoiceSampleData.java new file mode 100644 index 000000000..3f786c57e --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/support/PaymentsInvoiceSampleData.java @@ -0,0 +1,156 @@ +package com.demcha.examples.support; + +import com.demcha.compose.document.templates.data.invoice.InvoiceBrand; +import com.demcha.compose.document.templates.data.invoice.InvoiceContactBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceMasthead; +import com.demcha.compose.document.templates.data.invoice.InvoiceNotesBlock; +import com.demcha.compose.document.templates.data.invoice.InvoicePaymentBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceRecipient; +import com.demcha.compose.document.templates.data.invoice.InvoiceServiceLines; +import com.demcha.compose.document.templates.data.invoice.InvoiceSummaryBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceTotalsBlock; +import com.demcha.compose.document.templates.data.invoice.StructuredInvoiceData; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Shared sample data for the Payments invoice example. + * + *

Kept in lockstep with the qa module's {@code PaymentsInvoiceFixtures} — the + * two modules cannot share a source file, so a content change here belongs + * there too.

+ */ +public final class PaymentsInvoiceSampleData { + + private PaymentsInvoiceSampleData() { + } + + /** + * The sample invoice — seven metadata rows, two addressed parties, six + * marked service lines, seven payment fields, two summed rows and a + * two-line note. + * + * @return the document the example renders + */ + public static StructuredInvoiceData sample() { + return new StructuredInvoiceData( + brand(), supplier(), masthead(), billTo(), shipTo(), + new InvoiceSummaryBlock("", "", ""), + serviceLines(), totals(), payment(), notes(), "GBP"); + } + + /** No logo: the wordmark is what a document without one falls back to. */ + private static InvoiceBrand brand() { + return new InvoiceBrand(null, "Meridian", "", "", "", ""); + } + + private static InvoiceContactBlock supplier() { + return new InvoiceContactBlock( + "Meridian Payments Ltd.", + List.of("1 Harbour Exchange", "Canary Wharf", "London E14 9GE", + "United Kingdom"), + "", "", "", + "", "Company No. 09048900", + "", "VAT No. GB 123 4567 89"); + } + + private static InvoiceMasthead masthead() { + List entries = new ArrayList<>(); + entries.add(new InvoiceMasthead.Entry("Invoice Number", "INV-2025-05-10472", false)); + entries.add(new InvoiceMasthead.Entry("Invoice Date", "10 May 2025", false)); + entries.add(new InvoiceMasthead.Entry("Billing Period", "1 - 30 April 2025", false)); + entries.add(new InvoiceMasthead.Entry("Account ID", "acct_4Q8xTb2mKp", false)); + entries.add(new InvoiceMasthead.Entry("Payment Terms", "Net 30", false)); + entries.add(new InvoiceMasthead.Entry("Due Date", "14 June 2025", false)); + entries.add(new InvoiceMasthead.Entry("Status", "UNPAID", true)); + return new InvoiceMasthead("INVOICE", entries); + } + + /** The subline is where a billed party's registration goes on this sheet. */ + private static InvoiceRecipient billTo() { + return new InvoiceRecipient("BILL TO", "Northwind Ltd.", + "VAT No. GB 987 6543 21", + List.of("Attention: Finance Team", "42 Bridgewater Street", + "Manchester M15 4QT", "United Kingdom"), + "", ""); + } + + private static InvoiceRecipient shipTo() { + return new InvoiceRecipient("SHIP TO", "Northwind Ltd.", "", + List.of("Operations Centre", "7 Kingsway Park", + "Leeds LS12 6BD", "United Kingdom"), + "", ""); + } + + private static InvoiceServiceLines serviceLines() { + InvoiceServiceLines.Columns columns = new InvoiceServiceLines.Columns( + "", "DESCRIPTION", "", "QUANTITY", "UNIT PRICE", "AMOUNT", "VAT"); + List lines = new ArrayList<>(); + lines.add(line(1, "Subscription Billing", + "Subscription management and recurring billing", + "1", "250.00", "250.00", "20%", "card")); + lines.add(line(2, "Payment Processing", + "Online card payments processing (volume tier)", + "1", "1,180.00", "1,180.00", "20%", "card-settings")); + lines.add(line(3, "Fraud Screening", + "Advanced fraud screening and rules", + "1", "320.00", "320.00", "20%", "shield")); + lines.add(line(4, "Company Formation", + "Incorporation and compliance filings", + "1", "180.00", "180.00", "20%", "globe")); + lines.add(line(5, "In-Person Terminals", + "Terminal rental and firmware updates", + "4", "39.00", "156.00", "20%", "mobile")); + lines.add(line(6, "Priority Support", + "Named engineer, one-hour response", + "1", "100.00", "100.00", "20%", "headset")); + return new InvoiceServiceLines(columns, lines); + } + + private static InvoiceServiceLines.Line line(int number, String title, String description, + String quantity, String unitPrice, + String amount, String vat, String icon) { + return new InvoiceServiceLines.Line(number, title, description, "", + decimal(quantity), "", decimal(unitPrice), decimal(amount), vat, icon); + } + + private static InvoiceTotalsBlock totals() { + List rows = new ArrayList<>(); + rows.add(new InvoiceTotalsBlock.Row("Subtotal (excl. VAT)", decimal("2,186.00"))); + rows.add(new InvoiceTotalsBlock.Row("VAT at 20%", decimal("437.20"))); + return new InvoiceTotalsBlock(rows, "TOTAL DUE", decimal("2,623.20")); + } + + private static InvoicePaymentBlock payment() { + List fields = new ArrayList<>(); + fields.add(new InvoicePaymentBlock.Field("Bank Name", "Harbour Bank plc")); + fields.add(new InvoicePaymentBlock.Field("Account Name", "Meridian Payments Ltd.")); + fields.add(new InvoicePaymentBlock.Field("Sort Code", "40-02-50")); + fields.add(new InvoicePaymentBlock.Field("Account Number", "71295408")); + fields.add(new InvoicePaymentBlock.Field("IBAN", "GB29 HRBK 4002 5071 2954 08")); + fields.add(new InvoicePaymentBlock.Field("SWIFT / BIC", "HRBKGB2L")); + fields.add(new InvoicePaymentBlock.Field("Reference", "INV-2025-05-10472")); + return new InvoicePaymentBlock("PAYMENT DETAILS", fields, + "Please include the invoice number as payment reference.", + "Due by 14 June 2025", + "Payment due by 14 June 2025", + "", + "Questions? We're here to help."); + } + + private static InvoiceNotesBlock notes() { + return new InvoiceNotesBlock("NOTES", + List.of("Thank you for your business. This invoice covers services and " + + "fees for the period stated above.", + "Late payment may incur interest at the statutory rate."), + "support@meridianpayments.example", + "+44 (0) 20 3966 1900"); + } + + /** A printed figure back to a number: the grouping comes off. */ + private static BigDecimal decimal(String printed) { + return new BigDecimal(printed.replace(",", "")); + } +} 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 554b07dc5..98e18d416 100644 --- a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java +++ b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java @@ -99,6 +99,7 @@ record Entry(String title, String description, List tags, String codeUrl invoice("invoice-cinematic", "InvoiceCinematicFileExample", "Cinematic Invoice", "Layered ModernInvoice preset with theme-driven layout, advanced tables, and totals.", "invoice", "cinematic"); invoice("invoice-consulting-v2", "v2/ConsultingInvoiceV2Example", "Consulting Invoice", "The ConsultingInvoice preset on the structured invoice model — brand lockup with the caller's logo, labelled masthead metadata, priced service lines with service periods, a totals stack and bank payment fields.", "invoice"); invoice("invoice-luma-studio-v2", "v2/LumaStudioInvoiceV2Example", "Luma Studio Invoice", "The LumaStudioInvoice preset on the structured invoice model — a cream sidebar carrying the brand lockup and its ornament, a billed-to / shipped-to pair, priced service lines with a VAT column, and the notes and bank details above a sign-off band.", "invoice"); + invoice("invoice-payments-v2", "v2/PaymentsInvoiceV2Example", "Payments Invoice", "The PaymentsInvoice preset on the structured invoice model — a diagonal band crossing the masthead, a half-split issuer and metadata header, two addressed parties, marked service lines that repeat their header across pages, and a settlement row pairing bank details against the totals.", "invoice"); invoice("invoice-modern-v2", "v2/ModernInvoiceV2Example", "Modern Invoice", "The ModernInvoice preset composed straight from an InvoiceDocumentSpec — line items, totals and payment block driven by the BrandTheme rather than per-document styling.", "invoice"); invoice("invoice-classic-v2", "v2/ClassicInvoiceV2Example", "Classic Invoice", "The ClassicInvoice preset — letterhead header band, TOTAL DUE hero strip, BILL TO / FROM columns, and a dedicated Summary table after the line items.", "invoice"); diff --git a/examples/src/main/java/com/demcha/examples/templates/invoice/v2/PaymentsInvoiceV2Example.java b/examples/src/main/java/com/demcha/examples/templates/invoice/v2/PaymentsInvoiceV2Example.java new file mode 100644 index 000000000..278ac8ad1 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/templates/invoice/v2/PaymentsInvoiceV2Example.java @@ -0,0 +1,54 @@ +package com.demcha.examples.templates.invoice; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.data.invoice.StructuredInvoiceData; +import com.demcha.compose.document.templates.invoice.presets.PaymentsInvoice; +import com.demcha.examples.support.ExampleOutputPaths; +import com.demcha.examples.support.PaymentsInvoiceSampleData; + +import java.nio.file.Path; + +/** + * Renders the layered {@code invoice.v2} Payments preset against the design + * sample. + * + *

Output: + * {@code examples/target/generated-pdfs/templates/invoice/invoice-payments-v2.pdf}.

+ * + *

The preset owns its page geometry and its pagination — the table's header + * repeats and a continuation page reserves a deeper bottom margin — so the + * session starts unconfigured. The sample brings no logo, so the lockup box + * beside the title is filled with the brand's name as a wordmark.

+ */ +public final class PaymentsInvoiceV2Example { + + private PaymentsInvoiceV2Example() { + } + + /** + * @return absolute path of the rendered PDF + * @throws Exception if rendering fails + */ + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare("templates/invoice", + "invoice-payments-v2.pdf"); + StructuredInvoiceData data = PaymentsInvoiceSampleData.sample(); + DocumentTemplate template = PaymentsInvoice.create(); + + try (DocumentSession document = GraphCompose.document(outputFile).create()) { + template.compose(document, data); + document.buildPdf(); + } + return outputFile; + } + + /** + * @param args ignored + * @throws Exception if rendering fails + */ + public static void main(String[] args) throws Exception { + System.out.println("Generated: " + generate()); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceFixtures.java b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceFixtures.java new file mode 100644 index 000000000..8ee074ca7 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceFixtures.java @@ -0,0 +1,154 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.templates.data.invoice.InvoiceBrand; +import com.demcha.compose.document.templates.data.invoice.InvoiceContactBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceMasthead; +import com.demcha.compose.document.templates.data.invoice.InvoiceNotesBlock; +import com.demcha.compose.document.templates.data.invoice.InvoicePaymentBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceRecipient; +import com.demcha.compose.document.templates.data.invoice.InvoiceServiceLines; +import com.demcha.compose.document.templates.data.invoice.InvoiceSummaryBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceTotalsBlock; +import com.demcha.compose.document.templates.data.invoice.StructuredInvoiceData; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Shared fixture data for the {@link PaymentsInvoice} gates — the SAME document + * feeds the pixel parity test and the layout snapshot test, so a geometry shift + * the pixel budget absorbs still trips the exact snapshot, and vice versa. + */ +final class PaymentsInvoiceFixtures { + + private PaymentsInvoiceFixtures() { + } + + /** + * The canonical one-page invoice — seven metadata rows, two addressed + * parties, six marked service lines, seven payment fields, two summed rows + * and a two-line note. + * + * @return the document + */ + static StructuredInvoiceData canonicalInvoice() { + return new StructuredInvoiceData( + brand(), supplier(), masthead(), billTo(), shipTo(), + new InvoiceSummaryBlock("", "", ""), + serviceLines(), totals(), payment(), notes(), "GBP"); + } + + /** No logo: the wordmark is what a document without one falls back to. */ + static InvoiceBrand brand() { + return new InvoiceBrand(null, "Meridian", "", "", "", ""); + } + + static InvoiceContactBlock supplier() { + return new InvoiceContactBlock( + "Meridian Payments Ltd.", + List.of("1 Harbour Exchange", "Canary Wharf", "London E14 9GE", + "United Kingdom"), + "", "", "", + "", "Company No. 09048900", + "", "VAT No. GB 123 4567 89"); + } + + static InvoiceMasthead masthead() { + List entries = new ArrayList<>(); + entries.add(new InvoiceMasthead.Entry("Invoice Number", "INV-2025-05-10472", false)); + entries.add(new InvoiceMasthead.Entry("Invoice Date", "10 May 2025", false)); + entries.add(new InvoiceMasthead.Entry("Billing Period", "1 - 30 April 2025", false)); + entries.add(new InvoiceMasthead.Entry("Account ID", "acct_4Q8xTb2mKp", false)); + entries.add(new InvoiceMasthead.Entry("Payment Terms", "Net 30", false)); + entries.add(new InvoiceMasthead.Entry("Due Date", "14 June 2025", false)); + entries.add(new InvoiceMasthead.Entry("Status", "UNPAID", true)); + return new InvoiceMasthead("INVOICE", entries); + } + + /** The subline is where a billed party's registration goes on this sheet. */ + static InvoiceRecipient billTo() { + return new InvoiceRecipient("BILL TO", "Northwind Ltd.", + "VAT No. GB 987 6543 21", + List.of("Attention: Finance Team", "42 Bridgewater Street", + "Manchester M15 4QT", "United Kingdom"), + "", ""); + } + + static InvoiceRecipient shipTo() { + return new InvoiceRecipient("SHIP TO", "Northwind Ltd.", "", + List.of("Operations Centre", "7 Kingsway Park", + "Leeds LS12 6BD", "United Kingdom"), + "", ""); + } + + static InvoiceServiceLines serviceLines() { + InvoiceServiceLines.Columns columns = new InvoiceServiceLines.Columns( + "", "DESCRIPTION", "", "QUANTITY", "UNIT PRICE", "AMOUNT", "VAT"); + List lines = new ArrayList<>(); + lines.add(line(1, "Subscription Billing", + "Subscription management and recurring billing", + "1", "250.00", "250.00", "20%", "card")); + lines.add(line(2, "Payment Processing", + "Online card payments processing (volume tier)", + "1", "1,180.00", "1,180.00", "20%", "card-settings")); + lines.add(line(3, "Fraud Screening", + "Advanced fraud screening and rules", + "1", "320.00", "320.00", "20%", "shield")); + lines.add(line(4, "Company Formation", + "Incorporation and compliance filings", + "1", "180.00", "180.00", "20%", "globe")); + lines.add(line(5, "In-Person Terminals", + "Terminal rental and firmware updates", + "4", "39.00", "156.00", "20%", "mobile")); + lines.add(line(6, "Priority Support", + "Named engineer, one-hour response", + "1", "100.00", "100.00", "20%", "headset")); + return new InvoiceServiceLines(columns, lines); + } + + private static InvoiceServiceLines.Line line(int number, String title, String description, + String quantity, String unitPrice, + String amount, String vat, String icon) { + return new InvoiceServiceLines.Line(number, title, description, "", + decimal(quantity), "", decimal(unitPrice), decimal(amount), vat, icon); + } + + static InvoiceTotalsBlock totals() { + List rows = new ArrayList<>(); + rows.add(new InvoiceTotalsBlock.Row("Subtotal (excl. VAT)", decimal("2,186.00"))); + rows.add(new InvoiceTotalsBlock.Row("VAT at 20%", decimal("437.20"))); + return new InvoiceTotalsBlock(rows, "TOTAL DUE", decimal("2,623.20")); + } + + static InvoicePaymentBlock payment() { + List fields = new ArrayList<>(); + fields.add(new InvoicePaymentBlock.Field("Bank Name", "Harbour Bank plc")); + fields.add(new InvoicePaymentBlock.Field("Account Name", "Meridian Payments Ltd.")); + fields.add(new InvoicePaymentBlock.Field("Sort Code", "40-02-50")); + fields.add(new InvoicePaymentBlock.Field("Account Number", "71295408")); + fields.add(new InvoicePaymentBlock.Field("IBAN", "GB29 HRBK 4002 5071 2954 08")); + fields.add(new InvoicePaymentBlock.Field("SWIFT / BIC", "HRBKGB2L")); + fields.add(new InvoicePaymentBlock.Field("Reference", "INV-2025-05-10472")); + return new InvoicePaymentBlock("PAYMENT DETAILS", fields, + "Please include the invoice number as payment reference.", + "Due by 14 June 2025", + "Payment due by 14 June 2025", + "", + "Questions? We're here to help."); + } + + static InvoiceNotesBlock notes() { + return new InvoiceNotesBlock("NOTES", + List.of("Thank you for your business. This invoice covers services and " + + "fees for the period stated above.", + "Late payment may incur interest at the statutory rate."), + "support@meridianpayments.example", + "+44 (0) 20 3966 1900"); + } + + /** A printed figure back to a number: the grouping comes off. */ + private static BigDecimal decimal(String printed) { + return new BigDecimal(printed.replace(",", "")); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceLayoutSnapshotTest.java b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceLayoutSnapshotTest.java new file mode 100644 index 000000000..b97d53c9b --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceLayoutSnapshotTest.java @@ -0,0 +1,33 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.TemplateTestSupport; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Exact layout snapshot gate for {@link PaymentsInvoice} — freezes the resolved + * geometry of the canonical one-page invoice, which the pixel budget cannot + * see: a small column or spacing shift stays under the visual-diff budget but + * changes the snapshot. + * + *

The preset owns its page geometry, so the session starts unconfigured.

+ * + *

Refresh with {@code -Dgraphcompose.updateSnapshots=true} after a + * deliberate layout change, and commit the JSON with the change.

+ */ +class PaymentsInvoiceLayoutSnapshotTest { + + @Test + void canonicalInvoiceMatchesLayoutSnapshot() throws Exception { + try (DocumentSession session = GraphCompose.document().create()) { + PaymentsInvoice.create().compose(session, + PaymentsInvoiceFixtures.canonicalInvoice()); + assertThat(session.layoutSnapshot().totalPages()).as("total pages").isEqualTo(1); + TemplateTestSupport.assertCanonicalSnapshot( + session, "payments_invoice_layout", "invoice"); + } + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceSmokeTest.java b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceSmokeTest.java new file mode 100644 index 000000000..aed6a269d --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceSmokeTest.java @@ -0,0 +1,268 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.data.invoice.InvoiceBrand; +import com.demcha.compose.document.templates.data.invoice.InvoiceNotesBlock; +import com.demcha.compose.document.templates.data.invoice.InvoicePaymentBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceRecipient; +import com.demcha.compose.document.templates.data.invoice.InvoiceServiceLines; +import com.demcha.compose.document.templates.data.invoice.InvoiceSummaryBlock; +import com.demcha.compose.document.templates.data.invoice.StructuredInvoiceData; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; +import org.apache.pdfbox.text.PDFTextStripper; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Smoke test for {@link PaymentsInvoice} — proves the preset renders a + * {@link StructuredInvoiceData} end-to-end with its packaged marks, falls back + * from a logo to a wordmark, writes its figures with the locale stated rather + * than inherited, reports an unknown mark as a data error, repeats the table + * header when the lines run past a page, and carries the support contacts as + * link annotations, which move no pixel and no layout node so neither gate + * would notice them going missing. + */ +class PaymentsInvoiceSmokeTest { + + private static byte[] render(StructuredInvoiceData data) throws Exception { + // The preset owns its page geometry, so the session starts unconfigured. + try (DocumentSession session = GraphCompose.document().create()) { + PaymentsInvoice.create().compose(session, data); + assertThat(session.roots()).isNotEmpty(); + byte[] pdfBytes = session.toPdfBytes(); + assertThat(pdfBytes).isNotEmpty(); + return pdfBytes; + } + } + + private static String textOf(byte[] pdfBytes) throws Exception { + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + return new PDFTextStripper().getText(document); + } + } + + private static int pagesOf(StructuredInvoiceData data) throws Exception { + try (DocumentSession session = GraphCompose.document().create()) { + PaymentsInvoice.create().compose(session, data); + return session.layoutSnapshot().totalPages(); + } + } + + private static List linkTargets(byte[] pdfBytes) throws Exception { + List targets = new ArrayList<>(); + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + for (PDPage page : document.getPages()) { + for (PDAnnotation annotation : page.getAnnotations()) { + if (annotation instanceof PDAnnotationLink link + && link.getAction() instanceof PDActionURI uri) { + targets.add(uri.getURI()); + } + } + } + } + return targets; + } + + /** The canonical invoice with its service lines replaced. */ + private static StructuredInvoiceData withLines(InvoiceServiceLines lines) { + StructuredInvoiceData base = PaymentsInvoiceFixtures.canonicalInvoice(); + return new StructuredInvoiceData(base.brand(), base.supplier(), base.masthead(), + base.billTo(), base.shipTo(), base.summary(), lines, base.totals(), + base.payment(), base.notes(), base.currencyCode()); + } + + @Test + void exposesStableIdentity() { + DocumentTemplate template = PaymentsInvoice.create(); + assertThat(template.id()).isEqualTo(PaymentsInvoice.ID); + assertThat(template.displayName()).isEqualTo(PaymentsInvoice.DISPLAY_NAME); + } + + @Test + void rendersCanonicalInvoiceWithPackagedMarks() throws Exception { + render(PaymentsInvoiceFixtures.canonicalInvoice()); + } + + @Test + void canonicalRenderCarriesEveryBlocksText() throws Exception { + String text = textOf(render(PaymentsInvoiceFixtures.canonicalInvoice())); + assertThat(text) + .contains("INVOICE") + .contains("Meridian Payments Ltd.") + .contains("Company No. 09048900") + .contains("VAT No. GB 123 4567 89") + .contains("Invoice Number") + .contains("BILL TO") + .contains("SHIP TO") + .contains("Northwind Ltd.") + .contains("Subscription Billing") + .contains("PAYMENT DETAILS") + .contains("GB29 HRBK 4002 5071 2954 08") + .contains("TOTAL DUE") + .contains("NOTES") + .contains("Questions? We're here to help."); + } + + @Test + void aDocumentWithNoLogoFallsBackToItsName() throws Exception { + // The preset carries no mark of its own, so a document that brings only + // a name still gets a lockup rather than an empty box. + assertThat(textOf(render(PaymentsInvoiceFixtures.canonicalInvoice()))) + .contains("Meridian"); + } + + @Test + void figuresAreWrittenWithTheCurrencyStatedOnceAndCarried() throws Exception { + // The column names the currency; each figure carries its symbol. Both + // come from the ISO code rather than from a formatted string. + String text = textOf(render(PaymentsInvoiceFixtures.canonicalInvoice())); + assertThat(text).contains("(GBP)").contains("£2,623.20").contains("£1,180.00"); + } + + @Test + void theDueLineIsSetInCapitalsWhateverCaseTheDocumentUses() throws Exception { + // The design sets the footer's due line in capitals, so the preset is + // what uppercases it rather than the document having to. + assertThat(textOf(render(PaymentsInvoiceFixtures.canonicalInvoice()))) + .contains("PAYMENT DUE BY 14 JUNE 2025"); + } + + @Test + void theSupportContactsAreClickable() throws Exception { + List targets = linkTargets(render(PaymentsInvoiceFixtures.canonicalInvoice())); + assertThat(targets) + .contains("mailto:support@meridianpayments.example") + .contains("tel:+442039661900"); + } + + @Test + void aTrunkPrefixIsPrintedButNotDialled() throws Exception { + // "(0)" is for a domestic dialler; left in the digits it would make a + // number that reaches nobody. The fixture's own number carries one, so + // the sheet prints it and the annotation drops it. + byte[] pdfBytes = render(PaymentsInvoiceFixtures.canonicalInvoice()); + // The stripper's own spacing is not the design's, so the printed form + // is compared without it. + assertThat(textOf(pdfBytes).replaceAll("(?U)\\s", "")) + .contains("+44(0)2039661900"); + assertThat(linkTargets(pdfBytes)) + .contains("tel:+442039661900") + .doesNotContain("tel:+4402039661900"); + } + + @Test + void anUnknownMarkIsReportedAsADataError() { + InvoiceServiceLines wrong = new InvoiceServiceLines( + PaymentsInvoiceFixtures.serviceLines().columns(), + List.of(new InvoiceServiceLines.Line(1, "Telepathy", "Reading minds", "", + BigDecimal.ONE, "", BigDecimal.TEN, BigDecimal.TEN, "20%", "telescope"))); + + assertThatThrownBy(() -> render(withLines(wrong))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("telescope") + .hasMessageContaining("shield"); + } + + @Test + void aLineWithoutAMarkIsDrawnWithoutOne() throws Exception { + InvoiceServiceLines unmarked = new InvoiceServiceLines( + PaymentsInvoiceFixtures.serviceLines().columns(), + List.of(new InvoiceServiceLines.Line(1, "Consulting", "A day of it", "", + BigDecimal.ONE, "", new BigDecimal("1200"), new BigDecimal("1200"), + "20%"))); + + assertThat(textOf(render(withLines(unmarked)))).contains("Consulting"); + } + + @Test + void aLongerInvoiceRunsOnAndRepeatsItsTableHeader() throws Exception { + // The table is the region that grows, so the sheet flows rather than + // refusing — and a continuation page has to say what its columns are. + List many = new ArrayList<>(); + for (int index = 1; index <= 30; index++) { + many.add(new InvoiceServiceLines.Line(index, "Service " + index, + "A line of description for it", "", + BigDecimal.ONE, "", new BigDecimal("120"), new BigDecimal("120"), + "20%", "card")); + } + StructuredInvoiceData longer = withLines(new InvoiceServiceLines( + PaymentsInvoiceFixtures.serviceLines().columns(), many)); + + assertThat(pagesOf(longer)).isGreaterThan(1); + byte[] pdfBytes = render(longer); + assertThat(textOf(pdfBytes)).contains("Service 30"); + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + PDFTextStripper stripper = new PDFTextStripper(); + stripper.setStartPage(2); + stripper.setEndPage(2); + assertThat(stripper.getText(document)) + .as("the second page names its columns") + .contains("DESCRIPTION") + .contains("UNIT PRICE"); + } + } + + @Test + void everyPageCarriesItsNumber() throws Exception { + List many = new ArrayList<>(); + for (int index = 1; index <= 30; index++) { + many.add(new InvoiceServiceLines.Line(index, "Service " + index, "", "", + BigDecimal.ONE, "", new BigDecimal("120"), new BigDecimal("120"), + "20%", "card")); + } + String text = textOf(render(withLines(new InvoiceServiceLines( + PaymentsInvoiceFixtures.serviceLines().columns(), many)))); + assertThat(text).contains("Page 1 of").contains("Page 2 of"); + } + + @Test + void anInvoiceWithNoShipToDrawsOneParty() throws Exception { + StructuredInvoiceData base = PaymentsInvoiceFixtures.canonicalInvoice(); + StructuredInvoiceData single = new StructuredInvoiceData(base.brand(), base.supplier(), + base.masthead(), base.billTo(), + new InvoiceRecipient("", "", "", List.of(), "", ""), + base.summary(), base.serviceLines(), base.totals(), base.payment(), + base.notes(), base.currencyCode()); + + String text = textOf(render(single)); + assertThat(text).contains("BILL TO").doesNotContain("SHIP TO"); + } + + @Test + void anInvoiceWithNoNotesDropsTheBlockRatherThanLeavingItsDisc() throws Exception { + StructuredInvoiceData base = PaymentsInvoiceFixtures.canonicalInvoice(); + StructuredInvoiceData bare = new StructuredInvoiceData(base.brand(), base.supplier(), + base.masthead(), base.billTo(), base.shipTo(), base.summary(), + base.serviceLines(), base.totals(), base.payment(), + new InvoiceNotesBlock("NOTES", List.of(), "ops@example.test", ""), + base.currencyCode()); + + String text = textOf(render(bare)); + assertThat(text).doesNotContain("Late payment may incur interest"); + assertThat(linkTargets(render(bare))).contains("mailto:ops@example.test"); + } + + @Test + void anEmptyDocumentStillRenders() throws Exception { + // Every block absent: the preset draws its furniture and nothing else, + // rather than failing on the first missing field. + render(new StructuredInvoiceData(new InvoiceBrand(null, "", "", ""), null, null, + null, null, new InvoiceSummaryBlock("", "", ""), + new InvoiceServiceLines(null, List.of()), null, + new InvoicePaymentBlock("", List.of(), "", "", "", "", ""), + new InvoiceNotesBlock("", List.of(), "", ""), "")); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceVisualParityTest.java b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceVisualParityTest.java new file mode 100644 index 000000000..9b6046383 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoiceVisualParityTest.java @@ -0,0 +1,41 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.testing.visual.PdfVisualRegression; +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; + +/** + * Pixel-diff visual parity gate for {@link PaymentsInvoice} — the canonical + * one-page invoice against a checked-in baseline. + * + *

Re-blessing baselines — after a deliberate visual change, + * re-run with {@code -Dgraphcompose.visual.approve=true} and commit the updated + * PNG with the change.

+ */ +class PaymentsInvoiceVisualParityTest { + + private static final Path BASELINE_ROOT = Path.of( + "src", "test", "resources", "visual-baselines", "invoice-v2-layered"); + + private static final long PIXEL_DIFF_BUDGET = 50_000L; + private static final int PER_PIXEL_TOLERANCE = 8; + + @Test + void rendersWithinPixelDiffBudget() throws Exception { + byte[] pdfBytes; + try (DocumentSession document = GraphCompose.document().create()) { + PaymentsInvoice.create().compose(document, + PaymentsInvoiceFixtures.canonicalInvoice()); + pdfBytes = document.toPdfBytes(); + } + + PdfVisualRegression.standard() + .baselineRoot(BASELINE_ROOT) + .perPixelTolerance(PER_PIXEL_TOLERANCE) + .mismatchedPixelBudget(PIXEL_DIFF_BUDGET) + .assertMatchesBaseline("payments_invoice", pdfBytes); + } +} diff --git a/qa/src/test/resources/layout-snapshots/canonical-templates/invoice/payments_invoice_layout.json b/qa/src/test/resources/layout-snapshots/canonical-templates/invoice/payments_invoice_layout.json new file mode 100644 index 000000000..ffcebc4f9 --- /dev/null +++ b/qa/src/test/resources/layout-snapshots/canonical-templates/invoice/payments_invoice_layout.json @@ -0,0 +1,5357 @@ +{ + "formatVersion" : "2.0", + "canvas" : { + "pageWidth" : 595.276, + "pageHeight" : 841.89, + "innerWidth" : 537.159, + "innerHeight" : 808.035, + "margin" : { + "top" : 15.799, + "right" : 29.341, + "bottom" : 18.056, + "left" : 28.776 + } + }, + "totalPages" : 1, + "nodes" : [ { + "path" : "PaymentsInvoice[0]", + "entityName" : "PaymentsInvoice", + "entityKind" : "ContainerNode", + "parentPath" : null, + "childIndex" : 0, + "depth" : 1, + "layer" : 1, + "computedX" : 28.776, + "computedY" : 34.229, + "placementX" : 28.776, + "placementY" : 34.229, + "placementWidth" : 537.159, + "placementHeight" : 791.862, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 791.862, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]", + "entityName" : "MastheadStack", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 0, + "depth" : 2, + "layer" : 2, + "computedX" : 28.776, + "computedY" : 622.964, + "placementX" : 28.776, + "placementY" : 622.964, + "placementWidth" : 537.159, + "placementHeight" : 203.127, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 203.127, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]", + "entityName" : "HeaderBand", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 217.459, + "computedY" : 638.763, + "placementX" : 217.459, + "placementY" : 638.763, + "placementWidth" : 96.26, + "placementHeight" : 203.127, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 96.26, + "contentHeight" : 203.127, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]/HeaderBandLayers[0]", + "entityName" : "HeaderBandLayers", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 217.459, + "computedY" : 638.763, + "placementX" : 217.459, + "placementY" : 638.763, + "placementWidth" : 96.26, + "placementHeight" : 203.127, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 96.26, + "contentHeight" : 203.127, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]/HeaderBandLayers[0]/BandLavender[0]", + "entityName" : "BandLavender", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]/HeaderBandLayers[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 217.459, + "computedY" : 638.763, + "placementX" : 217.459, + "placementY" : 638.763, + "placementWidth" : 96.26, + "placementHeight" : 203.127, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 96.26, + "contentHeight" : 203.127, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]/HeaderBandLayers[0]/BandNavy[1]", + "entityName" : "BandNavy", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderBand[0]/HeaderBandLayers[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 217.459, + "computedY" : 638.763, + "placementX" : 217.459, + "placementY" : 638.763, + "placementWidth" : 96.26, + "placementHeight" : 203.127, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 96.26, + "contentHeight" : 203.127, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/AccentBarHolder[1]", + "entityName" : "AccentBarHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 16.363, + "computedY" : 651.176, + "placementX" : 16.363, + "placementY" : 651.176, + "placementWidth" : 1.128, + "placementHeight" : 174.915, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 1.128, + "contentHeight" : 174.915, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/AccentBarHolder[1]/HeaderAccentBar[0]", + "entityName" : "HeaderAccentBar", + "entityKind" : "LineNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/AccentBarHolder[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 16.363, + "computedY" : 651.176, + "placementX" : 16.363, + "placementY" : 651.176, + "placementWidth" : 1.128, + "placementHeight" : 174.915, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 1.128, + "contentHeight" : 174.915, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]", + "entityName" : "HeaderContent", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]", + "childIndex" : 2, + "depth" : 3, + "layer" : 3, + "computedX" : 28.776, + "computedY" : 646.402, + "placementX" : 28.776, + "placementY" : 646.402, + "placementWidth" : 537.159, + "placementHeight" : 179.689, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 179.689, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]", + "entityName" : "Masthead", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 28.776, + "computedY" : 770.962, + "placementX" : 28.776, + "placementY" : 770.962, + "placementWidth" : 537.159, + "placementHeight" : 44.973, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 44.973, + "margin" : { + "top" : 10.156, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]/BrandLockup[0]", + "entityName" : "BrandLockup", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 28.776, + "computedY" : 789.713, + "placementX" : 28.776, + "placementY" : 789.713, + "placementWidth" : 94.178, + "placementHeight" : 26.222, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 94.178, + "contentHeight" : 26.222, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 6.771 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]/BrandLockup[0]/BrandWordmark[0]", + "entityName" : "BrandWordmark", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]/BrandLockup[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 35.547, + "computedY" : 789.713, + "placementX" : 35.547, + "placementY" : 789.713, + "placementWidth" : 87.407, + "placementHeight" : 26.222, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 87.407, + "contentHeight" : 26.222, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]/InvoiceTitle[1]", + "entityName" : "InvoiceTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/Masthead[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 360.551, + "computedY" : 770.962, + "placementX" : 360.551, + "placementY" : 770.962, + "placementWidth" : 149.064, + "placementHeight" : 43.575, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 149.064, + "contentHeight" : 43.575, + "margin" : { + "top" : 1.397, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]", + "entityName" : "HeaderSplit", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 28.776, + "computedY" : 646.402, + "placementX" : 28.776, + "placementY" : 646.402, + "placementWidth" : 537.159, + "placementHeight" : 119.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 119.2, + "margin" : { + "top" : 5.36, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]", + "entityName" : "IssuerBlock", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 28.776, + "computedY" : 664.414, + "placementX" : 28.776, + "placementY" : 664.414, + "placementWidth" : 127.249, + "placementHeight" : 101.188, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 127.249, + "contentHeight" : 101.188, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 6.207 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerName[0]", + "entityName" : "IssuerName", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 34.983, + "computedY" : 751.882, + "placementX" : 34.983, + "placementY" : 751.882, + "placementWidth" : 121.042, + "placementHeight" : 13.72, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 121.042, + "contentHeight" : 13.72, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]", + "entityName" : "IssuerAddress", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 34.983, + "computedY" : 700.333, + "placementX" : 34.983, + "placementY" : 700.333, + "placementWidth" : 78.364, + "placementHeight" : 46.576, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 78.364, + "contentHeight" : 46.576, + "margin" : { + "top" : 4.972, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]/IssuerAddressLine1[0]", + "entityName" : "IssuerAddressLine1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 34.983, + "computedY" : 736.439, + "placementX" : 34.983, + "placementY" : 736.439, + "placementWidth" : 78.364, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 78.364, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]/IssuerAddressLine2[1]", + "entityName" : "IssuerAddressLine2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 34.983, + "computedY" : 724.404, + "placementX" : 34.983, + "placementY" : 724.404, + "placementWidth" : 53.766, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 53.766, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]/IssuerAddressLine3[2]", + "entityName" : "IssuerAddressLine3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]", + "childIndex" : 2, + "depth" : 7, + "layer" : 7, + "computedX" : 34.983, + "computedY" : 712.369, + "placementX" : 34.983, + "placementY" : 712.369, + "placementWidth" : 65.118, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 65.118, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]/IssuerAddressLine4[3]", + "entityName" : "IssuerAddressLine4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerAddress[1]", + "childIndex" : 3, + "depth" : 7, + "layer" : 7, + "computedX" : 34.983, + "computedY" : 700.333, + "placementX" : 34.983, + "placementY" : 700.333, + "placementWidth" : 62.544, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 62.544, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerRegistration[2]", + "entityName" : "IssuerRegistration", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 34.983, + "computedY" : 664.414, + "placementX" : 34.983, + "placementY" : 664.414, + "placementWidth" : 99.383, + "placementHeight" : 22.32, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 99.383, + "contentHeight" : 22.32, + "margin" : { + "top" : 13.6, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerRegistration[2]/IssuerRegistrationLine1[0]", + "entityName" : "IssuerRegistrationLine1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerRegistration[2]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 34.983, + "computedY" : 676.263, + "placementX" : 34.983, + "placementY" : 676.263, + "placementWidth" : 95.186, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 95.186, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerRegistration[2]/IssuerRegistrationLine2[1]", + "entityName" : "IssuerRegistrationLine2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/IssuerBlock[0]/IssuerRegistration[2]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 34.983, + "computedY" : 664.414, + "placementX" : 34.983, + "placementY" : 664.414, + "placementWidth" : 99.383, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 99.383, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]", + "entityName" : "InvoiceMeta", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 360.551, + "computedY" : 646.402, + "placementX" : 360.551, + "placementY" : 646.402, + "placementWidth" : 205.384, + "placementHeight" : 119.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 205.384, + "contentHeight" : 119.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 6.207, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]", + "entityName" : "MetaGridLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 360.551, + "computedY" : 646.402, + "placementX" : 360.551, + "placementY" : 646.402, + "placementWidth" : 205.384, + "placementHeight" : 112.993, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 205.384, + "contentHeight" : 112.993, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]", + "entityName" : "MetaGridHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 360.551, + "computedY" : 646.402, + "placementX" : 360.551, + "placementY" : 646.402, + "placementWidth" : 205.384, + "placementHeight" : 112.993, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 205.384, + "contentHeight" : 112.993, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]", + "entityName" : "MetaGrid", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 360.551, + "computedY" : 646.402, + "placementX" : 360.551, + "placementY" : 646.402, + "placementWidth" : 205.384, + "placementHeight" : 112.993, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 205.384, + "contentHeight" : 112.993, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "entityName" : "MetaLabels", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 360.551, + "computedY" : 646.402, + "placementX" : 360.551, + "placementY" : 646.402, + "placementWidth" : 64.446, + "placementHeight" : 112.993, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 64.446, + "contentHeight" : 112.993, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel1[0]", + "entityName" : "MetaLabel1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 748.541, + "placementX" : 360.551, + "placementY" : 748.541, + "placementWidth" : 64.446, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 64.446, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel2[1]", + "entityName" : "MetaLabel2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 1, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 731.518, + "placementX" : 360.551, + "placementY" : 731.518, + "placementWidth" : 51.123, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 51.123, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel3[2]", + "entityName" : "MetaLabel3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 2, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 714.495, + "placementX" : 360.551, + "placementY" : 714.495, + "placementWidth" : 53.574, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 53.574, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel4[3]", + "entityName" : "MetaLabel4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 3, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 697.471, + "placementX" : 360.551, + "placementY" : 697.471, + "placementWidth" : 45.542, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 45.542, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel5[4]", + "entityName" : "MetaLabel5", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 4, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 680.448, + "placementX" : 360.551, + "placementY" : 680.448, + "placementWidth" : 63.234, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 63.234, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel6[5]", + "entityName" : "MetaLabel6", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 5, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 663.425, + "placementX" : 360.551, + "placementY" : 663.425, + "placementWidth" : 38.686, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 38.686, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]/MetaLabel7[6]", + "entityName" : "MetaLabel7", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaLabels[0]", + "childIndex" : 6, + "depth" : 10, + "layer" : 10, + "computedX" : 360.551, + "computedY" : 646.402, + "placementX" : 360.551, + "placementY" : 646.402, + "placementWidth" : 25.371, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 25.371, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "entityName" : "MetaValues", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 440.955, + "computedY" : 646.402, + "placementX" : 440.955, + "placementY" : 646.402, + "placementWidth" : 88.384, + "placementHeight" : 112.993, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 88.384, + "contentHeight" : 112.993, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 13.26 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue1[0]", + "entityName" : "MetaValue1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 749.614, + "placementX" : 454.215, + "placementY" : 749.614, + "placementWidth" : 75.124, + "placementHeight" : 9.781, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 75.124, + "contentHeight" : 9.781, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue2[1]", + "entityName" : "MetaValue2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 1, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 732.591, + "placementX" : 454.215, + "placementY" : 732.591, + "placementWidth" : 48.366, + "placementHeight" : 9.781, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 48.366, + "contentHeight" : 9.781, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue3[2]", + "entityName" : "MetaValue3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 2, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 715.568, + "placementX" : 454.215, + "placementY" : 715.568, + "placementWidth" : 61.407, + "placementHeight" : 9.781, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 61.407, + "contentHeight" : 9.781, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue4[3]", + "entityName" : "MetaValue4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 3, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 698.545, + "placementX" : 454.215, + "placementY" : 698.545, + "placementWidth" : 69.345, + "placementHeight" : 9.781, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 69.345, + "contentHeight" : 9.781, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue5[4]", + "entityName" : "MetaValue5", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 4, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 681.522, + "placementX" : 454.215, + "placementY" : 681.522, + "placementWidth" : 24.998, + "placementHeight" : 9.781, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 24.998, + "contentHeight" : 9.781, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue6[5]", + "entityName" : "MetaValue6", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 5, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 664.498, + "placementX" : 454.215, + "placementY" : 664.498, + "placementWidth" : 49.385, + "placementHeight" : 9.781, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 49.385, + "contentHeight" : 9.781, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]/MetaValue7[6]", + "entityName" : "MetaValue7", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/MastheadStack[0]/HeaderContent[2]/HeaderSplit[1]/InvoiceMeta[1]/MetaGridLayer[0]/MetaGridHolder[0]/MetaGrid[0]/MetaValues[1]", + "childIndex" : 6, + "depth" : 10, + "layer" : 10, + "computedX" : 454.215, + "computedY" : 646.402, + "placementX" : 454.215, + "placementY" : 646.402, + "placementWidth" : 35.149, + "placementHeight" : 10.854, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 35.149, + "contentHeight" : 10.854, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/HeaderRule[1]", + "entityName" : "HeaderRule", + "entityKind" : "LineNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 1, + "depth" : 2, + "layer" : 2, + "computedX" : 16.927, + "computedY" : 638.198, + "placementX" : 16.927, + "placementY" : 638.198, + "placementWidth" : 557.471, + "placementHeight" : 1.128, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 557.471, + "contentHeight" : 1.128, + "margin" : { + "top" : -16.363, + "right" : -8.464, + "bottom" : 9.592, + "left" : -11.849 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]", + "entityName" : "PartiesRow", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 2, + "depth" : 2, + "layer" : 2, + "computedX" : 28.776, + "computedY" : 522.368, + "placementX" : 28.776, + "placementY" : 522.368, + "placementWidth" : 537.159, + "placementHeight" : 106.238, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 106.238, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]", + "entityName" : "Party1", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.776, + "computedY" : 522.368, + "placementX" : 28.776, + "placementY" : 522.368, + "placementWidth" : 268.579, + "placementHeight" : 106.238, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 268.579, + "contentHeight" : 106.238, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 5.078 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]", + "entityName" : "PartyHeadLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 33.855, + "computedY" : 606.036, + "placementX" : 33.855, + "placementY" : 606.036, + "placementWidth" : 263.501, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 263.501, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]", + "entityName" : "PartyHeadHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 33.855, + "computedY" : 606.036, + "placementX" : 33.855, + "placementY" : 606.036, + "placementWidth" : 263.501, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 263.501, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]", + "entityName" : "PartyHead", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 33.855, + "computedY" : 606.036, + "placementX" : 33.855, + "placementY" : 606.036, + "placementWidth" : 263.501, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 263.501, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]", + "entityName" : "PartyDisc", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 33.855, + "computedY" : 606.036, + "placementX" : 33.855, + "placementY" : 606.036, + "placementWidth" : 22.57, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]", + "entityName" : "Discbill-to", + "entityKind" : "ShapeContainerNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 33.855, + "computedY" : 606.036, + "placementX" : 33.855, + "placementY" : 606.036, + "placementWidth" : 22.57, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]", + "entityName" : "Glyphbill-to", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 33.855, + "computedY" : 609.422, + "placementX" : 33.855, + "placementY" : 609.422, + "placementWidth" : 22.57, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]/Align[0]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 33.855, + "computedY" : 609.422, + "placementX" : 33.855, + "placementY" : 609.422, + "placementWidth" : 22.57, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]/Align[0]/SvgIcon[0]", + "entityName" : "SvgIcon", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]/Align[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 37.24, + "computedY" : 609.422, + "placementX" : 37.24, + "placementY" : 609.422, + "placementWidth" : 15.799, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 15.799, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]/Align[0]/SvgIcon[0]/SvgLayer0[0]", + "entityName" : "SvgLayer0", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discbill-to[0]/Glyphbill-to[0]/Align[0]/SvgIcon[0]", + "childIndex" : 0, + "depth" : 12, + "layer" : 12, + "computedX" : 37.24, + "computedY" : 609.422, + "placementX" : 37.24, + "placementY" : 609.422, + "placementWidth" : 15.799, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 15.799, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyLabel[1]", + "entityName" : "PartyLabel", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 66.581, + "computedY" : 613.055, + "placementX" : 66.581, + "placementY" : 613.055, + "placementWidth" : 31.052, + "placementHeight" : 10.217, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 31.052, + "contentHeight" : 10.217, + "margin" : { + "top" : 5.334, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]", + "entityName" : "PartyBody", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 33.855, + "computedY" : 522.368, + "placementX" : 33.855, + "placementY" : 522.368, + "placementWidth" : 132.109, + "placementHeight" : 83.668, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 132.109, + "contentHeight" : 83.668, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 32.726 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyName[0]", + "entityName" : "PartyName", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 66.581, + "computedY" : 593.759, + "placementX" : 66.581, + "placementY" : 593.759, + "placementWidth" : 69.419, + "placementHeight" : 12.278, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 69.419, + "contentHeight" : 12.278, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]", + "entityName" : "PartyAddress", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 66.581, + "computedY" : 544.374, + "placementX" : 66.581, + "placementY" : 544.374, + "placementWidth" : 94.506, + "placementHeight" : 45.51, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 94.506, + "contentHeight" : 45.51, + "margin" : { + "top" : 3.875, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]/PartyAddressLine1[0]", + "entityName" : "PartyAddressLine1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 66.581, + "computedY" : 579.413, + "placementX" : 66.581, + "placementY" : 579.413, + "placementWidth" : 94.506, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 94.506, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]/PartyAddressLine2[1]", + "entityName" : "PartyAddressLine2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 66.581, + "computedY" : 567.733, + "placementX" : 66.581, + "placementY" : 567.733, + "placementWidth" : 85.039, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 85.039, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]/PartyAddressLine3[2]", + "entityName" : "PartyAddressLine3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 66.581, + "computedY" : 556.054, + "placementX" : 66.581, + "placementY" : 556.054, + "placementWidth" : 85.553, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 85.553, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]/PartyAddressLine4[3]", + "entityName" : "PartyAddressLine4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 3, + "depth" : 6, + "layer" : 6, + "computedX" : 66.581, + "computedY" : 544.374, + "placementX" : 66.581, + "placementY" : 544.374, + "placementWidth" : 62.544, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 62.544, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]/PartySubline[2]", + "entityName" : "PartySubline", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party1[0]/PartyBody[1]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 66.581, + "computedY" : 522.368, + "placementX" : 66.581, + "placementY" : 522.368, + "placementWidth" : 99.383, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 99.383, + "contentHeight" : 10.471, + "margin" : { + "top" : 11.535, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]", + "entityName" : "Party2", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 297.356, + "computedY" : 544.374, + "placementX" : 297.356, + "placementY" : 544.374, + "placementWidth" : 268.579, + "placementHeight" : 84.232, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 268.579, + "contentHeight" : 84.232, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 22.57 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]", + "entityName" : "PartyHeadLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 319.925, + "computedY" : 606.036, + "placementX" : 319.925, + "placementY" : 606.036, + "placementWidth" : 246.01, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 246.01, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]", + "entityName" : "PartyHeadHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 319.925, + "computedY" : 606.036, + "placementX" : 319.925, + "placementY" : 606.036, + "placementWidth" : 246.01, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 246.01, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]", + "entityName" : "PartyHead", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 319.925, + "computedY" : 606.036, + "placementX" : 319.925, + "placementY" : 606.036, + "placementWidth" : 246.01, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 246.01, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]", + "entityName" : "PartyDisc", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 319.925, + "computedY" : 606.036, + "placementX" : 319.925, + "placementY" : 606.036, + "placementWidth" : 22.57, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]", + "entityName" : "Discship-to", + "entityKind" : "ShapeContainerNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 319.925, + "computedY" : 606.036, + "placementX" : 319.925, + "placementY" : 606.036, + "placementWidth" : 22.57, + "placementHeight" : 22.57, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 22.57, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]", + "entityName" : "Glyphship-to", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 319.925, + "computedY" : 609.422, + "placementX" : 319.925, + "placementY" : 609.422, + "placementWidth" : 22.57, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]/Align[0]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 319.925, + "computedY" : 609.422, + "placementX" : 319.925, + "placementY" : 609.422, + "placementWidth" : 22.57, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.57, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]/Align[0]/SvgIcon[0]", + "entityName" : "SvgIcon", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]/Align[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 323.311, + "computedY" : 609.422, + "placementX" : 323.311, + "placementY" : 609.422, + "placementWidth" : 15.799, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 15.799, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]/Align[0]/SvgIcon[0]/SvgLayer0[0]", + "entityName" : "SvgLayer0", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyDisc[0]/Discship-to[0]/Glyphship-to[0]/Align[0]/SvgIcon[0]", + "childIndex" : 0, + "depth" : 12, + "layer" : 12, + "computedX" : 323.311, + "computedY" : 609.422, + "placementX" : 323.311, + "placementY" : 609.422, + "placementWidth" : 15.799, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 15.799, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]/PartyLabel[1]", + "entityName" : "PartyLabel", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyHeadLayer[0]/PartyHeadHolder[0]/PartyHead[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 352.651, + "computedY" : 613.055, + "placementX" : 352.651, + "placementY" : 613.055, + "placementWidth" : 33.095, + "placementHeight" : 10.217, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 33.095, + "contentHeight" : 10.217, + "margin" : { + "top" : 5.334, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]", + "entityName" : "PartyBody", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 319.925, + "computedY" : 544.374, + "placementX" : 319.925, + "placementY" : 544.374, + "placementWidth" : 104.004, + "placementHeight" : 61.663, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.004, + "contentHeight" : 61.663, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 32.726 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyName[0]", + "entityName" : "PartyName", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 352.651, + "computedY" : 593.759, + "placementX" : 352.651, + "placementY" : 593.759, + "placementWidth" : 69.419, + "placementHeight" : 12.278, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 69.419, + "contentHeight" : 12.278, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]", + "entityName" : "PartyAddress", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 352.651, + "computedY" : 544.374, + "placementX" : 352.651, + "placementY" : 544.374, + "placementWidth" : 71.278, + "placementHeight" : 45.51, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 71.278, + "contentHeight" : 45.51, + "margin" : { + "top" : 3.875, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]/PartyAddressLine1[0]", + "entityName" : "PartyAddressLine1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 352.651, + "computedY" : 579.413, + "placementX" : 352.651, + "placementY" : 579.413, + "placementWidth" : 71.278, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 71.278, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]/PartyAddressLine2[1]", + "entityName" : "PartyAddressLine2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 352.651, + "computedY" : 567.733, + "placementX" : 352.651, + "placementY" : 567.733, + "placementWidth" : 63.478, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 63.478, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]/PartyAddressLine3[2]", + "entityName" : "PartyAddressLine3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 352.651, + "computedY" : 556.054, + "placementX" : 352.651, + "placementY" : 556.054, + "placementWidth" : 63.521, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 63.521, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]/PartyAddressLine4[3]", + "entityName" : "PartyAddressLine4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/PartiesRow[2]/Party2[1]/PartyBody[1]/PartyAddress[1]", + "childIndex" : 3, + "depth" : 6, + "layer" : 6, + "computedX" : 352.651, + "computedY" : 544.374, + "placementX" : 352.651, + "placementY" : 544.374, + "placementWidth" : 62.544, + "placementHeight" : 10.471, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 62.544, + "contentHeight" : 10.471, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/LineItemsBox[3]", + "entityName" : "LineItemsBox", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 3, + "depth" : 2, + "layer" : 2, + "computedX" : 28.776, + "computedY" : 272.254, + "placementX" : 28.776, + "placementY" : 272.254, + "placementWidth" : 537.159, + "placementHeight" : 233.751, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 233.751, + "margin" : { + "top" : 16.363, + "right" : 0.0, + "bottom" : 10.156, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/LineItemsBox[3]/LineItems[0]", + "entityName" : "LineItems", + "entityKind" : "TableNode", + "parentPath" : "PaymentsInvoice[0]/LineItemsBox[3]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.776, + "computedY" : 272.254, + "placementX" : 28.776, + "placementY" : 272.254, + "placementWidth" : 537.159, + "placementHeight" : 233.751, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 233.751, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]", + "entityName" : "SettlementRow", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 4, + "depth" : 2, + "layer" : 2, + "computedX" : 28.776, + "computedY" : 153.463, + "placementX" : 28.776, + "placementY" : 153.463, + "placementWidth" : 537.159, + "placementHeight" : 108.635, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 108.635, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]", + "entityName" : "PaymentDetails", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.776, + "computedY" : 153.82, + "placementX" : 28.776, + "placementY" : 153.82, + "placementWidth" : 248.267, + "placementHeight" : 108.278, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 248.267, + "contentHeight" : 108.278, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 2.821, + "right" : 11.285, + "bottom" : 7.617, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]", + "entityName" : "CardHeadLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 28.776, + "computedY" : 237.835, + "placementX" : 28.776, + "placementY" : 237.835, + "placementWidth" : 236.982, + "placementHeight" : 21.441, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.982, + "contentHeight" : 21.441, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]", + "entityName" : "CardHeadHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 28.776, + "computedY" : 237.835, + "placementX" : 28.776, + "placementY" : 237.835, + "placementWidth" : 236.982, + "placementHeight" : 21.441, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.982, + "contentHeight" : 21.441, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]", + "entityName" : "CardHead", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 28.776, + "computedY" : 237.835, + "placementX" : 28.776, + "placementY" : 237.835, + "placementWidth" : 236.982, + "placementHeight" : 21.441, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.982, + "contentHeight" : 21.441, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]", + "entityName" : "CardDisc", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 28.776, + "computedY" : 237.835, + "placementX" : 28.776, + "placementY" : 237.835, + "placementWidth" : 28.494, + "placementHeight" : 21.441, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 28.494, + "contentHeight" : 21.441, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 7.053 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]", + "entityName" : "Discbank", + "entityKind" : "ShapeContainerNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 35.829, + "computedY" : 237.835, + "placementX" : 35.829, + "placementY" : 237.835, + "placementWidth" : 21.441, + "placementHeight" : 21.441, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 21.441, + "contentHeight" : 21.441, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]", + "entityName" : "Glyphbank", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 35.829, + "computedY" : 240.656, + "placementX" : 35.829, + "placementY" : 240.656, + "placementWidth" : 21.441, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 21.441, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]/Align[0]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 35.829, + "computedY" : 240.656, + "placementX" : 35.829, + "placementY" : 240.656, + "placementWidth" : 21.441, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 21.441, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]/Align[0]/SvgIcon[0]", + "entityName" : "SvgIcon", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]/Align[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 38.651, + "computedY" : 240.656, + "placementX" : 38.651, + "placementY" : 240.656, + "placementWidth" : 15.799, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 15.799, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]/Align[0]/SvgIcon[0]/SvgLayer0[0]", + "entityName" : "SvgLayer0", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardDisc[0]/Discbank[0]/Glyphbank[0]/Align[0]/SvgIcon[0]", + "childIndex" : 0, + "depth" : 12, + "layer" : 12, + "computedX" : 38.651, + "computedY" : 240.656, + "placementX" : 38.651, + "placementY" : 240.656, + "placementWidth" : 15.799, + "placementHeight" : 15.799, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 15.799, + "contentHeight" : 15.799, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]/CardLabel[1]", + "entityName" : "CardLabel", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardHeadLayer[0]/CardHeadHolder[0]/CardHead[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 66.863, + "computedY" : 243.431, + "placementX" : 66.863, + "placementY" : 243.431, + "placementWidth" : 80.189, + "placementHeight" : 10.582, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 80.189, + "contentHeight" : 10.582, + "margin" : { + "top" : 5.263, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]", + "entityName" : "CardFieldsLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 28.776, + "computedY" : 161.437, + "placementX" : 28.776, + "placementY" : 161.437, + "placementWidth" : 236.982, + "placementHeight" : 76.398, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.982, + "contentHeight" : 76.398, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]", + "entityName" : "CardFieldsHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 28.776, + "computedY" : 161.437, + "placementX" : 28.776, + "placementY" : 161.437, + "placementWidth" : 236.982, + "placementHeight" : 76.398, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.982, + "contentHeight" : 76.398, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]", + "entityName" : "CardFields", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 65.452, + "computedY" : 161.437, + "placementX" : 65.452, + "placementY" : 161.437, + "placementWidth" : 200.306, + "placementHeight" : 74.18, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 200.306, + "contentHeight" : 74.18, + "margin" : { + "top" : 2.218, + "right" : 0.0, + "bottom" : 0.0, + "left" : 36.676 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "entityName" : "CardFieldLabels", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 65.452, + "computedY" : 161.437, + "placementX" : 65.452, + "placementY" : 161.437, + "placementWidth" : 56.174, + "placementHeight" : 74.18, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 56.174, + "contentHeight" : 74.18, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel1[0]", + "entityName" : "CardFieldLabel1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 226.776, + "placementX" : 65.452, + "placementY" : 226.776, + "placementWidth" : 38.014, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 38.014, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel2[1]", + "entityName" : "CardFieldLabel2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 215.886, + "placementX" : 65.452, + "placementY" : 215.886, + "placementWidth" : 48.829, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 48.829, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel3[2]", + "entityName" : "CardFieldLabel3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 2, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 204.996, + "placementX" : 65.452, + "placementY" : 204.996, + "placementWidth" : 32.791, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 32.791, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel4[3]", + "entityName" : "CardFieldLabel4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 3, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 194.107, + "placementX" : 65.452, + "placementY" : 194.107, + "placementWidth" : 56.174, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 56.174, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel5[4]", + "entityName" : "CardFieldLabel5", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 4, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 183.217, + "placementX" : 65.452, + "placementY" : 183.217, + "placementWidth" : 17.843, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 17.843, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel6[5]", + "entityName" : "CardFieldLabel6", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 5, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 172.327, + "placementX" : 65.452, + "placementY" : 172.327, + "placementWidth" : 41.499, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 41.499, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]/CardFieldLabel7[6]", + "entityName" : "CardFieldLabel7", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldLabels[0]", + "childIndex" : 6, + "depth" : 8, + "layer" : 8, + "computedX" : 65.452, + "computedY" : 161.437, + "placementX" : 65.452, + "placementY" : 161.437, + "placementWidth" : 33.572, + "placementHeight" : 8.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 33.572, + "contentHeight" : 8.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "entityName" : "CardFieldValues", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 144.446, + "computedY" : 161.461, + "placementX" : 144.446, + "placementY" : 161.461, + "placementWidth" : 107.558, + "placementHeight" : 74.156, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.558, + "contentHeight" : 74.156, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue1[0]", + "entityName" : "CardFieldValue1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 226.8, + "placementX" : 144.446, + "placementY" : 226.8, + "placementWidth" : 56.362, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 56.362, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue2[1]", + "entityName" : "CardFieldValue2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 215.91, + "placementX" : 144.446, + "placementY" : 215.91, + "placementWidth" : 76.501, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 76.501, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue3[2]", + "entityName" : "CardFieldValue3", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 2, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 205.02, + "placementX" : 144.446, + "placementY" : 205.02, + "placementWidth" : 31.035, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 31.035, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue4[3]", + "entityName" : "CardFieldValue4", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 3, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 194.13, + "placementX" : 144.446, + "placementY" : 194.13, + "placementWidth" : 34.092, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 34.092, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue5[4]", + "entityName" : "CardFieldValue5", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 4, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 183.24, + "placementX" : 144.446, + "placementY" : 183.24, + "placementWidth" : 107.558, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.558, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue6[5]", + "entityName" : "CardFieldValue6", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 5, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 172.351, + "placementX" : 144.446, + "placementY" : 172.351, + "placementWidth" : 38.008, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 38.008, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]/CardFieldValue7[6]", + "entityName" : "CardFieldValue7", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/PaymentDetails[0]/CardFieldsLayer[1]/CardFieldsHolder[0]/CardFields[0]/CardFieldValues[1]", + "childIndex" : 6, + "depth" : 8, + "layer" : 8, + "computedX" : 144.446, + "computedY" : 161.461, + "placementX" : 144.446, + "placementY" : 161.461, + "placementWidth" : 67.721, + "placementHeight" : 8.817, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 67.721, + "contentHeight" : 8.817, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]", + "entityName" : "Settlement", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 297.638, + "computedY" : 153.463, + "placementX" : 297.638, + "placementY" : 153.463, + "placementWidth" : 268.297, + "placementHeight" : 108.635, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 268.297, + "contentHeight" : 108.635, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 11.003 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]", + "entityName" : "Totals", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 308.641, + "computedY" : 219.153, + "placementX" : 308.641, + "placementY" : 219.153, + "placementWidth" : 257.294, + "placementHeight" : 32.745, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 257.294, + "contentHeight" : 32.745, + "margin" : { + "top" : 10.2, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 9.028, + "bottom" : 0.0, + "left" : 10.721 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]", + "entityName" : "TotalsGridLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 319.361, + "computedY" : 219.153, + "placementX" : 319.361, + "placementY" : 219.153, + "placementWidth" : 237.546, + "placementHeight" : 32.745, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 237.546, + "contentHeight" : 32.745, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]", + "entityName" : "TotalsGridHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 319.361, + "computedY" : 219.153, + "placementX" : 319.361, + "placementY" : 219.153, + "placementWidth" : 237.546, + "placementHeight" : 32.745, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 237.546, + "contentHeight" : 32.745, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]", + "entityName" : "TotalsGrid", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 319.361, + "computedY" : 219.153, + "placementX" : 319.361, + "placementY" : 219.153, + "placementWidth" : 237.546, + "placementHeight" : 32.745, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 237.546, + "contentHeight" : 32.745, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsLabels[0]", + "entityName" : "TotalsLabels", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 319.361, + "computedY" : 219.153, + "placementX" : 319.361, + "placementY" : 219.153, + "placementWidth" : 82.968, + "placementHeight" : 32.745, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 82.968, + "contentHeight" : 32.745, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsLabels[0]/TotalsLabel1[0]", + "entityName" : "TotalsLabel1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsLabels[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 319.361, + "computedY" : 240.594, + "placementX" : 319.361, + "placementY" : 240.594, + "placementWidth" : 82.968, + "placementHeight" : 11.304, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 82.968, + "contentHeight" : 11.304, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsLabels[0]/TotalsLabel2[1]", + "entityName" : "TotalsLabel2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsLabels[0]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 319.361, + "computedY" : 219.153, + "placementX" : 319.361, + "placementY" : 219.153, + "placementWidth" : 50.263, + "placementHeight" : 11.304, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 50.263, + "contentHeight" : 11.304, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsValues[1]", + "entityName" : "TotalsValues", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 438.134, + "computedY" : 219.381, + "placementX" : 438.134, + "placementY" : 219.381, + "placementWidth" : 118.773, + "placementHeight" : 32.517, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 118.773, + "contentHeight" : 32.517, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsValues[1]/TotalsValue1[0]", + "entityName" : "TotalsValue1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsValues[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 438.134, + "computedY" : 240.822, + "placementX" : 438.134, + "placementY" : 240.822, + "placementWidth" : 118.773, + "placementHeight" : 11.075, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 118.773, + "contentHeight" : 11.075, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsValues[1]/TotalsValue2[1]", + "entityName" : "TotalsValue2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/Totals[0]/TotalsGridLayer[0]/TotalsGridHolder[0]/TotalsGrid[0]/TotalsValues[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 438.134, + "computedY" : 219.381, + "placementX" : 438.134, + "placementY" : 219.381, + "placementWidth" : 118.773, + "placementHeight" : 11.075, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 118.773, + "contentHeight" : 11.075, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalsRule[1]", + "entityName" : "TotalsRule", + "entityKind" : "LineNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 308.641, + "computedY" : 207.93, + "placementX" : 308.641, + "placementY" : 207.93, + "placementWidth" : 256.73, + "placementHeight" : 1.128, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 256.73, + "contentHeight" : 1.128, + "margin" : { + "top" : 10.094, + "right" : 0.0, + "bottom" : 9.592, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]", + "entityName" : "TotalDuePanel", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 308.641, + "computedY" : 153.463, + "placementX" : 308.641, + "placementY" : 153.463, + "placementWidth" : 257.294, + "placementHeight" : 44.875, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 257.294, + "contentHeight" : 44.875, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 9.458, + "right" : 10.156, + "bottom" : 9.028, + "left" : 10.721 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]", + "entityName" : "TotalDueRowLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 319.361, + "computedY" : 162.491, + "placementX" : 319.361, + "placementY" : 162.491, + "placementWidth" : 236.418, + "placementHeight" : 26.39, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.418, + "contentHeight" : 26.39, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]", + "entityName" : "TotalDueRowHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 319.361, + "computedY" : 162.491, + "placementX" : 319.361, + "placementY" : 162.491, + "placementWidth" : 236.418, + "placementHeight" : 26.39, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.418, + "contentHeight" : 26.39, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]", + "entityName" : "TotalDueRow", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 319.361, + "computedY" : 162.491, + "placementX" : 319.361, + "placementY" : 162.491, + "placementWidth" : 236.418, + "placementHeight" : 26.39, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.418, + "contentHeight" : 26.39, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]/TotalDueText[0]", + "entityName" : "TotalDueText", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 319.361, + "computedY" : 162.491, + "placementX" : 319.361, + "placementY" : 162.491, + "placementWidth" : 80.671, + "placementHeight" : 26.39, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 80.671, + "contentHeight" : 26.39, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]/TotalDueText[0]/TotalDueLabel[0]", + "entityName" : "TotalDueLabel", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]/TotalDueText[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 319.361, + "computedY" : 176.669, + "placementX" : 319.361, + "placementY" : 176.669, + "placementWidth" : 56.43, + "placementHeight" : 12.212, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 56.43, + "contentHeight" : 12.212, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]/TotalDueText[0]/TotalDueSubLabel[1]", + "entityName" : "TotalDueSubLabel", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]/TotalDueText[0]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 319.361, + "computedY" : 162.491, + "placementX" : 319.361, + "placementY" : 162.491, + "placementWidth" : 80.671, + "placementHeight" : 10.197, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 80.671, + "contentHeight" : 10.197, + "margin" : { + "top" : 3.981, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]/TotalDueValue[1]", + "entityName" : "TotalDueValue", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/SettlementRow[4]/Settlement[1]/TotalDuePanel[2]/TotalDueRowLayer[0]/TotalDueRowHolder[0]/TotalDueRow[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 437.57, + "computedY" : 165.3, + "placementX" : 437.57, + "placementY" : 165.3, + "placementWidth" : 118.209, + "placementHeight" : 20.772, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 118.209, + "contentHeight" : 20.772, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]", + "entityName" : "Notes", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 5, + "depth" : 2, + "layer" : 2, + "computedX" : 29.905, + "computedY" : 102.364, + "placementX" : 29.905, + "placementY" : 102.364, + "placementWidth" : 536.03, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 536.03, + "contentHeight" : 35.3, + "margin" : { + "top" : 15.799, + "right" : 0.0, + "bottom" : 0.0, + "left" : 1.128 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]", + "entityName" : "NotesRow", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 29.905, + "computedY" : 102.364, + "placementX" : 29.905, + "placementY" : 102.364, + "placementWidth" : 536.03, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 536.03, + "contentHeight" : 35.3, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]", + "entityName" : "NotesDisc", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 29.905, + "computedY" : 115.658, + "placementX" : 29.905, + "placementY" : 115.658, + "placementWidth" : 22.005, + "placementHeight" : 22.005, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.005, + "contentHeight" : 22.005, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]", + "entityName" : "Discdocument", + "entityKind" : "ShapeContainerNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 29.905, + "computedY" : 115.658, + "placementX" : 29.905, + "placementY" : 115.658, + "placementWidth" : 22.005, + "placementHeight" : 22.005, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.005, + "contentHeight" : 22.005, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]", + "entityName" : "Glyphdocument", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 29.905, + "computedY" : 118.386, + "placementX" : 29.905, + "placementY" : 118.386, + "placementWidth" : 22.005, + "placementHeight" : 16.551, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.005, + "contentHeight" : 16.551, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]/Align[0]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 29.905, + "computedY" : 118.386, + "placementX" : 29.905, + "placementY" : 118.386, + "placementWidth" : 22.005, + "placementHeight" : 16.551, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.005, + "contentHeight" : 16.551, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]/Align[0]/SvgIcon[0]", + "entityName" : "SvgIcon", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]/Align[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 32.632, + "computedY" : 118.386, + "placementX" : 32.632, + "placementY" : 118.386, + "placementWidth" : 16.551, + "placementHeight" : 16.551, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 16.551, + "contentHeight" : 16.551, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]/Align[0]/SvgIcon[0]/SvgLayer0[0]", + "entityName" : "SvgLayer0", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesDisc[0]/Discdocument[0]/Glyphdocument[0]/Align[0]/SvgIcon[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 32.632, + "computedY" : 118.386, + "placementX" : 32.632, + "placementY" : 118.386, + "placementWidth" : 16.551, + "placementHeight" : 16.551, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 16.551, + "contentHeight" : 16.551, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]", + "entityName" : "NotesText", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 59.81, + "computedY" : 102.364, + "placementX" : 59.81, + "placementY" : 102.364, + "placementWidth" : 328.75, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 328.75, + "contentHeight" : 35.3, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]/NotesLabel[0]", + "entityName" : "NotesLabel", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 59.81, + "computedY" : 127.093, + "placementX" : 59.81, + "placementY" : 127.093, + "placementWidth" : 26.525, + "placementHeight" : 9.637, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.525, + "contentHeight" : 9.637, + "margin" : { + "top" : 0.934, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]/NotesBody[1]", + "entityName" : "NotesBody", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 59.81, + "computedY" : 102.364, + "placementX" : 59.81, + "placementY" : 102.364, + "placementWidth" : 328.75, + "placementHeight" : 21.929, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 328.75, + "contentHeight" : 21.929, + "margin" : { + "top" : 2.8, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]/NotesBody[1]/NotesLine1[0]", + "entityName" : "NotesLine1", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]/NotesBody[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 59.81, + "computedY" : 114.777, + "placementX" : 59.81, + "placementY" : 114.777, + "placementWidth" : 328.75, + "placementHeight" : 9.516, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 328.75, + "contentHeight" : 9.516, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]/NotesBody[1]/NotesLine2[1]", + "entityName" : "NotesLine2", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/Notes[5]/NotesRow[0]/NotesText[1]/NotesBody[1]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 59.81, + "computedY" : 102.364, + "placementX" : 59.81, + "placementY" : 102.364, + "placementWidth" : 186.262, + "placementHeight" : 9.516, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 186.262, + "contentHeight" : 9.516, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/FooterRule[6]", + "entityName" : "FooterRule", + "entityKind" : "LineNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 6, + "depth" : 2, + "layer" : 2, + "computedX" : 28.212, + "computedY" : 83.759, + "placementX" : 28.212, + "placementY" : 83.759, + "placementWidth" : 538.851, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 538.851, + "contentHeight" : 1.0, + "margin" : { + "top" : 17.604, + "right" : -1.128, + "bottom" : 12.3, + "left" : -0.564 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]", + "entityName" : "DocumentFooter", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]", + "childIndex" : 7, + "depth" : 2, + "layer" : 2, + "computedX" : 28.776, + "computedY" : 34.229, + "placementX" : 28.776, + "placementY" : 34.229, + "placementWidth" : 537.159, + "placementHeight" : 37.23, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 537.159, + "contentHeight" : 37.23, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]", + "entityName" : "FooterDue", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.776, + "computedY" : 43.153, + "placementX" : 28.776, + "placementY" : 43.153, + "placementWidth" : 276.479, + "placementHeight" : 28.306, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 276.479, + "contentHeight" : 28.306, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 2.257 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]", + "entityName" : "FooterDueRowLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 31.033, + "computedY" : 43.153, + "placementX" : 31.033, + "placementY" : 43.153, + "placementWidth" : 274.222, + "placementHeight" : 28.306, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 274.222, + "contentHeight" : 28.306, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]", + "entityName" : "FooterDueRowHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 31.033, + "computedY" : 43.153, + "placementX" : 31.033, + "placementY" : 43.153, + "placementWidth" : 274.222, + "placementHeight" : 28.306, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 274.222, + "contentHeight" : 28.306, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]", + "entityName" : "FooterDueRow", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 31.033, + "computedY" : 43.153, + "placementX" : 31.033, + "placementY" : 43.153, + "placementWidth" : 274.222, + "placementHeight" : 24.356, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 274.222, + "contentHeight" : 24.356, + "margin" : { + "top" : 3.95, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]", + "entityName" : "FooterDueIcon", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 39.121, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 39.121, + "contentHeight" : 27.836, + "margin" : { + "top" : -3.479, + "right" : 0.0, + "bottom" : 0.0, + "left" : -3.479 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]", + "entityName" : "Glyphcalendar", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 35.641, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 35.641, + "contentHeight" : 27.836, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 35.641, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 35.641, + "contentHeight" : 27.836, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]", + "entityName" : "SvgIcon", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 27.836, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.836, + "contentHeight" : 27.836, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]/SvgLayer0[0]", + "entityName" : "SvgLayer0", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 27.836, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.836, + "contentHeight" : 27.836, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]/SvgLayer1[1]", + "entityName" : "SvgLayer1", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]", + "childIndex" : 1, + "depth" : 11, + "layer" : 11, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 27.836, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.836, + "contentHeight" : 27.836, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]/SvgLayer2[2]", + "entityName" : "SvgLayer2", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueIcon[0]/Glyphcalendar[0]/Align[0]/SvgIcon[0]", + "childIndex" : 2, + "depth" : 11, + "layer" : 11, + "computedX" : 27.554, + "computedY" : 43.153, + "placementX" : 27.554, + "placementY" : 43.153, + "placementWidth" : 27.836, + "placementHeight" : 27.836, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.836, + "contentHeight" : 27.836, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueText[1]", + "entityName" : "FooterDueText", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 63.195, + "computedY" : 46.33, + "placementX" : 63.195, + "placementY" : 46.33, + "placementWidth" : 197.629, + "placementHeight" : 21.179, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 197.629, + "contentHeight" : 21.179, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueText[1]/FooterDueTitle[0]", + "entityName" : "FooterDueTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueText[1]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 63.195, + "computedY" : 57.6, + "placementX" : 63.195, + "placementY" : 57.6, + "placementWidth" : 122.528, + "placementHeight" : 9.515, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 122.528, + "contentHeight" : 9.515, + "margin" : { + "top" : 0.394, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueText[1]/FooterDueSubtitle[1]", + "entityName" : "FooterDueSubtitle", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterDue[0]/FooterDueRowLayer[0]/FooterDueRowHolder[0]/FooterDueRow[0]/FooterDueText[1]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 63.195, + "computedY" : 46.33, + "placementX" : 63.195, + "placementY" : 46.33, + "placementWidth" : 197.629, + "placementHeight" : 9.497, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 197.629, + "contentHeight" : 9.497, + "margin" : { + "top" : 1.773, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]", + "entityName" : "FooterSupport", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 305.255, + "computedY" : 34.229, + "placementX" : 305.255, + "placementY" : 34.229, + "placementWidth" : 260.68, + "placementHeight" : 37.23, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 260.68, + "contentHeight" : 37.23, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 29.905 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]", + "entityName" : "FooterSupportRowLayer", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 335.16, + "computedY" : 34.229, + "placementX" : 335.16, + "placementY" : 34.229, + "placementWidth" : 230.775, + "placementHeight" : 37.23, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 230.775, + "contentHeight" : 37.23, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]", + "entityName" : "FooterSupportRowHolder", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 335.16, + "computedY" : 34.229, + "placementX" : 335.16, + "placementY" : 34.229, + "placementWidth" : 230.775, + "placementHeight" : 37.23, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 230.775, + "contentHeight" : 37.23, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]", + "entityName" : "FooterSupportRow", + "entityKind" : "RowNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 335.16, + "computedY" : 34.229, + "placementX" : 335.16, + "placementY" : 34.229, + "placementWidth" : 230.775, + "placementHeight" : 32.716, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 230.775, + "contentHeight" : 32.716, + "margin" : { + "top" : 4.514, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]", + "entityName" : "FooterSupportIcon", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 331.492, + "computedY" : 41.272, + "placementX" : 331.492, + "placementY" : 41.272, + "placementWidth" : 44.011, + "placementHeight" : 29.341, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 44.011, + "contentHeight" : 29.341, + "margin" : { + "top" : -3.668, + "right" : 0.0, + "bottom" : 0.0, + "left" : -3.668 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]", + "entityName" : "Glyphsupport", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 331.492, + "computedY" : 41.272, + "placementX" : 331.492, + "placementY" : 41.272, + "placementWidth" : 40.343, + "placementHeight" : 29.341, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 40.343, + "contentHeight" : 29.341, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 331.492, + "computedY" : 41.272, + "placementX" : 331.492, + "placementY" : 41.272, + "placementWidth" : 40.343, + "placementHeight" : 29.341, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 40.343, + "contentHeight" : 29.341, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]/SvgIcon[0]", + "entityName" : "SvgIcon", + "entityKind" : "LayerStackNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 331.492, + "computedY" : 41.272, + "placementX" : 331.492, + "placementY" : 41.272, + "placementWidth" : 29.341, + "placementHeight" : 29.341, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 29.341, + "contentHeight" : 29.341, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]/SvgIcon[0]/SvgLayer0[0]", + "entityName" : "SvgLayer0", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]/SvgIcon[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 331.492, + "computedY" : 41.272, + "placementX" : 331.492, + "placementY" : 41.272, + "placementWidth" : 29.341, + "placementHeight" : 29.341, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 29.341, + "contentHeight" : 29.341, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]/SvgIcon[0]/SvgLayer1[1]", + "entityName" : "SvgLayer1", + "entityKind" : "Path", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportIcon[0]/Glyphsupport[0]/Align[0]/SvgIcon[0]", + "childIndex" : 1, + "depth" : 11, + "layer" : 11, + "computedX" : 331.492, + "computedY" : 41.272, + "placementX" : 331.492, + "placementY" : 41.272, + "placementWidth" : 29.341, + "placementHeight" : 29.341, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 29.341, + "contentHeight" : 29.341, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportText[1]", + "entityName" : "FooterSupportText", + "entityKind" : "SectionNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 371.836, + "computedY" : 34.229, + "placementX" : 371.836, + "placementY" : 34.229, + "placementWidth" : 182.481, + "placementHeight" : 32.716, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 182.481, + "contentHeight" : 32.716, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportText[1]/FooterSupportTitle[0]", + "entityName" : "FooterSupportTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportText[1]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 371.836, + "computedY" : 56.531, + "placementX" : 371.836, + "placementY" : 56.531, + "placementWidth" : 119.69, + "placementHeight" : 10.414, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 119.69, + "contentHeight" : 10.414, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportText[1]/FooterSupportContacts[1]", + "entityName" : "FooterSupportContacts", + "entityKind" : "ParagraphNode", + "parentPath" : "PaymentsInvoice[0]/DocumentFooter[7]/FooterSupport[1]/FooterSupportRowLayer[0]/FooterSupportRowHolder[0]/FooterSupportRow[0]/FooterSupportText[1]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 371.836, + "computedY" : 34.229, + "placementX" : 371.836, + "placementY" : 34.229, + "placementWidth" : 182.481, + "placementHeight" : 18.994, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 182.481, + "contentHeight" : 18.994, + "margin" : { + "top" : 3.307, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + } ] +} diff --git a/qa/src/test/resources/visual-baselines/invoice-v2-layered/payments_invoice-page-0.png b/qa/src/test/resources/visual-baselines/invoice-v2-layered/payments_invoice-page-0.png new file mode 100644 index 000000000..ee7f1c12b Binary files /dev/null and b/qa/src/test/resources/visual-baselines/invoice-v2-layered/payments_invoice-page-0.png differ diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsClosing.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsClosing.java new file mode 100644 index 000000000..03b3ca8ba --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsClosing.java @@ -0,0 +1,289 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.DocumentLinkOptions; +import com.demcha.compose.document.node.HorizontalAlign; +import com.demcha.compose.document.node.RowVerticalAlign; +import com.demcha.compose.document.style.DocumentBorders; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.templates.data.invoice.InvoiceNotesBlock; +import com.demcha.compose.document.templates.data.invoice.InvoicePaymentBlock; + +import java.util.Locale; +import java.util.regex.Pattern; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DIVIDER; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_DUE_ICON; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_DUE_ICON_COL; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_DUE_ICON_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_DUE_TITLE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_RULE_GAP_ABOVE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_RULE_GAP_BELOW; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_RULE_OVERHANG_L; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_RULE_OVERHANG_R; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_RULE_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_SPLIT_X; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_SUB_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_SUPPORT_CELL_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_SUPPORT_ICON; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_SUPPORT_ICON_COL; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.FOOTER_SUPPORT_TITLE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.INK; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.LINE_BOX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.MUTED; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_DISC_D; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_DISC_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_GLYPH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_LABEL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_SURFACE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.NOTES_TEXT_INDENT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_STRONG; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_THIN; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TEXT_TOP_BEARING; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.capOffset; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.gap; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.glyphPad; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.px; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.style; + +/** + * What closes the sheet: the notes, the rule under them, and the two-cell + * document footer. + * + *

The footer is body content rather than page chrome: it carries glyphs and + * a reachable address, and a header/footer zone takes only strings. Page + * identity on a continuation page is carried by the page number instead.

+ */ +final class PaymentsClosing { + + /** + * A trunk prefix in a printed number is for a domestic dialler and is not + * part of the international one, so it is dropped from the dialled form. + */ + private static final Pattern TRUNK_PREFIX = Pattern.compile("\\(0+\\)"); + + private PaymentsClosing() { + } + + /** + * Draws the closing blocks. + * + * @param page the page flow + * @param notes the note block and the contacts the footer offers + * @param payment the due notice and the closing line + */ + static void render(PageFlowBuilder page, InvoiceNotesBlock notes, + InvoicePaymentBlock payment) { + if (!notes.paragraphs().isEmpty()) { + renderNotes(page, notes); + } + renderFooterRule(page); + renderFooter(page, notes, payment); + } + + /** + * Glyph column and text column as two cells, so the second body line + * returns to the text axis rather than to the glyph's. The notes introduce + * the footer, and a page that ends on one and opens on the other reads as + * two fragments — so they are kept together. + */ + private static void renderNotes(PageFlowBuilder page, InvoiceNotesBlock notes) { + page.addSection("Notes", block -> { + block.spacing(0) + .padding(DocumentInsets.zero()) + .margin(new DocumentInsets(px(1280 - 1252), 0, 0, NOTES_DISC_INSET)) + .keepWithNext(); + block.addRow("NotesRow", row -> { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + .columns(DocumentRowColumn.fixed(NOTES_TEXT_INDENT), + DocumentRowColumn.weight(1)); + row.addSection("NotesDisc", disc -> disc + .spacing(0) + .add(PaymentsWidgets.disc(PaymentsIcons.DOCUMENT, NOTES_SURFACE, + NOTES_DISC_D, NOTES_GLYPH))); + row.addSection("NotesText", text -> { + text.spacing(0); + text.addParagraph(p -> p + .name("NotesLabel") + .text(notes.heading()) + .textStyle(style(NOTES_LABEL_SIZE, ACCENT, + DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets( + capOffset(1285 - 1280, NOTES_LABEL_SIZE), 0, 0, 0))); + text.addSection("NotesBody", lines -> { + lines.spacing(gap(NOTES_PITCH, PaymentsStyles.NOTES_BODY_SIZE)) + .margin(new DocumentInsets( + px(1307 - 1285) - LINE_BOX * NOTES_LABEL_SIZE + + TEXT_TOP_BEARING * (NOTES_LABEL_SIZE + - PaymentsStyles.NOTES_BODY_SIZE), 0, 0, 0)); + PaymentsWidgets.textLines(lines, "NotesLine", notes.paragraphs(), + PaymentsStyles.NOTES_BODY_SIZE, MUTED); + }); + }); + }); + }); + } + + private static void renderFooterRule(PageFlowBuilder page) { + page.addLine(line -> line + .name("FooterRule") + .horizontal(FOOTER_RULE_W) + .thickness(RULE_THIN) + .color(RULE_STRONG) + // One design px past the content box on the left, two on the + // right — measured, and small enough to look like a rounding + // error until the renderer refuses a line wider than its box. + .margin(new DocumentInsets( + FOOTER_RULE_GAP_ABOVE, + -FOOTER_RULE_OVERHANG_R, + FOOTER_RULE_GAP_BELOW, + -FOOTER_RULE_OVERHANG_L))); + } + + private static void renderFooter(PageFlowBuilder page, InvoiceNotesBlock notes, + InvoicePaymentBlock payment) { + page.addRow("DocumentFooter", row -> { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + .columns(DocumentRowColumn.fixed(FOOTER_SPLIT_X), + DocumentRowColumn.weight(1)); + row.addSection("FooterDue", cell -> renderDue(cell, payment)); + row.addSection("FooterSupport", cell -> { + cell.spacing(0) + .borders(DocumentBorders.left(DocumentStroke.of(DIVIDER, RULE_THIN))) + .padding(new DocumentInsets(0, 0, 0, FOOTER_SUPPORT_CELL_INSET)); + renderSupport(cell, notes, payment); + }); + }); + } + + /** When the money is wanted, and what to quote when sending it. */ + private static void renderDue(SectionBuilder cell, InvoicePaymentBlock payment) { + cell.spacing(0).padding(new DocumentInsets(0, 0, 0, FOOTER_DUE_ICON_INSET)); + PaymentsWidgets.layeredRow(cell, "FooterDueRow", row -> { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + .margin(new DocumentInsets(px(1404 - 1397), 0, 0, 0)) + .columns(DocumentRowColumn.fixed(FOOTER_DUE_ICON_COL), + DocumentRowColumn.weight(1)); + row.addSection("FooterDueIcon", glyph -> glyph + .spacing(0) + // A disc centres its glyph, so the icon box's own padding + // cancels there. A bare glyph leading a text row does not: + // the padding sits between the column's edge and the ink and + // has to be taken back off. + .margin(new DocumentInsets(-glyphPad(37), 0, 0, -glyphPad(37))) + .add(PaymentsWidgets.glyphNode(PaymentsIcons.CALENDAR, FOOTER_DUE_ICON, + HorizontalAlign.LEFT))); + row.addSection("FooterDueText", text -> { + text.spacing(0); + text.addParagraph(p -> p + .name("FooterDueTitle") + .text(dueTitle(payment)) + .textStyle(style(FOOTER_DUE_TITLE_SIZE, INK, + DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets( + capOffset(1408 - 1404, FOOTER_DUE_TITLE_SIZE), 0, 0, 0))); + if (!payment.instruction().isBlank()) { + text.addParagraph(p -> p + .name("FooterDueSubtitle") + .text(payment.instruction()) + .textStyle(style(FOOTER_SUB_SIZE, BODY, + DocumentTextDecoration.DEFAULT)) + .margin(new DocumentInsets( + px(1428 - 1408) - LINE_BOX * FOOTER_DUE_TITLE_SIZE + + TEXT_TOP_BEARING + * (FOOTER_DUE_TITLE_SIZE - FOOTER_SUB_SIZE), + 0, 0, 0))); + } + }); + }); + } + + /** + * The design sets this line in capitals, so the preset uppercases whichever + * of the two due strings the document filled — the emphasis when it has one, + * the notice itself otherwise. + */ + private static String dueTitle(InvoicePaymentBlock payment) { + String text = payment.dueNoticeEmphasis().isBlank() + ? payment.dueNotice() + : payment.dueNoticeEmphasis(); + return text.toUpperCase(Locale.ROOT); + } + + /** Who to ask, and how to reach them. */ + private static void renderSupport(SectionBuilder cell, InvoiceNotesBlock notes, + InvoicePaymentBlock payment) { + PaymentsWidgets.layeredRow(cell, "FooterSupportRow", row -> { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + .margin(new DocumentInsets(px(1405 - 1397), 0, 0, 0)) + .columns(DocumentRowColumn.fixed(FOOTER_SUPPORT_ICON_COL), + DocumentRowColumn.weight(1)); + row.addSection("FooterSupportIcon", glyph -> glyph + .spacing(0) + .margin(new DocumentInsets(-glyphPad(39), 0, 0, -glyphPad(39))) + .add(PaymentsWidgets.glyphNode(PaymentsIcons.SUPPORT, FOOTER_SUPPORT_ICON, + HorizontalAlign.LEFT))); + row.addSection("FooterSupportText", text -> { + text.spacing(0); + if (!payment.signOff().isBlank()) { + text.addParagraph(p -> p + .name("FooterSupportTitle") + .text(payment.signOff()) + .textStyle(style(FOOTER_SUPPORT_TITLE_SIZE, INK, + DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets( + capOffset(1408 - 1405, FOOTER_SUPPORT_TITLE_SIZE), 0, 0, 0))); + } + // Separate runs, so each address reaches the PDF as a real + // annotation rather than as coloured text. + text.addParagraph(p -> { + p.name("FooterSupportContacts"); + boolean hasEmail = !notes.contactEmail().isBlank(); + if (hasEmail) { + p.inlineText(notes.contactEmail(), + style(FOOTER_SUB_SIZE, ACCENT, DocumentTextDecoration.DEFAULT), + new DocumentLinkOptions("mailto:" + notes.contactEmail())); + } + if (!notes.contactPhone().isBlank()) { + if (hasEmail) { + p.inlineText(" | ", style(FOOTER_SUB_SIZE, MUTED, + DocumentTextDecoration.DEFAULT)); + } + String dialled = telUri(notes.contactPhone()); + if (dialled == null) { + p.inlineText(notes.contactPhone(), style(FOOTER_SUB_SIZE, BODY, + DocumentTextDecoration.DEFAULT)); + } else { + p.inlineText(notes.contactPhone(), style(FOOTER_SUB_SIZE, BODY, + DocumentTextDecoration.DEFAULT), + new DocumentLinkOptions(dialled)); + } + } + p.margin(new DocumentInsets( + px(1432 - 1408) - LINE_BOX * FOOTER_SUPPORT_TITLE_SIZE + + TEXT_TOP_BEARING + * (FOOTER_SUPPORT_TITLE_SIZE - FOOTER_SUB_SIZE), 0, 0, 0)); + }); + }); + }); + } + + /** A printed number as something a reader can dial, or {@code null}. */ + private static String telUri(String phone) { + String dialled = TRUNK_PREFIX.matcher(phone).replaceAll(""); + String digits = dialled.replaceAll("[^0-9]", ""); + return digits.isEmpty() ? null + : "tel:" + (phone.trim().startsWith("+") ? "+" : "") + digits; + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsIcons.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsIcons.java new file mode 100644 index 000000000..3d59df9ee --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsIcons.java @@ -0,0 +1,94 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.svg.SvgIcon; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Classpath loader for the Payments invoice marks: the two party glyphs, the + * six a service line may take, and the four the preset places itself — the + * bank on the payment card, the document on the notes, the calendar on the due + * block and the headset on the support block. + * + *

They ship inside the templates artifact under + * {@code templates/invoice/payments/icons/} as SVG, so they scale with the box + * they are drawn into rather than being resampled. Parsed icons are cached per + * token, so a sheet that repeats a mark reads each file once per JVM.

+ * + *

The set is coloured, not tinted

+ * + *

Each glyph carries its colour in its own markup, in one of the design's + * two tints: ink for the marks that sit inside a lavender disc, accent for the + * ones that stand on the page. A token therefore names a glyph and the + * tint it is drawn in, which is why {@link #HEADSET} and {@link #SUPPORT} are + * the same drawing under two names — one opens a service line inside a disc, + * the other closes the sheet on the page.

+ * + *

{@link #LINE_TOKENS} is the vocabulary a document names through + * {@code InvoiceServiceLines.Line.icon()}. It is scoped to this preset — + * another preset packages its own set.

+ */ +final class PaymentsIcons { + + static final String BILL_TO = "bill-to"; + static final String SHIP_TO = "ship-to"; + static final String BANK = "bank"; + static final String DOCUMENT = "document"; + static final String CALENDAR = "calendar"; + static final String SUPPORT = "support"; + static final String HEADSET = "headset"; + + private static final String ICON_ROOT = "/templates/invoice/payments/icons/"; + + /** Every mark this preset packages. */ + private static final Set TOKENS = Set.of( + BILL_TO, SHIP_TO, BANK, DOCUMENT, CALENDAR, SUPPORT, + "card", "card-settings", "shield", "globe", "mobile", HEADSET); + + /** The marks a document may name on a service line. */ + static final Set LINE_TOKENS = Set.of( + "card", "card-settings", "shield", "globe", "mobile", HEADSET); + + private static final Map CACHE = new ConcurrentHashMap<>(); + + private PaymentsIcons() { + } + + /** + * Reads one mark. + * + * @param token one of the packaged tokens + * @return the parsed icon + * @throws IllegalArgumentException when the token names no packaged mark, + * listing the ones a document may choose — an icon token is data, + * so a wrong one is a data error + */ + static SvgIcon icon(String token) { + if (!TOKENS.contains(token)) { + throw new IllegalArgumentException( + "Unknown payments invoice icon token '" + token + + "'. This preset packages " + LINE_TOKENS.stream().sorted().toList() + + " for a service line's mark."); + } + return CACHE.computeIfAbsent(token, PaymentsIcons::read); + } + + private static SvgIcon read(String token) { + String resourcePath = ICON_ROOT + token + ".svg"; + try (InputStream input = PaymentsIcons.class.getResourceAsStream(resourcePath)) { + if (input == null) { + throw new IllegalStateException("Missing payments invoice icon: " + resourcePath); + } + return SvgIcon.parse(new String(input.readAllBytes(), StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new UncheckedIOException( + "Failed to read payments invoice icon: " + resourcePath, e); + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoice.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoice.java new file mode 100644 index 000000000..a51312a8b --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsInvoice.java @@ -0,0 +1,167 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.api.PageMarginRule; +import com.demcha.compose.document.output.DocumentHeaderFooter; +import com.demcha.compose.document.output.DocumentHeaderFooterZone; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.data.invoice.StructuredInvoiceData; + +import java.util.List; +import java.util.Objects; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CONTINUATION_MARGIN; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.MUTED; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PAGE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PAGE_MARGIN; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PAGE_NUMBER_BAND; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PAGE_NUMBER_SIZE; + +/** + * Payments — a lavender-and-navy invoice: a masthead crossed by a diagonal + * band, a half-split issuer and metadata header, two addressed parties, a + * marked line-item table, a settlement row pairing bank details against the + * totals, a note block and a two-cell document footer. + * + *

It flows

+ * + *

Unlike most presets in this package the sheet is not drawn for one page. + * The design shows six service lines and a real month of usage brings dozens, + * so the table is the region that grows: its header repeats on every page it + * reaches, a continuation page reserves a deeper bottom margin than page one, + * and every page carries its number. Pagination here is load-bearing rather + * than incidental — a financial record that runs over has to make a missing + * page detectable.

+ * + *

The lockup is the caller's

+ * + *

The design's mark occupies a measured 136 × 55.3 design px box beside the + * title. What fills it comes from {@code InvoiceBrand}: a document that brings + * a logo has it drawn to that height, so a wider or narrower mark keeps the + * design's optical weight; a document that brings only a name has the name set + * as a wordmark. The templates artifact carries no mark of its own.

+ * + *

One column, not five

+ * + *

The table has an outer box and a rule between every row and no interior + * verticals, which is exactly what a single-column table draws — a cell strokes + * all four of its own edges, and a one-column table has no interior vertical + * edge. The five columns are a row inside each cell, on shares of the table's + * width. Building it the other way round, with the box from a stroked section + * wrapped round the table, is wrong on a document that paginates: a section's + * box fills its page fragment rather than hugging its rows, so every + * continuation page would end with an empty bordered strip.

+ * + *

Every horizontal pair inside a cell is a wrapped row

+ * + *

A row nested directly in a row cell is refused by the layout compiler, so + * each such pair — a party's mark and its label, a line's mark and its text, + * the card's fields, the totals grid — is a row wrapped in a single layer of a + * stack.

+ * + *

What the document says, and what the preset chooses

+ * + *

A service line names its own mark through + * {@code InvoiceServiceLines.Line.icon()}, from this preset's own vocabulary, + * and an unknown token is reported as a data error naming the set. A line with + * no token is drawn without a mark. The other four marks are the preset's: + * which glyph opens the bill-to block, the payment card, the notes and the two + * footer cells is a property of the design rather than of the document.

+ * + *

Figures are written with the locale stated rather than inherited, so the + * same data renders the same sheet on any machine. The currency is named once + * in the amount column's header and its symbol carried on each figure.

+ * + *

Fonts

+ * + *

The sheet is set in Lato throughout. The templates artifact carries no + * fonts — register the family on the session, or the engine substitutes and the + * measured geometry no longer matches the type sitting in it.

+ * + *

Usage:

+ *
{@code
+ * DocumentTemplate template = PaymentsInvoice.create();
+ * template.compose(session, invoice);
+ * }
+ * + * @since 2.2.3 + */ +public final class PaymentsInvoice { + + /** Stable identifier of this preset. */ + public static final String ID = "payments-invoice"; + + /** Human-readable name of this preset. */ + public static final String DISPLAY_NAME = "Payments Invoice"; + + private PaymentsInvoice() { + } + + /** + * Creates the template. + * + * @return a template composing a {@link StructuredInvoiceData} + */ + public static DocumentTemplate create() { + return new Template(); + } + + private record Template() implements DocumentTemplate { + + @Override + public String id() { + return ID; + } + + @Override + public String displayName() { + return DISPLAY_NAME; + } + + @Override + public void compose(DocumentSession document, StructuredInvoiceData data) { + Objects.requireNonNull(document, "document"); + Objects.requireNonNull(data, "data"); + + // Every length on the sheet is a share of the design's own grid, so + // the preset sets the page and its margins rather than following + // what the caller configured. + document.pageSize(PAGE); + document.margin(PAGE_MARGIN); + document.pageMargins(List.of(PageMarginRule.from(2, CONTINUATION_MARGIN))); + renderPageNumber(document); + + String currency = data.currencyCode(); + document.pageFlow(page -> { + page.name("PaymentsInvoice").padding(DocumentInsets.zero()).spacing(0); + PaymentsMasthead.render(page, data.brand(), data.supplier(), data.masthead()); + PaymentsParties.render(page, data.billTo(), data.shipTo()); + PaymentsLines.render(page, data.serviceLines(), currency); + PaymentsSettlement.render(page, data.payment(), data.totals(), currency); + PaymentsClosing.render(page, data.notes(), data.payment()); + }); + } + + /** + * The only chrome the sheet has, and an addition rather than a + * reproduction: the design is one page and carries no number. + * + *

The height is stated because a default band would take more than + * the bottom margin leaves and push the footer onto a second page. + * Sized to the margin, the band sits inside it rather than taking + * content space, and the type is sized to the band rather than the + * other way round.

+ */ + private static void renderPageNumber(DocumentSession document) { + document.footer(DocumentHeaderFooter.builder() + .zone(DocumentHeaderFooterZone.FOOTER) + .centerText("Page {page} of {pages}") + .height((float) PAGE_NUMBER_BAND) + .fontSize((float) PAGE_NUMBER_SIZE) + .textColor(MUTED) + .showSeparator(false) + .build()); + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsLines.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsLines.java new file mode 100644 index 000000000..62186a101 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsLines.java @@ -0,0 +1,237 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.RowBuilder; +import com.demcha.compose.document.dsl.TableBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.RowVerticalAlign; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.table.DocumentTableCell; +import com.demcha.compose.document.table.DocumentTableColumn; +import com.demcha.compose.document.templates.data.invoice.InvoiceServiceLines; + +import java.util.List; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT_SURFACE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.AMOUNT_PAD_R; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CELL_AMOUNT_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CELL_PAD_R; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CELL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.COLUMN_SHARES; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CONTENT_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DESC_PAD_L; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DISC_D; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.HEADER_CELL_STYLE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.INK; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_CELL_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_CELL_STYLE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_DISC_GAP; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_SUB_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_SUB_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_TEXT_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ITEM_TITLE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.LINE_BOX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.MUTED; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TABLE_HEAD_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TABLE_HEAD_SMALL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TEXT_TOP_BEARING; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.gap; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.px; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.style; + +/** + * The service lines. + * + *

One column, not five

+ * + *

The design's table has an outer box and a rule between every row and + * no interior verticals — which is exactly the rule topology a + * single-column table draws, because a cell strokes all four of its own edges + * and a one-column table has no interior vertical edge to draw. The five + * columns are a row inside each cell, laid out on shares of the table's width, + * so they stay aligned down the page without the engine drawing a line between + * them.

+ * + *

The alternative — unstroked cells, separators as line rows, and the outer + * box from a stroked section wrapped round the table — is wrong on a document + * that paginates: a section's box fills its page fragment rather than hugging + * its rows, so every continuation page would end with an empty bordered strip + * below its last line. Cell borders have no such problem, because the engine + * redraws each page's cell edges from the same styles and every fragment closes + * on its own last row.

+ */ +final class PaymentsLines { + + private PaymentsLines() { + } + + /** + * Draws the table. + * + * @param page the page flow + * @param serviceLines the columns and the lines + * @param currencyCode the ISO code the figures are stated in + */ + static void render(PageFlowBuilder page, InvoiceServiceLines serviceLines, + String currencyCode) { + page.addSection("LineItemsBox", box -> { + box.spacing(0) + .padding(DocumentInsets.zero()) + .margin(new DocumentInsets(px(596 - 567), 0, px(1060 - 1042), 0)); + box.addTable(table -> { + table.name("LineItems") + .width(CONTENT_W) + .columns(DocumentTableColumn.fixed(CONTENT_W)); + renderHeader(table, serviceLines.columns(), currencyCode); + List lines = serviceLines.lines(); + for (int i = 0; i < lines.size(); i++) { + table.rowCells(DocumentTableCell + .node(renderLine(lines.get(i), i, currencyCode)) + .withStyle(ITEM_CELL_STYLE)); + } + }); + }); + } + + /** + * The header repeats on every page its table reaches. It is one cell like + * every other row, so its own stroke draws the table's top edge and the rule + * under the band, and its fill is the tint. + */ + private static void renderHeader(TableBuilder table, InvoiceServiceLines.Columns columns, + String currencyCode) { + RowBuilder row = new RowBuilder(); + row.name("LineItemsHeader").spacing(0).columns(columnWidths()); + row.addParagraph(p -> p + .name("HeadDescription") + .text(columns.description()) + .textStyle(style(TABLE_HEAD_SIZE, INK, DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets(0, 0, 0, DESC_PAD_L))); + headCell(row, "HeadQuantity", columns.quantity(), TextAlign.CENTER, 0); + headCell(row, "HeadUnitPrice", columns.unitPrice(), TextAlign.RIGHT, CELL_PAD_R); + headCell(row, "HeadVat", columns.vat(), TextAlign.CENTER, 0); + // Two runs: the currency qualifier is set smaller than the label it + // follows, which one paragraph of one style cannot express. The design + // names the currency once, here, rather than on every amount. + row.addParagraph(p -> { + p.name("HeadAmount"); + p.inlineText(columns.amount(), style(TABLE_HEAD_SIZE, INK, DocumentTextDecoration.BOLD)); + if (!currencyCode.isBlank()) { + p.inlineText(" (" + currencyCode + ")", + style(TABLE_HEAD_SMALL_SIZE, INK, DocumentTextDecoration.BOLD)); + } + p.align(TextAlign.RIGHT); + p.padding(new DocumentInsets(0, AMOUNT_PAD_R, 0, 0)); + }); + table.headerCells(DocumentTableCell.node(row.build()).withStyle(HEADER_CELL_STYLE)) + .repeatHeader(); + } + + private static void headCell(RowBuilder row, String name, String label, TextAlign align, + double padRight) { + row.addParagraph(p -> p + .name(name) + .text(label) + .textStyle(style(TABLE_HEAD_SIZE, INK, DocumentTextDecoration.BOLD)) + .align(align) + .padding(new DocumentInsets(0, padRight, 0, 0))); + } + + /** + * The five columns as a row inside the row's single cell. Alignment differs + * per column and is part of the design rather than an accident: description + * left, quantity centred, unit price right, tax centred, amount right. + */ + private static DocumentNode renderLine(InvoiceServiceLines.Line line, int index, + String currencyCode) { + int n = index + 1; + RowBuilder row = new RowBuilder(); + row.name("Item" + n) + .spacing(0) + .verticalAlign(RowVerticalAlign.CENTER) + .columns(columnWidths()); + row.addSection("ItemDescription" + n, cell -> { + cell.spacing(0).padding(new DocumentInsets(0, 0, 0, ITEM_CELL_INSET)); + // Mark and text are a horizontal pair inside a row cell, which the + // layout compiler refuses as a bare row. + PaymentsWidgets.layeredRow(cell, "ItemBody" + n, body -> { + body.spacing(ITEM_DISC_GAP) + .verticalAlign(RowVerticalAlign.CENTER) + .columns(DocumentRowColumn.fixed(DISC_D), + DocumentRowColumn.fixed(ITEM_TEXT_W)); + body.addSection("ItemDisc" + n, disc -> { + disc.spacing(0); + if (!line.icon().isBlank()) { + disc.add(PaymentsWidgets.disc(line.icon(), ACCENT_SURFACE)); + } + }); + body.addSection("ItemText" + n, text -> { + text.spacing(0); + text.addParagraph(p -> p + .name("ItemTitle" + n) + .text(line.title()) + .textStyle(style(ITEM_TITLE_SIZE, INK, DocumentTextDecoration.BOLD))); + if (!line.description().isBlank()) { + text.addParagraph(p -> p + .name("ItemSubtitle" + n) + .text(line.description()) + .textStyle(style(ITEM_SUB_SIZE, MUTED, + DocumentTextDecoration.DEFAULT)) + .lineSpacing(gap(ITEM_SUB_PITCH, ITEM_SUB_SIZE)) + .margin(new DocumentInsets( + px(668 - 648) - LINE_BOX * ITEM_TITLE_SIZE + + TEXT_TOP_BEARING + * (ITEM_TITLE_SIZE - ITEM_SUB_SIZE), + 0, 0, 0))); + } + }); + }); + }); + valueCell(row, "ItemQuantity" + n, PaymentsText.quantity(line.quantity()), + CELL_SIZE, BODY, DocumentTextDecoration.DEFAULT, TextAlign.CENTER, 0); + valueCell(row, "ItemUnitPrice" + n, PaymentsText.amount(currencyCode, line.unitPrice()), + CELL_SIZE, BODY, DocumentTextDecoration.DEFAULT, TextAlign.RIGHT, CELL_PAD_R); + valueCell(row, "ItemVat" + n, line.vatRate(), + CELL_SIZE, BODY, DocumentTextDecoration.DEFAULT, TextAlign.CENTER, 0); + valueCell(row, "ItemAmount" + n, PaymentsText.amount(currencyCode, line.amount()), + CELL_AMOUNT_SIZE, INK, DocumentTextDecoration.BOLD, TextAlign.RIGHT, AMOUNT_PAD_R); + return row.build(); + } + + private static void valueCell(RowBuilder row, String name, String value, double size, + DocumentColor color, DocumentTextDecoration decoration, + TextAlign align, double padRight) { + row.addParagraph(p -> p + .name(name) + .text(value) + .textStyle(style(size, color, decoration)) + .align(align) + // padding, not margin: a paragraph's right margin inside a row + // column is taken twice — once off the box's width and again as + // an offset — so a 22 px inset would land the text 44 px in. + .padding(new DocumentInsets(0, padRight, 0, 0))); + } + + /** + * The five column widths as shares of the table's own width. The last takes + * whatever the others leave: five independently rounded shares sum to the + * width plus a half-thousandth of a point, and the engine refuses a row + * narrower than its columns. + */ + private static DocumentRowColumn[] columnWidths() { + DocumentRowColumn[] columns = new DocumentRowColumn[COLUMN_SHARES.length]; + double assigned = 0; + for (int i = 0; i < COLUMN_SHARES.length - 1; i++) { + double width = CONTENT_W * COLUMN_SHARES[i]; + assigned += width; + columns[i] = DocumentRowColumn.fixed(width); + } + columns[COLUMN_SHARES.length - 1] = DocumentRowColumn.fixed(CONTENT_W - assigned); + return columns; + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsMasthead.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsMasthead.java new file mode 100644 index 000000000..c603a47f8 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsMasthead.java @@ -0,0 +1,319 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.dsl.ImageBuilder; +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.PathBuilder; +import com.demcha.compose.document.dsl.RowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.LayerAlign; +import com.demcha.compose.document.node.RowVerticalAlign; +import com.demcha.compose.document.style.DocumentBorders; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.templates.data.invoice.InvoiceBrand; +import com.demcha.compose.document.templates.data.invoice.InvoiceContactBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceMasthead; + +import java.util.ArrayList; +import java.util.List; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT_BAR_H; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT_BAR_OFFSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT_BAR_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_BOTTOM_PX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_H; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_LAVENDER; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_LAVENDER_QUAD; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_LEFT_PX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_NAVY_QUAD; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_RIGHT_PX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BAND_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BRAND_LOCKUP_H; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BRAND_LOCKUP_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DIVIDER; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.HEADER_RULE_OVERHANG_L; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.HEADER_RULE_OVERHANG_R; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.HEADER_RULE_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.HEADER_STACK_BOTTOM_PX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.INK; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ISSUER_NAME_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.LINE_BOX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.MARGIN_T; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.META_COL_X; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.META_LABEL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.META_LABEL_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.META_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.META_VALUE_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.META_VALUE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_MED; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_SOFT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TEXT_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TEXT_TOP_BEARING; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TITLE_DROP; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TITLE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.WORDMARK_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.gap; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.px; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.style; + +/** + * The header: the diagonal band, the lockup beside the title, the issuer block + * against the metadata grid, and the rule that closes them. + * + *

One stack, because two of its three parts are page furniture

+ * + *

The band bleeds past the top margin to the paper's edge and the accent bar + * stands left of the content box, so neither belongs to the flow. Both are + * positioned by offset from the stack's top-left; only the header content is a + * layer.

+ * + *

The lockup box is the design's, whatever fills it

+ * + *

The design's mark measures 136 × 55.3 design px. A document that brings a + * logo has it drawn to that height, so a wider or narrower mark keeps the + * design's optical weight; a document that brings only a name has the name set + * as a wordmark instead. Either way the title beside it does not move, because + * the title is a left-anchored member of the metadata column rather than a + * right-anchored member of the page.

+ */ +final class PaymentsMasthead { + + private PaymentsMasthead() { + } + + /** + * Draws the header and its closing rule. + * + * @param page the page flow + * @param brand the lockup + * @param supplier who is invoicing + * @param masthead the title and the metadata rows + */ + static void render(PageFlowBuilder page, InvoiceBrand brand, InvoiceContactBlock supplier, + InvoiceMasthead masthead) { + page.addLayerStack(stack -> stack + .name("MastheadStack") + .padding(DocumentInsets.zero()) + .position(band(), px(BAND_LEFT_PX - 51), -MARGIN_T, LayerAlign.TOP_LEFT, 0) + .position(accentBar(), ACCENT_BAR_OFFSET, 0, LayerAlign.TOP_LEFT, 0) + .layer(headerContent(brand, supplier, masthead), LayerAlign.TOP_LEFT, 1)); + renderHeaderRule(page); + } + + /** The lavender quadrilateral, then the navy one over it. */ + private static DocumentNode band() { + SectionBuilder band = new SectionBuilder(); + band.name("HeaderBand").spacing(0).padding(DocumentInsets.zero()); + band.addLayerStack(stack -> stack + .name("HeaderBandLayers") + .layer(quad("BandLavender", BAND_LAVENDER, BAND_LAVENDER_QUAD), + LayerAlign.TOP_LEFT, 0) + .layer(quad("BandNavy", INK, BAND_NAVY_QUAD), LayerAlign.TOP_LEFT, 1)); + return band.build(); + } + + /** + * One quadrilateral of the band. The corners arrive in page design pixels + * and are normalised here — {@link #nx} and {@link #ny} are the only place + * the bottom-left origin of a path's coordinate space is dealt with. + */ + private static DocumentNode quad(String name, DocumentColor fill, double[][] corners) { + PathBuilder path = new PathBuilder(); + path.name(name).size(BAND_W, BAND_H).fillColor(fill); + path.moveTo(nx(corners[0][0]), ny(corners[0][1])); + for (int i = 1; i < corners.length; i++) { + path.lineTo(nx(corners[i][0]), ny(corners[i][1])); + } + return path.closePath().build(); + } + + private static double nx(double pagePx) { + return (pagePx - BAND_LEFT_PX) / (BAND_RIGHT_PX - BAND_LEFT_PX); + } + + /** Page y runs down; a path's y runs up from its own bottom edge. */ + private static double ny(double pagePx) { + return 1.0 - pagePx / BAND_BOTTOM_PX; + } + + private static DocumentNode accentBar() { + SectionBuilder holder = new SectionBuilder(); + holder.name("AccentBarHolder").spacing(0).padding(DocumentInsets.zero()); + holder.addLine(line -> line + .name("HeaderAccentBar") + .vertical(ACCENT_BAR_H) + .thickness(ACCENT_BAR_W) + .color(ACCENT)); + return holder.build(); + } + + private static DocumentNode headerContent(InvoiceBrand brand, InvoiceContactBlock supplier, + InvoiceMasthead masthead) { + SectionBuilder content = new SectionBuilder(); + content.name("HeaderContent").spacing(0).padding(DocumentInsets.zero()); + content.addRow("Masthead", row -> renderLockupRow(row, brand, masthead)); + content.addRow("HeaderSplit", row -> { + // The two cells of the split do not share a top edge: the metadata + // block starts 11 px below the issuer name's box. + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + // 12 px by the design's own ink gap, less the 2.5 the title's + // line box gives back. + .margin(new DocumentInsets(px(12 - 2.5), 0, 0, 0)) + .columns(DocumentRowColumn.fixed(META_COL_X), DocumentRowColumn.weight(1)); + row.addSection("IssuerBlock", cell -> renderIssuer(cell, supplier)); + row.addSection("InvoiceMeta", cell -> { + cell.spacing(0).padding(new DocumentInsets(px(11), 0, 0, 0)); + renderMeta(cell, masthead.entries()); + }); + }); + return content.build(); + } + + /** + * TOP, not CENTER: the title's line box is much taller than the lockup, so + * centring would push the lockup down the row it is supposed to set the top + * edge of. + */ + private static void renderLockupRow(RowBuilder row, InvoiceBrand brand, + InvoiceMasthead masthead) { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + .margin(new DocumentInsets(px(46 - 28), 0, 0, 0)) + .columns(DocumentRowColumn.fixed(META_COL_X), DocumentRowColumn.weight(1)); + row.addSection("BrandLockup", cell -> { + cell.spacing(0).padding(new DocumentInsets(0, 0, 0, BRAND_LOCKUP_INSET)); + if (brand.logo() != null) { + cell.add(new ImageBuilder() + .name("BrandLogo") + .source(brand.logo()) + .height(BRAND_LOCKUP_H) + .build()); + } else if (!brand.name().isBlank()) { + cell.addParagraph(p -> p + .name("BrandWordmark") + .text(brand.name()) + .textStyle(style(WORDMARK_SIZE, ACCENT, DocumentTextDecoration.BOLD))); + } + }); + row.addParagraph(p -> p + .name("InvoiceTitle") + .text(masthead.title()) + .textStyle(style(TITLE_SIZE, INK, DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets(TITLE_DROP, 0, 0, 0))); + } + + /** + * The issuer's name, its address, and its registrations — the last set off + * by exactly two line pitches. That gap is authored, not drift. + */ + private static void renderIssuer(SectionBuilder cell, InvoiceContactBlock supplier) { + cell.spacing(0).padding(new DocumentInsets(0, 0, 0, TEXT_INSET)); + cell.addParagraph(p -> p + .name("IssuerName") + .text(supplier.legalName()) + .textStyle(style(ISSUER_NAME_SIZE, INK, DocumentTextDecoration.BOLD))); + cell.addSection("IssuerAddress", lines -> { + lines.spacing(gap(21.33, BODY_SIZE)) + .margin(new DocumentInsets(px(172 - 140) - LINE_BOX * ISSUER_NAME_SIZE + + TEXT_TOP_BEARING * (ISSUER_NAME_SIZE - BODY_SIZE), 0, 0, 0)); + PaymentsWidgets.textLines(lines, "IssuerAddressLine", supplier.addressLines(), + BODY_SIZE, BODY); + }); + List registrations = registrationLines(supplier); + if (!registrations.isEmpty()) { + cell.addSection("IssuerRegistration", lines -> { + lines.spacing(gap(21.0, BODY_SIZE)) + .margin(new DocumentInsets(gap(2 * 21.33, BODY_SIZE), 0, 0, 0)); + PaymentsWidgets.textLines(lines, "IssuerRegistrationLine", registrations, + BODY_SIZE, BODY); + }); + } + } + + /** The two labelled registrations a supplier may carry, as printed lines. */ + private static List registrationLines(InvoiceContactBlock supplier) { + List lines = new ArrayList<>(); + addRegistration(lines, supplier.registrationLabel(), supplier.registrationNumber()); + addRegistration(lines, supplier.taxRegistrationLabel(), supplier.taxRegistrationNumber()); + return lines; + } + + private static void addRegistration(List lines, String label, String number) { + if (number == null || number.isBlank()) { + return; + } + lines.add(label == null || label.isBlank() ? number : label + " " + number); + } + + /** + * Two stacks side by side rather than a row per entry, so the divider + * between them is one continuous border. It is the value column's left + * border, not a node: that is why it begins with the first row and ends with + * the last rather than running the block's full box. + * + *

The two stacks carry different spacings because their type sizes differ + * — a bold label and a regular value have different line boxes — and the + * shared pitch is what makes their rows line up.

+ */ + private static void renderMeta(SectionBuilder cell, List entries) { + PaymentsWidgets.layeredRow(cell, "MetaGrid", row -> { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + .columns(DocumentRowColumn.fixed(META_LABEL_W), DocumentRowColumn.weight(1)); + row.addSection("MetaLabels", labels -> { + labels.spacing(gap(META_PITCH, META_LABEL_SIZE)); + int index = 0; + for (InvoiceMasthead.Entry entry : entries) { + String name = "MetaLabel" + (++index); + labels.addParagraph(p -> p + .name(name) + .text(entry.label()) + .textStyle(style(META_LABEL_SIZE, INK, DocumentTextDecoration.BOLD))); + } + }); + row.addSection("MetaValues", values -> { + values.spacing(gap(META_PITCH, META_VALUE_SIZE)) + .borders(DocumentBorders.left(DocumentStroke.of(DIVIDER, RULE_MED))) + .padding(new DocumentInsets(0, 0, 0, META_VALUE_INSET)); + int index = 0; + for (InvoiceMasthead.Entry entry : entries) { + String name = "MetaValue" + (++index); + values.addParagraph(p -> p + .name(name) + .text(entry.value()) + .textStyle(entry.emphasized() + ? style(META_LABEL_SIZE, ACCENT, DocumentTextDecoration.BOLD) + : style(META_VALUE_SIZE, BODY, + DocumentTextDecoration.DEFAULT))); + } + }); + }); + } + + /** + * Wider than the content box on both sides — it is furniture for the header + * block rather than a divider between two pieces of content, which is why it + * overhangs. Its top margin is negative because the band, not the content, + * is the tallest layer of the stack above it. + */ + private static void renderHeaderRule(PageFlowBuilder page) { + page.addLine(line -> line + .name("HeaderRule") + .horizontal(HEADER_RULE_W) + .thickness(RULE_MED) + .color(RULE_SOFT) + .margin(new DocumentInsets( + px(359 - HEADER_STACK_BOTTOM_PX), + -HEADER_RULE_OVERHANG_R, + px(378 - 361), + -HEADER_RULE_OVERHANG_L))); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsParties.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsParties.java new file mode 100644 index 000000000..8ed94a8ed --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsParties.java @@ -0,0 +1,127 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.RowVerticalAlign; +import com.demcha.compose.document.style.DocumentBorders; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.templates.data.invoice.InvoiceRecipient; + +import java.util.ArrayList; +import java.util.List; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT_SURFACE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DISC_D; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DIVIDER; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.INK; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.LINE_BOX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_ADDRESS_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_CELL_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_DISC_GAP; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_DISC_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_LABEL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_NAME_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.PARTY_TEXT_INDENT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_THIN; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TEXT_TOP_BEARING; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.capOffset; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.gap; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.px; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.style; + +/** + * The addressed parties: who is billed, and where the work went. + * + *

One method draws both — they are the same component with different data, + * down to the registration line only one of them usually has. The divider + * belongs to the split, so it is the second cell's own left edge rather than a + * rule drawn between the two.

+ */ +final class PaymentsParties { + + private PaymentsParties() { + } + + /** + * Draws the row. + * + * @param page the page flow + * @param billTo who is billed + * @param shipTo where the work went, or a party with no name when absent + */ + static void render(PageFlowBuilder page, InvoiceRecipient billTo, InvoiceRecipient shipTo) { + List parties = new ArrayList<>(); + parties.add(new Party(PaymentsIcons.BILL_TO, billTo)); + if (!shipTo.name().isBlank()) { + parties.add(new Party(PaymentsIcons.SHIP_TO, shipTo)); + } + + page.addRow("PartiesRow", row -> { + row.spacing(0).verticalAlign(RowVerticalAlign.TOP).evenWeights(); + for (int i = 0; i < parties.size(); i++) { + Party party = parties.get(i); + boolean first = i == 0; + row.addSection("Party" + (i + 1), cell -> { + cell.spacing(0); + if (first) { + cell.padding(new DocumentInsets(0, 0, 0, PARTY_DISC_INSET)); + } else { + cell.borders(DocumentBorders.left(DocumentStroke.of(DIVIDER, RULE_THIN))) + .padding(new DocumentInsets(0, 0, 0, PARTY_CELL_INSET)); + } + renderParty(cell, party); + }); + } + }); + } + + private static void renderParty(SectionBuilder cell, Party party) { + InvoiceRecipient who = party.recipient(); + PaymentsWidgets.layeredRow(cell, "PartyHead", row -> { + row.spacing(PARTY_DISC_GAP) + .verticalAlign(RowVerticalAlign.TOP) + .columns(DocumentRowColumn.fixed(DISC_D), DocumentRowColumn.weight(1)); + row.addSection("PartyDisc", disc -> disc + .spacing(0) + .add(PaymentsWidgets.disc(party.token(), ACCENT_SURFACE))); + row.addParagraph(p -> p + .name("PartyLabel") + .text(who.heading()) + .textStyle(style(PARTY_LABEL_SIZE, INK, DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets(capOffset(391 - 378, PARTY_LABEL_SIZE), 0, 0, 0))); + }); + cell.addSection("PartyBody", block -> { + block.spacing(0).padding(new DocumentInsets(0, 0, 0, PARTY_TEXT_INDENT)); + block.addParagraph(p -> p + .name("PartyName") + .text(who.name()) + .textStyle(style(PARTY_NAME_SIZE, INK, DocumentTextDecoration.BOLD))); + block.addSection("PartyAddress", lines -> { + lines.spacing(gap(PARTY_ADDRESS_PITCH, BODY_SIZE)) + .margin(new DocumentInsets(px(451 - 423) - LINE_BOX * PARTY_NAME_SIZE + + TEXT_TOP_BEARING * (PARTY_NAME_SIZE - BODY_SIZE), 0, 0, 0)); + PaymentsWidgets.textLines(lines, "PartyAddressLine", who.addressLines(), + BODY_SIZE, BODY); + }); + // The subline is drawn UNDER the address, set off by the design's own + // gap: this sheet uses it for the registration a billed party prints + // below where it is, not for a second line of its name. + if (!who.subline().isBlank()) { + block.addParagraph(p -> p + .name("PartySubline") + .text(who.subline()) + .textStyle(style(BODY_SIZE, BODY, DocumentTextDecoration.DEFAULT)) + .margin(new DocumentInsets(gap(552 - 513, BODY_SIZE), 0, 0, 0))); + } + }); + } + + /** A party and the mark the preset gives it. */ + private record Party(String token, InvoiceRecipient recipient) { + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsSettlement.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsSettlement.java new file mode 100644 index 000000000..b3ea7847c --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsSettlement.java @@ -0,0 +1,273 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.RowVerticalAlign; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.DocumentBorders; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.templates.data.invoice.InvoicePaymentBlock; +import com.demcha.compose.document.templates.data.invoice.InvoiceTotalsBlock; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.ACCENT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.BODY; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_DISC_D; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_DISC_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_DISC_SURFACE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_FIELD_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_GLYPH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_HEAD_BOTTOM_PX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_HEAD_GAP; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_HEAD_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_LABEL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_LABEL_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_PAD; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_RADIUS; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_SURFACE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_TEXT_INDENT; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_VALUE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.CARD_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DIVIDER; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DUE_PAD; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DUE_RADIUS; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DUE_SUB_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.INK; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.LINE_BOX; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_MED; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.RULE_THIN; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.SETTLEMENT_GUTTER; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.SETTLEMENT_RIGHT_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.SURFACE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TEXT_TOP_BEARING; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTALS_LABEL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTALS_PAD_R; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTALS_PITCH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTALS_RULE_W; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTALS_TEXT_INSET; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTALS_VALUE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTAL_DUE_LABEL_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTAL_DUE_SUB_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.TOTAL_DUE_VALUE_SIZE; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.capOffset; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.gap; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.px; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.style; + +/** + * The settlement row: the payment card against the totals and the panel that + * closes them. + * + *

The split is not the even one the two rows above it use — the card is + * narrower than the totals cell, and the divider sits inside the + * gutter rather than on either cell's edge. That asymmetry is in the design.

+ */ +final class PaymentsSettlement { + + private PaymentsSettlement() { + } + + /** + * Draws the row. + * + * @param page the page flow + * @param payment the bank details and the due notice + * @param totals the summed rows and the amount due + * @param currencyCode the ISO code the figures are stated in + */ + static void render(PageFlowBuilder page, InvoicePaymentBlock payment, + InvoiceTotalsBlock totals, String currencyCode) { + page.addRow("SettlementRow", row -> { + row.spacing(SETTLEMENT_GUTTER) + .verticalAlign(RowVerticalAlign.TOP) + .columns(DocumentRowColumn.fixed(CARD_W), DocumentRowColumn.weight(1)); + row.addSection("PaymentDetails", cell -> renderCard(cell, payment)); + row.addSection("Settlement", cell -> { + // The divider sits inside the gutter rather than on either cell's + // edge, so it is this cell's left border and the cell then pads + // itself back out to where the panel's edge actually is. + cell.spacing(0) + .borders(DocumentBorders.left(DocumentStroke.of(DIVIDER, RULE_THIN))) + .padding(new DocumentInsets(0, 0, 0, SETTLEMENT_RIGHT_INSET)); + renderTotals(cell, totals, currencyCode); + renderDuePanel(cell, payment, totals, currencyCode); + }); + }); + } + + /** + * A tinted rounded card with no border — the fill alone separates it from + * the page. Built from a fill, a radius and a padding rather than a panel + * helper, which takes one padding value where this card's insets are + * asymmetric. + */ + private static void renderCard(SectionBuilder cell, InvoicePaymentBlock payment) { + cell.spacing(0) + .fillColor(CARD_SURFACE) + .cornerRadius(CARD_RADIUS) + .padding(CARD_PAD) + .keepTogether(); + + PaymentsWidgets.layeredRow(cell, "CardHead", row -> { + row.spacing(CARD_HEAD_GAP) + .verticalAlign(RowVerticalAlign.TOP) + // The disc's own inset lives inside this column, so the column + // has to be wide enough for both — otherwise the heading + // starts on the disc's left edge plus the gap rather than on + // its right edge plus the gap. + .columns(DocumentRowColumn.fixed(CARD_DISC_INSET + CARD_DISC_D), + DocumentRowColumn.weight(1)); + row.addSection("CardDisc", disc -> disc + .spacing(0) + .padding(new DocumentInsets(0, 0, 0, CARD_DISC_INSET)) + .add(PaymentsWidgets.disc(PaymentsIcons.BANK, CARD_DISC_SURFACE, + CARD_DISC_D, CARD_GLYPH))); + row.addParagraph(p -> p + .name("CardLabel") + .text(payment.heading()) + .textStyle(style(CARD_HEAD_SIZE, ACCENT, DocumentTextDecoration.BOLD)) + .margin(new DocumentInsets( + capOffset(1078 - 1065, CARD_HEAD_SIZE), 0, 0, 0))); + }); + + // Two stacks, as in the metadata grid: the rows line up on a shared + // pitch rather than on a two-column row each. + PaymentsWidgets.layeredRow(cell, "CardFields", row -> { + row.spacing(0) + .verticalAlign(RowVerticalAlign.TOP) + // Measured from the head ROW's bottom edge, which the disc + // sets — not from the heading's own line box, which is + // shorter than the disc and therefore not what the fields + // follow. + .margin(new DocumentInsets(px(1110 - CARD_HEAD_BOTTOM_PX) + - TEXT_TOP_BEARING * CARD_LABEL_SIZE, 0, 0, CARD_TEXT_INDENT)) + .columns(DocumentRowColumn.fixed(CARD_LABEL_W), DocumentRowColumn.weight(1)); + row.addSection("CardFieldLabels", labels -> { + labels.spacing(gap(CARD_FIELD_PITCH, CARD_LABEL_SIZE)); + int index = 0; + for (InvoicePaymentBlock.Field field : payment.fields()) { + String name = "CardFieldLabel" + (++index); + labels.addParagraph(p -> p + .name(name) + .text(field.label()) + .textStyle(style(CARD_LABEL_SIZE, INK, DocumentTextDecoration.BOLD))); + } + }); + row.addSection("CardFieldValues", values -> { + values.spacing(gap(CARD_FIELD_PITCH, CARD_VALUE_SIZE)); + int index = 0; + for (InvoicePaymentBlock.Field field : payment.fields()) { + String name = "CardFieldValue" + (++index); + values.addParagraph(p -> p + .name(name) + .text(field.value()) + .textStyle(style(CARD_VALUE_SIZE, BODY, + DocumentTextDecoration.DEFAULT))); + } + }); + }); + } + + private static void renderTotals(SectionBuilder cell, InvoiceTotalsBlock totals, + String currencyCode) { + cell.addSection("Totals", block -> { + block.spacing(0) + .padding(new DocumentInsets(0, TOTALS_PAD_R, 0, TOTALS_TEXT_INSET)) + .margin(new DocumentInsets( + capOffset(1082 - 1060, TOTALS_LABEL_SIZE), 0, 0, 0)) + .keepTogether(); + PaymentsWidgets.layeredRow(block, "TotalsGrid", row -> { + row.spacing(0).verticalAlign(RowVerticalAlign.TOP).evenWeights(); + row.addSection("TotalsLabels", labels -> { + labels.spacing(gap(TOTALS_PITCH, TOTALS_LABEL_SIZE)); + int index = 0; + for (InvoiceTotalsBlock.Row entry : totals.rows()) { + String name = "TotalsLabel" + (++index); + labels.addParagraph(p -> p + .name(name) + .text(entry.label()) + .textStyle(style(TOTALS_LABEL_SIZE, INK, + DocumentTextDecoration.BOLD))); + } + }); + row.addSection("TotalsValues", values -> { + values.spacing(gap(TOTALS_PITCH, TOTALS_VALUE_SIZE)); + int index = 0; + for (InvoiceTotalsBlock.Row entry : totals.rows()) { + String name = "TotalsValue" + (++index); + values.addParagraph(p -> p + .name(name) + .text(PaymentsText.amount(currencyCode, entry.amount())) + .textStyle(style(TOTALS_VALUE_SIZE, BODY, + DocumentTextDecoration.DEFAULT)) + .align(TextAlign.RIGHT)); + } + }); + }); + }); + + cell.addLine(line -> line + .name("TotalsRule") + .horizontal(TOTALS_RULE_W) + .thickness(RULE_MED) + .color(DIVIDER) + // Measured cap-to-rule, less what the last label's box hangs + // below its own cap: the line box minus the top bearing, not the + // whole line box. + .margin(new DocumentInsets( + px(1154 - 1120) - (LINE_BOX - TEXT_TOP_BEARING) * TOTALS_LABEL_SIZE, + 0, + px(1173 - 1156), + 0))); + } + + /** + * Navy fill owning white type. CENTER, not TOP: the amount is centred + * against the label block rather than aligned to its first line. + * + *

The line under the label is the payment block's due notice — the sheet + * says when the money is wanted twice, once here beside the amount and once + * in the footer, and both come from the same field.

+ */ + private static void renderDuePanel(SectionBuilder cell, InvoicePaymentBlock payment, + InvoiceTotalsBlock totals, String currencyCode) { + cell.addSection("TotalDuePanel", panel -> { + panel.spacing(0) + .fillColor(INK) + .cornerRadius(DUE_RADIUS) + .padding(DUE_PAD) + .keepTogether(); + PaymentsWidgets.layeredRow(panel, "TotalDueRow", row -> { + row.spacing(0).verticalAlign(RowVerticalAlign.CENTER).evenWeights(); + row.addSection("TotalDueText", text -> { + text.spacing(0); + text.addParagraph(p -> p + .name("TotalDueLabel") + .text(totals.totalLabel()) + .textStyle(style(TOTAL_DUE_LABEL_SIZE, SURFACE, + DocumentTextDecoration.BOLD))); + if (!payment.dueNotice().isBlank()) { + text.addParagraph(p -> p + .name("TotalDueSubLabel") + .text(payment.dueNotice()) + .textStyle(style(TOTAL_DUE_SUB_SIZE, SURFACE, + DocumentTextDecoration.DEFAULT)) + .margin(new DocumentInsets( + px(DUE_SUB_PITCH) - LINE_BOX * TOTAL_DUE_LABEL_SIZE + + TEXT_TOP_BEARING + * (TOTAL_DUE_LABEL_SIZE - TOTAL_DUE_SUB_SIZE), + 0, 0, 0))); + } + }); + row.addParagraph(p -> p + .name("TotalDueValue") + .text(PaymentsText.amount(currencyCode, totals.totalAmount())) + .textStyle(style(TOTAL_DUE_VALUE_SIZE, SURFACE, + DocumentTextDecoration.BOLD)) + .align(TextAlign.RIGHT)); + }); + }); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsStyles.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsStyles.java new file mode 100644 index 000000000..8c76cc924 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsStyles.java @@ -0,0 +1,456 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.table.DocumentTableStyle; +import com.demcha.compose.document.table.DocumentTableTextAnchor; +import com.demcha.compose.font.FontName; + +/** + * Design tokens for {@link PaymentsInvoice} — the palette, the type scale and + * the geometry every part of the sheet measures against. + * + *

One scale, and one content box

+ * + *

The design was drawn at 1055 px across an A4 page, so {@link #PX} converts + * every length below from that raster to points. The content box is the + * table's box, not the text's: body copy sits a further + * {@link #TEXT_INSET} inside it, while the table, the payment card and the + * total-due panel sit on it exactly, which is what makes it the page's real + * edge.

+ * + *

Ink measurements against box measurements

+ * + *

Every vertical distance read off the design is between two pieces of ink; + * every one the engine accepts is between two boxes. Text sits about + * {@link #TEXT_TOP_BEARING} of its size below its own box top — the half-leading + * of a 1.2× line box plus the run from the ascender down to the cap — so a gap + * read ink-to-ink loses that before it can be a margin. {@link #gap} and + * {@link #capOffset} are the two places that conversion happens.

+ * + *

Sizes come from widths, not heights

+ * + *

Each size below is a measured ink width divided by the face's own + * advance for the same string at unit cap. The two disagree for all-caps runs, + * where the body face sets about 6% wider than the design's at the same cap + * height, and width is the one to match: it keeps every glyph after the first in + * place, where matching the height would leave the run's right edge short by 6% + * of its length.

+ */ +final class PaymentsStyles { + + private PaymentsStyles() { + } + + static final DocumentPageSize PAGE = DocumentPageSize.A4; + static final double PAGE_WIDTH = PAGE.width(); + + /** The design's raster is 1055 px wide; every length below came off it. */ + static final double PX = PAGE_WIDTH / 1055.0; + + static final double MARGIN_L = px(51); + static final double MARGIN_R = px(52); + static final double MARGIN_T = px(28); + + /** + * The band the page number sits in, measured up from the paper's edge, and + * page one's bottom margin, which is the same thing. + * + *

The design is a one-page sheet and has no page number, but this preset + * flows, and a financial record that paginates has to make a missing page + * detectable. The band is reserved rather than merely drawn into: on a + * paginated invoice page one is all table, and without the margin the last + * row runs down to where the number is.

+ */ + static final double PAGE_NUMBER_BAND = px(32); + static final double MARGIN_B_FIRST = PAGE_NUMBER_BAND; + + /** + * A continuation page reserves more. Its last table row would otherwise end + * against the paper rather than against a margin, which page one never shows + * because its content stops well above the fold. + */ + static final double MARGIN_B_LATER = px(58); + + static final DocumentInsets PAGE_MARGIN = + new DocumentInsets(MARGIN_T, MARGIN_R, MARGIN_B_FIRST, MARGIN_L); + static final DocumentInsets CONTINUATION_MARGIN = + new DocumentInsets(MARGIN_T, MARGIN_R, MARGIN_B_LATER, MARGIN_L); + + /** Every width in this preset is a fraction of this: 952 design px. */ + static final double CONTENT_W = PAGE_WIDTH - MARGIN_L - MARGIN_R; + + /** How far body copy sits inside the content box the table defines. */ + static final double TEXT_INSET = px(11); + + static final DocumentColor ACCENT = DocumentColor.rgb(99, 91, 255); + static final DocumentColor INK = DocumentColor.rgb(14, 24, 72); + static final DocumentColor BODY = DocumentColor.rgb(46, 54, 92); + static final DocumentColor MUTED = DocumentColor.rgb(70, 76, 110); + static final DocumentColor SURFACE = DocumentColor.WHITE; + + /** + * Three lavender surfaces, not one. Sampled as rings inside each disc, clear + * of the glyph: the party and line item discs agree, the notes disc is two + * steps paler and the payment card's is two steps deeper. They look like one + * colour and measure as three. + */ + static final DocumentColor ACCENT_SURFACE = DocumentColor.rgb(224, 218, 253); + static final DocumentColor NOTES_SURFACE = DocumentColor.rgb(226, 222, 253); + static final DocumentColor CARD_DISC_SURFACE = DocumentColor.rgb(216, 208, 252); + static final DocumentColor CARD_SURFACE = DocumentColor.rgb(248, 247, 254); + static final DocumentColor TABLE_HEAD_FILL = DocumentColor.rgb(240, 238, 254); + static final DocumentColor BAND_LAVENDER = DocumentColor.rgb(205, 194, 248); + static final DocumentColor HAIRLINE = DocumentColor.rgb(227, 227, 238); + static final DocumentColor RULE_SOFT = DocumentColor.rgb(213, 208, 239); + static final DocumentColor RULE_STRONG = DocumentColor.rgb(159, 145, 222); + static final DocumentColor DIVIDER = DocumentColor.rgb(199, 191, 236); + + static final FontName FACE = FontName.LATO; + + /** + * The face's cap heights in ems. Regular and bold differ, and the difference + * matters: every size below is a measured cap divided by the ratio for the + * weight it is set in. + */ + private static final double CAP_RATIO_REGULAR = 0.7165; + private static final double CAP_RATIO_BOLD = 0.723; + + /** How far below its own box top a line of text sits. */ + static final double TEXT_TOP_BEARING = 0.235; + + /** + * {@code lineSpacing} is points added between lines over a line box + * of 1.2× the type size, so a measured pitch converts as + * {@code pitch - 1.2 * size}, never as a ratio. Section spacing works the + * same way: it is the gap between sibling boxes, so a measured top-to-top + * pitch has the first box's height taken off it. + */ + static final double LINE_BOX = 1.2; + + static final double TITLE_SIZE = sizeB(46.53); + static final double ISSUER_NAME_SIZE = sizeB(14.65); + static final double PARTY_NAME_SIZE = sizeB(13.11); + static final double PARTY_LABEL_SIZE = sizeB(10.91); + static final double META_LABEL_SIZE = sizeB(11.59); + static final double META_VALUE_SIZE = sizeR(10.35); + static final double BODY_SIZE = sizeR(11.08); + static final double TABLE_HEAD_SIZE = sizeB(10.22); + + /** The currency qualifier after the amount label is a smaller run of it. */ + static final double TABLE_HEAD_SMALL_SIZE = sizeB(8.0); + static final double ITEM_TITLE_SIZE = sizeB(12.18); + static final double ITEM_SUB_SIZE = sizeR(10.17); + static final double CELL_SIZE = sizeR(11.19); + static final double CELL_AMOUNT_SIZE = sizeB(12.25); + static final double CARD_HEAD_SIZE = sizeB(11.30); + static final double CARD_LABEL_SIZE = sizeB(9.44); + static final double CARD_VALUE_SIZE = sizeR(9.33); + static final double TOTALS_LABEL_SIZE = sizeB(12.07); + static final double TOTALS_VALUE_SIZE = sizeR(11.72); + static final double TOTAL_DUE_LABEL_SIZE = sizeB(13.04); + static final double TOTAL_DUE_SUB_SIZE = sizeR(10.79); + static final double TOTAL_DUE_VALUE_SIZE = sizeB(22.18); + static final double NOTES_LABEL_SIZE = sizeB(10.29); + static final double NOTES_BODY_SIZE = sizeR(10.07); + static final double FOOTER_DUE_TITLE_SIZE = sizeB(10.16); + static final double FOOTER_SUPPORT_TITLE_SIZE = sizeB(11.12); + static final double FOOTER_SUB_SIZE = sizeR(10.05); + static final double PAGE_NUMBER_SIZE = sizeR(7.5); + + /* + * The diagonal band: two slanted quadrilaterals in the gutter between the + * lockup and the title, the lavender running from the paper's top edge and + * the navy drawn over it from a third of the way down. Corners are given in + * PAGE design pixels — the coordinates that were actually measured — and + * normalised in one place by nx/ny, because a path's own coordinates run + * 0..1 inside its box with the origin at the BOTTOM-LEFT, which is the + * opposite of the direction every measurement here was taken in. + */ + static final double BAND_LEFT_PX = 385.4; + static final double BAND_RIGHT_PX = 556.0; + static final double BAND_BOTTOM_PX = 360.0; + static final double BAND_W = px(BAND_RIGHT_PX - BAND_LEFT_PX); + static final double BAND_H = px(BAND_BOTTOM_PX); + + /** Top-left, top-right, bottom-right, bottom-left, in page design pixels. */ + static final double[][] BAND_LAVENDER_QUAD = { + {495.0, 0.0}, {539.6, 0.0}, {422.4, 360.0}, {385.4, 360.0} + }; + static final double[][] BAND_NAVY_QUAD = { + {507.7, 98.0}, {554.7, 98.0}, {469.3, 360.0}, {422.3, 360.0} + }; + + /** + * How tall the masthead's layer stack comes out. The band is the tallest + * layer in it, so the stack's own height is the band's — and the header rule + * that follows the stack has to be pulled back up by the difference between + * the band's bottom and where the stack ends. The band is offset up by + * {@link #MARGIN_T} to reach the paper, and the stack is sized to its layers + * without accounting for that offset, so the stack ends that much below the + * band's visible bottom. + */ + static final double HEADER_STACK_BOTTOM_PX = BAND_BOTTOM_PX + 28.0; + + /** + * Where the metadata column starts, as an offset into the content box. The + * title shares it: the title is a left-anchored member of the metadata + * column, not a right-anchored member of the page, and its measured left + * edge is the metadata labels' to the pixel. + */ + static final double META_COL_X = px(639 - 51); + + /** + * The lockup's box, measured off the design's own ink: 136 design px wide + * and 55.3 tall, starting at {@link #BRAND_LOCKUP_INSET} into the content + * box. A caller's logo is sized by the height, so a wider or narrower mark + * keeps the design's optical weight and stays on the same top edge. + */ + static final double BRAND_LOCKUP_W = px(136); + static final double BRAND_LOCKUP_H = px(55.3); + static final double BRAND_LOCKUP_INSET = px(63 - 51); + + /** + * The size a wordmark is set at when a document brings a name rather than a + * logo. Not the lockup's own height: a name is usually longer than a + * drawn mark and set at that height would run out of the column, so it is + * sized to read as a lockup beside the title rather than to fill the box. + */ + static final double WORDMARK_SIZE = sizeB(28); + + /** + * The lockup sets the row's top edge, so the title's own line box needs + * pushing down to its measured cap. The 1.4 px correction is measured, not + * derived: at 46 design px of cap the top bearing runs slightly deeper than + * the fraction that holds at body sizes. + */ + static final double TITLE_DROP = px(65 - 46 - 1.4) - TEXT_TOP_BEARING * TITLE_SIZE; + + static final double ACCENT_BAR_W = px(2); + static final double ACCENT_BAR_H = px(310); + + /** The bar is anchored to the PAGE, 22 design px left of the content box. */ + static final double ACCENT_BAR_OFFSET = px(-22); + + static final double META_LABEL_W = px(781.5 - 639); + static final double META_VALUE_INSET = px(805 - 781.5); + static final double META_PITCH = 30.17; + + static final double HEADER_RULE_W = px(1018 - 30); + + /** How far the rule reaches past the content box: 21 design px left, 15 right. */ + static final double HEADER_RULE_OVERHANG_L = px(51 - 30); + static final double HEADER_RULE_OVERHANG_R = px(1018 - 1003); + static final double RULE_THIN = px(1.4); + static final double RULE_MED = px(2.0); + + static final double DISC_D = px(40); + + /** + * Box, not ink. An icon's size is the box the artwork is fitted into, + * and every glyph here carries about two units of padding on each side of + * its 24-unit viewBox — so the ink comes back at roughly this fraction of + * what is asked for. Each glyph size below is therefore the design's + * measured ink divided by that ratio, which is why they read larger than the + * marks they produce. + */ + static final double GLYPH_INK_RATIO = 0.75; + static final double DISC_GLYPH = glyph(21); + static final double PARTY_DISC_INSET = px(60 - 51); + static final double PARTY_DISC_GAP = px(118 - 100); + static final double PARTY_TEXT_INDENT = DISC_D + PARTY_DISC_GAP; + + /** The second cell's own left padding, past the divider that is its left border. */ + static final double PARTY_CELL_INSET = px(567 - 527); + static final double PARTY_ADDRESS_PITCH = 20.7; + + /** + * Column shares of the content width. Derived from each column's measured + * anchor rather than from boundaries the design never draws: the + * description text's left edge, the quantity digits' centre, the unit + * price's right edge, the tax centre and the amount's right edge. + */ + static final double[] COLUMN_SHARES = { + 379.0 / 952.0, 127.0 / 952.0, 143.0 / 952.0, 139.0 / 952.0, 164.0 / 952.0 + }; + + static final double HEAD_PAD_T = px(11.7); + static final double HEAD_PAD_B = px(12.3); + static final double CELL_PAD_V = px(8.5); + + /** The description cell's own inset; the disc sits on it and the text past it. */ + static final double ITEM_CELL_INSET = px(62 - 51); + static final double ITEM_DISC_GAP = px(118 - 102); + + /** + * What a line's description wraps at. Not a free choice: the design breaks + * one description before its parenthetical and keeps the next whole, which + * puts the width between 248 and 256 design px. Everything about the table's + * row heights follows from it. + */ + static final double ITEM_TEXT_W = px(250); + static final double ITEM_SUB_PITCH = 18.0; + static final double CELL_PAD_R = px(22); + static final double AMOUNT_PAD_R = px(23); + static final double DESC_PAD_L = px(118 - 51); + + static final DocumentTableStyle HEADER_CELL_STYLE = DocumentTableStyle.builder() + .fillColor(TABLE_HEAD_FILL) + .stroke(DocumentStroke.of(HAIRLINE, RULE_THIN)) + .padding(new DocumentInsets(HEAD_PAD_T, 0, HEAD_PAD_B, 0)) + .textAnchor(DocumentTableTextAnchor.CENTER_LEFT) + .build(); + + /** + * Every rule in the table comes from this one stroke. A cell strokes all + * four of its own edges and a one-column table has no interior vertical edge + * — so the same stroke that draws the separator between two rows draws the + * table's left and right sides, and nothing else. That is exactly the + * topology the design shows. + */ + static final DocumentTableStyle ITEM_CELL_STYLE = DocumentTableStyle.builder() + .stroke(DocumentStroke.of(HAIRLINE, RULE_THIN)) + .padding(new DocumentInsets(CELL_PAD_V, 0, CELL_PAD_V, 0)) + .textAnchor(DocumentTableTextAnchor.CENTER_LEFT) + .build(); + + /** + * The settlement split is NOT the even split the two rows above it use: the + * card is 440 design px, the totals cell 455, and the divider sits inside + * the gutter rather than on either cell's edge. That asymmetry is in the + * design, not in the measurement. + */ + static final double CARD_W = px(440); + static final double SETTLEMENT_GUTTER = px(527.5 - 491); + static final double SETTLEMENT_RIGHT_INSET = px(547 - 527.5); + + static final double CARD_RADIUS = px(10); + + /** + * The card's own disc is smaller than the party and line item discs — 38 + * design px against 40 — and it sits 5 px below the card's top edge, not 12. + * Both were measured off the card rather than assumed from the other three, + * and both matter: the disc sets the head row's height, and that height is + * what the field block's margin is measured from. + */ + static final double CARD_DISC_D = px(38); + static final double CARD_GLYPH = glyph(21); + static final DocumentInsets CARD_PAD = + new DocumentInsets(px(1065 - 1060), px(20), px(1252 - 1238.5), 0); + static final double CARD_DISC_INSET = px(63.5 - 51); + static final double CARD_HEAD_GAP = px(118 - 101); + static final double CARD_LABEL_W = px(256 - 116); + static final double CARD_TEXT_INDENT = px(116 - 51); + static final double CARD_FIELD_PITCH = 19.3; + + /** The disc's bottom edge, measured down from the card's content top. */ + static final double CARD_HEAD_BOTTOM_PX = 1065 + 38; + + static final double TOTALS_TEXT_INSET = px(566 - 547); + static final double TOTALS_PAD_R = px(1003 - 987); + static final double TOTALS_PITCH = 38.0; + static final double TOTALS_RULE_W = px(1002 - 547); + + static final double DUE_RADIUS = px(10); + + /** + * The top inset is the distance to the label's box, not to its cap: + * the measured cap gap less what the text hangs below its own box top. + * Taking the cap gap as a padding makes the panel 4 px too tall and pushes + * its bottom past the card's, which the design has ending on the same line. + */ + static final DocumentInsets DUE_PAD = new DocumentInsets( + px(1194 - 1173) - TEXT_TOP_BEARING * TOTAL_DUE_LABEL_SIZE, + px(1002 - 984), + px(1251 - 1235), + px(566 - 547)); + static final double DUE_SUB_PITCH = 28.0; + + static final double NOTES_DISC_D = px(39); + static final double NOTES_DISC_INSET = px(53 - 51); + static final double NOTES_TEXT_INDENT = px(106 - 53); + static final double NOTES_GLYPH = glyph(22); + static final double NOTES_PITCH = 22.0; + + static final double FOOTER_RULE_W = px(1005 - 50); + + /** + * The rule's own gaps. The 1.2 px correction is measured and is taken off + * the gap below as well as added to the gap above: the footer's text already + * sits on the design's line, so the rule has to move without taking the + * footer with it. + */ + static final double FOOTER_RULE_NUDGE = px(1.2); + static final double FOOTER_RULE_GAP_ABOVE = px(1373 - 1343) + FOOTER_RULE_NUDGE; + static final double FOOTER_RULE_GAP_BELOW = px(1397 - 1374) - FOOTER_RULE_NUDGE; + + /** One design px past the content box on the left, two on the right. Measured. */ + static final double FOOTER_RULE_OVERHANG_L = px(51 - 50); + static final double FOOTER_RULE_OVERHANG_R = px(1005 - 1003); + static final double FOOTER_SPLIT_X = px(541 - 51); + static final double FOOTER_DUE_ICON_COL = px(112 - 55); + static final double FOOTER_DUE_ICON_INSET = px(55 - 51); + static final double FOOTER_DUE_ICON = glyph(37); + static final double FOOTER_SUPPORT_CELL_INSET = px(594 - 541); + static final double FOOTER_SUPPORT_ICON_COL = px(659 - 594); + static final double FOOTER_SUPPORT_ICON = glyph(39); + + static double px(double designPixels) { + return designPixels * PX; + } + + /** + * An icon box sized so its ink comes out at {@code inkPx} design pixels — + * see {@link #GLYPH_INK_RATIO}. Every glyph goes through here, so the one + * measured ratio lives in one place. + */ + static double glyph(double inkPx) { + return px(inkPx) / GLYPH_INK_RATIO; + } + + /** Half the padding {@link #glyph} adds — what a left-anchored glyph gives back. */ + static double glyphPad(double inkPx) { + return (glyph(inkPx) - px(inkPx)) / 2.0; + } + + /** A regular-weight size named by the cap height it reproduces, in design px. */ + static double sizeR(double capPx) { + return px(capPx) / CAP_RATIO_REGULAR; + } + + /** The same for bold, whose cap ratio differs. */ + static double sizeB(double capPx) { + return px(capPx) / CAP_RATIO_BOLD; + } + + /** + * Where to start a text box so its caps land {@code capPx} below the top of + * the block it shares a row with — a label beside a glyph, whose design + * position is its own cap top rather than the glyph's centre. + */ + static double capOffset(double capPx, double size) { + return Math.max(0, px(capPx) - TEXT_TOP_BEARING * size); + } + + /** + * A measured top-to-top pitch turned into the gap the engine wants: the + * first box's own height comes off it, because spacing is between boxes. + */ + static double gap(double pitchPx, double sizeOfFirst) { + return Math.max(0, px(pitchPx) - LINE_BOX * sizeOfFirst); + } + + static DocumentTextStyle style(double size, DocumentColor color, + DocumentTextDecoration decoration) { + return DocumentTextStyle.builder() + .fontName(FACE) + .size(size) + .color(color) + .decoration(decoration) + .build(); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsText.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsText.java new file mode 100644 index 000000000..827ce83cf --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsText.java @@ -0,0 +1,62 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import java.math.BigDecimal; +import java.text.NumberFormat; +import java.util.Currency; +import java.util.Locale; + +/** + * How the Payments sheet writes its figures. + * + *

The locale is stated, never inherited

+ * + *

Both the grouping of a number and the symbol for a currency code are + * locale-dependent — {@code USD} is {@code $} in English and {@code US$} in the + * root locale, {@code JPY} is {@code ¥} or {@code JP¥} — so a preset that let + * the JVM's default locale decide would render a different sheet on a different + * machine, and its baselines would move with the machine rather than with the + * design. Both helpers below pin {@link Locale#ENGLISH}.

+ */ +final class PaymentsText { + + private PaymentsText() { + } + + /** An amount, grouped and always to two places. */ + static String money(BigDecimal value) { + NumberFormat format = NumberFormat.getNumberInstance(Locale.ENGLISH); + format.setGroupingUsed(true); + format.setMinimumFractionDigits(2); + format.setMaximumFractionDigits(2); + return format.format(value); + } + + /** A quantity, written without trailing zeros a whole number does not need. */ + static String quantity(BigDecimal value) { + BigDecimal stripped = value.stripTrailingZeros(); + return stripped.scale() <= 0 ? stripped.toBigInteger().toString() : stripped.toPlainString(); + } + + /** + * The symbol for an ISO currency code, or the code itself when there is no + * symbol for it and the empty string when a document states no currency. + * + * @param currencyCode the ISO code the figures are stated in + * @return what to print before an amount + */ + static String symbol(String currencyCode) { + if (currencyCode == null || currencyCode.isBlank()) { + return ""; + } + try { + return Currency.getInstance(currencyCode.trim()).getSymbol(Locale.ENGLISH); + } catch (IllegalArgumentException notACurrency) { + return currencyCode.trim(); + } + } + + /** An amount behind its currency's symbol. */ + static String amount(String currencyCode, BigDecimal value) { + return symbol(currencyCode) + money(value); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsWidgets.java b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsWidgets.java new file mode 100644 index 000000000..0fffc324d --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/invoice/presets/PaymentsWidgets.java @@ -0,0 +1,129 @@ +package com.demcha.compose.document.templates.invoice.presets; + +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.RowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +import com.demcha.compose.document.node.DocumentLinkOptions; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.HorizontalAlign; +import com.demcha.compose.document.node.LayerAlign; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentTextDecoration; + +import java.util.List; +import java.util.function.Consumer; + +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DISC_D; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.DISC_GLYPH; +import static com.demcha.compose.document.templates.invoice.presets.PaymentsStyles.style; + +/** + * The pieces more than one part of the Payments sheet draws: the wrapper every + * horizontal pair inside a cell goes through, the disc that owns its glyph, and + * the two text helpers. + */ +final class PaymentsWidgets { + + private PaymentsWidgets() { + } + + /** + * A row that survives inside a row cell. + * + *

A row nested directly in a row cell is refused by the layout compiler. + * Every horizontal pair that lives inside a cell in this preset goes through + * here, wrapped in a single layer of a stack.

+ * + * @param parent the section to add the row to + * @param name the node name, for review and rollback + * @param spec describes the row + */ + static void layeredRow(SectionBuilder parent, String name, Consumer spec) { + SectionBuilder holder = new SectionBuilder(); + holder.name(name + "Holder"); + holder.addRow(name, spec); + DocumentNode node = holder.build(); + parent.addLayerStack(stack -> stack + .name(name + "Layer") + .layer(node, LayerAlign.TOP_LEFT, 0)); + } + + /** + * A filled disc that owns its glyph: the glyph sits inside the fill, so it + * is a child of the shape rather than a coloured circle with an icon + * dropped near it. + * + * @param token the packaged mark + * @param fill the disc colour + * @return the disc node + */ + static DocumentNode disc(String token, DocumentColor fill) { + return disc(token, fill, DISC_D, DISC_GLYPH); + } + + /** + * The same, at a stated diameter — the payment card's and the notes' discs + * are each their own size, measured off the block they belong to. + * + * @param token the packaged mark + * @param fill the disc colour + * @param diameter the disc's diameter + * @param glyph the glyph's box inside it + * @return the disc node + */ + static DocumentNode disc(String token, DocumentColor fill, double diameter, double glyph) { + return new ShapeContainerBuilder() + .name("Disc" + token) + .circle(diameter) + .fillColor(fill) + .center(glyphNode(token, glyph, HorizontalAlign.CENTER)) + .build(); + } + + /** + * A glyph in a box of its own, so a shape can own it. A glyph that leads a + * text row is aligned left: its column's width is the distance to the text, + * not a box for the glyph to sit in the middle of. + * + * @param token the packaged mark + * @param size the glyph's box + * @param align where the glyph sits in it + * @return the glyph node + */ + static DocumentNode glyphNode(String token, double size, HorizontalAlign align) { + return new SectionBuilder() + .name("Glyph" + token) + .spacing(0) + .addSvgIcon(PaymentsIcons.icon(token), size, align) + .build(); + } + + /** + * A block of plain lines, one paragraph each. + * + * @param block the section to fill + * @param prefix the node-name prefix, numbered from one + * @param lines the lines + * @param size their type size + * @param color their colour + */ + static void textLines(SectionBuilder block, String prefix, List lines, + double size, DocumentColor color) { + int index = 0; + for (String line : lines) { + String name = prefix + (++index); + block.addParagraph(p -> p + .name(name) + .text(line) + .textStyle(style(size, color, DocumentTextDecoration.DEFAULT))); + } + } + + /** The paragraph, made reachable when there is somewhere for it to point. */ + static ParagraphBuilder linked(ParagraphBuilder paragraph, String href) { + return href == null || href.isBlank() + ? paragraph + : paragraph.link(new DocumentLinkOptions(href)); + } +} diff --git a/templates/src/main/resources/templates/invoice/payments/icons/bank.svg b/templates/src/main/resources/templates/invoice/payments/icons/bank.svg new file mode 100644 index 000000000..0058b58a9 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/bank.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/bill-to.svg b/templates/src/main/resources/templates/invoice/payments/icons/bill-to.svg new file mode 100644 index 000000000..0aaae9c3f --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/bill-to.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/calendar.svg b/templates/src/main/resources/templates/invoice/payments/icons/calendar.svg new file mode 100644 index 000000000..d1952bcc7 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/calendar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/card-settings.svg b/templates/src/main/resources/templates/invoice/payments/icons/card-settings.svg new file mode 100644 index 000000000..a10a3b2c0 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/card-settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/card.svg b/templates/src/main/resources/templates/invoice/payments/icons/card.svg new file mode 100644 index 000000000..8a9fe32ed --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/document.svg b/templates/src/main/resources/templates/invoice/payments/icons/document.svg new file mode 100644 index 000000000..5aec8edf5 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/document.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/globe.svg b/templates/src/main/resources/templates/invoice/payments/icons/globe.svg new file mode 100644 index 000000000..323edcf15 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/headset.svg b/templates/src/main/resources/templates/invoice/payments/icons/headset.svg new file mode 100644 index 000000000..3031d0940 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/headset.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/mobile.svg b/templates/src/main/resources/templates/invoice/payments/icons/mobile.svg new file mode 100644 index 000000000..4e5915f59 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/mobile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/shield.svg b/templates/src/main/resources/templates/invoice/payments/icons/shield.svg new file mode 100644 index 000000000..b08b7e7a9 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/shield.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/ship-to.svg b/templates/src/main/resources/templates/invoice/payments/icons/ship-to.svg new file mode 100644 index 000000000..03efd2212 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/ship-to.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/invoice/payments/icons/support.svg b/templates/src/main/resources/templates/invoice/payments/icons/support.svg new file mode 100644 index 000000000..ef8b01748 --- /dev/null +++ b/templates/src/main/resources/templates/invoice/payments/icons/support.svg @@ -0,0 +1 @@ + \ No newline at end of file