diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44a792bf..7c94c42a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,80 @@
All notable changes to GraphCompose are documented here. Versions
follow semantic versioning; release dates are ISO 8601.
+## v1.6.4 — Planned
+
+Bug fix + structured-block patch. Adds two new public Block types —
+`WorkHistoryBlock` and `EducationBlock` — that let template authors
+declare work-history and education entries with explicit (title,
+organisation, date, description) / (degree, institution, year,
+details) fields instead of relying on the legacy
+`MultiParagraphBlock` pipe-separated string parser. Also closes a
+Boxed Sections layout defect that bundled the date and description
+into the right-aligned date column for any author-supplied line that
+used an em-dash (`" — "`), en-dash (`" – "`), or contained
+prose-shaped content the parser misread as a date. **No public API
+break** — the sealed `Block` permit list grows from six to eight,
+existing `MultiParagraphBlock` work-history strings continue to
+parse, and the deprecated parser path stays in place for backward
+compatibility.
+
+### Templates — new structured blocks
+
+- **`WorkHistoryBlock`.** New public record block carrying a list of
+ `Item(title, organisation, date, description)` entries. The
+ `BoxedSections` preset renders each item as a structured row:
+ title bold on the left, date right-aligned on the same row,
+ organisation italic on the next line under the title, and
+ description as a full-width paragraph beneath. Other presets fall
+ back to a single concatenated paragraph per item. Authors who use
+ `WorkHistoryBlock` bypass the legacy
+ `BoxedSections#parseWorkEntry` heuristic parser entirely.
+- **`EducationBlock`.** New public record block carrying a list of
+ `Item(degree, institution, year, details)` entries. Renders with
+ the same structured layout as `WorkHistoryBlock` (degree bold
+ left, year right, institution italic, details paragraph) so
+ Education & Certifications sections visually match Professional
+ Experience.
+- **Sample data migrated.** `ExampleDataFactory.sampleCvSpecV2` now
+ uses `WorkHistoryBlock` for Professional Experience and
+ `EducationBlock` for Education & Certifications. The legacy
+ `MultiParagraphBlock` pattern remains supported and is exercised
+ by `PresetLayoutSnapshotTest` / `PresetVisualParityTest` to lock
+ the backward-compat path.
+
+### Templates — parser robustness (legacy path)
+
+- **`parseWorkEntry` accepts em-dash and en-dash.** Used to split
+ the post-pipe segment on ASCII `" - "` only; now tries `" — "`,
+ `" – "`, and `" - "` in order, mirroring `splitHeading`. Authors
+ who typed `"*2024-Present* — Led reusable document flows."` saw
+ the whole tail collapse into the date column — this no longer
+ happens.
+- **`parseWorkEntry` rejects prose dressed up as a date.** The
+ loose `looksLikeDate` check accepted any string containing a
+ year and a hyphen anywhere, which caused education lines like
+ `"... | 2019. First-class honours. Specialisation ..."` to
+ parse as work entries (the hyphen inside `"First-class"` was
+ enough to satisfy the heuristic). Parser now rejects post-pipe
+ segments that contain sentence-ending punctuation (`.`, `:`,
+ `;`) when no explicit date / description separator was found,
+ letting these lines fall back to plain paragraph rendering.
+ Marked `@Deprecated` with a `@deprecated` Javadoc pointing
+ callers to `WorkHistoryBlock` / `EducationBlock`.
+- **`parseProjectItem`** picks up the same em-dash / en-dash /
+ ASCII separator set so future Project items typed with em-dash
+ don't regress into "title only" rendering.
+
+### Tests
+
+- `BlockTest.blockSealingPermitsAllEightVariants` updated for the
+ two new permitted block types.
+- `PresetVisualGalleryTest.sampleSpec` migrated to
+ `WorkHistoryBlock` so the visible "primary example" exercises the
+ new structured shape.
+- `PresetLayoutSnapshotTest` intentionally retained on
+ `MultiParagraphBlock` to lock the legacy parser's behaviour.
+
## v1.6.3 — 2026-05-22
Bug fix patch. Closes two independent hyperlink clickable-area
diff --git a/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java b/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java
index 39d71b7f..ce5a1035 100644
--- a/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java
+++ b/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java
@@ -1,10 +1,12 @@
package com.demcha.examples.support;
import com.demcha.compose.document.templates.blocks.BulletListBlock;
+import com.demcha.compose.document.templates.blocks.EducationBlock;
import com.demcha.compose.document.templates.blocks.IndentedBlock;
import com.demcha.compose.document.templates.blocks.KeyValueBlock;
import com.demcha.compose.document.templates.blocks.MultiParagraphBlock;
import com.demcha.compose.document.templates.blocks.ParagraphBlock;
+import com.demcha.compose.document.templates.blocks.WorkHistoryBlock;
import com.demcha.compose.document.templates.coverletter.spec.CoverLetterHeader;
import com.demcha.compose.document.templates.coverletter.spec.CoverLetterSpec;
import com.demcha.compose.document.templates.cv.spec.CvHeader;
@@ -286,16 +288,32 @@ public static CvSpec sampleCvSpecV2() {
"**Distribution:** Maven Central, Sonatype OSSRH, GPG signing, "
+ "JitPack, semantic versioning discipline"))))
.module(CvModule.of("Education & Certifications",
- new MultiParagraphBlock(List.of(
- "**MSc Computer Science** - University of Manchester | 2021. "
- + "Distinction. Thesis: *Composable layout primitives for "
- + "deterministic document rendering*.",
- "**BSc Software Engineering** - Imperial College London | 2019. "
- + "First-class honours. Specialisation in compilers and "
- + "static analysis.",
- "**Oracle Java Certification** - Professional track | 2023. "
- + "Java 17 platform deep-dive: records, sealed types, "
- + "pattern matching, virtual threads."))))
+ // Preferred: structured EducationBlock with
+ // explicit (degree, institution, year, details)
+ // fields. BoxedSections renders each item with
+ // the same structured layout as Professional
+ // Experience — degree bold left, year right,
+ // institution italic on the next line, and
+ // details as a full-width paragraph below.
+ new EducationBlock(List.of(
+ new EducationBlock.Item(
+ "MSc Computer Science",
+ "University of Manchester",
+ "2021",
+ "Distinction. Thesis: *Composable layout primitives "
+ + "for deterministic document rendering*."),
+ new EducationBlock.Item(
+ "BSc Software Engineering",
+ "Imperial College London",
+ "2019",
+ "First-class honours. Specialisation in compilers and "
+ + "static analysis."),
+ new EducationBlock.Item(
+ "Oracle Java Certification",
+ "Professional track",
+ "2023",
+ "Java 17 platform deep-dive: records, sealed types, "
+ + "pattern matching, virtual threads.")))))
.module(CvModule.of("Projects",
new BulletListBlock(List.of(
"**GraphCompose (Java 21, PDFBox, Maven, JMH)** - "
@@ -316,23 +334,40 @@ public static CvSpec sampleCvSpecV2() {
+ "GraphCompose: cinematic covers, pull quotes, "
+ "multi-column flow, sidebar callouts."))))
.module(CvModule.of("Professional Experience",
- new MultiParagraphBlock(List.of(
- "**Senior Platform Engineer**, Northwind Systems | "
- + "*2024-Present* - Led the reusable document-generation "
- + "platform serving billing, hiring, and reporting flows "
- + "across **8 product teams**. Reduced template "
- + "maintenance time by **70%** by retiring per-team "
- + "PDF scripts in favour of one canonical engine.",
- "**Software Engineer**, BrightLeaf Labs | *2021-2024* - Built "
- + "backend services and production document rendering "
- + "pipelines processing **2M+ documents per month**. "
- + "Drove the migration from iText to a custom layout "
- + "engine, eliminating licensing risk and cutting "
- + "p99 render latency from 1.4s to 380ms.",
- "**Backend Engineer**, Helix Print Co | *2019-2021* - "
- + "Maintained a high-volume invoice-printing service "
- + "(15M PDFs/year) and authored the compliance test "
- + "harness that gated every template change."))))
+ // Preferred: structured WorkHistoryBlock with
+ // explicit (title, organisation, date,
+ // description) fields. BoxedSections renders
+ // each item as a structured row (title bold
+ // left, date right, organisation italic on the
+ // next line, description full-width below)
+ // without falling back to the legacy
+ // pipe-separated string parser.
+ new WorkHistoryBlock(List.of(
+ new WorkHistoryBlock.Item(
+ "Senior Platform Engineer",
+ "Northwind Systems",
+ "2024-Present",
+ "Led the reusable document-generation platform serving "
+ + "billing, hiring, and reporting flows across "
+ + "**8 product teams**. Reduced template maintenance "
+ + "time by **70%** by retiring per-team PDF scripts "
+ + "in favour of one canonical engine."),
+ new WorkHistoryBlock.Item(
+ "Software Engineer",
+ "BrightLeaf Labs",
+ "2021-2024",
+ "Built backend services and production document rendering "
+ + "pipelines processing **2M+ documents per month**. "
+ + "Drove the migration from iText to a custom layout "
+ + "engine, eliminating licensing risk and cutting "
+ + "p99 render latency from 1.4s to 380ms."),
+ new WorkHistoryBlock.Item(
+ "Backend Engineer",
+ "Helix Print Co",
+ "2019-2021",
+ "Maintained a high-volume invoice-printing service "
+ + "(15M PDFs/year) and authored the compliance test "
+ + "harness that gated every template change.")))))
.module(CvModule.of("Additional Information",
new KeyValueBlock(List.of(
new KeyValueBlock.Entry("Languages",
diff --git a/src/main/java/com/demcha/compose/document/templates/blocks/Block.java b/src/main/java/com/demcha/compose/document/templates/blocks/Block.java
index 2f9110ff..049dba6b 100644
--- a/src/main/java/com/demcha/compose/document/templates/blocks/Block.java
+++ b/src/main/java/com/demcha/compose/document/templates/blocks/Block.java
@@ -11,8 +11,8 @@
*
*
The sealed permit list is intentionally exhaustive: every body
* shape that a CV / cover-letter / invoice / proposal preset can
- * declare today is one of the six concrete records. To add a new body
- * shape, extend the {@code permits} list and update the Module
+ * declare today is one of the eight concrete records. To add a new
+ * body shape, extend the {@code permits} list and update the Module
* composer to handle the new variant.
*
* Block records are immutable and safe to reuse across documents.
@@ -23,5 +23,7 @@ public sealed interface Block
NumberedListBlock,
IndentedBlock,
KeyValueBlock,
- MultiParagraphBlock {
+ MultiParagraphBlock,
+ WorkHistoryBlock,
+ EducationBlock {
}
diff --git a/src/main/java/com/demcha/compose/document/templates/blocks/EducationBlock.java b/src/main/java/com/demcha/compose/document/templates/blocks/EducationBlock.java
new file mode 100644
index 00000000..83c42b36
--- /dev/null
+++ b/src/main/java/com/demcha/compose/document/templates/blocks/EducationBlock.java
@@ -0,0 +1,84 @@
+package com.demcha.compose.document.templates.blocks;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link Block} that captures a stack of education / certification
+ * entries with each field (degree, institution, year, details)
+ * supplied separately so presets can place them precisely without
+ * re-parsing a concatenated source string.
+ *
+ * This is the preferred shape for "Education",
+ * "Education & Certifications", "Qualifications" or any module
+ * whose body is a list of degree / course entries. The
+ * {@code BoxedSections} preset renders each {@link Item} with the
+ * same structured layout as {@code WorkHistoryBlock}: degree bold on
+ * the left, year right-aligned on the same row, institution italic
+ * on the next line under the degree, and details as a full-width
+ * paragraph beneath. Other presets fall back to a single inline
+ * paragraph per item.
+ *
+ * Legacy alternative. Authors may still pass
+ * education as a {@link MultiParagraphBlock} of pipe-separated
+ * strings — e.g.
+ * {@code "**Degree** - Institution | Year. Details..."} — and the
+ * legacy parser tries to interpret them. Prefer
+ * {@code EducationBlock} in new code: the structured fields are
+ * explicit, do not depend on the parser's separator and date
+ * heuristics (which over-trigger on prose containing stray hyphens
+ * like "First-class"), and survive copy-paste from spreadsheets
+ * without quoting concerns.
+ *
+ * @param items education entries in source order, most-recent-first
+ * by convention (must not be null; may be empty;
+ * individual items must not be null)
+ */
+public record EducationBlock(List- items) implements Block {
+
+ /**
+ * Compact constructor that defensively copies the supplied list and
+ * validates that no item reference is null.
+ *
+ * @throws NullPointerException if {@code items} or any element is
+ * null
+ */
+ public EducationBlock {
+ Objects.requireNonNull(items, "items");
+ items = List.copyOf(items);
+ }
+
+ /**
+ * One row in an education stack. All four fields are required
+ * non-null strings but may be blank — a blank {@code institution}
+ * collapses the subtitle line, a blank {@code details} collapses
+ * the body paragraph, and a blank {@code year} renders the degree
+ * row without a right-aligned year column.
+ *
+ * @param degree degree / qualification name, e.g.
+ * {@code "MSc Computer Science"} or
+ * {@code "Oracle Java Certification"}
+ * @param institution awarding institution, e.g.
+ * {@code "University of Manchester"} or
+ * {@code "Professional track"}
+ * @param year year or year range, e.g. {@code "2021"},
+ * {@code "2018-2021"}
+ * @param details additional details (honours, thesis,
+ * specialisation, course content); free prose,
+ * may contain inline markdown
+ * ({@code **bold**}, {@code *italic*})
+ */
+ public record Item(String degree, String institution, String year, String details) {
+
+ /**
+ * Compact constructor: rejects null fields. Use empty strings
+ * for absent values rather than null.
+ */
+ public Item {
+ Objects.requireNonNull(degree, "degree");
+ Objects.requireNonNull(institution, "institution");
+ Objects.requireNonNull(year, "year");
+ Objects.requireNonNull(details, "details");
+ }
+ }
+}
diff --git a/src/main/java/com/demcha/compose/document/templates/blocks/WorkHistoryBlock.java b/src/main/java/com/demcha/compose/document/templates/blocks/WorkHistoryBlock.java
new file mode 100644
index 00000000..434c6525
--- /dev/null
+++ b/src/main/java/com/demcha/compose/document/templates/blocks/WorkHistoryBlock.java
@@ -0,0 +1,73 @@
+package com.demcha.compose.document.templates.blocks;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link Block} that captures a stack of work-history entries with
+ * each field (title, organisation, date, description) supplied
+ * separately so presets can place them precisely without re-parsing a
+ * concatenated source string.
+ *
+ *
This is the preferred shape for "Professional
+ * Experience", "Work History", or any module whose body is a list of
+ * job entries. The {@code BoxedSections} preset renders each
+ * {@link Item} as a row with the title bold on the left and the date
+ * right-aligned, the organisation italic on the next line under the
+ * title, and the description as a full-width paragraph beneath. Other
+ * presets fall back to a single inline paragraph per item.
+ *
+ * Legacy alternative. Authors may still pass work
+ * history as a {@link MultiParagraphBlock} of pipe-separated strings —
+ * e.g. {@code "**Title**, Organisation | *Date* — Description"} — and
+ * {@code BoxedSections} parses that shape for backward compatibility.
+ * Prefer {@code WorkHistoryBlock} in new code: the structured fields
+ * are explicit, do not depend on the parser's separator heuristics,
+ * and survive copy-paste from spreadsheets without quoting concerns.
+ *
+ * @param items work-history entries in source order, oldest-last by
+ * convention (must not be null; may be empty;
+ * individual items must not be null)
+ */
+public record WorkHistoryBlock(List- items) implements Block {
+
+ /**
+ * Compact constructor that defensively copies the supplied list and
+ * validates that no item reference is null.
+ *
+ * @throws NullPointerException if {@code items} or any element is
+ * null
+ */
+ public WorkHistoryBlock {
+ Objects.requireNonNull(items, "items");
+ items = List.copyOf(items);
+ }
+
+ /**
+ * One row in a work-history stack. All four fields are required
+ * non-null strings but may be blank — a blank {@code organisation}
+ * collapses the subtitle line, a blank {@code description}
+ * collapses the body paragraph, and a blank {@code date} renders
+ * the title row without a right-aligned date column.
+ *
+ * @param title role / position, e.g. {@code "Senior Platform Engineer"}
+ * @param organisation employer or organisation, e.g. {@code "Northwind Systems"}
+ * @param date date range or label, e.g. {@code "2024-Present"}, {@code "Jan 2023 – Mar 2024"}
+ * @param description what the role delivered, free prose; may
+ * contain inline markdown ({@code **bold**},
+ * {@code *italic*})
+ */
+ public record Item(String title, String organisation, String date, String description) {
+
+ /**
+ * Compact constructor: rejects null fields. Use empty strings
+ * for absent values rather than null.
+ */
+ public Item {
+ Objects.requireNonNull(title, "title");
+ Objects.requireNonNull(organisation, "organisation");
+ Objects.requireNonNull(date, "date");
+ Objects.requireNonNull(description, "description");
+ }
+ }
+}
diff --git a/src/main/java/com/demcha/compose/document/templates/components/Module.java b/src/main/java/com/demcha/compose/document/templates/components/Module.java
index 45eb161c..ad7da1c5 100644
--- a/src/main/java/com/demcha/compose/document/templates/components/Module.java
+++ b/src/main/java/com/demcha/compose/document/templates/components/Module.java
@@ -14,8 +14,10 @@
import com.demcha.compose.document.templates.blocks.IndentedBlock;
import com.demcha.compose.document.templates.blocks.KeyValueBlock;
import com.demcha.compose.document.templates.blocks.MultiParagraphBlock;
+import com.demcha.compose.document.templates.blocks.EducationBlock;
import com.demcha.compose.document.templates.blocks.NumberedListBlock;
import com.demcha.compose.document.templates.blocks.ParagraphBlock;
+import com.demcha.compose.document.templates.blocks.WorkHistoryBlock;
import com.demcha.compose.document.templates.themes.Spacing;
import com.demcha.compose.document.theme.BusinessTheme;
@@ -176,9 +178,152 @@ private DocumentNode renderBody(BusinessTheme theme, Spacing spacing) {
if (body instanceof KeyValueBlock k) {
return renderKeyValue(k, theme, spacing);
}
+ if (body instanceof WorkHistoryBlock w) {
+ return renderWorkHistory(w, theme, spacing);
+ }
+ if (body instanceof EducationBlock e) {
+ return renderEducation(e, theme, spacing);
+ }
throw new IllegalStateException("Unsupported module body: " + body);
}
+ /**
+ * Default rendering for {@link WorkHistoryBlock} when the active
+ * preset has not overridden the dispatch (e.g. {@code BoxedSections}
+ * renders this block as a structured title/date row + company
+ * subtitle + description paragraph). Generic presets degrade to a
+ * single concatenated paragraph per item, mirroring the legacy
+ * {@code MultiParagraphBlock} format
+ * ({@code "**Title**, Organisation | *Date* — Description"}) so
+ * the data still renders sensibly without requiring every preset
+ * to special-case the new block type.
+ */
+ private DocumentNode renderWorkHistory(WorkHistoryBlock block, BusinessTheme theme, Spacing spacing) {
+ List sources = new ArrayList<>(block.items().size());
+ for (WorkHistoryBlock.Item item : block.items()) {
+ sources.add(concatStructuredFields(
+ item.title(), item.organisation(), item.date(), item.description(),
+ ", ", " | ", " — ",
+ /* italicizeDate */ true));
+ }
+ return renderConcatenatedParagraphs(sources, "workEntry", theme, spacing);
+ }
+
+ /**
+ * Default rendering for {@link EducationBlock} when the active
+ * preset has not overridden the dispatch (e.g. {@code BoxedSections}
+ * renders this block with the same structured layout as a work
+ * entry — degree bold left, year right, institution italic below,
+ * details paragraph beneath). Generic presets degrade to a single
+ * concatenated paragraph per item so the data still renders
+ * sensibly without requiring every preset to special-case the new
+ * block type.
+ */
+ private DocumentNode renderEducation(EducationBlock block, BusinessTheme theme, Spacing spacing) {
+ List sources = new ArrayList<>(block.items().size());
+ for (EducationBlock.Item item : block.items()) {
+ sources.add(concatStructuredFields(
+ item.degree(), item.institution(), item.year(), item.details(),
+ " - ", " | ", ". ",
+ /* italicizeDate */ false));
+ }
+ return renderConcatenatedParagraphs(sources, "educationEntry", theme, spacing);
+ }
+
+ /**
+ * Concatenates the four fields of a structured entry (work history
+ * or education) into a single markdown source line, using the
+ * supplied separators. The first field is rendered bold, the third
+ * is optionally italicized, and the others stay in regular weight.
+ * Empty fields are skipped along with their preceding separator so
+ * a missing organisation / date / description does not leave a
+ * dangling {@code " | "} or {@code " — "} on screen.
+ *
+ *
Extracted from {@link #renderWorkHistory} and
+ * {@link #renderEducation} so adding a future 4-field structured
+ * block (publications, projects, etc.) does not require another
+ * copy of the same string-building loop.
+ */
+ private static String concatStructuredFields(
+ String main, String secondary, String date, String detail,
+ String secondarySep, String dateSep, String detailSep,
+ boolean italicizeDate) {
+ StringBuilder source = new StringBuilder();
+ if (main != null && !main.isBlank()) {
+ source.append("**").append(main).append("**");
+ }
+ if (secondary != null && !secondary.isBlank()) {
+ if (source.length() > 0) {
+ source.append(secondarySep);
+ }
+ source.append(secondary);
+ }
+ if (date != null && !date.isBlank()) {
+ if (source.length() > 0) {
+ source.append(dateSep);
+ }
+ if (italicizeDate) {
+ source.append('*').append(date).append('*');
+ } else {
+ source.append(date);
+ }
+ }
+ if (detail != null && !detail.isBlank()) {
+ if (source.length() > 0) {
+ source.append(detailSep);
+ }
+ source.append(detail);
+ }
+ return source.toString();
+ }
+
+ /**
+ * Wraps a list of pre-built markdown source strings into a
+ * {@link ContainerNode} of {@link ParagraphNode}s using the active
+ * body style and paragraph spacing. Extracted from
+ * {@link #renderWorkHistory} and {@link #renderEducation} so the
+ * boilerplate around constructing ParagraphNode + ContainerNode
+ * lives in one place — any future 4-field structured block can
+ * call this directly after building its own per-item source via
+ * {@link #concatStructuredFields}.
+ *
+ * @param sources per-item markdown source lines
+ * @param entryNameSuffix name suffix appended to {@code name}
+ * for each paragraph (e.g.
+ * {@code "workEntry"} → {@code ".workEntry[0]"})
+ */
+ private DocumentNode renderConcatenatedParagraphs(List sources, String entryNameSuffix,
+ BusinessTheme theme, Spacing spacing) {
+ DocumentTextStyle bodyStyle = bodyStyle(theme);
+ List paragraphs = new ArrayList<>(sources.size());
+ for (int i = 0; i < sources.size(); i++) {
+ paragraphs.add(new ParagraphNode(
+ name + "." + entryNameSuffix + "[" + i + "]",
+ "",
+ MarkdownText.parse(sources.get(i), bodyStyle),
+ bodyStyle,
+ TextAlign.LEFT,
+ spacing.lineSpacing(),
+ "",
+ null,
+ null,
+ null,
+ DocumentInsets.zero(),
+ DocumentInsets.zero(),
+ null));
+ }
+ return new ContainerNode(
+ name + ".body",
+ paragraphs,
+ /* spacing */ spacing.paragraphSpacing(),
+ /* padding */ leftIndent(spacing),
+ DocumentInsets.zero(),
+ null,
+ null,
+ null,
+ null);
+ }
+
private ParagraphNode renderParagraph(ParagraphBlock block, BusinessTheme theme, Spacing spacing) {
DocumentTextStyle bodyStyle = bodyStyle(theme);
List runs = MarkdownText.parse(block.text(), bodyStyle);
diff --git a/src/main/java/com/demcha/compose/document/templates/cv/presets/BoxedSections.java b/src/main/java/com/demcha/compose/document/templates/cv/presets/BoxedSections.java
index eb0282e0..a54c87ac 100644
--- a/src/main/java/com/demcha/compose/document/templates/cv/presets/BoxedSections.java
+++ b/src/main/java/com/demcha/compose/document/templates/cv/presets/BoxedSections.java
@@ -20,8 +20,10 @@
import com.demcha.compose.document.templates.blocks.IndentedBlock;
import com.demcha.compose.document.templates.blocks.KeyValueBlock;
import com.demcha.compose.document.templates.blocks.MultiParagraphBlock;
+import com.demcha.compose.document.templates.blocks.EducationBlock;
import com.demcha.compose.document.templates.blocks.NumberedListBlock;
import com.demcha.compose.document.templates.blocks.ParagraphBlock;
+import com.demcha.compose.document.templates.blocks.WorkHistoryBlock;
import com.demcha.compose.document.templates.cv.spec.CvHeader;
import com.demcha.compose.document.templates.cv.spec.CvModule;
import com.demcha.compose.document.templates.cv.spec.CvSpec;
@@ -217,6 +219,30 @@ private void addModuleBody(SectionBuilder section, CvModule module) {
private void renderBody(SectionBuilder section, Block body) {
if (body instanceof ParagraphBlock p) {
renderParagraph(section, p.text());
+ } else if (body instanceof WorkHistoryBlock w) {
+ // Preferred structured shape — render each item directly
+ // via renderWorkEntry, bypassing the separator-heuristic
+ // parser that the MultiParagraphBlock legacy path uses.
+ for (WorkHistoryBlock.Item item : w.items()) {
+ renderWorkEntry(section, new WorkEntry(
+ safe(item.title()).trim(),
+ safe(item.organisation()).trim(),
+ safe(item.date()).trim(),
+ safe(item.description()).trim()));
+ }
+ } else if (body instanceof EducationBlock e) {
+ // Preferred structured shape for education / certs —
+ // maps onto the same WorkEntry render so the visual
+ // layout (degree bold left, year right, institution
+ // italic below, details paragraph beneath) stays
+ // consistent with Professional Experience.
+ for (EducationBlock.Item item : e.items()) {
+ renderWorkEntry(section, new WorkEntry(
+ safe(item.degree()).trim(),
+ safe(item.institution()).trim(),
+ safe(item.year()).trim(),
+ safe(item.details()).trim()));
+ }
} else if (body instanceof MultiParagraphBlock m) {
for (String line : m.paragraphs()) {
WorkEntry entry = parseWorkEntry(line);
@@ -248,9 +274,49 @@ private void renderBody(SectionBuilder section, Block body) {
}
} else if (body instanceof KeyValueBlock kv) {
for (KeyValueBlock.Entry entry : kv.entries()) {
- renderParagraph(section, entry.key() + ": " + entry.value());
+ renderKeyValueEntry(section, entry);
+ }
+ }
+ }
+
+ /**
+ * Renders a {@link KeyValueBlock} entry as "Key: value"
+ * — the label rendered bold, the colon attached, then the
+ * value in regular weight. Improves readability over the
+ * legacy {@code key + ": " + value} flat-text path that
+ * rendered the entire line in body weight and left "Languages",
+ * "Open Source", and other labels visually indistinguishable
+ * from the prose that followed.
+ */
+ private void renderKeyValueEntry(SectionBuilder section, KeyValueBlock.Entry entry) {
+ String key = safe(entry.key()).trim();
+ String value = safe(entry.value()).trim();
+ if (key.isBlank() && value.isBlank()) {
+ return;
+ }
+ DocumentTextStyle base = style(BODY_FONT, 8.6,
+ DocumentTextDecoration.DEFAULT, INK);
+ // Wrap the key in **markdown bold** markers so the existing
+ // appendMarkdown parser emits a bold inline run for it and
+ // the value continues with the base body style. Authors who
+ // already typed markdown inside key or value keep their
+ // emphasis runs.
+ StringBuilder source = new StringBuilder();
+ if (!key.isBlank()) {
+ source.append("**").append(key).append(":**");
+ }
+ if (!value.isBlank()) {
+ if (source.length() > 0) {
+ source.append(' ');
}
+ source.append(value);
}
+ section.addParagraph(paragraph -> paragraph
+ .textStyle(base)
+ .lineSpacing(1.4)
+ .align(TextAlign.LEFT)
+ .margin(DocumentInsets.top(2))
+ .rich(rich -> appendMarkdown(rich, source.toString(), base)));
}
private void renderParagraph(SectionBuilder section, String rawLine) {
@@ -455,6 +521,26 @@ private static void addPart(List parts, String text,
}
}
+ /**
+ * Parses a single pipe-separated work-history line in the legacy
+ * format
+ * {@code "**Title**, Organisation | *Date* — Description"}. Accepts
+ * em-dash, en-dash, and ASCII hyphen as the date / description
+ * separator, mirroring {@link #splitHeading}. Returns {@code null}
+ * when the input does not look like a work-history line (no pipe,
+ * no recognisable date) so callers can fall back to plain paragraph
+ * rendering.
+ *
+ * @deprecated Backward-compatibility path for callers who feed
+ * work history via {@link MultiParagraphBlock}.
+ * New code should declare work entries via
+ * {@link WorkHistoryBlock} with explicit
+ * {@code (title, organisation, date, description)}
+ * fields — that route bypasses this heuristic parser
+ * entirely and renders directly via
+ * {@link #renderWorkEntry}.
+ */
+ @Deprecated
private static WorkEntry parseWorkEntry(String item) {
String clean = stripBasicMarkdown(safe(item).trim());
int pipeIndex = clean.indexOf('|');
@@ -468,20 +554,68 @@ private static WorkEntry parseWorkEntry(String item) {
}
String date;
String description = "";
- int dashIdx = afterPipe.indexOf(" - ");
- if (dashIdx > 0) {
+ // Date/description separator: accept em-dash " — ", en-dash " – ",
+ // and ASCII " - " (matching splitHeading). Without this, authors
+ // who type " — " in the data file get the whole date+description
+ // collapsed into the date column and an empty description
+ // paragraph beneath — the right-aligned date column then wraps
+ // the description text and the work entry's description never
+ // renders on its own full-width line under the company subtitle.
+ String separator = null;
+ int dashIdx = -1;
+ for (String candidate : new String[]{" — ", " – ", " - "}) {
+ int idx = afterPipe.indexOf(candidate);
+ if (idx > 0) {
+ dashIdx = idx;
+ separator = candidate;
+ break;
+ }
+ }
+ if (separator != null) {
date = afterPipe.substring(0, dashIdx).trim();
- description = afterPipe.substring(dashIdx + 3).trim();
+ description = afterPipe.substring(dashIdx + separator.length()).trim();
} else {
date = afterPipe;
}
if (!looksLikeDate(date)) {
return null;
}
+ // Reject lines whose post-pipe segment is prose dressed up as a
+ // date. Education entries like
+ // {@code "**BSc** - Uni | 2019. First-class honours. ..."} used
+ // to satisfy looksLikeDate via the hyphen inside "First-class"
+ // and would collapse the whole sentence into the right-aligned
+ // date column, hiding the description text. A genuine date is
+ // either a year, year range, or short month-year token without
+ // sentence-ending punctuation; if no explicit date/description
+ // separator was found AND the candidate date contains a period
+ // (or any character that signals prose) the line is treated as
+ // a non-work entry and the caller falls back to plain paragraph
+ // rendering.
+ if (separator == null && containsProseSignals(date)) {
+ return null;
+ }
HeadingParts heading = splitHeading(headingText);
return new WorkEntry(heading.main(), heading.sub(), date, description);
}
+ /**
+ * Detects characters that signal the candidate string is prose
+ * rather than a date or date range. Used by {@link #parseWorkEntry}
+ * as a defensive second pass when the loose {@link #looksLikeDate}
+ * heuristic accepts a sentence containing a stray hyphen / em-dash
+ * (e.g. {@code "2019. First-class honours."} would otherwise count
+ * as a date because of the hyphen inside {@code "First-class"}).
+ */
+ private static boolean containsProseSignals(String value) {
+ if (value == null || value.isBlank()) {
+ return false;
+ }
+ return value.contains(".")
+ || value.contains(":")
+ || value.contains(";");
+ }
+
private static HeadingParts splitHeading(String heading) {
for (String separator : new String[]{" – ", " — ", " - "}) {
int idx = heading.indexOf(separator);
@@ -531,15 +665,25 @@ private static boolean isProjectsModule(String title) {
}
private static ProjectParts parseProjectItem(String item) {
- // Split on " - " (space-hyphen-space, mirroring WorkEntry parsing)
- // so an em-dash or hyphen inside the description is not eaten.
- // Falls back to "title only" when no separator is present.
- int sepIndex = item.indexOf(" - ");
- if (sepIndex <= 0) {
+ // Split on " — " (em-dash), " – " (en-dash), or " - " (ASCII),
+ // mirroring WorkEntry parsing so an em-dash inside the source
+ // line doesn't eat the description into the name. Falls back
+ // to "title only" when no separator is present.
+ String separator = null;
+ int sepIndex = -1;
+ for (String candidate : new String[]{" — ", " – ", " - "}) {
+ int idx = item.indexOf(candidate);
+ if (idx > 0) {
+ sepIndex = idx;
+ separator = candidate;
+ break;
+ }
+ }
+ if (separator == null) {
return new ProjectParts(item.trim(), "");
}
String name = item.substring(0, sepIndex).trim();
- String description = item.substring(sepIndex + 3).trim();
+ String description = item.substring(sepIndex + separator.length()).trim();
return new ProjectParts(name, description);
}
diff --git a/src/test/java/com/demcha/compose/document/templates/blocks/BlockTest.java b/src/test/java/com/demcha/compose/document/templates/blocks/BlockTest.java
index 8058efd7..bc30971c 100644
--- a/src/test/java/com/demcha/compose/document/templates/blocks/BlockTest.java
+++ b/src/test/java/com/demcha/compose/document/templates/blocks/BlockTest.java
@@ -221,11 +221,11 @@ void keyValueBlockIsImmutable() {
// Sealing ---------------------------------------------------------
@Test
- void blockSealingPermitsAllSixVariants() {
- // The sealed permit list of Block must include all six concrete
- // block kinds. This is essentially a compile-time guarantee, but
- // the test fails fast if the permit list ever drifts from the
- // record set in this package.
+ void blockSealingPermitsAllEightVariants() {
+ // The sealed permit list of Block must include all eight
+ // concrete block kinds. This is essentially a compile-time
+ // guarantee, but the test fails fast if the permit list ever
+ // drifts from the record set in this package.
Class>[] permitted = Block.class.getPermittedSubclasses();
assertThat(permitted)
.containsExactlyInAnyOrder(
@@ -234,6 +234,8 @@ void blockSealingPermitsAllSixVariants() {
NumberedListBlock.class,
IndentedBlock.class,
KeyValueBlock.class,
- MultiParagraphBlock.class);
+ MultiParagraphBlock.class,
+ WorkHistoryBlock.class,
+ EducationBlock.class);
}
}
diff --git a/src/test/java/com/demcha/compose/document/templates/cv/presets/BoxedSectionsStructuredRegressionTest.java b/src/test/java/com/demcha/compose/document/templates/cv/presets/BoxedSectionsStructuredRegressionTest.java
new file mode 100644
index 00000000..867e2b12
--- /dev/null
+++ b/src/test/java/com/demcha/compose/document/templates/cv/presets/BoxedSectionsStructuredRegressionTest.java
@@ -0,0 +1,160 @@
+package com.demcha.compose.document.templates.cv.presets;
+
+import com.demcha.compose.document.api.DocumentSession;
+import com.demcha.compose.document.templates.TemplateTestSupport;
+import com.demcha.compose.document.templates.api.DocumentTemplate;
+import com.demcha.compose.document.templates.blocks.EducationBlock;
+import com.demcha.compose.document.templates.blocks.KeyValueBlock;
+import com.demcha.compose.document.templates.blocks.MultiParagraphBlock;
+import com.demcha.compose.document.templates.blocks.WorkHistoryBlock;
+import com.demcha.compose.document.templates.cv.spec.CvHeader;
+import com.demcha.compose.document.templates.cv.spec.CvModule;
+import com.demcha.compose.document.templates.cv.spec.CvSpec;
+import com.demcha.compose.document.theme.BusinessTheme;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+/**
+ * Regression tests for the v1.6.4 BoxedSections changes:
+ *
+ *
+ * - {@link WorkHistoryBlock} renders each item with the structured
+ * title / date / organisation / description layout (not the
+ * legacy "everything on one line" fallback).
+ * - {@link EducationBlock} renders each item with the same
+ * structured layout (degree / year / institution / details).
+ * - {@link KeyValueBlock} renders each entry as bold key:
+ * followed by a regular-weight value.
+ * - {@link MultiParagraphBlock} entries whose post-pipe segment is
+ * prose containing sentence-ending punctuation (e.g.
+ * {@code "... | 2019. First-class honours. Specialisation ..."})
+ * are rejected by the deprecated {@code parseWorkEntry} parser
+ * and fall back to plain paragraph rendering instead of
+ * collapsing the description into the date column.
+ *
+ *
+ * Each test renders a focused {@link CvSpec} into an in-memory
+ * session and compares the resulting layout tree against a checked-in
+ * baseline JSON under
+ * {@code src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/}.
+ * Re-run with {@code -Dgraphcompose.updateSnapshots=true} to refresh
+ * a baseline after an intentional change.
+ */
+class BoxedSectionsStructuredRegressionTest {
+
+ private static final BusinessTheme THEME = BusinessTheme.modern();
+
+ private static CvHeader header() {
+ return CvHeader.builder()
+ .name("Artem Demchyshyn")
+ .address("London, UK")
+ .email("artem@demo.dev")
+ .build();
+ }
+
+ @Test
+ void workHistoryBlockRendersStructuredLayout() throws Exception {
+ CvSpec spec = CvSpec.builder()
+ .header(header())
+ .module(CvModule.of("Professional Experience",
+ new WorkHistoryBlock(List.of(
+ new WorkHistoryBlock.Item(
+ "Senior Platform Engineer",
+ "Northwind Systems",
+ "2024-Present",
+ "Led the reusable document-generation platform."),
+ new WorkHistoryBlock.Item(
+ "Software Engineer",
+ "BrightLeaf Labs",
+ "2021-2024",
+ "Built backend services and rendering pipelines.")))))
+ .build();
+ DocumentTemplate template = BoxedSections.create(THEME);
+ try (DocumentSession document = TemplateTestSupport.openInMemoryDocument(
+ PDRectangle.A4, 15, 10, 15, 15)) {
+ template.compose(document, spec);
+ TemplateTestSupport.assertCanonicalSnapshot(document,
+ "boxed_sections_work_history_block", "cv-v2", "regression");
+ }
+ }
+
+ @Test
+ void educationBlockRendersStructuredLayout() throws Exception {
+ CvSpec spec = CvSpec.builder()
+ .header(header())
+ .module(CvModule.of("Education & Certifications",
+ new EducationBlock(List.of(
+ new EducationBlock.Item(
+ "MSc Computer Science",
+ "University of Manchester",
+ "2021",
+ "Distinction. Thesis: layout primitives for deterministic rendering."),
+ new EducationBlock.Item(
+ "Oracle Java Certification",
+ "Professional track",
+ "2023",
+ "Java 17 platform deep-dive: records, sealed types, pattern matching.")))))
+ .build();
+ DocumentTemplate template = BoxedSections.create(THEME);
+ try (DocumentSession document = TemplateTestSupport.openInMemoryDocument(
+ PDRectangle.A4, 15, 10, 15, 15)) {
+ template.compose(document, spec);
+ TemplateTestSupport.assertCanonicalSnapshot(document,
+ "boxed_sections_education_block", "cv-v2", "regression");
+ }
+ }
+
+ @Test
+ void keyValueBlockRendersKeysInBold() throws Exception {
+ CvSpec spec = CvSpec.builder()
+ .header(header())
+ .module(CvModule.of("Additional Information",
+ new KeyValueBlock(List.of(
+ new KeyValueBlock.Entry("Languages",
+ "English (Fluent), German (Intermediate)"),
+ new KeyValueBlock.Entry("Work Eligibility",
+ "Eligible to work in the UK and the EU"),
+ new KeyValueBlock.Entry("Open Source",
+ "Maintainer of GraphCompose.")))))
+ .build();
+ DocumentTemplate template = BoxedSections.create(THEME);
+ try (DocumentSession document = TemplateTestSupport.openInMemoryDocument(
+ PDRectangle.A4, 15, 10, 15, 15)) {
+ template.compose(document, spec);
+ TemplateTestSupport.assertCanonicalSnapshot(document,
+ "boxed_sections_key_value_bold_key", "cv-v2", "regression");
+ }
+ }
+
+ @Test
+ void multiParagraphProseDoesNotCollapseIntoDateColumn() throws Exception {
+ // Locks the parseWorkEntry prose-rejection: lines like
+ // "... | 2019. First-class honours. Specialisation ..." used
+ // to collapse the whole post-pipe segment into the right-
+ // aligned date column (the hyphen inside "First-class" tricked
+ // looksLikeDate into accepting the prose as a date). The
+ // deprecated parser now bails on sentence-ending punctuation
+ // when no explicit date/description separator is present and
+ // BoxedSections renders the line as a plain paragraph instead.
+ CvSpec spec = CvSpec.builder()
+ .header(header())
+ .module(CvModule.of("Education & Certifications",
+ new MultiParagraphBlock(List.of(
+ "**BSc Software Engineering** - Imperial College London | "
+ + "2019. First-class honours. Specialisation in "
+ + "compilers and static analysis.",
+ "**Oracle Java Certification** - Professional track | "
+ + "2023. Java 17 platform deep-dive: records, "
+ + "sealed types, pattern matching."))))
+ .build();
+ DocumentTemplate template = BoxedSections.create(THEME);
+ try (DocumentSession document = TemplateTestSupport.openInMemoryDocument(
+ PDRectangle.A4, 15, 10, 15, 15)) {
+ template.compose(document, spec);
+ TemplateTestSupport.assertCanonicalSnapshot(document,
+ "boxed_sections_prose_rejection_legacy_path", "cv-v2", "regression");
+ }
+ }
+}
diff --git a/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetLayoutSnapshotTest.java b/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetLayoutSnapshotTest.java
index 52ce1574..8ee21172 100644
--- a/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetLayoutSnapshotTest.java
+++ b/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetLayoutSnapshotTest.java
@@ -73,6 +73,14 @@ private static CvSpec canonicalCvSpec() {
+ "*2024-Present* — Led reusable document flows.",
"**Software Engineer**, BrightLeaf Labs | *2021-2024* "
+ "— Built backend services and rendering pipelines."))))
+ /* Note: this snapshot test intentionally exercises the
+ * legacy MultiParagraphBlock pipe-separated string path
+ * to pin the parser's em-dash / en-dash / ASCII-hyphen
+ * separator handling. The structurally equivalent
+ * preferred path uses WorkHistoryBlock — see
+ * PresetVisualGalleryTest#sampleSpec for the canonical
+ * example. Both paths converge on renderWorkEntry and
+ * produce the same LayoutGraph for equivalent data. */
.module(CvModule.of("Additional Information",
new KeyValueBlock(List.of(
new KeyValueBlock.Entry("Location",
diff --git a/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetVisualGalleryTest.java b/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetVisualGalleryTest.java
index 0b2cd780..ba7670df 100644
--- a/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetVisualGalleryTest.java
+++ b/src/test/java/com/demcha/compose/document/templates/cv/presets/PresetVisualGalleryTest.java
@@ -8,7 +8,7 @@
import com.demcha.compose.document.templates.blocks.BulletListBlock;
import com.demcha.compose.document.templates.blocks.IndentedBlock;
import com.demcha.compose.document.templates.blocks.KeyValueBlock;
-import com.demcha.compose.document.templates.blocks.MultiParagraphBlock;
+import com.demcha.compose.document.templates.blocks.WorkHistoryBlock;
import com.demcha.compose.document.templates.blocks.ParagraphBlock;
import com.demcha.compose.document.templates.cv.spec.CvHeader;
import com.demcha.compose.document.templates.cv.spec.CvModule;
@@ -76,11 +76,22 @@ private static CvSpec sampleSpec() {
"Template Studio (Kotlin, Compose Desktop)",
"Internal tool for evaluating CV, proposal, and invoice output")))))
.module(CvModule.of("Professional Experience",
- new MultiParagraphBlock(List.of(
- "**Senior Platform Engineer**, Northwind Systems | "
- + "*2024-Present* — Led reusable document flows.",
- "**Software Engineer**, BrightLeaf Labs | *2021-2024* "
- + "— Built backend services and rendering pipelines."))))
+ // Preferred: structured WorkHistoryBlock with
+ // separate title / organisation / date /
+ // description fields. BoxedSections routes this
+ // straight to renderWorkEntry, bypassing the
+ // legacy pipe-separated string parser.
+ new WorkHistoryBlock(List.of(
+ new WorkHistoryBlock.Item(
+ "Senior Platform Engineer",
+ "Northwind Systems",
+ "2024-Present",
+ "Led reusable document flows."),
+ new WorkHistoryBlock.Item(
+ "Software Engineer",
+ "BrightLeaf Labs",
+ "2021-2024",
+ "Built backend services and rendering pipelines.")))))
.module(CvModule.of("Additional Information",
new KeyValueBlock(List.of(
new KeyValueBlock.Entry("Location",
diff --git a/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_education_block.json b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_education_block.json
new file mode 100644
index 00000000..249b3ea4
--- /dev/null
+++ b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_education_block.json
@@ -0,0 +1,677 @@
+{
+ "formatVersion" : "2.0",
+ "canvas" : {
+ "pageWidth" : 595.276,
+ "pageHeight" : 841.89,
+ "innerWidth" : 570.276,
+ "innerHeight" : 811.89,
+ "margin" : {
+ "top" : 15.0,
+ "right" : 10.0,
+ "bottom" : 15.0,
+ "left" : 15.0
+ }
+ },
+ "totalPages" : 1,
+ "nodes" : [ {
+ "path" : "BoxedSectionsRoot[0]",
+ "entityName" : "BoxedSectionsRoot",
+ "entityKind" : "ContainerNode",
+ "parentPath" : null,
+ "childIndex" : 0,
+ "depth" : 1,
+ "layer" : 1,
+ "computedX" : 15.0,
+ "computedY" : 629.99,
+ "placementX" : 15.0,
+ "placementY" : 629.99,
+ "placementWidth" : 570.276,
+ "placementHeight" : 196.9,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 196.9,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "entityName" : "BoxedSectionsHeader",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 0,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 794.402,
+ "placementX" : 15.0,
+ "placementY" : 794.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 32.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 32.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 798.402,
+ "placementX" : 15.0,
+ "placementY" : 798.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 28.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 28.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "entityName" : "BoxedSectionsContact",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 1,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 768.14,
+ "placementX" : 15.0,
+ "placementY" : 768.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 19.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 19.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 772.14,
+ "placementX" : 15.0,
+ "placementY" : 772.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 11.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 11.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "entityName" : "BoxedSectionsBanner_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 2,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 734.42,
+ "placementX" : 15.0,
+ "placementY" : 734.42,
+ "placementWidth" : 570.276,
+ "placementHeight" : 22.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 22.72,
+ "margin" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 5.0,
+ "right" : 5.0,
+ "bottom" : 5.0,
+ "left" : 5.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 20.0,
+ "computedY" : 739.42,
+ "placementX" : 20.0,
+ "placementY" : 739.42,
+ "placementWidth" : 560.276,
+ "placementHeight" : 12.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 560.276,
+ "contentHeight" : 12.72,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "entityName" : "BoxedSectionsBody_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 3,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 629.99,
+ "placementX" : 15.0,
+ "placementY" : 629.99,
+ "placementWidth" : 570.276,
+ "placementHeight" : 97.43,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 97.43,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 4.0,
+ "bottom" : 0.0,
+ "left" : 4.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]",
+ "entityName" : "BoxedSectionsEntryHeader",
+ "entityKind" : "RowNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 711.23,
+ "placementX" : 19.0,
+ "placementY" : 711.23,
+ "placementWidth" : 562.276,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 562.276,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Title[0]",
+ "entityName" : "Title",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]",
+ "childIndex" : 0,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 19.0,
+ "computedY" : 711.23,
+ "placementX" : 19.0,
+ "placementY" : 711.23,
+ "placementWidth" : 99.875,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 99.875,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Title[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Title[0]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 19.0,
+ "computedY" : 711.23,
+ "placementX" : 19.0,
+ "placementY" : 711.23,
+ "placementWidth" : 99.875,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 99.875,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Date[1]",
+ "entityName" : "Date",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]",
+ "childIndex" : 1,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 409.259,
+ "computedY" : 711.76,
+ "placementX" : 409.259,
+ "placementY" : 711.76,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Date[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Date[1]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 409.259,
+ "computedY" : 711.76,
+ "placementX" : 409.259,
+ "placementY" : 711.76,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[1]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 1,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 696.1,
+ "placementX" : 19.0,
+ "placementY" : 696.1,
+ "placementWidth" : 88.393,
+ "placementHeight" : 11.13,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 88.393,
+ "contentHeight" : 11.13,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[2]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 2,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 678.705,
+ "placementX" : 19.0,
+ "placementY" : 678.705,
+ "placementWidth" : 250.432,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 250.432,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]",
+ "entityName" : "BoxedSectionsEntryHeader",
+ "entityKind" : "RowNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 3,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 662.515,
+ "placementX" : 19.0,
+ "placementY" : 662.515,
+ "placementWidth" : 562.276,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 562.276,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Title[0]",
+ "entityName" : "Title",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]",
+ "childIndex" : 0,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 19.0,
+ "computedY" : 662.515,
+ "placementX" : 19.0,
+ "placementY" : 662.515,
+ "placementWidth" : 107.806,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 107.806,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Title[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Title[0]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 19.0,
+ "computedY" : 662.515,
+ "placementX" : 19.0,
+ "placementY" : 662.515,
+ "placementWidth" : 107.806,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 107.806,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Date[1]",
+ "entityName" : "Date",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]",
+ "childIndex" : 1,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 409.259,
+ "computedY" : 663.045,
+ "placementX" : 409.259,
+ "placementY" : 663.045,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Date[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Date[1]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 409.259,
+ "computedY" : 663.045,
+ "placementX" : 409.259,
+ "placementY" : 663.045,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[4]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 4,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 647.385,
+ "placementX" : 19.0,
+ "placementY" : 647.385,
+ "placementWidth" : 64.05,
+ "placementHeight" : 11.13,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 64.05,
+ "contentHeight" : 11.13,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[5]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 5,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 629.99,
+ "placementX" : 19.0,
+ "placementY" : 629.99,
+ "placementWidth" : 260.632,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 260.632,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.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/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_key_value_bold_key.json b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_key_value_bold_key.json
new file mode 100644
index 00000000..04c294fe
--- /dev/null
+++ b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_key_value_bold_key.json
@@ -0,0 +1,347 @@
+{
+ "formatVersion" : "2.0",
+ "canvas" : {
+ "pageWidth" : 595.276,
+ "pageHeight" : 841.89,
+ "innerWidth" : 570.276,
+ "innerHeight" : 811.89,
+ "margin" : {
+ "top" : 15.0,
+ "right" : 10.0,
+ "bottom" : 15.0,
+ "left" : 15.0
+ }
+ },
+ "totalPages" : 1,
+ "nodes" : [ {
+ "path" : "BoxedSectionsRoot[0]",
+ "entityName" : "BoxedSectionsRoot",
+ "entityKind" : "ContainerNode",
+ "parentPath" : null,
+ "childIndex" : 0,
+ "depth" : 1,
+ "layer" : 1,
+ "computedX" : 15.0,
+ "computedY" : 675.235,
+ "placementX" : 15.0,
+ "placementY" : 675.235,
+ "placementWidth" : 570.276,
+ "placementHeight" : 151.655,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 151.655,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "entityName" : "BoxedSectionsHeader",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 0,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 794.402,
+ "placementX" : 15.0,
+ "placementY" : 794.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 32.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 32.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 798.402,
+ "placementX" : 15.0,
+ "placementY" : 798.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 28.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 28.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "entityName" : "BoxedSectionsContact",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 1,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 768.14,
+ "placementX" : 15.0,
+ "placementY" : 768.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 19.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 19.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 772.14,
+ "placementX" : 15.0,
+ "placementY" : 772.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 11.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 11.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "entityName" : "BoxedSectionsBanner_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 2,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 734.42,
+ "placementX" : 15.0,
+ "placementY" : 734.42,
+ "placementWidth" : 570.276,
+ "placementHeight" : 22.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 22.72,
+ "margin" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 5.0,
+ "right" : 5.0,
+ "bottom" : 5.0,
+ "left" : 5.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 20.0,
+ "computedY" : 739.42,
+ "placementX" : 20.0,
+ "placementY" : 739.42,
+ "placementWidth" : 560.276,
+ "placementHeight" : 12.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 560.276,
+ "contentHeight" : 12.72,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "entityName" : "BoxedSectionsBody_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 3,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 675.235,
+ "placementX" : 15.0,
+ "placementY" : 675.235,
+ "placementWidth" : 219.766,
+ "placementHeight" : 52.185,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 219.766,
+ "contentHeight" : 52.185,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 4.0,
+ "bottom" : 0.0,
+ "left" : 4.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 710.025,
+ "placementX" : 19.0,
+ "placementY" : 710.025,
+ "placementWidth" : 202.986,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 202.986,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[1]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 1,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 692.63,
+ "placementX" : 19.0,
+ "placementY" : 692.63,
+ "placementWidth" : 211.766,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 211.766,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[2]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 2,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 675.235,
+ "placementX" : 19.0,
+ "placementY" : 675.235,
+ "placementWidth" : 172.482,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.482,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.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/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_prose_rejection_legacy_path.json b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_prose_rejection_legacy_path.json
new file mode 100644
index 00000000..c2419d70
--- /dev/null
+++ b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_prose_rejection_legacy_path.json
@@ -0,0 +1,317 @@
+{
+ "formatVersion" : "2.0",
+ "canvas" : {
+ "pageWidth" : 595.276,
+ "pageHeight" : 841.89,
+ "innerWidth" : 570.276,
+ "innerHeight" : 811.89,
+ "margin" : {
+ "top" : 15.0,
+ "right" : 10.0,
+ "bottom" : 15.0,
+ "left" : 15.0
+ }
+ },
+ "totalPages" : 1,
+ "nodes" : [ {
+ "path" : "BoxedSectionsRoot[0]",
+ "entityName" : "BoxedSectionsRoot",
+ "entityKind" : "ContainerNode",
+ "parentPath" : null,
+ "childIndex" : 0,
+ "depth" : 1,
+ "layer" : 1,
+ "computedX" : 15.0,
+ "computedY" : 692.63,
+ "placementX" : 15.0,
+ "placementY" : 692.63,
+ "placementWidth" : 570.276,
+ "placementHeight" : 134.26,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 134.26,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "entityName" : "BoxedSectionsHeader",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 0,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 794.402,
+ "placementX" : 15.0,
+ "placementY" : 794.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 32.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 32.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 798.402,
+ "placementX" : 15.0,
+ "placementY" : 798.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 28.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 28.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "entityName" : "BoxedSectionsContact",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 1,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 768.14,
+ "placementX" : 15.0,
+ "placementY" : 768.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 19.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 19.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 772.14,
+ "placementX" : 15.0,
+ "placementY" : 772.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 11.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 11.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "entityName" : "BoxedSectionsBanner_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 2,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 734.42,
+ "placementX" : 15.0,
+ "placementY" : 734.42,
+ "placementWidth" : 570.276,
+ "placementHeight" : 22.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 22.72,
+ "margin" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 5.0,
+ "right" : 5.0,
+ "bottom" : 5.0,
+ "left" : 5.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 20.0,
+ "computedY" : 739.42,
+ "placementX" : 20.0,
+ "placementY" : 739.42,
+ "placementWidth" : 560.276,
+ "placementHeight" : 12.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 560.276,
+ "contentHeight" : 12.72,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "entityName" : "BoxedSectionsBody_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 3,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 692.63,
+ "placementX" : 15.0,
+ "placementY" : 692.63,
+ "placementWidth" : 500.66,
+ "placementHeight" : 34.79,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 500.66,
+ "contentHeight" : 34.79,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 4.0,
+ "bottom" : 0.0,
+ "left" : 4.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 710.025,
+ "placementX" : 19.0,
+ "placementY" : 710.025,
+ "placementWidth" : 492.66,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 492.66,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[1]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 1,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 692.63,
+ "placementX" : 19.0,
+ "placementY" : 692.63,
+ "placementWidth" : 466.937,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 466.937,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.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/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_work_history_block.json b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_work_history_block.json
new file mode 100644
index 00000000..900e90d6
--- /dev/null
+++ b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/regression/boxed_sections_work_history_block.json
@@ -0,0 +1,677 @@
+{
+ "formatVersion" : "2.0",
+ "canvas" : {
+ "pageWidth" : 595.276,
+ "pageHeight" : 841.89,
+ "innerWidth" : 570.276,
+ "innerHeight" : 811.89,
+ "margin" : {
+ "top" : 15.0,
+ "right" : 10.0,
+ "bottom" : 15.0,
+ "left" : 15.0
+ }
+ },
+ "totalPages" : 1,
+ "nodes" : [ {
+ "path" : "BoxedSectionsRoot[0]",
+ "entityName" : "BoxedSectionsRoot",
+ "entityKind" : "ContainerNode",
+ "parentPath" : null,
+ "childIndex" : 0,
+ "depth" : 1,
+ "layer" : 1,
+ "computedX" : 15.0,
+ "computedY" : 629.99,
+ "placementX" : 15.0,
+ "placementY" : 629.99,
+ "placementWidth" : 570.276,
+ "placementHeight" : 196.9,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 196.9,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "entityName" : "BoxedSectionsHeader",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 0,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 794.402,
+ "placementX" : 15.0,
+ "placementY" : 794.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 32.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 32.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsHeader[0]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 798.402,
+ "placementX" : 15.0,
+ "placementY" : 798.402,
+ "placementWidth" : 570.276,
+ "placementHeight" : 28.488,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 28.488,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "entityName" : "BoxedSectionsContact",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 1,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 768.14,
+ "placementX" : 15.0,
+ "placementY" : 768.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 19.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 19.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 4.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsContact[1]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 15.0,
+ "computedY" : 772.14,
+ "placementX" : 15.0,
+ "placementY" : 772.14,
+ "placementWidth" : 570.276,
+ "placementHeight" : 11.263,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 11.263,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "entityName" : "BoxedSectionsBanner_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 2,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 734.42,
+ "placementX" : 15.0,
+ "placementY" : 734.42,
+ "placementWidth" : 570.276,
+ "placementHeight" : 22.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 22.72,
+ "margin" : {
+ "top" : 4.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 5.0,
+ "right" : 5.0,
+ "bottom" : 5.0,
+ "left" : 5.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_0[2]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 20.0,
+ "computedY" : 739.42,
+ "placementX" : 20.0,
+ "placementY" : 739.42,
+ "placementWidth" : 560.276,
+ "placementHeight" : 12.72,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 560.276,
+ "contentHeight" : 12.72,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "entityName" : "BoxedSectionsBody_0",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]",
+ "childIndex" : 3,
+ "depth" : 2,
+ "layer" : 2,
+ "computedX" : 15.0,
+ "computedY" : 629.99,
+ "placementX" : 15.0,
+ "placementY" : 629.99,
+ "placementWidth" : 570.276,
+ "placementHeight" : 97.43,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 570.276,
+ "contentHeight" : 97.43,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 4.0,
+ "right" : 4.0,
+ "bottom" : 0.0,
+ "left" : 4.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]",
+ "entityName" : "BoxedSectionsEntryHeader",
+ "entityKind" : "RowNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 0,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 711.23,
+ "placementX" : 19.0,
+ "placementY" : 711.23,
+ "placementWidth" : 562.276,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 562.276,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Title[0]",
+ "entityName" : "Title",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]",
+ "childIndex" : 0,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 19.0,
+ "computedY" : 711.23,
+ "placementX" : 19.0,
+ "placementY" : 711.23,
+ "placementWidth" : 112.038,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 112.038,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Title[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Title[0]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 19.0,
+ "computedY" : 711.23,
+ "placementX" : 19.0,
+ "placementY" : 711.23,
+ "placementWidth" : 112.038,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 112.038,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Date[1]",
+ "entityName" : "Date",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]",
+ "childIndex" : 1,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 409.259,
+ "computedY" : 711.76,
+ "placementX" : 409.259,
+ "placementY" : 711.76,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Date[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[0]/Date[1]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 409.259,
+ "computedY" : 711.76,
+ "placementX" : 409.259,
+ "placementY" : 711.76,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[1]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 1,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 696.1,
+ "placementX" : 19.0,
+ "placementY" : 696.1,
+ "placementWidth" : 68.88,
+ "placementHeight" : 11.13,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 68.88,
+ "contentHeight" : 11.13,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[2]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 2,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 678.705,
+ "placementX" : 19.0,
+ "placementY" : 678.705,
+ "placementWidth" : 186.534,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 186.534,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]",
+ "entityName" : "BoxedSectionsEntryHeader",
+ "entityKind" : "RowNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 3,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 662.515,
+ "placementX" : 19.0,
+ "placementY" : 662.515,
+ "placementWidth" : 562.276,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 562.276,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Title[0]",
+ "entityName" : "Title",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]",
+ "childIndex" : 0,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 19.0,
+ "computedY" : 662.515,
+ "placementX" : 19.0,
+ "placementY" : 662.515,
+ "placementWidth" : 80.914,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 80.914,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Title[0]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Title[0]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 19.0,
+ "computedY" : 662.515,
+ "placementX" : 19.0,
+ "placementY" : 662.515,
+ "placementWidth" : 80.914,
+ "placementHeight" : 12.19,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 80.914,
+ "contentHeight" : 12.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" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Date[1]",
+ "entityName" : "Date",
+ "entityKind" : "SectionNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]",
+ "childIndex" : 1,
+ "depth" : 4,
+ "layer" : 4,
+ "computedX" : 409.259,
+ "computedY" : 663.045,
+ "placementX" : 409.259,
+ "placementY" : 663.045,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Date[1]/ParagraphNode[0]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/BoxedSectionsEntryHeader[3]/Date[1]",
+ "childIndex" : 0,
+ "depth" : 5,
+ "layer" : 5,
+ "computedX" : 409.259,
+ "computedY" : 663.045,
+ "placementX" : 409.259,
+ "placementY" : 663.045,
+ "placementWidth" : 172.017,
+ "placementHeight" : 11.66,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 172.017,
+ "contentHeight" : 11.66,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[4]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 4,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 647.385,
+ "placementX" : 19.0,
+ "placementY" : 647.385,
+ "placementWidth" : 56.448,
+ "placementHeight" : 11.13,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 56.448,
+ "contentHeight" : 11.13,
+ "margin" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]/ParagraphNode[5]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_0[3]",
+ "childIndex" : 5,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 629.99,
+ "placementX" : 19.0,
+ "placementY" : 629.99,
+ "placementWidth" : 180.05,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 180.05,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.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/src/test/resources/layout-snapshots/canonical-templates/cv-v2/template_v2_cv_boxed_sections.json b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/template_v2_cv_boxed_sections.json
index 851bcc4d..5c51975a 100644
--- a/src/test/resources/layout-snapshots/canonical-templates/cv-v2/template_v2_cv_boxed_sections.json
+++ b/src/test/resources/layout-snapshots/canonical-templates/cv-v2/template_v2_cv_boxed_sections.json
@@ -22,15 +22,15 @@
"depth" : 1,
"layer" : 1,
"computedX" : 15.0,
- "computedY" : 234.18,
+ "computedY" : 221.65,
"placementX" : 15.0,
- "placementY" : 234.18,
+ "placementY" : 221.65,
"placementWidth" : 570.276,
- "placementHeight" : 592.71,
+ "placementHeight" : 605.24,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 570.276,
- "contentHeight" : 592.71,
+ "contentHeight" : 605.24,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -892,15 +892,15 @@
"depth" : 2,
"layer" : 2,
"computedX" : 15.0,
- "computedY" : 309.69,
+ "computedY" : 297.16,
"placementX" : 15.0,
- "placementY" : 309.69,
+ "placementY" : 297.16,
"placementWidth" : 570.276,
- "placementHeight" : 84.9,
+ "placementHeight" : 97.43,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 570.276,
- "contentHeight" : 84.9,
+ "contentHeight" : 97.43,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -922,15 +922,15 @@
"depth" : 3,
"layer" : 3,
"computedX" : 19.0,
- "computedY" : 367.27,
+ "computedY" : 378.4,
"placementX" : 19.0,
- "placementY" : 367.27,
+ "placementY" : 378.4,
"placementWidth" : 562.276,
- "placementHeight" : 23.32,
+ "placementHeight" : 12.19,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 562.276,
- "contentHeight" : 23.32,
+ "contentHeight" : 12.19,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -1012,15 +1012,15 @@
"depth" : 4,
"layer" : 4,
"computedX" : 409.259,
- "computedY" : 367.27,
+ "computedY" : 378.93,
"placementX" : 409.259,
- "placementY" : 367.27,
+ "placementY" : 378.93,
"placementWidth" : 172.017,
- "placementHeight" : 23.32,
+ "placementHeight" : 11.66,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 172.017,
- "contentHeight" : 23.32,
+ "contentHeight" : 11.66,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -1042,15 +1042,15 @@
"depth" : 5,
"layer" : 5,
"computedX" : 409.259,
- "computedY" : 367.27,
+ "computedY" : 378.93,
"placementX" : 409.259,
- "placementY" : 367.27,
+ "placementY" : 378.93,
"placementWidth" : 172.017,
- "placementHeight" : 23.32,
+ "placementHeight" : 11.66,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 172.017,
- "contentHeight" : 23.32,
+ "contentHeight" : 11.66,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -1072,9 +1072,9 @@
"depth" : 3,
"layer" : 3,
"computedX" : 19.0,
- "computedY" : 352.14,
+ "computedY" : 363.27,
"placementX" : 19.0,
- "placementY" : 352.14,
+ "placementY" : 363.27,
"placementWidth" : 68.88,
"placementHeight" : 11.13,
"startPage" : 0,
@@ -1094,23 +1094,53 @@
"left" : 0.0
}
}, {
- "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]",
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/ParagraphNode[2]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]",
+ "childIndex" : 2,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 345.875,
+ "placementX" : 19.0,
+ "placementY" : 345.875,
+ "placementWidth" : 113.511,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 113.511,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]",
"entityName" : "BoxedSectionsEntryHeader",
"entityKind" : "RowNode",
"parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]",
- "childIndex" : 2,
+ "childIndex" : 3,
"depth" : 3,
"layer" : 3,
"computedX" : 19.0,
- "computedY" : 324.82,
+ "computedY" : 329.685,
"placementX" : 19.0,
- "placementY" : 324.82,
+ "placementY" : 329.685,
"placementWidth" : 562.276,
- "placementHeight" : 23.32,
+ "placementHeight" : 12.19,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 562.276,
- "contentHeight" : 23.32,
+ "contentHeight" : 12.19,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -1124,17 +1154,17 @@
"left" : 0.0
}
}, {
- "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]/Title[0]",
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]/Title[0]",
"entityName" : "Title",
"entityKind" : "SectionNode",
- "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]",
"childIndex" : 0,
"depth" : 4,
"layer" : 4,
"computedX" : 19.0,
- "computedY" : 335.95,
+ "computedY" : 329.685,
"placementX" : 19.0,
- "placementY" : 335.95,
+ "placementY" : 329.685,
"placementWidth" : 80.914,
"placementHeight" : 12.19,
"startPage" : 0,
@@ -1154,17 +1184,17 @@
"left" : 0.0
}
}, {
- "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]/Title[0]/ParagraphNode[0]",
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]/Title[0]/ParagraphNode[0]",
"entityName" : null,
"entityKind" : "ParagraphNode",
- "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]/Title[0]",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]/Title[0]",
"childIndex" : 0,
"depth" : 5,
"layer" : 5,
"computedX" : 19.0,
- "computedY" : 335.95,
+ "computedY" : 329.685,
"placementX" : 19.0,
- "placementY" : 335.95,
+ "placementY" : 329.685,
"placementWidth" : 80.914,
"placementHeight" : 12.19,
"startPage" : 0,
@@ -1184,23 +1214,23 @@
"left" : 0.0
}
}, {
- "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]/Date[1]",
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]/Date[1]",
"entityName" : "Date",
"entityKind" : "SectionNode",
- "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]",
"childIndex" : 1,
"depth" : 4,
"layer" : 4,
"computedX" : 409.259,
- "computedY" : 324.82,
+ "computedY" : 330.215,
"placementX" : 409.259,
- "placementY" : 324.82,
+ "placementY" : 330.215,
"placementWidth" : 172.017,
- "placementHeight" : 23.32,
+ "placementHeight" : 11.66,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 172.017,
- "contentHeight" : 23.32,
+ "contentHeight" : 11.66,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -1214,23 +1244,23 @@
"left" : 0.0
}
}, {
- "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]/Date[1]/ParagraphNode[0]",
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]/Date[1]/ParagraphNode[0]",
"entityName" : null,
"entityKind" : "ParagraphNode",
- "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[2]/Date[1]",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/BoxedSectionsEntryHeader[3]/Date[1]",
"childIndex" : 0,
"depth" : 5,
"layer" : 5,
"computedX" : 409.259,
- "computedY" : 324.82,
+ "computedY" : 330.215,
"placementX" : 409.259,
- "placementY" : 324.82,
+ "placementY" : 330.215,
"placementWidth" : 172.017,
- "placementHeight" : 23.32,
+ "placementHeight" : 11.66,
"startPage" : 0,
"endPage" : 0,
"contentWidth" : 172.017,
- "contentHeight" : 23.32,
+ "contentHeight" : 11.66,
"margin" : {
"top" : 0.0,
"right" : 0.0,
@@ -1244,17 +1274,17 @@
"left" : 0.0
}
}, {
- "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/ParagraphNode[3]",
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/ParagraphNode[4]",
"entityName" : null,
"entityKind" : "ParagraphNode",
"parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]",
- "childIndex" : 3,
+ "childIndex" : 4,
"depth" : 3,
"layer" : 3,
"computedX" : 19.0,
- "computedY" : 309.69,
+ "computedY" : 314.555,
"placementX" : 19.0,
- "placementY" : 309.69,
+ "placementY" : 314.555,
"placementWidth" : 56.448,
"placementHeight" : 11.13,
"startPage" : 0,
@@ -1273,6 +1303,36 @@
"bottom" : 0.0,
"left" : 0.0
}
+ }, {
+ "path" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]/ParagraphNode[5]",
+ "entityName" : null,
+ "entityKind" : "ParagraphNode",
+ "parentPath" : "BoxedSectionsRoot[0]/BoxedSectionsBody_4[11]",
+ "childIndex" : 5,
+ "depth" : 3,
+ "layer" : 3,
+ "computedX" : 19.0,
+ "computedY" : 297.16,
+ "placementX" : 19.0,
+ "placementY" : 297.16,
+ "placementWidth" : 180.05,
+ "placementHeight" : 11.395,
+ "startPage" : 0,
+ "endPage" : 0,
+ "contentWidth" : 180.05,
+ "contentHeight" : 11.395,
+ "margin" : {
+ "top" : 2.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ },
+ "padding" : {
+ "top" : 0.0,
+ "right" : 0.0,
+ "bottom" : 0.0,
+ "left" : 0.0
+ }
}, {
"path" : "BoxedSectionsRoot[0]/BoxedSectionsBanner_5[12]",
"entityName" : "BoxedSectionsBanner_5",
@@ -1282,9 +1342,9 @@
"depth" : 2,
"layer" : 2,
"computedX" : 15.0,
- "computedY" : 275.97,
+ "computedY" : 263.44,
"placementX" : 15.0,
- "placementY" : 275.97,
+ "placementY" : 263.44,
"placementWidth" : 570.276,
"placementHeight" : 22.72,
"startPage" : 0,
@@ -1312,9 +1372,9 @@
"depth" : 3,
"layer" : 3,
"computedX" : 20.0,
- "computedY" : 280.97,
+ "computedY" : 268.44,
"placementX" : 20.0,
- "placementY" : 280.97,
+ "placementY" : 268.44,
"placementWidth" : 560.276,
"placementHeight" : 12.72,
"startPage" : 0,
@@ -1342,14 +1402,14 @@
"depth" : 2,
"layer" : 2,
"computedX" : 15.0,
- "computedY" : 234.18,
+ "computedY" : 221.65,
"placementX" : 15.0,
- "placementY" : 234.18,
- "placementWidth" : 295.085,
+ "placementY" : 221.65,
+ "placementWidth" : 297.76,
"placementHeight" : 34.79,
"startPage" : 0,
"endPage" : 0,
- "contentWidth" : 295.085,
+ "contentWidth" : 297.76,
"contentHeight" : 34.79,
"margin" : {
"top" : 0.0,
@@ -1372,14 +1432,14 @@
"depth" : 3,
"layer" : 3,
"computedX" : 19.0,
- "computedY" : 251.575,
+ "computedY" : 239.045,
"placementX" : 19.0,
- "placementY" : 251.575,
- "placementWidth" : 287.085,
+ "placementY" : 239.045,
+ "placementWidth" : 289.76,
"placementHeight" : 11.395,
"startPage" : 0,
"endPage" : 0,
- "contentWidth" : 287.085,
+ "contentWidth" : 289.76,
"contentHeight" : 11.395,
"margin" : {
"top" : 2.0,
@@ -1402,14 +1462,14 @@
"depth" : 3,
"layer" : 3,
"computedX" : 19.0,
- "computedY" : 234.18,
+ "computedY" : 221.65,
"placementX" : 19.0,
- "placementY" : 234.18,
- "placementWidth" : 273.962,
+ "placementY" : 221.65,
+ "placementWidth" : 276.499,
"placementHeight" : 11.395,
"startPage" : 0,
"endPage" : 0,
- "contentWidth" : 273.962,
+ "contentWidth" : 276.499,
"contentHeight" : 11.395,
"margin" : {
"top" : 2.0,
diff --git a/src/test/resources/visual-baselines/cv-v2/boxed_sections-page-0.png b/src/test/resources/visual-baselines/cv-v2/boxed_sections-page-0.png
index 6722c836..c3651be8 100644
Binary files a/src/test/resources/visual-baselines/cv-v2/boxed_sections-page-0.png and b/src/test/resources/visual-baselines/cv-v2/boxed_sections-page-0.png differ