22
33import com .demcha .compose .GraphCompose ;
44import com .demcha .compose .document .api .DocumentSession ;
5+ import com .demcha .compose .document .api .PageMarginRule ;
56import com .demcha .compose .document .chart .AxisSpec ;
67import com .demcha .compose .document .chart .ChartData ;
78import com .demcha .compose .document .chart .ChartSize ;
2526import com .demcha .examples .support .ExampleVersion ;
2627
2728import java .nio .file .Path ;
29+ import java .util .List ;
2830import java .util .Locale ;
2931
3032/**
4648 */
4749public final class LinkedInCarouselExample {
4850
49- /** LinkedIn renders document posts in a 4:5 frame; this is that frame. */
50- public static final double PAGE_WIDTH = 1080 ;
51- /** LinkedIn renders document posts in a 4:5 frame; this is that frame. */
52- public static final double PAGE_HEIGHT = 1350 ;
51+ /**
52+ * LinkedIn renders document posts in a 4:5 frame; this is that frame, sized
53+ * so the opening slide can carry {@link SocialCardExample}'s card at its
54+ * native {@value SocialCardExample#CARD_WIDTH}pt width without scaling.
55+ */
56+ public static final double PAGE_WIDTH = SocialCardExample .CARD_WIDTH ;
57+ /** Four-by-five against {@link #PAGE_WIDTH}. */
58+ public static final double PAGE_HEIGHT = PAGE_WIDTH * 5 / 4 ;
59+
60+ /**
61+ * Everything below was drawn against a 1080-wide page. Rather than restate
62+ * every size, the type helpers scale by this ratio, so the slides keep their
63+ * proportions on the wider frame.
64+ */
65+ private static final double SCALE = PAGE_WIDTH / 1080.0 ;
5366
54- private static final double MARGIN = 80 ;
67+ private static final double MARGIN = 80 * SCALE ;
5568 private static final double CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN ;
5669
5770 private static final DocumentColor NIGHT = DocumentColor .rgb (9 , 12 , 27 );
@@ -66,7 +79,7 @@ public final class LinkedInCarouselExample {
6679 private static final DocumentColor AMBER = DocumentColor .rgb (230 , 160 , 68 );
6780
6881 /** Uniform padding inside every card, as passed to {@code softPanel}. */
69- private static final double CARD_PAD = 30 ;
82+ private static final double CARD_PAD = 30 * SCALE ;
7083
7184 /**
7285 * Width available inside a card.
@@ -173,9 +186,17 @@ static void compose(DocumentSession document) {
173186 .producer ("GraphCompose" )
174187 .build ());
175188
189+ // The opening slide is the social card at native size. Its own page gets
190+ // no margin so the artwork reaches the edges, and a spacer centres it in
191+ // the taller frame - both share the same night background, so the space above
192+ // and below reads as deliberate letterboxing rather than a gap.
193+ document .pageMargins (List .of (PageMarginRule .page (1 , DocumentInsets .zero ())));
194+
176195 document .pageFlow ()
177196 .name ("Carousel" )
178197 .spacing (0 )
198+ .addSection ("Card" , LinkedInCarouselExample ::cardSlide )
199+ .addPageBreak (b -> b .name ("ToCover" ))
179200 .addSection ("Cover" , LinkedInCarouselExample ::cover )
180201 .addPageBreak (b -> b .name ("ToBackend" ))
181202 .addSection ("Backend" , LinkedInCarouselExample ::backendSlide )
@@ -190,15 +211,22 @@ static void compose(DocumentSession document) {
190211
191212 // ───────────────────────── slides ─────────────────────────
192213
214+ private static void cardSlide (SectionBuilder slide ) {
215+ slide .spacing (0 );
216+ slide .addSpacer (sp -> sp .width (PAGE_WIDTH )
217+ .height ((PAGE_HEIGHT - SocialCardExample .CARD_HEIGHT ) / 2 ));
218+ slide .add (SocialCardExample .scene ());
219+ }
220+
193221 private static void cover (SectionBuilder slide ) {
194- slide .spacing (56 );
222+ slide .spacing (56 * SCALE );
195223 eyebrow (slide , "GRAPHCOMPOSE " + ExampleVersion .currentLine (), VIOLET_LIGHT );
196224 slide .addParagraph (p -> p
197225 .text ("One composition.\n Two formats." )
198226 .textStyle (headline (132 ))
199227 .lineSpacing (1.03 )
200228 .margin (DocumentInsets .zero ()));
201- slide .addShape (shape -> shape .size (160 , 7 ).fillColor (VIOLET )
229+ slide .addShape (shape -> shape .size (160 * SCALE , 7 * SCALE ).fillColor (VIOLET )
202230 .margin (DocumentInsets .symmetric (14 , 0 )));
203231 slide .addParagraph (p -> p
204232 .text ("A Java DSL describes the document. The engine resolves layout "
@@ -219,7 +247,7 @@ private static void cover(SectionBuilder slide) {
219247 }
220248
221249 private static void backendSlide (SectionBuilder slide ) {
222- slide .spacing (30 );
250+ slide .spacing (30 * SCALE );
223251 eyebrow (slide , "WHAT " + ExampleVersion .currentLine () + " ADDS" , MINT );
224252 slide .addParagraph (p -> p
225253 .text ("PowerPoint is a\n first-class backend." )
@@ -245,7 +273,7 @@ private static void backendSlide(SectionBuilder slide) {
245273 }
246274
247275 private static void numbersSlide (SectionBuilder slide , EngineDeckData .BenchRun bench ) {
248- slide .spacing (40 );
276+ slide .spacing (40 * SCALE );
249277 eyebrow (slide , "MEASURED, NOT CLAIMED" , AMBER );
250278 slide .addParagraph (p -> p
251279 .text ("A " + TIER + "-row report" )
@@ -279,7 +307,7 @@ private static void numbersSlide(SectionBuilder slide, EngineDeckData.BenchRun b
279307 }
280308
281309 private static void guaranteesSlide (SectionBuilder slide ) {
282- slide .spacing (30 );
310+ slide .spacing (30 * SCALE );
283311 eyebrow (slide , "HOW IT STAYS HONEST" , VIOLET_LIGHT );
284312 slide .addParagraph (p -> p
285313 .text ("Guarantees you\n can run." )
@@ -304,8 +332,8 @@ private static void guaranteesSlide(SectionBuilder slide) {
304332 }
305333
306334 private static void closeSlide (SectionBuilder slide ) {
307- slide .spacing (30 );
308- eyebrow (slide , "OPEN SOURCE · APACHE-2.0 " , MINT );
335+ slide .spacing (30 * SCALE );
336+ eyebrow (slide , "OPEN SOURCE · MIT " , MINT );
309337 slide .addParagraph (p -> p
310338 .text ("Try it." )
311339 .textStyle (headline (132 ))
@@ -315,9 +343,15 @@ private static void closeSlide(SectionBuilder slide) {
315343 .stroke (DocumentStroke .of (LINE , 1 ))
316344 .spacing (4 );
317345 card .addSpacer (sp -> sp .width (CARD_INNER ).height (0 ));
346+ // Split on the colon rather than set in one line: the coordinate is
347+ // wider than the card at this size and broke mid-artifact.
348+ card .addParagraph (p -> p
349+ .text ("io.github.demchaav" )
350+ .textStyle (mono (31 , ON_DARK_MUTED ))
351+ .margin (DocumentInsets .zero ()));
318352 card .addParagraph (p -> p
319- .text ("io.github.demchaav: graph-compose-bundle" )
320- .textStyle (mono (37 , ON_DARK ))
353+ .text ("graph-compose-bundle" )
354+ .textStyle (mono (40 , ON_DARK ))
321355 .margin (DocumentInsets .zero ()));
322356 // Deliberately not the reactor version: this file renders from a
323357 // -SNAPSHOT tree, and printing that would advertise a coordinate that
@@ -372,7 +406,7 @@ private static void point(SectionBuilder slide, String title, DocumentColor acce
372406 .text (title )
373407 .textStyle (DocumentTextStyle .builder ()
374408 .fontName (FontName .HELVETICA_BOLD )
375- .size (40 )
409+ .size (40 * SCALE )
376410 .color (ON_DARK )
377411 .build ())
378412 .margin (DocumentInsets .zero ()));
@@ -394,7 +428,7 @@ private static void formatChip(RowBuilder row, String name, String lane,
394428 .text (name )
395429 .textStyle (DocumentTextStyle .builder ()
396430 .fontName (FontName .HELVETICA_BOLD )
397- .size (56 )
431+ .size (56 * SCALE )
398432 .color (accent )
399433 .build ())
400434 .align (TextAlign .CENTER )
@@ -421,7 +455,7 @@ private static void ratio(RowBuilder row, String value, String label, DocumentCo
421455 .text (value )
422456 .textStyle (DocumentTextStyle .builder ()
423457 .fontName (FontName .HELVETICA_BOLD )
424- .size (64 )
458+ .size (64 * SCALE )
425459 .color (accent )
426460 .build ())
427461 .margin (DocumentInsets .zero ()));
@@ -446,7 +480,7 @@ private static ChartSpec latencyChart(EngineDeckData.BenchRun bench) {
446480 .showCategoryLabels (true )
447481 .legend (LegendPosition .NONE )
448482 .valueLabels (ValueLabelMode .OUTSIDE )
449- .size (ChartSize .fixedHeight (470 ))
483+ .size (ChartSize .fixedHeight (470 * SCALE ))
450484 .build ();
451485 }
452486
@@ -479,19 +513,19 @@ private static String snapshotDate(EngineDeckData.BenchRun bench) {
479513 private static DocumentTextStyle headline (double size ) {
480514 return DocumentTextStyle .builder ()
481515 .fontName (FontName .HELVETICA_BOLD )
482- .size (size )
516+ .size (size * SCALE )
483517 .color (ON_DARK )
484518 .build ();
485519 }
486520
487521 private static DocumentTextStyle body (double size , DocumentColor color ) {
488- return DocumentTextStyle .builder ().size (size ).color (color ).build ();
522+ return DocumentTextStyle .builder ().size (size * SCALE ).color (color ).build ();
489523 }
490524
491525 private static DocumentTextStyle mono (double size , DocumentColor color ) {
492526 return DocumentTextStyle .builder ()
493527 .fontName (FontName .COURIER )
494- .size (size )
528+ .size (size * SCALE )
495529 .color (color )
496530 .build ();
497531 }
0 commit comments