From 352f5a0e9665b0e1470f8fa6e1567d3890717dc1 Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Wed, 27 May 2026 16:11:02 +0100 Subject: [PATCH] feat(cv-v2): migrate Monogram Sidebar preset + add multi-rect pageBackgrounds engine API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New v2 preset port of the legacy MonogramSidebarCvTemplateComposer: pale teal sidebar with a Crimson Text monogram badge, centered icon contact stack, education + expertise modules; right column carries the spaced-caps name, professional summary, experience, projects, and additional info. Engine — new multi-rect page-background API (additive, backward-compat): - PageBackgroundFill record with factory helpers (fullPage, leftColumn, rightColumn, column) defined as ratios of the canvas page size so the same fill scales across page formats. - DocumentSession.pageBackgrounds(List) + parallel GraphCompose.DocumentBuilder.pageBackgrounds(List) builder method. - DocumentPageBackgrounds.apply now emits one fragment per fill per page, painted at z=0 in list order. The legacy single-color pageBackground(color) keeps working unchanged via delegation to a one-fill list. Theme — additive CvPalette extension: - New 5th field mainFill (defaults to WHITE) carrying the right-column background. 4-arg legacy constructor preserved so all 12 existing palette factories compile and behave identically. Preset highlights: - Two-column chrome painted via pageBackgrounds, so overflow content on page 2+ inherits the same visual structure with no preset-side filler logic. - Options exposes sidebarFillColor + mainFillColor for runtime overrides without forking the theme. - Reuses shared ProjectRenderer, LabelValueRenderer, SectionLookup, MarkdownInline, TextOrnaments.spacedUpper — no preset-local helpers that duplicate existing components. Tests: - 5 new direct tests for the multi-rect API (PageBackgroundTest): multi-fill emission, fullPage parity, empty-list clear, ratio validation, factory helpers. - New MonogramSidebarSmokeTest assertion verifying the two-column page chrome is emitted on every rendered page. - Visual parity baseline at src/test/resources/visual-baselines/cv-v2-layered/monogram_sidebar-page-0.png. - Full canonical suite: 958/958 passing. Refs: ADR-aligned (additive engine extension via new public record PageBackgroundFill; no engine internals leak to the public API). --- .../cv/v2/CvMonogramSidebarExample.java | 49 + .../java/com/demcha/compose/GraphCompose.java | 26 +- .../document/api/DocumentPageBackgrounds.java | 67 +- .../compose/document/api/DocumentSession.java | 32 +- .../document/api/PageBackgroundFill.java | 87 ++ .../cv/v2/presets/MonogramSidebar.java | 880 ++++++++++++++++++ .../templates/cv/v2/theme/CvPalette.java | 49 +- .../templates/cv/v2/theme/CvSpacing.java | 25 + .../templates/cv/v2/theme/CvTheme.java | 17 + .../templates/cv/v2/theme/CvTypography.java | 21 + .../cv/monogram-sidebar/icons/email.png | Bin 1201 -> 879 bytes .../cv/monogram-sidebar/icons/github.png | Bin 7072 -> 5674 bytes .../cv/monogram-sidebar/icons/linkedin.png | Bin 1270 -> 1318 bytes .../cv/monogram-sidebar/icons/location.png | Bin 797 -> 591 bytes .../cv/monogram-sidebar/icons/phone.png | Bin 1086 -> 945 bytes .../document/api/PageBackgroundTest.java | 119 +++ .../cv/v2/presets/CvV2VisualParityTest.java | 5 +- .../v2/presets/MonogramSidebarSmokeTest.java | 127 +++ .../cv-v2-layered/monogram_sidebar-page-0.png | Bin 0 -> 92428 bytes 19 files changed, 1465 insertions(+), 39 deletions(-) create mode 100644 examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMonogramSidebarExample.java create mode 100644 src/main/java/com/demcha/compose/document/api/PageBackgroundFill.java create mode 100644 src/main/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebar.java create mode 100644 src/test/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebarSmokeTest.java create mode 100644 src/test/resources/visual-baselines/cv-v2-layered/monogram_sidebar-page-0.png diff --git a/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMonogramSidebarExample.java b/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMonogramSidebarExample.java new file mode 100644 index 00000000..a50ea1f5 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvMonogramSidebarExample.java @@ -0,0 +1,49 @@ +package com.demcha.examples.templates.cv.v2; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentPageSize; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.v2.data.CvDocument; +import com.demcha.compose.document.templates.cv.v2.presets.MonogramSidebar; +import com.demcha.examples.support.ExampleDataFactory; +import com.demcha.examples.support.ExampleOutputPaths; + +import java.nio.file.Path; + +/** + * Renders the v2 Monogram Sidebar CV preset against the shared + * grouped skills sample data — pale teal-grey sidebar with a dark + * monogram ring badge, centered icon-driven contact stack, education + * and expertise blocks, plus a two-line spaced-caps headline and the + * main career narrative on the right. + * + *

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

+ */ +public final class CvMonogramSidebarExample { + + private CvMonogramSidebarExample() { + } + + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare( + "templates/cv", "cv-monogram-sidebar-v2.pdf"); + CvDocument doc = ExampleDataFactory.sampleCvDocumentV2(); + DocumentTemplate template = MonogramSidebar.create(); + + float m = (float) MonogramSidebar.RECOMMENDED_MARGIN; + try (DocumentSession document = GraphCompose.document(outputFile) + .pageSize(DocumentPageSize.A4) + .margin(m, m, m, m) + .create()) { + template.compose(document, doc); + document.buildPdf(); + } + return outputFile; + } + + public static void main(String[] args) throws Exception { + System.out.println("Generated: " + generate()); + } +} diff --git a/src/main/java/com/demcha/compose/GraphCompose.java b/src/main/java/com/demcha/compose/GraphCompose.java index 5f33b5c5..eaa03d13 100644 --- a/src/main/java/com/demcha/compose/GraphCompose.java +++ b/src/main/java/com/demcha/compose/GraphCompose.java @@ -135,6 +135,7 @@ public static final class DocumentBuilder { private boolean markdown = true; private boolean guideLines; private com.demcha.compose.document.style.DocumentColor pageBackground; + private java.util.List pageBackgrounds; private final List customFontFamilies = new ArrayList<>(); private DocumentBuilder(Path outputFile) { @@ -243,6 +244,22 @@ public DocumentBuilder pageBackground(java.awt.Color color) { : com.demcha.compose.document.style.DocumentColor.of(color)); } + /** + * Configures one or more rectangular page background fills applied + * behind every fragment on every page. See + * {@link com.demcha.compose.document.api.PageBackgroundFill} and + * {@link com.demcha.compose.document.api.DocumentSession#pageBackgrounds} + * for the full semantics. + * + * @param fills ordered fills, or {@code null}/empty to clear + * @return this builder + */ + public DocumentBuilder pageBackgrounds( + java.util.List fills) { + this.pageBackgrounds = fills; + return this; + } + /** * Registers a document-local font family available to text measurement and * PDF rendering. @@ -363,7 +380,14 @@ public DocumentSession create() { List.copyOf(customFontFamilies), markdown, guideLines); - if (pageBackground != null) { + if (pageBackgrounds != null) { + // Explicit pageBackgrounds() call wins — even an empty + // list is an intentional clear that should override any + // earlier pageBackground(color) on the same builder. + if (!pageBackgrounds.isEmpty()) { + session.pageBackgrounds(pageBackgrounds); + } + } else if (pageBackground != null) { session.pageBackground(pageBackground); } return session; diff --git a/src/main/java/com/demcha/compose/document/api/DocumentPageBackgrounds.java b/src/main/java/com/demcha/compose/document/api/DocumentPageBackgrounds.java index 5ec4fbc4..5c964690 100644 --- a/src/main/java/com/demcha/compose/document/api/DocumentPageBackgrounds.java +++ b/src/main/java/com/demcha/compose/document/api/DocumentPageBackgrounds.java @@ -3,53 +3,64 @@ import com.demcha.compose.document.layout.LayoutGraph; import com.demcha.compose.document.layout.PlacedFragment; import com.demcha.compose.document.layout.payloads.ShapeFragmentPayload; -import com.demcha.compose.document.style.DocumentColor; import java.util.ArrayList; import java.util.List; /** - * Splices a session-wide page background fill into a compiled - * {@link LayoutGraph}. Extracted from {@link DocumentSession} as part - * of the Phase E.3 slim. + * Splices session-wide page background fills into a compiled + * {@link LayoutGraph}. Supports any number of partial-page rectangular + * fills per page (multi-column backgrounds, accent stripes, etc.). * - *

Pages get one extra {@link PlacedFragment} at the bottom of their - * z-order so every other fragment paints on top of the background fill. - * If the session has no page background or the layout has no pages, - * the original graph is returned unchanged.

+ *

Background fragments are placed at the very bottom of the page + * z-order (z=0), so every other fragment paints on top of them. Within + * a single page, fills are emitted in list order — later entries paint + * over earlier entries where they overlap. If the layout has no pages + * or no fills were configured, the original graph is returned + * unchanged.

*/ final class DocumentPageBackgrounds { private DocumentPageBackgrounds() { } /** - * Returns a copy of {@code base} with a page-background fragment - * spliced into every page, or {@code base} unchanged when there is - * nothing to do. + * Multi-rect form. Emits one fragment per fill per page, with + * coordinates computed from the fill's ratios and the canvas size. * - * @param base freshly compiled layout graph - * @param color session-wide background color, or {@code null} + * @param base freshly compiled layout graph + * @param fills ordered list of page-background fills (each painted + * on every page); {@code null}/empty leaves {@code base} + * unchanged * @return a layout graph with background fragments, or {@code base} */ - static LayoutGraph apply(LayoutGraph base, DocumentColor color) { - if (color == null || base.totalPages() == 0) { + static LayoutGraph apply(LayoutGraph base, List fills) { + if (fills == null || fills.isEmpty() || base.totalPages() == 0) { return base; } - List combined = new ArrayList<>(base.fragments().size() + base.totalPages()); + double pageWidth = base.canvas().width(); + double pageHeight = base.canvas().height(); + int extra = base.totalPages() * fills.size(); + List combined = + new ArrayList<>(base.fragments().size() + extra); for (int page = 0; page < base.totalPages(); page++) { - combined.add(new PlacedFragment( - "@page-background[" + page + "]", - 0, - page, - 0.0, - 0.0, - base.canvas().width(), - base.canvas().height(), - com.demcha.compose.engine.components.style.Margin.zero(), - com.demcha.compose.engine.components.style.Padding.zero(), - new ShapeFragmentPayload(color.color(), null, 0.0, null, null, null))); + for (int i = 0; i < fills.size(); i++) { + PageBackgroundFill fill = fills.get(i); + combined.add(new PlacedFragment( + "@page-background[" + page + "][" + i + "]", + 0, + page, + fill.xRatio() * pageWidth, + fill.yRatio() * pageHeight, + fill.widthRatio() * pageWidth, + fill.heightRatio() * pageHeight, + com.demcha.compose.engine.components.style.Margin.zero(), + com.demcha.compose.engine.components.style.Padding.zero(), + new ShapeFragmentPayload(fill.color().color(), + null, 0.0, null, null, null))); + } } combined.addAll(base.fragments()); - return new LayoutGraph(base.canvas(), base.totalPages(), base.nodes(), combined); + return new LayoutGraph(base.canvas(), base.totalPages(), + base.nodes(), combined); } } diff --git a/src/main/java/com/demcha/compose/document/api/DocumentSession.java b/src/main/java/com/demcha/compose/document/api/DocumentSession.java index 798b7ba4..beb33139 100644 --- a/src/main/java/com/demcha/compose/document/api/DocumentSession.java +++ b/src/main/java/com/demcha/compose/document/api/DocumentSession.java @@ -75,7 +75,7 @@ public final class DocumentSession implements AutoCloseable { private LayoutCanvas canvas; private boolean markdown; private boolean guideLines; - private DocumentColor pageBackground; + private List pageBackgrounds = List.of(); private final DocumentChromeOptions chromeOptions = new DocumentChromeOptions(); @@ -321,7 +321,9 @@ public DocumentSession guideLines(boolean enabled) { */ public DocumentSession pageBackground(DocumentColor color) { ensureOpen(); - this.pageBackground = color; + this.pageBackgrounds = color == null + ? List.of() + : List.of(PageBackgroundFill.fullPage(color)); invalidate(); return this; } @@ -336,6 +338,30 @@ public DocumentSession pageBackground(Color color) { return pageBackground(color == null ? null : DocumentColor.of(color)); } + /** + * Configures one or more rectangular background fills applied behind + * every fragment on every page. Each fill is defined as a fraction of + * the canvas (see {@link PageBackgroundFill}), so the layout works + * across page sizes. Use this for multi-column page chrome — a pale + * sidebar column plus a white main column, accent stripes, etc. — + * that should repeat automatically when content paginates onto a new + * page. + * + *

Pass {@code null} or an empty list to clear. Fills paint in list + * order; later entries paint on top of earlier ones where they + * overlap.

+ * + * @param fills ordered list of fills, or {@code null}/empty to clear + * @return this session + * @throws IllegalStateException if this session has already been closed + */ + public DocumentSession pageBackgrounds(List fills) { + ensureOpen(); + this.pageBackgrounds = fills == null ? List.of() : List.copyOf(fills); + invalidate(); + return this; + } + /** * Returns a fluent facade for chrome configuration (metadata, * watermark, protection, header, footer). The facade is a thin @@ -617,7 +643,7 @@ public LayoutGraph layoutGraph() { LIFECYCLE_LOG.debug("document.layout.start sessionId={} revision={} roots={}", sessionId, revision, roots.size()); try { DocumentLayoutPassContext context = new DocumentLayoutPassContext(registry, canvas, measurementResources.fontLibrary(), measurementResources.textMeasurementSystem(), markdown); - LayoutGraph computed = layoutCache.layout(() -> DocumentPageBackgrounds.apply(compiler.compile(documentGraph(), context, context), pageBackground)); + LayoutGraph computed = layoutCache.layout(() -> DocumentPageBackgrounds.apply(compiler.compile(documentGraph(), context, context), pageBackgrounds)); LIFECYCLE_LOG.debug("document.layout.end sessionId={} revision={} roots={} pages={} nodes={} fragments={} durationMs={}", sessionId, revision, roots.size(), computed.totalPages(), computed.nodes().size(), computed.fragments().size(), elapsedMillis(startNanos)); return computed; } catch (RuntimeException ex) { diff --git a/src/main/java/com/demcha/compose/document/api/PageBackgroundFill.java b/src/main/java/com/demcha/compose/document/api/PageBackgroundFill.java new file mode 100644 index 00000000..3d7ae990 --- /dev/null +++ b/src/main/java/com/demcha/compose/document/api/PageBackgroundFill.java @@ -0,0 +1,87 @@ +package com.demcha.compose.document.api; + +import com.demcha.compose.document.style.DocumentColor; + +import java.util.Objects; + +/** + * Per-page rectangular background fill, defined as ratios of the + * canvas page size so the same fill scales correctly to any page + * format. Used by {@link DocumentSession#pageBackgrounds(java.util.List)} + * to paint multi-column or partial-page backgrounds that repeat on + * every page automatically. + * + *

Use the factory methods for the common cases:

+ *
    + *
  • {@link #fullPage(DocumentColor)} — entire page (same effect as + * the legacy single-color {@link DocumentSession#pageBackground}).
  • + *
  • {@link #leftColumn(double, DocumentColor)} — full-height column + * aligned to the left edge.
  • + *
  • {@link #rightColumn(double, DocumentColor)} — full-height + * column aligned to the right edge.
  • + *
  • {@link #column(double, double, DocumentColor)} — arbitrary + * horizontal slice spanning the full page height.
  • + *
+ * + *

Fills supplied to a session are painted at z=0 (below every other + * fragment) in list order, so later entries paint on top of earlier + * entries when they overlap. This is the natural way to layer a + * narrow accent column over a full-page tint.

+ * + * @param xRatio 0.0 = left edge, 1.0 = right edge + * @param yRatio 0.0 = top edge, 1.0 = bottom edge + * @param widthRatio width as a fraction of the canvas width (0..1] + * @param heightRatio height as a fraction of the canvas height (0..1] + * @param color fill color (required) + */ +public record PageBackgroundFill(double xRatio, + double yRatio, + double widthRatio, + double heightRatio, + DocumentColor color) { + + public PageBackgroundFill { + Objects.requireNonNull(color, "color"); + if (xRatio < 0.0 || xRatio > 1.0) { + throw new IllegalArgumentException( + "xRatio must be in [0,1] but was " + xRatio); + } + if (yRatio < 0.0 || yRatio > 1.0) { + throw new IllegalArgumentException( + "yRatio must be in [0,1] but was " + yRatio); + } + if (widthRatio <= 0.0 || widthRatio > 1.0) { + throw new IllegalArgumentException( + "widthRatio must be in (0,1] but was " + widthRatio); + } + if (heightRatio <= 0.0 || heightRatio > 1.0) { + throw new IllegalArgumentException( + "heightRatio must be in (0,1] but was " + heightRatio); + } + } + + /** Full-page fill, equivalent to the legacy single-color page background. */ + public static PageBackgroundFill fullPage(DocumentColor color) { + return new PageBackgroundFill(0.0, 0.0, 1.0, 1.0, color); + } + + /** Full-height column at the left page edge, with width = ratio of page width. */ + public static PageBackgroundFill leftColumn(double widthRatio, + DocumentColor color) { + return new PageBackgroundFill(0.0, 0.0, widthRatio, 1.0, color); + } + + /** Full-height column at the right page edge, with width = ratio of page width. */ + public static PageBackgroundFill rightColumn(double widthRatio, + DocumentColor color) { + return new PageBackgroundFill(1.0 - widthRatio, 0.0, + widthRatio, 1.0, color); + } + + /** Full-height column at an arbitrary horizontal offset. */ + public static PageBackgroundFill column(double xRatio, + double widthRatio, + DocumentColor color) { + return new PageBackgroundFill(xRatio, 0.0, widthRatio, 1.0, color); + } +} diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebar.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebar.java new file mode 100644 index 00000000..f72b70c0 --- /dev/null +++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebar.java @@ -0,0 +1,880 @@ +package com.demcha.compose.document.templates.cv.v2.presets; + +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.api.PageBackgroundFill; +import com.demcha.compose.document.dsl.EllipseBuilder; +import com.demcha.compose.document.dsl.LayerStackBuilder; +import com.demcha.compose.document.dsl.ParagraphBuilder; +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.image.DocumentImageData; +import com.demcha.compose.document.node.DocumentLinkOptions; +import com.demcha.compose.document.node.InlineImageAlignment; +import com.demcha.compose.document.node.LayerAlign; +import com.demcha.compose.document.node.LayerStackNode; +import com.demcha.compose.document.node.SpacerNode; +import com.demcha.compose.document.node.TextAlign; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentStroke; +import com.demcha.compose.document.style.DocumentTextDecoration; +import com.demcha.compose.document.style.DocumentTextStyle; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.v2.components.CvTextStyles; +import com.demcha.compose.document.templates.cv.v2.components.LabelValueRenderer; +import com.demcha.compose.document.templates.cv.v2.components.MarkdownInline; +import com.demcha.compose.document.templates.cv.v2.components.ProjectRenderer; +import com.demcha.compose.document.templates.cv.v2.components.SectionLookup; +import com.demcha.compose.document.templates.cv.v2.components.TextOrnaments; +import com.demcha.compose.document.templates.cv.v2.data.CvDocument; +import com.demcha.compose.document.templates.cv.v2.data.CvEntry; +import com.demcha.compose.document.templates.cv.v2.data.CvIdentity; +import com.demcha.compose.document.templates.cv.v2.data.CvLink; +import com.demcha.compose.document.templates.cv.v2.data.CvName; +import com.demcha.compose.document.templates.cv.v2.data.CvRow; +import com.demcha.compose.document.templates.cv.v2.data.CvSection; +import com.demcha.compose.document.templates.cv.v2.data.EntriesSection; +import com.demcha.compose.document.templates.cv.v2.data.ParagraphSection; +import com.demcha.compose.document.templates.cv.v2.data.RowsSection; +import com.demcha.compose.document.templates.cv.v2.data.SkillGroup; +import com.demcha.compose.document.templates.cv.v2.data.SkillsSection; +import com.demcha.compose.document.templates.cv.v2.data.Slot; +import com.demcha.compose.document.templates.cv.v2.theme.CvTheme; +import com.demcha.compose.font.FontName; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * v2 port of the legacy "Monogram Sidebar" CV preset. + * + *

Two-column resume with a pale teal-grey sidebar carrying a + * monogram badge (initials inside a dark ring), centered contact + * icons, education and expertise blocks; the right column carries a + * two-line letter-spaced headline plus the main career narrative. + * Visual signature ported from the v1 + * {@code MonogramSidebarCvTemplateComposer}: Crimson Text headline, + * PT Serif monogram, muted gold accent.

+ * + *

The page uses a zero margin so the pale sidebar fill bleeds to + * the page edge. Two-column page chrome is painted via + * {@link com.demcha.compose.document.api.DocumentSession#pageBackgrounds}, + * which repeats automatically on every page — when content overflows + * onto page 2 the sidebar tint and white main area continue without + * any preset-side filler logic. The preset draws its visual ornaments + * (monogram ring, section rules) inline because none of these visuals + * are shared with another v2 preset today.

+ */ +public final class MonogramSidebar { + + /** Stable template identifier. */ + public static final String ID = "monogram-sidebar"; + + /** Human-readable display name. */ + public static final String DISPLAY_NAME = "Monogram Sidebar"; + + /** Recommended page margin (in points) — 0 so the sidebar bleeds to the edge. */ + public static final double RECOMMENDED_MARGIN = 0.0; + + /** V1 default muted-gold accent — used for the subtitle, dates. */ + private static final DocumentColor DEFAULT_ACCENT = + DocumentColor.rgb(158, 146, 104); + + /** V1 default dark monogram ring + initials colour. */ + private static final DocumentColor DEFAULT_MONOGRAM_RING = + DocumentColor.rgb(54, 62, 74); + + /** V1 dark main-column rule colour (theme rule is sidebar-only). */ + private static final DocumentColor MAIN_RULE = + DocumentColor.rgb(72, 79, 84); + + /** PT Serif used only for the monogram initials inside the ring badge. */ + private static final FontName MONOGRAM_FONT = FontName.PT_SERIF; + + private static final double MONOGRAM_DIAMETER = 122; + private static final double SIDEBAR_RULE_WIDTH = 118; + private static final double CONTACT_ICON_SIZE = 22; + + /** Sidebar column width as a fraction of the page width. */ + private static final double SIDEBAR_WIDTH_RATIO = 0.33; + + private static final double MAIN_SECTION_RULE_WIDTH = 355.0; + + private static final int EDUCATION_LIMIT = 2; + private static final int SKILL_LIMIT = 7; + private static final int EXPERIENCE_LIMIT = 2; + private static final int PROJECT_LIMIT = 3; + private static final int ADDITIONAL_LIMIT = 3; + + private static final String CONTACT_ICON_ROOT = + "/templates/cv/monogram-sidebar/icons/"; + private static final Map CONTACT_ICON_CACHE = + new ConcurrentHashMap<>(); + + private static final List EDUCATION_KEYS = + List.of("education", "certifications"); + private static final List SKILL_KEYS = + List.of("skills", "technical skills", "expertise"); + private static final List SUMMARY_KEYS = + List.of("profile", "professional profile", "summary", + "professional summary"); + private static final List EXPERIENCE_KEYS = + List.of("experience", "employment", "professional experience", + "work"); + private static final List PROJECT_KEYS = + List.of("projects", "project"); + private static final List ADDITIONAL_KEYS = + List.of("additional information", "additional"); + + private MonogramSidebar() { + } + + /** + * Builds the preset with its Monogram Sidebar theme and default + * options (theme's banner fill for the sidebar, muted-gold accent, + * dark slate monogram ring). + */ + public static DocumentTemplate create() { + return create(CvTheme.monogramSidebar(), Options.defaults()); + } + + /** + * Builds the preset with a caller-supplied theme and default + * options. + */ + public static DocumentTemplate create(CvTheme theme) { + return create(theme, Options.defaults()); + } + + /** + * Builds the preset with explicit colour options. Use this to + * override the sidebar fill, accent colour, or monogram ring + * without forking the theme. + */ + public static DocumentTemplate create(CvTheme theme, + Options options) { + Objects.requireNonNull(theme, "theme"); + Objects.requireNonNull(options, "options"); + return new Template(theme, options); + } + + /** + * Monogram Sidebar customisation knobs. {@code null} fields fall + * back to the theme palette / V1 defaults documented on each + * accessor. + * + * @param sidebarFillColor sidebar (left column) background fill; + * {@code null} → {@code theme.palette().banner()} + * @param mainFillColor main (right column) background fill; + * {@code null} → {@code theme.palette().mainFill()} + * (defaults to {@link DocumentColor#WHITE}) + * @param accentColor muted-gold accent for subtitle, + * education date, experience date; + * {@code null} → V1 rgb(158,146,104) + * @param monogramRingColor ring stroke + initials colour; + * {@code null} → V1 rgb(54,62,74) + */ + public record Options(DocumentColor sidebarFillColor, + DocumentColor mainFillColor, + DocumentColor accentColor, + DocumentColor monogramRingColor) { + + public static Options defaults() { + return new Options(null, null, null, null); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private DocumentColor sidebarFillColor; + private DocumentColor mainFillColor; + private DocumentColor accentColor; + private DocumentColor monogramRingColor; + + private Builder() { + } + + public Builder sidebarFillColor(DocumentColor value) { + this.sidebarFillColor = value; + return this; + } + + public Builder mainFillColor(DocumentColor value) { + this.mainFillColor = value; + return this; + } + + public Builder accentColor(DocumentColor value) { + this.accentColor = value; + return this; + } + + public Builder monogramRingColor(DocumentColor value) { + this.monogramRingColor = value; + return this; + } + + public Options build() { + return new Options(sidebarFillColor, mainFillColor, + accentColor, monogramRingColor); + } + } + } + + private static final class Template implements DocumentTemplate { + + private final CvTheme theme; + private final DocumentColor sidebarFill; + private final DocumentColor mainFill; + private final DocumentColor accent; + private final DocumentColor monogramRing; + + Template(CvTheme theme, Options options) { + this.theme = theme; + this.sidebarFill = options.sidebarFillColor() != null + ? options.sidebarFillColor() + : theme.palette().banner(); + this.mainFill = options.mainFillColor() != null + ? options.mainFillColor() + : theme.palette().mainFill(); + this.accent = options.accentColor() != null + ? options.accentColor() + : DEFAULT_ACCENT; + this.monogramRing = options.monogramRingColor() != null + ? options.monogramRingColor() + : DEFAULT_MONOGRAM_RING; + } + + @Override + public String id() { + return ID; + } + + @Override + public String displayName() { + return DISPLAY_NAME; + } + + @Override + public void compose(DocumentSession document, CvDocument doc) { + Objects.requireNonNull(document, "document"); + Objects.requireNonNull(doc, "doc"); + + double pageInnerWidth = document.canvas().innerWidth(); + double sidebarOuterWidth = pageInnerWidth * 0.33; + double sidebarHorizontalPadding = 13.0 * 2.0; + double sidebarInnerWidth = Math.max(0.0, + sidebarOuterWidth - sidebarHorizontalPadding); + double mainOuterWidth = pageInnerWidth - sidebarOuterWidth; + // Main section has 20pt left + 18pt right padding (see addMain). + // Spacer width must be the content-area width so the inner + // content fills exactly the section's allocated outer width + // (= mainOuterWidth) — passing the outer width directly + // would overflow because outer = content + padding. + double mainContentWidth = Math.max(0.0, + mainOuterWidth - (20.0 + 18.0)); + List sections = doc.sectionsIn(Slot.MAIN); + + // Paint the two-column chrome via pageBackgrounds — the + // engine emits both fills on every page automatically, so + // overflow content on page 2+ keeps the same visual + // structure without any preset-side filler logic. + document.pageBackgrounds(List.of( + PageBackgroundFill.leftColumn(SIDEBAR_WIDTH_RATIO, + sidebarFill), + PageBackgroundFill.rightColumn(1.0 - SIDEBAR_WIDTH_RATIO, + mainFill))); + + document.dsl() + .pageFlow() + .name("CvV2MonogramSidebarRoot") + .spacing(theme.spacing().pageFlowSpacing()) + .padding(DocumentInsets.zero()) + .addRow("CvV2MonogramSidebarFrame", row -> row + .spacing(0) + .weights(SIDEBAR_WIDTH_RATIO, + 1.0 - SIDEBAR_WIDTH_RATIO) + .addSection("CvV2MonogramSidebarSidebar", + section -> addSidebar(section, doc, sections, + sidebarInnerWidth)) + .addSection("CvV2MonogramSidebarMain", + section -> addMain(section, doc.identity(), + sections, mainContentWidth))) + .build(); + } + + // -- Sidebar ------------------------------------------------------- + + private void addSidebar(SectionBuilder section, CvDocument doc, + List sections, double innerWidth) { + // Sidebar section deliberately has no fillColor — the + // pageBackgrounds emitted in compose() paint the pale fill + // edge-to-edge on every page, including continuation + // pages. Top padding establishes the breathing room above + // the monogram badge. + section.spacing(8) + .padding(new DocumentInsets(36, 13, 0, 13)); + + addMonogramBlock(section, initials(doc.identity().name()), + innerWidth); + + addSidebarHeader(section, "CONTACT", innerWidth); + addContactBlock(section, doc.identity()); + + CvSection education = SectionLookup.firstMatching(sections, + EDUCATION_KEYS); + if (hasContent(education)) { + addSidebarHeader(section, education.title(), innerWidth); + addEducationEntries(section, education); + } + + CvSection skills = SectionLookup.firstMatching(sections, SKILL_KEYS); + if (hasContent(skills)) { + addSidebarHeader(section, "EXPERTISE", innerWidth); + addSkillsList(section, skills); + } + } + + private void addMonogramBlock(SectionBuilder section, + String initialsText, double innerWidth) { + LayerStackNode badge = new LayerStackBuilder() + .name("CvV2MonogramSidebarBadge") + .back(new EllipseBuilder() + .name("CvV2MonogramSidebarRing") + .size(MONOGRAM_DIAMETER, MONOGRAM_DIAMETER) + .stroke(DocumentStroke.of(monogramRing, 1.25)) + .build()) + .layer(new ParagraphBuilder() + .name("CvV2MonogramSidebarInitials") + .text(initialsText) + .textStyle(CvTextStyles.of(MONOGRAM_FONT, 44.0, + DocumentTextDecoration.BOLD, monogramRing)) + .align(TextAlign.LEFT) + .build(), LayerAlign.CENTER) + .build(); + + section.addLayerStack(outer -> outer + .name("CvV2MonogramSidebarFrame") + .margin(DocumentInsets.bottom(42)) + .back(new SpacerNode( + "CvV2MonogramSidebarSpace", + Math.max(MONOGRAM_DIAMETER, innerWidth), + MONOGRAM_DIAMETER, + DocumentInsets.zero(), + DocumentInsets.zero())) + .layer(badge, LayerAlign.TOP_CENTER)); + } + + private void addSidebarHeader(SectionBuilder section, String title, + double innerWidth) { + if (title == null || title.isBlank()) { + return; + } + section.addParagraph(paragraph -> paragraph + .text(TextOrnaments.spacedUpper(title)) + .textStyle(sidebarHeaderStyle()) + .align(TextAlign.CENTER) + .lineSpacing(1.2) + .margin(DocumentInsets.top(6))); + double ruleWidth = Math.min(innerWidth, SIDEBAR_RULE_WIDTH); + double sideInset = Math.max(0.0, (innerWidth - ruleWidth) / 2.0); + section.addLine(line -> line + .horizontal(ruleWidth) + .color(theme.palette().rule()) + .thickness(0.45) + .margin(new DocumentInsets(1, sideInset, 2, sideInset))); + } + + private void addContactBlock(SectionBuilder section, CvIdentity identity) { + List items = contactItems(identity); + if (items.isEmpty()) { + return; + } + DocumentTextStyle textStyle = sidebarBodyStyle(); + for (ContactItem item : items) { + if (item.iconFile() != null) { + section.addParagraph(paragraph -> paragraph + .textStyle(textStyle) + .align(TextAlign.CENTER) + .margin(DocumentInsets.top(4)) + .rich(rich -> rich.image( + contactIcon(item.iconFile()), + CONTACT_ICON_SIZE, + CONTACT_ICON_SIZE, + InlineImageAlignment.CENTER, + 0.0, + item.linkOptions()))); + } + section.addParagraph(paragraph -> paragraph + .textStyle(textStyle) + .align(TextAlign.CENTER) + .lineSpacing(1.2) + .margin(DocumentInsets.zero()) + .link(item.linkOptions()) + .rich(rich -> { + if (item.linkOptions() != null) { + rich.link(item.text(), item.linkOptions()); + } else { + rich.style(item.text(), textStyle); + } + })); + } + } + + private void addEducationEntries(SectionBuilder section, + CvSection eduSection) { + if (!(eduSection instanceof EntriesSection entries)) { + return; + } + DocumentTextStyle headingStyle = sidebarEntryTitleStyle(); + DocumentTextStyle subStyle = sidebarEntrySubtitleStyle(); + DocumentTextStyle metaStyle = sidebarEntryDateStyle(); + + List list = entries.entries(); + for (int i = 0; i < Math.min(list.size(), EDUCATION_LIMIT); i++) { + CvEntry entry = list.get(i); + section.addParagraph(paragraph -> paragraph + .text(MarkdownInline.plainText(entry.title()) + .toUpperCase(Locale.ROOT)) + .textStyle(headingStyle) + .align(TextAlign.CENTER) + .lineSpacing(1.2) + .margin(DocumentInsets.top(6))); + if (!entry.subtitle().isBlank()) { + section.addParagraph(paragraph -> paragraph + .text(MarkdownInline.plainText(entry.subtitle())) + .textStyle(subStyle) + .align(TextAlign.CENTER) + .lineSpacing(1.2) + .margin(DocumentInsets.zero())); + } + if (!entry.date().isBlank()) { + section.addParagraph(paragraph -> paragraph + .text(MarkdownInline.plainText(entry.date())) + .textStyle(metaStyle) + .align(TextAlign.CENTER) + .lineSpacing(1.2) + .margin(DocumentInsets.zero())); + } + } + } + + private void addSkillsList(SectionBuilder section, CvSection skillSection) { + if (!(skillSection instanceof SkillsSection skills)) { + return; + } + DocumentTextStyle skillStyle = sidebarSkillStyle(); + List tokens = skillTokens(skills); + for (String token : tokens.stream().limit(SKILL_LIMIT).toList()) { + section.addParagraph(paragraph -> paragraph + .text(MarkdownInline.plainText(token)) + .textStyle(skillStyle) + .align(TextAlign.CENTER) + .lineSpacing(1.25) + .margin(DocumentInsets.top(1))); + } + } + + // -- Main column --------------------------------------------------- + + private void addMain(SectionBuilder section, CvIdentity identity, + List sections, double anchorWidth) { + // No fillColor — the page background defaults to white, so + // the right column is naturally white from top to bottom. + section.spacing(5) + .padding(new DocumentInsets(38, 20, 24, 18)); + + addNameBlock(section, identity); + + CvSection profile = SectionLookup.firstMatching(sections, + SUMMARY_KEYS); + if (hasContent(profile)) { + addMainSectionHeader(section, + profile.title().isBlank() + ? "Professional Profile" + : profile.title()); + addProfileBody(section, profile); + } + + CvSection experience = SectionLookup.firstMatching(sections, + EXPERIENCE_KEYS); + if (hasContent(experience)) { + addMainSectionHeader(section, + experience.title().isBlank() + ? "Experience" + : experience.title()); + addExperienceEntries(section, experience); + } + + CvSection projects = SectionLookup.firstMatching(sections, + PROJECT_KEYS); + if (hasContent(projects)) { + addMainSectionHeader(section, + projects.title().isBlank() + ? "Projects" + : projects.title()); + addProjectsList(section, projects); + } + + CvSection additional = SectionLookup.firstMatching(sections, + ADDITIONAL_KEYS); + if (hasContent(additional)) { + addMainSectionHeader(section, + additional.title().isBlank() + ? "Additional Information" + : additional.title()); + addAdditionalList(section, additional); + } + } + + private void addProjectsList(SectionBuilder section, + CvSection projectsSection) { + if (!(projectsSection instanceof RowsSection rows)) { + return; + } + DocumentTextStyle titleStyle = mainEntryTitleStyle(); + DocumentTextStyle stackStyle = mainEntryDateStyle(); + DocumentTextStyle bodyStyle = mainBodyStyle(); + List list = rows.rows(); + for (int i = 0; i < Math.min(list.size(), PROJECT_LIMIT); i++) { + ProjectRenderer.inline(section, list.get(i), + titleStyle, stackStyle, bodyStyle, + theme.typography().bodyLineSpacing(), + DocumentInsets.top(4)); + } + } + + private void addAdditionalList(SectionBuilder section, + CvSection addSection) { + if (!(addSection instanceof RowsSection rows)) { + return; + } + DocumentTextStyle labelStyle = mainEntryTitleStyle(); + DocumentTextStyle valueStyle = mainBodyStyle(); + List list = rows.rows(); + for (int i = 0; i < Math.min(list.size(), ADDITIONAL_LIMIT); i++) { + CvRow row = list.get(i); + LabelValueRenderer.render(section, row.label(), row.body(), + labelStyle, valueStyle, + theme.typography().bodyLineSpacing(), + DocumentInsets.top(3)); + } + } + + private void addNameBlock(SectionBuilder section, CvIdentity identity) { + CvName name = identity == null ? CvName.of("", "") : identity.name(); + List parts = new ArrayList<>(); + if (!name.first().isBlank()) { + parts.add(name.first()); + } + if (!name.last().isBlank()) { + parts.add(name.last()); + } + if (parts.isEmpty()) { + parts.add(""); + } + String jobTitle = identity == null ? "" : identity.jobTitle(); + String subline = jobTitle == null || jobTitle.isBlank() + ? "Your Professional Title" + : jobTitle; + DocumentTextStyle nameStyle = nameStyle(); + DocumentTextStyle titleStyle = subtitleStyle(); + + for (int index = 0; index < parts.size(); index++) { + String part = parts.get(index); + DocumentInsets margin = index == parts.size() - 1 + ? DocumentInsets.zero() + : DocumentInsets.bottom(6); + section.addParagraph(paragraph -> paragraph + .text(TextOrnaments.spacedUpper(part)) + .textStyle(nameStyle) + .align(TextAlign.CENTER) + .lineSpacing(1.0) + .margin(margin)); + } + section.addParagraph(paragraph -> paragraph + .text(TextOrnaments.spacedUpper(subline)) + .textStyle(titleStyle) + .align(TextAlign.CENTER) + .margin(new DocumentInsets(12, 0, 22, 0))); + } + + private void addMainSectionHeader(SectionBuilder section, String title) { + if (title == null || title.isBlank()) { + return; + } + section.addParagraph(paragraph -> paragraph + .text(TextOrnaments.spacedUpper(title)) + .textStyle(mainHeaderStyle()) + .align(TextAlign.LEFT) + .margin(DocumentInsets.top(6))); + section.addLine(line -> line + .horizontal(MAIN_SECTION_RULE_WIDTH) + .color(MAIN_RULE) + .thickness(theme.spacing().accentRuleWidth()) + .margin(new DocumentInsets(1, 0, 4, 0))); + } + + private void addProfileBody(SectionBuilder section, + CvSection profileSection) { + if (!(profileSection instanceof ParagraphSection paragraphSection)) { + return; + } + DocumentTextStyle base = mainBodyStyle(); + String body = paragraphSection.body(); + if (body == null || body.isBlank()) { + return; + } + section.addParagraph(paragraph -> paragraph + .textStyle(base) + .lineSpacing(theme.typography().bodyLineSpacing()) + .align(TextAlign.LEFT) + .margin(new DocumentInsets(4, 0, 12, 0)) + .rich(rich -> MarkdownInline.appendTrimmed(rich, body, base))); + } + + private void addExperienceEntries(SectionBuilder section, + CvSection expSection) { + if (!(expSection instanceof EntriesSection entries)) { + return; + } + DocumentTextStyle positionStyle = mainEntryTitleStyle(); + DocumentTextStyle dateStyle = mainEntryDateStyle(); + DocumentTextStyle bodyStyle = mainBodyStyle(); + + List list = entries.entries(); + for (int i = 0; i < Math.min(list.size(), EXPERIENCE_LIMIT); i++) { + CvEntry entry = list.get(i); + section.addParagraph(paragraph -> paragraph + .text(MarkdownInline.plainText(entry.title()) + .toUpperCase(Locale.ROOT)) + .textStyle(positionStyle) + .align(TextAlign.LEFT) + .lineSpacing(1.15) + .margin(DocumentInsets.top(5))); + if (!entry.date().isBlank()) { + section.addParagraph(paragraph -> paragraph + .text(TextOrnaments.spacedUpper( + MarkdownInline.plainText(entry.date()))) + .textStyle(dateStyle) + .align(TextAlign.LEFT) + .margin(DocumentInsets.zero())); + } + if (!entry.body().isBlank()) { + String description = entry.body(); + section.addParagraph(paragraph -> paragraph + .textStyle(bodyStyle) + .lineSpacing(theme.typography().bodyLineSpacing()) + .align(TextAlign.LEFT) + .margin(DocumentInsets.top(1)) + .rich(rich -> MarkdownInline.appendTrimmed(rich, + description, bodyStyle))); + } + } + } + + // -- Style factories ---------------------------------------------- + + private DocumentTextStyle nameStyle() { + return CvTextStyles.of(theme.typography().headlineFont(), + theme.typography().sizeHeadline(), + DocumentTextDecoration.DEFAULT, + theme.palette().ink()); + } + + private DocumentTextStyle subtitleStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeContact(), + DocumentTextDecoration.BOLD, + accent); + } + + private DocumentTextStyle sidebarHeaderStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + 8.0, + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle sidebarBodyStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeContact(), + DocumentTextDecoration.DEFAULT, + theme.palette().muted()); + } + + private DocumentTextStyle sidebarEntryTitleStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + 7.6, + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle sidebarEntrySubtitleStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeContact(), + DocumentTextDecoration.DEFAULT, + theme.palette().ink()); + } + + private DocumentTextStyle sidebarEntryDateStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + 7.2, + DocumentTextDecoration.DEFAULT, + accent); + } + + private DocumentTextStyle sidebarSkillStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeContact(), + DocumentTextDecoration.DEFAULT, + theme.palette().muted()); + } + + private DocumentTextStyle mainHeaderStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeBanner(), + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle mainBodyStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeBody(), + DocumentTextDecoration.DEFAULT, + theme.palette().ink()); + } + + private DocumentTextStyle mainEntryTitleStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeEntryTitle(), + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle mainEntryDateStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeEntryDate(), + DocumentTextDecoration.BOLD, + accent); + } + } + + // -- Static helpers ---------------------------------------------------- + + private static boolean hasContent(CvSection section) { + return section != null && SectionLookup.hasContent(section); + } + + private static String initials(CvName name) { + if (name == null) { + return ""; + } + StringBuilder builder = new StringBuilder(); + appendInitial(builder, name.first()); + appendInitial(builder, name.last()); + return builder.toString(); + } + + private static void appendInitial(StringBuilder builder, String value) { + if (builder.length() >= 2 || value == null) { + return; + } + String trimmed = value.trim(); + if (!trimmed.isEmpty() && Character.isLetter(trimmed.charAt(0))) { + builder.append(Character.toUpperCase(trimmed.charAt(0))); + } + } + + private static List contactItems(CvIdentity identity) { + if (identity == null) { + return List.of(); + } + List items = new ArrayList<>(); + addContactItem(items, "phone.png", identity.contact().phone(), null); + String email = identity.contact().email(); + if (!email.isBlank()) { + addContactItem(items, "email.png", email, + new DocumentLinkOptions("mailto:" + email)); + } + addContactItem(items, "location.png", identity.contact().address(), + null); + for (CvLink link : identity.links()) { + String label = link.label(); + if (label.isBlank()) { + continue; + } + String url = link.url(); + addContactItem(items, pickIconFile(label), label, + url.isBlank() + ? null + : new DocumentLinkOptions(url.trim())); + } + return List.copyOf(items); + } + + private static void addContactItem(List items, + String iconFile, String text, + DocumentLinkOptions linkOptions) { + if (text != null && !text.isBlank()) { + items.add(new ContactItem(iconFile, text.trim(), linkOptions)); + } + } + + private static String pickIconFile(String label) { + String normalized = SectionLookup.normalize(label); + if (normalized.contains("github")) { + return "github.png"; + } + if (normalized.contains("linkedin")) { + return "linkedin.png"; + } + return "linkedin.png"; + } + + private static DocumentImageData contactIcon(String iconFile) { + return DocumentImageData.fromBytes( + CONTACT_ICON_CACHE.computeIfAbsent(CONTACT_ICON_ROOT + iconFile, + MonogramSidebar::readIconBytes)); + } + + private static byte[] readIconBytes(String resourcePath) { + try (InputStream input = MonogramSidebar.class + .getResourceAsStream(resourcePath)) { + if (input == null) { + throw new IllegalStateException( + "Missing monogram sidebar icon: " + resourcePath); + } + return input.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException( + "Failed to read monogram sidebar icon: " + resourcePath, e); + } + } + + private static List skillTokens(SkillsSection skills) { + List tokens = new ArrayList<>(); + for (SkillGroup group : skills.groups()) { + String inline = MarkdownInline.plainText(group.skillsInline()); + for (String token : inline.split(",")) { + String clean = token.trim(); + if (!clean.isBlank()) { + tokens.add(clean); + } + } + } + return tokens; + } + + private record ContactItem(String iconFile, String text, + DocumentLinkOptions linkOptions) { + } +} diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvPalette.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvPalette.java index d60c55f1..0161e10b 100644 --- a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvPalette.java +++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvPalette.java @@ -7,22 +7,43 @@ /** * Colour tokens for a {@link CvTheme}. * - * @param ink primary text colour — headlines, body, entry titles - * @param muted secondary text colour — italic subtitles (employer, - * institution) - * @param rule thin horizontal rules + the contact-line pipe glyph - * @param banner pale fill behind section title banners + * @param ink primary text colour — headlines, body, entry titles + * @param muted secondary text colour — italic subtitles (employer, + * institution) + * @param rule thin horizontal rules + the contact-line pipe glyph + * @param banner pale fill behind section title banners + * @param mainFill main content area background fill — used by presets + * with a two-column layout (sidebar + main) to paint + * the right column on every page. Defaults to + * {@link DocumentColor#WHITE} for palettes that do not + * need a custom main-area fill. */ public record CvPalette(DocumentColor ink, DocumentColor muted, DocumentColor rule, - DocumentColor banner) { + DocumentColor banner, + DocumentColor mainFill) { public CvPalette { Objects.requireNonNull(ink, "ink"); Objects.requireNonNull(muted, "muted"); Objects.requireNonNull(rule, "rule"); Objects.requireNonNull(banner, "banner"); + Objects.requireNonNull(mainFill, "mainFill"); + } + + /** + * Backward-compatible 4-arg constructor that defaults + * {@code mainFill} to {@link DocumentColor#WHITE}. Retained so + * factories defined before the {@code mainFill} token landed keep + * compiling and behaving identically (none of them rendered a + * coloured main area). + */ + public CvPalette(DocumentColor ink, + DocumentColor muted, + DocumentColor rule, + DocumentColor banner) { + this(ink, muted, rule, banner, DocumentColor.WHITE); } /** @@ -123,6 +144,22 @@ public static CvPalette editorialBlue() { DocumentColor.rgb(193, 201, 211)); } + /** + * Monogram Sidebar palette ported from the v1 + * {@code MonogramSidebarCvTemplateComposer}: navy-slate body ink, + * soft grey for sidebar metadata, mid-grey sidebar rule, and the + * pale teal-grey sidebar background fill. The dark monogram ring + * and muted-gold accent stay preset-local because no other v2 + * preset shares them today. + */ + public static CvPalette monogramSidebar() { + return new CvPalette( + DocumentColor.rgb(37, 45, 58), // ink — V1 INK + DocumentColor.rgb(112, 119, 125), // muted — V1 SOFT + DocumentColor.rgb(138, 146, 148), // rule — V1 SIDEBAR_RULE + DocumentColor.rgb(226, 235, 235)); // banner — V1 SIDEBAR_BG + } + /** * Engineering Resume palette ported from the v1 * {@code TechLeadCvTemplateComposer}: body-slate ink, muted slate diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvSpacing.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvSpacing.java index ca44a98e..05ec14d5 100644 --- a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvSpacing.java +++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvSpacing.java @@ -279,6 +279,31 @@ public static CvSpacing editorialBlue() { 3.0); // entrySeparation } + /** + * Spacing for the Monogram Sidebar preset: zero page-flow gap so + * the pale sidebar fill bleeds edge-to-edge against the + * RECOMMENDED_MARGIN=0 page bounds. Banner tokens are unused — + * the preset draws its visual chrome inline (monogram badge, rules, + * filler shape). + */ + public static CvSpacing monogramSidebar() { + return new CvSpacing( + 0, // pageFlowSpacing + 5, // sectionBodySpacing + DocumentInsets.zero(), // sectionBodyPadding + DocumentInsets.zero(), // headlinePadding + DocumentInsets.zero(), // contactPadding + 0.0, // bannerCornerRadius (unused) + 0.0, // bannerInnerPadding (unused) + DocumentInsets.zero(), // bannerMargin (unused) + 0.55, // accentRuleWidth (main section rule) + 1.0, // paragraphMarginTop + 8.0, // entryHeaderRowSpacing + 1.0, // entryTitleWeight + 0.45, // entryDateWeight + 2.0); // entrySeparation + } + /** * Spacing for the Engineering Resume preset: navy command header * + 2-column body (navy skill rail / white evidence cards), with diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTheme.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTheme.java index 81649c11..9009e57b 100644 --- a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTheme.java +++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTheme.java @@ -167,6 +167,23 @@ public static CvTheme editorialBlue() { CvDecoration.classic()); } + /** + * The "Monogram Sidebar" look — Crimson Text display + Lato body, + * pale teal-grey sidebar with a dark monogram ring badge holding + * the subject's initials, centered icon-driven contact stack, + * education and expertise blocks, plus a two-line spaced-caps + * headline and main career narrative on the right. Visual + * signature ported from the v1 + * {@code MonogramSidebarCvTemplateComposer}. + */ + public static CvTheme monogramSidebar() { + return new CvTheme( + CvPalette.monogramSidebar(), + CvTypography.monogramSidebar(), + CvSpacing.monogramSidebar(), + CvDecoration.classic()); + } + /** * The "Engineering Resume" look — Barlow display + Lato body, deep * navy command header with cyan-green contact links, dark navy diff --git a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTypography.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTypography.java index a35cc4ce..3b34292d 100644 --- a/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTypography.java +++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/theme/CvTypography.java @@ -191,6 +191,27 @@ public static CvTypography editorialBlue() { 1.45); // line spacing } + /** + * Crimson Text headline + Lato body scale ported from the v1 + * {@code MonogramSidebarCvTemplateComposer}: 30pt spaced-caps + * name in the main column, 9pt section title rule, and 7.2-7.8pt + * body sizes across the pale-teal sidebar and the right column. + * The PT-Serif monogram font is preset-local because no other v2 + * preset uses it for a circle-ring badge. + */ + public static CvTypography monogramSidebar() { + return new CvTypography( + FontName.CRIMSON_TEXT, FontName.LATO, + 30.0, // headline (spaced-caps name lines) + 7.4, // contact (sidebar contact stack) + 9.0, // banner / main section title + 7.8, // entry title (experience position) + 7.4, // entry date (accent gold) + 7.4, // entry subtitle + 7.5, // body (profile + experience description) + 1.35); // line spacing + } + /** * Barlow headline + Lato body scale ported from the v1 * {@code TechLeadCvTemplateComposer}: 24.5pt UPPERCASE masthead, diff --git a/src/main/resources/templates/cv/monogram-sidebar/icons/email.png b/src/main/resources/templates/cv/monogram-sidebar/icons/email.png index d5680b73525922260e4b1a63c3098f8856a13821..c5f270a51edf0b29ac65862d58a219c7557bb543 100644 GIT binary patch literal 879 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N#&hz$e62Ro7~eAYl=f=|ETMmIV0)GjPP7`1bwB z&tJd){QY-2hEZ0UjXgg4Qa#Ia)#QU>-t&0OMBd04EV1qPs)@{U*V}srsF5+r+uens zgH_f8$YC$>^mS!_#VXAsEi90@uL~%oTH+c}l9E`GYL#4+3Zxi}42;Zl4NP>64MGf! ztxOE8OpLV+46F1`W6kC7HRY#U+Wk1yDVvx<=+9 zhDKIK=2nK55K97gf4&HGbgQR}V@Sl|x6^KxH7f`>1SZEMt=?+ZeN%k%`Tzgp9GeuF zG}qhSQRhE%=YWq8yJGv{hY}Bd9^?=fIyQqtI9bT2Yi5EG&w=CV>rUu9ZFrD9A^z49 z<|FqvE!ZS{pkHT36cl@lpX6KZjXS`<^RDOb-Maf11Wnk+w9x1Gy>D?k8v_qWtD1yF8FMf3 zckT+Fc)U}`W5_$4b&M>j?kE_u-tB=DW3UHP?n@WGD>iWZvn_Ueljgjh^{J9b^U z9{ktc(fRQCxa&6WedML|PJC(N=H2&f&B7g(Kd0)&eQ7cjzLW7{uIZPnd#q#k?G?4j zco2Ng-GA4|3!F|`0*6v|+)@zWw_31!PLM$sxB9d!4sHD}`$`rqxUR$6`(f)P?wiEQ1ggZ44OyKrC*87jI{F zZg=laa6Y)=cHYds-<5;Cov{J?N9YsEye}01tK|;hd$*Mo@K{63YDQv@)w@4{*i#;Nw_mjF=3_V;WeoS|04M z_=CVoj`&<+GGK%Jf5mb?uxzy)2W(9FDIvTD2w*0%OVIZ!GN6$e;Dyz)w@u=Effp@^ z&m|@RS+lthnD^M906thPM=BOS0(@}eXEE>mYKfRfAd8Q{Ny4f4)M_~kD51<+3E{f{ z4V(*@jRThK$qG_50}#q=2xT4u1Hh`M?>XSB)pD?)_(9-ne?a^yFd&q9SajVfy#@$n z-U0oDSK|e%<&-O1F~WPSmQ%n&z-$`mclR$$C5xa?=9f_BIxv*uaBjJbEN??Xnd^~z zkJ42jI<0+F3-Di)+oGYwLDM#(6!&wf0p{*elgakJgP2(2xZoUG6EP4 zm~APSk>zbve<&l$ET}~TP;^tX0DKO#H1$?lv&Rymz(8n9XRXyW%1hGuQZiBe*#v^Vf<%$a22@W={6#id0vJ1zkC@6o_U(D0XJ!$ zunnP%e|MsbX26|*_!I3A58%f2ZJ2I2zy78zRp656`ca|G%XWxw2*OL?Xp4ST9)K(c zgfeg1DZU{HZ>YbAC{0WiSR#DP-DKHn*$Mrv@;1~MlL7Z5Z2o0#mkq!ZV7=QaN)X&5 zse{7$)$x9>>i9{li gNF)-8L?W@3-zCy8=prEILI3~&07*qoM6N<$g4o#ABLDyZ diff --git a/src/main/resources/templates/cv/monogram-sidebar/icons/github.png b/src/main/resources/templates/cv/monogram-sidebar/icons/github.png index ddc1a210d205358975d251d2b3744ded8ebff192..db77f96f6d337858c565350361f896173e2e7e38 100644 GIT binary patch literal 5674 zcmeHL_fOQ{w*BDHkva%S@4XB|2kE`(4APX2bQpS<-UIRgvD5 zB3*hJ{K8O#fy)msH+jjsf5E#u`(*92vsc#1N={C4l3)+D$w?VW0RSM^)zL5l0KD50 z48P%zqhTZZ2$;>0Kl#-0LZ@u0DA9tZSV&GaOW?~ zz*v(N2xz;! z{Mh?*>z|A3*<@FIa(HC*==S+ol#w9CT@mZ7fy->0`!Bk8aXpE8%qICi&fb}_&>H5> z{hiNyNF)(G5+eyVuG!nKW%C;R?!(T%lEfLi8pq}9PilVkMEX>x52Mzv9#l0kMRM;o zcS9qO)Hkj5-?om2&PSi=^d~6|EDQeFY3?-1S!ghoixM*`5=>LO59JV=ohYJJ5t&g zJy7rK5tWeP7OS?-2-QHRij6N&82+*6_^lCm_WIfyKq3K)&h3W8Tjz-%0N~%2x66~e z3U^x(`Rf{L673Q&-DLt(*>%3WZSnYPn)^H0`8&!#^L4xh01AOhi$J7AATq{KsQhh8 z$q7Lq@(@VJ2qV@1A|SjRT%AJxGhsNrq~?~u|8E6?D697Q9r>miA96Wze5Mnt!Lx*XMe);P5 zk;9EGAweK`$V&@G`;u0%C&lHCdD&t%p7HR`LTpM+jWhNtT*)gT>236~?;%bkD6DAt z472?1`DpGX>g;;7AaJ$9)ot-#a+mOM$sk9N%=Q?VvboLY4@B2PgY zv1W^zMrPzS1(uX}ZtYKYs%x9c1W~4DSG_P@UtJn(F0IH(_EYeh%p*;k{My=BXG0&! z(w{V{jIMK+317LHsGd-))b@UKL*==!NBjqKVPE(DuuO^`FsW3>C?!B>*WF1ZPRP@U zO)`*CIanO$C1qldcxN_$OLQ!ua-C8lD^!vYQKK2?{B5W zOU{R&rWq{Nsvvgk$ek9?7%Z!eDPhjfrg(b@n{Y^Bx_Qt}B3Kc}P@`IuU;iZo>=CYF z{BT^z*z_3G(xx0sZ+SW$HTq$9y5VW-ID!RtXa z&tc(qZ0PVjArsk7IMd!rlY;Yi@!MQL>rj0`s|#yooK12yf?2>v$^I z9qJEBN0Mvl70aF~P^aCf&dI9Yy#y&Vv@n(=Wx2$S4`}Vo#=;{sdlu>}(Tq2*c3ChD zu6~Uoa@QrE)b7djfd=ObJruq76I1KFpYzlNCwZfN~bteTf{X+a%%_3pP$*(^N%`F(}qM}^-hse4x_4Jxp| z&O*_e@i*$xtQki@P%-v-E%Uc)&?CFES1hth(NjaaIF(lS8>Sva-UVB|SY6$uK_I0w znLItRV5r!RDUa=56Nb}3Oov52hWE$ou|H4pxK=h>Cv(=bM&)%bQR6#4S0l>Pe+=iB z+P5H=m?n~*5&Tna**7Q3-;ahqeBDkPPGHuv3J<>u$f*+OZ(L<~(&X5!IQP?(ibII2{R*$Vh zh{UX-kM$-IEA}hkoqnyq-NQ_)^(Fj>7glQJLs*CDMC?(H^v+z-_NbH;d_FC{K9Vtr z7Ghe4c@)^LylO1$^bt5P*z`zf^Dc-3(c)UA+(*0aZB6|e`4ya+2)Z7vozqann0|4x z0AH8jv&-g^!VomeTD*Wfup#QkpO=9jBm*sD=M}T{^d-p4pxK^WH zT0l0Gy1N4M@jGth6Z$q%_gEntMrO7$czGa0VL|qlsI>RM>h%82BTe1F%xm|Q1 zC)v4}v~ATROFn8Vipz`ovkm-O&RCsg^eZVht+DzyDd?#XEy~sgL~CkW`JAot=xp@8 zK7Atb_6J9^6IDSMCRiY8F+Do&j2_0btew3G3g!DmFi18G7PQlI`^lb6K{y|o>@}Ui zb{6f%s&bE+n8l`ExjD{mA9$O`2csO_!P&)hO}`F^Dj_Z3LY5}_qVOos+jZpQqmOgg z71gykzGLI(Fyd+TIMNxFx8)=*_A~EZs13j4F6>tQOoFs$Nu_IcCrXi)Ffv%Pf8PAu?l#^t5wSbMP@@ zi!k#-%$~%f#+-!l^E23W?LhtSV75%@MFT1qGVl&VvZBW_JWAa~Z5+d)@r3ftfdbKN zYgB%jq0D1C08Y01{348qzQ1Y>8c`MuK}qv1eLTb88p7cta|?5vlAAJUaHKGLz-`0$rp=K@2h9 zOxcs{By{pUoyLOL18&H3D#P%&|6Jq|e*AId?*IJWKPW+DWqc$>Q>1t7(9r1!A(F5eC z@@Rpdknq>QC9^8Ghk!vp$-8pA*BHC`65)6|Hg#jc+-fNHg0TD`96zIe%uWxiIv)sL zdS*L+sGUx7HxagV>v+lCz)C9=NR=1Dt<}tpXeU`wDkpUG+80`JOi@C@4xNbi4H}8` zsxaR*XJKHpBu zjbo{&33lgn6UsDKJgGS+ztb-w3>U={+X#X z*nXKMAM^B$$PvD=WOMp(tt!~C8#jF~WH1!i2iycdzMP#}2~vP_mN@-DgV+Cdy%M&f z(kpfc)?ulIPmjVu?POQOd~h-)$|yS~uES7=9i^2&{oBhQGiPKQ(5_sTy?hMC)wSp-c9G;Gc=Z>}DpzcOGVVq+`qPtaU7+9oLAnK2R?W8PsWJs_6F==F9Hu*Y{Oy`ix$P+lcgXk7vYE8yF%Hka-W^D^-0vA|#Qz0_W8I;Qd5aB7axQ<4 zLM`3-$2r5DPWpjOWyi&!DKp|sD>Q1Ydz@i^-tg(0d?@)v)2ds2@&-X)w}|-~saJ}zY%M^I z#X65v*__YSjX!c_H=_5PX^ae5-;bwfExfAVt+2uT?w|zBD{O8nxrM{&Z3{>icEJ5Xgy2;)L9;FE=48CRA74z18 zGw0qdZFSz!+ETm!kG|Fu2P319&mKRnqYJ~riN-<;{Ne?q&iLZ*Ubuq@e=4qb6v+*W z47@y{&^j54@vy5q4wfkR)@x23>Vtjy&J(2Cj*hZ;UE4F< zuUU7+S~{P+saAKvdT8ApjQV6ofK41Omoq`Kv=htrz;EYzTlBF}LnC>`GD88CGOLWB zA~N&c2n z%=ejLmHAHin{}6#P5SN?6%`lKFT@sc=mZrD9qCOY#~pk8wE+^1H=t1zqGAvq>n9>%1t2b>w^I>^hP_GfeC6o$P%eM;-z==hb- zWg>N&9a*Jde{{xtE;cGk`g8utMrZCI3a5~d)G;Y^3qfy28IN_#Li^RTj7Mnt6cf07 zX&XV3W|;Yb1+T$1xLgWjDPKK=X7J>xt4ybQ=O`CTh%1PJ;;~3Kbx?XY9ylCPb7lcK zrT?33*e%=RG%kb7D&n1C2l2+W_U#Hzv+ zHfAZ2o)M{Je-Fk!o)x5+yxtSqoAvuvBFHzX`KtSkVm0y#cFodpipnVAqvL!@Y15Rt zmH_)6S^2$kadun;&4lE<+Z-8DA)VvF5ih}cz#OyauXTJT)n7AI*_P=UmZfHDg4sU% zb%G0}+qar4{rMesLu`nCm~q=g!DZdw2U7!0rK z-v9u}nF#@q@H4RYuW&yDL6@sHt^+`bH~crt3I3wB>vwB0k?hJ?gcpc;Qvc=QASSQLJp&*bWu(IzXmCW^qj4i+sja2$0B(0 zXHig-A&CLWas2i zOcoZg^lrV(7Egaw7NScq{R}mf5R%<$pDWPn=zO^w+_8AvuNhPv8u_nr%z>aA@$X6K zP{+zfBONAn;eY$O=ycgadXqc0I;L!eP~p?SVzjUv=QuwnV>0Y=&Z!y|C!C%-Hv6>Q zvgFm#93-27#VOX&1Ve-Q#sLyy3{snY8Ufw5J)B!_h`_(NG!3{Wi8q10&@AF@a0q0> zA#n7z+fxu9ZMwbA1>X{==apC|XdT>|u?^6$%jF>41grbvZd-8kzw zUoemDhRCpR8Zod*-5Med;YaX8p1>S*)!|ys`tdx3)^Yyuulw*n=+pZiIHk!3j|42m ztw}u?*@_4Tf*JW_3z2vz&Y0O}WuJ->!UzjApzE;yC%_{n=6dc4?1X9>q7k3UAm+8i z&8`CcFxR&`fD+1x(Zg1e>CMiPjWpJ@(lB4>COmikz2MDh;8`zc)9M`cN4=zucea&R zSGC_$OhB?{+_u7|3Nn?4P}d~XJLBg$|LOTo$M3J78Kre&DUZOirPDz_g&t3eC7q2y~MH%f2rWi}~42I~w85Ktpk_a?oSsy+y4ubOFt%Do~{%3R# z&woONa^+zAufZddqNHN6H zMN8+_=ZvplXYO447jT1WI{C918C7ol`ZLit$lcaN7Zsie)`LCcCi)z957<W76G@2r$96$daK?+|5WG#BJ54rdOU>kXKsxYcM<(KJt4llP&tExctKpvnD zSni}c-Bq7om8U|RjpCL+BtbAVsz*m3w_=|6BBiPMUs^tQ*u?><@S3jnV{8b$4tbgb zVob`Zj3e<7UV-~N&-hW{v89LmR-xegV18u$B zq=e9L9Ow3Ri#KyoLk-!cef9tNzdz$?f5B*R_~HAllq=*uU+5$m;VRgGPrU#Lz_D@% zKd*x~!I@RJmJ-ti!@eCwy1jA%y};z#4y$knJ>~}{xo(nm@QVnUr(UmrnJqfxx1A;T z0bKP1SKT5W$F58*wCT1!|Bf0bA;&Xw)jmyp-d*GtkB-sOD?ECeh62EqJ3O4f>^oh= zyY79N`7x+Z!ET68KdK&Xj^B(ifkecseF-V3cXbuHGxf)C(MQ1IUFZbOn@l_%k*{j_ z>kk7~ykuaOFSH+0`|$iC{a==CPPIq2vQMS^ZC$T%n1&_>L0X5lxh|l1uKZlZoL_RV zYi|uDWZb``2l+`mbVYt)Hj1QRLvy5F^>Hd6v{&O(7inYUl6yfbULsSxB-$o5@4<}- zL#oDwT^a5q8CbRdODCD%hqt_Q;`lyM#cr-`=P~f>9twY_WThZ}s3D1=SXTJ~-+?^- zoKwp{G+~bCk|RxP97<|0Ch1(twld3|Ebz8D zxaDngq{%k$Ik_;;rw^bvU~H9_=yOA`A?ig!Ok7M$ERAzMowD?=9;G$Zls;%Znzr2G zW*wfsloa0pcTog-S>t+KX>2j2KXhL$GC@25j*=oB$JZmzY#hdXn73iuXVpZu%qQTA z(So_YdbCrY#5G8ujy!WCp|Vfzw+Gv#S=#&BEZ@gJ#U${a`bM4K1b&V1o|cv}jwn0{ z{=0wk-f6Mu8!DBO&@G0FzZ*=ZqKi7K>jg1Z8f=p zK4wkMc*_$#mpGu_A1pEOQFCNU?$ukUloQVy;COj(wfI=0N+f~+_gdo=aBtX^u zvJHA(wBI|sk`rBK26wA^<^#NcN~#-woXnD4cvM&0OvNrI@HO>XN)rQkqJQqx9@1m_ zg$5;%EW`uHDNBBOqr{*M2|upBy@vb1%=P)}gmNge$45iQ$zg6opIgsw1LBZhH;&pv zCfZ4jS>bU58y255ybGc&M80J;#xx~VyzkfpErsbuWC&SWLpon`$SdR8yD-(H%bMreUF!J9 ziLR=&efV8N^tQ~#%7k`9LiXB?=gotdm;=qVq6)o1zOh71*EqfXW!ISpe@jz%!{b{J zC&}$M`L0H!_S+WDhYcl3S|ZLMsWR$E*dR2y1{2ufuwu4BJ^OH@@+ zzqOzF!Nv;?Tu$%mUl%ESU&6{o@F1~Iz{?DU+%7Rd>l}?b&C#DAbG>v#Wp0zF@1ND* z(Dp(}lyWS~eU~{J>0ugaPWlpjq~h24uieyR85g4rWSKg)T0dak8lCLZU^?ovP4KBx zB=$t<@epiQrCHH0pOQw62kap-HJre42H;keN6gFqOK)~WjO zFX>@lvc6Wbn5hrp%z@dN1=`~KvsAH;mLkDH(ayJN}Yj~Hh0#;m!tH&bDoyv2t^v=zCgAj#V7*PY8^ zNNo$hub}+?#?iudD<1|>y}wFh^Pu-fTHeZ)=Y@nR1aExE&QYX?R{-nQ5whXAE*C)v zf)F0_2y8GRV$y2Pty;>sisF*lkZd=8EJji7Sk}>gwn1k@JQ*=o>FSXtEuVURyh=p*W1u%HeS%QW(iquYU;a2atyX8u;v?NfwjL5Y{%Ls* z{GWD=0#NTzuH9y6l&Tq>pb{t6mrv^iyiRCrv1O81k@EBAGHO#NV4GT4#6}qf$2_G> zeeaX7M!mTiZ@qvhQl%+ea7>&ISlBKAUPQiU-jdNy=Y4oaF2@wew~7q^DCSED3Tl;q zE5O|kwDIEw*WB24a?$s^6PnXyU`FPQZh$iEECO6cl-vQV_-%3A@iFZGMqv-OZ7r^k zUVFKWY3HK7)@AQYK3QuqFFlKnmi3xkp?-_rbmB~LJ87`5OSJ5R`;dP`>> z63PV&ev5x|6o{_tJlj4>*I_if}OsZcaYLhI|y-te#(Tm1e1< z3%)dt7wy?RxL+c|C^mZzH*DK~O&Q1Z34YX=g`*GI!MZ4N=XsFd0x&ycAqMc?+i-Ry zo_@zYQuRk2)9dr8IoDlBfi~@F6$o&Ts7cDff0O{W#5dExkkJt}eR!s7}t|B)ECXPzybSi~}01+n-Dl7ZRqR8!;}B9rdV3=jvR}(rlPUkdQad6VO_XkTQem~MNdv&wOnGJw^f z9x+lx@{hjb!|_is*zh(6OmVQ+>E1Gg>d`mjGR0xdw|0CLvMUgT9eN&CMt#6J(Fc<$ zZO(eI3{+<@_eD*|axe^{QBQAG?(tUZTc$JUT@=(Ks*7+A8Ur zz-rQPIIj$K;w=(lxH4ay(j$R-7s7ZG{mq)m0f{vzO&N=}OPbcfYBVtD((NvgdLuS1 z&_BUYGC~fPMSRChk0<*JOvLkh1;hf5+Cv`es^&==si_}dl_BkUx`1yY#st=Fbhq_m zAdDm2fV^Tv-sM}@q5rVsx}{uF+eT%NOs?a ztg%ufI6jru{lr)2CZV>~NjPq+u-X)w{o|V8X9$B9Ww#;2Xqot0N@(^14^$^+clUGY z$cT#!%TF~t*1NDqYQ#6DizO<&3?`5~VpOQ5)9Fc8W=@5c)8=hxME-a1xfl=8&Me1f z3zMxuZPB@v)W=n@)EE)z&kCnDA`R8PpgK(m`7QR&9VaREv<}{UXHEO5 zjrk|?gzf+eLBCc;k}E8GBqa*&av%PB|lvh zWG8j7KwwZ`0r3%Gida27w5aV`7@J<)=$j_%9>hlf=Rp%iK#x6ScX_z_a);-kGYK{B zO?fHm0+|{S{>%L}=!Po28*Wg6Kg~Cru7mp>r$*cwi?l(kGlRcWjr`a>FM8egUTnL) z*DkBK7iEY93MZB%xAO+sY9tAtp4C2Q1IWyZxZ8F0;Ya1UAKRe>1DdtW-y=?}`4YX> zolQP+jJG`6v4M(OU$Tu!j@I-Wu^o6r#toK$8q!!}h{WGDTa)CI#rqVIXXfw8{O)BR z4cB)PZ#~huPs?XfEAs=gsZUh>x(UZ#PE}i{`D|wdmb-eaA29~R)bqd0TM}M>#Kx+K zv_exNvYX5$4HlmzR>n^Mbgd1V?;H;1KQ?k>-c7=^A1KYYb?uwYU~w2Dh59EFGZj_W zH#9e1D#v!F;EtTLE>IT6WKoyJGra?Fj)$63oMw{$SbCNJz%v;hW3#ATUn-bLyecSU zKf0EwXht7d(bbv1=1hGlNxEg}TjY~H zJ(Cg;Kkc0i^a7Dn^0Z|b8Q^Bybx zp6)Dcs7kk3_>Mw`yT`TNW#Xz;wg|zIliKXbIo;8~Mav(DhNf zT{iL8>W5NuH&aN^WxGy-^yg%SBi)X|q^i+M8cflm-lKm;s47Ah$7LXrLBqYie<@fX zA%6n0G!z%2YCQ6-(suc2Yn18Kp!`Kx}K{4qrTMPbI#~; zAs2NR{e4ZWlw}EPF!$2sMe?0t6&CI?Qr{h=pdvNN5<;PA&sv-DxW(s1vS9V@z1`<`*vn`*>Q8Ian@-?X`0jP`MNw&Ezl0ejX=A_P@X2b?l% z==BVk88@%3gR&o4(`lsj>&&G6?UF^Go<3L#pj_U)J^k08jutb8BVT=z_wc+CGAUfE z%A$~Mu;P4|1&)dB+V8;ZbR{mXCZnXh<%iTtmjfwfV9i)`k=6Ng&-H)1vo);6EFim= zAG{UxGIQZXouke+;(FS%EpQv0+>rE_P&=`f{cQR*lUp1t) zI84;XE%Q~yLyJt=rkEPuI6E!-Aq?ABmpX?!!+w_0zIXDAMWT+ct68~-qP1|IqBbr9 zJNSl#_{xmR3Twr4s4C09BBpYslM}X)_xuqvM*gY+8*IDwD607D-PlZ9ej-)gfXZ2v@(! zzc0aetoEC~DA^Riil%N}`r5xjLdXJFGTYBi@s#7m@%cwn2tC>D^Az6QmqkOu)D6va z!HlXoclEElw$Jo{4)+KU_Dk#LN$KSeGkZNkIB%i%7Yi11FUtWXo1h0(ry$RmhH;XZ z$*0kjOPn4+kS#h3p5hDuPjn!fR%M~bc^|r+E0-QSN7OJ<5UT^7eYTIAJVl|vYyP7E z8%sNMBg5{2gR{fYy4iY7+EdO*)C!v!%ZT21X#T&fBoaab_?6HoPB8~*7G=es3-2D3 zSH`g1+3-`iW6f-LaaN<_(Rd(m3#>KM{$_G}Sp7r;+jJ+G`v4KJ)zNSJ*kN&}{q65- z>u0|OUY}-*UKlsmKU@n+w|`U|vgkDiWg#pezfG5OzY@6l$hS`>($Tr0KR278RVLC^ zE?7ku0q}p^^C3?I-0d zz5QUc0sYf3^x<9)f};Of1@JNp-7F#r%zh9z7=8Xv{4?1zw0tibK@t6|2>h(D*w;8B zZHa<7B_c=WkmyP79uz@w?G~sss)$^C6}%*l0$j|R#)UJtu8rDt_#Br&FgbN_oMhti zub&;Pg_0nLS%eN!58u28g;SKeInTDdirLE2cvMOuuetUT*rB@{OW|IRqXU14dI6Te z9OTdYPlu^O)HpS~kQ%q>yhvXelOPQWM-$AiYPjFc(ZR}0jbp)I@;^UjezN8=#>7fE zu57oxhoGHkKbMyfZkn;+ot04$2VR%xEy@4TL7(Sx3_v&;kLvsD#|fD&LlALWEy99@ zF?zDf0XaPYrpPNpiiJ9dNQd;(-DGGmODpY?hqnj0X+gA0f?~ZmA4w(iXO`|%o+2VT zHy|jU2<}u|a@i8OfvB1dW0jgwrkPuQq3h78BySEAFL_?ZI;?I@9BM_6MD?k=r0+*a v`7e?$!?#txR}WJDw>_Hwwz*?2bjozAp|@rKHrDE_RRtItVD)QsaZ&#V)ZxG% diff --git a/src/main/resources/templates/cv/monogram-sidebar/icons/linkedin.png b/src/main/resources/templates/cv/monogram-sidebar/icons/linkedin.png index 5b2612c7257f59bf8560aa60d5cdcc827fe1ae4e..40b9aff2511935a62ddfee648e19219c033ec6cd 100644 GIT binary patch literal 1318 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4FdyXZGcaRtE#TmC>p2`IQZJcPZPS}Y$vrp!eSLd) z!^WM@w}1SQX{TouHRaY9CWU((%;J|0$TwNb2wzwx5%5T)O){L{#F%r7*6jPw)*02F z;VpCi?zq|JY@uy|sJ+8CCzrc!8BZsA|H|GyX%)~Vj7i?^E({&4vK~MVXMsm#F;J`l zjOCx4RsssLmw5WRvcF=L=8+Z_NZi*26jCj5jVMV;EJ?LWE=mPb3`PbJE}yZm=^|K^o3kLgSNrz~a^P%~$m_7p8sg%Gb+wM0Ab z?uLbDCmm<(%6VbqbhFDsf!(e@!0*nll^zVCIjX5yLJo5U70msYpV3!f|I+Hpv)E8% zGLx#?Mzg6xOqHoY&Ym1E3Krk-VGDWSwzQRJ#j0sirY-S~(c0HazqT-BWu*MvbaK|Y zKhLF4UW>VL>DIJsHEr99jxkuLXJ3!n$Ew4(yqiCr{kZttFva(;+*TaF`TASMp4z|6 z&#p9_*G>NUfuHTiAuExy?e1{+!xx|Y3;?m*{4?fB6n8eU0RB)N; z`j!K}PnI_t=oWC_>GWhM(NTSJ7_IR{xrZ1@50Gyp&{a z6kmVAqW-UtgW!CDIdjkJCcV=>*f3LA`jXVYO_yhWOPu|yaQ?qTjYoetGp4)n|=b52!p4qpUXO@geCx;th4U` delta 1187 zcmZ3+^^J3aBnKM<1H-D!!g~`H^#mB$lDyqrfC519(|LFOd(?ygx19TKjcDzlo`{W!$_WJ1M9g|3Q^BK!xl_jHKc zc5u9MbaV{xH(bPG)rXO*s)EUniz_Ypkv1R!z@I+m&8bmA2;Hy`PTt z#?GZzOaAVU{kp#1cG<^!(!bZH`yO5AkmJj61W4wdn7LkX_afG-a%>0O-W)r8Bgl~> zcg`Wfn#N}e3}%lkZMtvXn8vfRwn9`tdFAJ{Y5X^OnN1Dik`6Z>S*DlDc%ysg4O97r z&xB?dF=ixWzme*9j9&U<;~KF5)As4^E6sc#)i)@fGW)3Pr*eP$W4Rc~+bjMvAMo7M z(QGk!N$#QM3fAq%&2r{2O#8re@0CaXBjY)TkMGdiR#+KwgniC|cH4LL{f9U5N}D(A z|NX?WZR(979r&JRkl?u~&Uh7U}8HsnqHp*o^ zp8q6^V|9qJo6Rl}#tVnae|Ucv**rJoQ(aEnk~yQgBU6KWEs9^_n?CHJ2D@WWN> z4lF*j;k!lM+rWR~2MzhY1f9Ob^sL48TCC*v=dBitru%u`{Jom1!TMvsd>ej_hXvDS zImHBeGFX4P-?8`F(T86>Lc|zm7d?CVbTe1(^Qo*3_gJsgx$*HV)M0R2=(WARfwMu7 zG2(jcf(fr!5-zcJIjoRwXf$OM*nMrI;uTJZl~E2on;BafL?Q)d3O#$Pd-pd}ZcMYc z^`-SfW+s=Gt=1KPub7f){O-_8k>uz@eP0gfZ{cW=OystC&Eezx-Rj=nLzl{@>7D#| zC-;X(+3Q*7ep)*-N3$H~13CFKI_7CPZ`o>em+wAT6 xP+8QZPPb*=DQWgAg&_x2_+~Ifd}sK_{Ge`m!PU+B?Z7gC!PC{xWt~$(69A420|Wp7 diff --git a/src/main/resources/templates/cv/monogram-sidebar/icons/location.png b/src/main/resources/templates/cv/monogram-sidebar/icons/location.png index 48d2152f2cf5188453bcbaab4887d9fcb79325c0..4d11dd9ac8a9607a9aa719c9fda08e33cdb99556 100644 GIT binary patch delta 409 zcmbQscAjN|BnLAC1H;_yjc+C@>eojF_=LEs>RQnVU9OvN1GG)HB*-tALCHRiWvb9Q z4UaTjqgumSG?ESIJG~irc0T7 z{)62|ZY=+yYx+g<-)WzUxCc=aC)Y+ghJ*^$@Pp8Y2C@j6+92kD#4?<}zSaE0;gRQ8Gnc8hnF adl_WdYg^-286^W<%;4$j=d#Wzp$Pzo3XW9( delta 602 zcmX@lGM8diXdf@R zFmd9a`pNE$!u7{9CAAnB82@^@IEGZ*dOJh6Lo86FZGNm6%fSb1lU;SA{&?QHlVG4L z;dtvd0Os`r4J%bJzd=1R_R{<`MLIIhSyg=TQC_+eSIM1 z_-oIv`@*YYnU-y0uwl;g$^0g>V`kCMt;>PZt`B$%mbrc2exP;A>Fc(F4J&2XuSu2I zPAvL)Vd=JW-x-2;-nd|MAnNLVW2@PW%a{tdD=s~sq3B$4?jtIuS`Yo~2KUn5?@2rtb0Sa+Nygl&4&u@9my{&;Ow)$QO(fgVrm#|eT zKP_z@<1!=mJIr!lQvSuiW9ARmtB(P?>F)vUgqsKdn5}O-tvP-EUbY0S_QvlA9Nj

Mt10m;_q?rxI(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N&4qfKQ04s;0ajcLuTwuU)ua0NXqbUUM77{d@YBl8eLBP%0wD?>|&C4sv?Uj#bZ-_yl0 zB;xSfsW-z8IqWVOPowmu7QLvo#&e`Al`(0c8RxO{|V*R$>@_EgDPqmjJ zD>VZDkglvz~Ef z0*{DoN{0XYg&%4QytsZfiZ2wkuvO-5Zde$2_*$`70pr1t!qDkc9&&ZW9OpR9o4P|) z?8q{ff^zvhiAihPrWAFK!<~%$7dx6ox;OXk; Jvd$@?2>=8|G_?Q# delta 909 zcmV;819JSa2fhf983+ad0063Kao&+3FCqqFOGiWi{{a60|De66lK=n!2XskIMF;8x z9t|if$8^w5#KFjO%!Iy5jkG&3tOFgh?W$7vZV0009XNklhWd=hBEX5>`;im(=X?TFwR?D`~0ZZX!23Rd;08fDhfBCZz%3KZ| zv|<9RmUX~=U`3I#bHHJt%&*Xa$KvA=I9Vb*z(JrtohcIvrx{?iTm?MH*fvEd^CNWd zQF6}!tK}r%e{PP}W1&NP5ddrirsn87VzrzcI=GVnt0jPTxbCdb!JPyE^MMwJ>wXU% z+*QDOhil#v%8Y~#9#Vh|9lDUZoPDcv_RSH>d<-4Bki0WsVaEC{VCqB*5BX=nFwj_L z?Jl8ASLmR=1q_ubV6jlx@%U;X`w?_LcqfewI@P{ ztb~AP8EOw$E!Tt&>RZ6OjP>`dmQA6~!alP#-$57Xc}kGS;^OJyy%wtQWFc zHdrm^R$>{Qdq*h!H1q82+y=G?Wqy`%jd_3}*?Sn}4AALoSIKottP|+S(fSHlE0h^3 zGWKlXe?yTmT7)uhT?M#qieAXs*9v?Z@7uv@*<2z#z#FUOa#sO9w#Cl^?G8hn2Rei@ z-wMKKSu|}B%Jha5&DHbhd_0PqG9bF1Og5fKl88IdWeDtR#7vSqe0f9KZ~c{1B@TxFeK#ANrmn jkw_#Gi9{li$iK}WA#KDYVqEu700000NkvXXu0mjfN7s{w diff --git a/src/test/java/com/demcha/compose/document/api/PageBackgroundTest.java b/src/test/java/com/demcha/compose/document/api/PageBackgroundTest.java index ea600441..6cb8c6ee 100644 --- a/src/test/java/com/demcha/compose/document/api/PageBackgroundTest.java +++ b/src/test/java/com/demcha/compose/document/api/PageBackgroundTest.java @@ -137,6 +137,125 @@ void emptyDocumentEmitsNoBackgroundEvenWhenColorIsSet() { } } + // -- New multi-rect pageBackgrounds(List) API ------------------------ + + @Test + void pageBackgroundsListEmitsOneFragmentPerFillPerPage() { + DocumentColor sidebar = DocumentColor.of(Color.LIGHT_GRAY); + DocumentColor main = DocumentColor.WHITE; + try (DocumentSession session = GraphCompose.document() + .pageSize(400, 300) + .margin(DocumentInsets.zero()) + .pageBackgrounds(List.of( + PageBackgroundFill.leftColumn(0.33, sidebar), + PageBackgroundFill.rightColumn(0.67, main))) + .create()) { + + session.add(new SpacerNode("Block", 200, 80, + DocumentInsets.zero(), DocumentInsets.zero())); + LayoutGraph graph = session.layoutGraph(); + + assertThat(graph.totalPages()).isEqualTo(1); + List bg = graph.fragments().stream() + .filter(this::isPageBackgroundFragment) + .toList(); + assertThat(bg).hasSize(2); + // Painted in list order at z=0 + assertThat(bg.get(0).x()).isCloseTo(0.0, within(EPS)); + assertThat(bg.get(0).width()).isCloseTo(400.0 * 0.33, within(EPS)); + assertThat(bg.get(1).x()).isCloseTo(400.0 * 0.33, within(EPS)); + assertThat(bg.get(1).width()).isCloseTo(400.0 * 0.67, within(EPS)); + for (PlacedFragment f : bg) { + assertThat(f.y()).isCloseTo(0.0, within(EPS)); + assertThat(f.height()).isCloseTo(300.0, within(EPS)); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + void pageBackgroundFullPageMatchesLegacySingleColor() { + DocumentColor cream = DocumentColor.of(new Color(250, 245, 235)); + try (DocumentSession session = GraphCompose.document() + .pageSize(400, 300) + .margin(DocumentInsets.zero()) + .pageBackgrounds(List.of(PageBackgroundFill.fullPage(cream))) + .create()) { + + session.add(new SpacerNode("Block", 200, 80, + DocumentInsets.zero(), DocumentInsets.zero())); + LayoutGraph graph = session.layoutGraph(); + + List bg = graph.fragments().stream() + .filter(this::isPageBackgroundFragment) + .toList(); + assertThat(bg).hasSize(1); + assertThat(bg.get(0).x()).isCloseTo(0.0, within(EPS)); + assertThat(bg.get(0).y()).isCloseTo(0.0, within(EPS)); + assertThat(bg.get(0).width()).isCloseTo(400.0, within(EPS)); + assertThat(bg.get(0).height()).isCloseTo(300.0, within(EPS)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + void pageBackgroundsEmptyListClearsBackground() { + try (DocumentSession session = GraphCompose.document() + .pageSize(400, 300) + .margin(DocumentInsets.zero()) + .pageBackground(DocumentColor.of(Color.RED)) + .pageBackgrounds(List.of()) + .create()) { + + session.add(new SpacerNode("Block", 200, 80, + DocumentInsets.zero(), DocumentInsets.zero())); + LayoutGraph graph = session.layoutGraph(); + + assertThat(graph.fragments()) + .noneMatch(this::isPageBackgroundFragment); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + void pageBackgroundFillRejectsOutOfRangeRatios() { + DocumentColor c = DocumentColor.WHITE; + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> new PageBackgroundFill(-0.1, 0, 0.5, 1, c)); + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> new PageBackgroundFill(0, 1.1, 0.5, 1, c)); + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> new PageBackgroundFill(0, 0, 0, 1, c)); + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> new PageBackgroundFill(0, 0, 1.1, 1, c)); + org.junit.jupiter.api.Assertions.assertThrows( + IllegalArgumentException.class, + () -> new PageBackgroundFill(0, 0, 0.5, 0, c)); + org.junit.jupiter.api.Assertions.assertThrows( + NullPointerException.class, + () -> new PageBackgroundFill(0, 0, 0.5, 1, null)); + } + + @Test + void pageBackgroundFillFactoryHelpersComputeRectsCorrectly() { + DocumentColor c = DocumentColor.WHITE; + assertThat(PageBackgroundFill.fullPage(c)) + .isEqualTo(new PageBackgroundFill(0.0, 0.0, 1.0, 1.0, c)); + assertThat(PageBackgroundFill.leftColumn(0.3, c)) + .isEqualTo(new PageBackgroundFill(0.0, 0.0, 0.3, 1.0, c)); + assertThat(PageBackgroundFill.rightColumn(0.4, c)) + .isEqualTo(new PageBackgroundFill(0.6, 0.0, 0.4, 1.0, c)); + assertThat(PageBackgroundFill.column(0.25, 0.5, c)) + .isEqualTo(new PageBackgroundFill(0.25, 0.0, 0.5, 1.0, c)); + } + private boolean isPageBackgroundFragment(PlacedFragment fragment) { return fragment.payload() instanceof ShapeFragmentPayload payload && payload.fillColor() != null diff --git a/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/CvV2VisualParityTest.java b/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/CvV2VisualParityTest.java index d761360a..511a4d0e 100644 --- a/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/CvV2VisualParityTest.java +++ b/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/CvV2VisualParityTest.java @@ -123,7 +123,10 @@ private static Stream presets() { (Supplier>) TimelineMinimal::create), Arguments.of("engineering_resume", EngineeringResume.RECOMMENDED_MARGIN, - (Supplier>) EngineeringResume::create)); + (Supplier>) EngineeringResume::create), + Arguments.of("monogram_sidebar", + MonogramSidebar.RECOMMENDED_MARGIN, + (Supplier>) MonogramSidebar::create)); } /** diff --git a/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebarSmokeTest.java b/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebarSmokeTest.java new file mode 100644 index 00000000..72e581f0 --- /dev/null +++ b/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/MonogramSidebarSmokeTest.java @@ -0,0 +1,127 @@ +package com.demcha.compose.document.templates.cv.v2.presets; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.layout.LayoutGraph; +import com.demcha.compose.document.layout.PlacedFragment; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.v2.data.CvDocument; +import com.demcha.compose.document.templates.cv.v2.data.CvIdentity; +import com.demcha.compose.document.templates.cv.v2.data.EntriesSection; +import com.demcha.compose.document.templates.cv.v2.data.ParagraphSection; +import com.demcha.compose.document.templates.cv.v2.data.RowStyle; +import com.demcha.compose.document.templates.cv.v2.data.RowsSection; +import com.demcha.compose.document.templates.cv.v2.data.SkillsSection; +import com.demcha.compose.document.templates.cv.v2.theme.CvTheme; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Smoke test for the v2 Monogram Sidebar preset. Covers the badge + * initials extraction, contact stack icon resolution, and the + * Profile + Experience main-column composition. + */ +class MonogramSidebarSmokeTest { + + @Test + void exposes_stable_identity() { + DocumentTemplate template = MonogramSidebar.create(); + assertThat(template.id()).isEqualTo("monogram-sidebar"); + assertThat(template.displayName()).isEqualTo("Monogram Sidebar"); + } + + @Test + void default_factory_renders_full_document() throws Exception { + renderAndAssertNonEmpty(MonogramSidebar.create(), fullDocument()); + } + + @Test + void custom_theme_factory_renders() throws Exception { + renderAndAssertNonEmpty(MonogramSidebar.create(CvTheme.monogramSidebar()), + fullDocument()); + } + + /** + * Asserts the two-column page chrome is emitted via + * {@code pageBackgrounds(List)} — protects the + * engine contract that both the sidebar fill and main fill are + * splice-painted on every page, so multi-page content + * automatically inherits the same visual structure without any + * preset-side filler logic. + */ + @Test + void emits_two_column_page_chrome_on_every_page() throws Exception { + try (DocumentSession session = GraphCompose.document() + .pageSize(595, 842) + .margin(DocumentInsets.zero()) + .create()) { + MonogramSidebar.create().compose(session, fullDocument()); + LayoutGraph layout = session.layoutGraph(); + assertThat(layout.totalPages()).isGreaterThanOrEqualTo(1); + for (int page = 0; page < layout.totalPages(); page++) { + int pageBgFragments = 0; + for (PlacedFragment frag : layout.fragments()) { + if (frag.pageIndex() == page + && frag.path().startsWith("@page-background[")) { + pageBgFragments++; + } + } + assertThat(pageBgFragments) + .as("page %d must paint both sidebar + main fills", + page) + .isEqualTo(2); + } + } + } + + private static void renderAndAssertNonEmpty( + DocumentTemplate template, + CvDocument doc) throws Exception { + try (DocumentSession session = GraphCompose.document() + .pageSize(420, 595) + .margin(DocumentInsets.zero()) + .create()) { + template.compose(session, doc); + assertThat(session.roots()).isNotEmpty(); + } + } + + private static CvDocument fullDocument() { + return CvDocument.builder() + .identity(CvIdentity.builder() + .name("Jane", "Doe") + .jobTitle("Senior Platform Engineer") + .contact("+44 0", "j@d.com", "London") + .link("LinkedIn", "https://linkedin.com/in/jane-doe") + .link("GitHub", "https://github.com/jane") + .build()) + .sections( + new ParagraphSection("Professional Summary", + "Builds **reliable** document pipelines."), + SkillsSection.builder("Technical Skills") + .group("Languages", "Java 21", "Kotlin") + .group("Testing", "JUnit 5", "AssertJ") + .build(), + EntriesSection.builder("Education & Certifications") + .entry("MSc Computer Science", + "University of Manchester", + "2019-2021", + "Distinction.") + .build(), + RowsSection.builder("Projects", RowStyle.BULLETED_STACKED) + .row("GraphCompose (Java, PDFBox)", + "Declarative PDF layout engine.") + .build(), + EntriesSection.builder("Professional Experience") + .entry("Senior Platform Engineer", "Acme", + "2021-2024", + "Built rendering services.") + .build(), + RowsSection.builder("Additional Information", RowStyle.PLAIN) + .row("Languages", "English, German") + .build()) + .build(); + } +} diff --git a/src/test/resources/visual-baselines/cv-v2-layered/monogram_sidebar-page-0.png b/src/test/resources/visual-baselines/cv-v2-layered/monogram_sidebar-page-0.png new file mode 100644 index 0000000000000000000000000000000000000000..3540def7547ed6f37d5be45cfa27a9e4467f8531 GIT binary patch literal 92428 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¥l=ni(z1IidOMzuE5}I+u7lKNV;1 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 iqmncjF|3UJ_@7yLsv literal 0 HcmV?d00001