use std::cmp::{max, min};
use std::collections::HashMap;
-use std::iter::once;
+use std::iter::{once, zip};
use std::ops::Range;
use std::sync::Arc;
/// "Cell positions".
///
- /// 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[X][2 * n[X]] = x position of the rightmost vertical rule.
- /// cp[X][2 * n[X] + 1] = total table width including all rules.
+ /// `cp[X]` represents `x` positions within the table:
///
- /// 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[Y][2 * n[Y]] = y position of the bottommost horizontal rule.
- /// cp[Y][2 * n[Y] + 1] = total table height including all rules.
+ /// - `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.
+ /// - ...
+ /// - `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[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.
+ /// - ...
+ /// - `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.
cp: EnumMap<Axis2, Vec<usize>>,
- /// [Break::next] can break a table such that some cells are not fully
- /// contained within a render_page. This will happen if a cell is too wide
- /// or two tall to fit on a single page, or if a cell spans multiple rows
- /// or columns and the page only includes some of those rows or columns.
+ /// [Break::next] can break a [Page] in the middle of a cell, if a cell is
+ /// too wide or two tall to fit on a single page, or if a cell spans
+ /// multiple rows or columns and the page only includes some of those rows
+ /// or columns.
///
- /// This hash table contains represents each such cell that doesn't
- /// completely fit on this page.
+ /// This hash table represents each such cell that doesn't completely fit on
+ /// this page.
///
/// Each overflow cell borders at least one header edge of the table and may
/// border more. (A single table cell that is so large that it fills the
///
/// # Interpretation
///
- /// 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.
+ /// Given `overflow` as a value in the [HashMap]:
+ ///
+ /// - `overflow[Axis2::X][0]`: space trimmed off the cell's left side.
+ /// - `overflow[Axis2::X][1]`: space trimmed off the cell's right side.
+ /// - `overflow[Axis2::Y][0]`: space trimmed off the cell's top.
+ /// - `overflow[Axis2::Y][1]`: space trimmed off the cell's bottom.
///
/// During rendering, this information is used to position the rendered
/// portion of the cell within the available space.
/// still included in overflow values.
///
/// Suppose, for example, that a cell that joins 2 columns has a width of 60
- /// pixels and content "abcdef", that the 2 columns that it joins have
+ /// pixels and content `abcdef`, that the 2 columns that it joins have
/// widths of 20 and 30 pixels, respectively, and that therefore the rule
/// between the two joined columns has a width of 10 (20 + 10 + 30 = 60).
/// It might render like this, if each character is 10x10, and showing a few
/// extra table cells for context:
///
/// ```text
- /// +------+
- /// |abcdef|
- /// +--+---+
- /// |gh|ijk|
- /// +--+---+
+ /// ┌──────┐
+ /// │abcdef│
+ /// ├──┬───┤
+ /// │gh│ijk│
+ /// └──┴───┘
/// ```
///
- /// 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[X][1] of 10 + 30 = 40 for its portion of the cell,
+ /// If this [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[Axis2::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[X][0] of 20 + 10 = 30. The two resulting pages would look like
+ /// `overflow[Axis2::X][0]` of 20 + 10 = 30. The two resulting pages would look like
/// this:
///
/// ```text
- /// +---
- /// |abc
- /// +--+
- /// |gh|
- /// +--+
+ /// ┌───
+ /// │abc
+ /// ├──┬
+ /// │gh│
+ /// └──┴
/// ```
///
/// and:
///
/// ```text
- /// ----+
- /// cdef|
- /// +---+
- /// |ijk|
- /// +---+
+ /// ────┐
+ /// cdef│
+ /// ┬───┤
+ /// │ijk│
+ /// ┴───┘
/// ```
/// Each entry maps from a cell that overflows to the space that has been
- /// trimmed off the cell:
+ /// trimmed off the cell.
overflows: HashMap<Coord2, EnumMap<Axis2, [usize; 2]>>,
/// If a single column (or row) is too wide (or tall) to fit on a page
- /// reasonably, then render_break_next() will split a single row or column
- /// across multiple render_pages. This member indicates when this has
- /// happened:
+ /// reasonably, then [Break::next] will split a single row or column across
+ /// multiple [Page]s. This member indicates when this has happened:
///
/// 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.
let cp_x = if table_widths[Max] <= device.params().size[X] {
// Fits even with maximum widths. Use them.
Self::use_row_widths(&columns[Max], &rules[X])
- } else if table_widths[Min] <= device.params().size[X] {
+ } else if device.params().size[X] > table_widths[Min] {
// Fits with minimum widths, so distribute the leftover space.
- //Self::new_with_interpolated_widths()
- Self::interpolate_row_widths(
- device.params(),
- &columns[Min],
- &columns[Max],
- table_widths[Min],
- table_widths[Max],
+ Self::interpolate_column_widths(
+ device.params().size[Axis2::X],
+ &columns,
+ &table_widths,
&rules[X],
)
} else {
vec
}
- fn interpolate_row_widths(
- params: &Params,
- rows_min: &[usize],
- rows_max: &[usize],
- w_min: usize,
- w_max: usize,
+ fn interpolate_column_widths(
+ target: usize,
+ columns: &EnumMap<Extreme, Vec<usize>>,
+ widths: &EnumMap<Extreme, usize>,
rules: &[usize],
) -> Vec<usize> {
- let avail = params.size[Axis2::X] - w_min;
- let wanted = w_max - w_min;
+ use Extreme::*;
+
+ let avail = target - widths[Min];
+ let wanted = widths[Max] - widths[Min];
let mut w = wanted / 2;
- let rows_mid = rows_min
- .iter()
- .copied()
- .zip(rows_max.iter().copied())
+ let rows_mid = zip(columns[Min].iter().copied(), columns[Max].iter().copied())
.map(|(min, max)| {
w += avail * (max - min);
let extra = w / wanted;