@@ -31,6 +31,16 @@ pub use anstyle_lossy::palette::Palette;
3131pub use anstyle_lossy:: palette:: VGA ;
3232pub use anstyle_lossy:: palette:: WIN10_CONSOLE ;
3333
34+ use std:: fmt:: Write as _;
35+
36+ /// Contains the different parts of a HTML rendered page.
37+ pub struct HtmlParts {
38+ /// Content that can be used directly in a `<style>` tag.
39+ pub style : String ,
40+ /// Content that can be put in the HTML body or any tag inside the `<body>`.
41+ pub body : String ,
42+ }
43+
3444/// Define the terminal-like settings for rendering output
3545#[ derive( Copy , Clone , Debug ) ]
3646pub struct Term {
@@ -108,8 +118,7 @@ impl Term {
108118 let bg_color = rgb_value ( self . bg_color , self . palette ) ;
109119 let font_family = self . font_family ;
110120
111- let line_height = 18 ;
112- let height = styled_lines. len ( ) * line_height + self . padding_px * 2 ;
121+ let height = styled_lines. len ( ) * LINE_HEIGHT + self . padding_px * 2 ;
113122 let max_width = styled_lines
114123 . iter ( )
115124 . map ( |l| l. iter ( ) . map ( |e| e. text . width ( ) ) . sum ( ) )
@@ -148,13 +157,13 @@ impl Term {
148157 }
149158 writeln ! ( & mut buffer, r#" .container {{"# ) . unwrap ( ) ;
150159 writeln ! ( & mut buffer, r#" padding: 0 10px;"# ) . unwrap ( ) ;
151- writeln ! ( & mut buffer, r#" line-height: {line_height }px;"# ) . unwrap ( ) ;
160+ writeln ! ( & mut buffer, r#" line-height: {LINE_HEIGHT }px;"# ) . unwrap ( ) ;
152161 writeln ! ( & mut buffer, r#" }}"# ) . unwrap ( ) ;
153162 write_effects_in_use ( & mut buffer, & elements) ;
154163 writeln ! ( & mut buffer, r#" tspan {{"# ) . unwrap ( ) ;
155164 writeln ! ( & mut buffer, r#" font: 14px {font_family};"# ) . unwrap ( ) ;
156165 writeln ! ( & mut buffer, r#" white-space: pre;"# ) . unwrap ( ) ;
157- writeln ! ( & mut buffer, r#" line-height: {line_height }px;"# ) . unwrap ( ) ;
166+ writeln ! ( & mut buffer, r#" line-height: {LINE_HEIGHT }px;"# ) . unwrap ( ) ;
158167 writeln ! ( & mut buffer, r#" }}"# ) . unwrap ( ) ;
159168 writeln ! ( & mut buffer, r#" </style>"# ) . unwrap ( ) ;
160169 writeln ! ( & mut buffer) . unwrap ( ) ;
@@ -169,7 +178,7 @@ impl Term {
169178 }
170179
171180 let text_x = self . padding_px ;
172- let mut text_y = self . padding_px + line_height ;
181+ let mut text_y = self . padding_px + LINE_HEIGHT ;
173182 writeln ! (
174183 & mut buffer,
175184 r#" <text xml:space="preserve" class="container {FG}">"#
@@ -200,7 +209,7 @@ impl Term {
200209 writeln ! ( & mut buffer) . unwrap ( ) ;
201210 writeln ! ( & mut buffer, r#"</tspan>"# ) . unwrap ( ) ;
202211
203- text_y += line_height ;
212+ text_y += LINE_HEIGHT ;
204213 }
205214 writeln ! ( & mut buffer, r#" </text>"# ) . unwrap ( ) ;
206215 writeln ! ( & mut buffer) . unwrap ( ) ;
@@ -214,23 +223,12 @@ impl Term {
214223 /// **Note:** Lines are not wrapped. This is intentional as this attempts to convey the exact
215224 /// output with escape codes translated to HTML elements.
216225 pub fn render_html ( & self , ansi : & str ) -> String {
217- use std:: fmt:: Write as _;
218-
219- const FG : & str = "fg" ;
220- const BG : & str = "bg" ;
221-
222226 let mut styled = adapter:: AnsiBytes :: new ( ) ;
223227 let mut elements = styled. extract_next ( ansi. as_bytes ( ) ) . collect :: < Vec < _ > > ( ) ;
224228 preprocess_invert_style ( & mut elements, self . bg_color , self . fg_color ) ;
225229
226230 let styled_lines = split_lines ( & elements) ;
227231
228- let fg_color = rgb_value ( self . fg_color , self . palette ) ;
229- let bg_color = rgb_value ( self . bg_color , self . palette ) ;
230- let font_family = self . font_family ;
231-
232- let line_height = 18 ;
233-
234232 let mut buffer = String :: new ( ) ;
235233 writeln ! ( & mut buffer, r#"<!DOCTYPE html>"# ) . unwrap ( ) ;
236234 writeln ! ( & mut buffer, r#"<html>"# ) . unwrap ( ) ;
@@ -247,73 +245,100 @@ impl Term {
247245 )
248246 . unwrap ( ) ;
249247 writeln ! ( & mut buffer, r#" <style>"# ) . unwrap ( ) ;
250- writeln ! ( & mut buffer, r#" .{FG} {{ color: {fg_color} }}"# ) . unwrap ( ) ;
251- writeln ! ( & mut buffer, r#" .{BG} {{ background: {bg_color} }}"# ) . unwrap ( ) ;
252- for ( name, rgb) in color_styles ( & elements, self . palette ) {
248+ self . render_classes ( & mut buffer, & elements) ;
249+ writeln ! ( & mut buffer, r#" </style>"# ) . unwrap ( ) ;
250+ writeln ! ( & mut buffer, r#"</head>"# ) . unwrap ( ) ;
251+ writeln ! ( & mut buffer) . unwrap ( ) ;
252+
253+ if !self . background {
254+ writeln ! ( & mut buffer, r#"<body>"# ) . unwrap ( ) ;
255+ } else {
256+ writeln ! ( & mut buffer, r#"<body class="{BG}">"# ) . unwrap ( ) ;
257+ }
258+ writeln ! ( & mut buffer) . unwrap ( ) ;
259+ self . render_content ( & mut buffer, styled_lines) ;
260+ writeln ! ( & mut buffer) . unwrap ( ) ;
261+
262+ writeln ! ( & mut buffer, r#"</body>"# ) . unwrap ( ) ;
263+ writeln ! ( & mut buffer, r#"</html>"# ) . unwrap ( ) ;
264+ buffer
265+ }
266+
267+ /// Returns the various parts needed to create an HTML page.
268+ pub fn render_html_parts ( & self , ansi : & str ) -> HtmlParts {
269+ let mut styled = adapter:: AnsiBytes :: new ( ) ;
270+ let mut elements = styled. extract_next ( ansi. as_bytes ( ) ) . collect :: < Vec < _ > > ( ) ;
271+ preprocess_invert_style ( & mut elements, self . bg_color , self . fg_color ) ;
272+
273+ let styled_lines = split_lines ( & elements) ;
274+
275+ let mut style = String :: new ( ) ;
276+ let mut body = String :: new ( ) ;
277+
278+ self . render_classes ( & mut style, & elements) ;
279+ self . render_content ( & mut body, styled_lines) ;
280+ HtmlParts { style, body }
281+ }
282+
283+ fn render_classes ( & self , buffer : & mut String , elements : & [ adapter:: Element ] ) {
284+ let fg_color = rgb_value ( self . fg_color , self . palette ) ;
285+ let bg_color = rgb_value ( self . bg_color , self . palette ) ;
286+ let font_family = self . font_family ;
287+
288+ writeln ! ( buffer, r#" .{FG} {{ color: {fg_color} }}"# ) . unwrap ( ) ;
289+ writeln ! ( buffer, r#" .{BG} {{ background: {bg_color} }}"# ) . unwrap ( ) ;
290+ for ( name, rgb) in color_styles ( elements, self . palette ) {
253291 if name. starts_with ( FG_PREFIX ) {
254- writeln ! ( & mut buffer, r#" .{name} {{ color: {rgb} }}"# ) . unwrap ( ) ;
292+ writeln ! ( buffer, r#" .{name} {{ color: {rgb} }}"# ) . unwrap ( ) ;
255293 }
256294 if name. starts_with ( BG_PREFIX ) {
257295 writeln ! (
258- & mut buffer,
296+ buffer,
259297 r#" .{name} {{ background: {rgb}; user-select: none; }}"#
260298 )
261299 . unwrap ( ) ;
262300 }
263301 if name. starts_with ( UNDERLINE_PREFIX ) {
264302 writeln ! (
265- & mut buffer,
303+ buffer,
266304 r#" .{name} {{ text-decoration-line: underline; text-decoration-color: {rgb} }}"#
267305 )
268306 . unwrap ( ) ;
269307 }
270308 }
271- writeln ! ( & mut buffer, r#" .container {{"# ) . unwrap ( ) ;
272- writeln ! ( & mut buffer, r#" line-height: {line_height}px;"# ) . unwrap ( ) ;
273- writeln ! ( & mut buffer, r#" }}"# ) . unwrap ( ) ;
274- write_effects_in_use ( & mut buffer, & elements) ;
275- writeln ! ( & mut buffer, r#" span {{"# ) . unwrap ( ) ;
276- writeln ! ( & mut buffer, r#" font: 14px {font_family};"# ) . unwrap ( ) ;
277- writeln ! ( & mut buffer, r#" white-space: pre;"# ) . unwrap ( ) ;
278- writeln ! ( & mut buffer, r#" line-height: {line_height}px;"# ) . unwrap ( ) ;
279- writeln ! ( & mut buffer, r#" }}"# ) . unwrap ( ) ;
280- writeln ! ( & mut buffer, r#" </style>"# ) . unwrap ( ) ;
281- writeln ! ( & mut buffer, r#"</head>"# ) . unwrap ( ) ;
282- writeln ! ( & mut buffer) . unwrap ( ) ;
283-
284- if !self . background {
285- writeln ! ( & mut buffer, r#"<body>"# ) . unwrap ( ) ;
286- } else {
287- writeln ! ( & mut buffer, r#"<body class="{BG}">"# ) . unwrap ( ) ;
288- }
289- writeln ! ( & mut buffer) . unwrap ( ) ;
290-
291- writeln ! ( & mut buffer, r#" <div class="container {FG}">"# ) . unwrap ( ) ;
309+ writeln ! ( buffer, r#" .container {{"# ) . unwrap ( ) ;
310+ writeln ! ( buffer, r#" line-height: {LINE_HEIGHT}px;"# ) . unwrap ( ) ;
311+ writeln ! ( buffer, r#" }}"# ) . unwrap ( ) ;
312+ write_effects_in_use ( buffer, elements) ;
313+ writeln ! ( buffer, r#" span {{"# ) . unwrap ( ) ;
314+ writeln ! ( buffer, r#" font: 14px {font_family};"# ) . unwrap ( ) ;
315+ writeln ! ( buffer, r#" white-space: pre;"# ) . unwrap ( ) ;
316+ writeln ! ( buffer, r#" line-height: {LINE_HEIGHT}px;"# ) . unwrap ( ) ;
317+ writeln ! ( buffer, r#" }}"# ) . unwrap ( ) ;
318+ }
319+
320+ fn render_content ( & self , buffer : & mut String , styled_lines : Vec < Vec < adapter:: Element > > ) {
321+ writeln ! ( buffer, r#" <div class="container {FG}">"# ) . unwrap ( ) ;
292322 for line in & styled_lines {
293323 if line. iter ( ) . any ( |e| e. style . get_bg_color ( ) . is_some ( ) ) {
294324 for element in line {
295325 if element. text . is_empty ( ) {
296326 continue ;
297327 }
298- write_bg_span ( & mut buffer, "span" , & element. style , & element. text ) ;
328+ write_bg_span ( buffer, "span" , & element. style , & element. text ) ;
299329 }
300- writeln ! ( & mut buffer, r#"<br />"# ) . unwrap ( ) ;
330+ writeln ! ( buffer, r#"<br />"# ) . unwrap ( ) ;
301331 }
302332
303333 for element in line {
304334 if element. text . is_empty ( ) {
305335 continue ;
306336 }
307- write_fg_span ( & mut buffer, "span" , element, & element. text ) ;
337+ write_fg_span ( buffer, "span" , element, & element. text ) ;
308338 }
309- writeln ! ( & mut buffer, r#"<br />"# ) . unwrap ( ) ;
339+ writeln ! ( buffer, r#"<br />"# ) . unwrap ( ) ;
310340 }
311- writeln ! ( & mut buffer, r#" </div>"# ) . unwrap ( ) ;
312- writeln ! ( & mut buffer) . unwrap ( ) ;
313-
314- writeln ! ( & mut buffer, r#"</body>"# ) . unwrap ( ) ;
315- writeln ! ( & mut buffer, r#"</html>"# ) . unwrap ( ) ;
316- buffer
341+ writeln ! ( buffer, r#" </div>"# ) . unwrap ( ) ;
317342 }
318343}
319344
@@ -518,9 +543,12 @@ fn rgb_value(color: anstyle::Color, palette: Palette) -> String {
518543 format ! ( "#{r:02X}{g:02X}{b:02X}" )
519544}
520545
546+ const FG : & str = "fg" ;
547+ const BG : & str = "bg" ;
521548const FG_PREFIX : & str = "fg" ;
522549const BG_PREFIX : & str = "bg" ;
523550const UNDERLINE_PREFIX : & str = "underline" ;
551+ const LINE_HEIGHT : usize = 18 ;
524552
525553fn color_name ( prefix : & str , color : anstyle:: Color ) -> String {
526554 match color {
0 commit comments