diff --git a/assets/readme/examples/financial-report.pdf b/assets/readme/examples/financial-report.pdf new file mode 100644 index 000000000..afa1c729f Binary files /dev/null and b/assets/readme/examples/financial-report.pdf differ diff --git a/examples/README.md b/examples/README.md index f1cb46086..9065b50bb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -101,6 +101,7 @@ are with the canonical DSL, then jump to its detailed section below. | [Layout snapshot regression](#layout-snapshot-regression) | Deterministic `layoutSnapshot()` workflow with baseline + drift report — production regression-testing pattern | [PDF](../assets/readme/examples/invoice-snapshot-regression.pdf) · [Source](src/main/java/com/demcha/examples/features/snapshots/LayoutSnapshotRegressionExample.java) | | [Debug overlay](#debug-overlay) | `DocumentDebugOptions` — guide lines + semantic node-path labels on the sheet; trace any misplaced block back to the builder call that authored it | [PDF](../assets/readme/examples/debug-overlay.pdf) · [Source](src/main/java/com/demcha/examples/features/debug/DebugOverlayExample.java) | | [Business report cover](#business-report-cover) | Single-page Q1 investor brief — hero image, KPI cards, bar chart, metrics table | [PDF](../assets/readme/examples/business-report.pdf) · [Source](src/main/java/com/demcha/examples/flagships/BusinessReportExample.java) | +| Financial report one-pager | Single-page monthly financial dashboard — three margin gauges, cash & stacked-OPEX charts, a revenue donut, and forecast bars; all v1.8 native vector charts plus inline sparklines and a path-clipped photo masthead | [PDF](../assets/readme/examples/financial-report.pdf) · [Source](src/main/java/com/demcha/examples/flagships/FinancialReportExample.java) | | [Master showcase](#master-showcase) | Kitchen-sink "Q2 sample report" combining the canonical surface end-to-end | [PDF](../assets/readme/examples/master-showcase.pdf) · [Source](src/main/java/com/demcha/examples/flagships/MasterShowcaseExample.java) | | Feature catalog | Browsable reference PDF: every shipped capability as a block — outline-clickable heading, the exact API call, the rendered result right under it | [PDF](../assets/readme/examples/feature-catalog.pdf) · [Source](src/main/java/com/demcha/examples/flagships/FeatureCatalogExample.java) | diff --git a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java index 3d5e4c916..e6705c261 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -24,6 +24,7 @@ import com.demcha.examples.flagships.BusinessReportExample; import com.demcha.examples.flagships.EngineDeckExample; import com.demcha.examples.flagships.FeatureCatalogExample; +import com.demcha.examples.flagships.FinancialReportExample; import com.demcha.examples.flagships.MasterShowcaseExample; import com.demcha.examples.flagships.ModuleFirstFileExample; import com.demcha.examples.templates.coverletter.v2.CvBlueBannerLetterV2Example; @@ -169,5 +170,6 @@ public static void main(String[] args) throws Exception { System.out.println("Generated: " + FeatureCatalogExample.generate()); System.out.println("Generated: " + BusinessReportExample.generate()); System.out.println("Generated: " + EngineDeckExample.generate()); + System.out.println("Generated: " + FinancialReportExample.generate()); } } diff --git a/examples/src/main/java/com/demcha/examples/flagships/FinancialReportExample.java b/examples/src/main/java/com/demcha/examples/flagships/FinancialReportExample.java new file mode 100644 index 000000000..6b1961ee3 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/flagships/FinancialReportExample.java @@ -0,0 +1,575 @@ +package com.demcha.examples.flagships; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.chart.AxisSpec; +import com.demcha.compose.document.chart.BarGrouping; +import com.demcha.compose.document.chart.ChartData; +import com.demcha.compose.document.chart.ChartSize; +import com.demcha.compose.document.chart.ChartSpec; +import com.demcha.compose.document.chart.ChartStyle; +import com.demcha.compose.document.chart.LegendPosition; +import com.demcha.compose.document.chart.NumberFormatSpec; +import com.demcha.compose.document.chart.SliceLabelMode; +import com.demcha.compose.document.chart.ValueLabelMode; +import com.demcha.compose.document.dsl.ImageBuilder; +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +import com.demcha.compose.document.image.DocumentImageData; +import com.demcha.compose.document.image.DocumentImageFitMode; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.HorizontalAlign; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.ClipPolicy; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentCornerRadius; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentPaint; +import com.demcha.compose.document.style.DocumentPathSegment; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.font.FontName; +import com.demcha.examples.support.ExampleOutputPaths; + +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +/** + * One-pager Business Monthly Financial Report dashboard, modelled + * after the classic single-sheet infographic brief: a photo masthead, three + * radial profit-margin gauges, a "Cash position" block (cash-balance KPI tile, + * a monthly cash bar chart, a stacked OPEX chart, and a revenue-breakdown + * donut), and a "Forecast Financial Analysis" block (a forecast revenue bar + * chart and a forecast-vs-actual cost breakdown). + * + *

This is a tour of the v1.8 illustrative additions, every + * visual drawn from deterministic geometry as native PDF curves — no raster:

+ * + * + *

Combo charts (bars with an overlaid trend line) are not a native chart + * kind, so the OPEX block reproduces the source's stacked bars and the trend + * is shown instead as a masthead sparkline.

+ * + * @author Artem Demchyshyn + */ +public final class FinancialReportExample { + + // ─────────────────── Palette (matches the reference) ────────────────── + private static final DocumentColor PAPER = DocumentColor.rgb(244, 239, 227); + private static final DocumentColor BAND = DocumentColor.rgb(224, 215, 191); + private static final DocumentColor CARD = DocumentColor.WHITE; + private static final DocumentColor CARD_RING = DocumentColor.rgb(223, 214, 192); + private static final DocumentColor INK = DocumentColor.rgb(38, 40, 46); + private static final DocumentColor MUTED = DocumentColor.rgb(122, 122, 128); + private static final DocumentColor TEAL = DocumentColor.rgb(52, 142, 140); + private static final DocumentColor TEAL_DARK = DocumentColor.rgb(34, 102, 100); + private static final DocumentColor ORANGE = DocumentColor.rgb(216, 90, 46); + private static final DocumentColor ORANGE_DEEP = DocumentColor.rgb(118, 50, 32); + private static final DocumentColor ORANGE_SOFT = DocumentColor.rgb(232, 150, 92); + private static final DocumentColor GAUGE_TRACK = DocumentColor.rgb(228, 220, 194); + + private FinancialReportExample() { + } + + /** + * Renders the one-pager financial report dashboard. + * + * @return the generated PDF path + * @throws Exception when rendering or resource IO fails + */ + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare("flagships", "financial-report.pdf"); + try (DocumentSession document = GraphCompose.document(outputFile) + .pageSize(DocumentPageSize.A4) + .pageBackground(PAPER) + .margin(18, 28, 14, 28) + .create()) { + compose(document); + document.buildPdf(); + } + return outputFile; + } + + /** + * Composes the one-pager into {@code document}. Shared by {@link #generate()} + * and the layout snapshot test, so both lay out identical geometry — the test + * creates its own session with the same page size and margin. + * + * @param document the open session to compose into + * @throws Exception when the masthead photo resource cannot be read + */ + static void compose(DocumentSession document) throws Exception { + DocumentImageData photo = loadMastheadPhoto(); + PageFlowBuilder flow = document.pageFlow() + .name("FinancialReport") + .spacing(8); + + // ── Masthead: slanted photo + title + subtitle + sparklines ── + flow.addRow("Masthead", row -> row + .spacing(16) + .weights(5, 13) + .addSection("Photo", s -> s.add(mastheadPhoto(photo))) + .addSection("Title", s -> s + .spacing(2) + .addParagraph(p -> p + .text("One Pager Business") + .textStyle(titleStyle()) + .margin(DocumentInsets.zero())) + .addParagraph(p -> p + .text("Monthly Financial Report") + .textStyle(titleStyle()) + .margin(DocumentInsets.zero())) + .addParagraph(p -> p + .text("This one pager covers the business monthly financial " + + "report to assess performance — gross, operating, and " + + "net profit margin, plus cash, OPEX, and forecast detail.") + .textStyle(subtitleStyle()) + .lineSpacing(1.35) + .margin(new DocumentInsets(5, 0, 0, 0))) + .addRich(r -> r + .style("Revenue ", legendStyle()) + .sparkline(46, 9, TEAL, 65, 70, 74, 81, 88) + .style(" Net margin ", legendStyle()) + .sparklineLine(46, 9, 1.6, ORANGE, 9, 10, 11, 11.5, 12)))); + + // ── Centred report-period band with a centred accent rule ── + flow.addSection("Period", s -> s + .spacing(4) + .addParagraph(p -> p + .text("Financial Report : May 2023") + .textStyle(periodStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .addAligned(HorizontalAlign.CENTER, new ShapeBuilder() + .name("PeriodRule") + .size(120, 2.5) + .fillColor(ORANGE) + .margin(DocumentInsets.zero()) + .build())); + + // ── Three profit-margin gauges ── + flow.addRow("Gauges", row -> row + .spacing(14) + .evenWeights() + .addSection("Gauge1", s -> gauge(s, 79, "Gross profit")) + .addSection("Gauge2", s -> gauge(s, 30, "Operating profit margin")) + .addSection("Gauge3", s -> gauge(s, 12, "Net profit margin"))); + + // ── Cash position ── + sectionBand(flow, "Cash position"); + + flow.addRow("CashRow1", row -> row + .spacing(14) + .weights(8, 12) + .addSection("CashBalance", FinancialReportExample::cashBalanceCard) + .addSection("CashEnd", FinancialReportExample::cashAtEndCard)); + + flow.addRow("CashRow2", row -> row + .spacing(14) + .weights(11, 9) + .addSection("Opex", FinancialReportExample::opexCard) + .addSection("Breakdown", FinancialReportExample::revenueBreakdownCard)); + + // ── Forecast Financial Analysis ── + sectionBand(flow, "Forecast Financial Analysis"); + + flow.addRow("ForecastRow", row -> row + .spacing(14) + .evenWeights() + .addSection("Revenue", FinancialReportExample::forecastRevenueCard) + .addSection("Costs", FinancialReportExample::costBreakdownCard)); + + flow.build(); + } + + /** + * CLI entry point. + * + * @param args ignored + * @throws Exception when rendering fails + */ + public static void main(String[] args) throws Exception { + System.out.println("Generated: " + generate()); + } + + // ─────────────────── Masthead photo (path clip) ────────────────────── + + private static DocumentNode mastheadPhoto(DocumentImageData photo) { + double w = 140; + double h = 96; + // Slanted parallelogram silhouette — unit box, (0,0) bottom-left, y up. + List parallelogram = List.of( + DocumentPathSegment.moveTo(0.12, 1.0), + DocumentPathSegment.lineTo(1.0, 1.0), + DocumentPathSegment.lineTo(0.88, 0.0), + DocumentPathSegment.lineTo(0.0, 0.0), + DocumentPathSegment.close()); + return new ShapeContainerBuilder() + .name("MastheadPhoto") + .path(w, h, parallelogram) + .clipPolicy(ClipPolicy.CLIP_PATH) + .stroke(DocumentStroke.of(TEAL, 1.5)) + .center(new ImageBuilder() + .source(photo) + .size(w, h) + .fitMode(DocumentImageFitMode.COVER) + .build()) + .build(); + } + + // ─────────────────── Gauges (donut + centre KPI) ───────────────────── + + private static void gauge(SectionBuilder section, double percent, String label) { + ChartData data = ChartData.builder() + .categories("value", "remainder") + .series("margin", percent, 100 - percent) + .build(); + ChartSpec spec = ChartSpec.pie() + .data(data) + .donutRatio(0.66) + .startAngleDegrees(90) + .clockwise(true) + .centerText((int) percent + "%") + .sliceLabels(SliceLabelMode.NONE) + .legend(LegendPosition.NONE) + .size(ChartSize.fixedHeight(84)) + .build(); + ChartStyle style = ChartStyle.builder() + .palette(DocumentPaint.solid(ORANGE), DocumentPaint.solid(GAUGE_TRACK)) + .donutCenterTextStyle(DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(22) + .color(INK) + .build()) + .build(); + section.spacing(3) + .chart(spec, style) + .addParagraph(p -> p + .text(label) + .textStyle(gaugeLabelStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())); + } + + // ─────────────────── Cash balance KPI tile ─────────────────────────── + + private static void cashBalanceCard(SectionBuilder section) { + // The card is a column of CashRow1, so it cannot host nested horizontal + // rows — each KPI line is a single rich paragraph (token + amount). + section.softPanel(CARD, 8, 10) + .stroke(DocumentStroke.of(CARD_RING, 0.6)) + .accentTop(TEAL, 1.5) + .spacing(13) + .addParagraph(p -> p + .text("Cash Balance") + .textStyle(cardTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .addRich(r -> r + .style("$ ", kpiTokenStyle(ORANGE, 15)) + .style("$199.28 MM", kpiValueStyle())) + .addRich(r -> r + .style("IN ", kpiTokenStyle(MUTED, 11)) + .style("$250.88 MM", kpiValueStyle())) + .addRich(r -> r + .style("OUT ", kpiTokenStyle(MUTED, 11)) + .style("$150.66 MM", kpiValueStyle())); + } + + // ─────────────────── Cash at end of month (bar) ────────────────────── + + private static void cashAtEndCard(SectionBuilder section) { + ChartData data = ChartData.builder() + .categories("Jan-22", "Feb-22", "Mar-22", "Apr-22", "May-22") + .series("Cash", 13, 12, 15, 16, 17) + .build(); + ChartSpec spec = ChartSpec.bar() + .data(data) + .valueAxis(AxisSpec.builder() + .baselineAtZero(true) + .max(20) + .format(NumberFormatSpec.pattern("#,##0").withSuffix("k")) + .build()) + .size(ChartSize.fixedHeight(96)) + .build(); + section.softPanel(CARD, 8, 10) + .stroke(DocumentStroke.of(CARD_RING, 0.6)) + .accentTop(ORANGE, 1.5) + .spacing(6) + .addParagraph(p -> p + .text("Cash at end of Month") + .textStyle(cardTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .chart(spec, tealBars()); + } + + // ─────────────────── OPEX (stacked bar) ────────────────────────────── + + private static void opexCard(SectionBuilder section) { + ChartData data = ChartData.builder() + .categories("M1", "M2", "M3", "M4", "M5") + .series("OPEX", 42, 36, 46, 40, 43) + .series("Marketing", 30, 20, 30, 28, 28) + .build(); + ChartSpec spec = ChartSpec.bar() + .data(data) + .grouping(BarGrouping.STACKED) + .valueAxis(AxisSpec.builder() + .baselineAtZero(true) + .max(100) + .format(NumberFormatSpec.pattern("#,##0").withSuffix("k")) + .build()) + .legend(LegendPosition.BOTTOM) + .size(ChartSize.fixedHeight(110)) + .build(); + ChartStyle style = ChartStyle.builder() + .palette(DocumentPaint.solid(ORANGE_DEEP), DocumentPaint.solid(ORANGE)) + .barCornerRadius(DocumentCornerRadius.top(2)) + .build(); + section.softPanel(CARD, 8, 10) + .stroke(DocumentStroke.of(CARD_RING, 0.6)) + .accentTop(ORANGE, 1.5) + .spacing(4) + .addParagraph(p -> p + .text("OPEX") + .textStyle(cardTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .addParagraph(p -> p + .text("Month to Month YTD") + .textStyle(captionStyle()) + .margin(DocumentInsets.zero())) + .chart(spec, style); + } + + // ─────────────────── Revenue breakdown (donut) ─────────────────────── + + private static void revenueBreakdownCard(SectionBuilder section) { + ChartData data = ChartData.builder() + .categories("Desktops", "Portables", "Accessories", "Wearables", "Other") + .series("Share", 30, 26, 18, 14, 12) + .build(); + ChartSpec spec = ChartSpec.pie() + .data(data) + .donutRatio(0.52) + .sliceLabels(SliceLabelMode.PERCENT) + .legend(LegendPosition.RIGHT) + .size(ChartSize.fixedHeight(130)) + .build(); + ChartStyle style = ChartStyle.builder() + .palette( + DocumentPaint.solid(TEAL), + DocumentPaint.solid(ORANGE), + DocumentPaint.solid(ORANGE_SOFT), + DocumentPaint.solid(TEAL_DARK), + DocumentPaint.solid(GAUGE_TRACK)) + .sliceGapDegrees(1.5) + .build(); + section.softPanel(CARD, 8, 10) + .stroke(DocumentStroke.of(CARD_RING, 0.6)) + .accentTop(TEAL, 1.5) + .spacing(4) + .addParagraph(p -> p + .text("Revenue Breakdown") + .textStyle(cardTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .chart(spec, style); + } + + // ─────────────────── Forecast revenue (bar) ────────────────────────── + + private static void forecastRevenueCard(SectionBuilder section) { + ChartData data = ChartData.builder() + .categories("Apr-22", "May-22", "Jun-22", "Jul-22", "Aug-22") + .series("Revenue", 130, 135, 140, 138, 150) + .build(); + ChartSpec spec = ChartSpec.bar() + .data(data) + .valueAxis(AxisSpec.builder() + .baselineAtZero(true) + .max(175) + .format(NumberFormatSpec.pattern("#,##0").withSuffix("k")) + .build()) + .valueLabels(ValueLabelMode.OUTSIDE) + .size(ChartSize.fixedHeight(110)) + .build(); + section.softPanel(CARD, 8, 10) + .stroke(DocumentStroke.of(CARD_RING, 0.6)) + .accentTop(TEAL, 1.5) + .spacing(6) + .addParagraph(p -> p + .text("Revenue") + .textStyle(cardTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .chart(spec, tealBars()); + } + + // ─────────────────── Cost breakdown (horizontal grouped bars) ───────── + + private static void costBreakdownCard(SectionBuilder section) { + ChartData data = ChartData.builder() + .categories("Cogs", "Sales", "Marketing", "Gen & Admin", "Other", "Taxes") + .series("Forecast", 300, 520, 180, 150, 60, 110) + .series("Actual", 280, 500, 150, 170, 50, 90) + .build(); + ChartSpec spec = ChartSpec.bar() + .data(data) + .horizontal(true) + .grouping(BarGrouping.GROUPED) + .valueAxis(AxisSpec.builder() + .baselineAtZero(true) + .max(600) + .build()) + .legend(LegendPosition.BOTTOM) + .size(ChartSize.fixedHeight(130)) + .build(); + ChartStyle style = ChartStyle.builder() + .seriesPaint(0, DocumentPaint.solid(TEAL)) + .seriesPaint(1, DocumentPaint.solid(ORANGE)) + .build(); + section.softPanel(CARD, 8, 10) + .stroke(DocumentStroke.of(CARD_RING, 0.6)) + .accentTop(ORANGE, 1.5) + .spacing(6) + .addParagraph(p -> p + .text("Breakdown of Costs") + .textStyle(cardTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero())) + .chart(spec, style); + } + + // ─────────────────── Shared building blocks ────────────────────────── + + private static void sectionBand(PageFlowBuilder flow, String title) { + flow.addSection("Band:" + title, s -> s + .softPanel(BAND, 3, 7) + .addParagraph(p -> p + .text(title) + .textStyle(bandTitleStyle()) + .align(TextAlign.CENTER) + .margin(DocumentInsets.zero()))); + } + + private static ChartStyle tealBars() { + return ChartStyle.builder() + .seriesPaint(0, DocumentPaint.solid(TEAL)) + .barCornerRadius(DocumentCornerRadius.top(2)) + .build(); + } + + // ─────────────────── Text styles ───────────────────────────────────── + + private static DocumentTextStyle titleStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(19) + .color(INK) + .build(); + } + + private static DocumentTextStyle subtitleStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA) + .size(8.4) + .color(MUTED) + .build(); + } + + private static DocumentTextStyle periodStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(15) + .color(INK) + .build(); + } + + private static DocumentTextStyle bandTitleStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(12.5) + .color(TEAL_DARK) + .build(); + } + + private static DocumentTextStyle cardTitleStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(11) + .color(TEAL_DARK) + .build(); + } + + private static DocumentTextStyle captionStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA) + .size(7.6) + .color(MUTED) + .build(); + } + + private static DocumentTextStyle gaugeLabelStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA) + .size(9.5) + .color(INK) + .build(); + } + + private static DocumentTextStyle kpiTokenStyle(DocumentColor color, double size) { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(size) + .color(color) + .build(); + } + + private static DocumentTextStyle kpiValueStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA_BOLD) + .size(11) + .color(INK) + .build(); + } + + private static DocumentTextStyle legendStyle() { + return DocumentTextStyle.builder() + .fontName(FontName.HELVETICA) + .size(8) + .color(MUTED) + .build(); + } + + // ─────────────────── Resource ──────────────────────────────────────── + + private static DocumentImageData loadMastheadPhoto() throws Exception { + try (InputStream in = Objects.requireNonNull( + FinancialReportExample.class.getResourceAsStream("/engine-hero.jpg"), + "engine-hero.jpg missing from examples/src/main/resources/")) { + return DocumentImageData.fromBytes(in.readAllBytes()); + } + } +} diff --git a/examples/src/test/java/com/demcha/examples/flagships/FinancialReportLayoutSnapshotTest.java b/examples/src/test/java/com/demcha/examples/flagships/FinancialReportLayoutSnapshotTest.java new file mode 100644 index 000000000..6c96a2cd9 --- /dev/null +++ b/examples/src/test/java/com/demcha/examples/flagships/FinancialReportLayoutSnapshotTest.java @@ -0,0 +1,39 @@ +package com.demcha.examples.flagships; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.testing.layout.LayoutSnapshotAssertions; + +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; + +/** + * Locks the FinancialReport one-pager layout. Composes the exact page + * {@code generate()} ships (via {@link FinancialReportExample#compose}) and diffs + * the resolved geometry against a committed baseline, so a layout regression + * surfaces in a PR instead of in a silently-changed PDF. + * + *

Re-bless after a deliberate layout change with + * {@code ./mvnw -f examples/pom.xml test -Dtest=FinancialReportLayoutSnapshotTest + * -Dgraphcompose.updateSnapshots=true}. Page size and margin mirror + * {@code FinancialReportExample.generate()}; the page background is a render-only + * option and does not affect the resolved layout geometry.

+ */ +class FinancialReportLayoutSnapshotTest { + + @Test + void financialReportLayoutMatchesBaseline() throws Exception { + try (DocumentSession document = GraphCompose.document() + .pageSize(DocumentPageSize.A4) + .margin(18, 28, 14, 28) + .create()) { + FinancialReportExample.compose(document); + LayoutSnapshotAssertions.assertMatches(document, + Path.of("src", "test", "resources", "layout-snapshots"), + Path.of("target", "visual-tests", "layout-snapshots"), + "financial-report", "flagships"); + } + } +} diff --git a/examples/src/test/resources/layout-snapshots/flagships/financial-report.json b/examples/src/test/resources/layout-snapshots/flagships/financial-report.json new file mode 100644 index 000000000..0730688c0 --- /dev/null +++ b/examples/src/test/resources/layout-snapshots/flagships/financial-report.json @@ -0,0 +1,1577 @@ +{ + "formatVersion" : "2.0", + "canvas" : { + "pageWidth" : 595.276, + "pageHeight" : 841.89, + "innerWidth" : 539.276, + "innerHeight" : 809.89, + "margin" : { + "top" : 18.0, + "right" : 28.0, + "bottom" : 14.0, + "left" : 28.0 + } + }, + "totalPages" : 1, + "nodes" : [ { + "path" : "FinancialReport[0]", + "entityName" : "FinancialReport", + "entityKind" : "ContainerNode", + "parentPath" : null, + "childIndex" : 0, + "depth" : 1, + "layer" : 1, + "computedX" : 28.0, + "computedY" : 42.077, + "placementX" : 28.0, + "placementY" : 42.077, + "placementWidth" : 539.276, + "placementHeight" : 781.812, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 781.812, + "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" : "FinancialReport[0]/Masthead[0]", + "entityName" : "Masthead", + "entityKind" : "RowNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 0, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 727.89, + "placementX" : 28.0, + "placementY" : 727.89, + "placementWidth" : 539.276, + "placementHeight" : 96.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 96.0, + "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" : "FinancialReport[0]/Masthead[0]/Photo[0]", + "entityName" : "Photo", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/Masthead[0]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 727.89, + "placementX" : 28.0, + "placementY" : 727.89, + "placementWidth" : 140.0, + "placementHeight" : 96.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 140.0, + "contentHeight" : 96.0, + "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" : "FinancialReport[0]/Masthead[0]/Photo[0]/MastheadPhoto[0]", + "entityName" : "MastheadPhoto", + "entityKind" : "ShapeContainerNode", + "parentPath" : "FinancialReport[0]/Masthead[0]/Photo[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 28.0, + "computedY" : 727.89, + "placementX" : 28.0, + "placementY" : 727.89, + "placementWidth" : 140.0, + "placementHeight" : 96.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 140.0, + "contentHeight" : 96.0, + "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" : "FinancialReport[0]/Masthead[0]/Photo[0]/MastheadPhoto[0]/ImageNode[0]", + "entityName" : null, + "entityKind" : "ImageNode", + "parentPath" : "FinancialReport[0]/Masthead[0]/Photo[0]/MastheadPhoto[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 28.0, + "computedY" : 727.89, + "placementX" : 28.0, + "placementY" : 727.89, + "placementWidth" : 140.0, + "placementHeight" : 96.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 140.0, + "contentHeight" : 96.0, + "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" : "FinancialReport[0]/Masthead[0]/Title[1]", + "entityName" : "Title", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/Masthead[0]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 189.354, + "computedY" : 751.85, + "placementX" : 189.354, + "placementY" : 751.85, + "placementWidth" : 345.005, + "placementHeight" : 72.04, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 345.005, + "contentHeight" : 72.04, + "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" : "FinancialReport[0]/Masthead[0]/Title[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Masthead[0]/Title[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 189.354, + "computedY" : 806.315, + "placementX" : 189.354, + "placementY" : 806.315, + "placementWidth" : 174.249, + "placementHeight" : 17.575, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 174.249, + "contentHeight" : 17.575, + "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" : "FinancialReport[0]/Masthead[0]/Title[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Masthead[0]/Title[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 189.354, + "computedY" : 786.74, + "placementX" : 189.354, + "placementY" : 786.74, + "placementWidth" : 210.121, + "placementHeight" : 17.575, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 210.121, + "contentHeight" : 17.575, + "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" : "FinancialReport[0]/Masthead[0]/Title[1]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Masthead[0]/Title[1]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 189.354, + "computedY" : 762.85, + "placementX" : 189.354, + "placementY" : 762.85, + "placementWidth" : 345.005, + "placementHeight" : 16.89, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 345.005, + "contentHeight" : 16.89, + "margin" : { + "top" : 5.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "FinancialReport[0]/Masthead[0]/Title[1]/ParagraphNode[3]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Masthead[0]/Title[1]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 189.354, + "computedY" : 751.85, + "placementX" : 189.354, + "placementY" : 751.85, + "placementWidth" : 176.48, + "placementHeight" : 9.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 176.48, + "contentHeight" : 9.0, + "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" : "FinancialReport[0]/Period[1]", + "entityName" : "Period", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 1, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 699.515, + "placementX" : 28.0, + "placementY" : 699.515, + "placementWidth" : 539.276, + "placementHeight" : 20.375, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 20.375, + "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" : "FinancialReport[0]/Period[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Period[1]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 706.015, + "placementX" : 28.0, + "placementY" : 706.015, + "placementWidth" : 539.276, + "placementHeight" : 13.875, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 13.875, + "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" : "FinancialReport[0]/Period[1]/Align[1]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "FinancialReport[0]/Period[1]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 699.515, + "placementX" : 28.0, + "placementY" : 699.515, + "placementWidth" : 539.276, + "placementHeight" : 2.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 2.5, + "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" : "FinancialReport[0]/Period[1]/Align[1]/PeriodRule[0]", + "entityName" : "PeriodRule", + "entityKind" : "ShapeNode", + "parentPath" : "FinancialReport[0]/Period[1]/Align[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 237.638, + "computedY" : 699.515, + "placementX" : 237.638, + "placementY" : 699.515, + "placementWidth" : 120.0, + "placementHeight" : 2.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 120.0, + "contentHeight" : 2.5, + "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" : "FinancialReport[0]/Gauges[2]", + "entityName" : "Gauges", + "entityKind" : "RowNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 2, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 595.727, + "placementX" : 28.0, + "placementY" : 595.727, + "placementWidth" : 539.276, + "placementHeight" : 95.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 95.787, + "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" : "FinancialReport[0]/Gauges[2]/Gauge1[0]", + "entityName" : "Gauge1", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/Gauges[2]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 595.727, + "placementX" : 28.0, + "placementY" : 595.727, + "placementWidth" : 170.425, + "placementHeight" : 95.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 95.787, + "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" : "FinancialReport[0]/Gauges[2]/Gauge1[0]/Chart[0]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/Gauges[2]/Gauge1[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 28.0, + "computedY" : 607.515, + "placementX" : 28.0, + "placementY" : 607.515, + "placementWidth" : 170.425, + "placementHeight" : 84.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 84.0, + "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" : "FinancialReport[0]/Gauges[2]/Gauge1[0]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Gauges[2]/Gauge1[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 28.0, + "computedY" : 595.727, + "placementX" : 28.0, + "placementY" : 595.727, + "placementWidth" : 170.425, + "placementHeight" : 8.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 8.787, + "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" : "FinancialReport[0]/Gauges[2]/Gauge2[1]", + "entityName" : "Gauge2", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/Gauges[2]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 212.425, + "computedY" : 595.727, + "placementX" : 212.425, + "placementY" : 595.727, + "placementWidth" : 170.425, + "placementHeight" : 95.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 95.787, + "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" : "FinancialReport[0]/Gauges[2]/Gauge2[1]/Chart[0]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/Gauges[2]/Gauge2[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 212.425, + "computedY" : 607.515, + "placementX" : 212.425, + "placementY" : 607.515, + "placementWidth" : 170.425, + "placementHeight" : 84.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 84.0, + "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" : "FinancialReport[0]/Gauges[2]/Gauge2[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Gauges[2]/Gauge2[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 212.425, + "computedY" : 595.727, + "placementX" : 212.425, + "placementY" : 595.727, + "placementWidth" : 170.425, + "placementHeight" : 8.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 8.787, + "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" : "FinancialReport[0]/Gauges[2]/Gauge3[2]", + "entityName" : "Gauge3", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/Gauges[2]", + "childIndex" : 2, + "depth" : 3, + "layer" : 3, + "computedX" : 396.85, + "computedY" : 595.727, + "placementX" : 396.85, + "placementY" : 595.727, + "placementWidth" : 170.425, + "placementHeight" : 95.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 95.787, + "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" : "FinancialReport[0]/Gauges[2]/Gauge3[2]/Chart[0]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/Gauges[2]/Gauge3[2]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 396.85, + "computedY" : 607.515, + "placementX" : 396.85, + "placementY" : 607.515, + "placementWidth" : 170.425, + "placementHeight" : 84.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 84.0, + "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" : "FinancialReport[0]/Gauges[2]/Gauge3[2]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Gauges[2]/Gauge3[2]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 396.85, + "computedY" : 595.727, + "placementX" : 396.85, + "placementY" : 595.727, + "placementWidth" : 170.425, + "placementHeight" : 8.787, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 170.425, + "contentHeight" : 8.787, + "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" : "FinancialReport[0]/Band:Cash position[3]", + "entityName" : "Band:Cash position", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 3, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 562.165, + "placementX" : 28.0, + "placementY" : 562.165, + "placementWidth" : 539.276, + "placementHeight" : 25.563, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 25.563, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 7.0, + "right" : 7.0, + "bottom" : 7.0, + "left" : 7.0 + } + }, { + "path" : "FinancialReport[0]/Band:Cash position[3]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Band:Cash position[3]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 35.0, + "computedY" : 569.165, + "placementX" : 35.0, + "placementY" : 569.165, + "placementWidth" : 525.276, + "placementHeight" : 11.563, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 525.276, + "contentHeight" : 11.563, + "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" : "FinancialReport[0]/CashRow1[4]", + "entityName" : "CashRow1", + "entityKind" : "RowNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 4, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 421.99, + "placementX" : 28.0, + "placementY" : 421.99, + "placementWidth" : 539.276, + "placementHeight" : 132.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 132.175, + "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" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]", + "entityName" : "CashBalance", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 450.765, + "placementX" : 28.0, + "placementY" : 450.765, + "placementWidth" : 210.11, + "placementHeight" : 103.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 210.11, + "contentHeight" : 103.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 10.0, + "right" : 10.0, + "bottom" : 10.0, + "left" : 10.0 + } + }, { + "path" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 533.99, + "placementX" : 38.0, + "placementY" : 533.99, + "placementWidth" : 190.11, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 190.11, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 507.115, + "placementX" : 38.0, + "placementY" : 507.115, + "placementWidth" : 77.818, + "placementHeight" : 13.875, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 77.818, + "contentHeight" : 13.875, + "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" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 483.94, + "placementX" : 38.0, + "placementY" : 483.94, + "placementWidth" : 81.312, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 81.312, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]/ParagraphNode[3]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]/CashBalance[0]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 460.765, + "placementX" : 38.0, + "placementY" : 460.765, + "placementWidth" : 90.475, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 90.475, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/CashRow1[4]/CashEnd[1]", + "entityName" : "CashEnd", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 252.11, + "computedY" : 421.99, + "placementX" : 252.11, + "placementY" : 421.99, + "placementWidth" : 315.165, + "placementHeight" : 132.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 315.165, + "contentHeight" : 132.175, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 10.0, + "right" : 10.0, + "bottom" : 10.0, + "left" : 10.0 + } + }, { + "path" : "FinancialReport[0]/CashRow1[4]/CashEnd[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow1[4]/CashEnd[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 262.11, + "computedY" : 533.99, + "placementX" : 262.11, + "placementY" : 533.99, + "placementWidth" : 295.165, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 295.165, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/CashRow1[4]/CashEnd[1]/Chart[1]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/CashRow1[4]/CashEnd[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 262.11, + "computedY" : 431.99, + "placementX" : 262.11, + "placementY" : 431.99, + "placementWidth" : 295.165, + "placementHeight" : 96.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 295.165, + "contentHeight" : 96.0, + "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" : "FinancialReport[0]/CashRow2[5]", + "entityName" : "CashRow2", + "entityKind" : "RowNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 5, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 249.815, + "placementX" : 28.0, + "placementY" : 249.815, + "placementWidth" : 539.276, + "placementHeight" : 164.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 164.175, + "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" : "FinancialReport[0]/CashRow2[5]/Opex[0]", + "entityName" : "Opex", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/CashRow2[5]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 258.785, + "placementX" : 28.0, + "placementY" : 258.785, + "placementWidth" : 288.902, + "placementHeight" : 155.205, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 288.902, + "contentHeight" : 155.205, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 10.0, + "right" : 10.0, + "bottom" : 10.0, + "left" : 10.0 + } + }, { + "path" : "FinancialReport[0]/CashRow2[5]/Opex[0]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow2[5]/Opex[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 393.815, + "placementX" : 38.0, + "placementY" : 393.815, + "placementWidth" : 268.902, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 268.902, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/CashRow2[5]/Opex[0]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow2[5]/Opex[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 382.785, + "placementX" : 38.0, + "placementY" : 382.785, + "placementWidth" : 70.118, + "placementHeight" : 7.03, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 70.118, + "contentHeight" : 7.03, + "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" : "FinancialReport[0]/CashRow2[5]/Opex[0]/Chart[2]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/CashRow2[5]/Opex[0]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 268.785, + "placementX" : 38.0, + "placementY" : 268.785, + "placementWidth" : 268.902, + "placementHeight" : 110.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 268.902, + "contentHeight" : 110.0, + "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" : "FinancialReport[0]/CashRow2[5]/Breakdown[1]", + "entityName" : "Breakdown", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/CashRow2[5]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 330.902, + "computedY" : 249.815, + "placementX" : 330.902, + "placementY" : 249.815, + "placementWidth" : 236.374, + "placementHeight" : 164.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 236.374, + "contentHeight" : 164.175, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 10.0, + "right" : 10.0, + "bottom" : 10.0, + "left" : 10.0 + } + }, { + "path" : "FinancialReport[0]/CashRow2[5]/Breakdown[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/CashRow2[5]/Breakdown[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 340.902, + "computedY" : 393.815, + "placementX" : 340.902, + "placementY" : 393.815, + "placementWidth" : 216.374, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 216.374, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/CashRow2[5]/Breakdown[1]/Chart[1]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/CashRow2[5]/Breakdown[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 340.902, + "computedY" : 259.815, + "placementX" : 340.902, + "placementY" : 259.815, + "placementWidth" : 216.374, + "placementHeight" : 130.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 216.374, + "contentHeight" : 130.0, + "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" : "FinancialReport[0]/Band:Forecast Financial Analysis[6]", + "entityName" : "Band:Forecast Financial Analysis", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 6, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 216.252, + "placementX" : 28.0, + "placementY" : 216.252, + "placementWidth" : 539.276, + "placementHeight" : 25.563, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 25.563, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 7.0, + "right" : 7.0, + "bottom" : 7.0, + "left" : 7.0 + } + }, { + "path" : "FinancialReport[0]/Band:Forecast Financial Analysis[6]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/Band:Forecast Financial Analysis[6]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 35.0, + "computedY" : 223.252, + "placementX" : 35.0, + "placementY" : 223.252, + "placementWidth" : 525.276, + "placementHeight" : 11.563, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 525.276, + "contentHeight" : 11.563, + "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" : "FinancialReport[0]/ForecastRow[7]", + "entityName" : "ForecastRow", + "entityKind" : "RowNode", + "parentPath" : "FinancialReport[0]", + "childIndex" : 7, + "depth" : 2, + "layer" : 2, + "computedX" : 28.0, + "computedY" : 42.077, + "placementX" : 28.0, + "placementY" : 42.077, + "placementWidth" : 539.276, + "placementHeight" : 166.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 539.276, + "contentHeight" : 166.175, + "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" : "FinancialReport[0]/ForecastRow[7]/Revenue[0]", + "entityName" : "Revenue", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/ForecastRow[7]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 28.0, + "computedY" : 62.077, + "placementX" : 28.0, + "placementY" : 62.077, + "placementWidth" : 262.638, + "placementHeight" : 146.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 262.638, + "contentHeight" : 146.175, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 10.0, + "right" : 10.0, + "bottom" : 10.0, + "left" : 10.0 + } + }, { + "path" : "FinancialReport[0]/ForecastRow[7]/Revenue[0]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/ForecastRow[7]/Revenue[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 188.077, + "placementX" : 38.0, + "placementY" : 188.077, + "placementWidth" : 242.638, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 242.638, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/ForecastRow[7]/Revenue[0]/Chart[1]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/ForecastRow[7]/Revenue[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 38.0, + "computedY" : 72.077, + "placementX" : 38.0, + "placementY" : 72.077, + "placementWidth" : 242.638, + "placementHeight" : 110.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 242.638, + "contentHeight" : 110.0, + "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" : "FinancialReport[0]/ForecastRow[7]/Costs[1]", + "entityName" : "Costs", + "entityKind" : "SectionNode", + "parentPath" : "FinancialReport[0]/ForecastRow[7]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 304.638, + "computedY" : 42.077, + "placementX" : 304.638, + "placementY" : 42.077, + "placementWidth" : 262.638, + "placementHeight" : 166.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 262.638, + "contentHeight" : 166.175, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 10.0, + "right" : 10.0, + "bottom" : 10.0, + "left" : 10.0 + } + }, { + "path" : "FinancialReport[0]/ForecastRow[7]/Costs[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "FinancialReport[0]/ForecastRow[7]/Costs[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 314.638, + "computedY" : 188.077, + "placementX" : 314.638, + "placementY" : 188.077, + "placementWidth" : 242.638, + "placementHeight" : 10.175, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 242.638, + "contentHeight" : 10.175, + "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" : "FinancialReport[0]/ForecastRow[7]/Costs[1]/Chart[1]", + "entityName" : null, + "entityKind" : "Chart", + "parentPath" : "FinancialReport[0]/ForecastRow[7]/Costs[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 314.638, + "computedY" : 52.077, + "placementX" : 314.638, + "placementY" : 52.077, + "placementWidth" : 242.638, + "placementHeight" : 130.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 242.638, + "contentHeight" : 130.0, + "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 + } + } ] +}