From 311148c6bae9c8203ba3baf09c1f78c10e6d22e4 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 1 May 2025 15:20:02 -0700 Subject: [PATCH] html driver works --- rust/pspp/src/output/html.rs | 68 +++++++++++++++++++++++------- rust/pspp/src/output/pivot/test.rs | 24 +++++------ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/rust/pspp/src/output/html.rs b/rust/pspp/src/output/html.rs index 6a316d22c0..3047916a3f 100644 --- a/rust/pspp/src/output/html.rs +++ b/rust/pspp/src/output/html.rs @@ -24,10 +24,10 @@ impl Stroke { fn as_css(&self) -> Option<&'static str> { match self { Stroke::None => None, - Stroke::Solid => Some("solid"), - Stroke::Dashed => Some("dashed"), - Stroke::Thick => Some("thick solid"), - Stroke::Thin => Some("thin solid"), + Stroke::Solid => Some("1pt solid"), + Stroke::Dashed => Some("1pt dashed"), + Stroke::Thick => Some("2pt solid"), + Stroke::Thin => Some("0.5pt solid"), Stroke::Double => Some("double"), } } @@ -108,7 +108,31 @@ where if output.caption.is_some() || output.footnotes.is_some() { writeln!(&mut self.writer, "")?; - todo!(); + writeln!(&mut self.writer, "")?; + if let Some(caption) = output.caption { + self.put_cell( + DrawCell::new(caption.get(Coord2::new(0, 0)).inner(), &caption), + Rect2::new(0..output.body.n[Axis2::X], 0..1), + false, + "td", + None, + )?; + } + writeln!(&mut self.writer, "")?; + + if let Some(footnotes) = output.footnotes { + for cell in footnotes.cells() { + writeln!(&mut self.writer, "")?; + self.put_cell( + DrawCell::new(cell.inner(), &footnotes), + Rect2::new(0..output.body.n[Axis2::X], 0..1), + false, + "td", + None, + )?; + writeln!(&mut self.writer, "")?; + } + } writeln!(&mut self.writer, "")?; } } @@ -161,7 +185,7 @@ where if !cell.style.font_style.font.is_empty() { write!( &mut style, - r#"font-family: "{}""#, + r#"font-family: "{}"; "#, Escape::new(&cell.style.font_style.font) ) .unwrap(); @@ -212,19 +236,21 @@ where if !style.is_empty() { write!( &mut self.writer, - r#" style="{}""#, - style.trim_end_matches("; ") + " style='{}'", + Escape::new(style.trim_end_matches("; ")) + .with_apos("'") + .with_quote("\"") )?; } let col_span = rect[Axis2::X].len(); if col_span > 1 { - write!(&mut self.writer, r#" colspan="{col_span}"#)?; + write!(&mut self.writer, r#" colspan="{col_span}""#)?; } let row_span = rect[Axis2::Y].len(); if row_span > 1 { - write!(&mut self.writer, r#" rowspan="{row_span}"#)?; + write!(&mut self.writer, r#" rowspan="{row_span}""#)?; } write!(&mut self.writer, ">")?; @@ -273,7 +299,7 @@ where write!(&mut self.writer, "")?; } - write!(&mut self.writer, "") + writeln!(&mut self.writer, "") } fn put_border(dst: &mut String, style: BorderStyle, border_name: &str) { @@ -297,8 +323,8 @@ where PSPP Output - + + {} "#, Escape::new(env!("CARGO_PKG_VERSION")), @@ -352,7 +378,8 @@ table { margin-bottom: 1em } caption { - text-align: left + text-align: left; + width: 100% } th { font-weight: normal } a:link { @@ -396,6 +423,8 @@ struct Escape<'a> { string: &'a str, space: &'static str, newline: &'static str, + quote: &'static str, + apos: &'static str, } impl<'a> Escape<'a> { @@ -404,6 +433,8 @@ impl<'a> Escape<'a> { string, space: " ", newline: "\n", + quote: """, + apos: "'", } } fn with_space(self, space: &'static str) -> Self { @@ -412,6 +443,12 @@ impl<'a> Escape<'a> { fn with_newline(self, newline: &'static str) -> Self { Self { newline, ..self } } + fn with_quote(self, quote: &'static str) -> Self { + Self { quote, ..self } + } + fn with_apos(self, apos: &'static str) -> Self { + Self { apos, ..self } + } } impl Display for Escape<'_> { @@ -423,7 +460,8 @@ impl Display for Escape<'_> { '&' => f.write_str("&")?, '<' => f.write_str("<")?, '>' => f.write_str(">")?, - '"' => f.write_str(""")?, + '"' => f.write_str(self.quote)?, + '\'' => f.write_str(self.apos)?, _ => f.write_char(c)?, } } diff --git a/rust/pspp/src/output/pivot/test.rs b/rust/pspp/src/output/pivot/test.rs index 5261c0fa47..eab8653300 100644 --- a/rust/pspp/src/output/pivot/test.rs +++ b/rust/pspp/src/output/pivot/test.rs @@ -1,10 +1,11 @@ -use std::{path::Path, sync::Arc}; +use std::{fs::File, path::Path, sync::Arc}; use enum_map::EnumMap; use crate::output::{ cairo::CairoDriver, driver::Driver, + html::HtmlRenderer, pivot::{ Area, Axis2, Border, BorderStyle, Class, Color, Dimension, Footnote, FootnoteMarkerPosition, FootnoteMarkerType, Footnotes, Group, HeadingRegion, LabelPosition, @@ -63,14 +64,6 @@ Columns ); } -#[test] -fn d1_pdf() { - let pt = d1("Columns", Axis3::X); - let mut cairo = CairoDriver::new("d1.pdf"); - let item = Arc::new(Item::new(Details::Table(Box::new(pt)))); - cairo.write(&item); -} - #[test] fn d1_r() { assert_rendering( @@ -143,10 +136,17 @@ fn assert_rendering(name: &str, pivot_table: &PivotTable, expected: &str) { panic!(); } - if let Some(dir) = std::env::var_os("PSPP_TEST_OUTPUT_DIR") { - let mut cairo = CairoDriver::new(Path::new(&dir).join(name).with_extension("pdf")); + if let Some(dir) = std::env::var_os("PSPP_TEST_HTML_DIR") { + let writer = File::create(Path::new(&dir).join(name).with_extension("html")).unwrap(); + let mut driver = HtmlRenderer::new(writer); + let item = Arc::new(Item::new(Details::Table(Box::new(pivot_table.clone())))); + driver.write(&item); + } + + if let Some(dir) = std::env::var_os("PSPP_TEST_PDF_DIR") { + let mut driver = CairoDriver::new(Path::new(&dir).join(name).with_extension("pdf")); let item = Arc::new(Item::new(Details::Table(Box::new(pivot_table.clone())))); - cairo.write(&item); + driver.write(&item); } } -- 2.30.2