use std::ops::Range;
use std::sync::Arc;
-use enum_map::EnumMap;
+use enum_map::{enum_map, Enum, EnumMap};
use itertools::interleave;
use num::Integer;
use smallvec::SmallVec;
}
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum Extreme {
+ Min,
+ Max,
+}
+
pub trait Device {
fn params(&self) -> &Params;
- /// Measures `cell`'s width. Returns an arary `[min_width, max_width]`,
- /// where `min_width` is the minimum width required to avoid splitting a
- /// single word across multiple lines (normally, this is the width of the
- /// longest word in the cell) and `max_width` is the minimum width required
- /// to avoid line breaks other than at new-lines.
- fn measure_cell_width(&self, cell: &DrawCell) -> [usize; 2];
+ /// Measures `cell`'s width. Returns `map`, where:
+ ///
+ /// - `map[Extreme::Min]` is the minimum width required to avoid splitting a
+ /// single word across multiple lines. This is usually the width of the
+ /// longest word in the cell.
+ ///
+ /// - `map[Extreme::Max]` is the minimum width required to avoid line breaks
+ /// other than at new-lines.
+ fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, usize>;
/// Returns the height required to render `cell` given a width of `width`.
fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize;
/// breaking it up to fit on such a device, using the [Break] abstraction.
fn new(table: Arc<Table>, device: &dyn Device, min_width: usize, look: &Look) -> Self {
use Axis2::*;
+ use Extreme::*;
let n = table.n;
});
let px_size = device.params().px_size.unwrap_or_default();
- let heading_widths = look
- .heading_widths
- .clone()
- .map(|_region, range| [*range.start() * px_size, *range.end() * px_size]);
+ let heading_widths = look.heading_widths.clone().map(|_region, range| {
+ enum_map![
+ Min => *range.start() * px_size,
+ Max => *range.end() * px_size
+ ]
+ });
// Calculate minimum and maximum widths of cells that do not span
// multiple columns.
- let mut unspanned_columns = [vec![0; n.x()], vec![0; n.x()]];
+ 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[0] < wr[0] {
- w[0] = wr[0];
- if w[0] > w[1] {
- w[1] = w[0];
+ if w[Min] < wr[Min] {
+ w[Min] = wr[Min];
+ if w[Min] > w[Max] {
+ w[Max] = w[Min];
}
- } else if w[1] > wr[1] {
- w[1] = wr[1];
- if w[1] < w[0] {
- w[0] = w[1];
+ } 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 i in 0..2 {
- if unspanned_columns[i][x] < w[i] {
- unspanned_columns[i][x] = w[i];
+ for ext in [Min, Max] {
+ if unspanned_columns[ext][x] < w[ext] {
+ unspanned_columns[ext][x] = w[ext];
}
}
}
let rect = cell.rect();
let w = device.measure_cell_width(&DrawCell::new(cell.inner(), &table));
- for i in 0..2 {
+ for ext in [Min, Max] {
+ dbg!(&cell, ext, w[ext]);
distribute_spanned_width(
- w[i],
- &unspanned_columns[i][rect[X].clone()],
- &mut columns[i][rect[X].clone()],
- &rules[X][rect[X].start..rect[X].end + 1],
+ w[ext],
+ dbg!(&unspanned_columns[ext][rect[X].clone()]),
+ dbg!(&mut columns[ext][rect[X].clone()]),
+ dbg!(&rules[X][rect[X].start..rect[X].end + 1]),
);
+ dbg!(&mut columns[ext][rect[X].clone()]);
+ eprintln!();
}
}
if min_width > 0 {
- for i in 0..2 {
+ for ext in [Min, Max] {
distribute_spanned_width(
min_width,
- &unspanned_columns[i],
- &mut columns[i],
+ &unspanned_columns[ext],
+ &mut columns[ext],
&rules[X],
);
}
// to exceed the maximum width. This bollixes our interpolation
// algorithm later, so fix it up.
for i in 0..n.x() {
- if columns[0][i] > columns[1][i] {
- columns[1][i] = columns[0][i];
+ if columns[Min][i] > columns[Max][i] {
+ columns[Max][i] = columns[Min][i];
}
}
// Decide final column widths.
let rule_widths = rules[X].iter().copied().sum::<usize>();
- let table_widths = columns
- .iter()
- .map(|row| row.iter().sum::<usize>() + rule_widths)
- .collect::<SmallVec<[usize; 2]>>();
+ let table_widths = EnumMap::from_fn(|ext| columns[ext].iter().sum::<usize>() + rule_widths);
- let cp_x = if table_widths[1] <= device.params().size[X] {
+ let cp_x = if table_widths[Max] <= device.params().size[X] {
// Fits even with maximum widths. Use them.
- Self::use_row_widths(&columns[1], &rules[X])
- } else if table_widths[0] <= device.params().size[X] {
+ Self::use_row_widths(&columns[Max], &rules[X])
+ } else if table_widths[Min] <= device.params().size[X] {
// Fits with minimum widths, so distribute the leftover space.
//Self::new_with_interpolated_widths()
Self::interpolate_row_widths(
device.params(),
- &columns[0],
- &columns[1],
- table_widths[0],
- table_widths[1],
+ &columns[Min],
+ &columns[Max],
+ table_widths[Min],
+ table_widths[Max],
&rules[X],
)
} else {
// Doesn't fit even with minimum widths. Assign minimums for now, and
// later we can break it horizontally into multiple pages.
- Self::use_row_widths(&columns[0], &rules[X])
+ Self::use_row_widths(&columns[Min], &rules[X])
};
// Calculate heights of cells that do not span multiple rows.
rules: &[usize],
) {
let n = unspanned.len();
- debug_assert!(n >= 1);
+ 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 - 1)
+ .get(1..n)
.map_or(0, |rules| rules.iter().copied().sum::<usize>());
+ dbg!(total_unspanned, width);
if total_unspanned >= width {
return;
}
sync::{Arc, LazyLock},
};
-use enum_map::{Enum, EnumMap};
+use enum_map::{enum_map, Enum, EnumMap};
use unicode_linebreak::{linebreaks, BreakOpportunity};
use unicode_width::UnicodeWidthStr;
-use crate::output::text_line::Emphasis;
+use crate::output::{render::Extreme, text_line::Emphasis};
use super::{
driver::Driver,
&self.params
}
- fn measure_cell_width(&self, cell: &DrawCell) -> [usize; 2] {
+ fn measure_cell_width(&self, cell: &DrawCell) -> EnumMap<Extreme, usize> {
let text = Self::display_cell(cell).to_string();
- let max_width = self.layout_cell(&text, Rect2::new(0..usize::MAX, 0..usize::MAX));
- let min_width = self.layout_cell(&text, Rect2::new(0..1, 0..usize::MAX));
- [min_width.x(), max_width.x()]
+ enum_map![
+ Extreme::Min => self.layout_cell(&text, Rect2::new(0..1, 0..usize::MAX)).x(),
+ Extreme::Max => self.layout_cell(&text, Rect2::new(0..usize::MAX, 0..usize::MAX)).x(),
+ ]
}
fn measure_cell_height(&self, cell: &DrawCell, width: usize) -> usize {