work
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 3 Dec 2025 21:15:56 +0000 (13:15 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Wed, 3 Dec 2025 21:15:56 +0000 (13:15 -0800)
rust/pspp/src/output/drivers/cairo/fsm.rs
rust/pspp/src/output/drivers/cairo/pager.rs
rust/pspp/src/output/render.rs

index 83f3739e15f95c89fa725ff9dd7c29f539752d62..cb6651b0a65874e56708640d2ce850e5ea7cfe4e 100644 (file)
@@ -168,9 +168,7 @@ impl CairoFsm {
     }
 
     fn draw_table(&mut self, context: &Context, space: isize) -> isize {
-        let Details::Table(pivot_table) = &self.item.details else {
-            unreachable!()
-        };
+        let pivot_table = self.item.details.as_table().unwrap();
         let Some(pager) = &mut self.pager else {
             return 0;
         };
index 1ca673e3db9ca6a4ee2a0cd8692e917d40f9398c..f197d5f4997ba8459d4b7f7951c7d77972c82ff8 100644 (file)
@@ -146,7 +146,8 @@ impl CairoPager {
             };
 
             // Prepare to render the current object.
-            let chunk = fsm.draw_slice(&context, self.y_max.saturating_sub(self.y));
+            let chunk = fsm.draw_slice(&context, dbg!((self.y_max - self.y).max(0)));
+            dbg!(chunk);
             self.y += chunk + self.fsm_style.object_spacing;
             context.translate(0.0, xr_to_pt(chunk + self.fsm_style.object_spacing));
 
index 2f3ff19cb60eaf11c9c4d018a20c406683cde07f..bca95562767cefbd09a9bd39b67165b4eb28eb72 100644 (file)
@@ -176,23 +176,22 @@ pub trait Device {
 /// [Pager] breaks a [Page] into smaller [page]s that will fit in the available
 /// space.
 ///
+/// A [Page] always has the same headers as its [Table].
+///
 /// # Rendered cells
 ///
-/// The horizontal cells rendered are the leftmost `h[X]`, then `r[X]`.
-/// The vertical cells rendered are the topmost `h[Y]`, then `r[Y]`.
-/// `n[i]` is the sum of `h[i]` and `r[i].len()`.
+/// - The columns rendered are the leftmost `self.table.h[X]`, then `r[X]`.
+/// - The rows rendered are the topmost `self.table.h[Y]`, then `r[Y]`.
 #[derive(Debug)]
 struct Page {
     table: Arc<Table>,
 
-    /// Size of the table in cells.
+    /// Table size in cells.
     ///
+    /// This is the sum of `self.table.h` and `self.r`.
     n: CellPos,
 
-    /// Header size.  Cells `0..h[X]` are rendered horizontally, and `0..h[Y]` vertically.
-    h: CellPos,
-
-    /// Main region of cells to render.
+    /// The region of cells in `self.table` to render.
     r: CellRect,
 
     /// Mappings from [Page] positions to those in the underlying [Table].
@@ -299,20 +298,20 @@ struct Page {
     /// 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.
+    /// - `is_edge_cutoff[Axis2::X][0]` is true if pixels have been cut off the
+    ///   left side of the leftmost column in this page, and false otherwise.
     ///
-    /// is_edge_cutoff[X][1] is true if pixels have been cut off the right side
-    /// of the rightmost column in this page, and false otherwise.
+    /// - `is_edge_cutoff[Axis2::X][1]` is true if pixels have been cut off the
+    ///   right side of the rightmost column in this page, and false otherwise.
     ///
-    /// is_edge_cutoff[Y][0] and is_edge_cutoff[Y][1] are similar for the top
-    /// and bottom of the table.
+    /// - `is_edge_cutoff[Axis2::Y][0]` and `is_edge_cutoff[Axis2::Y][1]` are
+    ///   similar for the top and bottom of the table.
     ///
-    /// The effect of is_edge_cutoff is to prevent rules along the edge in
-    /// question from being rendered.
+    /// The effect is to prevent rules along the edge in question from being
+    /// rendered.
     ///
-    /// When is_edge_cutoff is true for a given edge, the 'overflows' hmap will
-    /// contain a node for each cell along that edge.
+    /// When `is_edge_cutoff` is true for a given edge, 'overflows' will contain
+    /// a node for each cell along that edge.
     is_edge_cutoff: EnumMap<Axis2, [bool; 2]>,
 }
 
@@ -524,7 +523,6 @@ impl Page {
         Self {
             table,
             n,
-            h,
             r,
             cp: Axis2::new_enum(cp_x, cp_y),
             overflows: HashMap::new(),
@@ -533,6 +531,11 @@ impl Page {
         }
     }
 
+    /// A [Page] always has the same headers as its underlying [Table].
+    fn h(&self) -> CellPos {
+        self.table.h
+    }
+
     fn use_row_widths(rows: &[isize], rules: &[isize]) -> Vec<isize> {
         let mut vec = once(0)
             .chain(interleave(rules, rows).copied())
@@ -577,7 +580,7 @@ impl Page {
 
     /// Returns the width of the headers along `axis`.
     fn headers_width(&self, axis: Axis2) -> isize {
-        self.axis_width(axis, rule_ofs(0)..cell_ofs(self.h[axis]))
+        self.axis_width(axis, rule_ofs(0)..cell_ofs(self.h()[axis]))
     }
 
     /// Returns the width of rule `z` along `axis`.
@@ -610,7 +613,7 @@ impl Page {
 
     /// Returns the width of the widest cell, excluding headers, along `axis`.
     fn max_cell_width(&self, axis: Axis2) -> isize {
-        (self.h[axis]..self.n[axis])
+        (self.h()[axis]..self.n[axis])
             .map(|z| self.cell_width(axis, z))
             .max()
             .unwrap_or(0)
@@ -640,7 +643,7 @@ impl Page {
     }
 
     fn get_map(&self, axis: Axis2, z: usize) -> &Map {
-        if z < self.h[axis] {
+        if z < self.h()[axis] {
             &self.maps[axis][0]
         } else {
             &self.maps[axis][1]
@@ -677,8 +680,8 @@ impl Page {
     /// cell `extent.end - 1`.  (`pixel0` and `pixel1` are used to render cells
     /// that are too large to fit on a single page.)
     ///
-    /// The whole of axis `!axis` is included.  (The caller may follow up with
-    /// another call to select on `!axis`.)
+    /// The whole of axis `!a` is included.  (The caller may follow up with
+    /// another call to select on `!a`.)
     fn select(
         self: &Arc<Self>,
         a: Axis2,
@@ -689,17 +692,17 @@ impl Page {
         let b = !a;
         let z0 = extent.start;
         let z1 = extent.end;
+        let h = self.h();
 
         // If all of the page is selected, just make a copy.
-        if z0 == self.h[a] && z1 == self.n[a] && pixel0 == 0 && pixel1 == 0 {
+        if z0 == h[a] && z1 == self.n[a] && pixel0 == 0 && pixel1 == 0 {
             return self.clone();
         }
 
         // Figure out `n`, `h`, `r` for the subpage.
-        let trim = [z0 - self.h[a], self.n[a] - z1];
+        let trim = [z0 - self.h()[a], self.n[a] - z1];
         let mut n = self.n;
         n[a] -= trim[0] + trim[1];
-        let h = self.h;
         let mut r = self.r.clone();
         r[a].start += trim[0];
         r[a].end -= trim[1];
@@ -757,7 +760,7 @@ impl Page {
             p1: pixel1,
         };
         // Add overflows along the left side...
-        if self.h[a] == 0 || z0 > self.h[a] || pixel0 > 0 {
+        if h[a] == 0 || z0 > h[a] || pixel0 > 0 {
             let mut z = 0;
             while z < self.n[b] {
                 let d = CellPos::for_axis((a, z0), z);
@@ -774,11 +777,7 @@ impl Page {
                         overflow[a][1] +=
                             pixel1 + self.axis_width(a, cell_ofs(z1)..cell_ofs(cell.rect[a].end));
                     }
-                    assert!(
-                        overflows
-                            .insert(s.coord_to_subpage(cell.rect.top_left()), overflow)
-                            .is_none()
-                    );
+                    assert!(overflows.insert(s.coord_to_subpage(d), overflow).is_none());
                 }
                 z += cell.rect[b].len();
             }
@@ -821,7 +820,6 @@ impl Page {
         Arc::new(Self {
             table: self.table.clone(),
             n,
-            h,
             r,
             maps,
             cp,
@@ -912,10 +910,11 @@ impl Page {
         let coord = self.map_coord(coord);
 
         let border = self.table.get_rule(a, coord);
-        if self.h[a] > 0 && coord[a] == self.h[a] {
+        let h = self.h();
+        if h[a] > 0 && coord[a] == h[a] {
             let border2 = self
                 .table
-                .get_rule(a, CellPos::for_axis((a, self.h[a]), coord[!a]));
+                .get_rule(a, CellPos::for_axis((a, h[a]), coord[!a]));
             border.combine(border2)
         } else {
             border
@@ -988,12 +987,20 @@ impl Selection {
     /// Returns the coordinates of `coord` as it will appear in this subpage.
     ///
     /// `coord` must be in the selected region or the results will not make
-    /// sense.
+    /// sense (or will panic due to overflow).
     fn coord_to_subpage(&self, coord: CellPos) -> CellPos {
         let a = self.a;
         let b = self.b;
         let ha0 = self.h[a];
-        CellPos::for_axis((a, max(coord[a] + ha0 - self.z0, ha0)), coord[b])
+        let z = coord[a];
+        let z_subpage = if (0..ha0).contains(&z) {
+            z
+        } else if (self.z0..self.z1).contains(&z) {
+            z - self.z0 + ha0
+        } else {
+            unreachable!("{z} is not in {:?} or {:?}", 0..ha0, self.z0..self.z1);
+        };
+        CellPos::for_axis((a, z_subpage), coord[b])
     }
 }
 
@@ -1151,7 +1158,7 @@ struct Break {
 
 impl Break {
     fn new(page: Arc<Page>, axis: Axis2) -> Self {
-        let z = page.h[axis];
+        let z = page.h()[axis];
         let hw = page.headers_width(axis);
         Self {
             page,
@@ -1172,7 +1179,7 @@ impl Break {
         // Width of header not including its rightmost rule.
         let mut size = self
             .page
-            .axis_width(self.axis, 0..rule_ofs(self.page.h[self.axis]));
+            .axis_width(self.axis, 0..rule_ofs(self.page.h()[self.axis]));
 
         // If we have a pixel offset and there is no header, then we omit
         // the leftmost rule of the body.  Otherwise the rendering is deceptive
@@ -1183,9 +1190,9 @@ impl Break {
         // rightmost rule in the header and the leftmost rule in the body.  We
         // assume that the width of a merged rule is the larger of the widths of
         // either rule individually.
-        if self.pixel == 0 || self.page.h[self.axis] > 0 {
+        if self.pixel == 0 || self.page.h()[self.axis] > 0 {
             size += max(
-                self.page.rule_width(self.axis, self.page.h[self.axis]),
+                self.page.rule_width(self.axis, self.page.h()[self.axis]),
                 self.page.rule_width(self.axis, self.z),
             );
         }