From 936ee17f13b075271ce02baf1a90d8ae9de4971b Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 12 Jan 2025 10:33:33 -0800 Subject: [PATCH] Add cells iterator --- rust/pspp/src/output/pivot/output.rs | 6 +- rust/pspp/src/output/render.rs | 124 +++++++++++---------------- rust/pspp/src/output/table.rs | 73 +++++++++++++--- 3 files changed, 110 insertions(+), 93 deletions(-) diff --git a/rust/pspp/src/output/pivot/output.rs b/rust/pspp/src/output/pivot/output.rs index 59bc362074..678350ec06 100644 --- a/rust/pspp/src/output/pivot/output.rs +++ b/rust/pspp/src/output/pivot/output.rs @@ -310,15 +310,15 @@ impl PivotTable { let mut refs = vec![false; self.footnotes.len()]; for table in tables.into_iter().flatten() { - table.visit_cells(|inner| { - if let Some(value) = &inner.value { + for cell in table.cells() { + if let Some(value) = &cell.inner().value { if let Some(styling) = &value.styling { for index in &styling.footnote_indexes { refs[*index] = true; } } } - }); + } } refs.iter() .enumerate() diff --git a/rust/pspp/src/output/render.rs b/rust/pspp/src/output/render.rs index 3a2e84e1ac..f417dc17c1 100644 --- a/rust/pspp/src/output/render.rs +++ b/rust/pspp/src/output/render.rs @@ -300,36 +300,29 @@ impl Page { // Calculate minimum and maximum widths of cells that do not span // multiple columns. let mut columns = [vec![0; table.n.x()], vec![0; table.n.x()]]; - for y in 0..table.n[Y] { - for x in table.iter_x(y) { - let coord = Coord2::new(x, y); - let contents = table.get(coord); - if !contents.is_top_left() || contents.col_span() != 1 { - continue; - } - - let mut w = device.measure_cell_width(contents.inner()); - if device.params().px_size.is_some() { - if let Some(region) = table.heading_region(coord) { - let wr = &heading_widths[region]; - if w[0] < wr[0] { - w[0] = wr[0]; - if w[0] > w[1] { - w[1] = w[0]; - } - } else if w[1] > wr[1] { - w[1] = wr[1]; - if w[1] < w[0] { - w[0] = w[1]; - } + for cell in table.cells().filter(|cell| cell.col_span() == 1) { + let mut w = device.measure_cell_width(cell.inner()); + if device.params().px_size.is_some() { + if let Some(region) = table.heading_region(cell.coord) { + let wr = &heading_widths[region]; + if w[0] < wr[0] { + w[0] = wr[0]; + if w[0] > w[1] { + w[1] = w[0]; + } + } else if w[1] > wr[1] { + w[1] = wr[1]; + if w[1] < w[0] { + w[0] = w[1]; } } } + } - for i in 0..2 { - if columns[i][x] < w[i] { - columns[i][x] = w[i]; - } + let x = cell.coord[X]; + for i in 0..2 { + if columns[i][x] < w[i] { + columns[i][x] = w[i]; } } } @@ -344,23 +337,16 @@ impl Page { }) .collect::>() }); - for y in 0..table.n[Y] { - for x in table.iter_x(y) { - let coord = Coord2::new(x, y); - let contents = table.get(coord); - if !contents.is_top_left() || contents.col_span() == 1 { - continue; - } - let rect = contents.rect(); - - let w = device.measure_cell_width(contents.inner()); - for i in 0..2 { - distribute_spanned_width( - w[i], - &mut columns[i][rect[X].clone()], - &rules[X][rect[X].start..rect[X].end + 1], - ); - } + for cell in table.cells().filter(|cell| cell.col_span() > 1) { + let rect = cell.rect(); + + let w = device.measure_cell_width(cell.inner()); + for i in 0..2 { + distribute_spanned_width( + w[i], + &mut columns[i][rect[X].clone()], + &rules[X][rect[X].start..rect[X].end + 1], + ); } } if min_width > 0 { @@ -400,43 +386,29 @@ impl Page { // Calculate heights of cells that do not span multiple rows. let mut rows = vec![Row::default(); table.n[Y]]; - for y in 0..table.n[Y] { - let row = &mut rows[y]; - for x in table.iter_x(y) { - let coord = Coord2::new(x, y); - let contents = table.get(coord); - if !contents.is_top_left() { - continue; - } - let rect = contents.rect(); - - if contents.row_span() == 1 { - let w = page.joined_width(X, rect[X].clone()); - let h = device.measure_cell_height(contents.inner(), w); - if h > row.unspanned { - row.unspanned = h; - row.width = h; - } - } + for cell in table.cells().filter(|cell| cell.row_span() == 1) { + let rect = cell.rect(); + + let w = page.joined_width(X, rect[X].clone()); + let h = device.measure_cell_height(cell.inner(), w); + + let row = &mut rows[cell.coord.y()]; + if h > row.unspanned { + row.unspanned = h; + row.width = h; } } // Distribute heights of spanned rows. - for y in 0..table.n[Y] { - for x in table.iter_x(y) { - let coord = Coord2::new(x, y); - let contents = table.get(coord); - if contents.is_top_left() && contents.row_span() > 1 { - let rect = contents.rect(); - let w = page.joined_width(X, rect[X].clone()); - let h = device.measure_cell_height(contents.inner(), w); - distribute_spanned_width( - h, - &mut rows[rect[Y].clone()], - &rules[Y][rect[Y].start..rect[Y].end + 1], - ); - } - } + for cell in table.cells().filter(|cell| cell.row_span() > 1) { + let rect = cell.rect(); + let w = page.joined_width(X, rect[X].clone()); + let h = device.measure_cell_height(cell.inner(), w); + distribute_spanned_width( + h, + &mut rows[rect[Y].clone()], + &rules[Y][rect[Y].start..rect[Y].end + 1], + ); } // Decide final row heights. diff --git a/rust/pspp/src/output/table.rs b/rust/pspp/src/output/table.rs index d7ae0505a6..4b3ba58dfe 100644 --- a/rust/pspp/src/output/table.rs +++ b/rust/pspp/src/output/table.rs @@ -18,8 +18,9 @@ use crate::output::pivot::Coord2; use super::pivot::{Area, AreaStyle, Axis2, Border, BorderStyle, HeadingRegion, Rect2, Value}; +#[derive(Clone)] pub struct CellRef<'a> { - coord: Coord2, + pub coord: Coord2, content: &'a Content, } @@ -281,19 +282,6 @@ impl Table { } } - /// Visits all the nonempty cells once. - pub fn visit_cells(&self, mut f: impl FnMut(&CellInner)) { - for y in 0..self.n.y() { - for x in self.iter_x(y) { - let coord = Coord2::new(x, y); - let content = self.get(coord); - if !content.is_empty() && content.is_top_left() { - f(content.inner()); - } - } - } - } - /// The heading region that `pos` is part of, if any. pub fn heading_region(&self, pos: Coord2) -> Option { if pos.x() < self.h.x() { @@ -304,6 +292,18 @@ impl Table { None } } + + /// Iterates across all of the cells in the table, visiting each of them + /// once in top-down, left-to-right order. Spanned cells are visited once, + /// at the point in the iteration where their top-left cell would appear if + /// they were not spanned. + pub fn cells(&self) -> Cells<'_> { + Cells::new(self) + } + + pub fn is_empty(&self) -> bool { + self.n[Axis2::X] == 0 || self.n[Axis2::Y] == 0 + } } pub struct XIter<'a> { @@ -327,3 +327,48 @@ impl<'a> Iterator for XIter<'a> { } } } + +/// Iterator for all of the cells in a table (see [Table::cells]). +pub struct Cells<'a> { + table: &'a Table, + next: Option>, +} + +impl<'a> Cells<'a> { + fn new(table: &'a Table) -> Self { + Self { + table, + next: if table.is_empty() { + None + } else { + Some(table.get(Coord2::new(0, 0))) + }, + } + } +} + +impl<'a> Iterator for Cells<'a> { + type Item = CellRef<'a>; + + fn next(&mut self) -> Option { + use Axis2::*; + let this = self.next.as_ref()?.clone(); + + let mut next = this.clone(); + self.next = loop { + let next_x = next.next_x(); + let coord = if next_x < self.table.n[X] { + Coord2::new(next_x, next.coord.y()) + } else if next.coord.y() + 1 < self.table.n[Y] { + Coord2::new(0, next.coord.y() + 1) + } else { + break None; + }; + next = self.table.get(coord); + if next.is_top_left() { + break Some(next); + } + }; + Some(this) + } +} -- 2.30.2