diff --git a/CHANGELOG.md b/CHANGELOG.md index 58d990d2a..cbec0eca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,20 @@ follow semantic versioning; release dates are ISO 8601. overflow that freezes the two-page table continuation), and the invoice pixel-parity gate; the examples showcase gains `invoice-classic-v2`. +- **The first structured proposal preset: `NorthlineProposal`.** A two-page + teal-and-navy business proposal on the Spectral/Lato pair — brand header with logo + mark and wordmark, three stacked title lines on the reference's own pitch, the + executive summary beside an at-a-glance fact card, icon goal cells, a numbered + scope list, deliverable columns, the phase grid, an investment table with subtotal / + optional / total row styling, and a signing card — with the navy footer band, the + teal page-number block and the `01`/`02` page numbers as page chrome rather than + flow content. Ships as `proposal.presets.NorthlineProposal` consuming the new + structured proposal model, with its icon set packaged in the templates artifact, + porting the rendered layout of the published standalone `northline-proposal` + template. Guarded by a smoke test (including the empty document and the + phase-grid header-count contract), an exact two-page layout snapshot, and a + pixel-parity gate; the examples showcase gains `proposal-northline-v2`. + - **Monogram Sidebar draws the employer.** Its experience entries rendered the position, the date and the description, and never `CvEntry.subtitle()` — so every company name was missing from the rendered CV while the education block, which does render its diff --git a/assets/readme/examples/proposal-northline-v2.pdf b/assets/readme/examples/proposal-northline-v2.pdf new file mode 100644 index 000000000..934c9474f Binary files /dev/null and b/assets/readme/examples/proposal-northline-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 006ec548a..2839b8cfd 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -100,6 +100,7 @@ import com.demcha.examples.templates.invoice.InvoiceCinematicFileExample; import com.demcha.examples.templates.invoice.ModernInvoiceV2Example; import com.demcha.examples.templates.proposal.CinematicProposalFileExample; +import com.demcha.examples.templates.proposal.NorthlineProposalV2Example; import com.demcha.examples.templates.proposal.ProposalCinematicFileExample; import com.demcha.examples.templates.proposal.ModernProposalV2Example; import com.demcha.examples.templates.schedule.WeeklyScheduleFileExample; @@ -168,6 +169,7 @@ public static void main(String[] args) throws Exception { // Proposals System.out.println("Generated: " + ProposalCinematicFileExample.generate()); System.out.println("Generated: " + ModernProposalV2Example.generate()); + System.out.println("Generated: " + NorthlineProposalV2Example.generate()); System.out.println("Generated: " + CinematicProposalFileExample.generate()); // Schedule 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 0f613f678..180535ec3 100644 --- a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java +++ b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java @@ -94,6 +94,7 @@ record Entry(String title, String description, List tags, String codeUrl proposal("proposal-cinematic", "ProposalCinematicFileExample", "Cinematic Proposal", "Layered ModernProposal layout with cover panel, hero spread, and rich typography.", "proposal", "cinematic"); proposal("project-proposal-cinematic", "CinematicProposalFileExample", "Project Proposal (cinematic)", "End-to-end project proposal with mountain hero, scope panels, and pricing summary.", "proposal", "cinematic"); proposal("proposal-modern-v2", "v2/ModernProposalV2Example", "Modern Proposal", "The ModernProposal preset composed straight from its document spec — cover, scope sections and pricing table themed through BrandTheme.", "proposal"); + proposal("proposal-northline-v2", "v2/NorthlineProposalV2Example", "Northline Proposal", "The NorthlineProposal preset on the structured proposal model — brand header, stacked title, glance card, goal cells, numbered scope, phase grid, investment table and signing card across two pages.", "proposal"); // ===== Templates / Schedule ===== schedule("weekly-schedule", "Weekly Schedule", "Multi-day weekly schedule with shift assignments, category fills, and repeated header.", "schedule", "table"); diff --git a/examples/src/main/java/com/demcha/examples/support/StructuredProposalSampleData.java b/examples/src/main/java/com/demcha/examples/support/StructuredProposalSampleData.java new file mode 100644 index 000000000..0c315f917 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/support/StructuredProposalSampleData.java @@ -0,0 +1,147 @@ +package com.demcha.examples.support; + +import com.demcha.compose.document.templates.data.proposal.ProposalAcceptance; +import com.demcha.compose.document.templates.data.proposal.ProposalBrand; +import com.demcha.compose.document.templates.data.proposal.ProposalDeliverables; +import com.demcha.compose.document.templates.data.proposal.ProposalGlance; +import com.demcha.compose.document.templates.data.proposal.ProposalGoals; +import com.demcha.compose.document.templates.data.proposal.ProposalInvestment; +import com.demcha.compose.document.templates.data.proposal.ProposalMetaLine; +import com.demcha.compose.document.templates.data.proposal.ProposalPhaseGrid; +import com.demcha.compose.document.templates.data.proposal.ProposalScope; +import com.demcha.compose.document.templates.data.proposal.ProposalSummaryBlock; +import com.demcha.compose.document.templates.data.proposal.ProposalTermsBlock; +import com.demcha.compose.document.templates.data.proposal.ProposalTitleLines; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalData; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalDocumentSpec; + +import java.util.List; + +/** + * Shared sample data for the structured proposal examples. + * + *

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

+ * + *

The sample is the preset's reference content: a two-page + * brand-refresh proposal exercising every band — all four glance facts + * (one with the optional note), four goal cells, five scope rows, both + * deliverable columns, the four-phase grid, an investment table with + * ordinary, subtotal and optional rows plus the total band, four terms, + * and the three-field signing card.

+ */ +public final class StructuredProposalSampleData { + + private StructuredProposalSampleData() { + } + + public static StructuredProposalDocumentSpec sample() { + return StructuredProposalDocumentSpec.from(StructuredProposalData.builder() + .brand(new ProposalBrand("N", "NORTHLINE", "STUDIO", "PROPOSAL", + "northlinestudio.com", "NORTHLINE STUDIO")) + .title(new ProposalTitleLines("Proposal –", "Brand Refresh &", + "Website Redesign")) + .meta(new ProposalMetaLine("PREPARED FOR ASTERA HEALTH CO.", + "PREPARED BY NORTHLINE STUDIO", "25 AUGUST 2026")) + .executiveSummary(new ProposalSummaryBlock("EXECUTIVE SUMMARY", + "badge-summary", List.of( + "Thank you for the opportunity to partner with Astera Health Co. " + + "on your next chapter. This proposal outlines our approach to " + + "refreshing your brand identity and redesigning your website to " + + "better reflect your mission, elevate credibility, and deliver a " + + "seamless experience for the people you serve.", + "Our process blends strategic thinking with thoughtful design to " + + "create a cohesive brand system and a modern, conversion-focused " + + "website. The outcome will be a clear, confident presence that " + + "strengthens trust, improves engagement, and supports your " + + "growth goals."))) + .glance(new ProposalGlance("PROJECT AT A GLANCE", List.of( + new ProposalGlance.Fact("fact-duration", "PROJECT DURATION", + "6 weeks", null), + new ProposalGlance.Fact("fact-start", "START WINDOW", + "September 2026", null), + new ProposalGlance.Fact("fact-contact", "PRIMARY CONTACT", + "Project Lead", null), + new ProposalGlance.Fact("fact-validity", "PROPOSAL VALIDITY", + "14 days", "(until 8 September 2026)")))) + .goals(new ProposalGoals("PROJECT GOALS", "badge-goals", List.of( + new ProposalGoals.Goal("goal-brand", + "Strengthen brand credibility and visual consistency."), + new ProposalGoals.Goal("goal-usability", + "Improve website usability and drive meaningful action."), + new ProposalGoals.Goal("goal-message", + "Communicate your value clearly across all touchpoints."), + new ProposalGoals.Goal("goal-growth", + "Build a scalable digital foundation for future growth.")))) + .scope(new ProposalScope("SCOPE OF WORK", "badge-scope", List.of( + new ProposalScope.Item("01", "Discovery & Research", + "Stakeholder interviews, brand audit, competitor review, and " + + "audience insights to inform strategy."), + new ProposalScope.Item("02", "Visual Identity Refinement", + "Refine logo system, color palette, typography, and visual " + + "guidelines for a cohesive, modern identity."), + new ProposalScope.Item("03", "Website UX/UI Design", + "Design a responsive, accessibility-minded website with " + + "intuitive navigation and clear conversion pathways."), + new ProposalScope.Item("04", "Content Structure & Messaging", + "Define site architecture, key messaging, and content " + + "hierarchy to communicate value and build trust."), + new ProposalScope.Item("05", "Launch Support", + "Developer handoff, QA support, training, and go-live " + + "assistance to ensure a smooth launch.")))) + .deliverables(new ProposalDeliverables("DELIVERABLES", "badge-deliverables", + List.of("Brand strategy summary", + "Refined logo suite & brand guidelines", + "Color, typography & design system", + "Website UX wireframes", + "High-fidelity website designs"), + List.of("Responsive design for desktop, tablet & mobile", + "Content strategy & page templates", + "Developer-ready assets & specifications", + "Launch checklist & training session"))) + .timeline(new ProposalPhaseGrid("TIMELINE", "badge-timeline", + List.of("PHASE", "FOCUS", "DURATION", "OUTPUT"), List.of( + new ProposalPhaseGrid.Phase("01", "Discover", + "Research, audit, insights, and strategy", "1 week", + "Discovery report & creative direction"), + new ProposalPhaseGrid.Phase("02", "Design", + "Brand refinement and website UX/UI design", "2 weeks", + "Design system & high-fidelity designs"), + new ProposalPhaseGrid.Phase("03", "Develop Prep", + "Content structure, developer handoff & asset preparation", + "1 week", "Specs, assets & content framework"), + new ProposalPhaseGrid.Phase("04", "Launch Support", + "QA, training & go-live assistance", "2 weeks", + "QA sign-off & successful launch")))) + .investment(new ProposalInvestment("INVESTMENT", "badge-investment", + "ITEM", "AMOUNT (GBP)", List.of( + new ProposalInvestment.Row("Discovery & Strategy", "£2,500", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Brand Identity Refinement", "£4,500", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Website UX/UI Design", "£7,000", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Content Structure & Messaging", "£2,000", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Launch Support", "£1,500", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Subtotal", "£17,500", + ProposalInvestment.Role.SUBTOTAL), + new ProposalInvestment.Row("Optional: 3 Months Post-Launch Support", + "£1,750", ProposalInvestment.Role.OPTIONAL)), + "TOTAL INVESTMENT", "£19,250")) + .terms(new ProposalTermsBlock("TERMS", "badge-terms", List.of( + "50% deposit is required to secure the project and schedule the " + + "kick-off.", + "Includes two rounds of revisions per major deliverable.", + "Final files and handoff delivered upon final payment.", + "Ownership of all final deliverables transfers to Astera Health Co. " + + "after full payment is received."))) + .acceptance(new ProposalAcceptance("ACCEPTANCE", "badge-acceptance", + "By signing below, you agree to the scope, timeline, investment, " + + "and terms outlined in this proposal.", + List.of("Accepted by:", "Signature:", "Date:"))) + .build()); + } +} diff --git a/examples/src/main/java/com/demcha/examples/templates/proposal/v2/NorthlineProposalV2Example.java b/examples/src/main/java/com/demcha/examples/templates/proposal/v2/NorthlineProposalV2Example.java new file mode 100644 index 000000000..0d91e2365 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/templates/proposal/v2/NorthlineProposalV2Example.java @@ -0,0 +1,53 @@ +package com.demcha.examples.templates.proposal; + +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.proposal.StructuredProposalDocumentSpec; +import com.demcha.compose.document.templates.proposal.presets.NorthlineProposal; +import com.demcha.examples.support.ExampleOutputPaths; +import com.demcha.examples.support.StructuredProposalSampleData; + +import java.nio.file.Path; + +/** + * Renders the layered {@code proposal.v2} Northline Proposal preset + * against the shared structured proposal sample data. + * + *

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

+ * + *

The preset owns its page geometry — size, margins, the footer band + * and the page background — so the session starts unconfigured; see + * {@code NorthlineProposal.RECOMMENDED_MARGIN}.

+ */ +public final class NorthlineProposalV2Example { + + private NorthlineProposalV2Example() { + } + + /** + * @return absolute path of the rendered PDF + * @throws Exception if rendering fails + */ + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare( + "templates/proposal", "proposal-northline-v2.pdf"); + StructuredProposalDocumentSpec spec = StructuredProposalSampleData.sample(); + DocumentTemplate template = NorthlineProposal.create(); + + try (DocumentSession document = GraphCompose.document(outputFile).create()) { + template.compose(document, spec); + 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/proposal/presets/NorthlineProposalFixtures.java b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalFixtures.java new file mode 100644 index 000000000..63844a745 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalFixtures.java @@ -0,0 +1,149 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.templates.data.proposal.ProposalAcceptance; +import com.demcha.compose.document.templates.data.proposal.ProposalBrand; +import com.demcha.compose.document.templates.data.proposal.ProposalDeliverables; +import com.demcha.compose.document.templates.data.proposal.ProposalGlance; +import com.demcha.compose.document.templates.data.proposal.ProposalGoals; +import com.demcha.compose.document.templates.data.proposal.ProposalInvestment; +import com.demcha.compose.document.templates.data.proposal.ProposalMetaLine; +import com.demcha.compose.document.templates.data.proposal.ProposalPhaseGrid; +import com.demcha.compose.document.templates.data.proposal.ProposalScope; +import com.demcha.compose.document.templates.data.proposal.ProposalSummaryBlock; +import com.demcha.compose.document.templates.data.proposal.ProposalTermsBlock; +import com.demcha.compose.document.templates.data.proposal.ProposalTitleLines; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalData; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalDocumentSpec; + +import java.util.List; + +/** + * Shared fixture data for the Northline proposal gates — the SAME spec + * feeds the smoke, layout-snapshot, and pixel gates, so a geometry shift + * the pixel budget absorbs still trips the exact snapshot, and vice versa. + * + *

The canonical fixture is the preset's reference content: a two-page + * brand-refresh proposal exercising every band — all four glance facts + * (one with the optional note), four goal cells, five scope rows, both + * deliverable columns, the four-phase grid, an investment table with + * ordinary, subtotal and optional rows plus the total band, four terms, + * and the three-field signing card.

+ * + *

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

+ */ +final class NorthlineProposalFixtures { + + private NorthlineProposalFixtures() { + } + + static StructuredProposalDocumentSpec canonicalProposal() { + return StructuredProposalDocumentSpec.from(StructuredProposalData.builder() + .brand(new ProposalBrand("N", "NORTHLINE", "STUDIO", "PROPOSAL", + "northlinestudio.com", "NORTHLINE STUDIO")) + .title(new ProposalTitleLines("Proposal –", "Brand Refresh &", + "Website Redesign")) + .meta(new ProposalMetaLine("PREPARED FOR ASTERA HEALTH CO.", + "PREPARED BY NORTHLINE STUDIO", "25 AUGUST 2026")) + .executiveSummary(new ProposalSummaryBlock("EXECUTIVE SUMMARY", + "badge-summary", List.of( + "Thank you for the opportunity to partner with Astera Health Co. " + + "on your next chapter. This proposal outlines our approach to " + + "refreshing your brand identity and redesigning your website to " + + "better reflect your mission, elevate credibility, and deliver a " + + "seamless experience for the people you serve.", + "Our process blends strategic thinking with thoughtful design to " + + "create a cohesive brand system and a modern, conversion-focused " + + "website. The outcome will be a clear, confident presence that " + + "strengthens trust, improves engagement, and supports your " + + "growth goals."))) + .glance(new ProposalGlance("PROJECT AT A GLANCE", List.of( + new ProposalGlance.Fact("fact-duration", "PROJECT DURATION", + "6 weeks", null), + new ProposalGlance.Fact("fact-start", "START WINDOW", + "September 2026", null), + new ProposalGlance.Fact("fact-contact", "PRIMARY CONTACT", + "Project Lead", null), + new ProposalGlance.Fact("fact-validity", "PROPOSAL VALIDITY", + "14 days", "(until 8 September 2026)")))) + .goals(new ProposalGoals("PROJECT GOALS", "badge-goals", List.of( + new ProposalGoals.Goal("goal-brand", + "Strengthen brand credibility and visual consistency."), + new ProposalGoals.Goal("goal-usability", + "Improve website usability and drive meaningful action."), + new ProposalGoals.Goal("goal-message", + "Communicate your value clearly across all touchpoints."), + new ProposalGoals.Goal("goal-growth", + "Build a scalable digital foundation for future growth.")))) + .scope(new ProposalScope("SCOPE OF WORK", "badge-scope", List.of( + new ProposalScope.Item("01", "Discovery & Research", + "Stakeholder interviews, brand audit, competitor review, and " + + "audience insights to inform strategy."), + new ProposalScope.Item("02", "Visual Identity Refinement", + "Refine logo system, color palette, typography, and visual " + + "guidelines for a cohesive, modern identity."), + new ProposalScope.Item("03", "Website UX/UI Design", + "Design a responsive, accessibility-minded website with " + + "intuitive navigation and clear conversion pathways."), + new ProposalScope.Item("04", "Content Structure & Messaging", + "Define site architecture, key messaging, and content " + + "hierarchy to communicate value and build trust."), + new ProposalScope.Item("05", "Launch Support", + "Developer handoff, QA support, training, and go-live " + + "assistance to ensure a smooth launch.")))) + .deliverables(new ProposalDeliverables("DELIVERABLES", "badge-deliverables", + List.of("Brand strategy summary", + "Refined logo suite & brand guidelines", + "Color, typography & design system", + "Website UX wireframes", + "High-fidelity website designs"), + List.of("Responsive design for desktop, tablet & mobile", + "Content strategy & page templates", + "Developer-ready assets & specifications", + "Launch checklist & training session"))) + .timeline(new ProposalPhaseGrid("TIMELINE", "badge-timeline", + List.of("PHASE", "FOCUS", "DURATION", "OUTPUT"), List.of( + new ProposalPhaseGrid.Phase("01", "Discover", + "Research, audit, insights, and strategy", "1 week", + "Discovery report & creative direction"), + new ProposalPhaseGrid.Phase("02", "Design", + "Brand refinement and website UX/UI design", "2 weeks", + "Design system & high-fidelity designs"), + new ProposalPhaseGrid.Phase("03", "Develop Prep", + "Content structure, developer handoff & asset preparation", + "1 week", "Specs, assets & content framework"), + new ProposalPhaseGrid.Phase("04", "Launch Support", + "QA, training & go-live assistance", "2 weeks", + "QA sign-off & successful launch")))) + .investment(new ProposalInvestment("INVESTMENT", "badge-investment", + "ITEM", "AMOUNT (GBP)", List.of( + new ProposalInvestment.Row("Discovery & Strategy", "£2,500", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Brand Identity Refinement", "£4,500", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Website UX/UI Design", "£7,000", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Content Structure & Messaging", "£2,000", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Launch Support", "£1,500", + ProposalInvestment.Role.NONE), + new ProposalInvestment.Row("Subtotal", "£17,500", + ProposalInvestment.Role.SUBTOTAL), + new ProposalInvestment.Row("Optional: 3 Months Post-Launch Support", + "£1,750", ProposalInvestment.Role.OPTIONAL)), + "TOTAL INVESTMENT", "£19,250")) + .terms(new ProposalTermsBlock("TERMS", "badge-terms", List.of( + "50% deposit is required to secure the project and schedule the " + + "kick-off.", + "Includes two rounds of revisions per major deliverable.", + "Final files and handoff delivered upon final payment.", + "Ownership of all final deliverables transfers to Astera Health Co. " + + "after full payment is received."))) + .acceptance(new ProposalAcceptance("ACCEPTANCE", "badge-acceptance", + "By signing below, you agree to the scope, timeline, investment, " + + "and terms outlined in this proposal.", + List.of("Accepted by:", "Signature:", "Date:"))) + .build()); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalLayoutSnapshotTest.java b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalLayoutSnapshotTest.java new file mode 100644 index 000000000..78e7d893b --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalLayoutSnapshotTest.java @@ -0,0 +1,34 @@ +package com.demcha.compose.document.templates.proposal.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 NorthlineProposal} — freezes the + * resolved geometry of both pages, including the natural page-one → page-two + * break, which the pixel budget alone could quietly absorb shifting. + * + *

The preset is a fixed two-page composition (each page's bands are + * composed explicitly), so the canonical fixture IS the pagination + * contract; there is no overflow fixture to grow.

+ * + *

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

+ */ +class NorthlineProposalLayoutSnapshotTest { + + @Test + void canonicalProposalMatchesLayoutSnapshot() throws Exception { + try (DocumentSession session = GraphCompose.document().create()) { + NorthlineProposal.create().compose( + session, NorthlineProposalFixtures.canonicalProposal()); + assertThat(session.layoutSnapshot().totalPages()).isEqualTo(2); + TemplateTestSupport.assertCanonicalSnapshot( + session, "northline_proposal_layout", "proposal"); + } + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalSmokeTest.java b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalSmokeTest.java new file mode 100644 index 000000000..4e7d7ac83 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalSmokeTest.java @@ -0,0 +1,127 @@ +package com.demcha.compose.document.templates.proposal.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.proposal.ProposalPhaseGrid; +import com.demcha.compose.document.templates.data.proposal.ProposalSummaryBlock; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalData; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalDocumentSpec; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.text.PDFTextStripper; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Smoke test for the structured proposal pipeline through + * {@link NorthlineProposal} — proves the preset renders a + * {@link StructuredProposalDocumentSpec} end-to-end, that the packaged + * icon set resolves from the templates artifact (a full render loads + * every icon token the canonical fixture names), that an empty document + * renders through the section guards, and that the phase-grid header + * contract fails with a named count rather than an index error. + */ +class NorthlineProposalSmokeTest { + + private static void render(StructuredProposalDocumentSpec spec) throws Exception { + // The preset owns its page geometry (size, margins, background), + // so the session starts unconfigured. + try (DocumentSession session = GraphCompose.document().create()) { + NorthlineProposal.create().compose(session, spec); + assertThat(session.roots()).isNotEmpty(); + // Drive layout + render — the icon loading, the zero-row table + // guards, and the chrome only exist at render time. + assertThat(session.toPdfBytes()).isNotEmpty(); + } + } + + @Test + void exposesStableIdentity() { + DocumentTemplate template = NorthlineProposal.create(); + assertThat(template.id()).isEqualTo(NorthlineProposal.ID); + assertThat(template.displayName()).isEqualTo(NorthlineProposal.DISPLAY_NAME); + } + + @Test + void rendersCanonicalProposalWithPackagedIcons() throws Exception { + render(NorthlineProposalFixtures.canonicalProposal()); + } + + @Test + void rendersEmptyProposal() throws Exception { + // Exercises the guards: no goal cells (the flattened column spec + // cannot describe zero cells), no phase grid, no signature row — + // and the investment table still carries its header + total band. + render(StructuredProposalDocumentSpec.from( + StructuredProposalData.builder().build())); + } + + @Test + void canonicalRenderCarriesTheTableText() throws Exception { + // The phase grid and the investment table are single leaf nodes in + // the layout snapshot (the recorded composed-cell blind spot), so + // their content is asserted on the rendered text layer instead. + byte[] pdfBytes; + try (DocumentSession session = GraphCompose.document().create()) { + NorthlineProposal.create().compose( + session, NorthlineProposalFixtures.canonicalProposal()); + pdfBytes = session.toPdfBytes(); + } + String text; + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + text = new PDFTextStripper().getText(document); + } + // Composed cells wrap, so the assertions stay within one line. + assertThat(text) + .contains("Discovery report") + .contains("Brand Identity Refinement") + .contains("2 weeks") + .contains("TOTAL INVESTMENT") + .contains("£19,250"); + } + + @Test + void rejectsUnknownIconTokenByName() { + StructuredProposalDocumentSpec spec = StructuredProposalDocumentSpec.from( + StructuredProposalData.builder() + .executiveSummary(new ProposalSummaryBlock("SUMMARY", + "no-such-icon", List.of("Body."))) + .build()); + try (DocumentSession session = GraphCompose.document().create()) { + assertThatThrownBy(() -> NorthlineProposal.create().compose(session, spec)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("no-such-icon"); + } + } + + @Test + void headerCountIsNotValidatedWhenTheGridNeverRenders() throws Exception { + // A wrong header count with zero phases constrains nothing — the + // grid is skipped before the validation. + render(StructuredProposalDocumentSpec.from(StructuredProposalData.builder() + .timeline(new ProposalPhaseGrid("TIMELINE", "badge-timeline", + List.of("PHASE", "FOCUS", "DURATION"), List.of())) + .build())); + } + + @Test + void phaseGridRejectsHeaderCountMismatchByName() { + StructuredProposalDocumentSpec spec = StructuredProposalDocumentSpec.from( + StructuredProposalData.builder() + .timeline(new ProposalPhaseGrid("TIMELINE", "badge-timeline", + List.of("PHASE", "FOCUS", "DURATION"), + List.of(new ProposalPhaseGrid.Phase("01", "Discover", + "Research", "1 week", "Report")))) + .build()); + try (DocumentSession session = GraphCompose.document().create()) { + assertThatThrownBy(() -> NorthlineProposal.create().compose(session, spec)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("one header per column"); + } + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalVisualParityTest.java b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalVisualParityTest.java new file mode 100644 index 000000000..c276e7186 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposalVisualParityTest.java @@ -0,0 +1,48 @@ +package com.demcha.compose.document.templates.proposal.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 NorthlineProposal} — both pages + * of the canonical fixture against checked-in baselines. + * + *

Separate from {@code ProposalV2VisualParityTest} because that gate is + * parameterised on the narrative {@code ProposalDocumentSpec} while this + * preset consumes the structured spec. Budget and tolerance mirror the + * other template parity gates (Lato/Spectral are bundled TTFs, the same + * cross-platform drift class as the CV presets' faces).

+ * + *

Re-blessing baselines — after a deliberate visual + * change, re-run with {@code -Dgraphcompose.visual.approve=true} and + * commit the updated PNGs with the change. Baselines live under + * {@code src/test/resources/visual-baselines/proposal-v2-layered/}.

+ */ +class NorthlineProposalVisualParityTest { + + private static final Path BASELINE_ROOT = Path.of( + "src", "test", "resources", "visual-baselines", "proposal-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()) { + NorthlineProposal.create().compose( + document, NorthlineProposalFixtures.canonicalProposal()); + pdfBytes = document.toPdfBytes(); + } + + PdfVisualRegression.standard() + .baselineRoot(BASELINE_ROOT) + .perPixelTolerance(PER_PIXEL_TOLERANCE) + .mismatchedPixelBudget(PIXEL_DIFF_BUDGET) + .assertMatchesBaseline("northline_proposal", pdfBytes); + } +} diff --git a/qa/src/test/resources/layout-snapshots/canonical-templates/proposal/northline_proposal_layout.json b/qa/src/test/resources/layout-snapshots/canonical-templates/proposal/northline_proposal_layout.json new file mode 100644 index 000000000..629dd3f5a --- /dev/null +++ b/qa/src/test/resources/layout-snapshots/canonical-templates/proposal/northline_proposal_layout.json @@ -0,0 +1,5267 @@ +{ + "formatVersion" : "2.0", + "canvas" : { + "pageWidth" : 595.276, + "pageHeight" : 841.89, + "innerWidth" : 523.276, + "innerHeight" : 785.89, + "margin" : { + "top" : 8.0, + "right" : 36.0, + "bottom" : 48.0, + "left" : 36.0 + } + }, + "totalPages" : 2, + "nodes" : [ { + "path" : "Proposal[0]", + "entityName" : "Proposal", + "entityKind" : "ContainerNode", + "parentPath" : null, + "childIndex" : 0, + "depth" : 1, + "layer" : 1, + "computedX" : 36.0, + "computedY" : -690.811, + "placementX" : 36.0, + "placementY" : -690.811, + "placementWidth" : 523.276, + "placementHeight" : 1524.701, + "startPage" : 0, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 1524.701, + "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" : "Proposal[0]/Header[0]", + "entityName" : "Header", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 0, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 775.89, + "placementX" : 36.0, + "placementY" : 775.89, + "placementWidth" : 523.276, + "placementHeight" : 58.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 58.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" : "Proposal[0]/Header[0]/HeaderRow[0]", + "entityName" : "HeaderRow", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/Header[0]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 783.89, + "placementX" : 36.0, + "placementY" : 783.89, + "placementWidth" : 523.276, + "placementHeight" : 50.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 50.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" : "Proposal[0]/Header[0]/HeaderRow[0]/LogoMark[0]", + "entityName" : "LogoMark", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 783.89, + "placementX" : 36.0, + "placementY" : 783.89, + "placementWidth" : 50.0, + "placementHeight" : 50.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 50.0, + "contentHeight" : 50.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" : "Proposal[0]/Header[0]/HeaderRow[0]/LogoMark[0]/Monogram[0]", + "entityName" : "Monogram", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]/LogoMark[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 790.89, + "placementX" : 36.0, + "placementY" : 790.89, + "placementWidth" : 50.0, + "placementHeight" : 36.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 50.0, + "contentHeight" : 36.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" : "Proposal[0]/Header[0]/HeaderRow[0]/Wordmark[1]", + "entityName" : "Wordmark", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 98.5, + "computedY" : 789.69, + "placementX" : 98.5, + "placementY" : 789.69, + "placementWidth" : 92.096, + "placementHeight" : 38.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 92.096, + "contentHeight" : 38.4, + "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" : "Proposal[0]/Header[0]/HeaderRow[0]/Wordmark[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]/Wordmark[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 98.5, + "computedY" : 808.89, + "placementX" : 98.5, + "placementY" : 808.89, + "placementWidth" : 92.096, + "placementHeight" : 19.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 92.096, + "contentHeight" : 19.2, + "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" : "Proposal[0]/Header[0]/HeaderRow[0]/Wordmark[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]/Wordmark[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 98.5, + "computedY" : 789.69, + "placementX" : 98.5, + "placementY" : 789.69, + "placementWidth" : 60.128, + "placementHeight" : 19.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 60.128, + "contentHeight" : 19.2, + "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" : "Proposal[0]/Header[0]/HeaderRow[0]/SpacerNode[2]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 210.422, + "computedY" : 808.89, + "placementX" : 210.422, + "placementY" : 808.89, + "placementWidth" : 0.0, + "placementHeight" : 0.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 0.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" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]", + "entityName" : "DocumentLabel", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 475.552, + "computedY" : 799.39, + "placementX" : 475.552, + "placementY" : 799.39, + "placementWidth" : 83.724, + "placementHeight" : 19.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 83.724, + "contentHeight" : 19.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" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 475.552, + "computedY" : 807.59, + "placementX" : 475.552, + "placementY" : 807.59, + "placementWidth" : 83.724, + "placementHeight" : 10.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 83.724, + "contentHeight" : 10.8, + "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" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]/Align[1]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 475.552, + "computedY" : 799.39, + "placementX" : 475.552, + "placementY" : 799.39, + "placementWidth" : 83.724, + "placementHeight" : 8.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 83.724, + "contentHeight" : 8.2, + "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" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]/Align[1]/DocumentLabelRule[0]", + "entityName" : "DocumentLabelRule", + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Header[0]/HeaderRow[0]/DocumentLabel[3]/Align[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 524.176, + "computedY" : 799.39, + "placementX" : 524.176, + "placementY" : 799.39, + "placementWidth" : 35.1, + "placementHeight" : 2.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 35.1, + "contentHeight" : 2.2, + "margin" : { + "top" : 6.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Header[0]/LineNode[1]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Header[0]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 775.89, + "placementX" : 36.0, + "placementY" : 775.89, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 1.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/TitleBlock[1]", + "entityName" : "TitleBlock", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 1, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 613.59, + "placementX" : 36.0, + "placementY" : 613.59, + "placementWidth" : 523.276, + "placementHeight" : 144.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 144.3, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/TitleBlock[1]/LineNode[0]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/TitleBlock[1]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 755.29, + "placementX" : 36.0, + "placementY" : 755.29, + "placementWidth" : 45.002, + "placementHeight" : 2.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 45.002, + "contentHeight" : 2.6, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/TitleBlock[1]/TitleLines[1]", + "entityName" : "TitleLines", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/TitleBlock[1]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 613.59, + "placementX" : 36.0, + "placementY" : 613.59, + "placementWidth" : 523.276, + "placementHeight" : 139.7, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 139.7, + "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" : "Proposal[0]/TitleBlock[1]/TitleLines[1]/TitleLead[0]", + "entityName" : "TitleLead", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/TitleBlock[1]/TitleLines[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 683.278, + "placementX" : 36.0, + "placementY" : 683.278, + "placementWidth" : 221.72, + "placementHeight" : 70.012, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 221.72, + "contentHeight" : 70.012, + "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" : "Proposal[0]/TitleBlock[1]/TitleLines[1]/TitleLine1[1]", + "entityName" : "TitleLine1", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/TitleBlock[1]/TitleLines[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 635.278, + "placementX" : 36.0, + "placementY" : 635.278, + "placementWidth" : 353.372, + "placementHeight" : 70.012, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 353.372, + "contentHeight" : 70.012, + "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" : "Proposal[0]/TitleBlock[1]/TitleLines[1]/TitleLine2[2]", + "entityName" : "TitleLine2", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/TitleBlock[1]/TitleLines[1]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 587.278, + "placementX" : 36.0, + "placementY" : 587.278, + "placementWidth" : 379.638, + "placementHeight" : 70.012, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 379.638, + "contentHeight" : 70.012, + "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" : "Proposal[0]/TitleMeta[2]", + "entityName" : "TitleMeta", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 2, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 585.99, + "placementX" : 36.0, + "placementY" : 585.99, + "placementWidth" : 366.336, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 366.336, + "contentHeight" : 9.6, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/TitleMeta[2]/MetaLine[0]", + "entityName" : "MetaLine", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/TitleMeta[2]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 585.99, + "placementX" : 36.0, + "placementY" : 585.99, + "placementWidth" : 366.336, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 366.336, + "contentHeight" : 9.6, + "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" : "Proposal[0]/SummaryBand[3]", + "entityName" : "SummaryBand", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]", + "childIndex" : 3, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 341.89, + "placementX" : 36.0, + "placementY" : 341.89, + "placementWidth" : 523.276, + "placementHeight" : 226.1, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 226.1, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]", + "entityName" : "SummaryColumn", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 360.04, + "placementX" : 36.0, + "placementY" : 360.04, + "placementWidth" : 324.431, + "placementHeight" : 207.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 324.431, + "contentHeight" : 207.95, + "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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 540.99, + "placementX" : 36.0, + "placementY" : 540.99, + "placementWidth" : 324.431, + "placementHeight" : 27.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 324.431, + "contentHeight" : 27.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 540.99, + "placementX" : 36.0, + "placementY" : 540.99, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]/SectionBadge[0]/Icon-badge-summary[0]", + "entityName" : "Icon-badge-summary", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 42.48, + "computedY" : 547.47, + "placementX" : 42.48, + "placementY" : 547.47, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 74.34, + "computedY" : 546.39, + "placementX" : 74.34, + "placementY" : 546.39, + "placementWidth" : 145.854, + "placementHeight" : 16.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 145.854, + "contentHeight" : 16.2, + "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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 468.19, + "placementX" : 36.0, + "placementY" : 468.19, + "placementWidth" : 318.63, + "placementHeight" : 65.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 318.63, + "contentHeight" : 65.8, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 405.04, + "placementX" : 36.0, + "placementY" : 405.04, + "placementWidth" : 314.24, + "placementHeight" : 52.35, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 314.24, + "contentHeight" : 52.35, + "margin" : { + "top" : 10.8, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SpacerNode[3]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 387.04, + "placementX" : 36.0, + "placementY" : 387.04, + "placementWidth" : 0.0, + "placementHeight" : 18.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 18.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]", + "childIndex" : 4, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 360.04, + "placementX" : 36.0, + "placementY" : 360.04, + "placementWidth" : 324.431, + "placementHeight" : 27.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 324.431, + "contentHeight" : 27.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 360.04, + "placementX" : 36.0, + "placementY" : 360.04, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]/SectionBadge[0]/Icon-badge-goals[0]", + "entityName" : "Icon-badge-goals", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 42.48, + "computedY" : 366.52, + "placementX" : 42.48, + "placementY" : 366.52, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/SummaryColumn[0]/SectionHeading[4]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 74.34, + "computedY" : 365.44, + "placementX" : 74.34, + "placementY" : 365.44, + "placementWidth" : 106.353, + "placementHeight" : 16.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 106.353, + "contentHeight" : 16.2, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]", + "entityName" : "GlanceColumn", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 376.129, + "computedY" : 341.89, + "placementX" : 376.129, + "placementY" : 341.89, + "placementWidth" : 183.147, + "placementHeight" : 226.1, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 183.147, + "contentHeight" : 226.1, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "entityName" : "GlancePanel", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 376.129, + "computedY" : 341.89, + "placementX" : 376.129, + "placementY" : 341.89, + "placementWidth" : 183.147, + "placementHeight" : 226.1, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 183.147, + "contentHeight" : 226.1, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 16.0, + "right" : 16.0, + "bottom" : 16.0, + "left" : 16.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 539.39, + "placementX" : 392.129, + "placementY" : 539.39, + "placementWidth" : 115.553, + "placementHeight" : 12.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 115.553, + "contentHeight" : 12.6, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 7.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]", + "entityName" : "GlanceFact", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 502.99, + "placementX" : 392.129, + "placementY" : 502.99, + "placementWidth" : 151.147, + "placementHeight" : 29.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 29.4, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]/Icon-fact-duration[0]", + "entityName" : "Icon-fact-duration", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 392.129, + "computedY" : 505.19, + "placementX" : 392.129, + "placementY" : 505.19, + "placementWidth" : 25.0, + "placementHeight" : 25.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 25.0, + "contentHeight" : 25.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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]/FactText[1]", + "entityName" : "FactText", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 429.129, + "computedY" : 504.49, + "placementX" : 429.129, + "placementY" : 504.49, + "placementWidth" : 79.184, + "placementHeight" : 26.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 79.184, + "contentHeight" : 26.4, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]/FactText[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]/FactText[1]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 521.29, + "placementX" : 429.129, + "placementY" : 521.29, + "placementWidth" : 79.184, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 79.184, + "contentHeight" : 9.6, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]/FactText[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[1]/FactText[1]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 504.49, + "placementX" : 429.129, + "placementY" : 504.49, + "placementWidth" : 51.366, + "placementHeight" : 16.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 51.366, + "contentHeight" : 16.8, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/LineNode[2]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 494.99, + "placementX" : 392.129, + "placementY" : 494.99, + "placementWidth" : 151.147, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 1.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 7.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]", + "entityName" : "GlanceFact", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 458.59, + "placementX" : 392.129, + "placementY" : 458.59, + "placementWidth" : 151.147, + "placementHeight" : 29.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 29.4, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]/Icon-fact-start[0]", + "entityName" : "Icon-fact-start", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 392.129, + "computedY" : 460.79, + "placementX" : 392.129, + "placementY" : 460.79, + "placementWidth" : 25.0, + "placementHeight" : 25.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 25.0, + "contentHeight" : 25.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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]/FactText[1]", + "entityName" : "FactText", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 429.129, + "computedY" : 460.09, + "placementX" : 429.129, + "placementY" : 460.09, + "placementWidth" : 104.062, + "placementHeight" : 26.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.062, + "contentHeight" : 26.4, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]/FactText[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]/FactText[1]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 476.89, + "placementX" : 429.129, + "placementY" : 476.89, + "placementWidth" : 64.552, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 64.552, + "contentHeight" : 9.6, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]/FactText[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[3]/FactText[1]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 460.09, + "placementX" : 429.129, + "placementY" : 460.09, + "placementWidth" : 104.062, + "placementHeight" : 16.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.062, + "contentHeight" : 16.8, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/LineNode[4]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 450.59, + "placementX" : 392.129, + "placementY" : 450.59, + "placementWidth" : 151.147, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 1.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 7.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]", + "entityName" : "GlanceFact", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 414.19, + "placementX" : 392.129, + "placementY" : 414.19, + "placementWidth" : 151.147, + "placementHeight" : 29.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 29.4, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]/Icon-fact-contact[0]", + "entityName" : "Icon-fact-contact", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 392.129, + "computedY" : 416.39, + "placementX" : 392.129, + "placementY" : 416.39, + "placementWidth" : 25.0, + "placementHeight" : 25.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 25.0, + "contentHeight" : 25.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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]/FactText[1]", + "entityName" : "FactText", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 429.129, + "computedY" : 415.69, + "placementX" : 429.129, + "placementY" : 415.69, + "placementWidth" : 78.148, + "placementHeight" : 26.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 78.148, + "contentHeight" : 26.4, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]/FactText[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]/FactText[1]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 432.49, + "placementX" : 429.129, + "placementY" : 432.49, + "placementWidth" : 76.248, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 76.248, + "contentHeight" : 9.6, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]/FactText[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[5]/FactText[1]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 415.69, + "placementX" : 429.129, + "placementY" : 415.69, + "placementWidth" : 78.148, + "placementHeight" : 16.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 78.148, + "contentHeight" : 16.8, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/LineNode[6]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 406.19, + "placementX" : 392.129, + "placementY" : 406.19, + "placementWidth" : 151.147, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 1.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 7.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]", + "entityName" : "GlanceFact", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]", + "childIndex" : 7, + "depth" : 5, + "layer" : 5, + "computedX" : 392.129, + "computedY" : 357.89, + "placementX" : 392.129, + "placementY" : 357.89, + "placementWidth" : 151.147, + "placementHeight" : 41.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 151.147, + "contentHeight" : 41.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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/Icon-fact-validity[0]", + "entityName" : "Icon-fact-validity", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 392.129, + "computedY" : 366.04, + "placementX" : 392.129, + "placementY" : 366.04, + "placementWidth" : 25.0, + "placementHeight" : 25.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 25.0, + "contentHeight" : 25.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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]", + "entityName" : "FactText", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 429.129, + "computedY" : 360.24, + "placementX" : 429.129, + "placementY" : 360.24, + "placementWidth" : 93.05, + "placementHeight" : 36.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 93.05, + "contentHeight" : 36.6, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 387.24, + "placementX" : 429.129, + "placementY" : 387.24, + "placementWidth" : 80.136, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 80.136, + "contentHeight" : 9.6, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 370.44, + "placementX" : 429.129, + "placementY" : 370.44, + "placementWidth" : 48.258, + "placementHeight" : 16.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 48.258, + "contentHeight" : 16.8, + "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" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/SummaryBand[3]/GlanceColumn[1]/GlancePanel[0]/GlanceFact[7]/FactText[1]", + "childIndex" : 2, + "depth" : 7, + "layer" : 7, + "computedX" : 429.129, + "computedY" : 360.24, + "placementX" : 429.129, + "placementY" : 360.24, + "placementWidth" : 93.05, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 93.05, + "contentHeight" : 10.2, + "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" : "Proposal[0]/GoalCells[4]", + "entityName" : "GoalCells", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]", + "childIndex" : 4, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 286.789, + "placementX" : 36.0, + "placementY" : 286.789, + "placementWidth" : 523.276, + "placementHeight" : 37.101, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 37.101, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/GoalCells[4]/Icon-goal-brand[0]", + "entityName" : "Icon-goal-brand", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 303.89, + "placementX" : 36.0, + "placementY" : 303.89, + "placementWidth" : 20.0, + "placementHeight" : 20.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 20.0, + "contentHeight" : 20.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" : "Proposal[0]/GoalCells[4]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 63.0, + "computedY" : 288.59, + "placementX" : 63.0, + "placementY" : 288.59, + "placementWidth" : 80.352, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 80.352, + "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" : "Proposal[0]/GoalCells[4]/LineNode[2]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 2, + "depth" : 3, + "layer" : 3, + "computedX" : 162.794, + "computedY" : 286.789, + "placementX" : 162.794, + "placementY" : 286.789, + "placementWidth" : 1.0, + "placementHeight" : 37.101, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 1.0, + "contentHeight" : 37.101, + "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" : "Proposal[0]/GoalCells[4]/Icon-goal-usability[3]", + "entityName" : "Icon-goal-usability", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 3, + "depth" : 3, + "layer" : 3, + "computedX" : 170.494, + "computedY" : 303.89, + "placementX" : 170.494, + "placementY" : 303.89, + "placementWidth" : 20.0, + "placementHeight" : 20.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 20.0, + "contentHeight" : 20.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" : "Proposal[0]/GoalCells[4]/ParagraphNode[4]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 4, + "depth" : 3, + "layer" : 3, + "computedX" : 197.494, + "computedY" : 288.59, + "placementX" : 197.494, + "placementY" : 288.59, + "placementWidth" : 72.54, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 72.54, + "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" : "Proposal[0]/GoalCells[4]/LineNode[5]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 5, + "depth" : 3, + "layer" : 3, + "computedX" : 297.288, + "computedY" : 286.789, + "placementX" : 297.288, + "placementY" : 286.789, + "placementWidth" : 1.0, + "placementHeight" : 37.101, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 1.0, + "contentHeight" : 37.101, + "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" : "Proposal[0]/GoalCells[4]/Icon-goal-message[6]", + "entityName" : "Icon-goal-message", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 6, + "depth" : 3, + "layer" : 3, + "computedX" : 304.988, + "computedY" : 303.89, + "placementX" : 304.988, + "placementY" : 303.89, + "placementWidth" : 20.0, + "placementHeight" : 20.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 20.0, + "contentHeight" : 20.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" : "Proposal[0]/GoalCells[4]/ParagraphNode[7]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 7, + "depth" : 3, + "layer" : 3, + "computedX" : 331.988, + "computedY" : 288.59, + "placementX" : 331.988, + "placementY" : 288.59, + "placementWidth" : 87.291, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 87.291, + "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" : "Proposal[0]/GoalCells[4]/LineNode[8]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 8, + "depth" : 3, + "layer" : 3, + "computedX" : 431.782, + "computedY" : 286.789, + "placementX" : 431.782, + "placementY" : 286.789, + "placementWidth" : 1.0, + "placementHeight" : 37.101, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 1.0, + "contentHeight" : 37.101, + "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" : "Proposal[0]/GoalCells[4]/Icon-goal-growth[9]", + "entityName" : "Icon-goal-growth", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 9, + "depth" : 3, + "layer" : 3, + "computedX" : 439.482, + "computedY" : 303.89, + "placementX" : 439.482, + "placementY" : 303.89, + "placementWidth" : 20.0, + "placementHeight" : 20.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 20.0, + "contentHeight" : 20.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" : "Proposal[0]/GoalCells[4]/ParagraphNode[10]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/GoalCells[4]", + "childIndex" : 10, + "depth" : 3, + "layer" : 3, + "computedX" : 466.482, + "computedY" : 288.59, + "placementX" : 466.482, + "placementY" : 288.59, + "placementWidth" : 86.607, + "placementHeight" : 35.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 86.607, + "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" : "Proposal[0]/ScopeOfWork[5]", + "entityName" : "ScopeOfWork", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 5, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 64.539, + "placementX" : 36.0, + "placementY" : 64.539, + "placementWidth" : 523.276, + "placementHeight" : 204.25, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 204.25, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 241.789, + "placementX" : 36.0, + "placementY" : 241.789, + "placementWidth" : 523.276, + "placementHeight" : 27.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 27.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" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 241.789, + "placementX" : 36.0, + "placementY" : 241.789, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]/SectionBadge[0]/Icon-badge-scope[0]", + "entityName" : "Icon-badge-scope", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 42.48, + "computedY" : 248.269, + "placementX" : 42.48, + "placementY" : 248.269, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.34, + "computedY" : 247.189, + "placementX" : 74.34, + "placementY" : 247.189, + "placementWidth" : 111.523, + "placementHeight" : 16.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 111.523, + "contentHeight" : 16.2, + "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" : "Proposal[0]/ScopeOfWork[5]/LineNode[1]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 233.789, + "placementX" : 36.0, + "placementY" : 233.789, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 1.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]", + "entityName" : "ScopeRow01", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 2, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 200.739, + "placementX" : 36.0, + "placementY" : 200.739, + "placementWidth" : 523.276, + "placementHeight" : 33.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 33.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 5.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]/ScopeBadge[0]", + "entityName" : "ScopeBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 213.789, + "placementX" : 36.0, + "placementY" : 213.789, + "placementWidth" : 26.0, + "placementHeight" : 15.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 15.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]/ScopeBadge[0]/ScopeNumber[0]", + "entityName" : "ScopeNumber", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]/ScopeBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 216.189, + "placementX" : 36.0, + "placementY" : 216.189, + "placementWidth" : 26.0, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 10.2, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.0, + "computedY" : 217.389, + "placementX" : 74.0, + "placementY" : 217.389, + "placementWidth" : 92.748, + "placementHeight" : 11.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 92.748, + "contentHeight" : 11.4, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow01[2]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 270.428, + "computedY" : 205.739, + "placementX" : 270.428, + "placementY" : 205.739, + "placementWidth" : 273.564, + "placementHeight" : 23.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 273.564, + "contentHeight" : 23.05, + "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" : "Proposal[0]/ScopeOfWork[5]/LineNode[3]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 3, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 199.739, + "placementX" : 36.0, + "placementY" : 199.739, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 1.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]", + "entityName" : "ScopeRow02", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 4, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 166.689, + "placementX" : 36.0, + "placementY" : 166.689, + "placementWidth" : 523.276, + "placementHeight" : 33.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 33.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 5.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]/ScopeBadge[0]", + "entityName" : "ScopeBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 179.739, + "placementX" : 36.0, + "placementY" : 179.739, + "placementWidth" : 26.0, + "placementHeight" : 15.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 15.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]/ScopeBadge[0]/ScopeNumber[0]", + "entityName" : "ScopeNumber", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]/ScopeBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 182.139, + "placementX" : 36.0, + "placementY" : 182.139, + "placementWidth" : 26.0, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 10.2, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.0, + "computedY" : 183.339, + "placementX" : 74.0, + "placementY" : 183.339, + "placementWidth" : 112.87, + "placementHeight" : 11.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 112.87, + "contentHeight" : 11.4, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow02[4]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 270.428, + "computedY" : 171.689, + "placementX" : 270.428, + "placementY" : 171.689, + "placementWidth" : 284.661, + "placementHeight" : 23.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 284.661, + "contentHeight" : 23.05, + "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" : "Proposal[0]/ScopeOfWork[5]/LineNode[5]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 5, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 165.689, + "placementX" : 36.0, + "placementY" : 165.689, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 1.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]", + "entityName" : "ScopeRow03", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 6, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 132.639, + "placementX" : 36.0, + "placementY" : 132.639, + "placementWidth" : 523.276, + "placementHeight" : 33.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 33.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 5.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]/ScopeBadge[0]", + "entityName" : "ScopeBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 145.689, + "placementX" : 36.0, + "placementY" : 145.689, + "placementWidth" : 26.0, + "placementHeight" : 15.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 15.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]/ScopeBadge[0]/ScopeNumber[0]", + "entityName" : "ScopeNumber", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]/ScopeBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 148.089, + "placementX" : 36.0, + "placementY" : 148.089, + "placementWidth" : 26.0, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 10.2, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.0, + "computedY" : 149.289, + "placementX" : 74.0, + "placementY" : 149.289, + "placementWidth" : 97.28, + "placementHeight" : 11.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 97.28, + "contentHeight" : 11.4, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow03[6]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 270.428, + "computedY" : 137.639, + "placementX" : 270.428, + "placementY" : 137.639, + "placementWidth" : 250.938, + "placementHeight" : 23.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 250.938, + "contentHeight" : 23.05, + "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" : "Proposal[0]/ScopeOfWork[5]/LineNode[7]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 7, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 131.639, + "placementX" : 36.0, + "placementY" : 131.639, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 1.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]", + "entityName" : "ScopeRow04", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 8, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 98.589, + "placementX" : 36.0, + "placementY" : 98.589, + "placementWidth" : 523.276, + "placementHeight" : 33.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 33.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 5.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]/ScopeBadge[0]", + "entityName" : "ScopeBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 111.639, + "placementX" : 36.0, + "placementY" : 111.639, + "placementWidth" : 26.0, + "placementHeight" : 15.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 15.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]/ScopeBadge[0]/ScopeNumber[0]", + "entityName" : "ScopeNumber", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]/ScopeBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 114.039, + "placementX" : 36.0, + "placementY" : 114.039, + "placementWidth" : 26.0, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 10.2, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.0, + "computedY" : 115.239, + "placementX" : 74.0, + "placementY" : 115.239, + "placementWidth" : 133.19, + "placementHeight" : 11.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 133.19, + "contentHeight" : 11.4, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow04[8]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 270.428, + "computedY" : 103.589, + "placementX" : 270.428, + "placementY" : 103.589, + "placementWidth" : 256.446, + "placementHeight" : 23.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 256.446, + "contentHeight" : 23.05, + "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" : "Proposal[0]/ScopeOfWork[5]/LineNode[9]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 9, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 97.589, + "placementX" : 36.0, + "placementY" : 97.589, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 1.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]", + "entityName" : "ScopeRow05", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]", + "childIndex" : 10, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 64.539, + "placementX" : 36.0, + "placementY" : 64.539, + "placementWidth" : 523.276, + "placementHeight" : 33.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 523.276, + "contentHeight" : 33.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 5.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]/ScopeBadge[0]", + "entityName" : "ScopeBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 77.589, + "placementX" : 36.0, + "placementY" : 77.589, + "placementWidth" : 26.0, + "placementHeight" : 15.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 15.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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]/ScopeBadge[0]/ScopeNumber[0]", + "entityName" : "ScopeNumber", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]/ScopeBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 79.989, + "placementX" : 36.0, + "placementY" : 79.989, + "placementWidth" : 26.0, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.0, + "contentHeight" : 10.2, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.0, + "computedY" : 81.189, + "placementX" : 74.0, + "placementY" : 81.189, + "placementWidth" : 66.633, + "placementHeight" : 11.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 66.633, + "contentHeight" : 11.4, + "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" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/ScopeOfWork[5]/ScopeRow05[10]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 270.428, + "computedY" : 69.539, + "placementX" : 270.428, + "placementY" : 69.539, + "placementWidth" : 260.577, + "placementHeight" : 23.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 260.577, + "contentHeight" : 23.05, + "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" : "Proposal[0]/Header[6]", + "entityName" : "Header", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 6, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 6.539, + "placementX" : 36.0, + "placementY" : 6.539, + "placementWidth" : 523.276, + "placementHeight" : 58.0, + "startPage" : 0, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 58.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" : "Proposal[0]/Header[6]/HeaderRow[0]", + "entityName" : "HeaderRow", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/Header[6]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 783.89, + "placementX" : 36.0, + "placementY" : 783.89, + "placementWidth" : 523.276, + "placementHeight" : 50.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 50.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" : "Proposal[0]/Header[6]/HeaderRow[0]/LogoMark[0]", + "entityName" : "LogoMark", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 783.89, + "placementX" : 36.0, + "placementY" : 783.89, + "placementWidth" : 50.0, + "placementHeight" : 50.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 50.0, + "contentHeight" : 50.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" : "Proposal[0]/Header[6]/HeaderRow[0]/LogoMark[0]/Monogram[0]", + "entityName" : "Monogram", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]/LogoMark[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 790.89, + "placementX" : 36.0, + "placementY" : 790.89, + "placementWidth" : 50.0, + "placementHeight" : 36.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 50.0, + "contentHeight" : 36.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" : "Proposal[0]/Header[6]/HeaderRow[0]/Wordmark[1]", + "entityName" : "Wordmark", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 98.5, + "computedY" : 789.69, + "placementX" : 98.5, + "placementY" : 789.69, + "placementWidth" : 92.096, + "placementHeight" : 38.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 92.096, + "contentHeight" : 38.4, + "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" : "Proposal[0]/Header[6]/HeaderRow[0]/Wordmark[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]/Wordmark[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 98.5, + "computedY" : 808.89, + "placementX" : 98.5, + "placementY" : 808.89, + "placementWidth" : 92.096, + "placementHeight" : 19.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 92.096, + "contentHeight" : 19.2, + "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" : "Proposal[0]/Header[6]/HeaderRow[0]/Wordmark[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]/Wordmark[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 98.5, + "computedY" : 789.69, + "placementX" : 98.5, + "placementY" : 789.69, + "placementWidth" : 60.128, + "placementHeight" : 19.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 60.128, + "contentHeight" : 19.2, + "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" : "Proposal[0]/Header[6]/HeaderRow[0]/SpacerNode[2]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 210.422, + "computedY" : 808.89, + "placementX" : 210.422, + "placementY" : 808.89, + "placementWidth" : 0.0, + "placementHeight" : 0.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 0.0, + "contentHeight" : 0.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" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]", + "entityName" : "DocumentLabel", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 475.552, + "computedY" : 799.39, + "placementX" : 475.552, + "placementY" : 799.39, + "placementWidth" : 83.724, + "placementHeight" : 19.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 83.724, + "contentHeight" : 19.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" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 475.552, + "computedY" : 807.59, + "placementX" : 475.552, + "placementY" : 807.59, + "placementWidth" : 83.724, + "placementHeight" : 10.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 83.724, + "contentHeight" : 10.8, + "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" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]/Align[1]", + "entityName" : null, + "entityKind" : "Align", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 475.552, + "computedY" : 799.39, + "placementX" : 475.552, + "placementY" : 799.39, + "placementWidth" : 83.724, + "placementHeight" : 8.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 83.724, + "contentHeight" : 8.2, + "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" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]/Align[1]/DocumentLabelRule[0]", + "entityName" : "DocumentLabelRule", + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Header[6]/HeaderRow[0]/DocumentLabel[3]/Align[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 524.176, + "computedY" : 799.39, + "placementX" : 524.176, + "placementY" : 799.39, + "placementWidth" : 35.1, + "placementHeight" : 2.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 35.1, + "contentHeight" : 2.2, + "margin" : { + "top" : 6.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Header[6]/LineNode[1]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Header[6]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 775.89, + "placementX" : 36.0, + "placementY" : 775.89, + "placementWidth" : 523.276, + "placementHeight" : 1.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 1.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]", + "entityName" : "Deliverables", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 7, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 638.89, + "placementX" : 36.0, + "placementY" : 638.89, + "placementWidth" : 523.276, + "placementHeight" : 119.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 119.0, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Deliverables[7]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 730.89, + "placementX" : 36.0, + "placementY" : 730.89, + "placementWidth" : 523.276, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 27.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" : "Proposal[0]/Deliverables[7]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Deliverables[7]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 730.89, + "placementX" : 36.0, + "placementY" : 730.89, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/Deliverables[7]/SectionHeading[0]/SectionBadge[0]/Icon-badge-deliverables[0]", + "entityName" : "Icon-badge-deliverables", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/Deliverables[7]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 42.48, + "computedY" : 737.37, + "placementX" : 42.48, + "placementY" : 737.37, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/Deliverables[7]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.34, + "computedY" : 736.29, + "placementX" : 74.34, + "placementY" : 736.29, + "placementWidth" : 95.364, + "placementHeight" : 16.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 95.364, + "contentHeight" : 16.2, + "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" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]", + "entityName" : "DeliverableColumns", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/Deliverables[7]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 638.89, + "placementX" : 36.0, + "placementY" : 638.89, + "placementWidth" : 523.276, + "placementHeight" : 92.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 92.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]", + "entityName" : "DeliverablesLeft", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 638.89, + "placementX" : 36.0, + "placementY" : 638.89, + "placementWidth" : 175.2, + "placementHeight" : 85.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 175.2, + "contentHeight" : 85.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" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 711.89, + "placementX" : 36.0, + "placementY" : 711.89, + "placementWidth" : 119.16, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 119.16, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 694.89, + "placementX" : 36.0, + "placementY" : 694.89, + "placementWidth" : 175.2, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 175.2, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 677.89, + "placementX" : 36.0, + "placementY" : 677.89, + "placementWidth" : 165.03, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 165.03, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]/ParagraphNode[3]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 660.89, + "placementX" : 36.0, + "placementY" : 660.89, + "placementWidth" : 116.83, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 116.83, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]/ParagraphNode[4]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesLeft[0]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 643.89, + "placementX" : 36.0, + "placementY" : 643.89, + "placementWidth" : 138.9, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 138.9, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]", + "entityName" : "DeliverablesRight", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 287.172, + "computedY" : 655.89, + "placementX" : 287.172, + "placementY" : 655.89, + "placementWidth" : 217.84, + "placementHeight" : 68.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 217.84, + "contentHeight" : 68.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" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 287.172, + "computedY" : 711.89, + "placementX" : 287.172, + "placementY" : 711.89, + "placementWidth" : 217.84, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 217.84, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 287.172, + "computedY" : 694.89, + "placementX" : 287.172, + "placementY" : 694.89, + "placementWidth" : 165.27, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 165.27, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 287.172, + "computedY" : 677.89, + "placementX" : 287.172, + "placementY" : 677.89, + "placementWidth" : 187.4, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 187.4, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]/ParagraphNode[3]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Deliverables[7]/DeliverableColumns[1]/DeliverablesRight[1]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 287.172, + "computedY" : 660.89, + "placementX" : 287.172, + "placementY" : 660.89, + "placementWidth" : 164.33, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 164.33, + "contentHeight" : 12.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Timeline[8]", + "entityName" : "Timeline", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 8, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 410.74, + "placementX" : 36.0, + "placementY" : 410.74, + "placementWidth" : 523.276, + "placementHeight" : 210.15, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 210.15, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Timeline[8]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Timeline[8]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 593.89, + "placementX" : 36.0, + "placementY" : 593.89, + "placementWidth" : 523.276, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 27.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" : "Proposal[0]/Timeline[8]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Timeline[8]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 593.89, + "placementX" : 36.0, + "placementY" : 593.89, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/Timeline[8]/SectionHeading[0]/SectionBadge[0]/Icon-badge-timeline[0]", + "entityName" : "Icon-badge-timeline", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/Timeline[8]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 42.48, + "computedY" : 600.37, + "placementX" : 42.48, + "placementY" : 600.37, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/Timeline[8]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Timeline[8]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 74.34, + "computedY" : 599.29, + "placementX" : 74.34, + "placementY" : 599.29, + "placementWidth" : 61.83, + "placementHeight" : 16.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 61.83, + "contentHeight" : 16.2, + "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" : "Proposal[0]/Timeline[8]/TimelineTable[1]", + "entityName" : "TimelineTable", + "entityKind" : "TableNode", + "parentPath" : "Proposal[0]/Timeline[8]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 410.74, + "placementX" : 36.0, + "placementY" : 410.74, + "placementWidth" : 523.276, + "placementHeight" : 176.15, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 176.15, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]", + "entityName" : "MoneyBand", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]", + "childIndex" : 9, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 188.34, + "placementX" : 36.0, + "placementY" : 188.34, + "placementWidth" : 523.276, + "placementHeight" : 204.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 204.4, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]", + "entityName" : "InvestmentColumn", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/MoneyBand[9]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 36.0, + "computedY" : 188.34, + "placementX" : 36.0, + "placementY" : 188.34, + "placementWidth" : 308.733, + "placementHeight" : 204.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 308.733, + "contentHeight" : 204.4, + "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" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 365.74, + "placementX" : 36.0, + "placementY" : 365.74, + "placementWidth" : 308.733, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 308.733, + "contentHeight" : 27.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" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 36.0, + "computedY" : 365.74, + "placementX" : 36.0, + "placementY" : 365.74, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]/SectionBadge[0]/Icon-badge-investment[0]", + "entityName" : "Icon-badge-investment", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 42.48, + "computedY" : 372.22, + "placementX" : 42.48, + "placementY" : 372.22, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 74.34, + "computedY" : 371.14, + "placementX" : 74.34, + "placementY" : 371.14, + "placementWidth" : 86.103, + "placementHeight" : 16.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 86.103, + "contentHeight" : 16.2, + "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" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]/InvestmentTable[1]", + "entityName" : "InvestmentTable", + "entityKind" : "TableNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/InvestmentColumn[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 36.0, + "computedY" : 188.34, + "placementX" : 36.0, + "placementY" : 188.34, + "placementWidth" : 308.733, + "placementHeight" : 170.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 308.733, + "contentHeight" : 170.4, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]", + "entityName" : "TermsColumn", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/MoneyBand[9]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 376.129, + "computedY" : 218.74, + "placementX" : 376.129, + "placementY" : 218.74, + "placementWidth" : 183.147, + "placementHeight" : 174.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 183.147, + "contentHeight" : 174.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" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 376.129, + "computedY" : 365.74, + "placementX" : 376.129, + "placementY" : 365.74, + "placementWidth" : 183.147, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 183.147, + "contentHeight" : 27.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" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 376.129, + "computedY" : 365.74, + "placementX" : 376.129, + "placementY" : 365.74, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]/SectionBadge[0]/Icon-badge-terms[0]", + "entityName" : "Icon-badge-terms", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 382.609, + "computedY" : 372.22, + "placementX" : 382.609, + "placementY" : 372.22, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 414.469, + "computedY" : 371.14, + "placementX" : 414.469, + "placementY" : 371.14, + "placementWidth" : 44.712, + "placementHeight" : 16.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 44.712, + "contentHeight" : 16.2, + "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" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]", + "entityName" : "TermsList", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 376.129, + "computedY" : 218.74, + "placementX" : 376.129, + "placementY" : 218.74, + "placementWidth" : 182.676, + "placementHeight" : 147.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 182.676, + "contentHeight" : 147.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 376.129, + "computedY" : 332.34, + "placementX" : 376.129, + "placementY" : 332.34, + "placementWidth" : 175.69, + "placementHeight" : 26.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 175.69, + "contentHeight" : 26.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 376.129, + "computedY" : 300.94, + "placementX" : 376.129, + "placementY" : 300.94, + "placementWidth" : 171.01, + "placementHeight" : 26.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 171.01, + "contentHeight" : 26.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 376.129, + "computedY" : 269.54, + "placementX" : 376.129, + "placementY" : 269.54, + "placementWidth" : 177.14, + "placementHeight" : 26.4, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 177.14, + "contentHeight" : 26.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]/ParagraphNode[3]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/MoneyBand[9]/TermsColumn[1]/TermsList[1]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 376.129, + "computedY" : 223.74, + "placementX" : 376.129, + "placementY" : 223.74, + "placementWidth" : 182.676, + "placementHeight" : 40.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 182.676, + "contentHeight" : 40.8, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Acceptance[10]", + "entityName" : "Acceptance", + "entityKind" : "SectionNode", + "parentPath" : "Proposal[0]", + "childIndex" : 10, + "depth" : 2, + "layer" : 2, + "computedX" : 36.0, + "computedY" : 78.54, + "placementX" : 36.0, + "placementY" : 78.54, + "placementWidth" : 523.276, + "placementHeight" : 91.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 523.276, + "contentHeight" : 91.8, + "margin" : { + "top" : 18.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 11.0, + "right" : 11.0, + "bottom" : 11.0, + "left" : 11.0 + } + }, { + "path" : "Proposal[0]/Acceptance[10]/SectionHeading[0]", + "entityName" : "SectionHeading", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Acceptance[10]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 47.0, + "computedY" : 132.34, + "placementX" : 47.0, + "placementY" : 132.34, + "placementWidth" : 501.276, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 501.276, + "contentHeight" : 27.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" : "Proposal[0]/Acceptance[10]/SectionHeading[0]/SectionBadge[0]", + "entityName" : "SectionBadge", + "entityKind" : "ShapeContainerNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SectionHeading[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 47.0, + "computedY" : 132.34, + "placementX" : 47.0, + "placementY" : 132.34, + "placementWidth" : 27.0, + "placementHeight" : 27.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 27.0, + "contentHeight" : 27.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" : "Proposal[0]/Acceptance[10]/SectionHeading[0]/SectionBadge[0]/Icon-badge-acceptance[0]", + "entityName" : "Icon-badge-acceptance", + "entityKind" : "ImageNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SectionHeading[0]/SectionBadge[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 53.48, + "computedY" : 138.82, + "placementX" : 53.48, + "placementY" : 138.82, + "placementWidth" : 14.04, + "placementHeight" : 14.04, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 14.04, + "contentHeight" : 14.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" : "Proposal[0]/Acceptance[10]/SectionHeading[0]/SectionHeadingTitle[1]", + "entityName" : "SectionHeadingTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SectionHeading[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 85.34, + "computedY" : 137.74, + "placementX" : 85.34, + "placementY" : 137.74, + "placementWidth" : 87.966, + "placementHeight" : 16.2, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 87.966, + "contentHeight" : 16.2, + "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" : "Proposal[0]/Acceptance[10]/ParagraphNode[1]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Acceptance[10]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 47.0, + "computedY" : 113.34, + "placementX" : 47.0, + "placementY" : 113.34, + "placementWidth" : 432.25, + "placementHeight" : 12.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 432.25, + "contentHeight" : 12.0, + "margin" : { + "top" : 7.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "entityName" : "SignatureFields", + "entityKind" : "RowNode", + "parentPath" : "Proposal[0]/Acceptance[10]", + "childIndex" : 2, + "depth" : 3, + "layer" : 3, + "computedX" : 47.0, + "computedY" : 89.54, + "placementX" : 47.0, + "placementY" : 89.54, + "placementWidth" : 501.276, + "placementHeight" : 23.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 501.276, + "contentHeight" : 23.8, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 13.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "Proposal[0]/Acceptance[10]/SignatureFields[2]/ParagraphNode[0]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 47.0, + "computedY" : 89.54, + "placementX" : 47.0, + "placementY" : 89.54, + "placementWidth" : 51.759, + "placementHeight" : 10.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 51.759, + "contentHeight" : 10.8, + "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" : "Proposal[0]/Acceptance[10]/SignatureFields[2]/LineNode[1]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 113.177, + "computedY" : 89.54, + "placementX" : 113.177, + "placementY" : 89.54, + "placementWidth" : 96.915, + "placementHeight" : 1.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 96.915, + "contentHeight" : 1.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" : "Proposal[0]/Acceptance[10]/SignatureFields[2]/ParagraphNode[2]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 216.092, + "computedY" : 89.54, + "placementX" : 216.092, + "placementY" : 89.54, + "placementWidth" : 39.753, + "placementHeight" : 10.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 39.753, + "contentHeight" : 10.8, + "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" : "Proposal[0]/Acceptance[10]/SignatureFields[2]/LineNode[3]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 282.269, + "computedY" : 89.54, + "placementX" : 282.269, + "placementY" : 89.54, + "placementWidth" : 96.915, + "placementHeight" : 1.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 96.915, + "contentHeight" : 1.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" : "Proposal[0]/Acceptance[10]/SignatureFields[2]/ParagraphNode[4]", + "entityName" : null, + "entityKind" : "ParagraphNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "childIndex" : 4, + "depth" : 4, + "layer" : 4, + "computedX" : 385.184, + "computedY" : 89.54, + "placementX" : 385.184, + "placementY" : 89.54, + "placementWidth" : 21.555, + "placementHeight" : 10.8, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 21.555, + "contentHeight" : 10.8, + "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" : "Proposal[0]/Acceptance[10]/SignatureFields[2]/LineNode[5]", + "entityName" : null, + "entityKind" : "LineNode", + "parentPath" : "Proposal[0]/Acceptance[10]/SignatureFields[2]", + "childIndex" : 5, + "depth" : 4, + "layer" : 4, + "computedX" : 451.361, + "computedY" : 89.54, + "placementX" : 451.361, + "placementY" : 89.54, + "placementWidth" : 96.915, + "placementHeight" : 1.0, + "startPage" : 1, + "endPage" : 1, + "contentWidth" : 96.915, + "contentHeight" : 1.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 + } + } ] +} diff --git a/qa/src/test/resources/visual-baselines/proposal-v2-layered/northline_proposal-page-0.png b/qa/src/test/resources/visual-baselines/proposal-v2-layered/northline_proposal-page-0.png new file mode 100644 index 000000000..895525f22 Binary files /dev/null and b/qa/src/test/resources/visual-baselines/proposal-v2-layered/northline_proposal-page-0.png differ diff --git a/qa/src/test/resources/visual-baselines/proposal-v2-layered/northline_proposal-page-1.png b/qa/src/test/resources/visual-baselines/proposal-v2-layered/northline_proposal-page-1.png new file mode 100644 index 000000000..bd2540df4 Binary files /dev/null and b/qa/src/test/resources/visual-baselines/proposal-v2-layered/northline_proposal-page-1.png differ diff --git a/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineIcons.java b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineIcons.java new file mode 100644 index 000000000..268f4f05b --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineIcons.java @@ -0,0 +1,55 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.image.DocumentImageData; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Classpath loader for the Northline icon set. + * + *

The icons ship inside the templates artifact under + * {@code templates/proposal/northline/icons/}, keyed by the icon tokens the + * structured proposal data carries (e.g. {@code badge-summary}, + * {@code fact-start}, {@code goal-brand}). Loaded bytes are cached per + * token, so a two-page proposal reads each icon file once per JVM.

+ */ +final class NorthlineIcons { + + private static final String ICON_ROOT = "/templates/proposal/northline/icons/"; + private static final Map CACHE = new ConcurrentHashMap<>(); + + private NorthlineIcons() { + } + + /** + * Loads (and caches) the icon for a data-supplied token. + * + * @param token the icon token, without extension + * @return the icon image data + * @throws IllegalArgumentException when the token names no packaged icon + * — an icon token is data, so a wrong one is a data error, the + * same class the phase-grid header contract throws + */ + static DocumentImageData image(String token) { + return CACHE.computeIfAbsent(token, NorthlineIcons::read); + } + + private static DocumentImageData read(String token) { + String resourcePath = ICON_ROOT + token + ".png"; + try (InputStream input = NorthlineIcons.class.getResourceAsStream(resourcePath)) { + if (input == null) { + throw new IllegalArgumentException( + "Unknown northline proposal icon token '" + token + + "' — no packaged icon at " + resourcePath + "."); + } + return DocumentImageData.fromBytes(input.readAllBytes()); + } catch (IOException e) { + throw new UncheckedIOException( + "Failed to read northline proposal icon: " + resourcePath, e); + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlinePageOne.java b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlinePageOne.java new file mode 100644 index 000000000..0bbfbd603 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlinePageOne.java @@ -0,0 +1,350 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +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.node.TextAlign; +import com.demcha.compose.document.style.ClipPolicy; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.templates.data.proposal.ProposalGlance; +import com.demcha.compose.document.templates.data.proposal.ProposalGoals; +import com.demcha.compose.document.templates.data.proposal.ProposalScope; +import com.demcha.compose.document.templates.data.proposal.ProposalSummaryBlock; +import com.demcha.compose.document.templates.data.proposal.ProposalTitleLines; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalData; + +import java.util.List; + +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ACCENT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BAND_TOP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BODY; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BODY_LEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.CONTENT_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_DIVIDER_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_ICON; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_LABEL; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_LINE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_NOTE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_VALUE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_VALUE_LINE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FACT_VALUE_SIZE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GAP_HEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GAP_SECTION; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GLANCE_WEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GLANCE_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GOAL_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GOAL_ICON; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GOAL_TEXT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GOAL_TEXT_LINES; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.HAIRLINE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.LABEL_SIZE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.LATO_LINE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.META; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.META_SEPARATOR; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PANEL_BACKGROUND; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PANEL_HEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PANEL_PADDING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PANEL_RADIUS; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.RULE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.RULE_QUIET; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_BADGE_H; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_BADGE_W; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_BODY; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_NUMBER; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_ROW_PAD; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_TITLE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SCOPE_TITLE_SPLIT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SUMMARY_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SUMMARY_WEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SUMMARY_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TABLE_SIZE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TITLE_ACCENT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TITLE_INK; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TITLE_LAST_LINE_HEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TITLE_PITCH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TITLE_RULE_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineWidgets.sectionHeading; + +/** + * Page one of the Northline proposal: the title block, the summary band + * with the glance card, the goal cells, and the numbered scope list. + * + *

Layout notes carried over from the ported template: the title lines + * stack as anchored children of one container because the reference sets + * the title at a pitch tighter than the font's own line height, which text + * leading (additive, non-negative) cannot reach; the goal cells flatten + * every icon/text pair into one row because a row is refused inside a row + * cell; the scope list is rows plus hairlines rather than a table because + * the reference has horizontal separators and no vertical rules.

+ */ +final class NorthlinePageOne { + + private NorthlinePageOne() { + } + + static void compose(PageFlowBuilder page, StructuredProposalData data) { + renderTitleBlock(page, data.title()); + renderTitleMeta(page, data); + renderSummaryBand(page, data); + renderGoalCells(page, data.goals()); + renderScopeOfWork(page, data.scope()); + } + + /** Accent rule, then the three title lines — ink lead, accent remainder. */ + private static void renderTitleBlock(PageFlowBuilder page, ProposalTitleLines title) { + page.addSection("TitleBlock", block -> { + block.margin(BAND_TOP, 0f, 0f, 0f); + block.spacing(0); + block.addLine(line -> line + .horizontal(TITLE_RULE_WIDTH) + .thickness(2.6) + .color(ACCENT) + .margin(DocumentInsets.bottom(2))); + block.add(renderTitleLines(title)); + }); + } + + /** + * The three title lines, stacked on the reference's own pitch: each + * line is a child of one container placed {@code TITLE_PITCH} apart — + * this stacks what the data already declares as separate lines. + */ + private static DocumentNode renderTitleLines(ProposalTitleLines title) { + return new ShapeContainerBuilder() + .name("TitleLines") + .rectangle(CONTENT_WIDTH, 2 * TITLE_PITCH + TITLE_LAST_LINE_HEIGHT) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE) + .position(titleLine("TitleLead", title.lead(), TITLE_INK), + 0, 0, LayerAlign.TOP_LEFT) + .position(titleLine("TitleLine1", title.second(), TITLE_ACCENT), + 0, TITLE_PITCH, LayerAlign.TOP_LEFT) + .position(titleLine("TitleLine2", title.third(), TITLE_ACCENT), + 0, 2 * TITLE_PITCH, LayerAlign.TOP_LEFT) + .build(); + } + + private static DocumentNode titleLine(String name, String text, DocumentTextStyle style) { + return new ParagraphBuilder() + .name(name).text(text).textStyle(style).build(); + } + + /** Prepared-for, prepared-by and date, joined by a quieter separator. */ + private static void renderTitleMeta(PageFlowBuilder page, StructuredProposalData data) { + page.addSection("TitleMeta", section -> { + section.margin(BAND_TOP, 0f, 0f, 0f); + section.addParagraph(p -> p + .name("MetaLine") + .inlineText(data.meta().preparedFor(), META) + .inlineText(" | ", META_SEPARATOR) + .inlineText(data.meta().preparedBy(), META) + .inlineText(" | ", META_SEPARATOR) + .inlineText(data.meta().date(), META)); + }); + } + + /** The two-column band: summary column left, glance card right. */ + private static void renderSummaryBand(PageFlowBuilder page, StructuredProposalData data) { + page.addRow("SummaryBand", row -> { + row.margin(DocumentInsets.top(BAND_TOP)); + row.verticalAlign(RowVerticalAlign.TOP) + .gap(SUMMARY_GAP) + .weights(SUMMARY_WEIGHT, GLANCE_WEIGHT); + row.addSection("SummaryColumn", column -> { + renderExecutiveSummary(column, data.executiveSummary()); + // The goals heading sits in this column, level with the + // bottom of the glance card — it belongs to the band. + column.add(sectionHeading( + data.goals().heading(), data.goals().icon(), SUMMARY_WIDTH)); + }); + row.addSection("GlanceColumn", column -> renderGlancePanel(column, data.glance())); + }); + } + + /** Heading plus the summary paragraphs. */ + private static void renderExecutiveSummary(SectionBuilder column, + ProposalSummaryBlock summary) { + column.spacing(0); + column.add(sectionHeading(summary.heading(), summary.icon(), SUMMARY_WIDTH)); + List paragraphs = summary.paragraphs(); + for (int i = 0; i < paragraphs.size(); i++) { + String text = paragraphs.get(i); + float top = i == 0 ? (float) GAP_HEADING : (float) GAP_SECTION * 0.6f; + column.addParagraph(p -> p + .text(text) + .textStyle(BODY) + .lineSpacing(BODY_LEADING) + .margin(top, 0f, 0f, 0f)); + } + // Clears the last paragraph before the goals heading. + column.addSpacer(spacer -> spacer.height(GAP_SECTION)); + } + + /** + * The quiet card of project facts. {@code softPanel} colours the + * section that already owns the facts, so the card is their parent + * rather than a rectangle drawn behind them. + */ + private static void renderGlancePanel(SectionBuilder column, ProposalGlance glance) { + double inner = GLANCE_WIDTH - 2 * PANEL_PADDING; + column.addSection("GlancePanel", panel -> { + panel.spacing(0).softPanel(PANEL_BACKGROUND, PANEL_RADIUS, PANEL_PADDING); + panel.addParagraph(p -> p + .text(glance.heading()) + .textStyle(PANEL_HEADING) + .margin(0f, 0f, (float) GAP_HEADING, 0f)); + List facts = glance.facts(); + for (int i = 0; i < facts.size(); i++) { + panel.add(renderGlanceFact(facts.get(i), inner)); + if (i < facts.size() - 1) { + panel.addLine(line -> line + .fill() + .thickness(HAIRLINE) + .color(RULE_QUIET) + .margin(DocumentInsets.symmetric(FACT_DIVIDER_GAP, 0))); + } + } + }); + } + + /** + * One fact: the icon anchored at the left of a label-over-value stack. + * The container's height derives from the number of text lines the fact + * actually has, so the optional note line makes its own room. + */ + private static DocumentNode renderGlanceFact(ProposalGlance.Fact fact, double width) { + boolean hasNote = !fact.note().isBlank(); + double height = LABEL_SIZE * FACT_LINE + FACT_VALUE_SIZE * FACT_VALUE_LINE + + (hasNote ? (LABEL_SIZE + 0.5) * FACT_LINE : 0); + + SectionBuilder text = new SectionBuilder(); + text.name("FactText"); + text.spacing(0); + text.addParagraph(p -> p.text(fact.label()).textStyle(FACT_LABEL)); + text.addParagraph(p -> p.text(fact.value()).textStyle(FACT_VALUE)); + if (hasNote) { + text.addParagraph(p -> p.text(fact.note()).textStyle(FACT_NOTE)); + } + + ShapeContainerBuilder container = new ShapeContainerBuilder() + .name("GlanceFact") + .rectangle(width, height) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE); + if (!fact.icon().isBlank()) { + container.position(NorthlineWidgets.icon(fact.icon(), FACT_ICON), + 0, 0, LayerAlign.CENTER_LEFT); + } + return container + .position(text.build(), FACT_ICON + FACT_GAP, 0, LayerAlign.CENTER_LEFT) + .build(); + } + + /** + * The goal cells, flattened deliberately: the icon/text pairs and the + * separators are all direct children of one row, and the column widths + * carry the structure. Skipped entirely when the data has no goals — + * the flattened column spec cannot describe zero cells. + */ + private static void renderGoalCells(PageFlowBuilder page, ProposalGoals goals) { + List items = goals.items(); + if (items.isEmpty()) { + return; + } + // Three goal-text lines set the separator height; the pitch is the + // font line plus the additive leading. + double separatorHeight = GOAL_TEXT_LINES * (TABLE_SIZE * LATO_LINE + BODY_LEADING); + page.addRow("GoalCells", row -> { + row.margin(DocumentInsets.top(BAND_TOP)); + row.verticalAlign(RowVerticalAlign.TOP).gap(GOAL_GAP); + row.columns(goalColumns(items.size())); + for (int i = 0; i < items.size(); i++) { + if (i > 0) { + row.addLine(line -> line + .vertical(separatorHeight) + .thickness(HAIRLINE) + .color(RULE)); + } + ProposalGoals.Goal goal = items.get(i); + if (goal.icon().isBlank()) { + // Keep the child-per-column mapping of the flattened row. + row.addSpacer(GOAL_ICON); + } else { + row.addImage(image -> NorthlineWidgets.configureIcon( + image, goal.icon(), GOAL_ICON)); + } + row.addParagraph(p -> p + .text(goal.text()) + .textStyle(GOAL_TEXT) + .lineSpacing(BODY_LEADING)); + } + }); + } + + /** Column spec for the flattened goal row. */ + private static DocumentRowColumn[] goalColumns(int goalCount) { + DocumentRowColumn[] columns = new DocumentRowColumn[goalCount * 2 + (goalCount - 1)]; + int at = 0; + for (int i = 0; i < goalCount; i++) { + if (i > 0) { + columns[at++] = DocumentRowColumn.fixed(HAIRLINE); + } + columns[at++] = DocumentRowColumn.fixed(GOAL_ICON); + columns[at++] = DocumentRowColumn.weight(1); + } + return columns; + } + + /** The numbered scope list: rows plus hairlines at the flow's top level. */ + private static void renderScopeOfWork(PageFlowBuilder page, ProposalScope scope) { + double titleWidth = CONTENT_WIDTH * SCOPE_TITLE_SPLIT - SCOPE_BADGE_W - 2 * SCOPE_GAP; + page.addSection("ScopeOfWork", section -> { + section.margin(BAND_TOP, 0f, 0f, 0f); + section.spacing(0); + section.add(sectionHeading(scope.heading(), scope.icon(), CONTENT_WIDTH)); + section.addLine(line -> line + .fill() + .thickness(HAIRLINE) + .color(RULE) + .margin(DocumentInsets.top(GAP_HEADING))); + List items = scope.items(); + for (int i = 0; i < items.size(); i++) { + ProposalScope.Item item = items.get(i); + section.addRow("ScopeRow" + item.number(), row -> { + row.verticalAlign(RowVerticalAlign.TOP) + .gap(SCOPE_GAP) + .padding(DocumentInsets.symmetric(SCOPE_ROW_PAD, 0)) + .columns(DocumentRowColumn.fixed(SCOPE_BADGE_W), + DocumentRowColumn.fixed(titleWidth), + DocumentRowColumn.weight(1)); + row.add(renderScopeBadge(item.number())); + row.addParagraph(p -> p.text(item.title()).textStyle(SCOPE_TITLE)); + row.addParagraph(p -> p + .text(item.description()) + .textStyle(SCOPE_BODY) + .lineSpacing(BODY_LEADING)); + }); + if (i < items.size() - 1) { + section.addLine(line -> line.fill().thickness(HAIRLINE).color(RULE)); + } + } + }); + } + + /** The teal step badge and the number it owns. */ + private static DocumentNode renderScopeBadge(String number) { + return new ShapeContainerBuilder() + .name("ScopeBadge") + .roundedRect(SCOPE_BADGE_W, SCOPE_BADGE_H, 2.0) + .fillColor(ACCENT) + .center(NorthlineWidgets.paragraph("ScopeNumber", number, SCOPE_NUMBER, + TextAlign.CENTER)) + .build(); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlinePageTwo.java b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlinePageTwo.java new file mode 100644 index 000000000..17db60deb --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlinePageTwo.java @@ -0,0 +1,333 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.dsl.PageFlowBuilder; +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.RowVerticalAlign; +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.DocumentTextStyle; +import com.demcha.compose.document.table.DocumentTableCell; +import com.demcha.compose.document.table.DocumentTableColumn; +import com.demcha.compose.document.table.DocumentTableStyle; +import com.demcha.compose.document.table.DocumentTableTextAnchor; +import com.demcha.compose.document.templates.data.proposal.ProposalAcceptance; +import com.demcha.compose.document.templates.data.proposal.ProposalDeliverables; +import com.demcha.compose.document.templates.data.proposal.ProposalInvestment; +import com.demcha.compose.document.templates.data.proposal.ProposalPhaseGrid; +import com.demcha.compose.document.templates.data.proposal.ProposalTermsBlock; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalData; + +import java.util.List; + +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ACCEPTANCE_FIELD_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ACCEPTANCE_PADDING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ACCENT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BAND_TOP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BODY; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BODY_LEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.CONTENT_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.DELIVERABLES_LEFT_WEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.DELIVERABLES_RIGHT_WEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GAP_HEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.HAIRLINE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.INK; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.INVESTMENT_ROW_PAD; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.INVESTMENT_WEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.INVESTMENT_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.MONEY_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PANEL_BACKGROUND; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PANEL_RADIUS; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.RULE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.RULE_QUIET; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SIGNATURE_LABEL; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SIGNATURE_LABEL_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TABLE_BODY; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TABLE_BODY_BOLD; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TABLE_HEADER; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TERMS_WEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TERMS_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TIMELINE_WEIGHTS; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.TOTAL_TEXT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineWidgets.renderBullet; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineWidgets.sectionHeading; + +/** + * Page two of the Northline proposal: the deliverable columns, the phase + * grid, the investment / terms band, and the signing card. + * + *

Layout notes carried over from the ported template: the phase grid is + * the one region with real vertical rules and therefore the one real + * table; the investment block sits inside a row cell where a nested row is + * refused, so it is a table too — with the recorded cost that its cell + * strokes also draw a rule down the ITEM/AMOUNT boundary. Prose cells in + * the phase grid are composed paragraphs (a plain-text cell reports its + * whole string as the column's natural width and a narrower fixed column is + * rejected rather than wrapped), while the phase cell stays a plain-text + * cell — {@code DocumentTableTextAnchor} centres a text cell's value but + * not a composed one's.

+ */ +final class NorthlinePageTwo { + + private NorthlinePageTwo() { + } + + static void compose(PageFlowBuilder page, StructuredProposalData data) { + renderDeliverables(page, data.deliverables()); + renderTimelineTable(page, data.timeline()); + renderMoneyBand(page, data); + renderAcceptance(page, data.acceptance()); + } + + /** Heading plus two bullet columns. */ + private static void renderDeliverables(PageFlowBuilder page, + ProposalDeliverables deliverables) { + page.addSection("Deliverables", section -> { + section.margin(BAND_TOP, 0f, 0f, 0f); + section.spacing(0); + section.add(sectionHeading( + deliverables.heading(), deliverables.icon(), CONTENT_WIDTH)); + section.addRow("DeliverableColumns", row -> { + row.verticalAlign(RowVerticalAlign.TOP) + .gap(0) + .padding(DocumentInsets.top(GAP_HEADING)) + .weights(DELIVERABLES_LEFT_WEIGHT, DELIVERABLES_RIGHT_WEIGHT); + row.addSection("DeliverablesLeft", column -> { + column.spacing(0); + deliverables.leftColumn().forEach(item -> renderBullet(column, item)); + }); + row.addSection("DeliverablesRight", column -> { + column.spacing(0); + deliverables.rightColumn().forEach(item -> renderBullet(column, item)); + }); + }); + }); + } + + /** + * The phase grid. Requires one authored header per column: the grid + * draws four columns, so four headers — anything else is a data error + * reported here rather than an index error deep in the table. Skipped + * entirely when the data has no phases, including that header-count + * validation — a grid that never renders constrains nothing. + */ + private static void renderTimelineTable(PageFlowBuilder page, ProposalPhaseGrid timeline) { + if (timeline.phases().isEmpty()) { + return; + } + List headers = timeline.columnHeaders(); + if (headers.size() != TIMELINE_WEIGHTS.length) { + throw new IllegalArgumentException( + "The phase grid renders " + TIMELINE_WEIGHTS.length + + " columns and needs exactly one header per column; " + + "columnHeaders carries " + headers.size() + "."); + } + DocumentTableStyle headerStyle = DocumentTableStyle.builder() + .fillColor(INK) + .stroke(DocumentStroke.of(INK, HAIRLINE)) + .padding(DocumentInsets.symmetric(7, 10)) + .textStyle(TABLE_HEADER) + .textAnchor(DocumentTableTextAnchor.CENTER_LEFT) + .build(); + DocumentTableStyle cellStyle = DocumentTableStyle.builder() + .stroke(DocumentStroke.of(RULE, HAIRLINE)) + .padding(DocumentInsets.symmetric(9, 10)) + .textStyle(TABLE_BODY) + .textAnchor(DocumentTableTextAnchor.TOP_LEFT) + .lineSpacing(BODY_LEADING) + .build(); + DocumentTableStyle centredCellStyle = DocumentTableStyle.builder() + .stroke(DocumentStroke.of(RULE, HAIRLINE)) + .padding(DocumentInsets.symmetric(9, 10)) + .textStyle(TABLE_BODY) + .textAnchor(DocumentTableTextAnchor.CENTER_LEFT) + .lineSpacing(BODY_LEADING) + .build(); + + page.addSection("Timeline", section -> { + section.margin(BAND_TOP, 0f, 0f, 0f); + section.spacing(0); + section.add(sectionHeading(timeline.heading(), timeline.icon(), CONTENT_WIDTH)); + section.addTable(table -> { + table.name("TimelineTable") + .width(CONTENT_WIDTH) + .margin(DocumentInsets.top(GAP_HEADING)) + .columns(DocumentTableColumn.fixed(CONTENT_WIDTH * TIMELINE_WEIGHTS[0]), + DocumentTableColumn.fixed(CONTENT_WIDTH * TIMELINE_WEIGHTS[1]), + DocumentTableColumn.fixed(CONTENT_WIDTH * TIMELINE_WEIGHTS[2]), + DocumentTableColumn.fixed(CONTENT_WIDTH * TIMELINE_WEIGHTS[3])) + .defaultCellStyle(cellStyle); + table.headerCells( + DocumentTableCell.text(headers.get(0)).withStyle(headerStyle), + DocumentTableCell.text(headers.get(1)).withStyle(headerStyle), + DocumentTableCell.text(headers.get(2)).withStyle(headerStyle), + DocumentTableCell.text(headers.get(3)).withStyle(headerStyle)); + for (ProposalPhaseGrid.Phase phase : timeline.phases()) { + table.rowCells( + DocumentTableCell.text(phase.number() + " " + phase.name()) + .withStyle(centredCellStyle), + DocumentTableCell.node(wrappingCell(phase.focus())) + .withStyle(cellStyle), + DocumentTableCell.text(phase.duration()) + .withStyle(centredCellStyle), + DocumentTableCell.node(wrappingCell(phase.output())) + .withStyle(cellStyle)); + } + table.repeatHeader(); + }); + }); + } + + /** A table cell that has to wrap: a composed paragraph wraps to its column. */ + private static DocumentNode wrappingCell(String text) { + return new ParagraphBuilder() + .name("Cell") + .text(text) + .textStyle(TABLE_BODY) + .lineSpacing(BODY_LEADING) + .build(); + } + + /** The two-column money band: the priced table left, terms right. */ + private static void renderMoneyBand(PageFlowBuilder page, StructuredProposalData data) { + page.addRow("MoneyBand", row -> { + row.margin(DocumentInsets.top(BAND_TOP)); + row.verticalAlign(RowVerticalAlign.TOP) + .gap(MONEY_GAP) + .weights(INVESTMENT_WEIGHT, TERMS_WEIGHT); + row.addSection("InvestmentColumn", + column -> renderInvestmentTable(column, data.investment())); + row.addSection("TermsColumn", column -> renderTerms(column, data.terms())); + }); + } + + /** The priced block, with the last band always the labelled total. */ + private static void renderInvestmentTable(SectionBuilder column, + ProposalInvestment investment) { + double labelWidth = INVESTMENT_WIDTH * 0.62; + double amountWidth = INVESTMENT_WIDTH - labelWidth; + + DocumentTableStyle headerLabel = investmentStyle(INK, TABLE_HEADER, + DocumentTableTextAnchor.CENTER_LEFT); + DocumentTableStyle headerAmount = investmentStyle(INK, TABLE_HEADER, + DocumentTableTextAnchor.CENTER_RIGHT); + DocumentTableStyle rowLabel = investmentStyle(null, TABLE_BODY, + DocumentTableTextAnchor.CENTER_LEFT); + DocumentTableStyle rowAmount = investmentStyle(null, TABLE_BODY, + DocumentTableTextAnchor.CENTER_RIGHT); + DocumentTableStyle subtotalLabel = investmentStyle(PANEL_BACKGROUND, TABLE_BODY_BOLD, + DocumentTableTextAnchor.CENTER_LEFT); + DocumentTableStyle subtotalAmount = investmentStyle(PANEL_BACKGROUND, TABLE_BODY_BOLD, + DocumentTableTextAnchor.CENTER_RIGHT); + DocumentTableStyle optionalAmount = investmentStyle(null, TABLE_BODY_BOLD, + DocumentTableTextAnchor.CENTER_RIGHT); + DocumentTableStyle totalLabel = investmentStyle(ACCENT, TOTAL_TEXT, + DocumentTableTextAnchor.CENTER_LEFT); + DocumentTableStyle totalAmount = investmentStyle(ACCENT, TOTAL_TEXT, + DocumentTableTextAnchor.CENTER_RIGHT); + + column.spacing(0); + column.add(sectionHeading( + investment.heading(), investment.icon(), INVESTMENT_WIDTH)); + column.addTable(table -> { + table.name("InvestmentTable") + .width(INVESTMENT_WIDTH) + .margin(DocumentInsets.top(GAP_HEADING)) + .columns(DocumentTableColumn.fixed(labelWidth), + DocumentTableColumn.fixed(amountWidth)) + .defaultCellStyle(rowLabel); + table.headerCells( + DocumentTableCell.text(investment.itemHeader()).withStyle(headerLabel), + DocumentTableCell.text(investment.amountHeader()).withStyle(headerAmount)); + for (ProposalInvestment.Row line : investment.rows()) { + switch (line.role()) { + case SUBTOTAL -> table.rowCells( + DocumentTableCell.text(line.label()).withStyle(subtotalLabel), + DocumentTableCell.text(line.amount()).withStyle(subtotalAmount)); + case OPTIONAL -> table.rowCells( + DocumentTableCell.text(line.label()).withStyle(rowLabel), + DocumentTableCell.text(line.amount()).withStyle(optionalAmount)); + default -> table.rowCells( + DocumentTableCell.text(line.label()).withStyle(rowLabel), + DocumentTableCell.text(line.amount()).withStyle(rowAmount)); + } + } + table.rowCells( + DocumentTableCell.text(investment.totalLabel()).withStyle(totalLabel), + DocumentTableCell.text(investment.totalAmount()).withStyle(totalAmount)); + }); + } + + /** One investment cell style; {@code fill} may be null for the plain rows. */ + private static DocumentTableStyle investmentStyle(DocumentColor fill, + DocumentTextStyle text, + DocumentTableTextAnchor anchor) { + DocumentTableStyle.Builder builder = DocumentTableStyle.builder() + .stroke(DocumentStroke.of(RULE_QUIET, HAIRLINE)) + .padding(DocumentInsets.symmetric(INVESTMENT_ROW_PAD, 10)) + .textStyle(text) + .textAnchor(anchor); + if (fill != null) { + builder.fillColor(fill); + } + return builder.build(); + } + + /** Heading plus the bulleted terms, reusing the deliverables bullet. */ + private static void renderTerms(SectionBuilder column, ProposalTermsBlock terms) { + column.spacing(0); + column.add(sectionHeading(terms.heading(), terms.icon(), TERMS_WIDTH)); + column.addSection("TermsList", list -> { + list.spacing(0).padding((float) GAP_HEADING, 0f, 0f, 0f); + terms.items().forEach(item -> renderBullet(list, item)); + }); + } + + /** + * The signing card. The signature fields are flat row columns — label, + * rule per field — with the column spec derived from the field count, + * so no pair nests inside a cell; the row is skipped when the data has + * no fields. + */ + private static void renderAcceptance(PageFlowBuilder page, ProposalAcceptance acceptance) { + double inner = CONTENT_WIDTH - 2 * ACCEPTANCE_PADDING; + page.addSection("Acceptance", card -> { + card.margin(BAND_TOP, 0f, 0f, 0f); + card.spacing(0).softPanel(PANEL_BACKGROUND, PANEL_RADIUS, ACCEPTANCE_PADDING, + DocumentStroke.of(RULE, HAIRLINE)); + card.add(sectionHeading(acceptance.heading(), acceptance.icon(), inner)); + card.addParagraph(p -> p + .text(acceptance.statement()) + .textStyle(BODY) + .lineSpacing(BODY_LEADING) + .margin((float) GAP_HEADING, 0f, 0f, 0f)); + List fields = acceptance.fields(); + if (fields.isEmpty()) { + return; + } + card.addRow("SignatureFields", row -> { + row.verticalAlign(RowVerticalAlign.BOTTOM) + .gap(6) + .padding(DocumentInsets.top(ACCEPTANCE_FIELD_GAP)) + .columns(signatureColumns(fields.size())); + for (String field : fields) { + row.addParagraph(p -> p.text(field).textStyle(SIGNATURE_LABEL)); + row.addLine(line -> line.fill().thickness(HAIRLINE).color(INK)); + } + }); + }); + } + + /** Label-strip + growing-rule column pair per signature field. */ + private static DocumentRowColumn[] signatureColumns(int fieldCount) { + DocumentRowColumn[] columns = new DocumentRowColumn[fieldCount * 2]; + for (int i = 0; i < fieldCount; i++) { + columns[i * 2] = DocumentRowColumn.fixed(SIGNATURE_LABEL_WIDTH); + columns[i * 2 + 1] = DocumentRowColumn.weight(1); + } + return columns; + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposal.java b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposal.java new file mode 100644 index 000000000..6936a0dab --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineProposal.java @@ -0,0 +1,164 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.api.PageBackgroundFill; +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.proposal.ProposalBrand; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalData; +import com.demcha.compose.document.templates.data.proposal.StructuredProposalDocumentSpec; + +import java.util.List; +import java.util.Objects; + +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ACCENT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FOOTER_HEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FOOTER_TEXT_SIZE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.FOOTER_ZONE_HEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.INK; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.MARGIN_SIDE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.MARGIN_TOP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ON_DARK; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PAGE_BACKGROUND; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PAGE_BLOCK_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PAGE_HEIGHT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.PAGE_WIDTH; + +/** + * Northline Proposal — the first structured proposal preset: a two-page + * teal-and-navy business proposal on the Spectral/Lato pair. + * + *

Page one carries the brand header, the three stacked title lines, the + * meta line, the executive summary beside the at-a-glance card, the goal + * cells, and the numbered scope list; page two repeats the header and + * carries the deliverable columns, the phase grid, the investment / terms + * band, and the signing card. Like the ported template, the preset issues + * no explicit page break: page one's bands are sized so reference-volume + * content ends just short of the content box and the page-two header opens + * the second page (a break issued after that natural advance would leave a + * blank page between them). The two-page distribution is therefore a + * property of the content volume — noticeably lighter content lets the + * page-two bands climb onto page one. A section whose data is empty keeps + * its badge disc and heading; only structures that cannot be composed + * empty — the goal cells, the phase grid, the signature row — are + * skipped.

+ * + *

The page chrome is not flow content: the navy footer band and the + * teal page-number block are {@link PageBackgroundFill}s (their geometry + * comes from the page and they repeat on every page), the brand line and + * the {@code 0{page}} number are a {@link DocumentHeaderFooter} in the + * FOOTER zone, and the bottom page margin equals the band height so + * content stops above the chrome. The preset therefore owns its session + * geometry — page size, margins, page background — and a session margin + * set by the caller would be overwritten; see {@link #RECOMMENDED_MARGIN}.

+ * + *

The preset consumes the structured proposal model + * ({@link StructuredProposalDocumentSpec}); the icon tokens the data + * carries resolve against the packaged Northline icon set. The colours and + * the measured geometry are preset-local ({@link NorthlineStyles}) — the + * look is not shared with any other family today.

+ */ +public final class NorthlineProposal { + + /** + * Stable template identifier. + */ + public static final String ID = "proposal-northline"; + + /** + * Human-readable display name. + */ + public static final String DISPLAY_NAME = "Northline Proposal"; + + /** + * Recommended session margin (in points). The preset sets its own page + * geometry — side margins, the top margin, and a bottom margin equal to + * the footer band — inside {@code compose}, so callers leave the + * session margin at zero and let the preset own the page frame. + */ + public static final double RECOMMENDED_MARGIN = 0.0; + + private NorthlineProposal() { + } + + /** + * Builds the preset. + * + * @return ready-to-use template + */ + 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, StructuredProposalDocumentSpec spec) { + Objects.requireNonNull(document, "document"); + StructuredProposalData data = Objects.requireNonNull(spec, "spec").proposal(); + + document.pageSize(PAGE_WIDTH, PAGE_HEIGHT) + // The bottom margin is the footer band: content stops + // above it, so nothing runs underneath the chrome. + .margin(new DocumentInsets(MARGIN_TOP, MARGIN_SIDE, + FOOTER_HEIGHT, MARGIN_SIDE)) + .pageBackground(PAGE_BACKGROUND); + + renderChrome(document, data.brand()); + + document.pageFlow(page -> { + // spacing(0): flow spacing survives a page break and would + // indent the page-two header; the rhythm lives on the bands. + page.name("Proposal").spacing(0); + page.addSection("Header", + header -> NorthlineWidgets.renderHeader(header, data.brand())); + NorthlinePageOne.compose(page, data); + page.addSection("Header", + header -> NorthlineWidgets.renderHeader(header, data.brand())); + NorthlinePageTwo.compose(page, data); + }); + } + + /** + * The band that closes every page: the navy band and the teal + * corner block as page-ratio background fills, and the brand line + + * page number as FOOTER-zone chrome with {@code {page}} resolved + * per page ({@code 0{page}} because the reference numbers its pages + * 01 and 02 and there is no zero-padded page-number style). + */ + private static void renderChrome(DocumentSession document, ProposalBrand brand) { + document.pageBackgrounds(List.of( + PageBackgroundFill.bottomBand(FOOTER_HEIGHT / PAGE_HEIGHT, INK), + new PageBackgroundFill( + 1.0 - PAGE_BLOCK_WIDTH / PAGE_WIDTH, + 1.0 - FOOTER_HEIGHT / PAGE_HEIGHT, + PAGE_BLOCK_WIDTH / PAGE_WIDTH, + FOOTER_HEIGHT / PAGE_HEIGHT, + ACCENT))); + + document.footer(DocumentHeaderFooter.builder() + .zone(DocumentHeaderFooterZone.FOOTER) + // The backend places the baseline at (height - fontSize) + // above the page edge; this height centres the cap band + // inside the footer band. + .height((float) FOOTER_ZONE_HEIGHT) + .leftText(brand.footerName() + " | " + brand.website()) + .rightText("0{page}") + .fontSize((float) FOOTER_TEXT_SIZE) + .textColor(ON_DARK) + .build()); + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineStyles.java b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineStyles.java new file mode 100644 index 000000000..964b6ebbf --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineStyles.java @@ -0,0 +1,204 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.font.FontName; + +/** + * The Northline look: colour roles, the Spectral/Lato type scale, and the + * measured geometry constants shared by the preset's bands. + * + *

Preset-local rather than a {@code BrandTheme}: the teal-and-navy + * proposal look is not shared with any other family today, and every value + * below was measured against the ported template's reference render — a + * theme slot would add indirection without a second consumer. The Northline + * orange variant parameterises the colours when it lands.

+ * + *

Geometry notes carried over from the ported template: every width is a + * fraction of {@link #CONTENT_WIDTH}; the only hardcoded points are + * dimensions nothing else depends on (icon sizes, badge diameter, logo + * square, footer band height, card padding), each named so a revision + * changes one constant. {@link #BODY_LEADING} is EXTRA leading in points, + * not a multiplier — the engine adds it to the font's own line height.

+ */ +final class NorthlineStyles { + + private NorthlineStyles() { + } + + // --- page --------------------------------------------------------------- + static final double PAGE_WIDTH = 595.276; // A4 portrait, points + static final double PAGE_HEIGHT = 841.890; + static final double MARGIN_SIDE = 36.0; + static final double MARGIN_TOP = 8.0; + /** The denominator of every derived width below. */ + static final double CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN_SIDE; + + // --- band splits, measured from the reference --------------------------- + static final double SUMMARY_WEIGHT = 0.62; + static final double GLANCE_WEIGHT = 0.35; + static final double SUMMARY_GAP = CONTENT_WIDTH * 0.03; + static final double INVESTMENT_WEIGHT = 0.59; + static final double TERMS_WEIGHT = 0.35; + static final double MONEY_GAP = CONTENT_WIDTH * 0.06; + static final double DELIVERABLES_LEFT_WEIGHT = 0.48; + static final double DELIVERABLES_RIGHT_WEIGHT = 0.52; + /** PHASE / FOCUS / DURATION / OUTPUT, from the four measured vertical rules. */ + static final double[] TIMELINE_WEIGHTS = {0.232, 0.298, 0.179, 0.291}; + /** Where the scope description column starts. */ + static final double SCOPE_TITLE_SPLIT = 0.448; + + static final double SUMMARY_WIDTH = CONTENT_WIDTH * SUMMARY_WEIGHT; + static final double GLANCE_WIDTH = CONTENT_WIDTH * GLANCE_WEIGHT; + static final double INVESTMENT_WIDTH = CONTENT_WIDTH * INVESTMENT_WEIGHT; + static final double TERMS_WIDTH = CONTENT_WIDTH * TERMS_WEIGHT; + + // --- independent dimensions --------------------------------------------- + static final double BADGE_DIAMETER = 27.0; + static final double BADGE_GLYPH = BADGE_DIAMETER * 0.52; + static final double BADGE_GAP = BADGE_DIAMETER * 0.42; + static final double GOAL_ICON = 20.0; + static final double GOAL_GAP = 7.0; + /** Lines a goal statement wraps to; sets the separator height. */ + static final double GOAL_TEXT_LINES = 3; + static final double FACT_ICON = 25.0; + static final double FACT_GAP = 12.0; + static final double LOGO_SIZE = 50.0; + static final double FOOTER_HEIGHT = 48.0; + static final double PANEL_PADDING = 16.0; + static final double PANEL_RADIUS = 7.0; + static final double SCOPE_BADGE_W = 26.0; + static final double SCOPE_BADGE_H = 15.0; + static final double SCOPE_GAP = 12.0; + static final double TITLE_RULE_WIDTH = CONTENT_WIDTH * 0.086; + static final double PAGE_BLOCK_WIDTH = 62.0; + static final double HAIRLINE = 0.7; + /** Helvetica cap height as a fraction of the type size. */ + static final double HELVETICA_CAP = 0.717; + /** Footer chrome text size (the backend draws footer chrome in Standard-14). */ + static final double FOOTER_TEXT_SIZE = 9.5; + /** + * Not the band height: the backend places the footer baseline at + * (height - fontSize) above the page edge, so this value seats the cap + * band in the middle of a {@link #FOOTER_HEIGHT} band. + */ + static final double FOOTER_ZONE_HEIGHT = + (FOOTER_HEIGHT - FOOTER_TEXT_SIZE * HELVETICA_CAP) / 2 + FOOTER_TEXT_SIZE; + static final double WORDMARK_WIDTH = CONTENT_WIDTH * 0.19; + static final double DOC_LABEL_WIDTH = CONTENT_WIDTH * 0.16; + static final double SIGNATURE_LABEL_WIDTH = CONTENT_WIDTH * 0.115; + + // --- vertical rhythm ----------------------------------------------------- + static final double GAP_SECTION = 18.0; + /** + * The gap above a band, carried by the band rather than by the page + * flow: flow spacing survives a page break and would indent the + * page-two header, so the flow spacing stays zero and the rhythm lives + * on the bands — a header carries no leading space on any page. + */ + static final float BAND_TOP = (float) GAP_SECTION; + static final double GAP_HEADING = 7.0; + static final double GAP_ROW = 5.0; + /** Inner padding of the acceptance card; the glance card keeps PANEL_PADDING. */ + static final double ACCEPTANCE_PADDING = 11.0; + /** Gap between the acceptance statement and the signature fields. */ + static final double ACCEPTANCE_FIELD_GAP = 13.0; + /** Vertical padding inside a scope row, each side (measured). */ + static final double SCOPE_ROW_PAD = 5.0; + /** Vertical padding in an investment cell, each side (measured). */ + static final double INVESTMENT_ROW_PAD = 4.0; + /** Gap above and below a divider between glance facts. */ + static final double FACT_DIVIDER_GAP = 7.0; + /** Glance label / value line heights, as multiples of the type size. */ + static final double FACT_LINE = 1.4; + static final double FACT_VALUE_LINE = 1.3; + + // --- type scale ---------------------------------------------------------- + static final double BODY_SIZE = 10.0; + /** EXTRA leading in points (added to the font's own line height). */ + static final double BODY_LEADING = 1.45; + /** Lato line height as a multiple of the type size (measured). */ + static final double LATO_LINE = 1.213; + static final double TITLE_SIZE = 46.0; + /** The reference's title pitch between stacked title lines. */ + static final double TITLE_PITCH = 48.0; + /** Height reserved for the last title line inside the stacked-title container. */ + static final double TITLE_LAST_LINE_HEIGHT = TITLE_SIZE * 0.95; + static final double HEADING_SIZE = 13.5; + static final double TABLE_SIZE = 9.0; + static final double LABEL_SIZE = 8.0; + static final double FACT_VALUE_SIZE = 14.0; + + // --- theme tokens: role names, not colour names --------------------------- + static final DocumentColor INK = DocumentColor.rgb(1, 25, 52); + static final DocumentColor ACCENT = DocumentColor.rgb(20, 111, 120); + static final DocumentColor PAGE_BACKGROUND = DocumentColor.rgb(252, 252, 252); + static final DocumentColor PANEL_BACKGROUND = DocumentColor.rgb(245, 246, 247); + static final DocumentColor RULE = DocumentColor.rgb(203, 208, 213); + static final DocumentColor RULE_QUIET = DocumentColor.rgb(228, 231, 234); + static final DocumentColor BODY_TEXT = DocumentColor.rgb(39, 51, 75); + static final DocumentColor MUTED_TEXT = DocumentColor.rgb(85, 93, 109); + static final DocumentColor ON_DARK = DocumentColor.rgb(255, 255, 255); + + static final FontName TITLE_FONT = FontName.SPECTRAL; + static final FontName BODY_FONT = FontName.LATO; + + // --- text roles ----------------------------------------------------------- + static final DocumentTextStyle TITLE_INK = + style(TITLE_FONT, TITLE_SIZE, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle TITLE_ACCENT = + style(TITLE_FONT, TITLE_SIZE, DocumentTextDecoration.BOLD, ACCENT); + static final DocumentTextStyle SECTION_HEADING = + style(BODY_FONT, HEADING_SIZE, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle PANEL_HEADING = + style(BODY_FONT, BODY_SIZE + 0.5, DocumentTextDecoration.BOLD, ACCENT); + static final DocumentTextStyle BODY = + style(BODY_FONT, BODY_SIZE, DocumentTextDecoration.DEFAULT, BODY_TEXT); + static final DocumentTextStyle META = + style(BODY_FONT, LABEL_SIZE, DocumentTextDecoration.BOLD, MUTED_TEXT); + static final DocumentTextStyle META_SEPARATOR = + style(BODY_FONT, LABEL_SIZE, DocumentTextDecoration.DEFAULT, RULE); + static final DocumentTextStyle FACT_LABEL = + style(BODY_FONT, LABEL_SIZE, DocumentTextDecoration.BOLD, MUTED_TEXT); + static final DocumentTextStyle FACT_VALUE = + style(BODY_FONT, FACT_VALUE_SIZE, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle FACT_NOTE = + style(BODY_FONT, LABEL_SIZE + 0.5, DocumentTextDecoration.DEFAULT, BODY_TEXT); + static final DocumentTextStyle GOAL_TEXT = + style(BODY_FONT, TABLE_SIZE, DocumentTextDecoration.DEFAULT, BODY_TEXT); + static final DocumentTextStyle SCOPE_TITLE = + style(BODY_FONT, TABLE_SIZE + 0.5, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle SCOPE_BODY = + style(BODY_FONT, TABLE_SIZE, DocumentTextDecoration.DEFAULT, MUTED_TEXT); + static final DocumentTextStyle SCOPE_NUMBER = + style(BODY_FONT, LABEL_SIZE + 0.5, DocumentTextDecoration.BOLD, ON_DARK); + static final DocumentTextStyle TABLE_HEADER = + style(BODY_FONT, TABLE_SIZE - 0.5, DocumentTextDecoration.BOLD, ON_DARK); + static final DocumentTextStyle TABLE_BODY = + style(BODY_FONT, TABLE_SIZE, DocumentTextDecoration.DEFAULT, BODY_TEXT); + static final DocumentTextStyle TABLE_BODY_BOLD = + style(BODY_FONT, TABLE_SIZE, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle TOTAL_TEXT = + style(BODY_FONT, TABLE_SIZE + 1.5, DocumentTextDecoration.BOLD, ON_DARK); + static final DocumentTextStyle WORDMARK = + style(BODY_FONT, 16.0, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle MONOGRAM = + style(BODY_FONT, 30.0, DocumentTextDecoration.BOLD, ON_DARK); + static final DocumentTextStyle DOC_LABEL = + style(BODY_FONT, LABEL_SIZE + 1, DocumentTextDecoration.BOLD, INK); + static final DocumentTextStyle SIGNATURE_LABEL = + style(BODY_FONT, TABLE_SIZE, DocumentTextDecoration.DEFAULT, INK); + + private static DocumentTextStyle style(FontName font, + double size, + DocumentTextDecoration decoration, + DocumentColor color) { + return DocumentTextStyle.builder() + .fontName(font) + .size(size) + .decoration(decoration) + .color(color) + .build(); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineWidgets.java b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineWidgets.java new file mode 100644 index 000000000..278e3a19b --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/proposal/presets/NorthlineWidgets.java @@ -0,0 +1,208 @@ +package com.demcha.compose.document.templates.proposal.presets; + +import com.demcha.compose.document.dsl.ImageBuilder; +import com.demcha.compose.document.dsl.LineBuilder; +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +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.node.RowVerticalAlign; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.node.TextVerticalAlign; +import com.demcha.compose.document.style.ClipPolicy; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentRowColumn; +import com.demcha.compose.document.style.DocumentTextIndent; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.templates.core.text.TextOrnaments; +import com.demcha.compose.document.templates.data.proposal.ProposalBrand; + +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.ACCENT; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BADGE_DIAMETER; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BADGE_GAP; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BADGE_GLYPH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BODY; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.BODY_LEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.DOC_LABEL; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.DOC_LABEL_WIDTH; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GAP_HEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.GAP_ROW; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.HAIRLINE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.INK; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.LOGO_SIZE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.MONOGRAM; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.RULE; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.SECTION_HEADING; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.WORDMARK; +import static com.demcha.compose.document.templates.proposal.presets.NorthlineStyles.WORDMARK_WIDTH; + +/** + * The building blocks the Northline bands share: the page header, the + * icon-badged section heading, the teal bullet, and the small node helpers. + * + *

Constraint notes carried over from the ported template: the engine + * refuses a row nested inside another row's cell, so the header flattens + * its columns, and the section heading is a shape container hosting two + * anchored children rather than a row — half its uses sit inside a row + * cell. The heading container carries no fill and no margin (a margin + * would displace its paint); the gap above it comes from the enclosing + * flow's spacing.

+ */ +final class NorthlineWidgets { + + private NorthlineWidgets() { + } + + /** + * Logo square, wordmark, document label and the hairline that closes + * the header. One flat row: the logo is a shape container owning its + * monogram, the wordmark and the label are sections, and the weight(1) + * column between them holds the two ends apart. + */ + static void renderHeader(SectionBuilder header, ProposalBrand brand) { + header.spacing(0); + header.addRow("HeaderRow", row -> { + row.verticalAlign(RowVerticalAlign.CENTER) + .gap(LOGO_SIZE * 0.25) + // Not auto(): a section's natural width is the width it + // is offered, so two auto columns each claim the whole + // row. Both ends are measured strips instead. + .columns(DocumentRowColumn.fixed(LOGO_SIZE), + DocumentRowColumn.fixed(WORDMARK_WIDTH), + DocumentRowColumn.weight(1), + DocumentRowColumn.fixed(DOC_LABEL_WIDTH)); + row.add(logoMark(brand.monogram())); + row.addSection("Wordmark", mark -> { + mark.spacing(0); + mark.addParagraph(p -> p.text(brand.nameLine1()).textStyle(WORDMARK)); + mark.addParagraph(p -> p.text(brand.nameLine2()).textStyle(WORDMARK)); + }); + // A zero-width spacer: the row distributes by columns, and the + // weight(1) column above already holds the ends apart. + row.addSpacer(0); + row.addSection("DocumentLabel", label -> { + label.spacing(0); + label.addParagraph(p -> p + .text(TextOrnaments.spacedUpper(brand.documentLabel())) + .textStyle(DOC_LABEL) + .align(TextAlign.RIGHT)); + // addAligned, not addLine: a section stacks children at the + // content width with no child-alignment option, and the rule + // sits flush RIGHT under the label. + label.addAligned(HorizontalAlign.RIGHT, new LineBuilder() + .name("DocumentLabelRule") + .horizontal(BADGE_DIAMETER * 1.3) + .thickness(2.2) + .color(ACCENT) + .margin(DocumentInsets.top(6)) + .build()); + }); + }); + header.addLine(line -> line + .fill() + .thickness(HAIRLINE) + .color(RULE) + .margin(DocumentInsets.top(GAP_HEADING))); + } + + /** The navy square and the monogram it owns. */ + private static DocumentNode logoMark(String monogram) { + return new ShapeContainerBuilder() + .name("LogoMark") + .rectangle(LOGO_SIZE, LOGO_SIZE) + .fillColor(INK) + .center(paragraph("Monogram", monogram, MONOGRAM, TextAlign.CENTER)) + .build(); + } + + /** + * The heading every section opens with: a teal badge owning a white + * glyph, and the title vertically centred beside it. + * + * @param title the heading text, already uppercase in the data + * @param iconToken the icon token from the data + * @param width the width of the column the heading sits in + */ + static DocumentNode sectionHeading(String title, String iconToken, double width) { + return new ShapeContainerBuilder() + .name("SectionHeading") + .rectangle(width, BADGE_DIAMETER) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE) + .position(badge(iconToken), 0, 0, LayerAlign.CENTER_LEFT) + .position(new ParagraphBuilder() + .name("SectionHeadingTitle") + .text(title) + .textStyle(SECTION_HEADING) + .verticalAlign(TextVerticalAlign.CENTER) + .build(), BADGE_DIAMETER + BADGE_GAP, 0, LayerAlign.CENTER_LEFT) + .build(); + } + + /** + * The teal disc and the glyph it owns. A blank token leaves the disc + * plain — the container still needs a layer, so an empty paragraph + * stands in for the glyph. + */ + private static DocumentNode badge(String iconToken) { + ShapeContainerBuilder badge = new ShapeContainerBuilder() + .name("SectionBadge") + .circle(BADGE_DIAMETER) + .fillColor(ACCENT); + if (iconToken.isBlank()) { + badge.center(new ParagraphBuilder() + .name("BadgeGlyph").text("").textStyle(SECTION_HEADING).build()); + } else { + badge.center(icon(iconToken, BADGE_GLYPH)); + } + return badge.build(); + } + + /** + * A bullet item: an inline teal dot followed by the text, with a + * hanging indent so wrapped lines clear the dot. The dot is an inline + * shape run rather than a bullet glyph, so it keeps its own colour and + * cannot be lost to font coverage. + */ + static void renderBullet(SectionBuilder host, String text) { + host.addParagraph(p -> p + .dot(4.2, ACCENT) + .inlineText(" " + text, BODY) + .bulletOffset(" ") + // Wrapped lines align under the first line text, not the dot. + .indentStrategy(DocumentTextIndent.FROM_SECOND_LINE) + .lineSpacing(BODY_LEADING) + .margin(0f, 0f, (float) GAP_ROW, 0f)); + } + + /** A named, vertically centred paragraph node. */ + static DocumentNode paragraph(String name, + String text, + DocumentTextStyle textStyle, + TextAlign align) { + return new ParagraphBuilder() + .name(name) + .text(text) + .textStyle(textStyle) + .align(align) + .verticalAlign(TextVerticalAlign.CENTER) + .build(); + } + + /** One packaged icon at a given size, as a node. */ + static DocumentNode icon(String token, double size) { + return new ImageBuilder() + .name("Icon-" + token) + .source(NorthlineIcons.image(token)) + .size(size, size) + .build(); + } + + /** The same icon, configured in place on a row's own image builder. */ + static void configureIcon(ImageBuilder image, String token, double size) { + image.name("Icon-" + token) + .source(NorthlineIcons.image(token)) + .size(size, size); + } +} diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-acceptance.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-acceptance.png new file mode 100644 index 000000000..2c7eca12b Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-acceptance.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-deliverables.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-deliverables.png new file mode 100644 index 000000000..ffe3a6432 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-deliverables.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-goals.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-goals.png new file mode 100644 index 000000000..94fa8267e Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-goals.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-investment.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-investment.png new file mode 100644 index 000000000..8d422c44d Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-investment.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-scope.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-scope.png new file mode 100644 index 000000000..c4af91d00 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-scope.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-summary.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-summary.png new file mode 100644 index 000000000..cb967c5cb Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-summary.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-terms.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-terms.png new file mode 100644 index 000000000..968a5b69d Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-terms.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/badge-timeline.png b/templates/src/main/resources/templates/proposal/northline/icons/badge-timeline.png new file mode 100644 index 000000000..861438a15 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/badge-timeline.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/fact-contact.png b/templates/src/main/resources/templates/proposal/northline/icons/fact-contact.png new file mode 100644 index 000000000..553d008b2 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/fact-contact.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/fact-duration.png b/templates/src/main/resources/templates/proposal/northline/icons/fact-duration.png new file mode 100644 index 000000000..c9c7cc34d Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/fact-duration.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/fact-start.png b/templates/src/main/resources/templates/proposal/northline/icons/fact-start.png new file mode 100644 index 000000000..6a39b6cbc Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/fact-start.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/fact-validity.png b/templates/src/main/resources/templates/proposal/northline/icons/fact-validity.png new file mode 100644 index 000000000..66fcde3bf Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/fact-validity.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/goal-brand.png b/templates/src/main/resources/templates/proposal/northline/icons/goal-brand.png new file mode 100644 index 000000000..711a15978 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/goal-brand.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/goal-growth.png b/templates/src/main/resources/templates/proposal/northline/icons/goal-growth.png new file mode 100644 index 000000000..e697c58d8 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/goal-growth.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/goal-message.png b/templates/src/main/resources/templates/proposal/northline/icons/goal-message.png new file mode 100644 index 000000000..a0300e3ae Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/goal-message.png differ diff --git a/templates/src/main/resources/templates/proposal/northline/icons/goal-usability.png b/templates/src/main/resources/templates/proposal/northline/icons/goal-usability.png new file mode 100644 index 000000000..3cc3c8a51 Binary files /dev/null and b/templates/src/main/resources/templates/proposal/northline/icons/goal-usability.png differ