render page measuring now implemented
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 26 Jan 2025 01:33:30 +0000 (17:33 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 26 Jan 2025 01:33:30 +0000 (17:33 -0800)
rust/pspp/src/output/pivot/mod.rs
rust/pspp/src/output/render.rs
rust/pspp/src/output/table.rs

index 28533606805aba9a05b42eb019acf4ed8ce1fea0..c8317b462686b5b7ba86bd0a5739f01c5096511f 100644 (file)
@@ -732,7 +732,7 @@ impl Not for Axis2 {
 }
 
 /// A 2-dimensional `(x,y)` pair.
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
 pub struct Coord2(pub EnumMap<Axis2, usize>);
 
 impl Coord2 {
index 7fdb5d53a456fe9dbeda48718ef3e20e99dfd450..318a7574155be3245d4f3f27fdff019594020776 100644 (file)
@@ -1,4 +1,4 @@
-use std::cmp::max;
+use std::cmp::{max, min};
 use std::collections::HashMap;
 use std::iter::once;
 use std::ops::Range;
@@ -9,7 +9,7 @@ use itertools::interleave;
 use smallvec::SmallVec;
 
 use super::pivot::{Axis2, BorderStyle, Coord2, Look, PivotTable, Rect2, Stroke};
-use super::table::{Cell, CellInner, Table};
+use super::table::{Cell, CellInner, Content, Table};
 
 /// Parameters for rendering a table_item to a device.
 ///
@@ -79,17 +79,17 @@ pub trait Device {
     /// Returns the height required to render `cell` given a width of `width`.
     fn measure_cell_height(&self, cell: &CellInner, width: usize) -> usize;
 
-    /// Given that there is space measuring `width` by `height` to render
-    /// `cell`, where `height` is insufficient to render the entire height of
-    /// the cell, returns the largest height less than `height` at which it is
+    /// Given that there is space measuring `size` to render `cell`, where
+    /// `size.y()` is insufficient to render the entire height of the cell,
+    /// returns the largest height less than `size.y()` at which it is
     /// appropriate to break the cell.  For example, if breaking at the
-    /// specified `height` would break in the middle of a line of text, the
+    /// specified `size.y()` would break in the middle of a line of text, the
     /// return value would be just sufficiently less that the breakpoint would
     /// be between lines of text.
     ///
     /// Optional.  If [RenderParams::can_adjust_break] is false, the rendering
     /// engine assumes that all breakpoints are acceptable.
-    fn adjust_break(&self, cell: &Cell, size: Coord2) -> usize;
+    fn adjust_break(&self, cell: &Content, size: Coord2) -> usize;
 
     /// Draws a generalized intersection of lines in `bb`.
     ///
@@ -309,6 +309,12 @@ fn cell_width(cp: &[usize], z: usize) -> usize {
     axis_width(cp, ofs..ofs + 1)
 }
 
+#[derive(Clone)]
+pub struct RenderCell<'a> {
+    rect: Rect2,
+    content: &'a Content,
+}
+
 impl Page {
     /// Creates and returns a new [Page] for rendering `table` with the given
     /// `look` on `device`.
@@ -316,7 +322,7 @@ impl Page {
     /// The new [Page] will be suitable for rendering on a device whose page
     /// size is `params.size`, but the caller is responsible for actually
     /// breaking it up to fit on such a device, using the [Break] abstraction.
-    fn new(table: &Arc<Table>, device: &Arc<dyn Device>, min_width: usize, look: &Look) -> Self {
+    fn new(table: Arc<Table>, device: Arc<dyn Device>, min_width: usize, look: &Look) -> Self {
         use Axis2::*;
 
         let n = table.n.clone();
@@ -324,7 +330,7 @@ impl Page {
         // Figure out rule widths.
         let rules = EnumMap::from_fn(|axis| {
             (0..n[axis])
-                .map(|z| measure_rule(table, &**device, axis, z))
+                .map(|z| measure_rule(&*table, &*device, axis, z))
                 .collect::<Vec<_>>()
         });
 
@@ -473,8 +479,8 @@ impl Page {
         let r = Rect2::new(h[X]..n[X], h[Y]..n[Y]);
         let maps = Self::new_mappings(h, &r);
         Self {
-            device: device.clone(),
-            table: table.clone(),
+            device,
+            table,
             n,
             h,
             r,
@@ -578,48 +584,49 @@ impl Page {
         *self.cp[axis].last().unwrap()
     }
 
-    /// XXX This could return a
-    fn get_map(&self, a: Axis2, z: usize) -> Map {
-        if z < self.h[a] {
-            Map {
-                p0: 0,
-                t0: 0,
-                n: self.h[a],
-            }
-        } else {
-            Map {
-                p0: self.h[a],
-                t0: self.r[a].start,
-                n: self.r[a].len(),
-            }
-        }
-    }
-
     fn new_mappings(h: Coord2, r: &Rect2) -> EnumMap<Axis2, [Map; 2]> {
         EnumMap::from_fn(|axis| {
             [
                 Map {
                     p0: 0,
                     t0: 0,
+                    ofs: 0,
                     n: h[axis],
                 },
                 Map {
                     p0: h[axis],
                     t0: r[axis].start,
+                    ofs: r[axis].start - h[axis],
                     n: r[axis].len(),
                 },
             ]
         })
     }
 
-    /*
-    fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
+    fn get_map(&self, axis: Axis2, ordinate: usize) -> &Map {
+        if ordinate < self.h[axis] {
+            &self.maps[axis][0]
+        } else {
+            &self.maps[axis][1]
+        }
+    }
 
-    }*/
+    fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
+        let maps = EnumMap::from_fn(|axis| self.get_map(axis, coord[axis]));
+        let coord = Coord2(coord.0.map(|axis, ordinate| ordinate + maps[axis].ofs));
+        let cell = self.table.get(coord);
+        RenderCell {
+            rect: Rect2(cell.rect().0.map(|axis, Range { start, end }| {
+                let m = &maps[axis];
+                max(m.p0, start - m.ofs)..min(m.p0 + m.n, end - m.ofs)
+            })),
+            content: cell.content,
+        }
+    }
 
-    /// Creates and returns a new [Page] whose contents are a subregion of
-    /// thispage's contents.  The new page includes cells `extent` (exclusive)
-    /// along `axis`, plus any headers on `axis`.
+    /// Creates and returns a new [Page] whose contents are a subregion of this
+    /// page's contents.  The new page includes cells `extent` (exclusive) along
+    /// `axis`, plus any headers on `axis`.
     ///
     /// If `pixel0` is nonzero, then it is a number of pixels to exclude from
     /// the left or top (according to `axis`) of cell `extent.start`.
@@ -628,46 +635,45 @@ impl Page {
     /// 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` to select on that axis as well.)
+    /// another call to select on `!axis`.)
     fn select(
         self: &Arc<Self>,
-        axis: Axis2,
+        a: Axis2,
         extent: Range<usize>,
         pixel0: usize,
         pixel1: usize,
     ) -> Arc<Self> {
+        let b = !a;
         let z0 = extent.start;
         let z1 = extent.end;
 
         // If all of the page is selected, just make a copy.
-        if z0 == self.h[axis] && z1 == self.n[axis] && pixel0 == 0 && pixel1 == 0 {
+        if z0 == self.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[axis], self.n[axis] - z1];
+        let trim = [z0 - self.h[a], self.n[a] - z1];
         let mut n = self.n;
-        n[axis] -= trim[0] + trim[1];
-        let mut h = self.h;
+        n[a] -= trim[0] + trim[1];
+        let h = self.h;
         let mut r = self.r.clone();
-        r[axis].start += trim[0];
-        r[axis].end -= trim[1];
+        r[a].start += trim[0];
+        r[a].end -= trim[1];
 
         // An edge is cut off if it was cut off in `self` or if we're trimming
         // pixels off that side of the page and there are no headers.
         let mut is_edge_cutoff = self.is_edge_cutoff.clone();
-        is_edge_cutoff[axis][0] =
-            h[axis] == 0 && (pixel0 > 0 || (z0 == 0 && self.is_edge_cutoff[axis][0]));
-        is_edge_cutoff[axis][1] =
-            pixel1 > 0 || (z1 == self.n[axis] && self.is_edge_cutoff[axis][1]);
+        is_edge_cutoff[a][0] = h[a] == 0 && (pixel0 > 0 || (z0 == 0 && self.is_edge_cutoff[a][0]));
+        is_edge_cutoff[a][1] = pixel1 > 0 || (z1 == self.n[a] && self.is_edge_cutoff[a][1]);
 
         // Select widths from `self` into subpage.
-        let scp = self.cp[axis].as_slice();
-        let mut dcp = Vec::with_capacity(2 * n[axis] + 1);
+        let scp = self.cp[a].as_slice();
+        let mut dcp = Vec::with_capacity(2 * n[a] + 1);
         dcp.push(0);
         let mut total = 0;
-        for z in 0..=rule_ofs(h[axis]) {
-            total += if z == 0 && is_edge_cutoff[axis][0] {
+        for z in 0..=rule_ofs(h[a]) {
+            total += if z == 0 && is_edge_cutoff[a][0] {
                 0
             } else {
                 scp[z + 1] - scp[z]
@@ -684,32 +690,127 @@ impl Page {
             }
             dcp.push(total);
         }
-        let z = self.rule_ofs_r(axis, 0);
-        if !is_edge_cutoff[axis][1] {
+        let z = self.rule_ofs_r(a, 0);
+        if !is_edge_cutoff[a][1] {
             total += scp[z + 1] - scp[z];
         }
         dcp.push(total);
-        debug_assert_eq!(dcp.len(), 2 * n[axis] + 1);
+        debug_assert_eq!(dcp.len(), 2 * n[a] + 1);
 
         let mut cp = EnumMap::default();
-        cp[axis] = dcp;
-        cp[!axis] = self.cp[!axis].clone();
+        cp[a] = dcp;
+        cp[!a] = self.cp[!a].clone();
+
+        let mut overflows = HashMap::new();
 
         // Add new overflows.
-        let s = Selection;
-        if self.h[axis] == 0 || z0 > self.h[axis] || pixel0 > 0 {
-            let b = !axis;
+        let s = Selection {
+            a,
+            b,
+            h,
+            z0,
+            z1,
+            p0: pixel0,
+            p1: pixel1,
+        };
+        if self.h[a] == 0 || z0 > self.h[a] || pixel0 > 0 {
             let mut z = 0;
             while z < self.n[b] {
-                let d = Coord2::for_axis((axis, z0), z);
+                let d = Coord2::for_axis((a, z0), z);
+                let cell = self.get_cell(d);
+                let overflow0 = pixel0 > 0 || cell.rect[a].start < z0;
+                let overflow1 = cell.rect[a].end > z1 || (cell.rect[a].end == z1 && pixel1 > 0);
+                if overflow0 || overflow1 {
+                    let mut overflow = self.overflows.get(&d).cloned().unwrap_or_default();
+                    if overflow0 {
+                        overflow[a][0] +=
+                            pixel0 + self.axis_width(a, cell_ofs(cell.rect[a].start)..cell_ofs(z0));
+                    }
+                    if overflow1 {
+                        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());
+                }
+                z += cell.rect[b].len();
             }
         }
 
-        todo!()
+        let mut z = 0;
+        while z < self.n[b] {
+            let d = Coord2::for_axis((a, z1 - 1), z);
+            let cell = self.get_cell(d);
+            if cell.rect[a].end > z1
+                || (cell.rect[a].end == z1 && pixel1 > 0)
+                    && overflows
+                        .get(&s.coord_to_subpage(cell.rect.top_left()))
+                        .is_none()
+            {
+                let mut overflow = self.overflows.get(&d).cloned().unwrap_or_default();
+                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());
+            }
+            z += cell.rect[b].len();
+        }
+
+        // Copy overflows from `self` into the subpage.
+        // XXX this could be done at the start, which would simplify the while loops above
+        for (coord, overflow) in self.overflows.iter() {
+            let cell = self.table.get(*coord);
+            let rect = cell.rect();
+            if rect[a].end > z0 && rect[a].start < z1 {
+                overflows
+                    .entry(s.coord_to_subpage(rect.top_left()))
+                    .or_insert(overflow.clone());
+            }
+        }
+
+        let maps = Self::new_mappings(h, &r);
+        Arc::new(Self {
+            device: self.device.clone(),
+            table: self.table.clone(),
+            n,
+            h,
+            r,
+            maps,
+            cp,
+            overflows,
+            is_edge_cutoff,
+        })
     }
+
+    fn table_width(&self, axis: Axis2) -> usize {
+        self.cp[axis].last().copied().unwrap()
+    }
+}
+
+struct Selection {
+    a: Axis2,
+    b: Axis2,
+    z0: usize,
+    z1: usize,
+    p0: usize,
+    p1: usize,
+    h: Coord2,
 }
 
-struct Selection;
+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.
+    fn coord_to_subpage(&self, coord: Coord2) -> Coord2 {
+        let a = self.a;
+        let b = self.b;
+        let ha0 = self.h[a];
+        Coord2::for_axis((a, max(coord[a] + ha0 - self.z0, ha0)), coord[b])
+    }
+}
 
 /// Maps a contiguous range of cells from a page to the underlying table along
 /// the horizontal or vertical dimension.
@@ -721,6 +822,9 @@ struct Map {
     /// First ordinate in the table.
     t0: usize,
 
+    /// `t0 - p0`.
+    ofs: usize,
+
     /// Number of ordinates in page and table.
     n: usize,
 }
@@ -859,13 +963,15 @@ struct Break {
 }
 
 impl Break {
-    fn new(page: &Arc<Page>, axis: Axis2) -> Self {
+    fn new(page: Arc<Page>, axis: Axis2) -> Self {
+        let z = page.h[axis];
+        let hw = page.headers_width(axis);
         Self {
-            page: page.clone(),
+            page,
             axis,
-            z: page.h[axis],
+            z,
             pixel: 0,
-            hw: page.headers_width(axis),
+            hw,
         }
     }
 
@@ -918,71 +1024,119 @@ impl Break {
     /// completely broken up, or if `size` is too small to reasonably render any
     /// cells.  The latter will never happen if `size` is at least as large as
     /// the page size passed to [Page::new] along the axis using for breaking.
-    fn next(&mut self, size: usize) -> Option<Page> {
+    fn next(&mut self, size: usize) -> Option<Arc<Page>> {
         if !self.has_next() {
             return None;
         }
 
-        // A small but visible width.
-        let em = self.page.device.params().font_size[Axis2::X];
+        self.find_breakpoint(size).map(|(z, pixel)| match pixel {
+            0 => self.page.select(self.axis, self.z..z, self.pixel, 0),
+            pixel => self.page.select(
+                self.axis,
+                self.z..z + 1,
+                pixel,
+                self.page.cell_width(self.axis, z) - pixel,
+            ),
+        })
+    }
 
-        let mut pixel = 0;
-        for z in self.z..self.page.n[self.axis] {
-            let needed = self.needed_size(z + 1);
-            if needed > size {
-                if self.cell_is_breakable(z) {
-                    // If there is no right header and we render a partial cell
-                    // on the right side of the body, then we omit the rightmost
-                    // rule of the body.  Otherwise the rendering is deceptive
-                    // because it looks like the whole cell is present instead
-                    // of a partial cell.
-                    //
-                    // This is similar to code for the left side in
-                    // [Self::needed_size].
-                    let rule_allowance = self.page.rule_width(self.axis, z);
-
-                    // The amount that, if we added cell `z`, the rendering
-                    // would overfill the allocated `size`.
-                    let overhang = needed - size - rule_allowance; // XXX could go negative
-
-                    // The width of cell `z`.
-                    let cell_size = self.page.cell_width(self.axis, z);
-
-                    // The amount trimmed off the left side of `z`, and the
-                    // amount left to render.
-                    let cell_ofs = if z == self.z { self.pixel } else { 0 };
-                    let cell_left = cell_size - cell_ofs;
-
-                    // If some of the cell remains to render, and there would
-                    // still be some of the cell left afterward, then partially
-                    // render that much of the cell.
-                    let mut pixel = if cell_left > 0 && cell_left > overhang {
-                        cell_left - overhang + cell_ofs
-                    } else {
-                        0
-                    };
-
-                    // If there would be only a tiny amount of the cell left
-                    // after rendering it partially, reduce the amount rendered
-                    // slightly to make the output look a little better.
-                    if pixel + em > cell_size {
-                        pixel = pixel.saturating_sub(em);
-                    }
+    /// Returns a small but visible width.
+    fn em(&self) -> usize {
+        self.page.device.params().font_size[Axis2::X]
+    }
 
-                    // If we're breaking vertically, then consider whether the
-                    // cells being broken have a better internal breakpoint than
-                    // the exact number of pixels available, which might look
-                    // bad e.g. because it breaks in the middle of a line of
-                    // text.
-                    if self.axis == Axis2::Y && self.page.device.params().can_adjust_break {
-                        for x in self.page.table.iter_x(z) {}
+    fn break_cell(&self, z: usize, overflow: usize) -> usize {
+        if self.cell_is_breakable(z) {
+            // If there is no right header and we render a partial cell
+            // on the right side of the body, then we omit the rightmost
+            // rule of the body.  Otherwise the rendering is deceptive
+            // because it looks like the whole cell is present instead
+            // of a partial cell.
+            //
+            // This is similar to code for the left side in
+            // [Self::needed_size].
+            let rule_allowance = self.page.rule_width(self.axis, z);
+
+            // The amount that, if we added cell `z`, the rendering
+            // would overfill the allocated `size`.
+            let overhang = overflow - rule_allowance; // XXX could go negative
+
+            // The width of cell `z`.
+            let cell_size = self.page.cell_width(self.axis, z);
+
+            // The amount trimmed off the left side of `z`, and the
+            // amount left to render.
+            let cell_ofs = if z == self.z { self.pixel } else { 0 };
+            let cell_left = cell_size - cell_ofs;
+
+            // If some of the cell remains to render, and there would
+            // still be some of the cell left afterward, then partially
+            // render that much of the cell.
+            let mut pixel = if cell_left > 0 && cell_left > overhang {
+                cell_left - overhang + cell_ofs
+            } else {
+                0
+            };
+
+            // If there would be only a tiny amount of the cell left
+            // after rendering it partially, reduce the amount rendered
+            // slightly to make the output look a little better.
+            let em = self.em();
+            if pixel + em > cell_size {
+                pixel = pixel.saturating_sub(em);
+            }
+
+            // If we're breaking vertically, then consider whether the
+            // cells being broken have a better internal breakpoint than
+            // the exact number of pixels available, which might look
+            // bad e.g. because it breaks in the middle of a line of
+            // text.
+            if self.axis == Axis2::Y && self.page.device.params().can_adjust_break {
+                let mut x = 0;
+                while x < self.page.n[Axis2::X] {
+                    let cell = self.page.get_cell(Coord2::new(x, z));
+                    let better_pixel = self.page.device.adjust_break(
+                        cell.content,
+                        Coord2::new(
+                            self.page
+                                .joined_width(Axis2::X, cell.rect[Axis2::X].clone()),
+                            pixel,
+                        ),
+                    );
+                    x += cell.rect[Axis2::X].len();
+
+                    if better_pixel < pixel {
+                        let start_pixel = if z > self.z { self.pixel } else { 0 };
+                        if better_pixel > start_pixel {
+                            pixel = better_pixel;
+                            break;
+                        } else if better_pixel == 0 && z != self.z {
+                            pixel = 0;
+                            break;
+                        }
                     }
                 }
-                break;
             }
+
+            pixel
+        } else {
+            0
         }
+    }
 
-        todo!()
+    fn find_breakpoint(&mut self, size: usize) -> Option<(usize, usize)> {
+        for z in self.z..self.page.n[self.axis] {
+            let needed = self.needed_size(z + 1);
+            if needed > size {
+                let pixel = self.break_cell(z, needed - size);
+                if z == self.z && pixel == 0 {
+                    return None;
+                } else {
+                    return Some((z, pixel));
+                }
+            }
+        }
+        Some((self.page.n[self.axis], 0))
     }
 
     /// Returns true if `cell` along this breaker's axis may be broken across a
@@ -1002,11 +1156,10 @@ pub struct Pager {
     /// [Page]s to be rendered, in order, vertically.  There may be up to 5
     /// pages, for the pivot table's title, layers, body, captions, and
     /// footnotes.
-    pages: SmallVec<[Box<Page>; 5]>,
+    pages: SmallVec<[Arc<Page>; 5]>,
 
-    cur_page: usize,
-    x_break: Break,
-    y_break: Break,
+    x_break: Option<Break>,
+    y_break: Option<Break>,
 }
 
 impl Pager {
@@ -1022,10 +1175,89 @@ impl Pager {
 
         // Figure out the width of the body of the table. Use this to determine
         // the base scale.
-        let body = Arc::new(output.body);
-        let body_page = Page::new(&body, &device, 0, &pivot_table.look);
+        let body_page = Page::new(Arc::new(output.body), device.clone(), 0, &pivot_table.look);
         let body_width = body_page.width(Axis2::X);
+        let mut scale = if body_width > device.params().size[Axis2::X]
+            && pivot_table.look.shrink_to_fit[Axis2::X]
+            && device.params().can_scale
+        {
+            device.params().size[Axis2::X] as f64 / body_width as f64
+        } else {
+            1.0
+        };
 
-        todo!()
+        let mut pages = SmallVec::new();
+        for table in [output.title, output.layers].into_iter().flatten() {
+            pages.push(Arc::new(Page::new(
+                Arc::new(table),
+                device.clone(),
+                body_width,
+                &pivot_table.look,
+            )));
+        }
+        pages.push(Arc::new(body_page));
+        for table in [output.caption, output.footnotes].into_iter().flatten() {
+            pages.push(Arc::new(Page::new(
+                Arc::new(table),
+                device.clone(),
+                0,
+                &pivot_table.look,
+            )));
+        }
+        pages.reverse();
+
+        // If we're shrinking tables to fit the page length, then adjust the
+        // scale factor.
+        //
+        // XXX This will sometimes shrink more than needed, because adjusting
+        // the scale factor allows for cells to be "wider", which means that
+        // sometimes they won't break across as much vertical space, thus
+        // shrinking the table vertically more than the scale would imply.
+        // Shrinking only as much as necessary would require an iterative
+        // search.
+        if pivot_table.look.shrink_to_fit[Axis2::Y] && device.params().can_scale {
+            let total_height = pages
+                .iter()
+                .map(|page: &Arc<Page>| page.table_width(Axis2::Y))
+                .sum::<usize>() as f64;
+            let max_height = device.params().size[Axis2::Y] as f64;
+            if total_height * scale >= max_height {
+                scale *= max_height / total_height;
+            }
+        }
+
+        Self {
+            device,
+            scale,
+            pages,
+            x_break: None,
+            y_break: None,
+        }
+    }
+
+    /// True if there's content left to rnder.
+    fn has_next(&mut self) -> bool {
+        while self
+            .y_break
+            .as_mut()
+            .is_none_or(|y_break| !y_break.has_next())
+        {
+            self.y_break = self
+                .x_break
+                .as_mut()
+                .and_then(|x_break| {
+                    x_break.next(
+                        (self.device.params().size[Axis2::X] as f64 / self.scale as f64) as usize,
+                    )
+                })
+                .map(|page| Break::new(page, Axis2::Y));
+            if self.y_break.is_none() {
+                match self.pages.pop() {
+                    Some(page) => self.x_break = Some(Break::new(page, Axis2::X)),
+                    _ => return false,
+                }
+            }
+        }
+        false
     }
 }
index bf6a2f8f0c35ff128aba89c4d79ae6d4e4073886..4060f05b45d8c25b926c28262de2188bffdb7aa1 100644 (file)
@@ -21,7 +21,7 @@ use super::pivot::{Area, AreaStyle, Axis2, Border, BorderStyle, HeadingRegion, R
 #[derive(Clone)]
 pub struct CellRef<'a> {
     pub coord: Coord2,
-    content: &'a Content,
+    pub content: &'a Content,
 }
 
 impl<'a> CellRef<'a> {