data::{ByteString, Case, Datum, WithEncoding},
dictionary::Dictionary,
format::{DisplayPlain, Type},
- output::{
- drivers::Driver, table::CellPos, Item
- },
+ output::{Item, drivers::Driver, table::CellPos},
util::ToSmallString as _,
variable::Variable,
};
return Ok(());
};
- for y in 0..table.n.y() {
- for x in 0..table.n.x() {
+ for y in 0..table.n.1 {
+ for x in 0..table.n.0 {
if x > 0 {
write!(&mut self.file, "{}", self.options.delimiter)?;
}
- let coord = CellPos::new(x, y);
+ let coord = CellPos(x, y);
let content = table.get(coord);
if content.is_top_left() {
let display = content.inner().value.display(pivot_table);
use smallstr::SmallString;
use crate::output::{
- drivers::Driver, pivot::{
- Axis2, BorderStyle, Color, HorzAlign, IndexRect2, PivotTable, Stroke, VertAlign
- }, table::{DrawCell, CellPos, Table}, Details, Item
+ Details, Item,
+ drivers::Driver,
+ pivot::{Axis2, BorderStyle, Color, HorzAlign, PivotTable, Stroke, VertAlign},
+ table::{CellPos, CellRect, DrawCell, Table},
};
#[derive(Clone, Debug, Deserialize, Serialize)]
writeln!(&mut self.writer, ">")?;
if let Some(title) = output.title {
- let cell = title.get(CellPos::new(0, 0));
+ let cell = title.get(CellPos(0, 0));
self.put_cell(
DrawCell::new(cell.inner(), &title),
- IndexRect2::new(0..1, 0..1),
+ CellRect::new(0..1, 0..1),
"caption",
None,
)?;
writeln!(&mut self.writer, "<tr>")?;
self.put_cell(
DrawCell::new(cell.inner(), &layers),
- IndexRect2::new(0..output.body.n[Axis2::X], 0..1),
+ CellRect::new(0..output.body.n[Axis2::X], 0..1),
"td",
None,
)?;
}
writeln!(&mut self.writer, "<tbody>")?;
- for y in 0..output.body.n.y() {
+ for y in 0..output.body.n.1 {
writeln!(&mut self.writer, "<tr>")?;
for x in output.body.iter_x(y) {
- let cell = output.body.get(CellPos::new(x, y));
+ let cell = output.body.get(CellPos(x, y));
if cell.is_top_left() {
let is_header = x < output.body.h[Axis2::X] || y < output.body.h[Axis2::Y];
let tag = if is_header { "th" } else { "td" };
writeln!(&mut self.writer, "<tr>")?;
if let Some(caption) = output.caption {
self.put_cell(
- DrawCell::new(caption.get(CellPos::new(0, 0)).inner(), &caption),
- IndexRect2::new(0..output.body.n[Axis2::X], 0..1),
+ DrawCell::new(caption.get(CellPos(0, 0)).inner(), &caption),
+ CellRect::new(0..output.body.n[Axis2::X], 0..1),
"td",
None,
)?;
writeln!(&mut self.writer, "<tr>")?;
self.put_cell(
DrawCell::new(cell.inner(), &footnotes),
- IndexRect2::new(0..output.body.n[Axis2::X], 0..1),
+ CellRect::new(0..output.body.n[Axis2::X], 0..1),
"td",
None,
)?;
fn put_cell(
&mut self,
cell: DrawCell<'_, '_>,
- rect: IndexRect2,
+ rect: CellRect,
tag: &str,
table: Option<&Table>,
) -> std::io::Result<()> {
if rect[Axis2::X].end == table.n[Axis2::X] {
Self::put_border(
&mut style,
- table.get_rule(
- Axis2::X,
- CellPos::new(rect[Axis2::X].end, rect[Axis2::Y].start),
- ),
+ table.get_rule(Axis2::X, CellPos(rect[Axis2::X].end, rect[Axis2::Y].start)),
"right",
);
}
if rect[Axis2::Y].end == table.n[Axis2::Y] {
Self::put_border(
&mut style,
- table.get_rule(
- Axis2::Y,
- CellPos::new(rect[Axis2::X].start, rect[Axis2::Y].end),
- ),
+ table.get_rule(Axis2::Y, CellPos(rect[Axis2::X].start, rect[Axis2::Y].end)),
"bottom",
);
}
calendar::date_time_to_pspp,
data::{ByteString, Datum, EncodedString},
format::{
- Decimal, Format, Settings as FormatSettings, Type, UncheckedFormat, DATETIME40_0, F40, F40_2, F40_3, F8_2, PCT40_1
+ DATETIME40_0, Decimal, F8_2, F40, F40_2, F40_3, Format, PCT40_1,
+ Settings as FormatSettings, Type, UncheckedFormat,
},
- output::{spv::html::Markup, table::CellPos},
+ output::spv::html::Markup,
settings::{Settings, Show},
util::ToSmallString,
variable::{VarType, Variable},
}
}
-#[derive(Clone, Debug, Default)]
-pub struct IndexRect2(pub EnumMap<Axis2, Range<usize>>);
-
-impl IndexRect2 {
- pub fn new(x_range: Range<usize>, y_range: Range<usize>) -> Self {
- Self(enum_map! {
- Axis2::X => x_range.clone(),
- Axis2::Y => y_range.clone(),
- })
- }
- pub fn for_cell(cell: CellPos) -> Self {
- Self::new(cell.x()..cell.x() + 1, cell.y()..cell.y() + 1)
- }
- pub fn for_ranges((a, a_range): (Axis2, Range<usize>), b_range: Range<usize>) -> Self {
- let b = !a;
- let mut ranges = EnumMap::default();
- ranges[a] = a_range;
- ranges[b] = b_range;
- Self(ranges)
- }
- pub fn top_left(&self) -> CellPos {
- use Axis2::*;
- CellPos::new(self[X].start, self[Y].start)
- }
- pub fn from_fn<F>(f: F) -> Self
- where
- F: FnMut(Axis2) -> Range<usize>,
- {
- Self(EnumMap::from_fn(f))
- }
- pub fn translate(self, offset: CellPos) -> IndexRect2 {
- Self::from_fn(|axis| self[axis].start + offset[axis]..self[axis].end + offset[axis])
- }
- pub fn is_empty(&self) -> bool {
- self[Axis2::X].is_empty() || self[Axis2::Y].is_empty()
- }
-}
-
-impl From<EnumMap<Axis2, Range<usize>>> for IndexRect2 {
- fn from(value: EnumMap<Axis2, Range<usize>>) -> Self {
- Self(value)
- }
-}
-
-impl Index<Axis2> for IndexRect2 {
- type Output = Range<usize>;
-
- fn index(&self, index: Axis2) -> &Self::Output {
- &self.0[index]
- }
-}
-
-impl IndexMut<Axis2> for IndexRect2 {
- fn index_mut(&mut self, index: Axis2) -> &mut Self::Output {
- &mut self.0[index]
- }
-}
-
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum FootnoteMarkerType {
use itertools::Itertools;
use crate::output::{
- pivot::{HeadingRegion, IndexRect2, LabelPosition, Path, RowParity},
- table::{CellInner, CellPos, Table},
+ pivot::{HeadingRegion, LabelPosition, Path, RowParity},
+ table::{CellInner, CellPos, CellRect, Table},
};
use super::{
I: Iterator<Item = Box<Value>> + ExactSizeIterator,
{
let mut table = Table::new(
- CellPos::new(1, rows.len()),
- CellPos::new(0, 0),
+ CellPos(1, rows.len()),
+ CellPos(0, 0),
self.style.look.areas.clone(),
self.borders(false),
self.into_value_options(),
);
for (y, row) in rows.enumerate() {
- table.put(
- IndexRect2::for_cell(CellPos::new(0, y)),
- CellInner::new(area, row),
- );
+ table.put(CellRect::for_cell(CellPos(0, y)), CellInner::new(area, row));
}
table
}
let data = CellPos::from_fn(|axis| headings[axis].width());
let mut stub = CellPos::from_fn(|axis| headings[!axis].height());
- if headings[Axis2::Y].row_label_position == LabelPosition::Corner && stub.y() == 0 {
+ if headings[Axis2::Y].row_label_position == LabelPosition::Corner && stub.1 == 0 {
stub[Axis2::Y] = 1;
}
let mut body = Table::new(
let data_indexes = self.convert_indexes_ptod(presentation_indexes);
let value = self.get(&*data_indexes);
body.put(
- IndexRect2::new(x..x + 1, y..y + 1),
+ CellRect::new(x..x + 1, y..y + 1),
CellInner::new(
Area::Data(RowParity::from(y - stub[Axis2::Y])),
Box::new(value.cloned().unwrap_or_default()),
// are not in the corner.
if self.metadata.corner_text.is_some()
&& self.style.look.row_label_position == LabelPosition::Nested
- && stub.x() > 0
- && stub.y() > 0
+ && stub.0 > 0
+ && stub.0 > 0
{
body.put(
- IndexRect2::new(0..stub.x(), 0..stub.y()),
+ CellRect::new(0..stub.0, 0..stub.1),
CellInner::new(
Area::Corner,
self.metadata.corner_text.clone().unwrap_or_default(),
);
}
- if body.n.x() > 0 && body.n.y() > 0 {
- body.h_line(Border::InnerFrame(BoxBorder::Top), 0..body.n.x(), 0);
- body.h_line(
- Border::InnerFrame(BoxBorder::Bottom),
- 0..body.n.x(),
- body.n.y(),
- );
- body.v_line(Border::InnerFrame(BoxBorder::Left), 0, 0..body.n.y());
- body.v_line(
- Border::InnerFrame(BoxBorder::Right),
- body.n.x(),
- 0..body.n.y(),
- );
+ if body.n.0 > 0 && body.n.1 > 0 {
+ body.h_line(Border::InnerFrame(BoxBorder::Top), 0..body.n.0, 0);
+ body.h_line(Border::InnerFrame(BoxBorder::Bottom), 0..body.n.0, body.n.1);
+ body.v_line(Border::InnerFrame(BoxBorder::Left), 0, 0..body.n.1);
+ body.v_line(Border::InnerFrame(BoxBorder::Right), body.n.0, 0..body.n.1);
- body.h_line(Border::DataTop, 0..body.n.x(), stub.y());
- body.v_line(Border::DataLeft, stub.x(), 0..body.n.y());
+ body.h_line(Border::DataTop, 0..body.n.0, stub.1);
+ body.v_line(Border::DataLeft, stub.0, 0..body.n.1);
}
body
}
let rotate =
(rotate_inner_labels && is_inner_row) || (rotate_outer_labels && is_outer_row);
table.put(
- IndexRect2::for_ranges((h, x1 + h_ofs..x2 + h_ofs), y1..y2),
+ CellRect::for_ranges((h, x1 + h_ofs..x2 + h_ofs), y1..y2),
CellInner::new(Area::Labels(h), Box::new(name.clone())).with_rotate(rotate),
);
if dimension_label_position == LabelPosition::Corner {
table.put(
- IndexRect2::new(v_ofs..v_ofs + 1, 0..h_ofs),
+ CellRect::new(v_ofs..v_ofs + 1, 0..h_ofs),
CellInner::new(Area::Corner, self.dimension.root.name.clone()),
);
}
use num::Integer;
use smallvec::SmallVec;
-use crate::output::pivot::{IndexRect2, VertAlign};
-use crate::output::table::{DrawCell, CellPos};
+use crate::output::pivot::VertAlign;
+use crate::output::table::{CellPos, CellRect, DrawCell};
use super::pivot::{Axis2, BorderStyle, Coord2, Look, PivotTable, Rect2, Stroke};
use super::table::{Content, Table};
h: CellPos,
/// Main region of cells to render.
- r: IndexRect2,
+ r: CellRect,
/// Mappings from [Page] positions to those in the underlying [Table].
maps: EnumMap<Axis2, [Map; 2]>,
#[derive(Clone)]
pub struct RenderCell<'a> {
- rect: IndexRect2,
+ rect: CellRect,
content: &'a Content,
}
// Calculate minimum and maximum widths of cells that do not span
// multiple columns.
- let mut unspanned_columns = EnumMap::from_fn(|_| vec![0; n.x()]);
+ let mut unspanned_columns = EnumMap::from_fn(|_| vec![0; n.0]);
for cell in table.cells().filter(|cell| cell.col_span() == 1) {
let mut w = device.measure_cell_width(&DrawCell::new(cell.inner(), &table));
if device.params().px_size.is_some() {
// In pathological cases, spans can cause the minimum width of a column
// to exceed the maximum width. This bollixes our interpolation
// algorithm later, so fix it up.
- for i in 0..n.x() {
+ for i in 0..n.0 {
if columns[Min][i] > columns[Max][i] {
columns[Max][i] = columns[Min][i];
}
let w = joined_width(&cp_x, rect[X].clone());
let h = device.measure_cell_height(&DrawCell::new(cell.inner(), &table), w);
- let row = &mut unspanned_rows[cell.pos.y()];
+ let row = &mut unspanned_rows[cell.pos.1];
if h > *row {
*row = h;
}
h[axis] = 0;
}
}
- let r = IndexRect2::new(h[X]..n[X], h[Y]..n[Y]);
+ let r = CellRect::new(h[X]..n[X], h[Y]..n[Y]);
let maps = Self::new_mappings(h, &r);
Self {
table,
*self.cp[axis].last().unwrap()
}
- fn new_mappings(h: CellPos, r: &IndexRect2) -> EnumMap<Axis2, [Map; 2]> {
+ fn new_mappings(h: CellPos, r: &CellRect) -> EnumMap<Axis2, [Map; 2]> {
EnumMap::from_fn(|axis| {
[
Map {
let maps = EnumMap::from_fn(|axis| self.get_map(axis, coord[axis]));
let cell = self.table.get(self.map_coord(coord));
RenderCell {
- rect: IndexRect2(cell.rect().0.map(|axis, Range { start, end }| {
+ rect: CellRect(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)
})),
self.draw_cells(
device,
ofs,
- IndexRect2::new(0..self.n[X] * 2 + 1, 0..self.n[Y] * 2 + 1),
+ CellRect::new(0..self.n[X] * 2 + 1, 0..self.n[Y] * 2 + 1),
);
}
- fn draw_cells(&self, device: &mut dyn Device, ofs: Coord2, cells: IndexRect2) {
+ fn draw_cells(&self, device: &mut dyn Device, ofs: Coord2, cells: CellRect) {
use Axis2::*;
for y in cells[Y].clone() {
let mut x = cells[X].start;
while x < cells[X].end {
if !is_rule(x) && !is_rule(y) {
- let cell = self.get_cell(CellPos::new(x / 2, y / 2));
+ let cell = self.get_cell(CellPos(x / 2, y / 2));
self.draw_cell(device, ofs, &cell);
x = rule_ofs(cell.rect[X].end);
} else {
for y in cells[Y].clone() {
for x in cells[X].clone() {
if is_rule(x) || is_rule(y) {
- self.draw_rule(device, ofs, CellPos::new(x, y));
+ self.draw_rule(device, ofs, CellPos(x, y));
}
}
}
if self.axis == Axis2::Y && device.params().can_adjust_break {
let mut x = 0;
while x < self.page.n[Axis2::X] {
- let cell = self.page.get_cell(CellPos::new(x, z));
+ let cell = self.page.get_cell(CellPos(x, z));
let better_pixel = device.adjust_break(
cell.content,
Coord2::new(
use ndarray::{Array, Array2};
use crate::output::{
- pivot::{CellStyle, DisplayValue, FontStyle, Footnote, HorzAlign, IndexRect2, ValueInner},
+ pivot::{CellStyle, DisplayValue, FontStyle, Footnote, HorzAlign, ValueInner},
spv::html,
};
/// The `(x,y)` position of a cell in a [Table].
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
-pub struct CellPos(pub EnumMap<Axis2, usize>);
+pub struct CellPos(
+ /// X
+ pub usize,
+ /// Y
+ pub usize,
+);
impl CellPos {
- pub fn new(x: usize, y: usize) -> Self {
- use Axis2::*;
- Self(enum_map! {
- X => x,
- Y => y
- })
- }
-
pub fn for_axis((a, az): (Axis2, usize), bz: usize) -> Self {
- let mut coord = Self::default();
- coord[a] = az;
- coord[!a] = bz;
- coord
+ match a {
+ Axis2::X => Self(az, bz),
+ Axis2::Y => Self(bz, az),
+ }
}
- pub fn from_fn<F>(f: F) -> Self
+ pub fn from_fn<F>(mut f: F) -> Self
where
F: FnMut(Axis2) -> usize,
{
- Self(EnumMap::from_fn(f))
- }
-
- pub fn x(&self) -> usize {
- self.0[Axis2::X]
- }
-
- pub fn y(&self) -> usize {
- self.0[Axis2::Y]
- }
-
- pub fn get(&self, axis: Axis2) -> usize {
- self.0[axis]
- }
-}
-
-impl From<EnumMap<Axis2, usize>> for CellPos {
- fn from(value: EnumMap<Axis2, usize>) -> Self {
- Self(value)
+ Self(f(Axis2::X), f(Axis2::Y))
}
}
type Output = usize;
fn index(&self, index: Axis2) -> &Self::Output {
- &self.0[index]
+ match index {
+ Axis2::X => &self.0,
+ Axis2::Y => &self.1,
+ }
}
}
impl IndexMut<Axis2> for CellPos {
fn index_mut(&mut self, index: Axis2) -> &mut Self::Output {
- &mut self.0[index]
+ match index {
+ Axis2::X => &mut self.0,
+ Axis2::Y => &mut self.1,
+ }
+ }
+}
+
+/// A rectangular group of cells in a [Table].
+#[derive(Clone, Debug, Default)]
+pub struct CellRect(
+ /// The `x` range and `y` range spanned by the rectangle.
+ pub EnumMap<Axis2, Range<usize>>,
+);
+
+impl CellRect {
+ pub fn new(x_range: Range<usize>, y_range: Range<usize>) -> Self {
+ Self(enum_map! {
+ Axis2::X => x_range.clone(),
+ Axis2::Y => y_range.clone(),
+ })
+ }
+ pub fn for_cell(cell: CellPos) -> Self {
+ Self::new(cell.0..cell.0 + 1, cell.1..cell.1 + 1)
+ }
+ pub fn for_ranges((a, a_range): (Axis2, Range<usize>), b_range: Range<usize>) -> Self {
+ let b = !a;
+ let mut ranges = EnumMap::default();
+ ranges[a] = a_range;
+ ranges[b] = b_range;
+ Self(ranges)
+ }
+ pub fn top_left(&self) -> CellPos {
+ use Axis2::*;
+ CellPos(self[X].start, self[Y].start)
+ }
+ pub fn is_empty(&self) -> bool {
+ self[Axis2::X].is_empty() || self[Axis2::Y].is_empty()
}
}
self.content.is_empty()
}
- pub fn rect(&self) -> IndexRect2 {
+ pub fn rect(&self) -> CellRect {
self.content.rect(self.pos)
}
pub fn next_x(&self) -> usize {
- self.content.next_x(self.pos.x())
+ self.content.next_x(self.pos.0)
}
pub fn is_top_left(&self) -> bool {
/// Returns the rectangle that this cell covers, only if the cell contains
/// that information. (Joined cells always do, and other cells usually
/// don't.)
- pub fn joined_rect(&self) -> Option<&IndexRect2> {
+ pub fn joined_rect(&self) -> Option<&CellRect> {
match self {
Content::Join(cell) => Some(&cell.region),
_ => None,
/// Returns the rectangle that this cell covers. If the cell doesn't contain
/// that information, returns a rectangle containing `pos`.
- pub fn rect(&self, pos: CellPos) -> IndexRect2 {
+ pub fn rect(&self, pos: CellPos) -> CellRect {
match self {
Content::Join(cell) => cell.region.clone(),
- _ => IndexRect2::for_cell(pos),
+ _ => CellRect::for_cell(pos),
}
}
inner: CellInner,
/// Occupied table region.
- region: IndexRect2,
+ region: CellRect,
}
impl Cell {
- fn new(inner: CellInner, region: IndexRect2) -> Self {
+ fn new(inner: CellInner, region: CellRect) -> Self {
Self { inner, region }
}
}
Self {
n,
h: headers,
- contents: Array::default((n.x(), n.y())),
+ contents: Array::default((n.0, n.1)),
areas,
borders,
rules: enum_map! {
- Axis2::X => Array::default((n.x() + 1, n.y())),
- Axis2::Y => Array::default((n.x(), n.y() + 1)),
+ Axis2::X => Array::default((n.0 + 1, n.1)),
+ Axis2::Y => Array::default((n.0, n.1 + 1)),
},
value_options,
}
pub fn get(&self, coord: CellPos) -> CellRef<'_> {
CellRef {
pos: coord,
- content: &self.contents[[coord.x(), coord.y()]],
+ content: &self.contents[[coord.0, coord.1]],
}
}
pub fn get_rule(&self, axis: Axis2, pos: CellPos) -> BorderStyle {
- self.rules[axis][[pos.x(), pos.y()]].map_or(BorderStyle::none(), |b| self.borders[b])
+ self.rules[axis][[pos.0, pos.1]].map_or(BorderStyle::none(), |b| self.borders[b])
}
- pub fn put(&mut self, region: IndexRect2, inner: CellInner) {
+ pub fn put(&mut self, region: CellRect, inner: CellInner) {
use Axis2::*;
if region[X].len() == 1 && region[Y].len() == 1 {
self.contents[[region[X].start, region[Y].start]] = Content::Value(inner);
/// The heading region that `pos` is part of, if any.
pub fn heading_region(&self, pos: CellPos) -> Option<HeadingRegion> {
- if pos.x() < self.h.x() {
+ if pos.0 < self.h.0 {
Some(HeadingRegion::Rows)
- } else if pos.y() < self.h.y() {
+ } else if pos.1 < self.h.1 {
Some(HeadingRegion::Columns)
} else {
None
fn next(&mut self) -> Option<Self::Item> {
let next_x = self
.x
- .map_or(0, |x| self.table.get(CellPos::new(x, self.y)).next_x());
- if next_x >= self.table.n.x() {
+ .map_or(0, |x| self.table.get(CellPos(x, self.y)).next_x());
+ if next_x >= self.table.n.0 {
None
} else {
self.x = Some(next_x);
next: if table.is_empty() {
None
} else {
- Some(table.get(CellPos::new(0, 0)))
+ Some(table.get(CellPos(0, 0)))
},
}
}
self.next = loop {
let next_x = next.next_x();
let coord = if next_x < self.table.n[X] {
- CellPos::new(next_x, next.pos.y())
- } else if next.pos.y() + 1 < self.table.n[Y] {
- CellPos::new(0, next.pos.y() + 1)
+ CellPos(next_x, next.pos.1)
+ } else if next.pos.1 + 1 < self.table.n[Y] {
+ CellPos(0, next.pos.1 + 1)
} else {
break None;
};
use serde::Serialize;
use crate::{
- format::{Format, Settings as FormatSettings, F8_2},
+ format::{F8_2, Format, Settings as FormatSettings},
message::Severity,
output::pivot::Look,
};