diff --git a/CHANGELOG.md b/CHANGELOG.md
index f04c042f6..cef198cb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -96,7 +96,8 @@ follow semantic versioning; release dates are ISO 8601.
terracotta square and no dash under its heading, the other a disc and a dash, which
is how the sheet tells two lists of one-liners apart. Each fact and each project
takes the mark its entry names in `CvEntry.icon()` from this preset's own vocabulary,
- a project title is a link when its entry carries one, and the monogram is drawn from
+ every title it draws — a role, a project, a degree, a credential — is a link when its
+ entry carries one, and the monogram is drawn from
the name's own initials rather than a field of its own — a document states its name
once, and a monogram that could disagree with it would be a second place to keep
true. **A link in the contact block is drawn as its own label with the address behind
@@ -111,8 +112,8 @@ follow semantic versioning; release dates are ISO 8601.
`AtomicNodeTooLargeException` rather than flowing or dropping entries. Guarded by a
smoke test (including the unknown-mark data error, an entry with no mark, an identity
with no links, a document with nothing but an identity, the monogram, the link
- targets, the four contact rows sharing one axis and the one-page limit), an exact
- layout snapshot and a pixel-parity gate;
+ targets on every kind of title, the four contact rows sharing one axis and the
+ one-page limit), an exact layout snapshot and a pixel-parity gate;
the examples showcase gains `cv-terracotta-rail-v2`.
- **The first invoice preset that paginates what it ports: `LumaStudioInvoice`.** A
diff --git a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java
index 1cbf9fc03..86332096f 100644
--- a/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java
+++ b/qa/src/test/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailSmokeTest.java
@@ -173,6 +173,46 @@ void everyContactRowStartsOnTheSameAxis() throws Exception {
}
}
+ @Test
+ void everyTitleBecomesALinkWhenItsEntryCarriesOne() throws Exception {
+ // A role, a degree and a credential, each pointing somewhere. None of
+ // it moves a pixel or a layout node, so this is the only thing that
+ // would notice the annotations going missing.
+ List
Every title the preset draws — a role, a project, a degree, a + * credential — becomes a link when its entry carries {@code CvEntry.link()}. + * It costs the layout nothing, because a link is an annotation rather than + * ink, so a linked title and a plain one are the same line. The closing facts + * are the exception: their bold line is a label for the values under it — + * "Languages:", "Availability:" — rather than the name of something a reader + * could open.
* *The email, the phone and each link are reachable from the PDF, with the
* {@code mailto:} and {@code tel:} targets built from the values. A link is
diff --git a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java
index b9a8aa33e..50e82e123 100644
--- a/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java
+++ b/templates/src/main/java/com/demcha/compose/document/templates/cv/presets/TerracottaRailAside.java
@@ -213,12 +213,12 @@ private static void renderBulletedSkills(SectionBuilder side, SkillsSection sect
String name = prefix + "_" + index++;
if (discBullet) {
bulletLine(block, name, p -> p.dot(3.0, ACCENT), skill.name(),
- INK, DETAIL_SIZE);
+ INK, DETAIL_SIZE, null);
} else {
bulletLine(block, name,
p -> inlineIcon(p, TerracottaRailIcons.SQUARE,
TerracottaRailIcons.BULLET_SIZE),
- skill.name(), INK, DETAIL_SIZE);
+ skill.name(), INK, DETAIL_SIZE, null);
}
}
}
@@ -232,8 +232,9 @@ private static void renderCertifications(SectionBuilder side, EntriesSection sec
headingWithDash(block, section.title(), SIDEBAR_HEADING_SPACER, SIDEBAR_DASH_WIDTH);
List
The title carries the entry's link when it has one. That costs the + * layout nothing — a link is an annotation rather than ink — so a linked + * title and a plain one are the same line.
+ * + * @param name the node name this line's parts are built from + * @param title the entry's title + * @param date the date, set flush right + * @param width the line's width + * @param dateShare how much of that width the date takes + * @param link the target the title points at, blank for none + * @return the line node */ static DocumentNode titleAndDate(String name, String title, String date, - double width, double dateShare) { + double width, double dateShare, String link) { double dateColumn = width * dateShare; + ParagraphBuilder titleCell = new ParagraphBuilder() + .name(name + "_Title") + .text(title) + .lineSpacing(0) + .textStyle(text(ITEM_TITLE_SIZE, INK, true)); + if (link != null && !link.isBlank()) { + titleCell.link(new DocumentLinkOptions(link)); + } return new TableBuilder() .name(name) .width(width) @@ -209,12 +230,7 @@ static DocumentNode titleAndDate(String name, String title, String date, .stroke(NO_BORDER) .build()) .rowCells( - DocumentTableCell.node(new ParagraphBuilder() - .name(name + "_Title") - .text(title) - .lineSpacing(0) - .textStyle(text(ITEM_TITLE_SIZE, INK, true)) - .build()), + DocumentTableCell.node(titleCell.build()), DocumentTableCell.node(new ParagraphBuilder() .name(name + "_Period") .text(date) @@ -246,14 +262,21 @@ static void layeredRow(SectionBuilder parent, String name, double marginTop, .layer(layer.build(), LayerAlign.TOP_LEFT, 0)); } - /** A bullet line: a mark, a gap, and the item beside it. */ + /** + * A bullet line: a mark, a gap, and the item beside it — carrying a link + * when one is given. + */ static void bulletLine(SectionBuilder block, String name, Consumer