html driver works
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 1 May 2025 22:20:02 +0000 (15:20 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 1 May 2025 22:20:02 +0000 (15:20 -0700)
rust/pspp/src/output/html.rs
rust/pspp/src/output/pivot/test.rs

index 6a316d22c0b5480c0d4123502228d0417dc1fcbf..3047916a3f57f5f49f76e2810c1767ef510c7117 100644 (file)
@@ -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, "<tfoot>")?;
-                todo!();
+                writeln!(&mut self.writer, "<tr>")?;
+                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, "</tr>")?;
+
+                if let Some(footnotes) = output.footnotes {
+                    for cell in footnotes.cells() {
+                        writeln!(&mut self.writer, "<tr>")?;
+                        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, "</tr>")?;
+                    }
+                }
                 writeln!(&mut self.writer, "</tfoot>")?;
             }
         }
@@ -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("&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, "</sup>")?;
         }
 
-        write!(&mut self.writer, "</{tag}>")
+        writeln!(&mut self.writer, "</{tag}>")
     }
 
     fn put_border(dst: &mut String, style: BorderStyle, border_name: &str) {
@@ -297,8 +323,8 @@ where
 <html>
 <head>
 <title>PSPP Output</title>
-<meta name="generator" content="PSPP {}"
-<meta http-equiv="content-type" content="text/html; charset=utf8">
+<meta name="generator" content="PSPP {}"/>
+<meta http-equiv="content-type" content="text/html; charset=utf8"/>
 {}
 "#,
         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: "&quot;",
+            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("&amp;")?,
                 '<' => f.write_str("&lt;")?,
                 '>' => f.write_str("&gt;")?,
-                '"' => f.write_str("&quot;")?,
+                '"' => f.write_str(self.quote)?,
+                '\'' => f.write_str(self.apos)?,
                 _ => f.write_char(c)?,
             }
         }
index 5261c0fa471e1c4e2c6b8e1d5e7872f481559609..eab8653300f2a001fe9a00f57e4795d06c6a14f1 100644 (file)
@@ -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);
     }
 }