diff --git a/CHANGELOG.md b/CHANGELOG.md index 111f047b5..66e4d4248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,29 @@ follow semantic versioning; release dates are ISO 8601. ### Templates +- **The first CV preset that owns its page: `ProfessionalSidebar`.** A one-page CV + in two columns on the Barlow Condensed / Lato pair — a navy monogram plate over a + pale sidebar carrying the contact channels, meter-bar skills, an education rail with + dot markers and five-dot language ratings, beside a white column of the tracked name, + the profile, the roles held with bulleted highlights, the projects and the references + note. Ships as `cv.presets.ProfessionalSidebar` on the existing `CvDocument` model — + no model change was needed — porting the rendered layout of the published standalone + `professional-sidebar-cv` template. The preset owns its page: a 491.6 x 737.28pt + sheet with no margin, the page fill and the pale sidebar painted as page backgrounds. The + sheet holds one page — its two columns are a single atomic row, so a CV longer than the + sheet raises `AtomicNodeTooLargeException` rather than flowing onto a second page or + silently dropping entries the way the capped sidebar presets do. The class documentation + says so, points at `TimelineMinimal` for a preset that splits its own columns, and + `docs/templates/v2-layered/using-templates.md` carries it beside the capped presets. + Sections reach their berth by title rather than by `Slot`, + because the columns are fixed; skills and languages are both `SkillsSection`s drawn + differently, and a level the document omits draws the name alone. The contact channels + come off `CvIdentity`, with `tel:` and `mailto:` targets built from the values and the + packaged marks chosen per channel. Guarded by a smoke test (including the identity-only + document, the unlevelled skill, the PDF link targets, the one-page limit and the fields + this design has no place for), an exact layout snapshot and a pixel-parity gate; the + examples showcase gains `cv-professional-sidebar-v2`. + - **A second structured proposal preset: `EditorialProposal`.** The same document the `NorthlineProposal` preset renders, in a different hand: an orange accent, section headings set in the display serif over short accent rules instead of in the body sans diff --git a/assets/readme/examples/cv-professional-sidebar-v2.pdf b/assets/readme/examples/cv-professional-sidebar-v2.pdf new file mode 100644 index 000000000..2abddb69b Binary files /dev/null and b/assets/readme/examples/cv-professional-sidebar-v2.pdf differ diff --git a/docs/templates/v2-layered/using-templates.md b/docs/templates/v2-layered/using-templates.md index e260f3a37..9017f5517 100644 --- a/docs/templates/v2-layered/using-templates.md +++ b/docs/templates/v2-layered/using-templates.md @@ -234,6 +234,13 @@ whole or the paginator raises `AtomicNodeTooLargeException`. Lifting a cap without teaching the preset to pick its own page boundaries turns a CV that silently lost an entry into one that fails to render. +`ProfessionalSidebar.create()` is the fourth composition for a fixed +amount of content and the one that caps nothing: it reproduces a +specific one-page sheet, so a CV longer than that sheet raises +`AtomicNodeTooLargeException` instead of losing an entry to a cap. Size +the document to it — roughly five or six roles with three or four +highlights each, alongside the sidebar blocks. + If the document's length is the author's rather than the template's, pick a preset that paginates — `TimelineMinimal` splits its own columns and carries every entry it is given onto as many pages as it needs. diff --git a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java index 0032601a4..d9c144721 100644 --- a/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java +++ b/examples/src/main/java/com/demcha/examples/GenerateAllExamples.java @@ -96,6 +96,7 @@ import com.demcha.examples.templates.cv.v2.CvPanelExample; import com.demcha.examples.templates.cv.v2.CvSidebarPortraitExample; import com.demcha.examples.templates.cv.v2.CvTimelineMinimalExample; +import com.demcha.examples.templates.cv.v2.ProfessionalSidebarExample; import com.demcha.examples.templates.invoice.ClassicInvoiceV2Example; import com.demcha.examples.templates.invoice.ConsultingInvoiceV2Example; import com.demcha.examples.templates.invoice.InvoiceCinematicFileExample; @@ -145,6 +146,7 @@ public static void main(String[] args) throws Exception { System.out.println("Generated: " + CvPanelExample.generate()); System.out.println("Generated: " + CvSidebarPortraitExample.generate()); System.out.println("Generated: " + CvTimelineMinimalExample.generate()); + System.out.println("Generated: " + ProfessionalSidebarExample.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/ProfessionalSidebarSampleData.java b/examples/src/main/java/com/demcha/examples/support/ProfessionalSidebarSampleData.java new file mode 100644 index 000000000..39c18438e --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/support/ProfessionalSidebarSampleData.java @@ -0,0 +1,125 @@ +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.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.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.List; + +/** + * Sample data for the Professional Sidebar CV example. + * + *

Sized to the design: the preset draws a fixed one-page sheet, and a CV + * much longer than this one does not compose at all — the two columns are a + * single atomic row, so it raises {@code AtomicNodeTooLargeException}. The + * sidebar's one-line rows are sized to it too: a degree, skill or language + * longer than the narrow column measures overflows the row it is anchored in + * rather than wrapping inside it.

+ */ +public final class ProfessionalSidebarSampleData { + + /** The en dash the design sets between dates. */ + private static final String DASH = "–"; + + private ProfessionalSidebarSampleData() { + } + + /** + * A backend engineer's one-page CV. + * + * @return the sample document + */ + public static CvDocument sample() { + return CvDocument.builder() + .identity(CvIdentity.builder() + .name("MARTA", "LINDQVIST") + .jobTitle("SENIOR BACKEND ENGINEER") + .contact(new Contact("+46 8 123 456 78", + "marta.lindqvist@example.com", + "Stockholm, Sweden")) + .link(new Link("linkedin.com/in/mlindqvist", + "https://linkedin.com/in/mlindqvist")) + .link(new Link("martalindqvist.dev", + "https://martalindqvist.dev")) + .build()) + .section(Slot.MAIN, new ParagraphSection("PROFILE", + "Backend engineer with nine years on payment and identity systems," + + " most of it owning services end to end: API design, the data" + + " model underneath, and the on-call rota that follows." + + " Comfortable in a team that ships small and often, and the" + + " one who writes the runbook nobody asked for.")) + .section(Slot.SIDEBAR, SkillsSection.of("SKILLS", + new SkillGroup("Core", List.of( + CvSkill.of("Java", 0.9), + CvSkill.of("Kotlin", 0.72), + CvSkill.of("Spring Boot", 0.86), + CvSkill.of("PostgreSQL", 0.8), + CvSkill.of("Kafka", 0.68), + CvSkill.of("Kubernetes", 0.62), + CvSkill.of("Terraform", 0.55), + CvSkill.of("Observability", 0.74), + CvSkill.of("Threat Modelling", 0.6), + CvSkill.of("Mentoring", 0.78))))) + .section(Slot.SIDEBAR, EntriesSection.builder("EDUCATION") + .entry("MSc COMPUTER SCIENCE", "KTH Royal Institute", + "2014 " + DASH + " 2016", "") + .entry("BSc SOFTWARE DESIGN", "Uppsala University", + "2011 " + DASH + " 2014", "") + .build()) + .section(Slot.SIDEBAR, SkillsSection.of("LANGUAGES", + new SkillGroup("Spoken", List.of( + CvSkill.of("Swedish", 1.0), + CvSkill.of("English", 1.0), + CvSkill.of("German", 0.6))))) + .section(Slot.MAIN, EntriesSection.builder("EXPERIENCE") + .entry("SENIOR BACKEND ENGINEER", + "Nordkassa | Stockholm, SE", + "2021 " + DASH + " Present", + String.join("\n", + "Led the split of the settlement monolith into four" + + " services with no customer-visible downtime.", + "Cut p99 authorisation latency from 780ms to 210ms by" + + " reworking the ledger write path.", + "Introduced contract tests between payments and risk," + + " ending a class of release-day rollbacks.", + "Mentors three engineers; runs the internal design" + + " review.")) + .entry("BACKEND ENGINEER", + "Vinge Identity | Gothenburg, SE", + "2018 " + DASH + " 2021", + String.join("\n", + "Built the OAuth2 and OIDC provider now serving eleven" + + " internal products.", + "Moved session storage to Redis with a migration that" + + " ran live for six weeks.", + "Wrote the audit pipeline the compliance team still" + + " reports from.")) + .entry("SOFTWARE ENGINEER", + "Almgren Data | Uppsala, SE", + "2016 " + DASH + " 2018", + String.join("\n", + "Delivered ingest and reporting services for retail" + + " forecasting customers.", + "Replaced nightly batch reconciliation with a streaming" + + " job on Kafka.")) + .build()) + .section(Slot.MAIN, EntriesSection.builder("PROJECTS") + .entry("LEDGERKIT", "", "2024", + "Open-source double-entry ledger for JVM services, with a" + + " property-based test suite over the posting rules.") + .entry("ONCALL DIGEST", "", "2022", + "Weekly incident summariser that reads alert history and drafts" + + " the retrospective agenda.") + .build()) + .section(Slot.MAIN, new ParagraphSection("REFERENCES", + "Available on request.")) + .build(); + } +} 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 d12d6b205..b63c554c0 100644 --- a/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java +++ b/examples/src/main/java/com/demcha/examples/support/ShowcaseMetadata.java @@ -60,6 +60,7 @@ record Entry(String title, String description, List tags, String codeUrl cv("cv-minimal-underlined-v2", "CvMinimalUnderlinedExample", "Minimal Underlined", "Single-column layout with underlined section titles and tight whitespace — minimalist reference shape.", "minimal"); cv("cv-mint-editorial-v2", "CvMintEditorialExample", "Mint Editorial", "Magazine-style editorial CV with mint accent palette and two-column body.", "editorial", "mint"); cv("cv-mint-editorial-v2-custom", "CvMintEditorialCustomExample", "Mint Editorial (custom band)", "The same preset with one colour changed through its Options — a kraft-paper masthead band, everything else left at the preset's defaults.", "editorial", "mint"); + cv("cv-professional-sidebar-v2", "ProfessionalSidebarExample", "Professional Sidebar", "Navy monogram plate over a pale sidebar of contact marks, skill meters, an education rail and language ratings, beside a white column of profile, roles and projects.", "sidebar", "navy"); // ===== 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/templates/cv/v2/ProfessionalSidebarExample.java b/examples/src/main/java/com/demcha/examples/templates/cv/v2/ProfessionalSidebarExample.java new file mode 100644 index 000000000..570a3fc4e --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/templates/cv/v2/ProfessionalSidebarExample.java @@ -0,0 +1,52 @@ +package com.demcha.examples.templates.cv.v2; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.presets.ProfessionalSidebar; +import com.demcha.examples.support.ExampleOutputPaths; +import com.demcha.examples.support.ProfessionalSidebarSampleData; + +import java.nio.file.Path; + +/** + * Renders the layered {@code cv.v2} Professional Sidebar preset against the + * backend-engineer sample. + * + *

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

+ * + *

The preset owns its page geometry — a 491.6 x 737.28pt sheet with no + * margin — so the session starts unconfigured.

+ */ +public final class ProfessionalSidebarExample { + + private ProfessionalSidebarExample() { + } + + /** + * @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-professional-sidebar-v2.pdf"); + CvDocument doc = ProfessionalSidebarSampleData.sample(); + DocumentTemplate template = ProfessionalSidebar.create(); + + try (DocumentSession document = GraphCompose.document(outputFile).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/ProfessionalSidebarFixtures.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarFixtures.java new file mode 100644 index 000000000..7d6fef3cb --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarFixtures.java @@ -0,0 +1,128 @@ +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.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.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.List; + +/** + * The canonical fixture for {@link ProfessionalSidebar} — the document the + * design was drawn around, and the one both gates measure. + * + *

One fixture feeds the layout snapshot and the pixel baseline, so a + * change that moves the sheet moves both together instead of leaving one + * gate measuring a document the other no longer renders.

+ */ +public final class ProfessionalSidebarFixtures { + + /** The en dash the design sets between dates. */ + private static final String DASH = "–"; + + private ProfessionalSidebarFixtures() { + } + + /** + * The canonical CV: five contact channels, ten levelled skills, two + * degrees, three languages, three roles, two projects and a references + * note — one page. + * + * @return the fixture document + */ + public static CvDocument canonicalCv() { + return CvDocument.builder() + .identity(CvIdentity.builder() + .name("YOUR", "NAME") + .jobTitle("YOUR PROFESSIONAL TITLE") + .contact(new Contact("+1 234 567 8900", + "your.email@example.com", + "City, State, Country")) + .link(new Link("linkedin.com/in/username", + "https://linkedin.com/in/username")) + .link(new Link("www.yourwebsite.com", + "https://www.yourwebsite.com")) + .build()) + .section(Slot.MAIN, new ParagraphSection("PROFILE", + "Java backend engineer experienced in building secure Spring Boot" + + " services, REST APIs and data-driven products. Delivers clean" + + " architecture, reliable integrations and well-tested" + + " persistence layers with PostgreSQL, JPA and Flyway." + + " Comfortable owning features from API design through" + + " Docker-based delivery and production support.")) + .section(Slot.SIDEBAR, SkillsSection.of("SKILLS", new SkillGroup("Core", List.of( + CvSkill.of("Java", 0.72), + CvSkill.of("Spring Boot", 0.7), + CvSkill.of("SQL / Databases", 0.7), + CvSkill.of("RESTful APIs", 0.6), + CvSkill.of("Microservices", 0.72), + CvSkill.of("Git / GitHub", 0.72), + CvSkill.of("Docker", 0.56), + CvSkill.of("Testing (JUnit)", 0.76), + CvSkill.of("Problem Solving", 0.72), + CvSkill.of("Communication", 0.7))))) + .section(Slot.SIDEBAR, EntriesSection.builder("EDUCATION") + .entry("DEGREE NAME", "University Name", "2016 " + DASH + " 2020", "") + .entry("DEGREE NAME", "University Name", "2012 " + DASH + " 2016", "") + .build()) + .section(Slot.SIDEBAR, SkillsSection.of("LANGUAGES", + new SkillGroup("Spoken", List.of( + CvSkill.of("English", 1.0), + CvSkill.of("Ukrainian", 0.8), + CvSkill.of("Russian", 0.6))))) + .section(Slot.MAIN, EntriesSection.builder("EXPERIENCE") + .entry("JAVA BACKEND DEVELOPER", + "Product Studio | London, UK", + "2023 " + DASH + " Present", + String.join("\n", + "Built secure Spring Boot REST APIs for customer" + + " workflows.", + "Added JWT authentication, validation and role-based" + + " access.", + "Tuned JPA queries and PostgreSQL indexes for faster" + + " responses.", + "Added JUnit tests and Docker environments for reliable" + + " releases.")) + .entry("SOFTWARE DEVELOPER", + "Digital Solutions Ltd | Manchester, UK", + "2021 " + DASH + " 2023", + String.join("\n", + "Integrated internal services with third-party REST" + + " APIs.", + "Delivered Flyway migrations and improved data" + + " consistency.", + "Introduced structured logging and production" + + " diagnostics.", + "Worked with frontend engineers on clear API contracts.")) + .entry("JUNIOR JAVA DEVELOPER", + "TechWorks | Remote", + "2019 " + DASH + " 2021", + String.join("\n", + "Developed Java services and reusable backend" + + " components.", + "Fixed defects across REST, persistence and security" + + " layers.", + "Wrote JUnit tests and supported continuous integration.", + "Documented endpoints and operational runbooks.")) + .build()) + .section(Slot.MAIN, EntriesSection.builder("PROJECTS") + .entry("CVREWRITER", "", "2026", + "AI-powered resume tailoring platform with Spring Boot, JWT" + + " authentication, PDF generation and application" + + " tracking.") + .entry("GRAPHCOMPOSE", "", "2025", + "Declarative Java and Kotlin document layout engine with" + + " semantic components, pagination and multi-format" + + " rendering.") + .build()) + .section(Slot.MAIN, new ParagraphSection("REFERENCES", + "Available upon request.")) + .build(); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarLayoutSnapshotTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarLayoutSnapshotTest.java new file mode 100644 index 000000000..01737a10b --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarLayoutSnapshotTest.java @@ -0,0 +1,30 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.TemplateTestSupport; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Exact layout snapshot gate for {@link ProfessionalSidebar} — freezes the + * resolved geometry of the canonical one-page CV, which is what catches a + * shift the pixel budget is too coarse to see. + * + *

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

+ */ +class ProfessionalSidebarLayoutSnapshotTest { + + @Test + void canonicalCvMatchesLayoutSnapshot() throws Exception { + try (DocumentSession session = GraphCompose.document().create()) { + ProfessionalSidebar.create().compose( + session, ProfessionalSidebarFixtures.canonicalCv()); + assertThat(session.layoutSnapshot().totalPages()).isEqualTo(1); + TemplateTestSupport.assertCanonicalSnapshot( + session, "professional_sidebar_layout", "cv-v2"); + } + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarSmokeTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarSmokeTest.java new file mode 100644 index 000000000..f9e7d6d12 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarSmokeTest.java @@ -0,0 +1,243 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.exceptions.AtomicNodeTooLargeException; +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.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 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 static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Smoke test for {@link ProfessionalSidebar} — proves the preset renders a + * {@link CvDocument} end-to-end with its packaged marks, reaches its + * channels as PDF links, degrades through its guards on a document that + * fills none of the berths, and draws a name without a level rather than an + * empty meter. + * + *

It also pins the two limits the class documents, because both are things + * a caller finds out the hard way otherwise: the sheet holds one page and + * raises past it, and three fields this design has no place for are not + * drawn.

+ */ +class ProfessionalSidebarSmokeTest { + + private static byte[] render(CvDocument doc) throws Exception { + // The preset owns its page geometry, so the session starts unconfigured. + try (DocumentSession session = GraphCompose.document().create()) { + ProfessionalSidebar.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); + } + } + + private static List linkUris(byte[] pdfBytes) throws Exception { + List uris = 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 action) { + uris.add(action.getURI()); + } + } + } + } + return uris; + } + + @Test + void exposesStableIdentity() { + DocumentTemplate template = ProfessionalSidebar.create(); + assertThat(template.id()).isEqualTo(ProfessionalSidebar.ID); + assertThat(template.displayName()).isEqualTo(ProfessionalSidebar.DISPLAY_NAME); + } + + @Test + void canonicalRenderCarriesTheUntrackedText() throws Exception { + // The headings and the name are set letter by letter to reach their + // tracking, so they arrive on the text layer spaced out. What is + // asserted here is the text drawn as written. + String text = textOf(render(ProfessionalSidebarFixtures.canonicalCv())); + assertThat(text) + .contains("your.email@example.com") + .contains("City, State, Country") + .contains("Java backend engineer") + .contains("DEGREE NAME") + .contains("University Name") + .contains("Docker") + .contains("English") + .contains("Product Studio") + .contains("Built secure Spring Boot REST APIs for customer workflows.") + .contains("Available upon request."); + } + + @Test + void channelsReachThePdfAsLinks() throws Exception { + // The dial and mail targets are built from the values, so nothing in + // the document carries them and nothing but the PDF can show them. + assertThat(linkUris(render(ProfessionalSidebarFixtures.canonicalCv()))) + .contains("tel:+12345678900", + "mailto:your.email@example.com", + "https://linkedin.com/in/username", + "https://www.yourwebsite.com"); + } + + @Test + void rendersOneCanonicalPage() throws Exception { + try (PDDocument document = + Loader.loadPDF(render(ProfessionalSidebarFixtures.canonicalCv()))) { + assertThat(document.getNumberOfPages()).isEqualTo(1); + } + } + + @Test + void rendersIdentityOnly() throws Exception { + // Every berth empty: the plate, the initials and the three channels + // the identity always carries, and no hairline left hanging under a + // section that was never drawn. + String text = textOf(render(CvDocument.builder() + .identity(identity()) + .build())); + assertThat(text).contains("empty@example.com"); + } + + @Test + void rendersWithoutAJobTitle() throws Exception { + // The role line is written letter by letter to reach its tracking, so + // an identity with no job title reaches the engine as a paragraph + // holding no runs at all. + String text = textOf(render(CvDocument.builder() + .identity(CvIdentity.builder() + .name("Ada", "Lovelace") + .contact(new Contact("+44 20 7946 0000", "empty@example.com", + "London, UK")) + .build()) + .build())); + assertThat(text).contains("empty@example.com"); + } + + @Test + void drawsAnUnlevelledSkillAsItsNameAlone() throws Exception { + // A meter needs a level; without one the row is the name, not an + // empty track that reads as a zero score. + String text = textOf(render(CvDocument.builder() + .identity(identity()) + .section(Slot.SIDEBAR, SkillsSection.of("SKILLS", + SkillGroup.of("Core", "Java", "Kotlin"))) + .section(Slot.SIDEBAR, SkillsSection.of("LANGUAGES", + new SkillGroup("Spoken", List.of(CvSkill.of("English"))))) + .build())); + assertThat(text).contains("Java").contains("Kotlin").contains("English"); + } + + @Test + void placesSectionsByTitleWhateverSlotTheDocumentNames() throws Exception { + // The columns are fixed, so a document that leaves everything in the + // main slot still gets its education in the sidebar. + String text = textOf(render(CvDocument.builder() + .identity(identity()) + .section(Slot.MAIN, EntriesSection.builder("EDUCATION") + .entry("BSc Computing", "University Name", "2016", "") + .build()) + .build())); + assertThat(text).contains("BSc Computing").contains("University Name"); + } + + @Test + void refusesACvLongerThanTheSheet() { + // The sheet holds one page: the two columns are a single row, and a + // row is atomic. A CV past that raises rather than flowing — pinned + // here because the alternative the sibling presets take is to cap + // each block and drop the rest without saying so. + EntriesSection.Builder experience = EntriesSection.builder("EXPERIENCE"); + for (int i = 0; i < 12; i++) { + experience.entry("ROLE " + i, "Employer " + i + " | City", + "2010 - 2011", + String.join("\n", + "Did a substantial thing that took a full line of text here.", + "Did a second substantial thing, also a full line of text.", + "And a third, so each role occupies real vertical space.")); + } + CvDocument doc = CvDocument.builder() + .identity(identity()) + .section(experience.build()) + .build(); + try (DocumentSession session = GraphCompose.document().create()) { + ProfessionalSidebar.create().compose(session, doc); + assertThatThrownBy(session::toPdfBytes) + .isInstanceOf(AtomicNodeTooLargeException.class) + .hasMessageContaining("PageGrid"); + } + } + + @Test + void drawsNothingTheDesignHasNoPlaceFor() throws Exception { + // A berth is filled once, and three fields have nowhere to go on this + // sheet. A caller who puts content there gets none of it drawn, which + // is why the class says so. + String text = textOf(render(CvDocument.builder() + .identity(identity()) + .section(EntriesSection.builder("PROJECTS") + .entry("LEDGERKIT", "Client name here", "2024", "A ledger.") + .build()) + .section(EntriesSection.builder("EDUCATION") + .entry("BSc", "University Name", "2016", "Thesis title here") + .build()) + .section(SkillsSection.of("SKILLS", + SkillGroup.of("Category name here", "Java"))) + .section(EntriesSection.builder("EXPERIENCE") + .entry("FIRST ROLE", "Employer", "2020", "Did a thing.") + .build()) + .section(EntriesSection.builder("MORE EXPERIENCE") + .entry("SECOND ROLE", "Employer", "2021", "Did another thing.") + .build()) + .build())); + assertThat(text) + .contains("LEDGERKIT") + .contains("University Name") + .contains("Java") + .contains("FIRST ROLE") + .doesNotContain("Client name here") + .doesNotContain("Thesis title here") + .doesNotContain("Category name here") + .doesNotContain("SECOND ROLE"); + } + + private static CvIdentity identity() { + return CvIdentity.builder() + .name("Ada", "Lovelace") + .jobTitle("ANALYST") + .contact(new Contact("+44 20 7946 0000", "empty@example.com", "London, UK")) + .link(new Link("linkedin.com/in/ada", "https://linkedin.com/in/ada")) + .build(); + } +} diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarVisualParityTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarVisualParityTest.java new file mode 100644 index 000000000..5596d0d78 --- /dev/null +++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarVisualParityTest.java @@ -0,0 +1,42 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.testing.visual.PdfVisualRegression; +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; + +/** + * Pixel-diff visual parity gate for {@link ProfessionalSidebar} — 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 ProfessionalSidebarVisualParityTest { + + 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().create()) { + ProfessionalSidebar.create().compose( + document, ProfessionalSidebarFixtures.canonicalCv()); + pdfBytes = document.toPdfBytes(); + } + + PdfVisualRegression.standard() + .baselineRoot(BASELINE_ROOT) + .perPixelTolerance(PER_PIXEL_TOLERANCE) + .mismatchedPixelBudget(PIXEL_DIFF_BUDGET) + .assertMatchesBaseline("professional_sidebar", pdfBytes); + } +} diff --git a/qa/src/test/resources/layout-snapshots/canonical-templates/cv-v2/professional_sidebar_layout.json b/qa/src/test/resources/layout-snapshots/canonical-templates/cv-v2/professional_sidebar_layout.json new file mode 100644 index 000000000..f8569aa6f --- /dev/null +++ b/qa/src/test/resources/layout-snapshots/canonical-templates/cv-v2/professional_sidebar_layout.json @@ -0,0 +1,4757 @@ +{ + "formatVersion" : "2.0", + "canvas" : { + "pageWidth" : 491.6, + "pageHeight" : 737.28, + "innerWidth" : 491.6, + "innerHeight" : 737.28, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, + "totalPages" : 1, + "nodes" : [ { + "path" : "ProfessionalSidebarCv[0]", + "entityName" : "ProfessionalSidebarCv", + "entityKind" : "ContainerNode", + "parentPath" : null, + "childIndex" : 0, + "depth" : 1, + "layer" : 1, + "computedX" : 0.0, + "computedY" : 12.73, + "placementX" : 0.0, + "placementY" : 12.73, + "placementWidth" : 491.6, + "placementHeight" : 724.55, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 491.6, + "contentHeight" : 724.55, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]", + "entityName" : "PageGrid", + "entityKind" : "RowNode", + "parentPath" : "ProfessionalSidebarCv[0]", + "childIndex" : 0, + "depth" : 2, + "layer" : 2, + "computedX" : 0.0, + "computedY" : 12.73, + "placementX" : 0.0, + "placementY" : 12.73, + "placementWidth" : 491.6, + "placementHeight" : 724.55, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 491.6, + "contentHeight" : 724.55, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]", + "entityName" : "Sidebar", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]", + "childIndex" : 0, + "depth" : 3, + "layer" : 3, + "computedX" : 0.0, + "computedY" : 24.204, + "placementX" : 0.0, + "placementY" : 24.204, + "placementWidth" : 143.056, + "placementHeight" : 713.076, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 143.056, + "contentHeight" : 713.076, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/MonogramPlate[0]", + "entityName" : "MonogramPlate", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 0.0, + "computedY" : 638.484, + "placementX" : 0.0, + "placementY" : 638.484, + "placementWidth" : 143.056, + "placementHeight" : 98.796, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 143.056, + "contentHeight" : 98.796, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/MonogramPlate[0]/MonogramRing[0]", + "entityName" : "MonogramRing", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/MonogramPlate[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 43.928, + "computedY" : 660.282, + "placementX" : 43.928, + "placementY" : 660.282, + "placementWidth" : 55.2, + "placementHeight" : 55.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 55.2, + "contentHeight" : 55.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/MonogramPlate[0]/MonogramRing[0]/MonogramInitials[0]", + "entityName" : "MonogramInitials", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/MonogramPlate[0]/MonogramRing[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 43.928, + "computedY" : 678.282, + "placementX" : 43.928, + "placementY" : 678.282, + "placementWidth" : 55.2, + "placementHeight" : 19.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 55.2, + "contentHeight" : 19.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "entityName" : "SidebarBody", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 0.0, + "computedY" : 24.204, + "placementX" : 0.0, + "placementY" : 24.204, + "placementWidth" : 143.056, + "placementHeight" : 614.28, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 143.056, + "contentHeight" : 614.28, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 22.0, + "right" : 17.8, + "bottom" : 0.0, + "left" : 17.8 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "entityName" : "Contact", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 491.434, + "placementX" : 17.8, + "placementY" : 491.434, + "placementWidth" : 98.484, + "placementHeight" : 125.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 98.484, + "contentHeight" : 125.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/SidebarHeading_CONTACT[0]", + "entityName" : "SidebarHeading_CONTACT", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 606.284, + "placementX" : 17.8, + "placementY" : 606.284, + "placementWidth" : 47.723, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.723, + "contentHeight" : 10.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/SidebarHeadingRule_CONTACT[1]", + "entityName" : "SidebarHeadingRule_CONTACT", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 601.234, + "placementX" : 17.8, + "placementY" : 601.234, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 16.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/Contact_phone[2]", + "entityName" : "Contact_phone", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 576.434, + "placementX" : 17.8, + "placementY" : 576.434, + "placementWidth" : 70.939, + "placementHeight" : 8.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 70.939, + "contentHeight" : 8.8, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 11.8, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/Contact_email[3]", + "entityName" : "Contact_email", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 3, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 555.434, + "placementX" : 17.8, + "placementY" : 555.434, + "placementWidth" : 95.827, + "placementHeight" : 9.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 95.827, + "contentHeight" : 9.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 11.8, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/Contact_location[4]", + "entityName" : "Contact_location", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 4, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 534.034, + "placementX" : 17.8, + "placementY" : 534.034, + "placementWidth" : 78.108, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 78.108, + "contentHeight" : 9.6, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 11.8, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/Contact_linkedin[5]", + "entityName" : "Contact_linkedin", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 5, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 512.834, + "placementX" : 17.8, + "placementY" : 512.834, + "placementWidth" : 98.484, + "placementHeight" : 9.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 98.484, + "contentHeight" : 9.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 11.8, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]/Contact_website[6]", + "entityName" : "Contact_website", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Contact[0]", + "childIndex" : 6, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 491.434, + "placementX" : 17.8, + "placementY" : 491.434, + "placementWidth" : 88.843, + "placementHeight" : 9.6, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 88.843, + "contentHeight" : 9.6, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/SidebarDivider[1]", + "entityName" : "SidebarDivider", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 466.434, + "placementX" : 17.8, + "placementY" : 466.434, + "placementWidth" : 107.456, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 1.0, + "margin" : { + "top" : 24.0, + "right" : 0.0, + "bottom" : 18.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]", + "entityName" : "Skills", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 250.984, + "placementX" : 17.8, + "placementY" : 250.984, + "placementWidth" : 107.456, + "placementHeight" : 197.45, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 197.45, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SidebarHeading_SKILLS[0]", + "entityName" : "SidebarHeading_SKILLS", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 438.234, + "placementX" : 17.8, + "placementY" : 438.234, + "placementWidth" : 32.312, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 32.312, + "contentHeight" : 10.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SidebarHeadingRule_SKILLS[1]", + "entityName" : "SidebarHeadingRule_SKILLS", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 433.184, + "placementX" : 17.8, + "placementY" : 433.184, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 13.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "entityName" : "SkillRows", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 250.984, + "placementX" : 17.8, + "placementY" : 250.984, + "placementWidth" : 107.456, + "placementHeight" : 168.7, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 168.7, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]", + "entityName" : "SkillRow_Java", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 411.184, + "placementX" : 17.8, + "placementY" : 411.184, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]/Skill_Java[0]", + "entityName" : "Skill_Java", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 411.164, + "placementX" : 17.8, + "placementY" : 411.164, + "placementWidth" : 13.724, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 13.724, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 414.034, + "placementX" : 77.975, + "placementY" : 414.034, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Java[0]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 414.034, + "placementX" : 77.975, + "placementY" : 414.034, + "placementWidth" : 34.042, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 34.042, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]", + "entityName" : "SkillRow_SpringBoot", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 393.384, + "placementX" : 17.8, + "placementY" : 393.384, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]/Skill_SpringBoot[0]", + "entityName" : "Skill_SpringBoot", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 393.364, + "placementX" : 17.8, + "placementY" : 393.364, + "placementWidth" : 36.792, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 36.792, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 396.234, + "placementX" : 77.975, + "placementY" : 396.234, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SpringBoot[1]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 396.234, + "placementX" : 77.975, + "placementY" : 396.234, + "placementWidth" : 33.096, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 33.096, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]", + "entityName" : "SkillRow_SQLDatabases", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 2, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 375.584, + "placementX" : 17.8, + "placementY" : 375.584, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]/Skill_SQLDatabases[0]", + "entityName" : "Skill_SQLDatabases", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 375.564, + "placementX" : 17.8, + "placementY" : 375.564, + "placementWidth" : 52.448, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 52.448, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 378.434, + "placementX" : 77.975, + "placementY" : 378.434, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_SQLDatabases[2]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 378.434, + "placementX" : 77.975, + "placementY" : 378.434, + "placementWidth" : 33.096, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 33.096, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]", + "entityName" : "SkillRow_RESTfulAPIs", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 3, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 357.784, + "placementX" : 17.8, + "placementY" : 357.784, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]/Skill_RESTfulAPIs[0]", + "entityName" : "Skill_RESTfulAPIs", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 357.764, + "placementX" : 17.8, + "placementY" : 357.764, + "placementWidth" : 40.69, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 40.69, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 360.634, + "placementX" : 77.975, + "placementY" : 360.634, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_RESTfulAPIs[3]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 360.634, + "placementX" : 77.975, + "placementY" : 360.634, + "placementWidth" : 28.368, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 28.368, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]", + "entityName" : "SkillRow_Microservices", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 4, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 339.984, + "placementX" : 17.8, + "placementY" : 339.984, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]/Skill_Microservices[0]", + "entityName" : "Skill_Microservices", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 339.964, + "placementX" : 17.8, + "placementY" : 339.964, + "placementWidth" : 43.296, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 43.296, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 342.834, + "placementX" : 77.975, + "placementY" : 342.834, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Microservices[4]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 342.834, + "placementX" : 77.975, + "placementY" : 342.834, + "placementWidth" : 34.042, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 34.042, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]", + "entityName" : "SkillRow_GitGitHub", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 5, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 322.184, + "placementX" : 17.8, + "placementY" : 322.184, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]/Skill_GitGitHub[0]", + "entityName" : "Skill_GitGitHub", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 322.164, + "placementX" : 17.8, + "placementY" : 322.164, + "placementWidth" : 39.093, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 39.093, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 325.034, + "placementX" : 77.975, + "placementY" : 325.034, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_GitGitHub[5]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 325.034, + "placementX" : 77.975, + "placementY" : 325.034, + "placementWidth" : 34.042, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 34.042, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]", + "entityName" : "SkillRow_Docker", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 6, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 304.384, + "placementX" : 17.8, + "placementY" : 304.384, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]/Skill_Docker[0]", + "entityName" : "Skill_Docker", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 304.364, + "placementX" : 17.8, + "placementY" : 304.364, + "placementWidth" : 22.763, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.763, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 307.234, + "placementX" : 77.975, + "placementY" : 307.234, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Docker[6]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 307.234, + "placementX" : 77.975, + "placementY" : 307.234, + "placementWidth" : 26.477, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 26.477, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]", + "entityName" : "SkillRow_TestingJUnit", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 7, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 286.584, + "placementX" : 17.8, + "placementY" : 286.584, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]/Skill_TestingJUnit[0]", + "entityName" : "Skill_TestingJUnit", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 286.564, + "placementX" : 17.8, + "placementY" : 286.564, + "placementWidth" : 44.979, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 44.979, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 289.434, + "placementX" : 77.975, + "placementY" : 289.434, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_TestingJUnit[7]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 289.434, + "placementX" : 77.975, + "placementY" : 289.434, + "placementWidth" : 35.933, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 35.933, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]", + "entityName" : "SkillRow_ProblemSolving", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 8, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 268.784, + "placementX" : 17.8, + "placementY" : 268.784, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]/Skill_ProblemSolving[0]", + "entityName" : "Skill_ProblemSolving", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 268.764, + "placementX" : 17.8, + "placementY" : 268.764, + "placementWidth" : 50.517, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 50.517, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 271.634, + "placementX" : 77.975, + "placementY" : 271.634, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_ProblemSolving[8]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 271.634, + "placementX" : 77.975, + "placementY" : 271.634, + "placementWidth" : 34.042, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 34.042, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]", + "entityName" : "SkillRow_Communication", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]", + "childIndex" : 9, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 250.984, + "placementX" : 17.8, + "placementY" : 250.984, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]/Skill_Communication[0]", + "entityName" : "Skill_Communication", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 250.964, + "placementX" : 17.8, + "placementY" : 250.964, + "placementWidth" : 49.246, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 49.246, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]/SkillMeter[1]", + "entityName" : "SkillMeter", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 77.975, + "computedY" : 253.834, + "placementX" : 77.975, + "placementY" : 253.834, + "placementWidth" : 47.28, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.28, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]/SkillMeter[1]/SkillMeterFill[0]", + "entityName" : "SkillMeterFill", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Skills[2]/SkillRows[2]/SkillRow_Communication[9]/SkillMeter[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 77.975, + "computedY" : 253.834, + "placementX" : 77.975, + "placementY" : 253.834, + "placementWidth" : 33.096, + "placementHeight" : 2.8, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 33.096, + "contentHeight" : 2.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/SidebarDivider[3]", + "entityName" : "SidebarDivider", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 234.984, + "placementX" : 17.8, + "placementY" : 234.984, + "placementWidth" : 107.456, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 1.0, + "margin" : { + "top" : 15.0, + "right" : 0.0, + "bottom" : 18.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]", + "entityName" : "Education", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 118.054, + "placementX" : 17.8, + "placementY" : 118.054, + "placementWidth" : 107.456, + "placementHeight" : 98.93, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 98.93, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/SidebarHeading_EDUCATION[0]", + "entityName" : "SidebarHeading_EDUCATION", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 206.784, + "placementX" : 17.8, + "placementY" : 206.784, + "placementWidth" : 59.505, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 59.505, + "contentHeight" : 10.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/SidebarHeadingRule_EDUCATION[1]", + "entityName" : "SidebarHeadingRule_EDUCATION", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 201.734, + "placementX" : 17.8, + "placementY" : 201.734, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "entityName" : "EducationRail", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 20.6, + "computedY" : 118.054, + "placementX" : 20.6, + "placementY" : 118.054, + "placementWidth" : 104.656, + "placementHeight" : 73.68, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.656, + "contentHeight" : 73.68, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 2.8 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]", + "entityName" : "EducationHead_0", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 20.6, + "computedY" : 182.434, + "placementX" : 20.6, + "placementY" : 182.434, + "placementWidth" : 104.656, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.656, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]/EducationRailMaskAboveFirstMarker[0]", + "entityName" : "EducationRailMaskAboveFirstMarker", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 20.1, + "computedY" : 189.234, + "placementX" : 20.1, + "placementY" : 189.234, + "placementWidth" : 1.55, + "placementHeight" : 2.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 1.55, + "contentHeight" : 2.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]/EducationRailFromFirstMarker[1]", + "entityName" : "EducationRailFromFirstMarker", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 20.6, + "computedY" : 182.434, + "placementX" : 20.6, + "placementY" : 182.434, + "placementWidth" : 0.55, + "placementHeight" : 2.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.55, + "contentHeight" : 2.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]/EducationMarker_0[2]", + "entityName" : "EducationMarker_0", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]", + "childIndex" : 2, + "depth" : 8, + "layer" : 8, + "computedX" : 18.45, + "computedY" : 184.934, + "placementX" : 18.45, + "placementY" : 184.934, + "placementWidth" : 4.3, + "placementHeight" : 4.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 4.3, + "contentHeight" : 4.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]/EducationDegree_0[3]", + "entityName" : "EducationDegree_0", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_0[0]", + "childIndex" : 3, + "depth" : 8, + "layer" : 8, + "computedX" : 33.6, + "computedY" : 182.704, + "placementX" : 33.6, + "placementY" : 182.704, + "placementWidth" : 51.713, + "placementHeight" : 8.76, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 51.713, + "contentHeight" : 8.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationInstitution_0[1]", + "entityName" : "EducationInstitution_0", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 33.6, + "computedY" : 171.414, + "placementX" : 33.6, + "placementY" : 171.414, + "placementWidth" : 49.21, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 49.21, + "contentHeight" : 8.52, + "margin" : { + "top" : 2.5, + "right" : 0.0, + "bottom" : 2.5, + "left" : 13.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationDates_0[2]", + "entityName" : "EducationDates_0", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "childIndex" : 2, + "depth" : 7, + "layer" : 7, + "computedX" : 33.6, + "computedY" : 160.394, + "placementX" : 33.6, + "placementY" : 160.394, + "placementWidth" : 40.697, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 40.697, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 11.0, + "left" : 13.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_1[3]", + "entityName" : "EducationHead_1", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "childIndex" : 3, + "depth" : 7, + "layer" : 7, + "computedX" : 20.6, + "computedY" : 140.094, + "placementX" : 20.6, + "placementY" : 140.094, + "placementWidth" : 104.656, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 104.656, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_1[3]/EducationMarker_1[0]", + "entityName" : "EducationMarker_1", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_1[3]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 18.45, + "computedY" : 142.594, + "placementX" : 18.45, + "placementY" : 142.594, + "placementWidth" : 4.3, + "placementHeight" : 4.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 4.3, + "contentHeight" : 4.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_1[3]/EducationDegree_1[1]", + "entityName" : "EducationDegree_1", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationHead_1[3]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 33.6, + "computedY" : 140.364, + "placementX" : 33.6, + "placementY" : 140.364, + "placementWidth" : 51.713, + "placementHeight" : 8.76, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 51.713, + "contentHeight" : 8.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationInstitution_1[4]", + "entityName" : "EducationInstitution_1", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "childIndex" : 4, + "depth" : 7, + "layer" : 7, + "computedX" : 33.6, + "computedY" : 129.074, + "placementX" : 33.6, + "placementY" : 129.074, + "placementWidth" : 49.21, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 49.21, + "contentHeight" : 8.52, + "margin" : { + "top" : 2.5, + "right" : 0.0, + "bottom" : 2.5, + "left" : 13.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]/EducationDates_1[5]", + "entityName" : "EducationDates_1", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Education[4]/EducationRail[2]", + "childIndex" : 5, + "depth" : 7, + "layer" : 7, + "computedX" : 33.6, + "computedY" : 118.054, + "placementX" : 33.6, + "placementY" : 118.054, + "placementWidth" : 40.697, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 40.697, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 13.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/SidebarDivider[5]", + "entityName" : "SidebarDivider", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 109.054, + "placementX" : 17.8, + "placementY" : 109.054, + "placementWidth" : 107.456, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 1.0, + "margin" : { + "top" : 8.0, + "right" : 0.0, + "bottom" : 16.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]", + "entityName" : "Languages", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 17.8, + "computedY" : 24.204, + "placementX" : 17.8, + "placementY" : 24.204, + "placementWidth" : 107.456, + "placementHeight" : 68.85, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 68.85, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/SidebarHeading_LANGUAGES[0]", + "entityName" : "SidebarHeading_LANGUAGES", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 82.854, + "placementX" : 17.8, + "placementY" : 82.854, + "placementWidth" : 60.253, + "placementHeight" : 10.2, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 60.253, + "contentHeight" : 10.2, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/SidebarHeadingRule_LANGUAGES[1]", + "entityName" : "SidebarHeadingRule_LANGUAGES", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 77.804, + "placementX" : 17.8, + "placementY" : 77.804, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 13.5, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]", + "entityName" : "LanguageRows", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]", + "childIndex" : 2, + "depth" : 6, + "layer" : 6, + "computedX" : 17.8, + "computedY" : 24.204, + "placementX" : 17.8, + "placementY" : 24.204, + "placementWidth" : 107.456, + "placementHeight" : 40.1, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 40.1, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]", + "entityName" : "LanguageRow_English", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]", + "childIndex" : 0, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 55.804, + "placementX" : 17.8, + "placementY" : 55.804, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/Language_English[0]", + "entityName" : "Language_English", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 55.784, + "placementX" : 17.8, + "placementY" : 55.784, + "placementWidth" : 22.173, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 22.173, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]", + "entityName" : "LanguageRating", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 70.453, + "computedY" : 57.554, + "placementX" : 70.453, + "placementY" : 57.554, + "placementWidth" : 45.4, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 45.4, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]/LanguageDot_0[0]", + "entityName" : "LanguageDot_0", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 70.453, + "computedY" : 57.554, + "placementX" : 70.453, + "placementY" : 57.554, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]/LanguageDot_1[1]", + "entityName" : "LanguageDot_1", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 80.553, + "computedY" : 57.554, + "placementX" : 80.553, + "placementY" : 57.554, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]/LanguageDot_2[2]", + "entityName" : "LanguageDot_2", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 90.653, + "computedY" : 57.554, + "placementX" : 90.653, + "placementY" : 57.554, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]/LanguageDot_3[3]", + "entityName" : "LanguageDot_3", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]", + "childIndex" : 3, + "depth" : 9, + "layer" : 9, + "computedX" : 100.753, + "computedY" : 57.554, + "placementX" : 100.753, + "placementY" : 57.554, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]/LanguageDot_4[4]", + "entityName" : "LanguageDot_4", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_English[0]/LanguageRating[1]", + "childIndex" : 4, + "depth" : 9, + "layer" : 9, + "computedX" : 110.853, + "computedY" : 57.554, + "placementX" : 110.853, + "placementY" : 57.554, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]", + "entityName" : "LanguageRow_Ukrainian", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]", + "childIndex" : 1, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 40.004, + "placementX" : 17.8, + "placementY" : 40.004, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/Language_Ukrainian[0]", + "entityName" : "Language_Ukrainian", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 39.984, + "placementX" : 17.8, + "placementY" : 39.984, + "placementWidth" : 29.806, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 29.806, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]", + "entityName" : "LanguageRating", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 70.453, + "computedY" : 41.754, + "placementX" : 70.453, + "placementY" : 41.754, + "placementWidth" : 45.4, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 45.4, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]/LanguageDot_0[0]", + "entityName" : "LanguageDot_0", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 70.453, + "computedY" : 41.754, + "placementX" : 70.453, + "placementY" : 41.754, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]/LanguageDot_1[1]", + "entityName" : "LanguageDot_1", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 80.553, + "computedY" : 41.754, + "placementX" : 80.553, + "placementY" : 41.754, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]/LanguageDot_2[2]", + "entityName" : "LanguageDot_2", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 90.653, + "computedY" : 41.754, + "placementX" : 90.653, + "placementY" : 41.754, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]/LanguageDot_3[3]", + "entityName" : "LanguageDot_3", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]", + "childIndex" : 3, + "depth" : 9, + "layer" : 9, + "computedX" : 100.753, + "computedY" : 41.754, + "placementX" : 100.753, + "placementY" : 41.754, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]/LanguageDot_4[4]", + "entityName" : "LanguageDot_4", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Ukrainian[1]/LanguageRating[1]", + "childIndex" : 4, + "depth" : 9, + "layer" : 9, + "computedX" : 110.853, + "computedY" : 41.754, + "placementX" : 110.853, + "placementY" : 41.754, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]", + "entityName" : "LanguageRow_Russian", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]", + "childIndex" : 2, + "depth" : 7, + "layer" : 7, + "computedX" : 17.8, + "computedY" : 24.204, + "placementX" : 17.8, + "placementY" : 24.204, + "placementWidth" : 107.456, + "placementHeight" : 8.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 107.456, + "contentHeight" : 8.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/Language_Russian[0]", + "entityName" : "Language_Russian", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]", + "childIndex" : 0, + "depth" : 8, + "layer" : 8, + "computedX" : 17.8, + "computedY" : 24.184, + "placementX" : 17.8, + "placementY" : 24.184, + "placementWidth" : 23.757, + "placementHeight" : 8.52, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 23.757, + "contentHeight" : 8.52, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]", + "entityName" : "LanguageRating", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]", + "childIndex" : 1, + "depth" : 8, + "layer" : 8, + "computedX" : 70.453, + "computedY" : 25.954, + "placementX" : 70.453, + "placementY" : 25.954, + "placementWidth" : 45.4, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 45.4, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]/LanguageDot_0[0]", + "entityName" : "LanguageDot_0", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]", + "childIndex" : 0, + "depth" : 9, + "layer" : 9, + "computedX" : 70.453, + "computedY" : 25.954, + "placementX" : 70.453, + "placementY" : 25.954, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]/LanguageDot_1[1]", + "entityName" : "LanguageDot_1", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]", + "childIndex" : 1, + "depth" : 9, + "layer" : 9, + "computedX" : 80.553, + "computedY" : 25.954, + "placementX" : 80.553, + "placementY" : 25.954, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]/LanguageDot_2[2]", + "entityName" : "LanguageDot_2", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]", + "childIndex" : 2, + "depth" : 9, + "layer" : 9, + "computedX" : 90.653, + "computedY" : 25.954, + "placementX" : 90.653, + "placementY" : 25.954, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]/LanguageDot_3[3]", + "entityName" : "LanguageDot_3", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]", + "childIndex" : 3, + "depth" : 9, + "layer" : 9, + "computedX" : 100.753, + "computedY" : 25.954, + "placementX" : 100.753, + "placementY" : 25.954, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]/LanguageDot_4[4]", + "entityName" : "LanguageDot_4", + "entityKind" : "EllipseNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Sidebar[0]/SidebarBody[1]/Languages[6]/LanguageRows[2]/LanguageRow_Russian[2]/LanguageRating[1]", + "childIndex" : 4, + "depth" : 9, + "layer" : 9, + "computedX" : 110.853, + "computedY" : 25.954, + "placementX" : 110.853, + "placementY" : 25.954, + "placementWidth" : 5.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 5.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]", + "entityName" : "Main", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]", + "childIndex" : 1, + "depth" : 3, + "layer" : 3, + "computedX" : 143.056, + "computedY" : 12.73, + "placementX" : 143.056, + "placementY" : 12.73, + "placementWidth" : 348.544, + "placementHeight" : 724.55, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 348.544, + "contentHeight" : 724.55, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 21.9, + "right" : 20.0, + "bottom" : 0.0, + "left" : 27.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]", + "entityName" : "Identity", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]", + "childIndex" : 0, + "depth" : 4, + "layer" : 4, + "computedX" : 170.056, + "computedY" : 617.19, + "placementX" : 170.056, + "placementY" : 617.19, + "placementWidth" : 301.544, + "placementHeight" : 98.19, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 98.19, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]/DisplayName[0]", + "entityName" : "DisplayName", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 671.34, + "placementX" : 170.056, + "placementY" : 671.34, + "placementWidth" : 176.476, + "placementHeight" : 44.04, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 176.476, + "contentHeight" : 44.04, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 6.3, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]/ProfessionalTitle[1]", + "entityName" : "ProfessionalTitle", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 653.64, + "placementX" : 170.056, + "placementY" : 653.64, + "placementWidth" : 163.438, + "placementHeight" : 11.4, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 163.438, + "contentHeight" : 11.4, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 14.9, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]/IdentityRule[2]", + "entityName" : "IdentityRule", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 637.69, + "placementX" : 170.056, + "placementY" : 637.69, + "placementWidth" : 301.544, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]/IdentityRule[2]/IdentityAccentSegment[0]", + "entityName" : "IdentityAccentSegment", + "entityKind" : "ShapeNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]/IdentityRule[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 637.69, + "placementX" : 170.056, + "placementY" : 637.69, + "placementWidth" : 27.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 27.0, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]/SpacerNode[3]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Identity[0]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 617.19, + "placementX" : 170.056, + "placementY" : 617.19, + "placementWidth" : 0.0, + "placementHeight" : 20.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 20.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]", + "entityName" : "Profile", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]", + "childIndex" : 1, + "depth" : 4, + "layer" : 4, + "computedX" : 170.056, + "computedY" : 525.58, + "placementX" : 170.056, + "placementY" : 525.58, + "placementWidth" : 287.543, + "placementHeight" : 91.61, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 287.543, + "contentHeight" : 91.61, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]/MainHeading_PROFILE[0]", + "entityName" : "MainHeading_PROFILE", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 606.75, + "placementX" : 170.056, + "placementY" : 606.75, + "placementWidth" : 42.147, + "placementHeight" : 10.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 42.147, + "contentHeight" : 10.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]/MainHeadingRule_PROFILE[1]", + "entityName" : "MainHeadingRule_PROFILE", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 601.7, + "placementX" : 170.056, + "placementY" : 601.7, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 14.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]/ProfileText[2]", + "entityName" : "ProfileText", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 550.08, + "placementX" : 170.056, + "placementY" : 550.08, + "placementWidth" : 287.543, + "placementHeight" : 37.62, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 287.543, + "contentHeight" : 37.62, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]/SpacerNode[3]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Profile[1]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 525.58, + "placementX" : 170.056, + "placementY" : 525.58, + "placementWidth" : 0.0, + "placementHeight" : 24.5, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 24.5, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "entityName" : "Experience", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]", + "childIndex" : 2, + "depth" : 4, + "layer" : 4, + "computedX" : 170.056, + "computedY" : 165.63, + "placementX" : 170.056, + "placementY" : 165.63, + "placementWidth" : 301.544, + "placementHeight" : 359.95, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 359.95, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/MainHeading_EXPERIENCE[0]", + "entityName" : "MainHeading_EXPERIENCE", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 515.14, + "placementX" : 170.056, + "placementY" : 515.14, + "placementWidth" : 62.533, + "placementHeight" : 10.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 62.533, + "contentHeight" : 10.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/MainHeadingRule_EXPERIENCE[1]", + "entityName" : "MainHeadingRule_EXPERIENCE", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 510.09, + "placementX" : 170.056, + "placementY" : 510.09, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 18.3, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_0[2]", + "entityName" : "ExperienceHead_0", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 482.49, + "placementX" : 170.056, + "placementY" : 482.49, + "placementWidth" : 301.544, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_0[2]/ExperienceHead_0Title[0]", + "entityName" : "ExperienceHead_0Title", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_0[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 482.64, + "placementX" : 170.056, + "placementY" : 482.64, + "placementWidth" : 101.858, + "placementHeight" : 9.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 101.858, + "contentHeight" : 9.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_0[2]/ExperienceHead_0Dates[1]", + "entityName" : "ExperienceHead_0Dates", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_0[2]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 482.82, + "placementX" : 170.056, + "placementY" : 482.82, + "placementWidth" : 301.544, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 8.64, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceMeta_0[3]", + "entityName" : "ExperienceMeta_0", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 471.85, + "placementX" : 170.056, + "placementY" : 471.85, + "placementWidth" : 91.62, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 91.62, + "contentHeight" : 8.64, + "margin" : { + "top" : 2.0, + "right" : 0.0, + "bottom" : 14.3, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHighlights_0[4]", + "entityName" : "ExperienceHighlights_0", + "entityKind" : "ListNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 409.07, + "placementX" : 170.056, + "placementY" : 409.07, + "placementWidth" : 208.058, + "placementHeight" : 48.48, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 208.058, + "contentHeight" : 48.48, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/MainDivider[5]", + "entityName" : "MainDivider", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 394.07, + "placementX" : 170.056, + "placementY" : 394.07, + "placementWidth" : 301.544, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 1.0, + "margin" : { + "top" : 14.0, + "right" : 0.0, + "bottom" : 13.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_1[6]", + "entityName" : "ExperienceHead_1", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 371.77, + "placementX" : 170.056, + "placementY" : 371.77, + "placementWidth" : 301.544, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_1[6]/ExperienceHead_1Title[0]", + "entityName" : "ExperienceHead_1Title", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_1[6]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 371.92, + "placementX" : 170.056, + "placementY" : 371.92, + "placementWidth" : 86.258, + "placementHeight" : 9.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 86.258, + "contentHeight" : 9.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_1[6]/ExperienceHead_1Dates[1]", + "entityName" : "ExperienceHead_1Dates", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_1[6]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 372.1, + "placementX" : 170.056, + "placementY" : 372.1, + "placementWidth" : 301.544, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 8.64, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceMeta_1[7]", + "entityName" : "ExperienceMeta_1", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 7, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 361.13, + "placementX" : 170.056, + "placementY" : 361.13, + "placementWidth" : 120.643, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 120.643, + "contentHeight" : 8.64, + "margin" : { + "top" : 2.0, + "right" : 0.0, + "bottom" : 14.3, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHighlights_1[8]", + "entityName" : "ExperienceHighlights_1", + "entityKind" : "ListNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 8, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 298.35, + "placementX" : 170.056, + "placementY" : 298.35, + "placementWidth" : 194.696, + "placementHeight" : 48.48, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 194.696, + "contentHeight" : 48.48, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/MainDivider[9]", + "entityName" : "MainDivider", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 9, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 283.35, + "placementX" : 170.056, + "placementY" : 283.35, + "placementWidth" : 301.544, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 1.0, + "margin" : { + "top" : 14.0, + "right" : 0.0, + "bottom" : 13.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_2[10]", + "entityName" : "ExperienceHead_2", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 10, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 261.05, + "placementX" : 170.056, + "placementY" : 261.05, + "placementWidth" : 301.544, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_2[10]/ExperienceHead_2Title[0]", + "entityName" : "ExperienceHead_2Title", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_2[10]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 261.2, + "placementX" : 170.056, + "placementY" : 261.2, + "placementWidth" : 93.48, + "placementHeight" : 9.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 93.48, + "contentHeight" : 9.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_2[10]/ExperienceHead_2Dates[1]", + "entityName" : "ExperienceHead_2Dates", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHead_2[10]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 261.38, + "placementX" : 170.056, + "placementY" : 261.38, + "placementWidth" : 301.544, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 8.64, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceMeta_2[11]", + "entityName" : "ExperienceMeta_2", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 11, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 250.41, + "placementX" : 170.056, + "placementY" : 250.41, + "placementWidth" : 68.875, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 68.875, + "contentHeight" : 8.64, + "margin" : { + "top" : 2.0, + "right" : 0.0, + "bottom" : 14.3, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/ExperienceHighlights_2[12]", + "entityName" : "ExperienceHighlights_2", + "entityKind" : "ListNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 12, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 187.63, + "placementX" : 170.056, + "placementY" : 187.63, + "placementWidth" : 193.731, + "placementHeight" : 48.48, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 193.731, + "contentHeight" : 48.48, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]/SpacerNode[13]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Experience[2]", + "childIndex" : 13, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 165.63, + "placementX" : 170.056, + "placementY" : 165.63, + "placementWidth" : 0.0, + "placementHeight" : 22.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "entityName" : "Projects", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]", + "childIndex" : 3, + "depth" : 4, + "layer" : 4, + "computedX" : 170.056, + "computedY" : 45.1, + "placementX" : 170.056, + "placementY" : 45.1, + "placementWidth" : 301.544, + "placementHeight" : 120.53, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 120.53, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/MainHeading_PROJECTS[0]", + "entityName" : "MainHeading_PROJECTS", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 155.19, + "placementX" : 170.056, + "placementY" : 155.19, + "placementWidth" : 50.795, + "placementHeight" : 10.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 50.795, + "contentHeight" : 10.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/MainHeadingRule_PROJECTS[1]", + "entityName" : "MainHeadingRule_PROJECTS", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 150.14, + "placementX" : 170.056, + "placementY" : 150.14, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_0[2]", + "entityName" : "ProjectHead_0", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 132.84, + "placementX" : 170.056, + "placementY" : 132.84, + "placementWidth" : 301.544, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_0[2]/ProjectHead_0Title[0]", + "entityName" : "ProjectHead_0Title", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_0[2]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 132.99, + "placementX" : 170.056, + "placementY" : 132.99, + "placementWidth" : 47.88, + "placementHeight" : 9.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 47.88, + "contentHeight" : 9.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_0[2]/ProjectHead_0Dates[1]", + "entityName" : "ProjectHead_0Dates", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_0[2]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 133.17, + "placementX" : 170.056, + "placementY" : 133.17, + "placementWidth" : 301.544, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 8.64, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/SpacerNode[3]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 3, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 127.84, + "placementX" : 170.056, + "placementY" : 127.84, + "placementWidth" : 0.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectDescription_0[4]", + "entityName" : "ProjectDescription_0", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 4, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 109.62, + "placementX" : 170.056, + "placementY" : 109.62, + "placementWidth" : 289.197, + "placementHeight" : 18.22, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 289.197, + "contentHeight" : 18.22, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/MainDivider[5]", + "entityName" : "MainDivider", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 5, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 102.62, + "placementX" : 170.056, + "placementY" : 102.62, + "placementWidth" : 301.544, + "placementHeight" : 1.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 1.0, + "margin" : { + "top" : 6.0, + "right" : 0.0, + "bottom" : 7.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_1[6]", + "entityName" : "ProjectHead_1", + "entityKind" : "ShapeContainerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 6, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 86.32, + "placementX" : 170.056, + "placementY" : 86.32, + "placementWidth" : 301.544, + "placementHeight" : 9.3, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 9.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_1[6]/ProjectHead_1Title[0]", + "entityName" : "ProjectHead_1Title", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_1[6]", + "childIndex" : 0, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 86.47, + "placementX" : 170.056, + "placementY" : 86.47, + "placementWidth" : 63.15, + "placementHeight" : 9.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 63.15, + "contentHeight" : 9.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_1[6]/ProjectHead_1Dates[1]", + "entityName" : "ProjectHead_1Dates", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectHead_1[6]", + "childIndex" : 1, + "depth" : 6, + "layer" : 6, + "computedX" : 170.056, + "computedY" : 86.65, + "placementX" : 170.056, + "placementY" : 86.65, + "placementWidth" : 301.544, + "placementHeight" : 8.64, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 301.544, + "contentHeight" : 8.64, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/SpacerNode[7]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 7, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 81.32, + "placementX" : 170.056, + "placementY" : 81.32, + "placementWidth" : 0.0, + "placementHeight" : 5.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 5.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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/ProjectDescription_1[8]", + "entityName" : "ProjectDescription_1", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 8, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 63.1, + "placementX" : 170.056, + "placementY" : 63.1, + "placementWidth" : 296.901, + "placementHeight" : 18.22, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 296.901, + "contentHeight" : 18.22, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]/SpacerNode[9]", + "entityName" : null, + "entityKind" : "SpacerNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/Projects[3]", + "childIndex" : 9, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 45.1, + "placementX" : 170.056, + "placementY" : 45.1, + "placementWidth" : 0.0, + "placementHeight" : 18.0, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 0.0, + "contentHeight" : 18.0, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]", + "entityName" : "References", + "entityKind" : "SectionNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]", + "childIndex" : 4, + "depth" : 4, + "layer" : 4, + "computedX" : 170.056, + "computedY" : 12.73, + "placementX" : 170.056, + "placementY" : 12.73, + "placementWidth" : 76.287, + "placementHeight" : 32.37, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 76.287, + "contentHeight" : 32.37, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]/MainHeading_REFERENCES[0]", + "entityName" : "MainHeading_REFERENCES", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]", + "childIndex" : 0, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 34.66, + "placementX" : 170.056, + "placementY" : 34.66, + "placementWidth" : 64.021, + "placementHeight" : 10.44, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 64.021, + "contentHeight" : 10.44, + "margin" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 4.0, + "left" : 0.0 + }, + "padding" : { + "top" : 0.0, + "right" : 0.0, + "bottom" : 0.0, + "left" : 0.0 + } + }, { + "path" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]/MainHeadingRule_REFERENCES[1]", + "entityName" : "MainHeadingRule_REFERENCES", + "entityKind" : "LineNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]", + "childIndex" : 1, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 29.61, + "placementX" : 170.056, + "placementY" : 29.61, + "placementWidth" : 19.0, + "placementHeight" : 1.05, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 19.0, + "contentHeight" : 1.05, + "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" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]/ReferencesNote[2]", + "entityName" : "ReferencesNote", + "entityKind" : "ParagraphNode", + "parentPath" : "ProfessionalSidebarCv[0]/PageGrid[0]/Main[1]/References[4]", + "childIndex" : 2, + "depth" : 5, + "layer" : 5, + "computedX" : 170.056, + "computedY" : 12.73, + "placementX" : 170.056, + "placementY" : 12.73, + "placementWidth" : 76.287, + "placementHeight" : 8.88, + "startPage" : 0, + "endPage" : 0, + "contentWidth" : 76.287, + "contentHeight" : 8.88, + "margin" : { + "top" : 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/professional_sidebar-page-0.png b/qa/src/test/resources/visual-baselines/cv-v2-layered/professional_sidebar-page-0.png new file mode 100644 index 000000000..2d4d70135 Binary files /dev/null and b/qa/src/test/resources/visual-baselines/cv-v2-layered/professional_sidebar-page-0.png differ diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebar.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebar.java new file mode 100644 index 000000000..2dcca81e6 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebar.java @@ -0,0 +1,220 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.api.PageBackgroundFill; +import com.demcha.compose.document.style.DocumentInsets; +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.ProfessionalSidebarStyles.MAIN_WEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PAGE_BACKGROUND; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PAGE_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PAGE_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_BACKGROUND; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_WEIGHT; + +/** + * Professional Sidebar — a one-page CV in two columns: a pale sidebar under + * a navy monogram plate carrying the contact channels, the skill meters, the + * education rail and the language ratings, and a white main column carrying + * the name, the profile, the roles held, the projects and the references + * note. + * + *

The preset owns its page: a 491.6 x 737.28pt sheet with no margin, the + * page fill and the pale sidebar painted as page backgrounds so the column + * reaches the foot of the sheet whatever the sidebar holds.

+ * + *

One page, and what happens past it

+ * + *

This sheet holds one page of content. 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. Feed it a CV of about the length the design was drawn around: five + * or six roles with three or four highlights each, alongside the sidebar + * blocks.

+ * + *

The preset draws no cap of its own, which is the deliberate half of + * that: its siblings {@link MonogramSidebar}, {@link SidebarPortrait} and + * {@link MintEditorial} cap each block and silently drop what does not fit, + * and 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}: profile, experience, + * projects and references to the main column; skills, education and + * languages to the sidebar. A section whose title matches no berth is not + * drawn, and a berth with no section takes its leading hairline with it.

+ * + *

A berth is filled by the first section that names it, so a second + * section naming the same berth is not drawn. Neither is anything the design + * has no place for: a project's {@code subtitle}, an education entry's + * {@code body}, and a {@code SkillGroup}'s {@code category} — this sheet + * heads its projects with a title and dates, its degrees with an institution + * and dates, and sets its skills as one ungrouped list.

+ * + *

Both the skills and the languages are {@link SkillsSection}s — the same + * shape, drawn differently: a skill's level fills a bar, a language's fills + * the nearest whole number of five dots. A level the document omits draws + * the name alone.

+ * + *

The sidebar's rows are one line each: a skill name, a language name and + * an education degree are anchored inside a fixed-height band, so text + * longer than the narrow column measures overflows the band instead of + * wrapping inside it. That is the design — the sheet is drawn around short + * labels — and it is the one length constraint a document has to respect.

+ * + *

The contact channels come off the identity rather than a section: the + * phone, the email and the address take the packaged marks, and each link + * takes the network mark when it names one and the globe otherwise. The + * phone and email rows carry {@code tel:} and {@code mailto:} targets built + * from the value.

+ * + *

Fonts

+ * + *

The sheet is set in Barlow Condensed and Lato. The templates artifact + * does not carry either face — 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 = ProfessionalSidebar.create();
+ * template.compose(session, cv);
+ * }
+ */ +public final class ProfessionalSidebar { + + /** Stable identifier of this preset. */ + public static final String ID = "professional-sidebar"; + + /** Human-readable name of this preset. */ + public static final String DISPLAY_NAME = "Professional Sidebar"; + + /** + * The margin this preset expects: none. It sets the page margin itself, + * because both columns run to the paper edge. + */ + public static final double RECOMMENDED_MARGIN = 0.0; + + private static final List PROFILE_KEYS = + List.of("profile", "summary", "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"); + private static final List REFERENCES_KEYS = + List.of("references", "reference"); + private static final List SKILL_KEYS = + List.of("skills", "expertise", "competencies"); + private static final List EDUCATION_KEYS = + List.of("education", "qualifications", "certifications"); + private static final List LANGUAGE_KEYS = + List.of("languages", "language"); + + private ProfessionalSidebar() { + } + + /** + * Creates the template. + * + * @return a template composing a {@link CvDocument} onto its own page + */ + 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(); + + document.pageSize(PAGE_WIDTH, PAGE_HEIGHT) + .margin(DocumentInsets.zero()) + // The sidebar fill is a page background rather than a + // section fill: a section is only as tall as what it + // holds, and the pale column has to reach the foot of + // the sheet whatever the sidebar carries. + .pageBackgrounds(List.of( + PageBackgroundFill.fullPage(PAGE_BACKGROUND), + PageBackgroundFill.leftColumn(SIDEBAR_WEIGHT, + SIDEBAR_BACKGROUND))); + + SkillsSection skills = berth(sections, SkillsSection.class, SKILL_KEYS); + EntriesSection education = berth(sections, EntriesSection.class, EDUCATION_KEYS); + // A title can name both berths — "Skills and Languages" does — + // and one section cannot fill two, so the second berth stays + // empty rather than drawing the same list twice in two hands. + SkillsSection named = berth(sections, SkillsSection.class, LANGUAGE_KEYS); + SkillsSection languages = named == skills ? null : named; + ParagraphSection profile = berth(sections, ParagraphSection.class, PROFILE_KEYS); + EntriesSection experience = berth(sections, EntriesSection.class, EXPERIENCE_KEYS); + EntriesSection projects = berth(sections, EntriesSection.class, PROJECT_KEYS); + ParagraphSection references = + berth(sections, ParagraphSection.class, REFERENCES_KEYS); + + document.pageFlow(page -> page + .name("ProfessionalSidebarCv") + .padding(DocumentInsets.zero()) + // spacing(0): every gap in this design is authored on the + // node that owns it, so a flow gap would add to all of + // them. + .spacing(0) + .addRow("PageGrid", row -> { + row.spacing(0); + row.weights(SIDEBAR_WEIGHT, MAIN_WEIGHT); + row.addSection("Sidebar", aside -> + ProfessionalSidebarAside.compose(aside, doc.identity(), + skills, education, languages)); + row.addSection("Main", main -> + ProfessionalSidebarMain.compose(main, doc.identity(), + profile, experience, projects, references)); + })); + } + + /** + * 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) { + for (CvSection section : sections) { + if (!type.isInstance(section)) { + 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/ProfessionalSidebarAside.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarAside.java new file mode 100644 index 000000000..a22ae816d --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarAside.java @@ -0,0 +1,444 @@ +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.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +import com.demcha.compose.document.image.DocumentImageData; +import com.demcha.compose.document.node.DocumentLinkOptions; +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.ClipPolicy; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.templates.core.identity.Link; +import com.demcha.compose.document.templates.cv.components.SectionLookup; +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.ProfessionalSidebarStyles.ACCENT_PRIMARY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.BODY_FONT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.BODY_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.CONTACT_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.CONTACT_ROW_GAP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.CONTACT_TO_SKILLS_ABOVE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.CONTACT_TO_SKILLS_BELOW; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_DEGREE_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_ENTRY_GAP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_LINE_GAP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_MARKER_DIAMETER; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_RAIL_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_RAIL_X; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_TEXT_X; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_TO_LANGUAGES_ABOVE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EDUCATION_TO_LANGUAGES_BELOW; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ENTRY_HEAD_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.HEADER_PLATE_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_DOTS; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_DOT_DIAMETER; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_DOT_PITCH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_RATING_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_RATING_X; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_ROW_GAP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.LANGUAGE_ROW_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MONOGRAM_DIAMETER; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MONOGRAM_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MONOGRAM_STROKE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MONOGRAM_TRACKING_EM; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.RATING_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.RULE_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_BACKGROUND; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_BODY_TOP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_INNER_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_PAD_X; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SKILLS_TO_EDUCATION_ABOVE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SKILLS_TO_EDUCATION_BELOW; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SKILL_ROW_GAP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SKILL_ROW_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SKILL_TRACK_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SKILL_TRACK_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PLATE_BACKGROUND; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.STANDARD_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.TEXT_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.TEXT_PRIMARY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.body; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.clamp01; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.compact; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.style; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.tracked; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.paragraph; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.sidebarDivider; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.sidebarHeading; + +/** + * The pale left column: the navy monogram plate, the contact channels, the + * skill meters, the education rail and the language ratings. + * + *

The column has four berths in a fixed order, each with its own measured + * gap to the one above. A berth with nothing to draw takes its leading + * hairline with it, so a CV that carries no languages does not end on a rule + * with empty space under it.

+ */ +final class ProfessionalSidebarAside { + + /** + * The contact heading. It is not read from the document because the + * channels are not a section — they come off the identity, which carries + * no title of its own. + */ + static final String CONTACT_HEADING = "CONTACT"; + + private ProfessionalSidebarAside() { + } + + static void compose(SectionBuilder section, + CvIdentity identity, + SkillsSection skills, + EntriesSection education, + SkillsSection languages) { + section.spacing(0); + section.add(monogramPlate(identity)); + section.addSection("SidebarBody", body -> { + body.spacing(0); + body.padding(new DocumentInsets(SIDEBAR_BODY_TOP, SIDEBAR_PAD_X, 0, SIDEBAR_PAD_X)); + body.addSection("Contact", contact -> renderContact(contact, identity)); + if (SectionLookup.hasContent(skills)) { + sidebarDivider(body, CONTACT_TO_SKILLS_ABOVE, CONTACT_TO_SKILLS_BELOW); + body.addSection("Skills", host -> renderSkills(host, skills)); + } + if (SectionLookup.hasContent(education)) { + sidebarDivider(body, SKILLS_TO_EDUCATION_ABOVE, SKILLS_TO_EDUCATION_BELOW); + body.addSection("Education", host -> renderEducation(host, education)); + } + if (SectionLookup.hasContent(languages)) { + sidebarDivider(body, EDUCATION_TO_LANGUAGES_ABOVE, EDUCATION_TO_LANGUAGES_BELOW); + body.addSection("Languages", host -> renderLanguages(host, languages)); + } + }); + } + + // -- monogram -------------------------------------------------------- + + /** + * The navy plate that caps the column, with the initials set inside a + * hairline ring. + * + *

The plate is drawn rather than painted as a page background because + * it is the first thing in the flow: the sidebar content stacks under it, + * and a background would not push anything down.

+ */ + private static DocumentNode monogramPlate(CvIdentity identity) { + DocumentTextStyle initialsStyle = style(BODY_FONT, MONOGRAM_SIZE, + DocumentColor.WHITE, DocumentTextDecoration.DEFAULT); + ParagraphBuilder initials = new ParagraphBuilder() + .name("MonogramInitials") + .textStyle(initialsStyle) + .align(TextAlign.CENTER); + tracked(initials, initials(identity), initialsStyle, MONOGRAM_TRACKING_EM); + + DocumentNode ring = new ShapeContainerBuilder() + .name("MonogramRing") + .circle(MONOGRAM_DIAMETER) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE) + .stroke(DocumentStroke.of(DocumentColor.WHITE, MONOGRAM_STROKE)) + .center(initials.margin(DocumentInsets.zero()).build()) + .build(); + + return new ShapeContainerBuilder() + .name("MonogramPlate") + .rectangle(SIDEBAR_WIDTH, HEADER_PLATE_HEIGHT) + .fillColor(PLATE_BACKGROUND) + .center(ring) + .build(); + } + + /** First letter of the given name, first letter of the family name. */ + private static String initials(CvIdentity identity) { + String first = initial(identity.name().first()); + String last = initial(identity.name().last()); + return (first + last).toUpperCase(Locale.ROOT); + } + + private static String initial(String value) { + return value == null || value.isBlank() ? "" : value.trim().substring(0, 1); + } + + // -- contact --------------------------------------------------------- + + /** + * The contact channels, in the order the design sets them: phone, email, + * address, then whatever links the identity carries. + * + *

The phone and email rows link to {@code tel:} and {@code mailto:} + * targets built from the value, so the channels are dialable and + * writable from the PDF without the document carrying the URI twice.

+ */ + private static void renderContact(SectionBuilder section, CvIdentity identity) { + sidebarHeading(section, CONTACT_HEADING, CONTACT_HEADING_TO_BODY); + List channels = channels(identity); + for (int i = 0; i < channels.size(); i++) { + Channel channel = channels.get(i); + double gapBelow = i + 1 < channels.size() ? CONTACT_ROW_GAP : 0; + renderChannel(section, channel, gapBelow); + } + } + + private static void renderChannel(SectionBuilder section, Channel channel, + double gapBelow) { + DocumentImageData icon = ProfessionalSidebarIcons.image(channel.token()); + double size = ProfessionalSidebarIcons.size(channel.token()); + DocumentLinkOptions link = channel.href() == null + ? null + : new DocumentLinkOptions(channel.href()); + ParagraphBuilder row = new ParagraphBuilder() + .name("Contact_" + compact(channel.token())) + .textStyle(body()) + .inlineImage(icon, size, size, InlineImageAlignment.CENTER, 0, link); + // The gap between the mark and the value is set as spaces rather than + // an indent: the row is one paragraph, so the mark and the text share + // a baseline and wrap together. + if (link == null) { + row.inlineText(" " + channel.value(), body()); + } else { + row.inlineText(" " + channel.value(), body(), link); + } + section.add(row.margin(new DocumentInsets(0, 0, gapBelow, 0)).build()); + } + + private static List channels(CvIdentity identity) { + List channels = new ArrayList<>(); + String phone = identity.contact().phone(); + channels.add(new Channel(ProfessionalSidebarIcons.PHONE, phone, telUri(phone))); + String email = identity.contact().email(); + channels.add(new Channel(ProfessionalSidebarIcons.EMAIL, email, "mailto:" + email)); + channels.add(new Channel(ProfessionalSidebarIcons.LOCATION, + identity.contact().address(), null)); + for (Link link : identity.links()) { + channels.add(new Channel(linkToken(link), link.label(), link.url())); + } + return channels; + } + + /** + * The dial target for a phone number: its digits, keeping a leading + * {@code +} so an international number stays international. + */ + private static String telUri(String phone) { + String digits = phone.replaceAll("[^0-9]", ""); + return digits.isEmpty() + ? null + : "tel:" + (phone.trim().startsWith("+") ? "+" : "") + digits; + } + + /** + * The mark for a link. The packaged set names one network; everything + * else is a site, which is what the globe stands for. + */ + private static String linkToken(Link link) { + String haystack = SectionLookup.normalize(link.label() + " " + link.url()); + return haystack.contains("linkedin") + ? ProfessionalSidebarIcons.LINKEDIN + : ProfessionalSidebarIcons.WEBSITE; + } + + private record Channel(String token, String value, String href) { + } + + // -- skills ---------------------------------------------------------- + + /** + * Skill rows: the name at the left of a fixed-height band, the meter at + * the right. A skill the document leaves unlevelled draws its name alone + * rather than an empty track. + */ + private static void renderSkills(SectionBuilder section, SkillsSection skills) { + sidebarHeading(section, skills.title(), STANDARD_HEADING_TO_BODY); + section.addSection("SkillRows", rows -> { + rows.spacing(SKILL_ROW_GAP); + for (CvSkill skill : flatten(skills)) { + DocumentNode label = paragraph("Skill_" + compact(skill.name()), + skill.name(), body(), TextAlign.LEFT); + rows.addContainer(row -> { + row.name("SkillRow_" + compact(skill.name())) + .rectangle(SIDEBAR_INNER_WIDTH, SKILL_ROW_HEIGHT) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE) + .centerLeft(label); + if (skill.level().isPresent()) { + row.centerRight(skillMeter(skill.level().getAsDouble())); + } + }); + } + }); + } + + private static DocumentNode skillMeter(double level) { + DocumentNode fill = new ShapeBuilder() + .name("SkillMeterFill") + .size(SKILL_TRACK_WIDTH * clamp01(level), SKILL_TRACK_HEIGHT) + .fillColor(PLATE_BACKGROUND) + .build(); + return new ShapeContainerBuilder() + .name("SkillMeter") + .rectangle(SKILL_TRACK_WIDTH, SKILL_TRACK_HEIGHT) + .fillColor(RULE_MUTED) + .centerLeft(fill) + .build(); + } + + // -- education ------------------------------------------------------- + + /** + * The education rail: a hairline down the left of the block with a dot + * on each entry's first line. + * + *

The rail is the section's left accent, which runs the full height of + * the block. The first entry masks the stretch above its dot and redraws + * it below, so the rail begins at the first marker instead of at the top + * of the block.

+ */ + private static void renderEducation(SectionBuilder section, EntriesSection education) { + sidebarHeading(section, education.title(), EDUCATION_HEADING_TO_BODY); + section.addSection("EducationRail", rail -> { + rail.spacing(0); + rail.margin(new DocumentInsets(0, 0, 0, EDUCATION_RAIL_X)); + rail.accentLeft(RULE_MUTED, EDUCATION_RAIL_WIDTH); + List entries = education.entries(); + for (int i = 0; i < entries.size(); i++) { + int index = i; + CvEntry entry = entries.get(i); + renderEducationHead(rail, entry, index); + rail.addParagraph(p -> p + .name("EducationInstitution_" + index) + .text(entry.subtitle()) + .textStyle(style(BODY_FONT, BODY_SIZE, TEXT_MUTED, + DocumentTextDecoration.ITALIC)) + .margin(new DocumentInsets( + EDUCATION_LINE_GAP, 0, EDUCATION_LINE_GAP, EDUCATION_TEXT_X))); + double entryGap = i + 1 < entries.size() ? EDUCATION_ENTRY_GAP : 0; + rail.addParagraph(p -> p + .name("EducationDates_" + index) + .text(entry.date()) + .textStyle(body()) + .margin(new DocumentInsets(0, 0, entryGap, EDUCATION_TEXT_X))); + } + }); + } + + private static void renderEducationHead(SectionBuilder rail, CvEntry entry, int index) { + DocumentNode marker = new EllipseBuilder() + .name("EducationMarker_" + index) + .circle(EDUCATION_MARKER_DIAMETER) + .fillColor(ACCENT_PRIMARY) + .build(); + DocumentNode degree = paragraph("EducationDegree_" + index, entry.title(), + style(BODY_FONT, EDUCATION_DEGREE_SIZE, TEXT_PRIMARY, + DocumentTextDecoration.BOLD), + TextAlign.LEFT); + rail.addContainer(head -> { + head.name("EducationHead_" + index) + .rectangle(SIDEBAR_INNER_WIDTH - EDUCATION_RAIL_X, ENTRY_HEAD_HEIGHT) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE); + if (index == 0) { + double halfBand = (ENTRY_HEAD_HEIGHT - EDUCATION_MARKER_DIAMETER) / 2.0; + DocumentNode maskAboveFirstMarker = new ShapeBuilder() + .name("EducationRailMaskAboveFirstMarker") + // One point wider than the rail, and pulled half a + // point left, so the mask covers the rail's own edge + // instead of leaving a hairline of it showing. + .size(EDUCATION_RAIL_WIDTH + 1.0, halfBand) + .fillColor(SIDEBAR_BACKGROUND) + .build(); + DocumentNode railFromFirstMarker = new ShapeBuilder() + .name("EducationRailFromFirstMarker") + .size(EDUCATION_RAIL_WIDTH, halfBand) + .fillColor(RULE_MUTED) + .build(); + head.fillColor(SIDEBAR_BACKGROUND) + .position(maskAboveFirstMarker, -0.5, 0, LayerAlign.TOP_LEFT) + .position(railFromFirstMarker, 0, + (ENTRY_HEAD_HEIGHT + EDUCATION_MARKER_DIAMETER) / 2.0, + LayerAlign.TOP_LEFT); + } + head.position(marker, -EDUCATION_MARKER_DIAMETER / 2.0, 0, LayerAlign.CENTER_LEFT) + .position(degree, EDUCATION_TEXT_X, 0, LayerAlign.CENTER_LEFT); + }); + } + + // -- languages ------------------------------------------------------- + + /** + * Language rows: the name at the left, a five-dot rating anchored at a + * fixed offset so the ratings line up whatever the names measure. A + * language the document leaves unlevelled draws its name alone. + */ + private static void renderLanguages(SectionBuilder section, SkillsSection languages) { + sidebarHeading(section, languages.title(), STANDARD_HEADING_TO_BODY); + section.addSection("LanguageRows", rows -> { + rows.spacing(LANGUAGE_ROW_GAP); + for (CvSkill language : flatten(languages)) { + DocumentNode label = paragraph("Language_" + compact(language.name()), + language.name(), body(), TextAlign.LEFT); + rows.addContainer(row -> { + row.name("LanguageRow_" + compact(language.name())) + .rectangle(SIDEBAR_INNER_WIDTH, LANGUAGE_ROW_HEIGHT) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE) + .centerLeft(label); + if (language.level().isPresent()) { + row.position(ratingDots(language.level().getAsDouble()), + LANGUAGE_RATING_X, 0, LayerAlign.CENTER_LEFT); + } + }); + } + }); + } + + /** + * The rating: five dots, of which the level fills the nearest whole + * number — the model carries a fraction, and this design counts. + */ + private static DocumentNode ratingDots(double level) { + long filled = Math.round(clamp01(level) * LANGUAGE_DOTS); + ShapeContainerBuilder rating = new ShapeContainerBuilder() + .name("LanguageRating") + .rectangle(LANGUAGE_RATING_WIDTH, LANGUAGE_DOT_DIAMETER) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE); + for (int i = 0; i < LANGUAGE_DOTS; i++) { + DocumentNode dot = new EllipseBuilder() + .name("LanguageDot_" + i) + .circle(LANGUAGE_DOT_DIAMETER) + .fillColor(i < filled ? PLATE_BACKGROUND : RATING_MUTED) + .build(); + rating.position(dot, i * LANGUAGE_DOT_PITCH, 0, LayerAlign.CENTER_LEFT); + } + return rating.build(); + } + + /** + * The section's skills as one list. This design sets no group headings, + * so a document that groups its skills gets them in the order the groups + * were declared. + */ + private static List flatten(SkillsSection section) { + List out = new ArrayList<>(); + for (SkillGroup group : section.groups()) { + out.addAll(group.entries()); + } + return out; + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarIcons.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarIcons.java new file mode 100644 index 000000000..0e35fa334 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarIcons.java @@ -0,0 +1,83 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.image.DocumentImageData; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Classpath loader for the Professional Sidebar CV contact marks. + * + *

They ship inside the templates artifact under + * {@code templates/cv/professional-sidebar/icons/} as PNG, at the point size + * the design draws each one at — the marks are not one size, because a round + * pin and a wide envelope need different heights to read as the same + * weight.

+ */ +final class ProfessionalSidebarIcons { + + static final String PHONE = "phone"; + static final String EMAIL = "email"; + static final String LOCATION = "location"; + static final String LINKEDIN = "linkedin"; + static final String WEBSITE = "website"; + + private static final String ICON_ROOT = "/templates/cv/professional-sidebar/icons/"; + + private static final Map SIZES = Map.of( + PHONE, 8.8, + EMAIL, 9.2, + LOCATION, 9.6, + LINKEDIN, 9.4, + WEBSITE, 9.6); + + private static final Map CACHE = new ConcurrentHashMap<>(); + + private ProfessionalSidebarIcons() { + } + + /** + * The drawn size of one mark. + * + * @param token one of the constants on this class + * @return the size in points + * @throws IllegalArgumentException when the token names no packaged mark + */ + static double size(String token) { + Double size = SIZES.get(token); + if (size == null) { + throw new IllegalArgumentException( + "Unknown professional sidebar CV icon token: " + token); + } + return size; + } + + /** + * Reads one mark, caching the decoded image per token so a CV that + * repeats a channel reads the file once per JVM. + * + * @param token one of the constants on this class + * @return the image data + */ + static DocumentImageData image(String token) { + return CACHE.computeIfAbsent(token, ProfessionalSidebarIcons::read); + } + + private static DocumentImageData read(String token) { + String resourcePath = ICON_ROOT + token + ".png"; + try (InputStream input = + ProfessionalSidebarIcons.class.getResourceAsStream(resourcePath)) { + if (input == null) { + throw new IllegalStateException( + "Missing professional sidebar CV icon: " + resourcePath); + } + return DocumentImageData.fromBytes(input.readAllBytes()); + } catch (IOException e) { + throw new UncheckedIOException( + "Failed to read professional sidebar CV icon: " + resourcePath, e); + } + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarMain.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarMain.java new file mode 100644 index 000000000..7b9e17e66 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarMain.java @@ -0,0 +1,244 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.dsl.ShapeBuilder; +import com.demcha.compose.document.dsl.ShapeContainerBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.templates.core.text.MarkdownInline; +import com.demcha.compose.document.templates.cv.components.SectionLookup; +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 static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ACCENT_PRIMARY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.BODY_FONT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.BODY_LEADING; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.DISPLAY_FONT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.DIVIDER_TO_ENTRY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.DIVIDER_TO_PROJECT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ENTRY_HEAD_TO_META; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ENTRY_META_TO_HIGHLIGHTS; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ENTRY_TO_DIVIDER; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EXPERIENCE_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.EXPERIENCE_TO_PROJECTS; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.HIGHLIGHT_ITEM_GAP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.IDENTITY_ACCENT_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.IDENTITY_TO_PROFILE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_CONTENT_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_PAD_LEFT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_PAD_RIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_PAD_TOP; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.NAME_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.NAME_TO_ROLE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.NAME_TRACKING_EM; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PROFESSIONAL_TITLE_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PROFILE_TO_EXPERIENCE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PROJECTS_TO_REFERENCES; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PROJECT_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PROJECT_HEAD_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.PROJECT_TO_DIVIDER; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.REFERENCES_HEADING_TO_BODY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.REFERENCES_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ROLE_TO_RULE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ROLE_TRACKING_EM; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.RULE_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SECTION_ACCENT_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.TEXT_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.TEXT_PRIMARY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.body; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.metaItalic; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.style; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.tracked; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.mainDivider; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.mainHeading; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.spacer; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarWidgets.titleDateBand; + +/** + * The white right column: the name and role over the identity rule, then the + * profile, the roles held, the projects and the references note. + */ +final class ProfessionalSidebarMain { + + private ProfessionalSidebarMain() { + } + + static void compose(SectionBuilder section, + CvIdentity identity, + ParagraphSection profile, + EntriesSection experience, + EntriesSection projects, + ParagraphSection references) { + section.spacing(0); + section.padding(new DocumentInsets(MAIN_PAD_TOP, MAIN_PAD_RIGHT, 0, MAIN_PAD_LEFT)); + section.addSection("Identity", host -> renderIdentity(host, identity)); + if (SectionLookup.hasContent(profile)) { + section.addSection("Profile", host -> renderProfile(host, profile)); + } + if (SectionLookup.hasContent(experience)) { + section.addSection("Experience", host -> renderExperience(host, experience)); + } + if (SectionLookup.hasContent(projects)) { + section.addSection("Projects", host -> renderProjects(host, projects)); + } + if (SectionLookup.hasContent(references)) { + section.addSection("References", host -> renderReferences(host, references)); + } + } + + // -- identity -------------------------------------------------------- + + /** + * The name in tracked condensed capitals, the role under it, and the + * rule that closes the block: a full-width hairline whose first stretch + * is the accent. + */ + private static void renderIdentity(SectionBuilder section, CvIdentity identity) { + DocumentTextStyle nameStyle = style(DISPLAY_FONT, NAME_SIZE, TEXT_PRIMARY, + DocumentTextDecoration.BOLD); + ParagraphBuilder name = new ParagraphBuilder() + .name("DisplayName") + .textStyle(nameStyle) + .align(TextAlign.LEFT); + tracked(name, identity.name().full(), nameStyle, NAME_TRACKING_EM); + section.add(name.margin(new DocumentInsets(0, 0, NAME_TO_ROLE, 0)).build()); + + DocumentTextStyle roleStyle = style(BODY_FONT, PROFESSIONAL_TITLE_SIZE, TEXT_MUTED, + DocumentTextDecoration.DEFAULT); + ParagraphBuilder role = new ParagraphBuilder() + .name("ProfessionalTitle") + .textStyle(roleStyle) + .align(TextAlign.LEFT); + tracked(role, identity.jobTitle(), roleStyle, ROLE_TRACKING_EM); + section.add(role.margin(new DocumentInsets(0, 0, ROLE_TO_RULE, 0)).build()); + + section.add(identityRule()); + spacer(section, IDENTITY_TO_PROFILE); + } + + private static DocumentNode identityRule() { + DocumentNode accent = new ShapeBuilder() + .name("IdentityAccentSegment") + .size(IDENTITY_ACCENT_WIDTH, SECTION_ACCENT_HEIGHT) + .fillColor(ACCENT_PRIMARY) + .build(); + return new ShapeContainerBuilder() + .name("IdentityRule") + .rectangle(MAIN_CONTENT_WIDTH, SECTION_ACCENT_HEIGHT) + .fillColor(RULE_MUTED) + .centerLeft(accent) + .build(); + } + + // -- profile --------------------------------------------------------- + + private static void renderProfile(SectionBuilder section, ParagraphSection profile) { + mainHeading(section, profile.title(), MAIN_HEADING_TO_BODY); + section.addParagraph(p -> p + .name("ProfileText") + .text(profile.body()) + .textStyle(body()) + .lineSpacing(BODY_LEADING) + .margin(DocumentInsets.zero())); + spacer(section, PROFILE_TO_EXPERIENCE); + } + + // -- experience ------------------------------------------------------ + + /** + * The roles held, each a title-and-dates band over the employer line and + * a bulleted list of what the role delivered, separated by hairlines. + */ + private static void renderExperience(SectionBuilder section, EntriesSection experience) { + mainHeading(section, experience.title(), EXPERIENCE_HEADING_TO_BODY); + List entries = experience.entries(); + for (int i = 0; i < entries.size(); i++) { + renderRole(section, entries.get(i), i); + if (i + 1 < entries.size()) { + mainDivider(section, ENTRY_TO_DIVIDER, DIVIDER_TO_ENTRY); + } + } + spacer(section, EXPERIENCE_TO_PROJECTS); + } + + private static void renderRole(SectionBuilder section, CvEntry entry, int index) { + titleDateBand(section, "ExperienceHead_" + index, entry.title(), entry.date()); + section.addParagraph(p -> p + .name("ExperienceMeta_" + index) + .text(entry.subtitle()) + .textStyle(metaItalic()) + .margin(new DocumentInsets( + ENTRY_HEAD_TO_META, 0, ENTRY_META_TO_HIGHLIGHTS, 0))); + List highlights = lines(entry.body()); + if (!highlights.isEmpty()) { + section.addList(list -> list + .name("ExperienceHighlights_" + index) + .items(highlights) + .bullet() + .textStyle(body()) + .lineSpacing(BODY_LEADING) + .itemSpacing(HIGHLIGHT_ITEM_GAP) + .margin(DocumentInsets.zero())); + } + } + + // -- projects -------------------------------------------------------- + + private static void renderProjects(SectionBuilder section, EntriesSection projects) { + mainHeading(section, projects.title(), PROJECT_HEADING_TO_BODY); + List entries = projects.entries(); + for (int i = 0; i < entries.size(); i++) { + int index = i; + CvEntry project = entries.get(i); + titleDateBand(section, "ProjectHead_" + index, project.title(), project.date()); + spacer(section, PROJECT_HEAD_TO_BODY); + section.addParagraph(p -> p + .name("ProjectDescription_" + index) + .text(project.body()) + .textStyle(body()) + .lineSpacing(BODY_LEADING) + .margin(DocumentInsets.zero())); + if (i + 1 < entries.size()) { + mainDivider(section, PROJECT_TO_DIVIDER, DIVIDER_TO_PROJECT); + } + } + spacer(section, PROJECTS_TO_REFERENCES); + } + + // -- references ------------------------------------------------------ + + private static void renderReferences(SectionBuilder section, ParagraphSection references) { + mainHeading(section, references.title(), REFERENCES_HEADING_TO_BODY); + section.addParagraph(p -> p + .name("ReferencesNote") + .text(references.body()) + .textStyle(style(BODY_FONT, REFERENCES_SIZE, TEXT_MUTED, + DocumentTextDecoration.DEFAULT)) + .margin(DocumentInsets.zero())); + } + + /** + * One bullet per non-blank line of an entry body — the family's way of + * carrying a list in a field the model types as a single string. + */ + private static List lines(String body) { + List out = new ArrayList<>(); + for (String line : body.split("\\R")) { + String clean = MarkdownInline.plainText(line).trim(); + if (!clean.isBlank()) { + out.add(clean); + } + } + return out; + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarStyles.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarStyles.java new file mode 100644 index 000000000..aef9d29ba --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarStyles.java @@ -0,0 +1,209 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.templates.core.text.TextStyles; +import com.demcha.compose.font.FontName; + +/** + * The measured geometry, palette and type scale of the Professional Sidebar + * CV. + * + *

Every value here is read off the design rather than derived, which is + * why so many carry two decimals: the preset reproduces a specific sheet, + * and rounding them to tidier numbers moves type off its baseline.

+ */ +final class ProfessionalSidebarStyles { + + private ProfessionalSidebarStyles() { + } + + // -- page ------------------------------------------------------------ + + /** + * The page: 1024 x 1536 pixels at 150 dpi, which is the frame the design + * was drawn in rather than a paper size. + */ + static final DocumentPageSize PAGE = DocumentPageSize.of(491.60, 737.28); + + static final double PAGE_WIDTH = PAGE.width(); + static final double PAGE_HEIGHT = PAGE.height(); + + static final double SIDEBAR_WEIGHT = 0.291; + static final double MAIN_WEIGHT = 1.0 - SIDEBAR_WEIGHT; + + static final double SIDEBAR_WIDTH = PAGE_WIDTH * SIDEBAR_WEIGHT; + static final double MAIN_WIDTH = PAGE_WIDTH * MAIN_WEIGHT; + + /** The navy plate behind the monogram, as a fraction of page height. */ + static final double HEADER_PLATE_RATIO = 0.134; + static final double HEADER_PLATE_HEIGHT = PAGE_HEIGHT * HEADER_PLATE_RATIO; + + static final double SIDEBAR_PAD_X = 17.8; + static final double SIDEBAR_INNER_WIDTH = SIDEBAR_WIDTH - 2.0 * SIDEBAR_PAD_X; + + static final double MAIN_PAD_LEFT = 27.0; + static final double MAIN_PAD_RIGHT = 20.0; + static final double MAIN_CONTENT_WIDTH = MAIN_WIDTH - MAIN_PAD_LEFT - MAIN_PAD_RIGHT; + + // -- ornaments ------------------------------------------------------- + + static final double MONOGRAM_DIAMETER = 55.2; + static final double MONOGRAM_STROKE = 0.8; + static final double MONOGRAM_SIZE = 16.0; + static final double MONOGRAM_TRACKING_EM = 0.24; + + static final double SECTION_ACCENT_WIDTH = 19.0; + static final double SECTION_ACCENT_HEIGHT = 1.05; + static final double IDENTITY_ACCENT_WIDTH = 27.0; + + static final double RULE_THICKNESS = 0.55; + + static final double SKILL_TRACK_WIDTH = SIDEBAR_INNER_WIDTH * 0.44; + static final double SKILL_TRACK_HEIGHT = 2.8; + static final double SKILL_ROW_HEIGHT = 8.5; + + static final double EDUCATION_RAIL_X = 2.8; + static final double EDUCATION_TEXT_X = 13.0; + static final double EDUCATION_MARKER_DIAMETER = 4.3; + static final double EDUCATION_RAIL_WIDTH = 0.55; + static final double EDUCATION_DEGREE_SIZE = 7.3; + + static final int LANGUAGE_DOTS = 5; + static final double LANGUAGE_RATING_X = SIDEBAR_INNER_WIDTH * 0.49; + static final double LANGUAGE_DOT_DIAMETER = 5.0; + static final double LANGUAGE_DOT_GAP = 5.1; + static final double LANGUAGE_DOT_PITCH = LANGUAGE_DOT_DIAMETER + LANGUAGE_DOT_GAP; + static final double LANGUAGE_RATING_WIDTH = + LANGUAGE_DOTS * LANGUAGE_DOT_DIAMETER + (LANGUAGE_DOTS - 1) * LANGUAGE_DOT_GAP; + static final double LANGUAGE_ROW_HEIGHT = 8.5; + + static final double ENTRY_HEAD_HEIGHT = 9.3; + + // -- type ------------------------------------------------------------ + + static final FontName DISPLAY_FONT = FontName.BARLOW_CONDENSED; + static final FontName BODY_FONT = FontName.LATO; + + static final double BODY_SIZE = 7.1; + static final double SIDEBAR_HEADING_SIZE = 8.5; + static final double MAIN_HEADING_SIZE = 8.7; + static final double NAME_SIZE = 36.7; + static final double PROFESSIONAL_TITLE_SIZE = 9.5; + static final double ENTRY_TITLE_SIZE = 7.5; + static final double META_SIZE = 7.2; + static final double REFERENCES_SIZE = 7.4; + static final double BODY_LEADING = 1.18; + + static final double NAME_TRACKING_EM = 0.114; + static final double HEADING_TRACKING_EM = 0.14; + static final double ROLE_TRACKING_EM = 0.18; + + /** + * The advance of a space in the body face, as a fraction of type size. + * {@link #tracked} sizes its spacer runs against this to hit a tracking + * measured in ems. + */ + static final double SPACE_ADVANCE_EM = 0.25; + + // -- vertical rhythm ------------------------------------------------- + + static final double SIDEBAR_BODY_TOP = 22.0; + static final double SIDEBAR_HEADING_TO_RULE = 4.0; + static final double CONTACT_HEADING_TO_BODY = 16.0; + static final double STANDARD_HEADING_TO_BODY = 13.5; + static final double CONTACT_ROW_GAP = 11.8; + static final double SKILL_ROW_GAP = 9.3; + static final double EDUCATION_HEADING_TO_BODY = 10.0; + static final double EDUCATION_LINE_GAP = 2.5; + static final double EDUCATION_ENTRY_GAP = 11.0; + static final double LANGUAGE_ROW_GAP = 7.3; + + static final double CONTACT_TO_SKILLS_ABOVE = 24.0; + static final double CONTACT_TO_SKILLS_BELOW = 18.0; + static final double SKILLS_TO_EDUCATION_ABOVE = 15.0; + static final double SKILLS_TO_EDUCATION_BELOW = 18.0; + static final double EDUCATION_TO_LANGUAGES_ABOVE = 8.0; + static final double EDUCATION_TO_LANGUAGES_BELOW = 16.0; + + static final double MAIN_PAD_TOP = 21.9; + static final double NAME_TO_ROLE = 6.3; + static final double ROLE_TO_RULE = 14.9; + static final double IDENTITY_TO_PROFILE = 20.5; + static final double MAIN_HEADING_TO_RULE = 4.0; + static final double MAIN_HEADING_TO_BODY = 14.0; + static final double EXPERIENCE_HEADING_TO_BODY = 18.3; + static final double PROJECT_HEADING_TO_BODY = 8.0; + static final double REFERENCES_HEADING_TO_BODY = 8.0; + static final double PROFILE_TO_EXPERIENCE = 24.5; + static final double ENTRY_HEAD_TO_META = 2.0; + static final double ENTRY_META_TO_HIGHLIGHTS = 14.3; + static final double HIGHLIGHT_ITEM_GAP = 4.8; + static final double ENTRY_TO_DIVIDER = 14.0; + static final double DIVIDER_TO_ENTRY = 13.0; + static final double EXPERIENCE_TO_PROJECTS = 22.0; + static final double PROJECT_HEAD_TO_BODY = 5.0; + static final double PROJECT_TO_DIVIDER = 6.0; + static final double DIVIDER_TO_PROJECT = 7.0; + static final double PROJECTS_TO_REFERENCES = 18.0; + + // -- palette --------------------------------------------------------- + + static final DocumentColor PAGE_BACKGROUND = DocumentColor.WHITE; + static final DocumentColor SIDEBAR_BACKGROUND = DocumentColor.rgb(247, 248, 250); + static final DocumentColor PLATE_BACKGROUND = DocumentColor.rgb(11, 39, 77); + static final DocumentColor TEXT_PRIMARY = DocumentColor.rgb(7, 17, 30); + static final DocumentColor TEXT_MUTED = DocumentColor.rgb(47, 74, 115); + static final DocumentColor ACCENT_PRIMARY = DocumentColor.rgb(11, 103, 200); + static final DocumentColor RULE_MUTED = DocumentColor.rgb(209, 211, 214); + static final DocumentColor RATING_MUTED = DocumentColor.rgb(194, 196, 199); + + // -- styles ---------------------------------------------------------- + + static DocumentTextStyle body() { + return style(BODY_FONT, BODY_SIZE, TEXT_PRIMARY, DocumentTextDecoration.DEFAULT); + } + + static DocumentTextStyle metaItalic() { + return style(BODY_FONT, META_SIZE, TEXT_MUTED, DocumentTextDecoration.ITALIC); + } + + static DocumentTextStyle style(FontName font, double size, DocumentColor color, + DocumentTextDecoration decoration) { + return TextStyles.of(font, size, decoration, color); + } + + /** + * Writes text letter by letter with a sized space between each pair, + * which is how this design gets its letter-spacing: a text style carries + * no tracking, so the gap is a run of its own whose type size is chosen + * to advance by {@code trackingEm} of the surrounding size. + * + * @param paragraph the paragraph being built + * @param text the text to space out + * @param style the style of the letters + * @param trackingEm the gap between letters, in ems of {@code style} + */ + static void tracked(ParagraphBuilder paragraph, String text, + DocumentTextStyle style, double trackingEm) { + DocumentTextStyle spacer = style.withSize(trackingEm * style.size() / SPACE_ADVANCE_EM); + for (int i = 0; i < text.length(); i++) { + paragraph.inlineText(String.valueOf(text.charAt(i)), style); + if (i + 1 < text.length()) { + paragraph.inlineText(" ", spacer); + } + } + } + + /** Strips a title down to the letters and digits a node name can carry. */ + static String compact(String text) { + return text == null ? "" : text.replaceAll("[^A-Za-z0-9]", ""); + } + + static double clamp01(double value) { + return Math.max(0.0, Math.min(1.0, value)); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarWidgets.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarWidgets.java new file mode 100644 index 000000000..7588b1ec5 --- /dev/null +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/ProfessionalSidebarWidgets.java @@ -0,0 +1,159 @@ +package com.demcha.compose.document.templates.cv.presets; + +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.node.DocumentNode; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.ClipPolicy; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; + +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ACCENT_PRIMARY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.BODY_FONT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.BODY_LEADING; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ENTRY_HEAD_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.ENTRY_TITLE_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.HEADING_TRACKING_EM; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_CONTENT_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_HEADING_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.MAIN_HEADING_TO_RULE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.META_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.RULE_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.RULE_THICKNESS; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SECTION_ACCENT_HEIGHT; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SECTION_ACCENT_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_HEADING_SIZE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_HEADING_TO_RULE; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.SIDEBAR_INNER_WIDTH; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.TEXT_MUTED; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.TEXT_PRIMARY; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.compact; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.style; +import static com.demcha.compose.document.templates.cv.presets.ProfessionalSidebarStyles.tracked; + +/** + * The parts both columns of the Professional Sidebar CV are built from: the + * heading over its short accent rule, the hairline that separates entries, + * and the band that sets an entry title against its dates. + */ +final class ProfessionalSidebarWidgets { + + private ProfessionalSidebarWidgets() { + } + + /** + * A sidebar heading: tracked capitals over a short accent rule. + * + *

The heading is drawn as authored — the design sets these in + * capitals, and the preset does not upper-case for the author, so a + * document that titles a section in sentence case gets sentence case.

+ * + * @param section the sidebar column + * @param title the heading text + * @param bodyGap the gap between the rule and the section body + */ + static void sidebarHeading(SectionBuilder section, String title, double bodyGap) { + heading(section, "Sidebar", title, SIDEBAR_HEADING_SIZE, + SIDEBAR_HEADING_TO_RULE, bodyGap); + } + + /** + * A main-column heading, a fraction larger than its sidebar counterpart + * and otherwise identical. + * + * @param section the main column + * @param title the heading text + * @param bodyGap the gap between the rule and the section body + */ + static void mainHeading(SectionBuilder section, String title, double bodyGap) { + heading(section, "Main", title, MAIN_HEADING_SIZE, + MAIN_HEADING_TO_RULE, bodyGap); + } + + private static void heading(SectionBuilder section, String column, String title, + double size, double ruleGap, double bodyGap) { + DocumentTextStyle headingStyle = + style(BODY_FONT, size, TEXT_PRIMARY, DocumentTextDecoration.BOLD); + ParagraphBuilder heading = new ParagraphBuilder() + .name(column + "Heading_" + compact(title)) + .textStyle(headingStyle); + tracked(heading, title, headingStyle, HEADING_TRACKING_EM); + section.add(heading.margin(new DocumentInsets(0, 0, ruleGap, 0)).build()); + section.addLine(line -> line + .name(column + "HeadingRule_" + compact(title)) + .horizontal(SECTION_ACCENT_WIDTH) + .thickness(SECTION_ACCENT_HEIGHT) + .color(ACCENT_PRIMARY) + .margin(new DocumentInsets(0, 0, bodyGap, 0))); + } + + /** The hairline between two sidebar sections. */ + static void sidebarDivider(SectionBuilder section, double gapAbove, double gapBelow) { + divider(section, "SidebarDivider", SIDEBAR_INNER_WIDTH, gapAbove, gapBelow); + } + + /** The hairline between two main-column entries. */ + static void mainDivider(SectionBuilder section, double gapAbove, double gapBelow) { + divider(section, "MainDivider", MAIN_CONTENT_WIDTH, gapAbove, gapBelow); + } + + private static void divider(SectionBuilder section, String name, double width, + double gapAbove, double gapBelow) { + section.addLine(line -> line + .name(name) + .horizontal(width) + .thickness(RULE_THICKNESS) + .color(RULE_MUTED) + .margin(new DocumentInsets(gapAbove, 0, gapBelow, 0))); + } + + /** + * An entry head: the title at the left of a fixed-height band with its + * dates set flush right on the same optical line. + * + *

It is a container rather than a two-column row because the two ends + * are centred against each other vertically, which a row of sections + * cannot do — a section is as tall as its content and starts at the top + * of its column.

+ * + * @param section the host column + * @param name the node name of the band + * @param title the entry title + * @param dates the entry dates, drawn flush right + */ + static void titleDateBand(SectionBuilder section, String name, + String title, String dates) { + DocumentNode titleNode = paragraph(name + "Title", title, + style(BODY_FONT, ENTRY_TITLE_SIZE, TEXT_PRIMARY, + DocumentTextDecoration.BOLD), + TextAlign.LEFT); + DocumentNode dateNode = paragraph(name + "Dates", dates, + style(BODY_FONT, META_SIZE, TEXT_MUTED, + DocumentTextDecoration.DEFAULT), + TextAlign.RIGHT); + section.addContainer(band -> band + .name(name) + .rectangle(MAIN_CONTENT_WIDTH, ENTRY_HEAD_HEIGHT) + .clipPolicy(ClipPolicy.OVERFLOW_VISIBLE) + .centerLeft(titleNode) + .centerRight(dateNode)); + } + + /** A standalone paragraph node, for anchoring inside a container. */ + static DocumentNode paragraph(String name, String text, + DocumentTextStyle style, TextAlign align) { + return new ParagraphBuilder() + .name(name) + .text(text) + .textStyle(style) + .align(align) + .lineSpacing(BODY_LEADING) + .margin(DocumentInsets.zero()) + .build(); + } + + static void spacer(SectionBuilder section, double height) { + section.addSpacer(spacer -> spacer.height(height)); + } +} diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/package-info.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/package-info.java index 40321b1ae..64a490a00 100644 --- a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/package-info.java +++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/package-info.java @@ -19,5 +19,13 @@ * a new sealed subtype in {@code cv/data} plus a renderer in * {@code cv/components}; a new preset is this file copied and * fed a different theme.

+ * + *

The package holds a second shape as well. A preset that reproduces a + * specific drawn sheet — {@code ProfessionalSidebar} is the one here — takes + * no {@code BrandTheme} and calls no shared renderer: its geometry, palette + * and type scale are measured constants of its own, and it sets its own page. + * The data model is the same {@code CvDocument}; what differs is that the + * cosmetics cannot be swapped, because moving them would stop it being the + * sheet it reproduces.

*/ package com.demcha.compose.document.templates.cv.presets; diff --git a/templates/src/main/resources/templates/cv/professional-sidebar/icons/email.png b/templates/src/main/resources/templates/cv/professional-sidebar/icons/email.png new file mode 100644 index 000000000..b7fc73ca2 Binary files /dev/null and b/templates/src/main/resources/templates/cv/professional-sidebar/icons/email.png differ diff --git a/templates/src/main/resources/templates/cv/professional-sidebar/icons/linkedin.png b/templates/src/main/resources/templates/cv/professional-sidebar/icons/linkedin.png new file mode 100644 index 000000000..710983a5f Binary files /dev/null and b/templates/src/main/resources/templates/cv/professional-sidebar/icons/linkedin.png differ diff --git a/templates/src/main/resources/templates/cv/professional-sidebar/icons/location.png b/templates/src/main/resources/templates/cv/professional-sidebar/icons/location.png new file mode 100644 index 000000000..ccdaf6f53 Binary files /dev/null and b/templates/src/main/resources/templates/cv/professional-sidebar/icons/location.png differ diff --git a/templates/src/main/resources/templates/cv/professional-sidebar/icons/phone.png b/templates/src/main/resources/templates/cv/professional-sidebar/icons/phone.png new file mode 100644 index 000000000..6d27c23de Binary files /dev/null and b/templates/src/main/resources/templates/cv/professional-sidebar/icons/phone.png differ diff --git a/templates/src/main/resources/templates/cv/professional-sidebar/icons/website.png b/templates/src/main/resources/templates/cv/professional-sidebar/icons/website.png new file mode 100644 index 000000000..d4a24b64d Binary files /dev/null and b/templates/src/main/resources/templates/cv/professional-sidebar/icons/website.png differ