use std::cmp::{max, min};
use std::collections::HashMap;
use std::iter::once;
use std::ops::Range;
use std::sync::Arc;
use enum_map::{enum_map, Enum, EnumMap};
use itertools::interleave;
use num::Integer;
use smallvec::SmallVec;
use crate::output::pivot::VertAlign;
use crate::output::table::DrawCell;
use super::pivot::{Axis2, BorderStyle, Coord2, Look, PivotTable, Rect2, Stroke};
use super::table::{Content, Table};
pub struct Params {
pub size: Coord2,
pub font_size: EnumMap<Axis2, usize>,
pub line_widths: EnumMap<Stroke, usize>,
pub px_size: Option<usize>,
pub min_break: EnumMap<Axis2, usize>,
pub supports_margins: bool,
pub rtl: bool,
pub printing: bool,
pub can_adjust_break: bool,
pub can_scale: bool,
}
impl Params {
fn em(&self) -> usize {
self.font_size[Axis2::X]
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
pub enum Extreme {
Min,
Max,
}
pub trait Device {
fn params(&self) -> &Params;
fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, usize>;
fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize;
fn adjust_break(&self, cell: &Content, size: Coord2) -> usize;
fn draw_line(&mut self, bb: Rect2, styles: EnumMap<Axis2, [BorderStyle; 2]>);
fn draw_cell(
&mut self,
draw_cell: &DrawCell,
alternate_row: bool,
bb: Rect2,
valign_offset: usize,
spill: EnumMap<Axis2, [usize; 2]>,
clip: &Rect2,
);
fn scale(&mut self, factor: f64);
}
#[derive(Debug)]
struct Page {
table: Arc<Table>,
n: Coord2,
h: Coord2,
r: Rect2,
maps: EnumMap<Axis2, [Map; 2]>,
cp: EnumMap<Axis2, Vec<usize>>,
overflows: HashMap<Coord2, EnumMap<Axis2, [usize; 2]>>,
is_edge_cutoff: EnumMap<Axis2, [bool; 2]>,
}
fn axis_width(cp: &[usize], extent: Range<usize>) -> usize {
cp[extent.end] - cp[extent.start]
}
fn joined_width(cp: &[usize], extent: Range<usize>) -> usize {
axis_width(cp, cell_ofs(extent.start)..cell_ofs(extent.end) - 1)
}
fn cell_ofs(cell_index: usize) -> usize {
cell_index * 2 + 1
}
fn rule_ofs(rule_index: usize) -> usize {
rule_index * 2
}
fn cell_width(cp: &[usize], z: usize) -> usize {
let ofs = cell_ofs(z);
axis_width(cp, ofs..ofs + 1)
}
fn is_rule(z: usize) -> bool {
z.is_even()
}
#[derive(Clone)]
pub struct RenderCell<'a> {
rect: Rect2,
content: &'a Content,
}
impl Page {
fn new(table: Arc<Table>, device: &dyn Device, min_width: usize, look: &Look) -> Self {
use Axis2::*;
use Extreme::*;
let n = table.n;
let rules = EnumMap::from_fn(|axis| {
(0..=n[axis])
.map(|z| measure_rule(device, &table, axis, z))
.collect::<Vec<_>>()
});
let px_size = device.params().px_size.unwrap_or_default();
let heading_widths = look.heading_widths.clone().map(|_region, range| {
enum_map![
Min => *range.start() * px_size,
Max => *range.end() * px_size
]
});
let mut unspanned_columns = EnumMap::from_fn(|_| vec![0; n.x()]);
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() {
if let Some(region) = table.heading_region(cell.coord) {
let wr = &heading_widths[region];
if w[Min] < wr[Min] {
w[Min] = wr[Min];
if w[Min] > w[Max] {
w[Max] = w[Min];
}
} else if w[Max] > wr[Max] {
w[Max] = wr[Max];
if w[Max] < w[Min] {
w[Min] = w[Max];
}
}
}
}
let x = cell.coord[X];
for ext in [Min, Max] {
if unspanned_columns[ext][x] < w[ext] {
unspanned_columns[ext][x] = w[ext];
}
}
}
let mut columns = unspanned_columns.clone();
for cell in table.cells().filter(|cell| cell.col_span() > 1) {
let rect = cell.rect();
let w = device.measure_cell_width(&DrawCell::new(cell.inner(), &table));
for ext in [Min, Max] {
distribute_spanned_width(
w[ext],
&unspanned_columns[ext][rect[X].clone()],
&mut columns[ext][rect[X].clone()],
&rules[X][rect[X].start..rect[X].end + 1],
);
}
}
if min_width > 0 {
for ext in [Min, Max] {
distribute_spanned_width(
min_width,
&unspanned_columns[ext],
&mut columns[ext],
&rules[X],
);
}
}
for i in 0..n.x() {
if columns[Min][i] > columns[Max][i] {
columns[Max][i] = columns[Min][i];
}
}
let rule_widths = rules[X].iter().copied().sum::<usize>();
let table_widths = EnumMap::from_fn(|ext| columns[ext].iter().sum::<usize>() + rule_widths);
let cp_x = if table_widths[Max] <= device.params().size[X] {
Self::use_row_widths(&columns[Max], &rules[X])
} else if table_widths[Min] <= device.params().size[X] {
Self::interpolate_row_widths(
device.params(),
&columns[Min],
&columns[Max],
table_widths[Min],
table_widths[Max],
&rules[X],
)
} else {
Self::use_row_widths(&columns[Min], &rules[X])
};
let mut unspanned_rows = vec![0; n[Y]];
for cell in table.cells().filter(|cell| cell.row_span() == 1) {
let rect = cell.rect();
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.coord.y()];
if h > *row {
*row = h;
}
}
let mut rows = unspanned_rows.clone();
for cell in table.cells().filter(|cell| cell.row_span() > 1) {
let rect = cell.rect();
let w = joined_width(&cp_x, rect[X].clone());
let h = device.measure_cell_height(&DrawCell::new(cell.inner(), &table), w);
distribute_spanned_width(
h,
&unspanned_rows[rect[Y].clone()],
&mut rows[rect[Y].clone()],
&rules[Y][rect[Y].start..rect[Y].end + 1],
);
}
let cp_y = Self::use_row_widths(&rows, &rules[Y]);
let mut h = table.h;
for (axis, cp) in [(X, cp_x.as_slice()), (Y, cp_y.as_slice())] {
let header_width = axis_width(cp, 0..table.h[axis]);
let max_cell_width = (table.h[axis]..n[axis])
.map(|z| cell_width(cp, z))
.max()
.unwrap_or(0);
let threshold = device.params().size[axis];
if header_width * 2 >= threshold || header_width + max_cell_width > threshold {
h[axis] = 0;
}
}
let r = Rect2::new(h[X]..n[X], h[Y]..n[Y]);
let maps = Self::new_mappings(h, &r);
Self {
table,
n,
h,
r,
cp: Axis2::new_enum(cp_x, cp_y),
overflows: HashMap::new(),
is_edge_cutoff: EnumMap::default(),
maps,
}
}
fn use_row_widths(rows: &[usize], rules: &[usize]) -> Vec<usize> {
let mut vec = once(0)
.chain(interleave(rules, rows).copied())
.collect::<Vec<_>>();
for i in 1..vec.len() {
vec[i] += vec[i - 1]
}
vec
}
fn interpolate_row_widths(
params: &Params,
rows_min: &[usize],
rows_max: &[usize],
w_min: usize,
w_max: usize,
rules: &[usize],
) -> Vec<usize> {
let avail = params.size[Axis2::X] - w_min;
let wanted = w_max - w_min;
let mut w = wanted / 2;
let rows_mid = rows_min
.iter()
.copied()
.zip(rows_max.iter().copied())
.map(|(min, max)| {
w += avail * (max - min);
let extra = w / wanted;
w -= extra * wanted;
min + extra
})
.collect::<Vec<_>>();
Self::use_row_widths(&rows_mid, rules)
}
fn axis_width(&self, axis: Axis2, extent: Range<usize>) -> usize {
axis_width(&self.cp[axis], extent)
}
fn joined_width(&self, axis: Axis2, extent: Range<usize>) -> usize {
joined_width(&self.cp[axis], extent)
}
fn headers_width(&self, axis: Axis2) -> usize {
self.axis_width(axis, rule_ofs(0)..cell_ofs(self.h[axis]))
}
fn rule_width(&self, axis: Axis2, z: usize) -> usize {
let ofs = rule_ofs(z);
self.axis_width(axis, ofs..ofs + 1)
}
fn rule_width_r(&self, axis: Axis2, z: usize) -> usize {
let ofs = self.rule_ofs_r(axis, z);
self.axis_width(axis, ofs..ofs + 1)
}
fn rule_ofs_r(&self, axis: Axis2, rule_index_r: usize) -> usize {
(self.n[axis] - rule_index_r) * 2
}
fn cell_width(&self, axis: Axis2, z: usize) -> usize {
let ofs = cell_ofs(z);
self.axis_width(axis, ofs..ofs + 1)
}
fn max_cell_width(&self, axis: Axis2) -> usize {
(self.h[axis]..self.n[axis])
.map(|z| self.cell_width(axis, z))
.max()
.unwrap_or(0)
}
fn width(&self, axis: Axis2) -> usize {
*self.cp[axis].last().unwrap()
}
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_map(&self, axis: Axis2, z: usize) -> &Map {
if z < self.h[axis] {
&self.maps[axis][0]
} else {
&self.maps[axis][1]
}
}
fn map_z(&self, axis: Axis2, z: usize) -> usize {
z + self.get_map(axis, z).ofs
}
fn map_coord(&self, coord: Coord2) -> Coord2 {
Coord2::from_fn(|a| self.map_z(a, coord[a]))
}
fn get_cell(&self, coord: Coord2) -> RenderCell<'_> {
let maps = EnumMap::from_fn(|axis| self.get_map(axis, coord[axis]));
let cell = self.table.get(self.map_coord(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,
}
}
fn select(
self: &Arc<Self>,
a: Axis2,
extent: Range<usize>,
pixel0: usize,
pixel1: usize,
) -> Arc<Self> {
let b = !a;
let z0 = extent.start;
let z1 = extent.end;
if z0 == self.h[a] && z1 == self.n[a] && pixel0 == 0 && pixel1 == 0 {
return self.clone();
}
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];
let mut is_edge_cutoff = self.is_edge_cutoff;
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]);
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[a]) {
total += if z == 0 && is_edge_cutoff[a][0] {
0
} else {
scp[z + 1] - scp[z]
};
dcp.push(total);
}
for z in cell_ofs(z0)..=cell_ofs(z1 - 1) {
total += scp[z + 1] - scp[z];
if z == cell_ofs(z0) {
total -= pixel0;
}
if z == cell_ofs(z1 - 1) {
total -= pixel1;
}
dcp.push(total);
}
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(), 1 + 2 * n[a] + 1);
let mut cp = EnumMap::default();
cp[a] = dcp;
cp[!a] = self.cp[!a].clone();
let mut overflows = HashMap::new();
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((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();
}
}
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.contains_key(&s.coord_to_subpage(cell.rect.top_left()))
{
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();
}
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);
}
}
let maps = Self::new_mappings(h, &r);
Arc::new(Self {
table: self.table.clone(),
n,
h,
r,
maps,
cp,
overflows,
is_edge_cutoff,
})
}
fn total_size(&self, axis: Axis2) -> usize {
self.cp[axis].last().copied().unwrap()
}
fn draw(&self, device: &mut dyn Device, ofs: Coord2) {
use Axis2::*;
self.draw_cells(
device,
ofs,
Rect2::new(0..self.n[X] * 2 + 1, 0..self.n[Y] * 2 + 1),
);
}
fn draw_cells(&self, device: &mut dyn Device, ofs: Coord2, cells: Rect2) {
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(Coord2::new(x / 2, y / 2));
self.draw_cell(device, ofs, &cell);
x = rule_ofs(cell.rect[X].end);
} else {
x += 1;
}
}
}
for y in cells[Y].clone() {
for x in cells[X].clone() {
if is_rule(x) || is_rule(y) {
self.draw_rule(device, ofs, Coord2::new(x, y));
}
}
}
}
fn draw_rule(&self, device: &mut dyn Device, ofs: Coord2, coord: Coord2) {
const NO_BORDER: BorderStyle = BorderStyle::none();
let styles = EnumMap::from_fn(|a: Axis2| {
let b = !a;
if !is_rule(coord[a])
|| (self.is_edge_cutoff[a][0] && coord[a] == 0)
|| (self.is_edge_cutoff[a][1] && coord[a] == self.n[a] * 2)
{
[NO_BORDER, NO_BORDER]
} else if is_rule(coord[b]) {
let first = if coord[b] > 0 {
let mut e = coord;
e[b] -= 1;
self.get_rule(a, e)
} else {
NO_BORDER
};
let second = if coord[b] / 2 < self.n[b] {
self.get_rule(a, coord)
} else {
NO_BORDER
};
[first, second]
} else {
let rule = self.get_rule(a, coord);
[rule, rule]
}
});
if !styles
.values()
.all(|border| border.iter().all(BorderStyle::is_none))
{
let bb =
Rect2::from_fn(|a| self.cp[a][coord[a]]..self.cp[a][coord[a] + 1]).translate(ofs);
device.draw_line(bb, styles);
}
}
fn get_rule(&self, a: Axis2, coord: Coord2) -> BorderStyle {
let coord = Coord2::from_fn(|a| coord[a] / 2);
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 border2 = self
.table
.get_rule(a, Coord2::for_axis((a, self.h[a]), coord[!a]));
border.combine(border2)
} else {
border
}
}
fn extra_height(&self, device: &dyn Device, bb: &Rect2, cell: &DrawCell) -> usize {
use Axis2::*;
let height = device.measure_cell_height(cell, bb[X].len());
usize::saturating_sub(bb[Y].len(), height)
}
fn draw_cell(&self, device: &mut dyn Device, ofs: Coord2, cell: &RenderCell) {
use Axis2::*;
let mut bb = Rect2::from_fn(|a| {
self.cp[a][cell.rect[a].start * 2 + 1]..self.cp[a][cell.rect[a].end * 2]
})
.translate(ofs);
let spill = EnumMap::from_fn(|_| [0, 0]);
let clip = if let Some(overflow) = self.overflows.get(&cell.rect.top_left()) {
Rect2::from_fn(|a| {
let mut clip = bb[a].clone();
if overflow[a][0] > 0 {
bb[a].start -= overflow[a][0];
if cell.rect[a].start == 0 && !self.is_edge_cutoff[a][0] {
clip.start = ofs[a] + self.cp[a][cell.rect[a].start * 2];
}
}
if overflow[a][1] > 0 {
bb[a].end += overflow[a][1];
if cell.rect[a].end == self.n[a] && !self.is_edge_cutoff[a][1] {
clip.end = ofs[a] + self.cp[a][cell.rect[a].end * 2 + 1];
}
}
clip
})
} else {
bb.clone()
};
let alternate_row =
usize::checked_sub(cell.rect[Y].start, self.h[Y]).is_some_and(|row| row % 2 == 1);
let draw_cell = DrawCell::new(cell.content.inner(), &self.table);
let valign_offset = match draw_cell.style.cell_style.vert_align {
VertAlign::Top => 0,
VertAlign::Middle => self.extra_height(device, &bb, &draw_cell) / 2,
VertAlign::Bottom => self.extra_height(device, &bb, &draw_cell),
};
device.draw_cell(&draw_cell, alternate_row, bb, valign_offset, spill, &clip)
}
}
struct Selection {
a: Axis2,
b: Axis2,
z0: usize,
z1: usize,
p0: usize,
p1: usize,
h: Coord2,
}
impl Selection {
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])
}
}
#[derive(Copy, Clone, Debug)]
struct Map {
p0: usize,
t0: usize,
ofs: usize,
n: usize,
}
fn distribute_spanned_width(
width: usize,
unspanned: &[usize],
spanned: &mut [usize],
rules: &[usize],
) {
let n = unspanned.len();
if n == 0 {
return;
}
debug_assert_eq!(spanned.len(), n);
debug_assert_eq!(rules.len(), n + 1);
let total_unspanned = unspanned.iter().sum::<usize>()
+ rules
.get(1..n)
.map_or(0, |rules| rules.iter().copied().sum::<usize>());
if total_unspanned >= width {
return;
}
let d0 = n;
let d1 = 2 * total_unspanned.max(1);
let d = if total_unspanned > 0 {
d0 * d1 * 2
} else {
d0 * d1
};
let mut w = d / 2;
for x in 0..n {
w += width * d1;
if total_unspanned > 0 {
let mut unspanned = unspanned[x] * 2;
if x + 1 < n {
unspanned += rules[x + 1];
}
if x > 0 {
unspanned += rules[x];
}
w += width * unspanned * d0;
}
spanned[x] = max(spanned[x], w / d);
w = w.checked_sub(spanned[x] * d).unwrap();
}
}
fn measure_rule(device: &dyn Device, table: &Table, a: Axis2, z: usize) -> usize {
let b = !a;
let mut rules = EnumMap::default();
for w in 0..table.n[b] {
let stroke = table.get_rule(a, Coord2::for_axis((a, z), w)).stroke;
rules[stroke] = true;
}
if rules[Stroke::None] {
rules[Stroke::None] = false;
if z > 0 && z < table.n[a] && !device.params().supports_margins && a == Axis2::X {
rules[Stroke::Solid] = true;
}
}
let line_widths = &device.params().line_widths;
rules
.into_iter()
.map(
|(rule, present)| {
if present {
line_widths[rule]
} else {
0
}
},
)
.max()
.unwrap_or(0)
}
#[derive(Debug)]
struct Break {
page: Arc<Page>,
axis: Axis2,
z: usize,
pixel: usize,
hw: usize,
}
impl Break {
fn new(page: Arc<Page>, axis: Axis2) -> Self {
let z = page.h[axis];
let hw = page.headers_width(axis);
Self {
page,
axis,
z,
pixel: 0,
hw,
}
}
fn has_next(&self) -> bool {
self.z < self.page.n[self.axis]
}
fn needed_size(&self, cell: usize) -> usize {
let mut size = self
.page
.axis_width(self.axis, 0..rule_ofs(self.page.h[self.axis]));
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.z),
);
}
size += self
.page
.joined_width(self.axis, self.z..cell)
.checked_sub(self.pixel)
.unwrap();
size += max(
self.page.rule_width_r(self.axis, 0),
self.page.rule_width(self.axis, cell),
);
size
}
fn next(&mut self, device: &dyn Device, size: usize) -> Option<Arc<Page>> {
if !self.has_next() {
return None;
}
self.find_breakpoint(device, size).map(|(z, pixel)| {
let page = 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,
),
};
self.z = z;
self.pixel = pixel;
page
})
}
fn break_cell(&self, device: &dyn Device, z: usize, overflow: usize) -> usize {
if self.cell_is_breakable(device, z) {
let rule_allowance = self.page.rule_width(self.axis, z);
let overhang = overflow - rule_allowance;
let cell_size = self.page.cell_width(self.axis, z);
let cell_ofs = if z == self.z { self.pixel } else { 0 };
let cell_left = cell_size - cell_ofs;
let mut pixel = if cell_left > 0 && cell_left > overhang {
cell_left - overhang + cell_ofs
} else {
0
};
let em = device.params().em();
if pixel + em > cell_size {
pixel = pixel.saturating_sub(em);
}
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(Coord2::new(x, z));
let better_pixel = 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;
}
}
}
}
pixel
} else {
0
}
}
fn find_breakpoint(&mut self, device: &dyn Device, 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(device, z, needed - size);
if z == self.z && pixel == 0 {
return None;
} else {
return Some((z, pixel));
}
}
}
Some((self.page.n[self.axis], 0))
}
fn cell_is_breakable(&self, device: &dyn Device, cell: usize) -> bool {
self.page.cell_width(self.axis, cell) >= device.params().min_break[self.axis]
}
}
pub struct Pager {
scale: f64,
pages: SmallVec<[Arc<Page>; 5]>,
x_break: Option<Break>,
y_break: Option<Break>,
}
impl Pager {
pub fn new(
device: &dyn Device,
pivot_table: &PivotTable,
layer_indexes: Option<&[usize]>,
) -> Self {
let output = pivot_table.output(
layer_indexes.unwrap_or(&pivot_table.current_layer),
device.params().printing,
);
let body_page = Page::new(Arc::new(output.body), device, 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
};
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,
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,
0,
&pivot_table.look,
)));
}
pages.reverse();
if pivot_table.look.shrink_to_fit[Axis2::Y] && device.params().can_scale {
let total_height = pages
.iter()
.map(|page: &Arc<Page>| page.total_size(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 {
scale,
pages,
x_break: None,
y_break: None,
}
}
pub fn has_next(&mut self, device: &dyn Device) -> 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(
device,
(device.params().size[Axis2::X] as f64 / self.scale) 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,
}
}
}
true
}
pub fn draw_next(&mut self, device: &mut dyn Device, mut space: usize) -> usize {
use Axis2::*;
if self.scale != 1.0 {
device.scale(self.scale);
space = (space as f64 / self.scale) as usize;
}
let mut ofs = Coord2::new(0, 0);
while self.has_next(device) {
let Some(page) = self
.y_break
.as_mut()
.and_then(|y_break| y_break.next(device, space - ofs[Y]))
else {
break;
};
page.draw(device, ofs);
ofs[Y] += page.total_size(Y);
}
if self.scale != 1.0 {
ofs[Y] = (ofs[Y] as f64 * self.scale) as usize;
}
ofs[Y]
}
}