From 4dc94fb087c0ce38dc0d2580c108fe901d6da8cd Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Wed, 27 May 2026 08:49:18 +0100 Subject: [PATCH] feat(cv-v2): migrate Timeline Minimal preset + extract reusable timeline axis widget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports the legacy Timeline Minimal CV preset onto the v2 layered architecture and extracts the central vertical timeline axis into a reusable shared widget. Preset * CvTheme.timelineMinimal() factory wires CvPalette.timelineMinimal() (all-grey ink/muted/rule/dot scale), CvTypography.timelineMinimal() (Barlow Condensed display + Lato body, 28/7.8/13.5/12.5/7.9/7.5/7.8, 1.2 line spacing), and CvSpacing.timelineMinimal() (compact 3-column layout, 0.8pt header rule). * 3-column body composition: sidebar (Education / Skills / Expertise / Languages) | central timeline axis | main column (Professional Profile / Work Experience), weights 0.74 / 0.12 / 1.74. * Right-aligned contact stack with PNG icons (location / phone / email / LinkedIn / GitHub / fallbacks for Dribbble / Google), reading from /templates/cv/timeline-minimal/icons/ resources. * Replaced github.png and linkedin.png with new SVG-derived (mdi-github, mage-linkedin) PNGs recoloured to #7A7A7A so they match the existing email/location/phone/dribbble/google icons exactly. * Preset-local sectionLines(CvSection) flattens every v2 section subtype to single-line strings so the sidebar can apply per-module truncation limits without breaking the visual flow around the fixed-height timeline axis. TimelineAxisWidget (new shared widget) * Lives in document.templates.widgets so proposals, cover letters and other process-style documents can reuse it. * Renders segmentCount vertical line segments separated by segmentCount - 1 markers (CIRCLE / SQUARE / NONE). * Style record exposes marker, markerSize, markerFillColor, markerStroke, segmentLength, segmentCount, lineColor, lineThickness, and padding — every aspect the v1 inline implementation hard-coded is now a builder knob. * render(host, style, totalHeight) overload solves the multi-page CV request: the widget recomputes segmentLength so the rendered axis matches the supplied total height; callers that need an axis to match a sibling column's height pass the column height in. Tests * CvV2VisualParityTest exercises 12 presets (added timeline_minimal); 12/12 pass at the existing 50k pixel-diff budget against the new visual-baselines/cv-v2-layered/timeline_minimal-page-0.png baseline. * TimelineMinimalSmokeTest covers stable identity + default-factory and custom-theme render paths. --- .../cv/v2/CvTimelineMinimalExample.java | 50 ++ .../cv/v2/presets/TimelineMinimal.java | 639 ++++++++++++++++++ .../templates/cv/v2/theme/CvPalette.java | 15 + .../templates/cv/v2/theme/CvSpacing.java | 24 + .../templates/cv/v2/theme/CvTheme.java | 16 + .../templates/cv/v2/theme/CvTypography.java | 20 + .../templates/widgets/TimelineAxisWidget.java | 266 ++++++++ .../cv/timeline-minimal/icons/github.png | Bin 1694 -> 5339 bytes .../cv/timeline-minimal/icons/linkedin.png | Bin 1227 -> 3471 bytes .../cv/v2/presets/CvV2VisualParityTest.java | 5 +- .../v2/presets/TimelineMinimalSmokeTest.java | 91 +++ .../cv-v2-layered/timeline_minimal-page-0.png | Bin 0 -> 73808 bytes 12 files changed, 1125 insertions(+), 1 deletion(-) create mode 100644 examples/src/main/java/com/demcha/examples/templates/cv/v2/CvTimelineMinimalExample.java create mode 100644 src/main/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimal.java create mode 100644 src/main/java/com/demcha/compose/document/templates/widgets/TimelineAxisWidget.java create mode 100644 src/test/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimalSmokeTest.java create mode 100644 src/test/resources/visual-baselines/cv-v2-layered/timeline_minimal-page-0.png diff --git a/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvTimelineMinimalExample.java b/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvTimelineMinimalExample.java new file mode 100644 index 00000000..884018c9 --- /dev/null +++ b/examples/src/main/java/com/demcha/examples/templates/cv/v2/CvTimelineMinimalExample.java @@ -0,0 +1,50 @@ +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.TimelineMinimal; +import com.demcha.examples.support.ExampleDataFactory; +import com.demcha.examples.support.ExampleOutputPaths; + +import java.nio.file.Path; + +/** + * Renders the v2 Timeline Minimal CV preset against the shared + * grouped skills sample data — spaced uppercase Barlow Condensed + * name, right-aligned contact stack with PNG icons, and the central + * vertical timeline axis (4 segments / 3 circles) separating the + * sidebar (Education / Skills / Expertise / Languages) from the main + * column (Professional Profile / Work Experience). + * + *

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

+ */ +public final class CvTimelineMinimalExample { + + private CvTimelineMinimalExample() { + } + + public static Path generate() throws Exception { + Path outputFile = ExampleOutputPaths.prepare( + "templates/cv", "cv-timeline-minimal-v2.pdf"); + CvDocument doc = ExampleDataFactory.sampleCvDocumentV2(); + DocumentTemplate template = TimelineMinimal.create(); + + float m = (float) TimelineMinimal.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/document/templates/cv/v2/presets/TimelineMinimal.java b/src/main/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimal.java new file mode 100644 index 00000000..00bf835b --- /dev/null +++ b/src/main/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimal.java @@ -0,0 +1,639 @@ +package com.demcha.compose.document.templates.cv.v2.presets; + +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.dsl.RowBuilder; +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.TextAlign; +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.MarkdownInline; +import com.demcha.compose.document.templates.cv.v2.components.SectionLookup; +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.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.document.templates.widgets.TimelineAxisWidget; + +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 "Timeline Minimal" CV preset. + * + *

Minimal two-column CV with a vertical timeline axis between the + * sidebar (Education / Skills / Expertise / Languages / Interests / + * References) and the main column (Professional Profile / Work + * Experience). Visual signature ported from the v1 + * {@code TimelineMinimalCvTemplateComposer}: spaced caps name in + * Barlow Condensed, contact stack with PNG icons, all-grey palette, + * three timeline dots between four axis segments.

+ * + *

The preset stays a thin orchestrator. The 3-column body layout + * (sidebar / axis / main) and the contact icon row are preset-local + * because no other v2 preset uses this visual today. Section bodies + * are flattened to a list of lines via a preset-local helper so the + * sidebar can apply per-module truncation limits — the canonical + * shared dispatchers do not enforce that shape.

+ */ +public final class TimelineMinimal { + + /** Stable template identifier. */ + public static final String ID = "timeline-minimal"; + + /** Human-readable display name. */ + public static final String DISPLAY_NAME = "Timeline Minimal"; + + /** Recommended page margin (in points) — matches V1 TimelineMinimal. */ + public static final double RECOMMENDED_MARGIN = 22.0; + + /** Diameter of each timeline marker; 4 segments + 3 markers by default. */ + private static final double TIMELINE_DOT = 7.0; + + /** Default total axis height — sized for a one-page CV. */ + private static final double TIMELINE_AXIS_HEIGHT = 620.0; + + /** Top inset before the first axis segment starts. */ + private static final double TIMELINE_TOP_PADDING = 28.0; + + /** Number of vertical line segments; markers between = segmentCount - 1. */ + private static final int TIMELINE_SEGMENT_COUNT = 4; + + /** Stroke thickness of every line segment. */ + private static final double TIMELINE_LINE_THICKNESS = 0.75; + + /** Stroke thickness of the marker outline. */ + private static final double TIMELINE_MARKER_STROKE = 0.8; + + private static final double CONTACT_ICON_SIZE = 10.5; + private static final double CONTACT_ICON_BASELINE_OFFSET = -1.35; + private static final String CONTACT_ICON_ROOT = + "/templates/cv/timeline-minimal/icons/"; + private static final Map CONTACT_ICON_CACHE = + new ConcurrentHashMap<>(); + + private static final List SUMMARY_KEYS = + List.of("summary", "professional summary", "profile"); + private static final List SKILL_KEYS = + List.of("technical skills", "skills"); + private static final List EDUCATION_KEYS = + List.of("education", "certifications"); + private static final List EXPERIENCE_KEYS = + List.of("experience", "professional experience", "employment", "work"); + private static final List PROJECT_KEYS = + List.of("projects", "project"); + private static final List ADDITIONAL_KEYS = + List.of("additional information", "additional"); + + private TimelineMinimal() { + } + + /** + * Builds the preset with its Timeline Minimal theme. + */ + public static DocumentTemplate create() { + return create(CvTheme.timelineMinimal()); + } + + /** + * Builds the preset with a caller-supplied theme. + */ + public static DocumentTemplate create(CvTheme theme) { + Objects.requireNonNull(theme, "theme"); + return new Template(theme); + } + + private static final class Template implements DocumentTemplate { + + private final CvTheme theme; + + Template(CvTheme theme) { + this.theme = theme; + } + + @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 width = document.canvas().innerWidth(); + List sections = doc.sectionsIn(Slot.MAIN); + + document.dsl() + .pageFlow() + .name("CvV2TimelineMinimalRoot") + .spacing(theme.spacing().pageFlowSpacing()) + .addRow("CvV2TimelineMinimalHeader", row -> row + .spacing(3) + .weights(1.00, 0.61) + .addSection("CvV2TimelineMinimalName", + section -> addNameBlock(section, doc.identity())) + .addSection("CvV2TimelineMinimalContact", + section -> addContact(section, doc.identity()))) + .addLine(line -> line + .name("CvV2TimelineMinimalHeaderRule") + .horizontal(width) + .color(theme.palette().rule()) + .thickness(theme.spacing().accentRuleWidth()) + .margin(DocumentInsets.zero())) + .addRow("CvV2TimelineMinimalBody", row -> addBodyRow(row, + List.of( + new ModulePlacement("Education", + SectionLookup.firstMatching(sections, + EDUCATION_KEYS), + 5), + new ModulePlacement("Skills", + SectionLookup.firstMatching(sections, + SKILL_KEYS), + 6), + new ModulePlacement("Expertise", + SectionLookup.firstMatching(sections, + PROJECT_KEYS), + 3), + new ModulePlacement("Languages", + SectionLookup.firstMatching(sections, + ADDITIONAL_KEYS), + 3)), + List.of( + new ModulePlacement("Professional Profile", + SectionLookup.firstMatching(sections, + SUMMARY_KEYS), + 1), + new ModulePlacement("Work Experience", + SectionLookup.firstMatching(sections, + EXPERIENCE_KEYS), + 4)), + TIMELINE_AXIS_HEIGHT)) + .build(); + } + + private void addNameBlock(SectionBuilder section, CvIdentity identity) { + section.spacing(4) + .addParagraph(paragraph -> paragraph + .text(spacedUpper(identity.name().full())) + .textStyle(nameStyle()) + .margin(DocumentInsets.zero())); + String jobTitle = identity.jobTitle(); + if (!jobTitle.isBlank()) { + section.addParagraph(paragraph -> paragraph + .text(jobTitle.toUpperCase(Locale.ROOT)) + .textStyle(jobTitleStyle()) + .margin(DocumentInsets.zero())); + } + } + + private void addBodyRow(RowBuilder row, + List sidebarModules, + List mainModules, + double axisHeight) { + row.spacing(16) + .weights(0.74, 0.12, 1.74) + .addSection("CvV2TimelineMinimalSidebar", sidebar -> { + sidebar.spacing(10); + for (ModulePlacement placement : sidebarModules) { + addSidebarModule(sidebar, placement.title(), + placement.section(), placement.limit()); + } + }) + .addSection("CvV2TimelineMinimalAxis", axis -> + TimelineAxisWidget.render(axis, + timelineAxisStyle(), axisHeight)) + .addSection("CvV2TimelineMinimalMain", main -> { + main.spacing(11); + for (ModulePlacement placement : mainModules) { + boolean bullets = placement.limit() > 1; + addMainModule(main, placement.title(), + placement.section(), bullets, placement.limit()); + } + }); + } + + private void addContact(SectionBuilder section, CvIdentity identity) { + section.spacing(3); + DocumentTextStyle textStyle = contactTextStyle(); + DocumentTextStyle fallbackIconStyle = fallbackIconStyle(); + for (ContactItem item : contactItems(identity)) { + section.addParagraph(paragraph -> paragraph + .textStyle(textStyle) + .align(TextAlign.RIGHT) + .link(item.linkOptions()) + .margin(DocumentInsets.zero()) + .rich(rich -> { + rich.style(item.text(), textStyle); + rich.plain(" "); + if (item.iconFile() != null) { + rich.image(contactIcon(item.iconFile()), + CONTACT_ICON_SIZE, + CONTACT_ICON_SIZE, + InlineImageAlignment.CENTER, + CONTACT_ICON_BASELINE_OFFSET, + item.linkOptions()); + } else { + rich.style(item.fallbackIcon(), + fallbackIconStyle); + } + })); + } + } + + private List contactItems(CvIdentity identity) { + if (identity == null) { + return List.of(); + } + List items = new ArrayList<>(); + addContactItem(items, "LOC", "location.png", + identity.contact().address(), null); + addContactItem(items, "TEL", "phone.png", + identity.contact().phone(), null); + String email = identity.contact().email(); + if (!email.isBlank()) { + addContactItem(items, "@", "email.png", email, + new DocumentLinkOptions("mailto:" + email)); + } + for (CvLink link : identity.links()) { + String label = link.label(); + if (label.isBlank()) { + continue; + } + String url = link.url(); + addContactItem(items, pickFallbackIcon(label), + pickIconFile(label), label, + url.isBlank() + ? null + : new DocumentLinkOptions(url.trim())); + } + return List.copyOf(items); + } + + private static void addContactItem(List items, + String fallbackIcon, + String iconFile, + String text, + DocumentLinkOptions linkOptions) { + if (text != null && !text.isBlank()) { + items.add(new ContactItem(fallbackIcon, iconFile, text, + linkOptions)); + } + } + + private DocumentImageData contactIcon(String iconFile) { + return DocumentImageData.fromBytes( + CONTACT_ICON_CACHE.computeIfAbsent(iconFile, + TimelineMinimal::readIconBytes)); + } + + private void addSidebarModule(SectionBuilder sidebar, String title, + CvSection section, int limit) { + List lines = sectionLines(section); + if (lines.isEmpty()) { + return; + } + sidebar.addSection("CvV2TimelineMinimalSidebar" + + SectionLookup.normalize(title), block -> { + block.spacing(6) + .addParagraph(paragraph -> paragraph + .text(title.toUpperCase(Locale.ROOT)) + .textStyle(sidebarTitleStyle()) + .margin(DocumentInsets.zero())); + for (String line : lines.stream().limit(limit).toList()) { + block.addParagraph(paragraph -> paragraph + .text(excerpt(line, 76)) + .textStyle(sidebarBodyStyle()) + .lineSpacing(1) + .margin(DocumentInsets.zero())); + } + block.addLine(line -> line + .horizontal(118) + .color(theme.palette().rule()) + .thickness(0.65) + .margin(DocumentInsets.top(5))); + }); + } + + /** + * Style applied to the central timeline axis. Drop a custom + * {@link TimelineAxisWidget.Style} here (or expose it through + * the theme) to swap the marker shape, sizing, or colours. + */ + private TimelineAxisWidget.Style timelineAxisStyle() { + return TimelineAxisWidget.Style.builder() + .marker(TimelineAxisWidget.Marker.CIRCLE) + .markerSize(TIMELINE_DOT) + .markerStroke(DocumentStroke.of( + theme.palette().banner(), + TIMELINE_MARKER_STROKE)) + .segmentCount(TIMELINE_SEGMENT_COUNT) + .lineColor(theme.palette().rule()) + .lineThickness(TIMELINE_LINE_THICKNESS) + .padding(new DocumentInsets(TIMELINE_TOP_PADDING, 0, 0, 0)) + .build(); + } + + private void addMainModule(SectionBuilder main, String title, + CvSection section, boolean bullets, + int limit) { + List lines = sectionLines(section); + if (lines.isEmpty()) { + return; + } + main.addSection("CvV2TimelineMinimalMain" + + SectionLookup.normalize(title), block -> { + block.spacing(5) + .addParagraph(paragraph -> paragraph + .text(title.toUpperCase(Locale.ROOT)) + .textStyle(mainTitleStyle()) + .margin(DocumentInsets.zero())); + if (bullets) { + for (String line : lines.stream().limit(limit).toList()) { + block.addParagraph(paragraph -> paragraph + .text(excerpt(line, 136)) + .textStyle(mainBulletStyle()) + .lineSpacing(1.2) + .bulletOffset("-") + .margin(DocumentInsets.zero())); + } + } else { + block.addParagraph(paragraph -> paragraph + .text(excerpt(lines.get(0), 245)) + .textStyle(mainBodyStyle()) + .lineSpacing(1.4) + .margin(DocumentInsets.zero())); + } + block.addLine(line -> line + .horizontal(300) + .color(theme.palette().rule()) + .thickness(0.65) + .margin(DocumentInsets.top(6))); + }); + } + + // -- style factories --------------------------------------------- + + private DocumentTextStyle nameStyle() { + return CvTextStyles.of(theme.typography().headlineFont(), + theme.typography().sizeHeadline(), + DocumentTextDecoration.DEFAULT, + theme.palette().ink()); + } + + private DocumentTextStyle jobTitleStyle() { + return CvTextStyles.of(theme.typography().headlineFont(), + 9.5, + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle contactTextStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeContact(), + DocumentTextDecoration.BOLD, + theme.palette().muted()); + } + + private DocumentTextStyle fallbackIconStyle() { + return CvTextStyles.of(theme.typography().headlineFont(), + 8.0, + DocumentTextDecoration.BOLD, + theme.palette().muted()); + } + + private DocumentTextStyle sidebarTitleStyle() { + return CvTextStyles.of(theme.typography().headlineFont(), + theme.typography().sizeEntryTitle(), + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle sidebarBodyStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeEntrySubtitle(), + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle mainTitleStyle() { + return CvTextStyles.of(theme.typography().headlineFont(), + theme.typography().sizeBanner(), + DocumentTextDecoration.BOLD, + theme.palette().ink()); + } + + private DocumentTextStyle mainBulletStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeBody(), + DocumentTextDecoration.DEFAULT, + theme.palette().ink()); + } + + private DocumentTextStyle mainBodyStyle() { + return CvTextStyles.of(theme.typography().bodyFont(), + theme.typography().sizeEntryDate(), + DocumentTextDecoration.DEFAULT, + theme.palette().ink()); + } + } + + // -- helpers ----------------------------------------------------------- + + /** + * Flattens a {@link CvSection} into a list of single-line strings + * suitable for the truncation-driven sidebar / main rendering. v2 + * {@code SectionDispatcher} would produce richly-styled multi-paragraph + * output, which is not what Timeline Minimal needs — its layout + * relies on knowing the exact line count so per-module + * {@code limit} can drop overflow without breaking the visual flow + * around the fixed-height timeline axis. + */ + private static List sectionLines(CvSection section) { + if (section == null || !SectionLookup.hasContent(section)) { + return List.of(); + } + List lines = new ArrayList<>(); + if (section instanceof ParagraphSection paragraph) { + addLines(lines, paragraph.body()); + } else if (section instanceof SkillsSection skills) { + for (SkillGroup group : skills.groups()) { + String label = group.category(); + String body = group.skillsInline(); + if (label.isBlank() && body.isBlank()) { + continue; + } + if (label.isBlank()) { + lines.add(body); + } else if (body.isBlank()) { + lines.add(label); + } else { + lines.add(label + ": " + body); + } + } + } else if (section instanceof EntriesSection entries) { + for (CvEntry entry : entries.entries()) { + String header = entry.title(); + String subtitle = entry.subtitle(); + String dates = entry.date(); + String body = entry.body(); + StringBuilder line = new StringBuilder(); + if (!header.isBlank()) { + line.append(header); + } + if (!subtitle.isBlank()) { + if (line.length() > 0) { + line.append(" | "); + } + line.append(subtitle); + } + if (!dates.isBlank()) { + if (line.length() > 0) { + line.append(" - "); + } + line.append(dates); + } + if (line.length() > 0) { + lines.add(line.toString()); + } + if (!body.isBlank()) { + addLines(lines, body); + } + } + } else if (section instanceof RowsSection rows) { + for (CvRow row : rows.rows()) { + String label = row.label(); + String body = row.body(); + if (label.isBlank() && body.isBlank()) { + continue; + } + if (label.isBlank()) { + lines.add(body); + } else if (body.isBlank()) { + lines.add(label); + } else { + lines.add(label + ": " + body); + } + } + } + return List.copyOf(lines); + } + + private static void addLines(List lines, String value) { + for (String line : safe(value).split("\\R")) { + String clean = MarkdownInline.plainText(line).trim(); + if (!clean.isBlank()) { + lines.add(clean); + } + } + } + + private static byte[] readIconBytes(String iconFile) { + try (InputStream input = TimelineMinimal.class.getResourceAsStream( + CONTACT_ICON_ROOT + iconFile)) { + if (input == null) { + throw new IllegalStateException( + "Missing timeline minimal contact icon: " + iconFile); + } + return input.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException( + "Failed to read timeline minimal contact icon: " + iconFile, + e); + } + } + + private static String pickIconFile(String label) { + String normalized = SectionLookup.normalize(label); + if (normalized.contains("linkedin")) { + return "linkedin.png"; + } + if (normalized.contains("github")) { + return "github.png"; + } + if (normalized.contains("dribbble")) { + return "dribbble.png"; + } + if (normalized.contains("google")) { + return "google.png"; + } + return null; + } + + private static String pickFallbackIcon(String label) { + String normalized = SectionLookup.normalize(label); + if (normalized.contains("linkedin")) { + return "in"; + } + if (normalized.contains("github")) { + return "GH"; + } + return "@"; + } + + private static String spacedUpper(String value) { + String upper = safe(value).toUpperCase(Locale.ROOT); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < upper.length(); i++) { + char current = upper.charAt(i); + builder.append(current); + if (Character.isWhitespace(current)) { + builder.append(" "); + } + if (Character.isLetter(current) && i + 1 < upper.length() + && Character.isLetter(upper.charAt(i + 1))) { + builder.append(' '); + } + } + return builder.toString(); + } + + private static String excerpt(String value, int maxChars) { + String clean = MarkdownInline.plainText(value) + .replaceAll("\\s+", " ").trim(); + if (clean.length() <= maxChars) { + return clean; + } + int boundary = clean.lastIndexOf(' ', maxChars - 1); + int end = boundary > maxChars / 2 ? boundary : maxChars - 1; + return clean.substring(0, end).trim() + "..."; + } + + private static String safe(String value) { + return value == null ? "" : value; + } + + private record ModulePlacement(String title, CvSection section, int limit) { + } + + private record ContactItem(String fallbackIcon, 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 c3e96546..ae51315c 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 @@ -123,6 +123,21 @@ public static CvPalette editorialBlue() { DocumentColor.rgb(193, 201, 211)); } + /** + * Timeline Minimal palette: an all-grey scale ported from the v1 + * {@code TimelineMinimalCvTemplateComposer} — medium-grey ink, + * softer grey for metadata + body bullets, pale rule for the + * timeline axis and module underlines, and the dot token reused + * for the three circles of the central timeline axis. + */ + public static CvPalette timelineMinimal() { + return new CvPalette( + DocumentColor.rgb(74, 74, 74), // ink — V1 INK + DocumentColor.rgb(122, 122, 122), // muted — V1 SOFT + DocumentColor.rgb(195, 195, 195), // rule — V1 RULE + DocumentColor.rgb(170, 170, 170)); // banner — V1 DOT (reused as "timeline accent") + } + /** * Panel palette ported from the v1 {@code PanelCvTemplateComposer} * (ProductLeader tokens): body slate ink, slightly lighter 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 477a6279..73e237bc 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,30 @@ public static CvSpacing editorialBlue() { 3.0); // entrySeparation } + /** + * Spacing for the Timeline Minimal preset: tight 3-column layout + * with a fixed-width axis between the sidebar and main column. + * Body content is text-only (no cards / banners), so banner + * tokens are unused but kept at neutral defaults. + */ + public static CvSpacing timelineMinimal() { + return new CvSpacing( + 12, // pageFlowSpacing (gap between header row + rule + body row) + 5, // sectionBodySpacing + DocumentInsets.zero(), // sectionBodyPadding + DocumentInsets.zero(), // headlinePadding + DocumentInsets.zero(), // contactPadding + 0.0, // bannerCornerRadius (unused) + 0.0, // bannerInnerPadding (unused) + DocumentInsets.zero(), // bannerMargin (unused) + 0.8, // accentRuleWidth (header underline) + 1.0, // paragraphMarginTop + 8.0, // entryHeaderRowSpacing + 1.0, // entryTitleWeight + 0.45, // entryDateWeight + 2.0); // entrySeparation + } + /** * Spacing for the Panel preset: card-led layout that has to fit * Header / Profile / two-column row / Additional on one A4 page, 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 9d3e59ab..6387c883 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,22 @@ public static CvTheme editorialBlue() { CvDecoration.classic()); } + /** + * The "Timeline Minimal" look — Barlow Condensed display + Lato + * body, all-grey palette, spaced uppercase name, right-aligned + * contact stack with PNG icons, and a thin vertical timeline axis + * with three circles separating the sidebar from the main column. + * Visual signature ported from the v1 + * {@code TimelineMinimalCvTemplateComposer}. + */ + public static CvTheme timelineMinimal() { + return new CvTheme( + CvPalette.timelineMinimal(), + CvTypography.timelineMinimal(), + CvSpacing.timelineMinimal(), + CvDecoration.classic()); + } + /** * The "Panel" look — Poppins headlines + Lato body, pale teal * header card and module panels with thin teal stroke, deep 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 c3c84669..0eac04e4 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,26 @@ public static CvTypography editorialBlue() { 1.45); // line spacing } + /** + * Barlow Condensed headline + Lato body scale ported from the v1 + * {@code TimelineMinimalCvTemplateComposer}: 28pt spaced-caps + * masthead, 12.5pt sidebar module titles, 13.5pt main module + * titles, 7.5-7.9pt body. Compact sizes squeeze the 3-column + * sidebar / axis / main layout onto one page. + */ + public static CvTypography timelineMinimal() { + return new CvTypography( + FontName.BARLOW_CONDENSED, FontName.LATO, + 28.0, // headline (spaced uppercase masthead) + 7.8, // contact (right-aligned contact stack) + 13.5, // banner / main module title + 12.5, // entry title (reused as sidebar module title size) + 7.9, // entry date (body size in main) + 7.5, // entry subtitle (sidebar body size) + 7.8, // body + 1.2); // line spacing + } + /** * Poppins headline + Lato body scale ported from the v1 * {@code PanelCvTemplateComposer} (ProductLeader tokens): a 22pt diff --git a/src/main/java/com/demcha/compose/document/templates/widgets/TimelineAxisWidget.java b/src/main/java/com/demcha/compose/document/templates/widgets/TimelineAxisWidget.java new file mode 100644 index 00000000..6753f62b --- /dev/null +++ b/src/main/java/com/demcha/compose/document/templates/widgets/TimelineAxisWidget.java @@ -0,0 +1,266 @@ +package com.demcha.compose.document.templates.widgets; + +import com.demcha.compose.document.dsl.SectionBuilder; +import com.demcha.compose.document.style.DocumentColor; +import com.demcha.compose.document.style.DocumentInsets; +import com.demcha.compose.document.style.DocumentStroke; + +import java.util.Objects; + +/** + * Shared timeline-axis widget for template presets. + * + *

Draws a vertical line broken by a configurable number of markers + * (circles, squares, or none). Used by the CV Timeline Minimal preset + * to separate the sidebar from the main column, but the visual is + * generic enough to live in the shared widget layer — proposals, + * cover letters, or process / step documents can reuse the same + * widget by tweaking marker shape, spacing, and stroke colour.

+ * + *

Geometry

+ * + *

The widget renders {@code segmentCount} vertical line segments + * separated by {@code segmentCount - 1} markers. Total axis height is: + *

+ * + *
+ *   total = segmentCount * segmentLength + (segmentCount - 1) * markerSize
+ * 
+ * + *

Use the {@link #render(SectionBuilder, Style, double)} overload + * if you want to fix the total height and let the widget compute the + * segment length automatically — handy when the axis must match a + * sibling column's height.

+ * + *

Cross-page behaviour

+ * + *

The widget itself is a deterministic sequence of lines and + * markers; it does not coordinate with the layout engine on page + * boundaries. If a host section is split across pages by the engine, + * the line / marker sequence is split with it, and a marker that + * straddles a page break may be clipped. Callers that need an axis + * that visually restarts on each page should compose the widget + * inside a flow that controls page breaks explicitly (for example + * one {@code render(...)} call per logical page).

+ */ +public final class TimelineAxisWidget { + + /** Marker shape drawn between line segments. */ + public enum Marker { + /** A circle with the configured stroke + fill. */ + CIRCLE, + /** A square with the configured stroke + fill. */ + SQUARE, + /** No marker — line segments join directly. */ + NONE + } + + private TimelineAxisWidget() { + } + + /** + * Renders the timeline axis using the supplied {@link Style}. The + * total height is implied by {@code segmentCount * segmentLength + * + (segmentCount - 1) * markerSize}. + */ + public static void render(SectionBuilder host, Style style) { + Objects.requireNonNull(host, "host"); + Style safeStyle = style == null ? Style.builder().build() : style; + drawAxis(host, safeStyle); + } + + /** + * Renders the timeline axis with an explicit overall height. The + * widget keeps the supplied {@code marker}, {@code markerSize} + * and {@code segmentCount} and adjusts {@code segmentLength} so + * the rendered axis is exactly {@code totalHeight} tall (subject + * to non-negative segment lengths — short axes with many markers + * fall back to zero-length segments). + * + * @param host host section receiving the axis + * @param style configured style; only {@code segmentLength} + * is recomputed + * @param totalHeight target total height of the axis + */ + public static void render(SectionBuilder host, Style style, + double totalHeight) { + Objects.requireNonNull(host, "host"); + Style safeStyle = style == null ? Style.builder().build() : style; + int markers = Math.max(0, safeStyle.segmentCount() - 1); + double markerOverhead = markers * safeStyle.markerSize(); + double segmentLength = Math.max(0.0, + (totalHeight - markerOverhead) / safeStyle.segmentCount()); + Style adjusted = safeStyle.toBuilder() + .segmentLength(segmentLength) + .build(); + drawAxis(host, adjusted); + } + + private static void drawAxis(SectionBuilder host, Style style) { + host.spacing(0).padding(style.padding()); + int segments = style.segmentCount(); + double lineLeftOffset = Math.max(0.0, + (style.markerSize() - style.lineThickness()) / 2.0); + for (int i = 0; i < segments; i++) { + host.addLine(line -> line + .vertical(style.segmentLength()) + .color(style.lineColor()) + .thickness(style.lineThickness()) + .margin(new DocumentInsets(0, 0, 0, lineLeftOffset))); + if (i < segments - 1) { + renderMarker(host, style); + } + } + } + + private static void renderMarker(SectionBuilder host, Style style) { + DocumentStroke stroke = style.markerStroke() != null + ? style.markerStroke() + : (style.lineColor() != null + ? DocumentStroke.of(style.lineColor(), 0.8) + : null); + DocumentColor fill = style.markerFillColor() != null + ? style.markerFillColor() + : DocumentColor.WHITE; + switch (style.marker()) { + case CIRCLE -> host.addCircle(style.markerSize(), circle -> { + if (stroke != null) { + circle.stroke(stroke); + } + circle.fillColor(fill); + }); + case SQUARE -> host.addShape(shape -> { + shape.name("TimelineAxisMarkerSquare") + .size(style.markerSize(), style.markerSize()) + .fillColor(fill) + .margin(DocumentInsets.zero()); + if (stroke != null) { + shape.stroke(stroke); + } + }); + case NONE -> { + // No marker — the next line segment starts immediately. + } + } + } + + /** + * Visual configuration for {@link TimelineAxisWidget}. + * + * @param marker shape drawn between segments + * @param markerSize diameter (CIRCLE) or side length (SQUARE) + * @param markerFillColor fill colour of the marker; {@code null} + * falls back to {@link DocumentColor#WHITE} + * @param markerStroke stroke around the marker; {@code null} + * falls back to {@code lineColor} at 0.8pt + * @param segmentLength length of each vertical line segment + * @param segmentCount number of segments (at least 1); + * markers between = {@code segmentCount - 1} + * @param lineColor colour of every line segment + * @param lineThickness thickness of every line segment + * @param padding inset applied to the host section + * before any drawing + */ + public record Style(Marker marker, + double markerSize, + DocumentColor markerFillColor, + DocumentStroke markerStroke, + double segmentLength, + int segmentCount, + DocumentColor lineColor, + double lineThickness, + DocumentInsets padding) { + + public Style { + marker = marker == null ? Marker.CIRCLE : marker; + markerSize = Math.max(0.0, markerSize); + segmentLength = Math.max(0.0, segmentLength); + segmentCount = Math.max(1, segmentCount); + lineThickness = lineThickness <= 0.0 ? 0.75 : lineThickness; + padding = padding == null ? DocumentInsets.zero() : padding; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder() + .marker(marker) + .markerSize(markerSize) + .markerFillColor(markerFillColor) + .markerStroke(markerStroke) + .segmentLength(segmentLength) + .segmentCount(segmentCount) + .lineColor(lineColor) + .lineThickness(lineThickness) + .padding(padding); + } + + public static final class Builder { + private Marker marker = Marker.CIRCLE; + private double markerSize = 7.0; + private DocumentColor markerFillColor = DocumentColor.WHITE; + private DocumentStroke markerStroke; + private double segmentLength = 150.0; + private int segmentCount = 4; + private DocumentColor lineColor; + private double lineThickness = 0.75; + private DocumentInsets padding = DocumentInsets.zero(); + + private Builder() { + } + + public Builder marker(Marker value) { + this.marker = value; + return this; + } + + public Builder markerSize(double value) { + this.markerSize = value; + return this; + } + + public Builder markerFillColor(DocumentColor value) { + this.markerFillColor = value; + return this; + } + + public Builder markerStroke(DocumentStroke value) { + this.markerStroke = value; + return this; + } + + public Builder segmentLength(double value) { + this.segmentLength = value; + return this; + } + + public Builder segmentCount(int value) { + this.segmentCount = value; + return this; + } + + public Builder lineColor(DocumentColor value) { + this.lineColor = value; + return this; + } + + public Builder lineThickness(double value) { + this.lineThickness = value; + return this; + } + + public Builder padding(DocumentInsets value) { + this.padding = value; + return this; + } + + public Style build() { + return new Style(marker, markerSize, markerFillColor, + markerStroke, segmentLength, segmentCount, + lineColor, lineThickness, padding); + } + } + } +} diff --git a/src/main/resources/templates/cv/timeline-minimal/icons/github.png b/src/main/resources/templates/cv/timeline-minimal/icons/github.png index ed809bdf62e8f0b515ff0b72b3c05a42abdbb9a4..a4ec311c87390059146f8e79c1b42210cd433c68 100644 GIT binary patch literal 5339 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 IbzGwV2Y=+C!vFvP delta 1668 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 delta 1198 zcmeB|KFz7v8Q|y6%O%Cdz`(%k>ERLtq#Zz*gAGWo$}GG$QBl91Ey>&61;_`2pU%7M zffQ$fM`SSr17iai%Rf1-1QcX1@$_|Nf5jrlE5q*B=HChwQY~?fC`m~yNwrEYN(E93 zMg~S^x&|h?M&==g7FNcFRt6^81_o9J2EP|OiK1x8%}>cps|0DNN7G>n(V^4%Q5L8} z18zl0W^QV6Nn&mR+!kYl5JO`tBMU17Gl-s7m75MQFfdQ^ba4!+xb^n-*=`wEiQ^y7 zpE>4hmL?kC^x&X|m&!$%l4iG$tSrfTaxQ}U_iXBa`u`~6w0v>2S)qeHQdChv?ubHG z-ac8SMdJLAPCF_FCYvRMm6zLu=tidRs@_$dcImG1-`1qIcdK4i)y2ksy%KzP%a$!? zjOH3XUoE{ikZ1OBXN#t6X$>d-^bd-5N3$j#`dq=My>XG+@~Ii8YXlkl9>nlU*{>_n zyJg~F?%Lp9zahe9HKVWehffnSg12YrUOu)>@YK3T z*>k%7e}5#fZq=<7-iik@_cWx=={>&fF{8|3*-&XSuDKZzx2A0BN;f_we@K7T{9nH= zZ(?dr5L^Di@7T}tg*AuFGjpbhKIm7h+;_n3>M>uhhWg?sk&~0_6@%DMacq#E&VRDl z`q?V8ydb7Ky@w^A=O26wG}V)_?BR+B(ck?SSAR6hTYYQ8o+)3M^F)_T=S^ksW zXY@k)ON;4|y*rfTesilA)-&DTx#QZ6R_Ww>hj-j;srs;C>w(I|i&Y#Zt5R&`7YO{ zwLitZp!x^r`Nxup4?|~J^)SjNTGhH%gnZi6_`T)cr&-r2dHtqQLjr+oIVDDsd^|KNX- z`?_mGb47v2yx*z$1?#x#T$NdGsPEVnSs(NM|GTddE4mqO7e6~Gwb?88`Bm43J&acp z-NeKe>M`{+yM1p6YcS+AxZLlcxy-ZSo1ljBp)H&NGr}5HMaORCxUAx^w#q^DJ=+wa z1wIZ-wAb9e&in4tmllivefu7Vb4yEkp1xXoXUgMU)`jB#d<@EBZ%ovyaX!tlK_RDc z`Cg{_%Z3uice``cE-#Gid;RX presets() { (Supplier>) Executive::create), Arguments.of("panel", Panel.RECOMMENDED_MARGIN, - (Supplier>) Panel::create)); + (Supplier>) Panel::create), + Arguments.of("timeline_minimal", + TimelineMinimal.RECOMMENDED_MARGIN, + (Supplier>) TimelineMinimal::create)); } /** diff --git a/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimalSmokeTest.java b/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimalSmokeTest.java new file mode 100644 index 00000000..96dbf381 --- /dev/null +++ b/src/test/java/com/demcha/compose/document/templates/cv/v2/presets/TimelineMinimalSmokeTest.java @@ -0,0 +1,91 @@ +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.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 Timeline Minimal preset. Covers the icon + * contact stack and section-line flattening across every + * {@code CvSection} subtype. + */ +class TimelineMinimalSmokeTest { + + @Test + void exposes_stable_identity() { + DocumentTemplate template = TimelineMinimal.create(); + assertThat(template.id()).isEqualTo("timeline-minimal"); + assertThat(template.displayName()).isEqualTo("Timeline Minimal"); + } + + @Test + void default_factory_renders_full_document() throws Exception { + renderAndAssertNonEmpty(TimelineMinimal.create(), fullDocument()); + } + + @Test + void custom_theme_factory_renders() throws Exception { + renderAndAssertNonEmpty(TimelineMinimal.create(CvTheme.timelineMinimal()), + fullDocument()); + } + + private static void renderAndAssertNonEmpty( + DocumentTemplate template, + CvDocument doc) throws Exception { + try (DocumentSession session = GraphCompose.document() + .pageSize(420, 595) + .margin(DocumentInsets.of(22)) + .create()) { + template.compose(session, doc); + assertThat(session.roots()).isNotEmpty(); + } + } + + private static CvDocument fullDocument() { + return CvDocument.builder() + .identity(CvIdentity.builder() + .name("Jane", "Doe") + .jobTitle("Backend 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("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/timeline_minimal-page-0.png b/src/test/resources/visual-baselines/cv-v2-layered/timeline_minimal-page-0.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1fd18f75d259c95219a1e825d3f285d6e950c9 GIT binary patch literal 73808 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