use header.range field

a bit verbose at times but seems potentially more consistent
This commit is contained in:
PgBiel 2025-06-10 01:36:53 -03:00
parent 88c9dea3fa
commit b997450a4b
4 changed files with 31 additions and 42 deletions

View File

@ -287,9 +287,9 @@ impl<'a> GridLayouter<'a> {
while y < self.grid.rows.len() { while y < self.grid.rows.len() {
if let Some(next_header) = self.upcoming_headers.get(consecutive_header_count) if let Some(next_header) = self.upcoming_headers.get(consecutive_header_count)
{ {
if next_header.range().contains(&y) { if next_header.range.contains(&y) {
self.place_new_headers(&mut consecutive_header_count, engine)?; self.place_new_headers(&mut consecutive_header_count, engine)?;
y = next_header.end; y = next_header.range.end;
// Skip header rows during normal layout. // Skip header rows during normal layout.
continue; continue;

View File

@ -39,7 +39,8 @@ impl<'a> GridLayouter<'a> {
if new_upcoming_headers.first().is_some_and(|next_header| { if new_upcoming_headers.first().is_some_and(|next_header| {
consecutive_headers.last().is_none_or(|latest_header| { consecutive_headers.last().is_none_or(|latest_header| {
!latest_header.short_lived && next_header.start == latest_header.end !latest_header.short_lived
&& next_header.range.start == latest_header.range.end
}) && !next_header.short_lived }) && !next_header.short_lived
}) { }) {
// More headers coming, so wait until we reach them. // More headers coming, so wait until we reach them.
@ -131,7 +132,7 @@ impl<'a> GridLayouter<'a> {
as_short_lived: bool, as_short_lived: bool,
) -> SourceResult<Abs> { ) -> SourceResult<Abs> {
let mut header_height = Abs::zero(); let mut header_height = Abs::zero();
for y in header.range() { for y in header.range.clone() {
header_height += self header_height += self
.layout_row_with_state( .layout_row_with_state(
y, y,
@ -261,7 +262,7 @@ impl<'a> GridLayouter<'a> {
self.unbreakable_rows_left += repeating_header_rows + pending_header_rows; self.unbreakable_rows_left += repeating_header_rows + pending_header_rows;
self.current.last_repeated_header_end = self.current.last_repeated_header_end =
self.repeating_headers.last().map(|h| h.end).unwrap_or_default(); self.repeating_headers.last().map(|h| h.range.end).unwrap_or_default();
// Reset the header height for this region. // Reset the header height for this region.
// It will be re-calculated when laying out each header row. // It will be re-calculated when laying out each header row.
@ -453,8 +454,8 @@ impl<'a> GridLayouter<'a> {
// assume that the amount of unbreakable rows following the first row // assume that the amount of unbreakable rows following the first row
// in the header will be precisely the rows in the header. // in the header will be precisely the rows in the header.
self.simulate_unbreakable_row_group( self.simulate_unbreakable_row_group(
header.start, header.range.start,
Some(header.end - header.start), Some(header.range.end - header.range.start),
regions, regions,
engine, engine,
disambiguator, disambiguator,
@ -569,5 +570,5 @@ impl<'a> GridLayouter<'a> {
pub fn total_header_row_count<'h>( pub fn total_header_row_count<'h>(
headers: impl IntoIterator<Item = &'h Header>, headers: impl IntoIterator<Item = &'h Header>,
) -> usize { ) -> usize {
headers.into_iter().map(|h| h.end - h.start).sum() headers.into_iter().map(|h| h.range.end - h.range.start).sum()
} }

View File

@ -428,10 +428,8 @@ pub struct Line {
/// A repeatable grid header. Starts at the first row. /// A repeatable grid header. Starts at the first row.
#[derive(Debug)] #[derive(Debug)]
pub struct Header { pub struct Header {
/// The first row included in this header. /// The range of rows included in this header.
pub start: usize, pub range: Range<usize>,
/// The index after the last row included in this header.
pub end: usize,
/// The header's level. /// The header's level.
/// ///
/// Higher level headers repeat together with lower level headers. If a /// Higher level headers repeat together with lower level headers. If a
@ -446,14 +444,6 @@ pub struct Header {
pub short_lived: bool, pub short_lived: bool,
} }
impl Header {
/// The header's range of included rows.
#[inline]
pub fn range(&self) -> Range<usize> {
self.start..self.end
}
}
/// A repeatable grid footer. Stops at the last row. /// A repeatable grid footer. Stops at the last row.
#[derive(Debug)] #[derive(Debug)]
pub struct Footer { pub struct Footer {
@ -1594,13 +1584,11 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
match row_group.kind { match row_group.kind {
RowGroupKind::Header => { RowGroupKind::Header => {
let data = Header { let data = Header {
start: group_range.start, // Later on, we have to correct this range in case there
// Later on, we have to correct this number in case there
// is gutter. But only once all cells have been analyzed // is gutter. But only once all cells have been analyzed
// and the header has fully expanded in the fixup loop // and the header has fully expanded in the fixup loop
// below. // below.
end: group_range.end, range: group_range,
level: row_group.repeatable_level.get(), level: row_group.repeatable_level.get(),
@ -1613,13 +1601,13 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
// lived if they would have a higher or equal level, as // lived if they would have a higher or equal level, as
// then they would immediately stop repeating during // then they would immediately stop repeating during
// layout. // layout.
let mut consecutive_header_start = data.start; let mut consecutive_header_start = data.range.start;
for conflicting_header in for conflicting_header in
headers.iter_mut().rev().take_while(move |h| { headers.iter_mut().rev().take_while(move |h| {
let conflicts = h.end == consecutive_header_start let conflicts = h.range.end == consecutive_header_start
&& h.level >= data.level; && h.level >= data.level;
consecutive_header_start = h.start; consecutive_header_start = h.range.start;
conflicts conflicts
}) })
{ {
@ -1841,9 +1829,9 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
let mut consecutive_header_start = let mut consecutive_header_start =
footer.as_ref().map(|(_, _, f)| f.start).unwrap_or(row_amount); footer.as_ref().map(|(_, _, f)| f.start).unwrap_or(row_amount);
for header_at_the_end in headers.iter_mut().rev().take_while(move |h| { for header_at_the_end in headers.iter_mut().rev().take_while(move |h| {
let at_the_end = h.end == consecutive_header_start; let at_the_end = h.range.end == consecutive_header_start;
consecutive_header_start = h.start; consecutive_header_start = h.range.start;
at_the_end at_the_end
}) { }) {
header_at_the_end.unwrap_mut().short_lived = true; header_at_the_end.unwrap_mut().short_lived = true;
@ -1858,7 +1846,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
// Index of first y is doubled, as each row before it // Index of first y is doubled, as each row before it
// receives a gutter row below. // receives a gutter row below.
header.start *= 2; header.range.start *= 2;
// - 'header.end' is always 'last y + 1'. The header stops // - 'header.end' is always 'last y + 1'. The header stops
// before that row. // before that row.
@ -1871,14 +1859,14 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
// to the index of the gutter row right below the header, // to the index of the gutter row right below the header,
// which is what we want (that gutter spacing should be // which is what we want (that gutter spacing should be
// repeated across pages to maintain uniformity). // repeated across pages to maintain uniformity).
header.end *= 2; header.range.end *= 2;
// If the header occupies the entire grid, ensure we don't // If the header occupies the entire grid, ensure we don't
// include an extra gutter row when it doesn't exist, since // include an extra gutter row when it doesn't exist, since
// the last row of the header is at the very bottom, // the last row of the header is at the very bottom,
// therefore '2 * last y + 1' is not a valid index. // therefore '2 * last y + 1' is not a valid index.
let row_amount = (2 * row_amount).saturating_sub(1); let row_amount = (2 * row_amount).saturating_sub(1);
header.end = header.end.min(row_amount); header.range.end = header.range.end.min(row_amount);
} }
} }
@ -1898,7 +1886,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
// only the gutter above the footer is kept, ensuring the same // only the gutter above the footer is kept, ensuring the same
// gutter row isn't laid out two times in a row. When laying // gutter row isn't laid out two times in a row. When laying
// out the footer for real, the mechanism can be disabled. // out the footer for real, the mechanism can be disabled.
let last_header_end = headers.last().map(|header| header.end); let last_header_end = headers.last().map(|header| header.range.end);
if has_gutter { if has_gutter {
// Convert the footer's start index to post-gutter coordinates. // Convert the footer's start index to post-gutter coordinates.
@ -2122,7 +2110,7 @@ fn check_for_conflicting_cell_row(
// conflict. // conflict.
if headers if headers
.iter() .iter()
.any(|header| cell_y < header.end && cell_y + rowspan > header.start) .any(|header| cell_y < header.range.end && cell_y + rowspan > header.range.start)
{ {
bail!( bail!(
"cell would conflict with header spanning the same position"; "cell would conflict with header spanning the same position";
@ -2341,14 +2329,14 @@ fn find_next_available_position(
} }
} else if let Some(header) = headers } else if let Some(header) = headers
.get(*next_header) .get(*next_header)
.filter(|header| resolved_index >= header.start * columns) .filter(|header| resolved_index >= header.range.start * columns)
{ {
// Skip header (can't place a cell inside it from outside it). // Skip header (can't place a cell inside it from outside it).
// No changes needed if we already passed this header (which // No changes needed if we already passed this header (which
// also triggers this branch) - in that case, we only update the // also triggers this branch) - in that case, we only update the
// counter. // counter.
if resolved_index < header.end * columns { if resolved_index < header.range.end * columns {
resolved_index = header.end * columns; resolved_index = header.range.end * columns;
if skip_rows { if skip_rows {
// Ensure the cell's chosen column is kept after the // Ensure the cell's chosen column is kept after the

View File

@ -306,8 +306,8 @@ fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
.headers .headers
.iter() .iter()
.take_while(|hd| { .take_while(|hd| {
let is_consecutive = hd.start == consecutive_header_end; let is_consecutive = hd.range.start == consecutive_header_end;
consecutive_header_end = hd.end; consecutive_header_end = hd.range.end;
is_consecutive is_consecutive
}) })
@ -315,7 +315,7 @@ fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
let (y_offset, header) = if first_mid_table_header > 0 { let (y_offset, header) = if first_mid_table_header > 0 {
let removed_header_rows = let removed_header_rows =
grid.headers.get(first_mid_table_header - 1).unwrap().end; grid.headers.get(first_mid_table_header - 1).unwrap().range.end;
let rows = rows.drain(..removed_header_rows); let rows = rows.drain(..removed_header_rows);
( (
@ -335,9 +335,9 @@ fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
Content::sequence(rows.into_iter().enumerate().map(|(relative_y, row)| { Content::sequence(rows.into_iter().enumerate().map(|(relative_y, row)| {
let y = relative_y + y_offset; let y = relative_y + y_offset;
if let Some(current_header) = if let Some(current_header) =
grid.headers.get(next_header).filter(|h| h.range().contains(&y)) grid.headers.get(next_header).filter(|h| h.range.contains(&y))
{ {
if y + 1 == current_header.end { if y + 1 == current_header.range.end {
next_header += 1; next_header += 1;
} }