From: Ben Pfaff Date: Sat, 5 Apr 2025 17:33:54 +0000 (-0700) Subject: some basic table rendering basically works X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cb4488e648e3da678c89b81c3993d32235036c5e;p=pspp some basic table rendering basically works --- diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 62a1ba2247..1a6e0fa4c4 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -334,6 +334,27 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "unicode-xid", +] + [[package]] name = "diff" version = "0.1.13" @@ -1012,6 +1033,7 @@ dependencies = [ "chrono", "clap", "color", + "derive_more", "diff", "either", "encoding_rs", @@ -1545,6 +1567,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "url" version = "2.5.2" diff --git a/rust/pspp/Cargo.toml b/rust/pspp/Cargo.toml index 9fc4a923f0..07f6d4ebf3 100644 --- a/rust/pspp/Cargo.toml +++ b/rust/pspp/Cargo.toml @@ -41,6 +41,7 @@ serde = { version = "1.0.218", features = ["derive"] } color = { version = "0.2.3", features = ["serde"] } binrw = "0.14.1" ndarray = "0.16.1" +derive_more = { version = "2.0.1", features = ["debug"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.48.0", features = ["Win32_Globalization"] } diff --git a/rust/pspp/src/output/pivot/mod.rs b/rust/pspp/src/output/pivot/mod.rs index f5350aa1b9..ec4b5ce22a 100644 --- a/rust/pspp/src/output/pivot/mod.rs +++ b/rust/pspp/src/output/pivot/mod.rs @@ -1713,7 +1713,7 @@ impl Display for Display26Adic { /// /// 5. A template. PSPP doesn't create these itself yet, but it can read and /// interpret those created by SPSS. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Default)] pub struct Value { pub inner: ValueInner, pub styling: Option>, @@ -2017,6 +2017,12 @@ impl Value { } } +impl Debug for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.display(()).to_string()) + } +} + #[derive(Clone, Debug, Default)] pub enum ValueInner { Number { diff --git a/rust/pspp/src/output/pivot/output.rs b/rust/pspp/src/output/pivot/output.rs index 868bb81df8..9f1cdbb808 100644 --- a/rust/pspp/src/output/pivot/output.rs +++ b/rust/pspp/src/output/pivot/output.rs @@ -305,11 +305,11 @@ impl PivotTable { self.output_footnotes(&self.collect_footnotes(tables.into_iter().flatten())); OutputTables { - title, - layers, + title: None, + layers: None, body, - caption, - footnotes, + caption: None, + footnotes: None, } } diff --git a/rust/pspp/src/output/render.rs b/rust/pspp/src/output/render.rs index c02a385dff..b2e7fa5c86 100644 --- a/rust/pspp/src/output/render.rs +++ b/rust/pspp/src/output/render.rs @@ -373,15 +373,19 @@ impl Page { fn new(table: Arc, device: &dyn Device, min_width: usize, look: &Look) -> Self { use Axis2::*; - println!("{table:#?}"); + dbg!(&table); let n = table.n.clone(); // Figure out rule widths. + // + // `rules[X]` is vertical rules. + // `rules[Y]` is horizontal rules. let rules = EnumMap::from_fn(|axis| { (0..=n[axis]) .map(|z| measure_rule(&*device, &*table, axis, z)) .collect::>() }); + dbg!(&rules); let px_size = device.params().px_size.unwrap_or_default(); let heading_widths = look @@ -418,6 +422,7 @@ impl Page { } } } + dbg!(&unspanned_columns); // Distribute widths of spanned columns. let mut columns = unspanned_columns.clone(); @@ -453,6 +458,7 @@ impl Page { columns[1][i] = columns[0][i]; } } + dbg!(&columns); // Decide final column widths. let rule_widths = rules[X].iter().copied().sum::(); @@ -460,6 +466,7 @@ impl Page { .iter() .map(|row| row.iter().sum::() + rule_widths) .collect::>(); + dbg!(&table_widths); let cp_x = if table_widths[1] <= device.params().size[X] { // Fits even with maximum widths. Use them. @@ -480,6 +487,7 @@ impl Page { // later we can break it horizontally into multiple pages. Self::use_row_widths(&columns[0], &rules[X]) }; + dbg!(&cp_x); // Calculate heights of cells that do not span multiple rows. let mut unspanned_rows = vec![0; n[Y]]; @@ -539,17 +547,16 @@ impl Page { } } - fn accumulate_vec(mut vec: Vec) -> Vec { + fn use_row_widths(rows: &[usize], rules: &[usize]) -> Vec { + let mut vec = once(0) + .chain(interleave(rules, rows).copied()) + .collect::>(); for i in 1..vec.len() { vec[i] += vec[i - 1] } vec } - fn use_row_widths(rows: &[usize], rules: &[usize]) -> Vec { - once(0).chain(interleave(rules, rows).copied()).collect() - } - fn interpolate_row_widths( params: &Params, rows_min: &[usize], @@ -854,7 +861,7 @@ impl Page { let mut x = cells[X].start; while x < cells[X].end { if !is_rule(x) && !is_rule(y) { - let cell = self.get_cell(Coord2::new(x, y)); + let cell = self.get_cell(Coord2::new(x / 2, y / 2)); self.draw_cell(device, ofs, &cell); x = rule_ofs(cell.rect[X].end); } else { @@ -865,7 +872,7 @@ impl Page { for y in cells[Y].clone() { for x in cells[X].clone() { - if is_rule(x) && is_rule(y) { + if is_rule(x) || is_rule(y) { self.draw_rule(device, ofs, Coord2::new(x, y)); } } @@ -940,12 +947,14 @@ impl Page { self.cp[a][cell.rect[a].start * 2 + 1]..self.cp[a][cell.rect[a].end * 2] }) .translate(ofs); - let spill = EnumMap::from_fn(|a| { - [ - self.rule_width(a, cell.rect[a].start) / 2, - self.rule_width(a, cell.rect[a].end) / 2, - ] - }); + /* + let spill = EnumMap::from_fn(|a| { + [ + self.rule_width(a, cell.rect[a].start) / 2, + self.rule_width(a, cell.rect[a].end) / 2, + ] + });*/ + let spill = EnumMap::from_fn(|_| [0, 0]); let clip = if let Some(overflow) = self.overflows.get(&cell.rect.top_left()) { Rect2::from_fn(|a| { @@ -1065,16 +1074,15 @@ fn distribute_spanned_width( spanned: &mut [usize], rules: &[usize], ) { - println!("{unspanned:?}"); - println!("{spanned:?}"); - println!("{rules:?}"); let n = unspanned.len(); debug_assert!(n >= 1); debug_assert_eq!(spanned.len(), n); debug_assert_eq!(rules.len(), n + 1); - let total_unspanned = - unspanned.iter().sum::() + (&rules[1..n - 1]).iter().copied().sum::(); + let total_unspanned = unspanned.iter().sum::() + + rules + .get(1..n - 1) + .map_or(0, |rules| rules.iter().copied().sum::()); if total_unspanned >= width { return; } @@ -1319,7 +1327,7 @@ impl Break { } fn find_breakpoint(&mut self, device: &dyn Device, size: usize) -> Option<(usize, usize)> { - println!("{:?} {:?}", self.axis, self.z..self.page.n[self.axis]); + dbg!(self.axis, self.z..self.page.n[self.axis]); for z in self.z..self.page.n[self.axis] { let needed = self.needed_size(z + 1); if needed > size { @@ -1366,8 +1374,6 @@ impl Pager { layer_indexes.unwrap_or(&pivot_table.current_layer), device.params().printing, ); - println!("{:#?}", pivot_table); - println!("{:#?}", output.body); // Figure out the width of the body of the table. Use this to determine // the base scale. @@ -1432,6 +1438,7 @@ impl Pager { /// True if there's content left to rnder. pub fn has_next(&mut self, device: &dyn Device) -> bool { + return !self.pages.is_empty(); while self .y_break .as_mut() @@ -1465,6 +1472,10 @@ impl Pager { pub fn draw_next(&mut self, device: &mut dyn Device, mut space: usize) -> usize { use Axis2::*; + let page = self.pages.pop().unwrap(); + page.draw(device, Coord2::new(0, 0)); + return page.total_size(Y); + if self.scale != 1.0 { device.scale(self.scale); space = (space as f64 / self.scale) as usize; diff --git a/rust/pspp/src/output/table.rs b/rust/pspp/src/output/table.rs index 6b9ef5f66a..7d6443bcdf 100644 --- a/rust/pspp/src/output/table.rs +++ b/rust/pspp/src/output/table.rs @@ -166,7 +166,7 @@ impl CellInner { } /// A table. -#[derive(Debug)] +#[derive(derive_more::Debug)] pub struct Table { /// Number of rows and columns. pub n: Coord2, @@ -177,15 +177,18 @@ pub struct Table { pub contents: Array2, /// Styles for areas of the table. + #[debug(skip)] pub areas: EnumMap, /// Styles for borders in the table. + #[debug(skip)] pub borders: EnumMap, - /// Horizontal and vertical rules. + /// Horizontal ([Axis2::Y]) and vertical ([Axis2::X]) rules. pub rules: EnumMap>, /// How to present values. + #[debug(skip)] pub value_options: ValueOptions, } @@ -204,8 +207,8 @@ impl Table { areas, borders, rules: enum_map! { - Axis2::X => Array::from_elem((n.x(), n.y() + 1), Border::Title), - Axis2::Y => Array::from_elem((n.x() + 1, n.y()), Border::Title), + Axis2::X => Array::from_elem((n.x() + 1, n.y()), Border::Title), + Axis2::Y => Array::from_elem((n.x(), n.y() + 1), Border::Title), }, value_options, } @@ -241,7 +244,7 @@ impl Table { debug_assert!(x.start <= x.end); debug_assert!(x.end <= self.n.x()); for x in x { - self.rules[Axis2::X][[x, y]] = border; + self.rules[Axis2::Y][[x, y]] = border; } } @@ -250,7 +253,7 @@ impl Table { debug_assert!(y.start <= y.end); debug_assert!(y.end <= self.n.y()); for y in y { - self.rules[Axis2::Y][[x, y]] = border; + self.rules[Axis2::X][[x, y]] = border; } } diff --git a/rust/pspp/src/output/text.rs b/rust/pspp/src/output/text.rs index 5c2cbfbffb..e275cd6d68 100644 --- a/rust/pspp/src/output/text.rs +++ b/rust/pspp/src/output/text.rs @@ -267,7 +267,12 @@ impl TextDriver { } self.n_objects += 1; - pager.draw_next(self, usize::MAX); + let h = pager.draw_next(self, usize::MAX); + + for (ln, line) in self.lines[..h].iter_mut().enumerate() { + println!("{ln}: {}", line); + line.clear(); + } } } } @@ -332,7 +337,6 @@ where } else { postindex }; - println!("index={index} {:?}", &self.text[index..]); if index <= self.indexes.end { dbg!(); continue; @@ -340,14 +344,12 @@ where let segment_width = self.text[self.indexes.end..index].width(); if self.width == 0 || self.width + segment_width <= self.max_width { - dbg!(); // Add this segment to the current line. self.width += segment_width; self.indexes.end = index; // If this was a new-line, we're done. if opportunity == BreakOpportunity::Mandatory { - dbg!(); let segment = self.text[self.indexes.clone()].trim_end_matches('\n'); self.indexes = postindex..postindex; self.width = 0; @@ -483,10 +485,10 @@ impl Device for TextDriver { } let lines = Lines { - l: styles[X][0].stroke.into(), - r: styles[X][1].stroke.into(), - t: styles[Y][0].stroke.into(), - b: styles[Y][1].stroke.into(), + l: styles[Y][0].stroke.into(), + r: styles[Y][1].stroke.into(), + t: styles[X][0].stroke.into(), + b: styles[X][1].stroke.into(), }; let c = self.box_chars[lines]; for y in y { @@ -505,6 +507,7 @@ impl Device for TextDriver { ) { let display = Self::display_cell(cell); let text = display.to_string(); + println!("text={text:?} bb={bb:?}"); let horz_align = cell .style .cell_style diff --git a/rust/pspp/src/output/text_line.rs b/rust/pspp/src/output/text_line.rs index b861da2df3..713b892b39 100644 --- a/rust/pspp/src/output/text_line.rs +++ b/rust/pspp/src/output/text_line.rs @@ -1,5 +1,9 @@ use enum_iterator::Sequence; -use std::{borrow::Cow, fmt::Debug, ops::Range}; +use std::{ + borrow::Cow, + fmt::{Debug, Display}, + ops::Range, +}; use unicode_width::UnicodeWidthChar; @@ -120,11 +124,17 @@ impl TextLine { } } - fn str(&self) -> &str { + pub fn str(&self) -> &str { &self.string } } +impl Display for TextLine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.string) + } +} + /// Position of one or more characters within a [TextLine]. #[derive(Debug)] struct Position {