diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c1ee8e3..d7f869e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,31 @@ follow semantic versioning; release dates are ISO 8601. ### Templates +- **An architect's two-column CV preset: `TerracottaRail`.** A one-page A4 sheet whose + narrow column carries a serif monogram over a terracotta rule, the contact channels + behind their marks, two bulleted lists and a block of closing facts, beside a wide + column carrying a letter-spaced masthead, the summary, the roles held on a ringed + rail, a projects grid and the degrees. Ships as `cv.presets.TerracottaRail` on the + existing `CvDocument` model with **no model change at all**, porting the rendered + layout of a published standalone template. Like `CharcoalGold` it leaves the page to + the caller. Eight berths reach their sections by title; the two bulleted lists are + skills without levels, because this design writes them as plain lines — one takes a + terracotta square and no dash under its heading, the other a disc and a dash, which + is how the sheet tells two lists of one-liners apart. Each fact and each project + takes the mark its entry names in `CvEntry.icon()` from this preset's own vocabulary, + a project title is a link when its entry carries one, and the monogram is drawn from + the name's own initials rather than a field of its own — a document states its name + once, and a monogram that could disagree with it would be a second place to keep + true. A link in the contact block is set smaller than the channels above it, because + a URL is one long token that cannot be broken and is the line that outgrows that + column first; it takes the mark of the network it points at, or a globe. Like its + siblings it holds one page: the body is a single atomic row, so a longer CV raises + `AtomicNodeTooLargeException` rather than flowing or dropping entries. Guarded by a + smoke test (including the unknown-mark data error, an entry with no mark, an identity + with no links, a document with nothing but an identity, the monogram, the link + targets and the one-page limit), an exact layout snapshot and a pixel-parity gate; + the examples showcase gains `cv-terracotta-rail-v2`. + - **The first invoice preset that paginates what it ports: `LumaStudioInvoice`.** A studio invoice built around a cream sidebar — the two-line monogram and the wordmark on a terracotta block at its head, a tinted quarter-disc, an arch and a sprig running diff --git a/assets/readme/examples/cv-terracotta-rail-v2.pdf b/assets/readme/examples/cv-terracotta-rail-v2.pdf new file mode 100644 index 000000000..f598c897e Binary files /dev/null and b/assets/readme/examples/cv-terracotta-rail-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 55bb1da8a..398cd51e9 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -80,6 +80,7 @@ import com.demcha.examples.templates.coverletter.CvSidebarPortraitLetterV2Example; import com.demcha.examples.templates.coverletter.CvTimelineMinimalLetterV2Example; import com.demcha.examples.templates.cv.v2.CharcoalGoldExample; +import com.demcha.examples.templates.cv.v2.TerracottaRailExample; import com.demcha.examples.templates.cv.v2.CvBlueBannerExample; import com.demcha.examples.templates.cv.v2.CvBoxedV2Example; import com.demcha.examples.templates.cv.v2.CvCenteredHeadlineExample; @@ -154,6 +155,7 @@ public static void main(String[] args) throws Exception { System.out.println("Generated: " + ProfessionalSidebarExample.generate()); System.out.println("Generated: " + SerifHeadlineExample.generate()); System.out.println("Generated: " + CharcoalGoldExample.generate()); + System.out.println("Generated: " + TerracottaRailExample.generate()); // Cover letters (v2 layered — 15 paired letters, one per CV preset) System.out.println("Generated: " + CvBlueBannerLetterV2Example.generate()); diff --git a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java index da69b14cf..9df177429 100644 --- a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java +++ b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java @@ -64,6 +64,7 @@ record Entry(String title, String description, List tags, String codeUrl cv("cv-navy-sidebar-v2", "NavySidebarExample", "Navy Sidebar", "Navy plate with a ringed portrait, contact marks, degrees, skills and languages, beside a white column of summary, badged sections and roles strung on a timeline rail.", "sidebar", "navy", "portrait"); cv("cv-serif-headline-v2", "SerifHeadlineExample", "Serif Headline", "Volkhov masthead over a two-column body: roles on a timeline rail and marked project cards beside degrees and skill meters, closing with full-width certification and achievement bands.", "serif", "two-column"); cv("cv-charcoal-gold-v2", "CharcoalGoldExample", "Charcoal Gold", "Charcoal sidebar with a ringed photograph, rated skills and languages, beside a paper column with a two-tone name, a dated experience rail and paired credential columns.", "sidebar", "photo", "gold"); + cv("cv-terracotta-rail-v2", "TerracottaRailExample", "Terracotta Rail", "Serif monogram over a terracotta rule, contact channels behind their marks and two bulleted lists, beside a letter-spaced masthead, roles on a ringed rail, a projects grid and the degrees.", "sidebar", "monogram", "terracotta"); // ===== Templates / Cover Letter (v2 layered, paired 1:1 with CV) ===== // Registered directly: letter() points at the layered preset examples under diff --git a/examples/src/main/java/com/demcha/examples/support/TerracottaRailSampleData.java b/examples/src/main/java/com/demcha/examples/support/TerracottaRailSampleData.java new file mode 100644 index 000000000..2a6490270 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/support/TerracottaRailSampleData.java @@ -0,0 +1,207 @@ +package com.demcha.examples.support; + +import com.demcha.compose.document.templates.core.identity.Contact; +import com.demcha.compose.document.templates.core.identity.Link; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.data.CvEntry; +import com.demcha.compose.document.templates.cv.data.CvIdentity; +import com.demcha.compose.document.templates.cv.data.CvName; +import com.demcha.compose.document.templates.cv.data.CvSkill; +import com.demcha.compose.document.templates.cv.data.EntriesSection; +import com.demcha.compose.document.templates.cv.data.ParagraphSection; +import com.demcha.compose.document.templates.cv.data.SkillGroup; +import com.demcha.compose.document.templates.cv.data.SkillsSection; +import com.demcha.compose.document.templates.cv.data.Slot; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; + +/** + * Shared sample data for the Terracotta Rail CV example. + * + *

The sample is the preset's reference content: an architect's one-page + * CV with a monogram, four contact rows including a link, two bulleted + * lists, two credentials, three facts behind their marks, a three-paragraph + * summary, three roles on the rail, three marked projects and two + * degrees.

+ * + *

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

+ */ +public final class TerracottaRailSampleData { + + /** The break a fact stacks its values on. */ + private static final String NEWLINE = String.valueOf((char) 10); + + /** The dash this sheet writes a date range with. */ + private static final String EN_DASH = String.valueOf((char) 0x2013); + + private TerracottaRailSampleData() { + } + + /** The single-page reference CV. */ + public static CvDocument sample() { + List placements = new ArrayList<>(); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, competencies())); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, software())); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, certifications())); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, facts())); + placements.add(new CvDocument.Placement(Slot.MAIN, summary())); + placements.add(new CvDocument.Placement(Slot.MAIN, experience())); + placements.add(new CvDocument.Placement(Slot.MAIN, projects())); + placements.add(new CvDocument.Placement(Slot.MAIN, education())); + return new CvDocument(identity(), placements); + } + + private static CvIdentity identity() { + return new CvIdentity( + CvName.of("OLIVER", "BENNETT"), + "SENIOR ARCHITECT", + new Contact("+44 7700 900123", "oliver.bennett@email.com", + "Bristol, United Kingdom"), + List.of(new Link("linkedin.com/in/oliverbennett-architect", + "https://linkedin.com/in/oliverbennett-architect")), + Optional.empty()); + } + + private static SkillsSection competencies() { + return plainSkills("CORE COMPETENCIES", + "Architectural Design", "Concept Development", "Planning Applications", + "Technical Drawings", "Revit", "AutoCAD", "BIM Coordination", + "Design Presentations", "Project Delivery", "Stakeholder Management", + "Sustainable Design", "Building Regulations"); + } + + private static SkillsSection software() { + return plainSkills("SOFTWARE", + "Revit", "AutoCAD", "Rhino", "SketchUp", "Adobe InDesign", "Photoshop", + "Microsoft Office"); + } + + private static EntriesSection certifications() { + return new EntriesSection("CERTIFICATIONS", List.of( + CvEntry.builder("ARB Registered Architect").build(), + CvEntry.builder("RIBA Chartered Member").build())); + } + + private static EntriesSection facts() { + return new EntriesSection("ADDITIONAL INFORMATION", List.of( + CvEntry.builder("Languages:") + .icon("globe") + .body("English (Native)," + NEWLINE + "Spanish (Conversational)") + .build(), + CvEntry.builder("Right to Work:") + .icon("badge") + .body("United Kingdom") + .build(), + CvEntry.builder("Availability:") + .icon("clock") + .body("1 month notice") + .build())); + } + + private static ParagraphSection summary() { + return new ParagraphSection("PROFESSIONAL SUMMARY", String.join(NEWLINE, + "Senior Architect with over 8 years of experience delivering residential, " + + "mixed-use, and commercial projects across all RIBA work stages.", + "Proven expertise in design development, planning applications, and technical " + + "delivery with a strong focus on sustainability, quality, and user " + + "experience.", + "Collaborative communicator skilled in coordinating consultants, engaging " + + "stakeholders, and leading multidisciplinary teams to achieve " + + "successful outcomes.")); + } + + private static EntriesSection experience() { + return new EntriesSection("PROFESSIONAL EXPERIENCE", List.of( + CvEntry.builder("Senior Architect") + .subtitle("Northline Studio, Bristol, UK") + .date("2021" + EN_DASH + "Present") + .body(String.join(NEWLINE, + "Lead design packages from concept through to detailed design " + + "across mid-scale residential and mixed-use projects.", + "Coordinate structural, M&E, landscape and planning consultants " + + "to ensure integrated and buildable solutions.", + "Prepare and manage planning applications, design and access " + + "statements, and supporting documentation.", + "Present and communicate design proposals to clients and " + + "stakeholders, securing approvals and driving projects " + + "forward.")) + .build(), + CvEntry.builder("Architect") + .subtitle("Urban Form Partners, Bath, UK") + .date("2018" + EN_DASH + "2021") + .body(String.join(NEWLINE, + "Developed concept and technical designs for a range of " + + "residential, commercial, and education projects.", + "Prepared coordinated drawing sets, schedules, and " + + "specifications to support planning and construction.", + "Supported project delivery on site, resolving design queries " + + "and ensuring quality outcomes.", + "Coordinated with contractors and consultants to maintain " + + "programme, budget and design intent.")) + .build(), + CvEntry.builder("Part II Architectural Assistant") + .subtitle("Axis Design Workshop, Cardiff, UK") + .date("2016" + EN_DASH + "2018") + .body(String.join(NEWLINE, + "Supported senior architects across all RIBA stages on a " + + "variety of project types.", + "Produced CAD and Revit drawings, models, and visualisations to " + + "communicate design intent.", + "Undertook research, prepared presentation boards, and assisted " + + "with planning documentation.")) + .build())); + } + + private static EntriesSection projects() { + return new EntriesSection("SELECTED PROJECTS", List.of( + CvEntry.builder("Harbour Point") + .subtitle("Mixed-Use Regeneration") + .place("Bristol") + .icon("building") + .body("Mixed-use development delivering 142 homes, retail space and " + + "public realm improvements; secured planning consent and is " + + "now on site.") + .build(), + CvEntry.builder("The Assembly Hotel") + .subtitle("Boutique Hotel Refurbishment") + .place("Bath") + .icon("hotel") + .body("Sensitive refurbishment of listed building to create a " + + "36-bedroom boutique hotel; enhanced heritage features and " + + "guest experience.") + .build(), + CvEntry.builder("Elmwood Mews") + .subtitle("Residential Development") + .place("Clifton, Bristol") + .icon("house") + .body("Design of 28 high-quality homes within a sustainable masterplan; " + + "achieved high environmental standards and strong sales " + + "performance.") + .build())); + } + + private static EntriesSection education() { + return new EntriesSection("EDUCATION", List.of( + CvEntry.builder("MArch Architecture") + .subtitle("University of Sheffield") + .date("2014" + EN_DASH + "2016") + .build(), + CvEntry.builder("BA (Hons) Architecture") + .subtitle("University of the West of England") + .date("2011" + EN_DASH + "2014") + .build())); + } + + private static SkillsSection plainSkills(String title, String... names) { + List entries = new ArrayList<>(); + for (String name : names) { + entries.add(new CvSkill(name, OptionalDouble.empty())); + } + return new SkillsSection(title, List.of(new SkillGroup(title, entries))); + } +} diff --git a/examples/src/main/java/com/demcha/examples/templates/cv/v2/TerracottaRailExample.java b/examples/src/main/java/com/demcha/examples/templates/cv/v2/TerracottaRailExample.java new file mode 100644 index 000000000..6bce9e01d --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/templates/cv/v2/TerracottaRailExample.java @@ -0,0 +1,57 @@ +package com.demcha.examples.templates.cv.v2; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.presets.TerracottaRail; +import com.demcha.examples.support.ExampleOutputPaths; +import com.demcha.examples.support.TerracottaRailSampleData; + +import java.nio.file.Path; + +/** + * Renders the layered {@code cv.v2} Terracotta Rail preset against the + * architect sample. + * + *

Output: + * {@code examples/target/generated-pdfs/templates/cv/cv-terracotta-rail-v2.pdf}.

+ * + *

The preset leaves the page to the caller, so the session sets A4 with + * no margin: both columns run to the paper edge and each carries its own + * padding.

+ */ +public final class TerracottaRailExample { + + private TerracottaRailExample() { + } + + /** + * @return absolute path of the rendered PDF + * @throws Exception if rendering fails + */ + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare( + "templates/cv", "cv-terracotta-rail-v2.pdf"); + CvDocument doc = TerracottaRailSampleData.sample(); + DocumentTemplate template = TerracottaRail.create(); + + try (DocumentSession document = GraphCompose.document(outputFile) + .pageSize(DocumentPageSize.A4) + .margin(0f, 0f, 0f, 0f) + .create()) { + template.compose(document, doc); + 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/cv/presets/TerracottaRailFixtures.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailFixtures.java new file mode 100644 index 000000000..6b8271bd5 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailFixtures.java @@ -0,0 +1,205 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.templates.core.identity.Contact; +import com.demcha.compose.document.templates.core.identity.Link; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.data.CvEntry; +import com.demcha.compose.document.templates.cv.data.CvIdentity; +import com.demcha.compose.document.templates.cv.data.CvName; +import com.demcha.compose.document.templates.cv.data.CvSkill; +import com.demcha.compose.document.templates.cv.data.EntriesSection; +import com.demcha.compose.document.templates.cv.data.ParagraphSection; +import com.demcha.compose.document.templates.cv.data.SkillGroup; +import com.demcha.compose.document.templates.cv.data.SkillsSection; +import com.demcha.compose.document.templates.cv.data.Slot; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; + +/** + * Shared fixture data for the {@link TerracottaRail} gates — the SAME + * document feeds the pixel parity test and the layout snapshot test, so a + * geometry shift the pixel budget absorbs still trips the exact snapshot, + * and vice versa. + */ +final class TerracottaRailFixtures { + + /** The break a fact stacks its values on. */ + private static final String NEWLINE = String.valueOf((char) 10); + + /** The dash this sheet writes a date range with. */ + private static final String EN_DASH = String.valueOf((char) 0x2013); + + private TerracottaRailFixtures() { + } + + /** + * The canonical one-page CV — the monogram, four contact rows including + * a link, two bulleted lists, two credentials, three facts behind their + * marks, a three-paragraph summary, three roles on the rail, three + * marked projects and two degrees. + */ + static CvDocument canonicalCv() { + List placements = new ArrayList<>(); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, competencies())); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, software())); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, certifications())); + placements.add(new CvDocument.Placement(Slot.SIDEBAR, facts())); + placements.add(new CvDocument.Placement(Slot.MAIN, summary())); + placements.add(new CvDocument.Placement(Slot.MAIN, experience())); + placements.add(new CvDocument.Placement(Slot.MAIN, projects())); + placements.add(new CvDocument.Placement(Slot.MAIN, education())); + return new CvDocument(identity(), placements); + } + + static CvIdentity identity() { + return new CvIdentity( + CvName.of("OLIVER", "BENNETT"), + "SENIOR ARCHITECT", + new Contact("+44 7700 900123", "oliver.bennett@email.com", + "Bristol, United Kingdom"), + List.of(new Link("linkedin.com/in/oliverbennett-architect", + "https://linkedin.com/in/oliverbennett-architect")), + Optional.empty()); + } + + static SkillsSection competencies() { + return plainSkills("CORE COMPETENCIES", + "Architectural Design", "Concept Development", "Planning Applications", + "Technical Drawings", "Revit", "AutoCAD", "BIM Coordination", + "Design Presentations", "Project Delivery", "Stakeholder Management", + "Sustainable Design", "Building Regulations"); + } + + static SkillsSection software() { + return plainSkills("SOFTWARE", + "Revit", "AutoCAD", "Rhino", "SketchUp", "Adobe InDesign", "Photoshop", + "Microsoft Office"); + } + + static EntriesSection certifications() { + return new EntriesSection("CERTIFICATIONS", List.of( + CvEntry.builder("ARB Registered Architect").build(), + CvEntry.builder("RIBA Chartered Member").build())); + } + + static EntriesSection facts() { + return new EntriesSection("ADDITIONAL INFORMATION", List.of( + CvEntry.builder("Languages:") + .icon("globe") + .body("English (Native)," + NEWLINE + "Spanish (Conversational)") + .build(), + CvEntry.builder("Right to Work:") + .icon("badge") + .body("United Kingdom") + .build(), + CvEntry.builder("Availability:") + .icon("clock") + .body("1 month notice") + .build())); + } + + static ParagraphSection summary() { + return new ParagraphSection("PROFESSIONAL SUMMARY", String.join(NEWLINE, + "Senior Architect with over 8 years of experience delivering residential, " + + "mixed-use, and commercial projects across all RIBA work stages.", + "Proven expertise in design development, planning applications, and technical " + + "delivery with a strong focus on sustainability, quality, and user " + + "experience.", + "Collaborative communicator skilled in coordinating consultants, engaging " + + "stakeholders, and leading multidisciplinary teams to achieve " + + "successful outcomes.")); + } + + static EntriesSection experience() { + return new EntriesSection("PROFESSIONAL EXPERIENCE", List.of( + CvEntry.builder("Senior Architect") + .subtitle("Northline Studio, Bristol, UK") + .date("2021" + EN_DASH + "Present") + .body(String.join(NEWLINE, + "Lead design packages from concept through to detailed design " + + "across mid-scale residential and mixed-use projects.", + "Coordinate structural, M&E, landscape and planning consultants " + + "to ensure integrated and buildable solutions.", + "Prepare and manage planning applications, design and access " + + "statements, and supporting documentation.", + "Present and communicate design proposals to clients and " + + "stakeholders, securing approvals and driving projects " + + "forward.")) + .build(), + CvEntry.builder("Architect") + .subtitle("Urban Form Partners, Bath, UK") + .date("2018" + EN_DASH + "2021") + .body(String.join(NEWLINE, + "Developed concept and technical designs for a range of " + + "residential, commercial, and education projects.", + "Prepared coordinated drawing sets, schedules, and " + + "specifications to support planning and construction.", + "Supported project delivery on site, resolving design queries " + + "and ensuring quality outcomes.", + "Coordinated with contractors and consultants to maintain " + + "programme, budget and design intent.")) + .build(), + CvEntry.builder("Part II Architectural Assistant") + .subtitle("Axis Design Workshop, Cardiff, UK") + .date("2016" + EN_DASH + "2018") + .body(String.join(NEWLINE, + "Supported senior architects across all RIBA stages on a " + + "variety of project types.", + "Produced CAD and Revit drawings, models, and visualisations to " + + "communicate design intent.", + "Undertook research, prepared presentation boards, and assisted " + + "with planning documentation.")) + .build())); + } + + static EntriesSection projects() { + return new EntriesSection("SELECTED PROJECTS", List.of( + CvEntry.builder("Harbour Point") + .subtitle("Mixed-Use Regeneration") + .place("Bristol") + .icon("building") + .body("Mixed-use development delivering 142 homes, retail space and " + + "public realm improvements; secured planning consent and is " + + "now on site.") + .build(), + CvEntry.builder("The Assembly Hotel") + .subtitle("Boutique Hotel Refurbishment") + .place("Bath") + .icon("hotel") + .body("Sensitive refurbishment of listed building to create a " + + "36-bedroom boutique hotel; enhanced heritage features and " + + "guest experience.") + .build(), + CvEntry.builder("Elmwood Mews") + .subtitle("Residential Development") + .place("Clifton, Bristol") + .icon("house") + .body("Design of 28 high-quality homes within a sustainable masterplan; " + + "achieved high environmental standards and strong sales " + + "performance.") + .build())); + } + + static EntriesSection education() { + return new EntriesSection("EDUCATION", List.of( + CvEntry.builder("MArch Architecture") + .subtitle("University of Sheffield") + .date("2014" + EN_DASH + "2016") + .build(), + CvEntry.builder("BA (Hons) Architecture") + .subtitle("University of the West of England") + .date("2011" + EN_DASH + "2014") + .build())); + } + + private static SkillsSection plainSkills(String title, String... names) { + List entries = new ArrayList<>(); + for (String name : names) { + entries.add(new CvSkill(name, OptionalDouble.empty())); + } + return new SkillsSection(title, List.of(new SkillGroup(title, entries))); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailLayoutSnapshotTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailLayoutSnapshotTest.java new file mode 100644 index 000000000..bb2a3af80 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailLayoutSnapshotTest.java @@ -0,0 +1,38 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.TemplateTestSupport; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Exact layout snapshot gate for {@link TerracottaRail} — freezes the + * resolved geometry of the canonical one-page CV, which the pixel budget + * cannot see: a small column or spacing shift stays under the visual-diff + * budget but changes the snapshot. + * + *

This preset leaves the page to the caller, so the session sets A4 with + * no margin: both columns run to the paper edge and each carries its own + * padding.

+ * + *

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

+ */ +class TerracottaRailLayoutSnapshotTest { + + @Test + void canonicalCvMatchesLayoutSnapshot() throws Exception { + try (DocumentSession session = GraphCompose.document() + .pageSize(DocumentPageSize.A4) + .margin(0f, 0f, 0f, 0f) + .create()) { + TerracottaRail.create().compose(session, TerracottaRailFixtures.canonicalCv()); + assertThat(session.layoutSnapshot().totalPages()).isEqualTo(1); + TemplateTestSupport.assertCanonicalSnapshot( + session, "terracotta_rail_layout", "cv-v2"); + } + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java new file mode 100644 index 000000000..d443008f6 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java @@ -0,0 +1,225 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.core.identity.Contact; +import com.demcha.compose.document.templates.core.identity.Link; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.data.CvEntry; +import com.demcha.compose.document.templates.cv.data.CvIdentity; +import com.demcha.compose.document.templates.cv.data.CvName; +import com.demcha.compose.document.templates.cv.data.EntriesSection; +import com.demcha.compose.document.templates.cv.data.Slot; +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.interactive.action.PDActionURI; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink; +import org.apache.pdfbox.text.PDFTextStripper; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Smoke test for {@link TerracottaRail} — proves the preset renders a + * {@link CvDocument} end-to-end with its packaged marks, draws the monogram + * from the name, reports an unknown icon token as a data error, refuses a CV + * longer than its sheet rather than losing an entry, and carries the contact + * channels and a project title as link annotations, which move no pixel and + * no layout node so neither gate would notice them going missing. + */ +class TerracottaRailSmokeTest { + + private static byte[] render(CvDocument doc) throws Exception { + try (DocumentSession session = GraphCompose.document() + .pageSize(DocumentPageSize.A4) + .margin(0f, 0f, 0f, 0f) + .create()) { + TerracottaRail.create().compose(session, doc); + assertThat(session.roots()).isNotEmpty(); + byte[] pdfBytes = session.toPdfBytes(); + assertThat(pdfBytes).isNotEmpty(); + return pdfBytes; + } + } + + private static String textOf(byte[] pdfBytes) throws Exception { + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + return new PDFTextStripper().getText(document); + } + } + + /** + * The text with its spaces removed. The sheet's headings and its masthead + * are letter-spaced with real space characters, so what is being asserted + * is the wording, not the tracking. + */ + private static String compact(String text) { + return text.replaceAll("(?U)\\s", ""); + } + + private static List linkTargets(byte[] pdfBytes) throws Exception { + List targets = new ArrayList<>(); + try (PDDocument document = Loader.loadPDF(pdfBytes)) { + for (PDPage page : document.getPages()) { + for (PDAnnotation annotation : page.getAnnotations()) { + if (annotation instanceof PDAnnotationLink link + && link.getAction() instanceof PDActionURI uri) { + targets.add(uri.getURI()); + } + } + } + } + return targets; + } + + /** The canonical document with one section replaced. */ + private static CvDocument withSection(Slot slot, EntriesSection replacement, + String replacedTitle) { + List placements = new ArrayList<>(); + for (CvDocument.Placement placement : TerracottaRailFixtures.canonicalCv().placements()) { + if (placement.section().title().equals(replacedTitle)) { + placements.add(new CvDocument.Placement(slot, replacement)); + } else { + placements.add(placement); + } + } + return new CvDocument(TerracottaRailFixtures.identity(), placements); + } + + @Test + void exposesStableIdentity() { + DocumentTemplate template = TerracottaRail.create(); + assertThat(template.id()).isEqualTo(TerracottaRail.ID); + assertThat(template.displayName()).isEqualTo(TerracottaRail.DISPLAY_NAME); + } + + @Test + void rendersCanonicalCvWithPackagedMarks() throws Exception { + render(TerracottaRailFixtures.canonicalCv()); + } + + @Test + void canonicalRenderCarriesEveryColumnsText() throws Exception { + String text = textOf(render(TerracottaRailFixtures.canonicalCv())); + assertThat(text) + .contains("oliver.bennett@email.com") + .contains("Architectural Design") + .contains("Microsoft Office") + .contains("ARB Registered Architect") + .contains("Northline Studio, Bristol, UK") + .contains("Harbour Point") + .contains("University of Sheffield"); + assertThat(compact(text)) + .contains("OLIVERBENNETT") + .contains("SENIORARCHITECT") + .contains("PROFESSIONALEXPERIENCE"); + } + + @Test + void theMonogramIsDrawnFromTheName() throws Exception { + // Two capitals on their own line, and no field of their own in the + // model: the initials are the whole source. + String text = textOf(render(TerracottaRailFixtures.canonicalCv())); + assertThat(text.lines().map(String::strip).toList()).contains("OB"); + } + + @Test + void contactChannelsAndLinksAreClickable() throws Exception { + List targets = linkTargets(render(TerracottaRailFixtures.canonicalCv())); + assertThat(targets) + .contains("mailto:oliver.bennett@email.com") + .contains("tel:+447700900123") + .contains("https://linkedin.com/in/oliverbennett-architect"); + } + + @Test + void aProjectTitleBecomesALinkWhenItsEntryCarriesOne() throws Exception { + EntriesSection linked = new EntriesSection("SELECTED PROJECTS", List.of( + CvEntry.builder("Harbour Point") + .subtitle("Mixed-Use Regeneration") + .place("Bristol") + .icon("building") + .link("https://example.test/harbour-point") + .body("Mixed-use development delivering 142 homes.") + .build())); + + List targets = linkTargets( + render(withSection(Slot.MAIN, linked, "SELECTED PROJECTS"))); + assertThat(targets).contains("https://example.test/harbour-point"); + } + + @Test + void anEntryWithoutAMarkIsDrawnWithoutOne() throws Exception { + EntriesSection unmarked = new EntriesSection("ADDITIONAL INFORMATION", List.of( + CvEntry.builder("Right to Work:").body("United Kingdom").build())); + + String text = textOf(render(withSection(Slot.SIDEBAR, unmarked, + "ADDITIONAL INFORMATION"))); + assertThat(text).contains("Right to Work:").contains("United Kingdom"); + } + + @Test + void anUnknownMarkIsReportedAsADataError() { + EntriesSection wrong = new EntriesSection("ADDITIONAL INFORMATION", List.of( + CvEntry.builder("Languages:").icon("telescope").body("English").build())); + + assertThatThrownBy(() -> render(withSection(Slot.SIDEBAR, wrong, + "ADDITIONAL INFORMATION"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("telescope") + .hasMessageContaining("badge"); + } + + @Test + void aDocumentWithNothingButAnIdentityStillRenders() throws Exception { + // Every berth empty: no heading, no divider, and nothing drawn but + // the monogram, its rule and the masthead. + render(new CvDocument(TerracottaRailFixtures.identity(), List.of())); + } + + @Test + void anIdentityWithoutLinksDrawsTheThreeChannelsAndNothingElse() throws Exception { + // The contact triple is non-blank by construction, so the three + // channels are always there; the links are what a document may omit. + CvIdentity noLinks = new CvIdentity(CvName.of("Ada", "Lovelace"), "Analyst", + new Contact("+44 20 7946 0000", "ada@example.test", "London, UK"), + List.of(), Optional.empty()); + + // A run that wraps or breaks at a space carries one annotation per + // piece, so it is the set of targets that is asserted, not the count. + byte[] pdfBytes = render(new CvDocument(noLinks, List.of())); + assertThat(Set.copyOf(linkTargets(pdfBytes))) + .containsExactlyInAnyOrder("mailto:ada@example.test", "tel:+442079460000"); + assertThat(textOf(pdfBytes).lines().map(String::strip).toList()).contains("AL"); + } + + @Test + void refusesACvLongerThanTheSheet() { + // The two columns are one row, and a row is atomic: a CV that outgrows + // the sheet raises rather than losing an entry to a silent cap. + List many = new ArrayList<>(); + for (int index = 0; index < 14; index++) { + many.add(CvEntry.builder("Senior Architect " + index) + .subtitle("Northline Studio, Bristol, UK") + .date("2021") + .body("Lead design packages from concept through to detailed design.") + .build()); + } + CvDocument tooLong = withSection(Slot.MAIN, + new EntriesSection("PROFESSIONAL EXPERIENCE", many), + "PROFESSIONAL EXPERIENCE"); + + assertThatThrownBy(() -> render(tooLong)) + .hasMessageContaining("Body"); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailVisualParityTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailVisualParityTest.java new file mode 100644 index 000000000..e4833448e --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailVisualParityTest.java @@ -0,0 +1,45 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +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 TerracottaRail} — the canonical + * one-page CV against a checked-in baseline. + * + *

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

+ */ +class TerracottaRailVisualParityTest { + + private static final Path BASELINE_ROOT = Path.of( + "src", "test", "resources", "visual-baselines", "cv-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() + .pageSize(DocumentPageSize.A4) + .margin(0f, 0f, 0f, 0f) + .create()) { + TerracottaRail.create().compose(document, TerracottaRailFixtures.canonicalCv()); + pdfBytes = document.toPdfBytes(); + } + + PdfVisualRegression.standard() + .baselineRoot(BASELINE_ROOT) + .perPixelTolerance(PER_PIXEL_TOLERANCE) + .mismatchedPixelBudget(PIXEL_DIFF_BUDGET) + .assertMatchesBaseline("terracotta_rail", pdfBytes); + } +} diff --git a/qa/src/test/resources/layout-snapshots/canonical-templates/cv-v2/terracotta_rail_layout.json b/qa/src/test/resources/layout-snapshots/canonical-templates/cv-v2/terracotta_rail_layout.json new file mode 100644 index 000000000..4e6f57818 --- /dev/null +++ b/qa/src/test/resources/layout-snapshots/canonical-templates/cv-v2/terracotta_rail_layout.json @@ -0,0 +1,4637 @@ +{ + "formatVersion" : "2.0", + "canvas" : { + "pageWidth" : 595.276, + "pageHeight" : 841.89, + "innerWidth" : 595.276, + "innerHeight" : 841.89, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, + "totalPages" : 1, + "nodes" : [ { + "path" : "TerracottaRailCv[0]", + "entityName" : "TerracottaRailCv", + "entityKind" : "ContainerNode", + "parentPath" : null, + "childIndex" : 0, + "depth" : 1, + "layer" : 1, + "computedX" : 0.0, + "computedY" : 47.306, + "placementX" : 0.0, + "placementY" : 47.306, + "placementWidth" : 595.276, + "placementHeight" : 794.584, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 595.276, + "contentHeight" : 794.584, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]", + "entityName" : "Body", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]", + "childIndex" : 0, + "depth" : 2, + "layer" : 2, + "computedX" : 0.0, + "computedY" : 47.306, + "placementX" : 0.0, + "placementY" : 47.306, + "placementWidth" : 595.276, + "placementHeight" : 794.584, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 595.276, + "contentHeight" : 794.584, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "entityName" : "Sidebar", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 0.0, + "computedY" : 100.839, + "placementX" : 0.0, + "placementY" : 100.839, + "placementWidth" : 183.345, + "placementHeight" : 741.051, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 183.345, + "contentHeight" : 741.051, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 20.0, + "right" : 11.4, + "bottom" : 5.0, + "left" : 25.4 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Monogram[0]", + "entityName" : "Monogram", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 774.19, + "placementX" : 25.4, + "placementY" : 774.19, + "placementWidth" : 48.78, + "placementHeight" : 47.7, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 48.78, + "contentHeight" : 47.7, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 8.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/MonogramRule[1]", + "entityName" : "MonogramRule", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 764.79, + "placementX" : 25.4, + "placementY" : 764.79, + "placementWidth" : 48.0, + "placementHeight" : 1.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 48.0, + "contentHeight" : 1.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 10.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]", + "entityName" : "Contact", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 684.99, + "placementX" : 25.4, + "placementY" : 684.99, + "placementWidth" : 138.313, + "placementHeight" : 69.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 138.313, + "contentHeight" : 69.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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]/Contact_0[0]", + "entityName" : "Contact_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 741.84, + "placementX" : 25.4, + "placementY" : 741.84, + "placementWidth" : 113.88, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 113.88, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]/Contact_1[1]", + "entityName" : "Contact_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 724.39, + "placementX" : 25.4, + "placementY" : 724.39, + "placementWidth" : 85.646, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 85.646, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]/Contact_2[2]", + "entityName" : "Contact_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 706.94, + "placementX" : 25.4, + "placementY" : 706.94, + "placementWidth" : 106.253, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 106.253, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]/Contact_3[3]", + "entityName" : "Contact_3", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Contact[2]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 689.49, + "placementX" : 25.4, + "placementY" : 689.49, + "placementWidth" : 138.313, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 138.313, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Divider_AfterContact[3]", + "entityName" : "Divider_AfterContact", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 675.99, + "placementX" : 25.4, + "placementY" : 675.99, + "placementWidth" : 146.545, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 1.0, + "margin" : { + "top" : 8.0, + "right" : 0.0, + "bottom" : 8.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "entityName" : "CoreCompetencies", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 4, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 457.063, + "placementX" : 25.4, + "placementY" : 457.063, + "placementWidth" : 106.829, + "placementHeight" : 210.926, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 106.829, + "contentHeight" : 210.926, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Heading_CORECOMPETENCIES[0]", + "entityName" : "Heading_CORECOMPETENCIES", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 657.463, + "placementX" : 25.4, + "placementY" : 657.463, + "placementWidth" : 101.553, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 101.553, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 9.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_0[1]", + "entityName" : "Competency_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 635.513, + "placementX" : 25.4, + "placementY" : 635.513, + "placementWidth" : 87.369, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 87.369, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_1[2]", + "entityName" : "Competency_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 619.563, + "placementX" : 25.4, + "placementY" : 619.563, + "placementWidth" : 95.358, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 95.358, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_2[3]", + "entityName" : "Competency_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 603.613, + "placementX" : 25.4, + "placementY" : 603.613, + "placementWidth" : 91.068, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 91.068, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_3[4]", + "entityName" : "Competency_3", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 587.663, + "placementX" : 25.4, + "placementY" : 587.663, + "placementWidth" : 83.888, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 83.888, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_4[5]", + "entityName" : "Competency_4", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 571.713, + "placementX" : 25.4, + "placementY" : 571.713, + "placementWidth" : 30.716, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 30.716, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_5[6]", + "entityName" : "Competency_5", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 555.763, + "placementX" : 25.4, + "placementY" : 555.763, + "placementWidth" : 47.546, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.546, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_6[7]", + "entityName" : "Competency_6", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 7, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 539.813, + "placementX" : 25.4, + "placementY" : 539.813, + "placementWidth" : 77.803, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 77.803, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_7[8]", + "entityName" : "Competency_7", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 8, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 523.863, + "placementX" : 25.4, + "placementY" : 523.863, + "placementWidth" : 90.141, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 90.141, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_8[9]", + "entityName" : "Competency_8", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 9, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 507.913, + "placementX" : 25.4, + "placementY" : 507.913, + "placementWidth" : 71.271, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 71.271, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_9[10]", + "entityName" : "Competency_9", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 10, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 491.963, + "placementX" : 25.4, + "placementY" : 491.963, + "placementWidth" : 106.829, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 106.829, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_10[11]", + "entityName" : "Competency_10", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 11, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 476.013, + "placementX" : 25.4, + "placementY" : 476.013, + "placementWidth" : 81.579, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 81.579, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]/Competency_11[12]", + "entityName" : "Competency_11", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/CoreCompetencies[4]", + "childIndex" : 12, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 460.063, + "placementX" : 25.4, + "placementY" : 460.063, + "placementWidth" : 86.913, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 86.913, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Divider_AfterCompetencies[5]", + "entityName" : "Divider_AfterCompetencies", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 5, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 448.063, + "placementX" : 25.4, + "placementY" : 448.063, + "placementWidth" : 146.545, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 1.0, + "margin" : { + "top" : 8.0, + "right" : 0.0, + "bottom" : 8.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "entityName" : "Software", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 6, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 307.487, + "placementX" : 25.4, + "placementY" : 307.487, + "placementWidth" : 72.249, + "placementHeight" : 132.576, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 72.249, + "contentHeight" : 132.576, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Heading_SOFTWARE[0]", + "entityName" : "Heading_SOFTWARE", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 429.537, + "placementX" : 25.4, + "placementY" : 429.537, + "placementWidth" : 52.0, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 52.0, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/AccentLine_SOFTWARE[1]", + "entityName" : "AccentLine_SOFTWARE", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 424.637, + "placementX" : 25.4, + "placementY" : 424.637, + "placementWidth" : 20.0, + "placementHeight" : 1.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 20.0, + "contentHeight" : 1.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_0[2]", + "entityName" : "Software_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 406.187, + "placementX" : 25.4, + "placementY" : 406.187, + "placementWidth" : 29.916, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 29.916, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_1[3]", + "entityName" : "Software_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 390.237, + "placementX" : 25.4, + "placementY" : 390.237, + "placementWidth" : 46.746, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 46.746, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_2[4]", + "entityName" : "Software_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 374.287, + "placementX" : 25.4, + "placementY" : 374.287, + "placementWidth" : 32.275, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 32.275, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_3[5]", + "entityName" : "Software_3", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 358.337, + "placementX" : 25.4, + "placementY" : 358.337, + "placementWidth" : 46.78, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 46.78, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_4[6]", + "entityName" : "Software_4", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 342.387, + "placementX" : 25.4, + "placementY" : 342.387, + "placementWidth" : 69.999, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 69.999, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_5[7]", + "entityName" : "Software_5", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 7, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 326.437, + "placementX" : 25.4, + "placementY" : 326.437, + "placementWidth" : 50.994, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 50.994, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]/Software_6[8]", + "entityName" : "Software_6", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Software[6]", + "childIndex" : 8, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 310.487, + "placementX" : 25.4, + "placementY" : 310.487, + "placementWidth" : 72.249, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 72.249, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Divider_AfterSoftware[7]", + "entityName" : "Divider_AfterSoftware", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 7, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 298.487, + "placementX" : 25.4, + "placementY" : 298.487, + "placementWidth" : 146.545, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 1.0, + "margin" : { + "top" : 8.0, + "right" : 0.0, + "bottom" : 8.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]", + "entityName" : "Certifications", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 8, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 237.661, + "placementX" : 25.4, + "placementY" : 237.661, + "placementWidth" : 105.102, + "placementHeight" : 52.826, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 105.102, + "contentHeight" : 52.826, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]/Heading_CERTIFICATIONS[0]", + "entityName" : "Heading_CERTIFICATIONS", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 279.961, + "placementX" : 25.4, + "placementY" : 279.961, + "placementWidth" : 77.553, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 77.553, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]/AccentLine_CERTIFICATIONS[1]", + "entityName" : "AccentLine_CERTIFICATIONS", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 275.061, + "placementX" : 25.4, + "placementY" : 275.061, + "placementWidth" : 20.0, + "placementHeight" : 1.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 20.0, + "contentHeight" : 1.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]/Certification_0[2]", + "entityName" : "Certification_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 256.611, + "placementX" : 25.4, + "placementY" : 256.611, + "placementWidth" : 105.102, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 105.102, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]/Certification_1[3]", + "entityName" : "Certification_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Certifications[8]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 240.661, + "placementX" : 25.4, + "placementY" : 240.661, + "placementWidth" : 102.658, + "placementHeight" : 12.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 102.658, + "contentHeight" : 12.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/Divider_AfterCertifications[9]", + "entityName" : "Divider_AfterCertifications", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 9, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 228.661, + "placementX" : 25.4, + "placementY" : 228.661, + "placementWidth" : 146.545, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 1.0, + "margin" : { + "top" : 8.0, + "right" : 0.0, + "bottom" : 8.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]", + "entityName" : "AdditionalInformation", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]", + "childIndex" : 10, + "depth" : 4, + "layer" : 4, + "computedX" : 25.4, + "computedY" : 105.839, + "placementX" : 25.4, + "placementY" : 105.839, + "placementWidth" : 146.545, + "placementHeight" : 114.822, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 114.822, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/Heading_ADDITIONALINFORMATION[0]", + "entityName" : "Heading_ADDITIONALINFORMATION", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 210.134, + "placementX" : 25.4, + "placementY" : 210.134, + "placementWidth" : 131.334, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 131.334, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 9.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]", + "entityName" : "AddInfo_0Stack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 169.293, + "placementX" : 25.4, + "placementY" : 169.293, + "placementWidth" : 146.545, + "placementHeight" : 31.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 31.841, + "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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]", + "entityName" : "AddInfo_0Layer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 25.4, + "computedY" : 169.293, + "placementX" : 25.4, + "placementY" : 169.293, + "placementWidth" : 146.545, + "placementHeight" : 31.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 31.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]", + "entityName" : "AddInfo_0", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 25.4, + "computedY" : 169.293, + "placementX" : 25.4, + "placementY" : 169.293, + "placementWidth" : 146.545, + "placementHeight" : 31.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 31.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Icon_0[0]", + "entityName" : "Icon_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 25.4, + "computedY" : 185.134, + "placementX" : 25.4, + "placementY" : 185.134, + "placementWidth" : 16.0, + "placementHeight" : 16.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 16.0, + "contentHeight" : 16.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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]", + "entityName" : "Text_0", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 51.778, + "computedY" : 169.293, + "placementX" : 51.778, + "placementY" : 169.293, + "placementWidth" : 90.753, + "placementHeight" : 31.841, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 90.753, + "contentHeight" : 31.841, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]/Label_0[0]", + "entityName" : "Label_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 191.021, + "placementX" : 51.778, + "placementY" : 191.021, + "placementWidth" : 41.735, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 41.735, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]/Val_0[1]", + "entityName" : "Val_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 180.407, + "placementX" : 51.778, + "placementY" : 180.407, + "placementWidth" : 59.367, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 59.367, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]/Val_0[2]", + "entityName" : "Val_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_0Stack[1]/AddInfo_0Layer[0]/AddInfo_0[0]/Text_0[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 169.793, + "placementX" : 51.778, + "placementY" : 169.793, + "placementWidth" : 90.753, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 90.753, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]", + "entityName" : "AddInfo_1Stack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 141.066, + "placementX" : 25.4, + "placementY" : 141.066, + "placementWidth" : 146.545, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 21.227, + "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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]", + "entityName" : "AddInfo_1Layer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 25.4, + "computedY" : 141.066, + "placementX" : 25.4, + "placementY" : 141.066, + "placementWidth" : 146.545, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 21.227, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]", + "entityName" : "AddInfo_1", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 25.4, + "computedY" : 141.066, + "placementX" : 25.4, + "placementY" : 141.066, + "placementWidth" : 146.545, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 21.227, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]/Icon_1[0]", + "entityName" : "Icon_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 25.4, + "computedY" : 146.293, + "placementX" : 25.4, + "placementY" : 146.293, + "placementWidth" : 16.0, + "placementHeight" : 16.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 16.0, + "contentHeight" : 16.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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]/Text_1[1]", + "entityName" : "Text_1", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 51.778, + "computedY" : 141.066, + "placementX" : 51.778, + "placementY" : 141.066, + "placementWidth" : 60.412, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 60.412, + "contentHeight" : 21.227, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]/Text_1[1]/Label_1[0]", + "entityName" : "Label_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]/Text_1[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 152.18, + "placementX" : 51.778, + "placementY" : 152.18, + "placementWidth" : 55.507, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 55.507, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]/Text_1[1]/Val_1[1]", + "entityName" : "Val_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_1Stack[2]/AddInfo_1Layer[0]/AddInfo_1[0]/Text_1[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 141.566, + "placementX" : 51.778, + "placementY" : 141.566, + "placementWidth" : 60.412, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 60.412, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]", + "entityName" : "AddInfo_2Stack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 25.4, + "computedY" : 112.839, + "placementX" : 25.4, + "placementY" : 112.839, + "placementWidth" : 146.545, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 21.227, + "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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]", + "entityName" : "AddInfo_2Layer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 25.4, + "computedY" : 112.839, + "placementX" : 25.4, + "placementY" : 112.839, + "placementWidth" : 146.545, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 21.227, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]", + "entityName" : "AddInfo_2", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 25.4, + "computedY" : 112.839, + "placementX" : 25.4, + "placementY" : 112.839, + "placementWidth" : 146.545, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 146.545, + "contentHeight" : 21.227, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]/Icon_2[0]", + "entityName" : "Icon_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 25.4, + "computedY" : 118.066, + "placementX" : 25.4, + "placementY" : 118.066, + "placementWidth" : 16.0, + "placementHeight" : 16.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 16.0, + "contentHeight" : 16.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" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]/Text_2[1]", + "entityName" : "Text_2", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 51.778, + "computedY" : 112.839, + "placementX" : 51.778, + "placementY" : 112.839, + "placementWidth" : 56.358, + "placementHeight" : 21.227, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 56.358, + "contentHeight" : 21.227, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]/Text_2[1]/Label_2[0]", + "entityName" : "Label_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]/Text_2[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 123.953, + "placementX" : 51.778, + "placementY" : 123.953, + "placementWidth" : 44.036, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 44.036, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]/Text_2[1]/Val_2[1]", + "entityName" : "Val_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/Sidebar[0]/AdditionalInformation[10]/AddInfo_2Stack[3]/AddInfo_2Layer[0]/AddInfo_2[0]/Text_2[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 51.778, + "computedY" : 113.339, + "placementX" : 51.778, + "placementY" : 113.339, + "placementWidth" : 56.358, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 56.358, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "entityName" : "MainColumn", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 183.345, + "computedY" : 47.306, + "placementX" : 183.345, + "placementY" : 47.306, + "placementWidth" : 411.931, + "placementHeight" : 794.584, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 411.931, + "contentHeight" : 794.584, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 20.0, + "right" : 24.0, + "bottom" : 5.0, + "left" : 22.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Masthead[0]", + "entityName" : "Masthead", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 759.69, + "placementX" : 205.345, + "placementY" : 759.69, + "placementWidth" : 321.748, + "placementHeight" : 62.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 321.748, + "contentHeight" : 62.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Masthead[0]/FullName[0]", + "entityName" : "FullName", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Masthead[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 788.29, + "placementX" : 205.345, + "placementY" : 788.29, + "placementWidth" : 321.748, + "placementHeight" : 33.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 321.748, + "contentHeight" : 33.6, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 8.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Masthead[0]/JobTitle[1]", + "entityName" : "JobTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Masthead[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 764.69, + "placementX" : 205.345, + "placementY" : 764.69, + "placementWidth" : 169.468, + "placementHeight" : 15.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 169.468, + "contentHeight" : 15.6, + "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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Divider_AfterMasthead[1]", + "entityName" : "Divider_AfterMasthead", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 754.69, + "placementX" : 205.345, + "placementY" : 754.69, + "placementWidth" : 365.931, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 1.0, + "margin" : { + "top" : 4.0, + "right" : 0.0, + "bottom" : 6.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]", + "entityName" : "Summary", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 654.443, + "placementX" : 205.345, + "placementY" : 654.443, + "placementWidth" : 346.356, + "placementHeight" : 94.246, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 346.356, + "contentHeight" : 94.246, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]/Heading_PROFESSIONALSUMMARY[0]", + "entityName" : "Heading_PROFESSIONALSUMMARY", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 738.163, + "placementX" : 205.345, + "placementY" : 738.163, + "placementWidth" : 133.212, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 133.212, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]/AccentLine_PROFESSIONALSUMMARY[1]", + "entityName" : "AccentLine_PROFESSIONALSUMMARY", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 733.263, + "placementX" : 205.345, + "placementY" : 733.263, + "placementWidth" : 22.0, + "placementHeight" : 1.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.0, + "contentHeight" : 1.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]/SummaryPara_0[2]", + "entityName" : "SummaryPara_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 705.823, + "placementX" : 205.345, + "placementY" : 705.823, + "placementWidth" : 323.91, + "placementHeight" : 21.94, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 323.91, + "contentHeight" : 21.94, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]/SummaryPara_1[3]", + "entityName" : "SummaryPara_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 681.383, + "placementX" : 205.345, + "placementY" : 681.383, + "placementWidth" : 346.356, + "placementHeight" : 21.94, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 346.356, + "contentHeight" : 21.94, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]/SummaryPara_2[4]", + "entityName" : "SummaryPara_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Summary[2]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 656.943, + "placementX" : 205.345, + "placementY" : 656.943, + "placementWidth" : 342.787, + "placementHeight" : 21.94, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 342.787, + "contentHeight" : 21.94, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Divider_AfterSummary[3]", + "entityName" : "Divider_AfterSummary", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 647.443, + "placementX" : 205.345, + "placementY" : 647.443, + "placementWidth" : 365.931, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 1.0, + "margin" : { + "top" : 6.0, + "right" : 0.0, + "bottom" : 6.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]", + "entityName" : "Experience", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 4, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 303.313, + "placementX" : 205.345, + "placementY" : 303.313, + "placementWidth" : 365.921, + "placementHeight" : 337.631, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.921, + "contentHeight" : 337.631, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/Heading_PROFESSIONALEXPERIENCE[0]", + "entityName" : "Heading_PROFESSIONALEXPERIENCE", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 630.417, + "placementX" : 205.345, + "placementY" : 630.417, + "placementWidth" : 143.475, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 143.475, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 9.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]", + "entityName" : "ExperienceEntry_0", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 497.072, + "placementX" : 205.345, + "placementY" : 497.072, + "placementWidth" : 365.921, + "placementHeight" : 124.345, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.921, + "contentHeight" : 124.345, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 9.0, + "left" : 13.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/RoleLine_0[0]", + "entityName" : "RoleLine_0", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 609.652, + "placementX" : 218.345, + "placementY" : 609.652, + "placementWidth" : 352.921, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 352.921, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/RoleLine_0[0]/RoleTable_0[0]", + "entityName" : "RoleTable_0", + "entityKind" : "TableNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/RoleLine_0[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 218.345, + "computedY" : 609.652, + "placementX" : 218.345, + "placementY" : 609.652, + "placementWidth" : 352.921, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 352.921, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/RoleLine_0[0]/RoleMarker_0[1]", + "entityName" : "RoleMarker_0", + "entityKind" : "EllipseNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/RoleLine_0[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 202.245, + "computedY" : 615.217, + "placementX" : 202.245, + "placementY" : 615.217, + "placementWidth" : 6.2, + "placementHeight" : 6.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 6.2, + "contentHeight" : 6.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/Employer_0[1]", + "entityName" : "Employer_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 599.332, + "placementX" : 218.345, + "placementY" : 599.332, + "placementWidth" : 100.783, + "placementHeight" : 10.32, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 100.783, + "contentHeight" : 10.32, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]/Highlights_0[2]", + "entityName" : "Highlights_0", + "entityKind" : "ListNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_0[1]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 506.072, + "placementX" : 218.345, + "placementY" : 506.072, + "placementWidth" : 348.171, + "placementHeight" : 90.76, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 348.171, + "contentHeight" : 90.76, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]", + "entityName" : "ExperienceEntry_1", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 384.197, + "placementX" : 205.345, + "placementY" : 384.197, + "placementWidth" : 365.921, + "placementHeight" : 112.875, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.921, + "contentHeight" : 112.875, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 9.0, + "left" : 13.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/RoleLine_1[0]", + "entityName" : "RoleLine_1", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 485.307, + "placementX" : 218.345, + "placementY" : 485.307, + "placementWidth" : 352.921, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 352.921, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/RoleLine_1[0]/RoleTable_1[0]", + "entityName" : "RoleTable_1", + "entityKind" : "TableNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/RoleLine_1[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 218.345, + "computedY" : 485.307, + "placementX" : 218.345, + "placementY" : 485.307, + "placementWidth" : 352.921, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 352.921, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/RoleLine_1[0]/RoleMarker_1[1]", + "entityName" : "RoleMarker_1", + "entityKind" : "EllipseNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/RoleLine_1[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 202.245, + "computedY" : 490.872, + "placementX" : 202.245, + "placementY" : 490.872, + "placementWidth" : 6.2, + "placementHeight" : 6.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 6.2, + "contentHeight" : 6.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/Employer_1[1]", + "entityName" : "Employer_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 474.987, + "placementX" : 218.345, + "placementY" : 474.987, + "placementWidth" : 111.654, + "placementHeight" : 10.32, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 111.654, + "contentHeight" : 10.32, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]/Highlights_1[2]", + "entityName" : "Highlights_1", + "entityKind" : "ListNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_1[2]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 393.197, + "placementX" : 218.345, + "placementY" : 393.197, + "placementWidth" : 349.599, + "placementHeight" : 79.29, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 349.599, + "contentHeight" : 79.29, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]", + "entityName" : "ExperienceEntry_2", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 303.313, + "placementX" : 205.345, + "placementY" : 303.313, + "placementWidth" : 365.921, + "placementHeight" : 80.885, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.921, + "contentHeight" : 80.885, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 13.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/RoleLine_2[0]", + "entityName" : "RoleLine_2", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 372.433, + "placementX" : 218.345, + "placementY" : 372.433, + "placementWidth" : 352.921, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 352.921, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/RoleLine_2[0]/RoleTable_2[0]", + "entityName" : "RoleTable_2", + "entityKind" : "TableNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/RoleLine_2[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 218.345, + "computedY" : 372.433, + "placementX" : 218.345, + "placementY" : 372.433, + "placementWidth" : 352.921, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 352.921, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/RoleLine_2[0]/RoleMarker_2[1]", + "entityName" : "RoleMarker_2", + "entityKind" : "EllipseNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/RoleLine_2[0]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 202.245, + "computedY" : 377.997, + "placementX" : 202.245, + "placementY" : 377.997, + "placementWidth" : 6.2, + "placementHeight" : 6.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 6.2, + "contentHeight" : 6.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/Employer_2[1]", + "entityName" : "Employer_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 362.113, + "placementX" : 218.345, + "placementY" : 362.113, + "placementWidth" : 123.771, + "placementHeight" : 10.32, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 123.771, + "contentHeight" : 10.32, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 2.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]/Highlights_2[2]", + "entityName" : "Highlights_2", + "entityKind" : "ListNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Experience[4]/ExperienceEntry_2[3]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 218.345, + "computedY" : 303.313, + "placementX" : 218.345, + "placementY" : 303.313, + "placementWidth" : 329.019, + "placementHeight" : 56.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 329.019, + "contentHeight" : 56.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Divider_AfterExperience[5]", + "entityName" : "Divider_AfterExperience", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 5, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 296.313, + "placementX" : 205.345, + "placementY" : 296.313, + "placementWidth" : 365.931, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 1.0, + "margin" : { + "top" : 6.0, + "right" : 0.0, + "bottom" : 6.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "entityName" : "SelectedProjects", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 6, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 139.902, + "placementX" : 205.345, + "placementY" : 139.902, + "placementWidth" : 365.931, + "placementHeight" : 149.91, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 149.91, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Heading_SELECTEDPROJECTS[0]", + "entityName" : "Heading_SELECTEDPROJECTS", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 279.286, + "placementX" : 205.345, + "placementY" : 279.286, + "placementWidth" : 104.79, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.79, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/AccentLine_SELECTEDPROJECTS[1]", + "entityName" : "AccentLine_SELECTEDPROJECTS", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 274.386, + "placementX" : 205.345, + "placementY" : 274.386, + "placementWidth" : 22.0, + "placementHeight" : 1.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.0, + "contentHeight" : 1.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]", + "entityName" : "Project_0Stack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 234.894, + "placementX" : 205.345, + "placementY" : 234.894, + "placementWidth" : 365.931, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 33.992, + "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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]", + "entityName" : "Project_0Layer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 205.345, + "computedY" : 234.894, + "placementX" : 205.345, + "placementY" : 234.894, + "placementWidth" : 365.931, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]", + "entityName" : "Project_0", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 205.345, + "computedY" : 234.894, + "placementX" : 205.345, + "placementY" : 234.894, + "placementWidth" : 365.931, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjIcon_0[0]", + "entityName" : "ProjIcon_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 205.345, + "computedY" : 246.886, + "placementX" : 205.345, + "placementY" : 246.886, + "placementWidth" : 22.0, + "placementHeight" : 22.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.0, + "contentHeight" : 22.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]", + "entityName" : "ProjMeta_0", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 234.619, + "computedY" : 234.894, + "placementX" : 234.619, + "placementY" : 234.894, + "placementWidth" : 98.269, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 98.269, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 8.0, + "bottom" : 0.0, + "left" : 4.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]/ProjTitle_0[0]", + "entityName" : "ProjTitle_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 257.121, + "placementX" : 238.619, + "placementY" : 257.121, + "placementWidth" : 62.344, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 62.344, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 1.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]/ProjSub_0[1]", + "entityName" : "ProjSub_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 246.008, + "placementX" : 238.619, + "placementY" : 246.008, + "placementWidth" : 86.269, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 86.269, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 1.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]/ProjLoc_0[2]", + "entityName" : "ProjLoc_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjMeta_0[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 234.894, + "placementX" : 238.619, + "placementY" : 234.894, + "placementWidth" : 23.986, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 23.986, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjDesc_0[2]", + "entityName" : "ProjDesc_0", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]", + "childIndex" : 2, + "depth" : 8, + "layer" : 8, + "computedX" : 366.354, + "computedY" : 235.446, + "placementX" : 366.354, + "placementY" : 235.446, + "placementWidth" : 187.16, + "placementHeight" : 33.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 187.16, + "contentHeight" : 33.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 10.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjDesc_0[2]/ProjDescText_0[0]", + "entityName" : "ProjDescText_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_0Stack[2]/Project_0Layer[0]/Project_0[0]/ProjDesc_0[2]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 376.354, + "computedY" : 235.446, + "placementX" : 376.354, + "placementY" : 235.446, + "placementWidth" : 177.16, + "placementHeight" : 33.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 177.16, + "contentHeight" : 33.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/ProjDiv_0[3]", + "entityName" : "ProjDiv_0", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 229.894, + "placementX" : 205.345, + "placementY" : 229.894, + "placementWidth" : 365.931, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 1.0, + "margin" : { + "top" : 2.0, + "right" : 0.0, + "bottom" : 2.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]", + "entityName" : "Project_1Stack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 193.902, + "placementX" : 205.345, + "placementY" : 193.902, + "placementWidth" : 365.931, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 33.992, + "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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]", + "entityName" : "Project_1Layer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 205.345, + "computedY" : 193.902, + "placementX" : 205.345, + "placementY" : 193.902, + "placementWidth" : 365.931, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]", + "entityName" : "Project_1", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 205.345, + "computedY" : 193.902, + "placementX" : 205.345, + "placementY" : 193.902, + "placementWidth" : 365.931, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjIcon_1[0]", + "entityName" : "ProjIcon_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 205.345, + "computedY" : 205.894, + "placementX" : 205.345, + "placementY" : 205.894, + "placementWidth" : 22.0, + "placementHeight" : 22.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.0, + "contentHeight" : 22.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]", + "entityName" : "ProjMeta_1", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 234.619, + "computedY" : 193.902, + "placementX" : 234.619, + "placementY" : 193.902, + "placementWidth" : 118.269, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 118.269, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 8.0, + "bottom" : 0.0, + "left" : 4.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]/ProjTitle_1[0]", + "entityName" : "ProjTitle_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 216.129, + "placementX" : 238.619, + "placementY" : 216.129, + "placementWidth" : 88.011, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 88.011, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 1.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]/ProjSub_1[1]", + "entityName" : "ProjSub_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 205.016, + "placementX" : 238.619, + "placementY" : 205.016, + "placementWidth" : 106.269, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 106.269, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 1.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]/ProjLoc_1[2]", + "entityName" : "ProjLoc_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjMeta_1[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 193.902, + "placementX" : 238.619, + "placementY" : 193.902, + "placementWidth" : 17.37, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 17.37, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjDesc_1[2]", + "entityName" : "ProjDesc_1", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]", + "childIndex" : 2, + "depth" : 8, + "layer" : 8, + "computedX" : 366.354, + "computedY" : 194.454, + "placementX" : 366.354, + "placementY" : 194.454, + "placementWidth" : 200.327, + "placementHeight" : 33.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 200.327, + "contentHeight" : 33.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 10.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjDesc_1[2]/ProjDescText_1[0]", + "entityName" : "ProjDescText_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_1Stack[4]/Project_1Layer[0]/Project_1[0]/ProjDesc_1[2]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 376.354, + "computedY" : 194.454, + "placementX" : 376.354, + "placementY" : 194.454, + "placementWidth" : 190.327, + "placementHeight" : 33.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 190.327, + "contentHeight" : 33.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/ProjDiv_1[5]", + "entityName" : "ProjDiv_1", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 188.902, + "placementX" : 205.345, + "placementY" : 188.902, + "placementWidth" : 365.931, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 1.0, + "margin" : { + "top" : 2.0, + "right" : 0.0, + "bottom" : 2.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]", + "entityName" : "Project_2Stack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 141.902, + "placementX" : 205.345, + "placementY" : 141.902, + "placementWidth" : 365.931, + "placementHeight" : 45.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 45.0, + "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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]", + "entityName" : "Project_2Layer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 205.345, + "computedY" : 141.902, + "placementX" : 205.345, + "placementY" : 141.902, + "placementWidth" : 365.931, + "placementHeight" : 45.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 45.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]", + "entityName" : "Project_2", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 205.345, + "computedY" : 141.902, + "placementX" : 205.345, + "placementY" : 141.902, + "placementWidth" : 365.931, + "placementHeight" : 45.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 45.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjIcon_2[0]", + "entityName" : "ProjIcon_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 205.345, + "computedY" : 164.902, + "placementX" : 205.345, + "placementY" : 164.902, + "placementWidth" : 22.0, + "placementHeight" : 22.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.0, + "contentHeight" : 22.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]", + "entityName" : "ProjMeta_2", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 234.619, + "computedY" : 152.91, + "placementX" : 234.619, + "placementY" : 152.91, + "placementWidth" : 99.272, + "placementHeight" : 33.992, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 99.272, + "contentHeight" : 33.992, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 8.0, + "bottom" : 0.0, + "left" : 4.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]/ProjTitle_2[0]", + "entityName" : "ProjTitle_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 175.137, + "placementX" : 238.619, + "placementY" : 175.137, + "placementWidth" : 70.079, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 70.079, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 1.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]/ProjSub_2[1]", + "entityName" : "ProjSub_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 164.024, + "placementX" : 238.619, + "placementY" : 164.024, + "placementWidth" : 87.272, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 87.272, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 1.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]/ProjLoc_2[2]", + "entityName" : "ProjLoc_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjMeta_2[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 238.619, + "computedY" : 152.91, + "placementX" : 238.619, + "placementY" : 152.91, + "placementWidth" : 53.164, + "placementHeight" : 10.114, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 53.164, + "contentHeight" : 10.114, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjDesc_2[2]", + "entityName" : "ProjDesc_2", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]", + "childIndex" : 2, + "depth" : 8, + "layer" : 8, + "computedX" : 366.354, + "computedY" : 141.902, + "placementX" : 366.354, + "placementY" : 141.902, + "placementWidth" : 166.176, + "placementHeight" : 45.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 166.176, + "contentHeight" : 45.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" : 10.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjDesc_2[2]/ProjDescText_2[0]", + "entityName" : "ProjDescText_2", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/SelectedProjects[6]/Project_2Stack[6]/Project_2Layer[0]/Project_2[0]/ProjDesc_2[2]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 376.354, + "computedY" : 141.902, + "placementX" : 376.354, + "placementY" : 141.902, + "placementWidth" : 156.176, + "placementHeight" : 45.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 156.176, + "contentHeight" : 45.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Divider_AfterProjects[7]", + "entityName" : "Divider_AfterProjects", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 7, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 132.902, + "placementX" : 205.345, + "placementY" : 132.902, + "placementWidth" : 365.931, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 1.0, + "margin" : { + "top" : 6.0, + "right" : 0.0, + "bottom" : 6.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]", + "entityName" : "Education", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]", + "childIndex" : 8, + "depth" : 4, + "layer" : 4, + "computedX" : 205.345, + "computedY" : 52.306, + "placementX" : 205.345, + "placementY" : 52.306, + "placementWidth" : 365.931, + "placementHeight" : 74.096, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 74.096, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/Heading_EDUCATION[0]", + "entityName" : "Heading_EDUCATION", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 115.876, + "placementX" : 205.345, + "placementY" : 115.876, + "placementWidth" : 60.632, + "placementHeight" : 10.526, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 60.632, + "contentHeight" : 10.526, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 3.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/AccentLine_EDUCATION[1]", + "entityName" : "AccentLine_EDUCATION", + "entityKind" : "LineNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 110.976, + "placementX" : 205.345, + "placementY" : 110.976, + "placementWidth" : 22.0, + "placementHeight" : 1.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.0, + "contentHeight" : 1.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 5.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]", + "entityName" : "EducationBandStack", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 205.345, + "computedY" : 52.306, + "placementX" : 205.345, + "placementY" : 52.306, + "placementWidth" : 365.931, + "placementHeight" : 53.17, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 53.17, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]", + "entityName" : "EducationBandLayer", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 205.345, + "computedY" : 52.306, + "placementX" : 205.345, + "placementY" : 52.306, + "placementWidth" : 365.931, + "placementHeight" : 53.17, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 53.17, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]", + "entityName" : "EducationBand", + "entityKind" : "RowNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 205.345, + "computedY" : 52.306, + "placementX" : 205.345, + "placementY" : 52.306, + "placementWidth" : 365.931, + "placementHeight" : 53.17, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 365.931, + "contentHeight" : 53.17, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]", + "entityName" : "EducationEntries", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 205.345, + "computedY" : 52.306, + "placementX" : 205.345, + "placementY" : 52.306, + "placementWidth" : 256.142, + "placementHeight" : 53.17, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 256.142, + "contentHeight" : 53.17, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 8.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]", + "entityName" : "EducationEntry_0", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 205.345, + "computedY" : 74.391, + "placementX" : 205.345, + "placementY" : 74.391, + "placementWidth" : 248.142, + "placementHeight" : 31.085, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 248.142, + "contentHeight" : 31.085, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 9.0, + "left" : 13.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]/EduLine_0[0]", + "entityName" : "EduLine_0", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 218.345, + "computedY" : 93.711, + "placementX" : 218.345, + "placementY" : 93.711, + "placementWidth" : 235.142, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 235.142, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]/EduLine_0[0]/EduTable_0[0]", + "entityName" : "EduTable_0", + "entityKind" : "TableNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]/EduLine_0[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 218.345, + "computedY" : 93.711, + "placementX" : 218.345, + "placementY" : 93.711, + "placementWidth" : 235.142, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 235.142, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]/EduLine_0[0]/EduMarker_0[1]", + "entityName" : "EduMarker_0", + "entityKind" : "EllipseNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]/EduLine_0[0]", + "childIndex" : 1, + "depth" : 11, + "layer" : 11, + "computedX" : 202.245, + "computedY" : 99.276, + "placementX" : 202.245, + "placementY" : 99.276, + "placementWidth" : 6.2, + "placementHeight" : 6.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 6.2, + "contentHeight" : 6.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]/Institution_0[1]", + "entityName" : "Institution_0", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_0[0]", + "childIndex" : 1, + "depth" : 10, + "layer" : 10, + "computedX" : 218.345, + "computedY" : 83.391, + "placementX" : 218.345, + "placementY" : 83.391, + "placementWidth" : 78.604, + "placementHeight" : 10.32, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 78.604, + "contentHeight" : 10.32, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]", + "entityName" : "EducationEntry_1", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 205.345, + "computedY" : 52.306, + "placementX" : 205.345, + "placementY" : 52.306, + "placementWidth" : 248.142, + "placementHeight" : 22.085, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 248.142, + "contentHeight" : 22.085, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 13.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]/EduLine_1[0]", + "entityName" : "EduLine_1", + "entityKind" : "LayerStackNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]", + "childIndex" : 0, + "depth" : 10, + "layer" : 10, + "computedX" : 218.345, + "computedY" : 62.626, + "placementX" : 218.345, + "placementY" : 62.626, + "placementWidth" : 235.142, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 235.142, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]/EduLine_1[0]/EduTable_1[0]", + "entityName" : "EduTable_1", + "entityKind" : "TableNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]/EduLine_1[0]", + "childIndex" : 0, + "depth" : 11, + "layer" : 11, + "computedX" : 218.345, + "computedY" : 62.626, + "placementX" : 218.345, + "placementY" : 62.626, + "placementWidth" : 235.142, + "placementHeight" : 11.765, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 235.142, + "contentHeight" : 11.765, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]/EduLine_1[0]/EduMarker_1[1]", + "entityName" : "EduMarker_1", + "entityKind" : "EllipseNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]/EduLine_1[0]", + "childIndex" : 1, + "depth" : 11, + "layer" : 11, + "computedX" : 202.245, + "computedY" : 68.191, + "placementX" : 202.245, + "placementY" : 68.191, + "placementWidth" : 6.2, + "placementHeight" : 6.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 6.2, + "contentHeight" : 6.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" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]/Institution_1[1]", + "entityName" : "Institution_1", + "entityKind" : "ParagraphNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationEntries[0]/EducationEntry_1[1]", + "childIndex" : 1, + "depth" : 10, + "layer" : 10, + "computedX" : 218.345, + "computedY" : 52.306, + "placementX" : 218.345, + "placementY" : 52.306, + "placementWidth" : 120.056, + "placementHeight" : 10.32, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 120.056, + "contentHeight" : 10.32, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]/EducationBandRight[1]", + "entityName" : "EducationBandRight", + "entityKind" : "SectionNode", + "parentPath" : "TerracottaRailCv[0]/Body[0]/MainColumn[1]/Education[8]/EducationBandStack[2]/EducationBandLayer[0]/EducationBand[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 461.496, + "computedY" : 105.476, + "placementX" : 461.496, + "placementY" : 105.476, + "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 + } + } ] +} diff --git a/qa/src/test/resources/visual-baselines/cv-v2-layered/terracotta_rail-page-0.png b/qa/src/test/resources/visual-baselines/cv-v2-layered/terracotta_rail-page-0.png new file mode 100644 index 000000000..67030d567 Binary files /dev/null and b/qa/src/test/resources/visual-baselines/cv-v2-layered/terracotta_rail-page-0.png differ diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRail.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRail.java new file mode 100644 index 000000000..6b20a5834 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRail.java @@ -0,0 +1,219 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.components.SectionLookup; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.data.CvSection; +import com.demcha.compose.document.templates.cv.data.EntriesSection; +import com.demcha.compose.document.templates.cv.data.ParagraphSection; +import com.demcha.compose.document.templates.cv.data.SkillsSection; + +import java.util.List; +import java.util.Objects; + +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_WEIGHT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_WEIGHT; + +/** + * Terracotta Rail — a one-page CV in two columns: a narrow column carrying a + * serif monogram over a terracotta rule, the contact channels behind their + * marks, two bulleted lists and a block of closing facts, beside a wide + * column carrying a letter-spaced masthead, the summary, the roles held on a + * ringed rail, a projects grid and the degrees. + * + *

The columns are divided by a hairline the sidebar carries as its own + * right border, so it is as tall as the sidebar rather than as tall as the + * page. The preset paints nothing: the sheet is drawn on white paper and + * leaves the page size and the margin to the caller — the geometry follows + * the page's own width, so another page rescales rather than breaks.

+ * + *

One page, and what happens past it

+ * + *

The two columns are a single row, and a row is atomic — it cannot be + * split — so a CV longer than the sheet does not flow onto a second page: + * composing it raises {@code AtomicNodeTooLargeException}, naming the node + * and the height it needed. It draws no cap of its own, because a CV that + * quietly loses a job is worse than one that refuses to compose. + * {@link TimelineMinimal} is the preset in this package that splits its own + * columns across pages.

+ * + *

How a document reaches its berth

+ * + *

The columns are fixed, so the preset assigns each section a berth by + * title rather than reading {@link + * com.demcha.compose.document.templates.cv.data.Slot}. It has eight:

+ * + *
    + *
  • competencies and software, {@link SkillsSection}s; certifications, + * an {@link EntriesSection} of one-line entries; and the closing + * facts, an {@link EntriesSection} whose bodies are one line per + * value — in the sidebar;
  • + *
  • the summary, a {@link ParagraphSection} of one paragraph per line; + * experience, projects and education, {@link EntriesSection}s — in the + * main column.
  • + *
+ * + *

A section whose title matches no berth is not drawn, a berth with no + * section takes its heading and the divider above it with it, and a berth is + * filled by the first section that names it. The two bulleted lists are + * skills without levels: this design writes them as plain lines, so a rating + * would have nowhere to go — one takes a terracotta square and no dash under + * its heading, the other a disc and a dash, which is how the sheet tells two + * lists of one-liners apart.

+ * + *

The monogram, the marks and the links

+ * + *

The monogram is drawn from the name's own initials rather than a field + * of its own: a document states its name once, and a monogram that could + * disagree with it would be a second place to keep true.

+ * + *

Each fact and each project takes the mark its entry names in + * {@code CvEntry.icon()}, from this preset's own vocabulary — + * {@code globe}, {@code badge}, {@code clock}, {@code building}, + * {@code hotel}, {@code house} — and an unknown token is reported as a data + * error naming the set. An entry with no token is drawn without a mark, and + * a project title becomes a link when its entry carries + * {@code CvEntry.link()}.

+ * + *

The email, the phone and each link are reachable from the PDF, with the + * {@code mailto:} and {@code tel:} targets built from the values. A link is + * set smaller than a channel above it, because a URL is one long token that + * cannot be broken and is the line that outgrows this column first; it takes + * the mark of the network it points at, or a globe.

+ * + *

Fonts

+ * + *

The sheet is set in Lato, with PT Serif for the monogram. The templates + * artifact carries neither — register them on the session, or the engine + * substitutes and the measured geometry here no longer matches the type + * sitting in it.

+ * + *

Usage:

+ *
{@code
+ * DocumentTemplate template = TerracottaRail.create();
+ * template.compose(session, cv);
+ * }
+ * + * @since 2.2.3 + */ +public final class TerracottaRail { + + /** Stable identifier of this preset. */ + public static final String ID = "terracotta-rail"; + + /** Human-readable name of this preset. */ + public static final String DISPLAY_NAME = "Terracotta Rail"; + + /** + * The margin this preset expects: none. Both columns run to the paper + * edge and each carries its own padding. + */ + public static final double RECOMMENDED_MARGIN = 0.0; + + private static final List COMPETENCY_KEYS = + List.of("competencies", "competency", "skills", "expertise"); + private static final List SOFTWARE_KEYS = + List.of("software", "tools", "stack"); + private static final List CERTIFICATION_KEYS = + List.of("certifications", "certification", "licences", "licenses"); + private static final List FACT_KEYS = + List.of("additional", "details", "other"); + private static final List SUMMARY_KEYS = + List.of("summary", "profile", "about"); + private static final List EXPERIENCE_KEYS = + List.of("experience", "employment", "work history", "career"); + private static final List PROJECT_KEYS = + List.of("projects", "project", "portfolio", "selected work"); + private static final List EDUCATION_KEYS = + List.of("education", "qualifications"); + + private TerracottaRail() { + } + + /** + * Creates the template. + * + * @return a template composing a {@link CvDocument} + */ + 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, CvDocument doc) { + Objects.requireNonNull(document, "document"); + Objects.requireNonNull(doc, "doc"); + List sections = doc.sections(); + + // Software first, and the competency berth then skips whatever it + // claimed: a section titled for both — "Software Skills" — would + // otherwise fill the two berths and be drawn twice. + SkillsSection software = berth(sections, SkillsSection.class, SOFTWARE_KEYS); + SkillsSection competencies = + berthExcept(sections, SkillsSection.class, COMPETENCY_KEYS, software); + EntriesSection certifications = + berth(sections, EntriesSection.class, CERTIFICATION_KEYS); + EntriesSection facts = berth(sections, EntriesSection.class, FACT_KEYS); + ParagraphSection summary = berth(sections, ParagraphSection.class, SUMMARY_KEYS); + EntriesSection experience = berth(sections, EntriesSection.class, EXPERIENCE_KEYS); + EntriesSection projects = berth(sections, EntriesSection.class, PROJECT_KEYS); + EntriesSection education = berth(sections, EntriesSection.class, EDUCATION_KEYS); + + document.pageFlow(page -> page + .name("TerracottaRailCv") + .spacing(0) + .addRow("Body", row -> { + row.name("Body"); + row.spacing(0); + row.weights(SIDEBAR_WEIGHT, MAIN_WEIGHT); + row.addSection("Sidebar", side -> + TerracottaRailAside.compose(side, doc.identity(), competencies, + software, certifications, facts)); + row.addSection("MainColumn", main -> + TerracottaRailMain.compose(main, doc.identity(), summary, + experience, projects, education)); + })); + } + + /** + * The first section of the wanted shape whose title names this berth, + * or {@code null} when the document fills no such berth. + */ + private static T berth(List sections, + Class type, + List keys) { + return berthExcept(sections, type, keys, null); + } + + /** The same, skipping a section another berth has already claimed. */ + private static T berthExcept(List sections, + Class type, + List keys, + CvSection taken) { + for (CvSection section : sections) { + if (!type.isInstance(section) || section == taken) { + continue; + } + for (String key : keys) { + if (SectionLookup.titleContains(section.title(), key)) { + return type.cast(section); + } + } + } + return null; + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java new file mode 100644 index 000000000..3a8749596 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java @@ -0,0 +1,306 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.DocumentLinkOptions; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.templates.core.identity.Contact; +import com.demcha.compose.document.templates.core.identity.Link; +import com.demcha.compose.document.templates.cv.data.CvEntry; +import com.demcha.compose.document.templates.cv.data.CvIdentity; +import com.demcha.compose.document.templates.cv.data.CvSkill; +import com.demcha.compose.document.templates.cv.data.EntriesSection; +import com.demcha.compose.document.templates.cv.data.SkillGroup; +import com.demcha.compose.document.templates.cv.data.SkillsSection; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ACCENT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ACCENT_RULE_THICKNESS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.COLUMN_PAD_BOTTOM; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.CONTACT_ROW_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.DETAIL_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.INFO_BLOCK_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.INFO_LINE_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.INFO_WEIGHTS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.INK; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.LINK_SCALE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MONOGRAM_RULE_TO_CONTACT_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MONOGRAM_RULE_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MONOGRAM_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MONOGRAM_TO_RULE_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.RULE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.RULE_THICKNESS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_DASH_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_DIVIDER_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_HEADING_SPACER; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_PAD_LEFT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_PAD_RIGHT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SIDEBAR_PAD_TOP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.serif; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.text; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.bulletLine; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.divider; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.heading; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.headingWithDash; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.inlineIcon; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.layeredRow; + +/** + * The narrow column: the monogram, the contact channels, the two bulleted + * lists, the credentials and the closing facts. + */ +final class TerracottaRailAside { + + private TerracottaRailAside() { + } + + static void compose(SectionBuilder side, CvIdentity identity, SkillsSection competencies, + SkillsSection software, EntriesSection certifications, + EntriesSection facts) { + side.name("Sidebar"); + side.spacing(0); + side.accentRight(RULE, RULE_THICKNESS); + side.padding((float) SIDEBAR_PAD_TOP, (float) SIDEBAR_PAD_RIGHT, + (float) COLUMN_PAD_BOTTOM, (float) SIDEBAR_PAD_LEFT); + + renderMonogram(side, identity); + renderContact(side, identity); + + if (hasSkills(competencies)) { + divider(side, "AfterContact", SIDEBAR_DIVIDER_GAP, SIDEBAR_DIVIDER_GAP); + renderBulletedSkills(side, competencies, "CoreCompetencies", "Competency", false); + } + if (hasSkills(software)) { + divider(side, "AfterCompetencies", SIDEBAR_DIVIDER_GAP, SIDEBAR_DIVIDER_GAP); + renderBulletedSkills(side, software, "Software", "Software", true); + } + if (hasEntries(certifications)) { + divider(side, "AfterSoftware", SIDEBAR_DIVIDER_GAP, SIDEBAR_DIVIDER_GAP); + renderCertifications(side, certifications); + } + if (hasEntries(facts)) { + divider(side, "AfterCertifications", SIDEBAR_DIVIDER_GAP, SIDEBAR_DIVIDER_GAP); + renderFacts(side, facts); + } + } + + // -- the lockup -------------------------------------------------------- + + /** + * The monogram over its terracotta rule. It is drawn from the name's own + * initials rather than a field of its own — a document states the name + * once, and a monogram that could disagree with it would be a second + * place to keep true. + */ + private static void renderMonogram(SectionBuilder side, CvIdentity identity) { + side.addParagraph(p -> p + .name("Monogram") + .text(monogram(identity)) + .textStyle(serif(MONOGRAM_SIZE, ACCENT)) + .margin(0f, 0f, (float) MONOGRAM_TO_RULE_GAP, 0f)); + side.addLine(line -> line + .name("MonogramRule") + .horizontal(MONOGRAM_RULE_WIDTH) + .thickness(ACCENT_RULE_THICKNESS) + .color(ACCENT) + .margin(new DocumentInsets(0, 0, MONOGRAM_RULE_TO_CONTACT_GAP, 0))); + } + + /** The initials of the given and the family name, in capitals. */ + private static String monogram(CvIdentity identity) { + StringBuilder out = new StringBuilder(2); + appendInitial(out, identity.name().first()); + appendInitial(out, identity.name().last()); + return out.toString(); + } + + private static void appendInitial(StringBuilder out, String part) { + if (!part.isBlank()) { + out.append(Character.toUpperCase(part.charAt(0))); + } + } + + // -- the channels ------------------------------------------------------ + + /** + * The contact block: each channel behind its mark, and the links beneath + * them. + * + *

A link is set smaller than a channel. A URL is one long token that + * cannot be broken, so it is the line that outgrows this column first, + * while a phone, an address or a city is short enough at full size.

+ */ + private static void renderContact(SectionBuilder side, CvIdentity identity) { + side.addSection("Contact", block -> { + block.spacing(0); + // The contact triple is non-blank by construction, so the three + // channels are always drawn; only the links are optional. + Contact contact = identity.contact(); + channel(block, 0, TerracottaRailIcons.EMAIL, contact.email(), + "mailto:" + contact.email(), false); + channel(block, 1, TerracottaRailIcons.PHONE, contact.phone(), + telUri(contact.phone()), false); + channel(block, 2, TerracottaRailIcons.LOCATION, contact.address(), null, false); + int index = 3; + for (Link link : identity.links()) { + channel(block, index++, markFor(link), link.label(), link.url(), true); + } + }); + } + + private static void channel(SectionBuilder block, int index, String token, String value, + String href, boolean compact) { + block.addParagraph(p -> { + p.name("Contact_" + index); + inlineIcon(p, token, TerracottaRailIcons.CONTACT_SIZE); + p.inlineText(compact ? " " : " "); + double size = compact ? DETAIL_SIZE * LINK_SCALE : DETAIL_SIZE; + if (href == null || href.isBlank()) { + p.inlineText(value, text(size, INK, false)); + } else { + p.inlineText(value, text(size, INK, false), new DocumentLinkOptions(href)); + } + p.margin(0f, 0f, (float) CONTACT_ROW_GAP, 0f); + }); + } + + /** A link takes the mark of the network it points at, or a globe. */ + private static String markFor(Link link) { + String target = (link.url() + " " + link.label()).toLowerCase(Locale.ROOT); + return target.contains("linkedin") + ? TerracottaRailIcons.LINKEDIN + : TerracottaRailIcons.GLOBE; + } + + /** + * The dial target for a phone number: its digits, keeping a leading + * {@code +} so an international number stays international, and dropping + * a parenthesised trunk prefix the way a caller dialling from abroad + * drops it. + */ + private static String telUri(String phone) { + String dialled = phone.replaceAll("\\(0+\\)", ""); + String digits = dialled.replaceAll("[^0-9]", ""); + return digits.isEmpty() + ? null + : "tel:" + (phone.trim().startsWith("+") ? "+" : "") + digits; + } + + // -- the lists --------------------------------------------------------- + + /** + * A bulleted list of skills. The competencies take a terracotta square + * and no dash under their heading; the software list takes a disc and a + * dash — the design's own way of telling two lists of one-liners apart. + */ + private static void renderBulletedSkills(SectionBuilder side, SkillsSection section, + String blockName, String prefix, + boolean discBullet) { + side.addSection(blockName, block -> { + block.spacing(0); + if (discBullet) { + headingWithDash(block, section.title(), SIDEBAR_HEADING_SPACER, + SIDEBAR_DASH_WIDTH); + } else { + heading(block, section.title(), SIDEBAR_HEADING_SPACER); + } + int index = 0; + for (SkillGroup group : section.groups()) { + for (CvSkill skill : group.entries()) { + String name = prefix + "_" + index++; + if (discBullet) { + bulletLine(block, name, p -> p.dot(3.0, ACCENT), skill.name(), + INK, DETAIL_SIZE); + } else { + bulletLine(block, name, + p -> inlineIcon(p, TerracottaRailIcons.SQUARE, + TerracottaRailIcons.BULLET_SIZE), + skill.name(), INK, DETAIL_SIZE); + } + } + } + }); + } + + /** The credentials: one disc-bulleted line each. */ + private static void renderCertifications(SectionBuilder side, EntriesSection section) { + side.addSection("Certifications", block -> { + block.spacing(0); + headingWithDash(block, section.title(), SIDEBAR_HEADING_SPACER, SIDEBAR_DASH_WIDTH); + List entries = section.entries(); + for (int index = 0; index < entries.size(); index++) { + bulletLine(block, "Certification_" + index, p -> p.dot(3.0, ACCENT), + entries.get(index).title(), INK, DETAIL_SIZE); + } + }); + } + + /** + * The closing facts: a mark beside a label and the lines under it. + * + *

The mark is an inline run inside a paragraph rather than a block + * icon node: a block icon in a row cell makes the whole row lay its cells + * out vertically.

+ */ + private static void renderFacts(SectionBuilder side, EntriesSection section) { + side.addSection("AdditionalInformation", block -> { + block.spacing(0); + heading(block, section.title(), SIDEBAR_HEADING_SPACER); + List entries = section.entries(); + for (int i = 0; i < entries.size(); i++) { + CvEntry entry = entries.get(i); + int index = i; + layeredRow(block, "AddInfo_" + index, 0.0, INFO_BLOCK_GAP, row -> { + row.weights(INFO_WEIGHTS[0], INFO_WEIGHTS[1]); + row.addParagraph(p -> { + p.name("Icon_" + index); + if (!entry.icon().isBlank()) { + inlineIcon(p, entry.icon(), TerracottaRailIcons.FACT_SIZE); + } + }); + row.addSection("Text_" + index, cell -> { + cell.spacing(0); + cell.addParagraph(p -> p + .name("Label_" + index) + .text(entry.title()) + .textStyle(text(DETAIL_SIZE, INK, true)) + .margin(0f, 0f, (float) INFO_LINE_GAP, 0f)); + for (String line : lines(entry.body())) { + cell.addParagraph(p -> p + .name("Val_" + index) + .text(line) + .textStyle(text(DETAIL_SIZE, INK, false)) + .margin(0f, 0f, (float) INFO_LINE_GAP, 0f)); + } + }); + }); + } + }); + } + + /** A fact's body, one line per line the document wrote. */ + private static List lines(String body) { + List out = new ArrayList<>(); + for (String line : body.split(String.valueOf((char) 10))) { + if (!line.isBlank()) { + out.add(line.strip()); + } + } + return out; + } + + // -- presence ---------------------------------------------------------- + + private static boolean hasSkills(SkillsSection section) { + if (section == null) { + return false; + } + return section.groups().stream().anyMatch(group -> !group.entries().isEmpty()); + } + + private static boolean hasEntries(EntriesSection section) { + return section != null && !section.entries().isEmpty(); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailIcons.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailIcons.java new file mode 100644 index 000000000..24f8795af --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailIcons.java @@ -0,0 +1,97 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.svg.SvgIcon; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Classpath loader for the Terracotta Rail CV marks — the four contact + * glyphs and the bullet in the sidebar, the three marks a fact block may + * name, and the three a project may. + * + *

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

+ * + *

{@link #ENTRY_TOKENS} is the vocabulary a document names through + * {@code CvEntry.icon()}. It is scoped to this preset — another preset + * packages its own set — and an entry that names none is drawn without a + * mark.

+ */ +final class TerracottaRailIcons { + + static final String EMAIL = "email"; + static final String PHONE = "phone"; + static final String LOCATION = "location"; + static final String LINKEDIN = "linkedin"; + static final String GLOBE = "globe"; + static final String SQUARE = "square"; + + private static final String ICON_ROOT = "/templates/cv/terracotta-rail/icons/"; + + /** A contact glyph is set at the size of the line beside it. */ + static final double CONTACT_SIZE = 10.0; + + /** The competency bullet — a terracotta square, not a disc. */ + static final double BULLET_SIZE = 3.8; + + /** A fact's mark stands beside a two-line block, so it is drawn larger. */ + static final double FACT_SIZE = 16.0; + + /** A project's mark is the sketch that opens its row. */ + static final double PROJECT_SIZE = 22.0; + + /** The marks a document may name on a fact or a project entry. */ + static final Set ENTRY_TOKENS = + Set.of(GLOBE, "badge", "clock", "building", "hotel", "house"); + + private static final Set TOKENS = + Set.of(EMAIL, PHONE, LOCATION, LINKEDIN, SQUARE, + GLOBE, "badge", "clock", "building", "hotel", "house"); + + private static final Map CACHE = new ConcurrentHashMap<>(); + + private TerracottaRailIcons() { + } + + /** + * Reads one mark. + * + * @param token one of the packaged tokens + * @return the parsed icon + * @throws IllegalArgumentException when the token names no packaged mark, + * listing the ones a document may choose — an icon token is data, + * so a wrong one is a data error + */ + static SvgIcon icon(String token) { + if (!TOKENS.contains(token)) { + throw new IllegalArgumentException( + "Unknown terracotta rail CV icon token '" + token + + "'. This preset packages " + ENTRY_TOKENS.stream().sorted().toList() + + " for an entry mark."); + } + return CACHE.computeIfAbsent(token, TerracottaRailIcons::read); + } + + private static SvgIcon read(String token) { + String resourcePath = ICON_ROOT + token + ".svg"; + try (InputStream input = TerracottaRailIcons.class.getResourceAsStream(resourcePath)) { + if (input == null) { + throw new IllegalStateException( + "Missing terracotta rail CV icon: " + resourcePath); + } + return SvgIcon.parse(new String(input.readAllBytes(), StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new UncheckedIOException( + "Failed to read terracotta rail CV icon: " + resourcePath, e); + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailMain.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailMain.java new file mode 100644 index 000000000..985c12538 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailMain.java @@ -0,0 +1,337 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.DocumentLinkOptions; +import com.demcha.compose.document.node.ListMarker; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.templates.cv.data.CvEntry; +import com.demcha.compose.document.templates.cv.data.CvIdentity; +import com.demcha.compose.document.templates.cv.data.EntriesSection; +import com.demcha.compose.document.templates.cv.data.ParagraphSection; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ACCENT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.BODY_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.COLUMN_PAD_BOTTOM; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.DETAIL_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.EDUCATION_BAND_PAD_RIGHT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.EDUCATION_BAND_WEIGHT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.EDUCATION_ENTRY_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.EDUCATION_PERIOD_SHARE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.EMPLOYER_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ENTRY_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ENTRY_INDENT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ENTRY_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.HIGHLIGHT_ITEM_SPACING; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.HIGHLIGHT_LINE_SPACING; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.INK; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ITEM_TITLE_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_DASH_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_DIVIDER_BOTTOM; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_DIVIDER_TOP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_HEADING_SPACER; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_PAD_LEFT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_PAD_RIGHT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MAIN_PAD_TOP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MASTHEAD_DIVIDER_BOTTOM; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MASTHEAD_DIVIDER_TOP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MUTED; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.NAME_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.NAME_TO_SUBTITLE_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_DESCRIPTION_PAD_LEFT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_DIVIDER_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_LINE_SPACING; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_META_PAD_LEFT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_META_PAD_RIGHT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_ROW_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PROJECT_WEIGHTS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ROLE_PERIOD_SHARE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.RULE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.RULE_THICKNESS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SUBTITLE_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SUBTITLE_TO_RULE_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SUMMARY_LINE_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SUMMARY_LINE_SPACING; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.italic; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.text; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.divider; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.heading; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.headingWithDash; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.inlineIcon; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.layeredRow; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.railedLine; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.titleAndDate; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailWidgets.trackedWide; + +/** + * The wide column: the masthead, the summary, the roles held on a rail, the + * projects grid and the degrees. + */ +final class TerracottaRailMain { + + private TerracottaRailMain() { + } + + static void compose(SectionBuilder main, CvIdentity identity, ParagraphSection summary, + EntriesSection experience, EntriesSection projects, + EntriesSection education) { + main.name("MainColumn"); + main.spacing(0); + main.padding((float) MAIN_PAD_TOP, (float) MAIN_PAD_RIGHT, + (float) COLUMN_PAD_BOTTOM, (float) MAIN_PAD_LEFT); + + renderMasthead(main, identity); + + if (hasBody(summary)) { + divider(main, "AfterMasthead", MASTHEAD_DIVIDER_TOP, + MASTHEAD_DIVIDER_BOTTOM); + renderSummary(main, summary); + } + if (hasEntries(experience)) { + mainDivider(main, "AfterSummary"); + renderExperience(main, experience); + } + if (hasEntries(projects)) { + mainDivider(main, "AfterExperience"); + renderProjects(main, projects); + } + if (hasEntries(education)) { + mainDivider(main, "AfterProjects"); + renderEducation(main, education); + } + } + + private static void mainDivider(SectionBuilder main, String name) { + divider(main, name, MAIN_DIVIDER_TOP, + MAIN_DIVIDER_BOTTOM); + } + + // -- masthead ---------------------------------------------------------- + + /** The name over its role, both letter-spaced, the role in terracotta. */ + private static void renderMasthead(SectionBuilder main, CvIdentity identity) { + main.addSection("Masthead", block -> { + block.spacing(0); + block.addParagraph(p -> p + .name("FullName") + .text(trackedWide(identity.name().full().toUpperCase(Locale.ROOT))) + .textStyle(text(NAME_SIZE, INK, true)) + .margin(0f, 0f, (float) NAME_TO_SUBTITLE_GAP, 0f)); + block.addParagraph(p -> p + .name("JobTitle") + .text(trackedWide(identity.jobTitle().toUpperCase(Locale.ROOT))) + .textStyle(text(SUBTITLE_SIZE, ACCENT, true)) + .margin(0f, 0f, (float) SUBTITLE_TO_RULE_GAP, 0f)); + }); + } + + // -- summary ----------------------------------------------------------- + + /** The summary, one paragraph per line the document wrote. */ + private static void renderSummary(SectionBuilder main, ParagraphSection summary) { + main.addSection("Summary", block -> { + block.spacing(0); + headingWithDash(block, summary.title(), MAIN_HEADING_SPACER, MAIN_DASH_WIDTH); + List paragraphs = lines(summary.body()); + for (int i = 0; i < paragraphs.size(); i++) { + int index = i; + block.addParagraph(p -> p + .name("SummaryPara_" + index) + .text(paragraphs.get(index)) + .textStyle(text(BODY_SIZE, INK, false)) + .lineSpacing(SUMMARY_LINE_SPACING) + .margin(0f, 0f, (float) SUMMARY_LINE_GAP, 0f)); + } + }); + } + + // -- experience -------------------------------------------------------- + + /** + * The roles held, each on a rail: a ringed marker on a grey hairline that + * runs from one entry to the next. The last entry carries no rail, so the + * line stops at the last marker instead of running past it. + */ + private static void renderExperience(SectionBuilder main, EntriesSection experience) { + main.addSection("Experience", block -> { + block.spacing(0); + heading(block, experience.title(), MAIN_HEADING_SPACER); + List entries = experience.entries(); + for (int i = 0; i < entries.size(); i++) { + CvEntry entry = entries.get(i); + boolean last = i == entries.size() - 1; + int index = i; + block.addSection("ExperienceEntry_" + index, body -> { + body.spacing(0); + if (!last) { + body.accentLeft(RULE, RULE_THICKNESS); + } + body.padding(0f, 0f, last ? 0f : (float) ENTRY_GAP, (float) ENTRY_INDENT); + railedLine(body, "Role", index, + titleAndDate("RoleTable_" + index, entry.title(), entry.date(), + ENTRY_WIDTH, ROLE_PERIOD_SHARE)); + body.addParagraph(p -> p + .name("Employer_" + index) + .text(entry.subtitle()) + .textStyle(italic(BODY_SIZE, MUTED)) + .margin(0f, 0f, (float) EMPLOYER_GAP, 0f)); + List highlights = lines(entry.body()); + if (!highlights.isEmpty()) { + body.addList(list -> list + .name("Highlights_" + index) + .items(highlights) + .marker(ListMarker.bullet()) + .textStyle(text(BODY_SIZE, INK, false)) + .itemSpacing(HIGHLIGHT_ITEM_SPACING) + .lineSpacing(HIGHLIGHT_LINE_SPACING)); + } + }); + } + }); + } + + // -- projects ---------------------------------------------------------- + + /** + * The projects grid: a sketch, the project's own three lines, and its + * description across a hairline. + * + *

The mark is an inline run inside a paragraph rather than a block + * icon node: a block icon in a row cell makes the whole row lay its cells + * out vertically.

+ */ + private static void renderProjects(SectionBuilder main, EntriesSection projects) { + main.addSection("SelectedProjects", block -> { + block.spacing(0); + headingWithDash(block, projects.title(), MAIN_HEADING_SPACER, MAIN_DASH_WIDTH); + List entries = projects.entries(); + for (int i = 0; i < entries.size(); i++) { + CvEntry entry = entries.get(i); + boolean last = i == entries.size() - 1; + int index = i; + layeredRow(block, "Project_" + index, 0.0, PROJECT_ROW_GAP, row -> { + row.weights(PROJECT_WEIGHTS[0], PROJECT_WEIGHTS[1], PROJECT_WEIGHTS[2]); + row.addParagraph(p -> { + p.name("ProjIcon_" + index); + if (!entry.icon().isBlank()) { + inlineIcon(p, entry.icon(), TerracottaRailIcons.PROJECT_SIZE); + } + }); + row.addSection("ProjMeta_" + index, meta -> { + meta.spacing(0); + meta.padding(0f, (float) PROJECT_META_PAD_RIGHT, 0f, + (float) PROJECT_META_PAD_LEFT); + meta.addParagraph(p -> { + p.name("ProjTitle_" + index) + .text(entry.title()) + .textStyle(text(ITEM_TITLE_SIZE, INK, true)) + .margin(0f, 0f, 1.0f, 0f); + if (!entry.link().isBlank()) { + p.link(new DocumentLinkOptions(entry.link())); + } + }); + meta.addParagraph(p -> p + .name("ProjSub_" + index) + .text(entry.subtitle()) + .textStyle(italic(DETAIL_SIZE, MUTED)) + .margin(0f, 0f, 1.0f, 0f)); + meta.addParagraph(p -> p + .name("ProjLoc_" + index) + .text(entry.place()) + .textStyle(text(DETAIL_SIZE, INK, false))); + }); + row.addSection("ProjDesc_" + index, description -> { + description.spacing(0); + description.accentLeft(RULE, RULE_THICKNESS); + description.padding(0f, 0f, 0f, (float) PROJECT_DESCRIPTION_PAD_LEFT); + description.addParagraph(p -> p + .name("ProjDescText_" + index) + .text(entry.body()) + .textStyle(text(BODY_SIZE, INK, false)) + .lineSpacing(PROJECT_LINE_SPACING)); + }); + }); + if (!last) { + block.addLine(line -> line + .name("ProjDiv_" + index) + .fill() + .thickness(RULE_THICKNESS) + .color(RULE) + .margin(new DocumentInsets( + PROJECT_DIVIDER_GAP, 0, PROJECT_DIVIDER_GAP, 0))); + } + } + }); + } + + // -- education --------------------------------------------------------- + + /** + * The degrees, on a rail of their own. + * + *

Their rail is terracotta where the roles' is grey, and the block + * stops short of the column: the design closes it with a hairline at + * about seven tenths of the main column, which is what the empty band + * beside it is for.

+ */ + private static void renderEducation(SectionBuilder main, EntriesSection education) { + main.addSection("Education", block -> { + block.spacing(0); + headingWithDash(block, education.title(), MAIN_HEADING_SPACER, MAIN_DASH_WIDTH); + layeredRow(block, "EducationBand", 0.0, 0.0, band -> { + band.weights(EDUCATION_BAND_WEIGHT, 1.0 - EDUCATION_BAND_WEIGHT); + band.addSection("EducationEntries", cell -> { + cell.spacing(0); + cell.accentRight(RULE, RULE_THICKNESS); + cell.padding(0f, (float) EDUCATION_BAND_PAD_RIGHT, 0f, 0f); + List entries = education.entries(); + for (int i = 0; i < entries.size(); i++) { + CvEntry entry = entries.get(i); + boolean last = i == entries.size() - 1; + int index = i; + cell.addSection("EducationEntry_" + index, body -> { + body.spacing(0); + if (!last) { + body.accentLeft(ACCENT, RULE_THICKNESS); + } + body.padding(0f, 0f, last ? 0f : (float) ENTRY_GAP, + (float) ENTRY_INDENT); + railedLine(body, "Edu", index, + titleAndDate("EduTable_" + index, entry.title(), entry.date(), + EDUCATION_ENTRY_WIDTH, EDUCATION_PERIOD_SHARE)); + body.addParagraph(p -> p + .name("Institution_" + index) + .text(entry.subtitle()) + .textStyle(italic(BODY_SIZE, MUTED))); + }); + } + }); + band.addSection("EducationBandRight", empty -> empty.spacing(0)); + }); + }); + } + + // -- shared ------------------------------------------------------------ + + /** A body, one entry per line the document wrote. */ + private static List lines(String body) { + List out = new ArrayList<>(); + for (String line : body.split(String.valueOf((char) 10))) { + if (!line.isBlank()) { + out.add(line.strip()); + } + } + return out; + } + + private static boolean hasBody(ParagraphSection section) { + return section != null && !section.body().isBlank(); + } + + private static boolean hasEntries(EntriesSection section) { + return section != null && !section.entries().isEmpty(); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailStyles.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailStyles.java new file mode 100644 index 000000000..83e5a3960 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailStyles.java @@ -0,0 +1,182 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.font.FontName; + +/** + * The measured geometry, palette and type scale of the Terracotta Rail CV. + * + *

Every size here is derived from one body size and every width from the + * page's own, so the sheet is one edit rather than a walk through the + * regions that draw it.

+ */ +final class TerracottaRailStyles { + + private TerracottaRailStyles() { + } + + // -- the two columns -------------------------------------------------- + + /** + * The width every horizontal measure here is a share of: A4's, as the + * design drew it. It is a constant rather than the session's own page, + * because the sheet is a fixed composition — the columns and the bands + * inside them are shares of this number, and a caller who sets another + * page gets the same sheet on it rather than a rescaled one. + */ + static final double PAGE_WIDTH = 595.276; + + static final double SIDEBAR_WEIGHT = 0.308; + static final double MAIN_WEIGHT = 1.0 - SIDEBAR_WEIGHT; + + static final double SIDEBAR_PAD_LEFT = 25.4; + static final double SIDEBAR_PAD_RIGHT = 11.4; + static final double SIDEBAR_PAD_TOP = 20.0; + static final double COLUMN_PAD_BOTTOM = 5.0; + static final double MAIN_PAD_LEFT = 22.0; + static final double MAIN_PAD_RIGHT = 24.0; + static final double MAIN_PAD_TOP = 20.0; + + static final double MAIN_CONTENT_WIDTH = + PAGE_WIDTH * MAIN_WEIGHT - MAIN_PAD_LEFT - MAIN_PAD_RIGHT; + + // -- type ------------------------------------------------------------- + + static final FontName SANS = FontName.LATO; + static final FontName SERIF = FontName.PT_SERIF; + + static final double BODY_SIZE = 8.6; + static final double DETAIL_SIZE = BODY_SIZE * 0.98; + static final double ITEM_TITLE_SIZE = BODY_SIZE * 1.14; + static final double SECTION_HEADING_SIZE = BODY_SIZE * 1.02; + static final double MONOGRAM_SIZE = 36.0; + static final double NAME_SIZE = 28.0; + static final double SUBTITLE_SIZE = 13.0; + + /** + * How much smaller a link is set than the channels above it. A URL is one + * long token that cannot be broken, so it is the line that outgrows this + * sidebar first. + */ + static final double LINK_SCALE = 0.85; + + // -- palette ---------------------------------------------------------- + + static final DocumentColor ACCENT = DocumentColor.rgb(201, 74, 41); + static final DocumentColor INK = DocumentColor.rgb(26, 29, 32); + static final DocumentColor MUTED = DocumentColor.rgb(85, 90, 96); + static final DocumentColor RULE = DocumentColor.rgb(216, 216, 216); + static final DocumentColor PAPER = DocumentColor.WHITE; + + static final DocumentStroke NO_BORDER = DocumentStroke.of(PAPER, 0.0); + + // -- rules, rails and the rhythm between blocks ------------------------ + + static final double RULE_THICKNESS = 0.6; + static final double ACCENT_RULE_THICKNESS = 1.4; + static final double MARKER_DIAMETER = 6.2; + + /** How far an entry sits from the rail its marker rides. */ + static final double ENTRY_INDENT = 13.0; + static final double ENTRY_GAP = 9.0; + + static final double HEADING_TO_BODY_GAP = 9.0; + static final double HEADING_TO_DASH_GAP = 3.5; + static final double SIDEBAR_DASH_WIDTH = 20.0; + static final double MAIN_DASH_WIDTH = 22.0; + + static final double SIDEBAR_DIVIDER_GAP = 8.0; + static final double MAIN_DIVIDER_TOP = 6.0; + static final double MAIN_DIVIDER_BOTTOM = 6.5; + static final double MASTHEAD_DIVIDER_TOP = 4.0; + static final double MASTHEAD_DIVIDER_BOTTOM = 6.0; + + static final double MONOGRAM_RULE_WIDTH = 48.0; + static final double MONOGRAM_TO_RULE_GAP = 8.0; + static final double MONOGRAM_RULE_TO_CONTACT_GAP = 10.0; + static final double NAME_TO_SUBTITLE_GAP = 8.0; + static final double SUBTITLE_TO_RULE_GAP = 5.0; + + static final double CONTACT_ROW_GAP = 4.5; + static final double BULLET_ROW_GAP = 3.0; + static final double INFO_BLOCK_GAP = 7.0; + static final double INFO_LINE_GAP = 0.5; + static final double PROJECT_ROW_GAP = 2.0; + static final double PROJECT_DIVIDER_GAP = 2.0; + static final double SUMMARY_LINE_GAP = 2.5; + static final double SUMMARY_LINE_SPACING = 1.30; + static final double EMPLOYER_GAP = 2.5; + static final double HIGHLIGHT_ITEM_SPACING = 1.2; + static final double HIGHLIGHT_LINE_SPACING = 1.15; + + // -- the bands the entries are laid on --------------------------------- + + /** + * The slack a fixed table needs inside the cell it spans. Without it a + * table exactly as wide as its cell is one rounding step too wide. + */ + static final double TABLE_SLACK = 0.01; + + static final double ENTRY_WIDTH = MAIN_CONTENT_WIDTH - ENTRY_INDENT - TABLE_SLACK; + static final double ROLE_PERIOD_SHARE = 0.30; + static final double EDUCATION_PERIOD_SHARE = 0.34; + + /** Education stops short of the column and closes on a hairline. */ + static final double EDUCATION_BAND_WEIGHT = 0.70; + static final double EDUCATION_BAND_PAD_RIGHT = 8.0; + static final double EDUCATION_ENTRY_WIDTH = + MAIN_CONTENT_WIDTH * EDUCATION_BAND_WEIGHT - EDUCATION_BAND_PAD_RIGHT + - ENTRY_INDENT - TABLE_SLACK; + + static final double[] PROJECT_WEIGHTS = {0.08, 0.36, 0.56}; + static final double PROJECT_META_PAD_RIGHT = 8.0; + static final double PROJECT_META_PAD_LEFT = 4.0; + static final double PROJECT_DESCRIPTION_PAD_LEFT = 10.0; + static final double PROJECT_LINE_SPACING = 1.24; + static final double[] INFO_WEIGHTS = {0.18, 0.82}; + + // -- tracking ---------------------------------------------------------- + + /** + * The gaps a heading is letter-spaced with. A text style carries no + * tracking, so a tracked line is written with a space character between + * the letters, and which space is the whole choice: the sidebar takes a + * hair space (a tenth of an em) because a full one would set ADDITIONAL + * INFORMATION wider than the column and wrap it, and the main column, + * being wider, takes a thin space (a fifth). + */ + static final char SIDEBAR_HEADING_SPACER = '\u200A'; + static final char MAIN_HEADING_SPACER = '\u2009'; + + // -- helpers ------------------------------------------------------------ + + static DocumentTextStyle text(double size, DocumentColor color, boolean bold) { + return DocumentTextStyle.builder() + .fontName(SANS) + .size(size) + .color(color) + .decoration(bold ? DocumentTextDecoration.BOLD : DocumentTextDecoration.DEFAULT) + .build(); + } + + static DocumentTextStyle italic(double size, DocumentColor color) { + return DocumentTextStyle.builder() + .fontName(SANS) + .size(size) + .color(color) + .decoration(DocumentTextDecoration.ITALIC) + .build(); + } + + static DocumentTextStyle serif(double size, DocumentColor color) { + return DocumentTextStyle.builder() + .fontName(SERIF) + .size(size) + .color(color) + .decoration(DocumentTextDecoration.DEFAULT) + .build(); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailWidgets.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailWidgets.java new file mode 100644 index 000000000..590883f1a --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailWidgets.java @@ -0,0 +1,260 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.dsl.EllipseBuilder; +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.RowBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.TableBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.InlineImageAlignment; +import com.demcha.compose.document.node.LayerAlign; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentStroke; +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 java.util.function.Consumer; + +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ACCENT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ACCENT_RULE_THICKNESS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.BULLET_ROW_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ENTRY_INDENT; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.HEADING_TO_BODY_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.HEADING_TO_DASH_GAP; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.INK; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.ITEM_TITLE_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.MARKER_DIAMETER; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.NO_BORDER; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.PAPER; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.RULE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.RULE_THICKNESS; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.SECTION_HEADING_SIZE; +import static com.demcha.compose.document.templates.cv.presets.TerracottaRailStyles.text; + +/** + * The idioms this sheet repeats: the tracked heading, the rule between + * blocks, the marker riding a rail, the title-and-date line, and the row a + * column has to wrap to hold. + */ +final class TerracottaRailWidgets { + + private TerracottaRailWidgets() { + } + + // -- headings ---------------------------------------------------------- + + /** + * A letter-spaced heading. + * + *

A text style carries no tracking, so the gap between letters is a + * space character — which one is the whole choice, and the caller makes + * it. The word gap stays clearly wider than the letter gap, or a tracked + * heading reads as one long word.

+ * + * @param text the heading + * @param spacer the space to set between letters + * @return the tracked string + */ + static String tracked(String text, char spacer) { + if (text == null || text.isEmpty()) { + return ""; + } + StringBuilder out = new StringBuilder(text.length() * 2); + for (int index = 0; index < text.length(); index++) { + char letter = text.charAt(index); + if (letter == ' ') { + out.append(spacer).append(' ').append(spacer); + continue; + } + if (index > 0 && text.charAt(index - 1) != ' ') { + out.append(spacer); + } + out.append(letter); + } + return out.toString(); + } + + /** The masthead's own tracking: a full space between letters. */ + static String trackedWide(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + StringBuilder out = new StringBuilder(text.length() * 2); + boolean startOfWord = true; + for (int index = 0; index < text.length(); index++) { + char letter = text.charAt(index); + if (letter == ' ') { + out.append(" "); + startOfWord = true; + continue; + } + if (!startOfWord) { + out.append(' '); + } + out.append(letter); + startOfWord = false; + } + return out.toString(); + } + + /** A heading with nothing under it. */ + static void heading(SectionBuilder block, String title, char spacer) { + block.addParagraph(p -> p + .name("Heading_" + compact(title)) + .text(tracked(title, spacer)) + .textStyle(text(SECTION_HEADING_SIZE, INK, true)) + .margin(0f, 0f, (float) HEADING_TO_BODY_GAP, 0f)); + } + + /** A heading over a short terracotta dash. */ + static void headingWithDash(SectionBuilder block, String title, char spacer, double dashWidth) { + block.addParagraph(p -> p + .name("Heading_" + compact(title)) + .text(tracked(title, spacer)) + .textStyle(text(SECTION_HEADING_SIZE, INK, true)) + .margin(0f, 0f, (float) HEADING_TO_DASH_GAP, 0f)); + block.addLine(line -> line + .name("AccentLine_" + compact(title)) + .horizontal(dashWidth) + .thickness(ACCENT_RULE_THICKNESS) + .color(ACCENT) + .margin(new DocumentInsets( + 0, 0, HEADING_TO_BODY_GAP - HEADING_TO_DASH_GAP, 0))); + } + + /** A node name built from a heading, with the spacing taken out of it. */ + static String compact(String value) { + StringBuilder out = new StringBuilder(value.length()); + for (int index = 0; index < value.length(); index++) { + char letter = value.charAt(index); + if (Character.isLetterOrDigit(letter)) { + out.append(letter); + } + } + return out.toString(); + } + + // -- rules ------------------------------------------------------------- + + /** The hairline that separates two blocks in a column. */ + static void divider(SectionBuilder column, String name, double top, double bottom) { + column.addLine(line -> line + .name("Divider_" + name) + .fill() + .thickness(RULE_THICKNESS) + .color(RULE) + .margin(new DocumentInsets(top, 0, bottom, 0))); + } + + // -- marks ------------------------------------------------------------- + + /** An inline mark, drawn at the size the preset gives its kind. */ + static void inlineIcon(ParagraphBuilder paragraph, String token, double size) { + paragraph.inlineSvgIcon(TerracottaRailIcons.icon(token), size, + InlineImageAlignment.CENTER); + } + + // -- the rail ---------------------------------------------------------- + + /** The marker an entry rides: a terracotta ring with the paper inside. */ + private static DocumentNode marker(String prefix, int index) { + return new EllipseBuilder() + .name(prefix + "Marker_" + index) + .circle(MARKER_DIAMETER) + .fillColor(PAPER) + .stroke(DocumentStroke.of(ACCENT, 1.0)) + .build(); + } + + /** + * An entry's first line, with its marker sitting ON the rail. + * + *

The marker is nudged left by the entry's own indent plus half the + * ring, so the ring's centre lands on the rail rather than beside it. + * Both terms are geometry the sheet already holds, not offsets tuned by + * eye.

+ */ + static void railedLine(SectionBuilder body, String prefix, int index, DocumentNode line) { + body.addLayerStack(stack -> stack + .name(prefix + "Line_" + index) + .layer(line, LayerAlign.TOP_LEFT, 0) + .position(marker(prefix, index), + -(ENTRY_INDENT + MARKER_DIAMETER / 2.0), 0.0, + LayerAlign.TOP_LEFT, 1)); + } + + /** + * A title with its date flush right on the same line. + * + *

A table, not a row: a row cannot nest in a row cell and a table can + * — it spans the cell and its columns lay out. Both cells hold a + * paragraph, because a table inside a row cell draws only leaf + * content.

+ */ + static DocumentNode titleAndDate(String name, String title, String date, + double width, double dateShare) { + double dateColumn = width * dateShare; + return new TableBuilder() + .name(name) + .width(width) + .columns(DocumentTableColumn.fixed(width - dateColumn), + DocumentTableColumn.fixed(dateColumn)) + .defaultCellStyle(DocumentTableStyle.builder() + .padding(DocumentInsets.zero()) + .textAnchor(DocumentTableTextAnchor.BOTTOM_LEFT) + .stroke(NO_BORDER) + .build()) + .rowCells( + DocumentTableCell.node(new ParagraphBuilder() + .name(name + "_Title") + .text(title) + .lineSpacing(0) + .textStyle(text(ITEM_TITLE_SIZE, INK, true)) + .build()), + DocumentTableCell.node(new ParagraphBuilder() + .name(name + "_Period") + .text(date) + .align(TextAlign.RIGHT) + .lineSpacing(0) + .textStyle(text(ITEM_TITLE_SIZE, ACCENT, false)) + .build())) + .build(); + } + + // -- rows -------------------------------------------------------------- + + /** + * A row inside a column. + * + *

A row cannot nest in a row cell on this engine, and both of this + * sheet's columns are cells of the body row — so every horizontal pair + * inside one is wrapped in a layer stack, which can.

+ */ + static void layeredRow(SectionBuilder parent, String name, double marginTop, + double marginBottom, Consumer spec) { + SectionBuilder layer = new SectionBuilder(); + layer.name(name + "Layer"); + layer.spacing(0); + layer.addRow(name, spec); + parent.addLayerStack(stack -> stack + .name(name + "Stack") + .margin(new DocumentInsets(marginTop, 0, marginBottom, 0)) + .layer(layer.build(), LayerAlign.TOP_LEFT, 0)); + } + + /** A bullet line: a mark, a gap, and the item beside it. */ + static void bulletLine(SectionBuilder block, String name, Consumer mark, + String item, DocumentColor color, double size) { + block.addParagraph(p -> { + p.name(name); + mark.accept(p); + p.inlineText(" "); + p.inlineText(item, text(size, color, false)); + p.margin(0f, 0f, (float) BULLET_ROW_GAP, 0f); + }); + } +} diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/badge.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/badge.svg new file mode 100644 index 000000000..0d35d8d18 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/badge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/building.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/building.svg new file mode 100644 index 000000000..9a6de887c --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/building.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/clock.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/clock.svg new file mode 100644 index 000000000..f06099851 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/email.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/email.svg new file mode 100644 index 000000000..817215132 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/globe.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/globe.svg new file mode 100644 index 000000000..4a6528580 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/hotel.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/hotel.svg new file mode 100644 index 000000000..13fb4cd92 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/hotel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/house.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/house.svg new file mode 100644 index 000000000..9c1299801 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/house.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/linkedin.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/linkedin.svg new file mode 100644 index 000000000..d955b83d5 --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/linkedin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/location.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/location.svg new file mode 100644 index 000000000..6564329cc --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/location.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/phone.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/phone.svg new file mode 100644 index 000000000..b8c11ac2d --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/templates/src/main/resources/templates/cv/terracotta-rail/icons/square.svg b/templates/src/main/resources/templates/cv/terracotta-rail/icons/square.svg new file mode 100644 index 000000000..ee5989d1e --- /dev/null +++ b/templates/src/main/resources/templates/cv/terracotta-rail/icons/square.svg @@ -0,0 +1 @@ + \ No newline at end of file