From 580c3ff48da14affb30df53519264e904a2a7580 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 30 Nov 2025 17:38:20 -0800 Subject: [PATCH] work --- rust/pspp/src/output/drivers/cairo/fsm.rs | 5 +- rust/pspp/src/output/drivers/text.rs | 2 +- rust/pspp/src/output/render.rs | 139 +++++++++++----------- rust/pspp/src/output/spv.rs | 4 +- 4 files changed, 72 insertions(+), 78 deletions(-) diff --git a/rust/pspp/src/output/drivers/cairo/fsm.rs b/rust/pspp/src/output/drivers/cairo/fsm.rs index 41e8d40480..b75479b7ae 100644 --- a/rust/pspp/src/output/drivers/cairo/fsm.rs +++ b/rust/pspp/src/output/drivers/cairo/fsm.rs @@ -160,10 +160,7 @@ impl CairoFsm { context.save().unwrap(); let used = match &self.item.details { Details::Table(_) => self.draw_table(context, space), - _ => { - eprintln!("{}", &self.item); - 0 - } + _ => 0, }; context.restore().unwrap(); diff --git a/rust/pspp/src/output/drivers/text.rs b/rust/pspp/src/output/drivers/text.rs index 181a6c2ddb..65757b220f 100644 --- a/rust/pspp/src/output/drivers/text.rs +++ b/rust/pspp/src/output/drivers/text.rs @@ -595,7 +595,7 @@ impl Device for TextRenderer { use Axis2::*; let x = bb[X].start.max(0)..bb[X].end.min(self.width); let y = bb[Y].start.max(0)..bb[Y].end; - if x.is_empty() || x.end >= self.width { + if x.is_empty() || x.start >= self.width { return; } diff --git a/rust/pspp/src/output/render.rs b/rust/pspp/src/output/render.rs index e8f900d971..a37618b83e 100644 --- a/rust/pspp/src/output/render.rs +++ b/rust/pspp/src/output/render.rs @@ -16,7 +16,7 @@ use std::cmp::{max, min}; use std::collections::HashMap; -use std::iter::once; +use std::iter::{once, zip}; use std::ops::Range; use std::sync::Arc; @@ -200,35 +200,37 @@ struct Page { /// "Cell positions". /// - /// cp[X] represents x positions within the table. - /// cp[X][0] = 0. - /// cp[X][1] = the width of the leftmost vertical rule. - /// cp[X][2] = cp[X][1] + the width of the leftmost column. - /// cp[X][3] = cp[X][2] + the width of the second-from-left vertical rule. - /// and so on: - /// cp[X][2 * n[X]] = x position of the rightmost vertical rule. - /// cp[X][2 * n[X] + 1] = total table width including all rules. + /// `cp[X]` represents `x` positions within the table: /// - /// Similarly, cp[Y] represents y positions within the table. - /// cp[Y][0] = 0. - /// cp[Y][1] = the height of the topmost horizontal rule. - /// cp[Y][2] = cp[Y][1] + the height of the topmost row. - /// cp[Y][3] = cp[Y][2] + the height of the second-from-top horizontal rule. - /// and so on: - /// cp[Y][2 * n[Y]] = y position of the bottommost horizontal rule. - /// cp[Y][2 * n[Y] + 1] = total table height including all rules. + /// - `cp[X][0]` = 0. + /// - `cp[X][1]` = the width of the leftmost vertical rule. + /// - `cp[X][2]` = `cp[X][1]` + the width of the leftmost column. + /// - `cp[X][3]` = `cp[X][2]` + the width of the second-from-left vertical rule. + /// - ... + /// - `cp[X][2 * n[X]]` = `x` position of the rightmost vertical rule. + /// - `cp[X][2 * n[X] + 1]` = total table width including all rules. + /// + /// Similarly, `cp[Y]` represents `y` positions within the table: + /// + /// - `cp[Y][0]` = 0. + /// - `cp[Y][1]` = the height of the topmost horizontal rule. + /// - `cp[Y][2]` = `cp[Y][1]` + the height of the topmost row. + /// - `cp[Y][3]` = `cp[Y][2]` + the height of the second-from-top horizontal rule. + /// - ... + /// - `cp[Y][2 * n[Y]]` = `y` position of the bottommost horizontal rule. + /// - `cp[Y][2 * n[Y] + 1]` = total table height including all rules. /// /// Rules and columns can have width or height 0, in which case consecutive /// values in this array are equal. cp: EnumMap>, - /// [Break::next] can break a table such that some cells are not fully - /// contained within a render_page. This will happen if a cell is too wide - /// or two tall to fit on a single page, or if a cell spans multiple rows - /// or columns and the page only includes some of those rows or columns. + /// [Break::next] can break a [Page] in the middle of a cell, if a cell is + /// too wide or two tall to fit on a single page, or if a cell spans + /// multiple rows or columns and the page only includes some of those rows + /// or columns. /// - /// This hash table contains represents each such cell that doesn't - /// completely fit on this page. + /// This hash table represents each such cell that doesn't completely fit on + /// this page. /// /// Each overflow cell borders at least one header edge of the table and may /// border more. (A single table cell that is so large that it fills the @@ -236,10 +238,12 @@ struct Page { /// /// # Interpretation /// - /// overflow[X][0]: space trimmed off its left side. - /// overflow[X][1]: space trimmed off its right side. - /// overflow[Y][0]: space trimmed off its top. - /// overflow[Y][1]: space trimmed off its bottom. + /// Given `overflow` as a value in the [HashMap]: + /// + /// - `overflow[Axis2::X][0]`: space trimmed off the cell's left side. + /// - `overflow[Axis2::X][1]`: space trimmed off the cell's right side. + /// - `overflow[Axis2::Y][0]`: space trimmed off the cell's top. + /// - `overflow[Axis2::Y][1]`: space trimmed off the cell's bottom. /// /// During rendering, this information is used to position the rendered /// portion of the cell within the available space. @@ -249,52 +253,51 @@ struct Page { /// still included in overflow values. /// /// Suppose, for example, that a cell that joins 2 columns has a width of 60 - /// pixels and content "abcdef", that the 2 columns that it joins have + /// pixels and content `abcdef`, that the 2 columns that it joins have /// widths of 20 and 30 pixels, respectively, and that therefore the rule /// between the two joined columns has a width of 10 (20 + 10 + 30 = 60). /// It might render like this, if each character is 10x10, and showing a few /// extra table cells for context: /// /// ```text - /// +------+ - /// |abcdef| - /// +--+---+ - /// |gh|ijk| - /// +--+---+ + /// ┌──────┐ + /// │abcdef│ + /// ├──┬───┤ + /// │gh│ijk│ + /// └──┴───┘ /// ``` /// - /// If this render_page is broken at the rule that separates "gh" from - /// "ijk", then the page that contains the left side of the "abcdef" cell - /// will have overflow[X][1] of 10 + 30 = 40 for its portion of the cell, + /// If this [Page] is broken at the rule that separates `gh` from + /// `ijk`, then the page that contains the left side of the `abcdef` cell + /// will have `overflow[Axis2::X][1]` of 10 + 30 = 40 for its portion of the cell, /// and the page that contains the right side of the cell will have - /// overflow[X][0] of 20 + 10 = 30. The two resulting pages would look like + /// `overflow[Axis2::X][0]` of 20 + 10 = 30. The two resulting pages would look like /// this: /// /// ```text - /// +--- - /// |abc - /// +--+ - /// |gh| - /// +--+ + /// ┌─── + /// │abc + /// ├──┬ + /// │gh│ + /// └──┴ /// ``` /// /// and: /// /// ```text - /// ----+ - /// cdef| - /// +---+ - /// |ijk| - /// +---+ + /// ────┐ + /// cdef│ + /// ┬───┤ + /// │ijk│ + /// ┴───┘ /// ``` /// Each entry maps from a cell that overflows to the space that has been - /// trimmed off the cell: + /// trimmed off the cell. overflows: HashMap>, /// If a single column (or row) is too wide (or tall) to fit on a page - /// reasonably, then render_break_next() will split a single row or column - /// across multiple render_pages. This member indicates when this has - /// happened: + /// reasonably, then [Break::next] will split a single row or column across + /// multiple [Page]s. This member indicates when this has happened: /// /// is_edge_cutoff[X][0] is true if pixels have been cut off the left side /// of the leftmost column in this page, and false otherwise. @@ -458,15 +461,12 @@ impl Page { let cp_x = if table_widths[Max] <= device.params().size[X] { // Fits even with maximum widths. Use them. Self::use_row_widths(&columns[Max], &rules[X]) - } else if table_widths[Min] <= device.params().size[X] { + } else if device.params().size[X] > table_widths[Min] { // Fits with minimum widths, so distribute the leftover space. - //Self::new_with_interpolated_widths() - Self::interpolate_row_widths( - device.params(), - &columns[Min], - &columns[Max], - table_widths[Min], - table_widths[Max], + Self::interpolate_column_widths( + device.params().size[Axis2::X], + &columns, + &table_widths, &rules[X], ) } else { @@ -543,21 +543,18 @@ impl Page { vec } - fn interpolate_row_widths( - params: &Params, - rows_min: &[usize], - rows_max: &[usize], - w_min: usize, - w_max: usize, + fn interpolate_column_widths( + target: usize, + columns: &EnumMap>, + widths: &EnumMap, rules: &[usize], ) -> Vec { - let avail = params.size[Axis2::X] - w_min; - let wanted = w_max - w_min; + use Extreme::*; + + let avail = target - widths[Min]; + let wanted = widths[Max] - widths[Min]; let mut w = wanted / 2; - let rows_mid = rows_min - .iter() - .copied() - .zip(rows_max.iter().copied()) + let rows_mid = zip(columns[Min].iter().copied(), columns[Max].iter().copied()) .map(|(min, max)| { w += avail * (max - min); let extra = w / wanted; diff --git a/rust/pspp/src/output/spv.rs b/rust/pspp/src/output/spv.rs index d87c608669..7be381b96b 100644 --- a/rust/pspp/src/output/spv.rs +++ b/rust/pspp/src/output/spv.rs @@ -285,7 +285,7 @@ impl Heading { ContainerContent::Tree => new_error_item("trees not yet implemented") .with_spv_info(SpvInfo::new(structure_member).with_error()), }; - items.push(item); + items.push(item.with_show(container.visibility == Visibility::Visible)); } HeadingContent::Heading(mut heading) => { let show = !heading.visibility.is_some(); @@ -525,7 +525,7 @@ enum PageBreakBefore { Inherit, } -#[derive(Deserialize, Debug, Default)] +#[derive(Deserialize, Debug, Default, PartialEq, Eq)] #[serde(rename_all = "camelCase")] enum Visibility { #[default] -- 2.30.2