@@ -60,6 +60,8 @@ public final class TwinOutputExample {
6060 private static final DocumentColor ON_DARK = DocumentColor .rgb (245 , 247 , 252 );
6161 private static final DocumentColor ON_DARK_MUTED = DocumentColor .rgb (177 , 185 , 207 );
6262 private static final DocumentColor CODE_TXT = DocumentColor .rgb (190 , 195 , 214 );
63+ private static final DocumentColor CODE_TYPE = DocumentColor .rgb (150 , 190 , 255 );
64+ private static final DocumentColor CODE_STR = DocumentColor .rgb (120 , 204 , 170 );
6365
6466 // Accent family.
6567 private static final DocumentColor VIOLET = DocumentColor .rgb (124 , 92 , 252 );
@@ -181,38 +183,75 @@ private static DocumentNode logoLockup() {
181183 .build ();
182184 }
183185
184- private static DocumentNode codeCard () {
185- // {indent level, code, trailing comment} — indentation is positional
186- // because paragraph text trims leading whitespace.
187- String [][] lines = {
188- {"0" , "Path deck = Path.of(\" twin-output.pptx\" );" , "" },
189- {"0" , "try (DocumentSession doc = GraphCompose" , "" },
190- {"2" , ".document(Path.of(\" twin-output.pdf\" ))" , "" },
191- {"2" , ".pageSize(DocumentPageSize.SLIDE_16_9)" , "" },
192- {"2" , ".create()) {" , "" },
193- {"1" , "compose(doc);" , "// one description" },
194- {"1" , "doc.buildPdf();" , "// print-ready PDF" },
195- {"1" , "doc.buildPptx(deck);" , "// editable PowerPoint" },
196- {"0" , "}" , "" },
186+ /** One syntax-coloured slice of a code line: {@code kind} selects the style. */
187+ private record CodeRun (char kind , String text ) {
188+ }
189+
190+ private static CodeRun t (String text ) {
191+ return new CodeRun ('t' , text ); // plain code
192+ }
193+
194+ private static CodeRun c (String text ) {
195+ return new CodeRun ('c' , text ); // class / type
196+ }
197+
198+ private static CodeRun s (String text ) {
199+ return new CodeRun ('s' , text ); // string literal
200+ }
201+
202+ private static CodeRun k (String text ) {
203+ return new CodeRun ('k' , text ); // keyword / hero call
204+ }
205+
206+ private static DocumentColor codeColor (char kind ) {
207+ return switch (kind ) {
208+ case 'c' -> CODE_TYPE ;
209+ case 's' -> CODE_STR ;
210+ case 'k' -> VIOLET_LIGHT ;
211+ default -> CODE_TXT ;
197212 };
213+ }
214+
215+ private static DocumentNode codeCard () {
216+ // {indent, runs, trailing comment} — indentation is positional because
217+ // paragraph text trims leading whitespace; each line is ONE paragraph
218+ // of styled inline runs, so both backends carry it as rich text (the
219+ // PPTX side lands one editable text frame with coloured runs per line).
220+ record CodeLine (int indent , List <CodeRun > runs , String note ) {
221+ }
222+ List <CodeLine > lines = List .of (
223+ new CodeLine (0 , List .of (c ("Path" ), t (" deck = " ), c ("Path" ), t (".of(" ),
224+ s ("\" twin-output.pptx\" " ), t (");" )), "" ),
225+ new CodeLine (0 , List .of (k ("try" ), t (" (" ), c ("DocumentSession" ), t (" doc = " ),
226+ c ("GraphCompose" )), "" ),
227+ new CodeLine (2 , List .of (t (".document(" ), c ("Path" ), t (".of(" ),
228+ s ("\" twin-output.pdf\" " ), t ("))" )), "" ),
229+ new CodeLine (2 , List .of (t (".pageSize(" ), c ("DocumentPageSize" ),
230+ t (".SLIDE_16_9)" )), "" ),
231+ new CodeLine (2 , List .of (t (".create()) {" )), "" ),
232+ new CodeLine (1 , List .of (t ("compose(doc);" )), "// one description" ),
233+ new CodeLine (1 , List .of (t ("doc." ), k ("buildPdf" ), t ("();" )), "// print-ready PDF" ),
234+ new CodeLine (1 , List .of (t ("doc." ), k ("buildPptx" ), t ("(deck);" )), "// editable PowerPoint" ),
235+ new CodeLine (0 , List .of (t ("}" )), "" ));
198236 ShapeContainerBuilder card = new ShapeContainerBuilder ()
199237 .name ("TwinCode" )
200238 .roundedRect (470 , 152 , 14 )
201239 .fillColor (DocumentColor .rgba (7 , 10 , 24 , 220 ))
202240 .stroke (DocumentStroke .of (DARK_LINE , 0.8 ));
203- for (int i = 0 ; i < lines .length ; i ++) {
241+ for (int i = 0 ; i < lines .size (); i ++) {
242+ CodeLine line = lines .get (i );
204243 double y = 12 + i * 14.6 ;
205- double indent = 20 + Integer .parseInt (lines [i ][0 ]) * 18 ;
206- card .position (new ParagraphBuilder ()
244+ ParagraphBuilder code = new ParagraphBuilder ()
207245 .name ("TwinCodeLine" + i )
208- .text (lines [i ][1 ])
209- .textStyle (mono (9.5 , CODE_TXT , false ))
210- .margin (DocumentInsets .zero ())
211- .build (), indent , y , LayerAlign .TOP_LEFT );
212- if (!lines [i ][2 ].isEmpty ()) {
246+ .margin (DocumentInsets .zero ());
247+ for (CodeRun run : line .runs ()) {
248+ code .inlineText (run .text (), mono (9.5 , codeColor (run .kind ()), false ));
249+ }
250+ card .position (code .build (), 20 + line .indent () * 18 , y , LayerAlign .TOP_LEFT );
251+ if (!line .note ().isEmpty ()) {
213252 card .position (new ParagraphBuilder ()
214253 .name ("TwinCodeNote" + i )
215- .text (lines [ i ][ 2 ] )
254+ .text (line . note () )
216255 .textStyle (mono (9.5 , MINT , false ))
217256 .margin (DocumentInsets .zero ())
218257 .build (), 250 , y , LayerAlign .TOP_LEFT );
0 commit comments