breaking tables mostly works
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 8 Dec 2025 16:44:40 +0000 (08:44 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 8 Dec 2025 16:44:40 +0000 (08:44 -0800)
rust/pspp/src/output/drivers/cairo/fsm.rs
rust/pspp/src/output/pivot.rs
rust/pspp/src/output/render.rs

index 60b006dcf762c0240730757460835321751f586e..5c411ec1d9927404b007d61803c11de5884bebaf 100644 (file)
@@ -738,9 +738,7 @@ impl Device for CairoDevice<'_> {
         bb[Axis2::Y].start += valign_offset;
         for axis in [Axis2::X, Axis2::Y] {
             bb[axis].start += px_to_xr(draw_cell.cell_style.margins[axis][0] as isize);
-            bb[axis].end = bb[axis]
-                .end
-                .saturating_sub(draw_cell.cell_style.margins[axis][0] as isize);
+            bb[axis].end -= px_to_xr(draw_cell.cell_style.margins[axis][1] as isize);
         }
         if bb[Axis2::X].start < bb[Axis2::X].end && bb[Axis2::Y].start < bb[Axis2::Y].end {
             self.cell_draw(draw_cell, bb, clip);
index 1b7f3672afa640f394552257186f2db6a48fae09..0ee01c6ba41fd73c7516d334dc4b2006095eb8de 100644 (file)
@@ -1382,6 +1382,7 @@ impl Color {
         self.with_alpha(255)
     }
 
+    /// Displays opaque colors as `#rrggbb` and others as `rgb(r, g, b, alpha)`.
     pub fn display_css(&self) -> DisplayCss {
         DisplayCss(*self)
     }
index 1714bc5cdb6f70a90d5dbdf2c884d2268e9d0708..4e8176da2490c4136da49366eb95fe06ce44f701 100644 (file)
@@ -540,13 +540,15 @@ impl Page {
                 let cell = self.table.table.get(CellPos { x, y });
                 // XXX skip if not top-left cell
                 let rect = cell.rect();
-                let bb = Rect2::from_fn(|a| {
+                let mut bb = Rect2::from_fn(|a| {
                     cp[a][rect[a].start * 2 + 1]..cp[a][(rect[a].end - 1) * 2 + 2]
                 });
-                let clip = if y < self.table.h().y {
+                let mut clip = if y < self.table.h().y {
                     if x < self.table.h().x {
+                        // Corner
                         bb.clone()
                     } else {
+                        // Top stub
                         Rect2::new(
                             max(bb[X].start, self.ranges[X].start)
                                 ..min(bb[X].end, self.ranges[X].end),
@@ -554,15 +556,29 @@ impl Page {
                         )
                     }
                 } else if x < self.table.h().x {
+                    // Left stub
                     Rect2::new(
                         bb[X].clone(),
                         max(bb[Y].start, self.ranges[Y].start)..min(bb[Y].end, self.ranges[Y].end),
                     )
                 } else {
+                    // Body
                     Rect2::from_fn(|a| {
                         max(bb[a].start, self.ranges[a].start)..min(bb[a].end, self.ranges[a].end)
                     })
                 };
+                if clip[X].start >= clip[X].end || clip[Y].start >= clip[Y].end {
+                    continue;
+                }
+                for a in [X, Y] {
+                    if bb[a].start >= self.ranges[a].start {
+                        let h = self.ranges[a].start - self.table.headers_width(a);
+                        bb[a].start -= h;
+                        bb[a].end -= h;
+                        clip[a].start -= h;
+                        clip[a].end -= h;
+                    }
+                }
                 let draw_cell = DrawCell::new(cell.content.inner(), &self.table.table);
                 let valign_offset = match draw_cell.cell_style.vert_align {
                     VertAlign::Top => 0,
@@ -594,12 +610,32 @@ impl Page {
                 .enumerate()
                 .filter(|(x, _)| *x % 2 == 0 || y % 2 == 0)
             {
-                self.draw_rule(device, ofs, CellPos { x, y });
+                let mut bb = Rect2::new(xr.clone(), yr.clone());
+
+                let h = self.table.headers_width(X);
+                if xr.start < h {
+                } else if self.ranges[X].contains(&xr.start) {
+                    bb[X].start -= self.ranges[X].start - h;
+                    bb[X].end -= self.ranges[X].start - h;
+                } else {
+                    continue;
+                }
+
+                let h = self.table.headers_width(Y);
+                if yr.start < h {
+                } else if self.ranges[Y].contains(&yr.start) {
+                    bb[Y].start -= self.ranges[Y].start - h;
+                    bb[Y].end -= self.ranges[Y].start - h;
+                } else {
+                    continue;
+                }
+
+                self.draw_rule(device, ofs, CellPos { x, y }, bb);
             }
         }
     }
 
-    fn draw_rule(&self, device: &mut dyn Device, ofs: Coord2, coord: CellPos) {
+    fn draw_rule(&self, device: &mut dyn Device, ofs: Coord2, coord: CellPos, bb: Rect2) {
         const NO_BORDER: BorderStyle = BorderStyle::none();
         let styles = EnumMap::from_fn(|a: Axis2| {
             let b = !a;
@@ -631,9 +667,7 @@ impl Page {
             .values()
             .all(|border| border.iter().all(BorderStyle::is_none))
         {
-            let bb = Rect2::from_fn(|a| self.table.cp[a][coord[a]]..self.table.cp[a][coord[a] + 1])
-                .translate(ofs);
-            device.draw_line(bb, styles);
+            device.draw_line(bb.translate(ofs), styles);
         }
     }
 
@@ -883,9 +917,10 @@ impl Break {
         if !self.has_next() {
             return Ok(None);
         }
-        let target = size
-            .checked_sub(self.page.table.headers_width(self.axis))
-            .ok_or(())?;
+        let target = size - self.page.table.headers_width(self.axis);
+        if target <= 0 {
+            return Err(());
+        }
         let start = self.page.ranges[self.axis].start;
         let (end, next_start) = self.find_breakpoint(start..start + target, device);
         let result = Page {