pub mod output;
/// Areas of a pivot table for styling purposes.
-#[derive(Copy, Clone, Debug, Enum, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Default, Enum, PartialEq, Eq)]
pub enum Area {
Title,
Caption,
ColumnLabels,
RowLabels,
+ #[default]
Data,
/// Layer indication.
#[derive(Clone, Debug)]
pub struct Group {
parent: Option<Weak<Group>>,
- name: Value,
+ name: Box<Value>,
label_depth: usize,
extra_depth: usize,
#[derive(Clone, Debug)]
pub struct Leaf {
parent: Weak<Group>,
- name: Value,
+ name: Box<Value>,
label_depth: usize,
extra_depth: usize,
pub current_layer: Vec<usize>,
/// Column and row sizing and page breaks.
- pub sizing: EnumMap<Axis2, Sizing>,
+ pub sizing: EnumMap<Axis2, Option<Box<Sizing>>>,
/// Format settings.
pub settings: FormatSettings,
pub datafile: Option<String>,
pub date: Option<NaiveDateTime>,
pub footnotes: Vec<Footnote>,
- pub title: Option<Value>,
- pub subtype: Option<Value>,
- pub corner_text: Option<Value>,
- pub caption: Option<Value>,
+ pub title: Option<Box<Value>>,
+ pub subtype: Option<Box<Value>>,
+ pub corner_text: Option<Box<Value>>,
+ pub caption: Option<Box<Value>>,
pub notes: Option<String>,
pub dimensions: Vec<Arc<Dimension>>,
pub axes: EnumMap<Axis3, Axis>,
/// "Cell positions".
///
- /// cp[H] represents x positions within the table.
- /// cp[H][0] = 0.
- /// cp[H][1] = the width of the leftmost vertical rule.
- /// cp[H][2] = cp[H][1] + the width of the leftmost column.
- /// cp[H][3] = cp[H][2] + the width of the second-from-left vertical rule.
+ /// 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[H][2 * n[H]] = x position of the rightmost vertical rule.
- /// cp[H][2 * n[H] + 1] = total table width including all rules.
+ /// 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[V] represents y positions within the table.
- /// cp[V][0] = 0.
- /// cp[V][1] = the height of the topmost horizontal rule.
- /// cp[V][2] = cp[V][1] + the height of the topmost row.
- /// cp[V][3] = cp[V][2] + the height of the second-from-top horizontal rule.
+ /// 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[V][2 * n[V]] = y position of the bottommost horizontal rule.
- /// cp[V][2 * n[V] + 1] = total table height including all rules.
+ /// 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.
///
/// # Interpretation
///
- /// overflow[H][0]: space trimmed off its left side.
- /// overflow[H][1]: space trimmed off its right side.
- /// overflow[V][0]: space trimmed off its top.
- /// overflow[V][1]: space trimmed off its bottom.
+ /// 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.
///
/// During rendering, this information is used to position the rendered
/// portion of the cell within the available space.
///
/// 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[H][1] of 10 + 30 = 40 for its portion of the cell,
+ /// will have overflow[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[H][0] of 20 + 10 = 30. The two resulting pages would look like
+ /// overflow[X][0] of 20 + 10 = 30. The two resulting pages would look like
/// this:
///
/// ```text
/// across multiple render_pages. This member indicates when this has
/// happened:
///
- /// is_edge_cutoff[H][0] is true if pixels have been cut off the left side
+ /// 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[H][1] is true if pixels have been cut off the right side
+ /// 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[V][0] and is_edge_cutoff[V][1] are similar for the top
+ /// is_edge_cutoff[Y][0] and is_edge_cutoff[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
*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 {
}
}
}
+
+/*
+ fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
+
+ }*/
+
+ /// 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`.
+ ///
+ /// 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`.
+ /// Similarly, `pixel1` is a number of pixels to exclude from the right or
+ /// bottom of 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` to select on that axis as well.)
+ fn select(
+ self: &Arc<Self>,
+ axis: Axis2,
+ extent: Range<usize>,
+ pixel0: usize,
+ pixel1: usize,
+ ) -> Arc<Self> {
+ 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 {
+ return self.clone();
+ }
+
+ // Figure out `n`, `h`, `r` for the subpage.
+ let trim = [z0 - self.h[axis], self.n[axis] - z1];
+ let mut n = self.n;
+ n[axis] -= trim[0] + trim[1];
+ let mut h = self.h;
+ let mut r = self.r.clone();
+ r[axis].start += trim[0];
+ r[axis].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]);
+
+ // Select widths from `self` into subpage.
+ let scp = self.cp[axis].as_slice();
+ let mut dcp = Vec::with_capacity(2 * n[axis] + 1);
+ dcp.push(0);
+ let mut total = 0;
+ for z in 0..=Self::rule_ofs(h[axis]) {
+ total += if z == 0 && is_edge_cutoff[axis][0] {
+ 0
+ } else {
+ scp[z + 1] - scp[z]
+ };
+ dcp.push(total);
+ }
+ for z in Self::cell_ofs(z0)..=Self::cell_ofs(z1 - 1) {
+ total += scp[z + 1] - scp[z];
+ if z== Self::cell_ofs(z0) {
+ total -= pixel0;
+ }
+ if z == Self::cell_ofs(z1 - 1) {
+ total -= pixel1;
+ }
+ dcp.push(total);
+ }
+ let z = self.rule_ofs_r(axis, 0);
+ if !is_edge_cutoff[axis][1] {
+ total += scp[z + 1] - scp[z];
+ }
+ dcp.push(total);
+ debug_assert_eq!(dcp.len(), 2 * n[axis] + 1);
+
+ let mut cp = EnumMap::default();
+ cp[axis] = dcp;
+ cp[!axis] = self.cp[!axis].clone();
+
+ // Add new overflows.
+ let s = Selection;
+ if self.h[axis] == 0 || z0 > self.h[axis] || pixel0 > 0 {
+ let b = !axis;
+ let mut z = 0;
+ while z < self.n[b] {
+ let d = Coord2::for_axis((axis, z0), z);
+
+ }
+ }
+
+ todo!()
+ }
}
+struct Selection;
+
/// Maps a contiguous range of cells from a page to the underlying table along
/// the horizontal or vertical dimension.
struct Map {
#[derive(Clone)]
pub enum Content {
- Empty,
- Value(Box<CellInner>),
+ Value(CellInner),
Join(Arc<Cell>),
}
impl Content {
pub fn inner(&self) -> &CellInner {
match self {
- Content::Empty => {
- static INNER: CellInner = CellInner {
- rotate: false,
- area: Area::Title,
- value: None,
- };
- &INNER
- }
Content::Value(cell_inner) => &cell_inner,
Content::Join(cell) => &cell.inner,
}
}
pub fn is_empty(&self) -> bool {
- if let Content::Empty = self {
- true
- } else {
- false
- }
+ self.inner().is_empty()
}
/// Returns the rectangle that this cell covers, only if the cell contains
}
}
+impl Default for Content {
+ fn default() -> Self {
+ Self::Value(CellInner::default())
+ }
+}
+
#[derive(Clone)]
pub struct Cell {
inner: CellInner,
}
}
-#[derive(Clone)]
+#[derive(Clone, Default)]
pub struct CellInner {
/// Rotate cell contents 90 degrees?
pub rotate: bool,
/// The area that the cell belongs to.
pub area: Area,
- pub value: Option<Value>,
+ pub value: Option<Box<Value>>,
}
impl CellInner {
- pub fn new(area: Area, value: Option<Value>) -> Self {
+ pub fn new(area: Area, value: Option<Box<Value>>) -> Self {
Self {
rotate: false,
area,
value,
}
}
+
+ pub fn is_empty(&self) -> bool {
+ self.value.is_none()
+ }
}
/// A table.
Self {
n,
h: headers,
- contents: vec![Content::Empty; n.y() * n.x()],
+ contents: vec![Content::default(); n.y() * n.x()],
areas,
borders,
rules: enum_map! {
use Axis2::*;
if region[X].len() == 1 && region[Y].len() == 1 {
let offset = self.offset(Coord2::new(region[X].start, region[Y].start));
- self.contents[offset] = Content::Value(Box::new(inner));
+ self.contents[offset] = Content::Value(inner);
} else {
let cell = Arc::new(Cell::new(inner, region.clone()));
for y in region[Y].clone() {