mirror of
https://github.com/typst/typst
synced 2025-07-16 00:52:54 +08:00
resolve multiple footers
This commit is contained in:
parent
f9b1bfd1b0
commit
0951fe13fd
@ -641,7 +641,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
vec![],
|
vec![],
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
vec![],
|
||||||
entries,
|
entries,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1179,7 +1179,7 @@ mod test {
|
|||||||
vec![],
|
vec![],
|
||||||
vec![],
|
vec![],
|
||||||
vec![],
|
vec![],
|
||||||
None,
|
vec![],
|
||||||
entries,
|
entries,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -697,7 +697,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
cells: impl IntoIterator<Item = Cell<'a>>,
|
cells: impl IntoIterator<Item = Cell<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let entries = cells.into_iter().map(Entry::Cell).collect();
|
let entries = cells.into_iter().map(Entry::Cell).collect();
|
||||||
Self::new_internal(tracks, gutter, vec![], vec![], vec![], None, entries)
|
Self::new_internal(tracks, gutter, vec![], vec![], vec![], vec![], entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the cell grid, given the tracks and resolved entries.
|
/// Generates the cell grid, given the tracks and resolved entries.
|
||||||
@ -707,7 +707,7 @@ impl<'a> CellGrid<'a> {
|
|||||||
vlines: Vec<Vec<Line>>,
|
vlines: Vec<Vec<Line>>,
|
||||||
hlines: Vec<Vec<Line>>,
|
hlines: Vec<Vec<Line>>,
|
||||||
headers: Vec<Repeatable<Header>>,
|
headers: Vec<Repeatable<Header>>,
|
||||||
footer: Option<Repeatable<Footer>>,
|
footers: Vec<Repeatable<Footer>>,
|
||||||
entries: Vec<Entry<'a>>,
|
entries: Vec<Entry<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
@ -754,7 +754,6 @@ impl<'a> CellGrid<'a> {
|
|||||||
rows.pop();
|
rows.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let footers: Vec<Repeatable<Footer>> = footer.into_iter().collect();
|
|
||||||
let sorted_footers = simulate_footer_repetition(&footers);
|
let sorted_footers = simulate_footer_repetition(&footers);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -991,6 +990,7 @@ struct RowGroupData {
|
|||||||
///
|
///
|
||||||
/// This stays as `None` for fully empty headers and footers.
|
/// This stays as `None` for fully empty headers and footers.
|
||||||
range: Option<Range<usize>>,
|
range: Option<Range<usize>>,
|
||||||
|
#[allow(dead_code)] // TODO: should we remove this?
|
||||||
span: Span,
|
span: Span,
|
||||||
kind: RowGroupKind,
|
kind: RowGroupKind,
|
||||||
|
|
||||||
@ -1048,11 +1048,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
let has_gutter = self.gutter.any(|tracks| !tracks.is_empty());
|
let has_gutter = self.gutter.any(|tracks| !tracks.is_empty());
|
||||||
|
|
||||||
let mut headers: Vec<Repeatable<Header>> = vec![];
|
let mut headers: Vec<Repeatable<Header>> = vec![];
|
||||||
|
let mut footers: Vec<Repeatable<Footer>> = vec![];
|
||||||
// Stores where the footer is supposed to end, its span, and the
|
|
||||||
// actual footer structure.
|
|
||||||
let mut footer: Option<(usize, Span, Footer)> = None;
|
|
||||||
let mut repeat_footer = false;
|
|
||||||
|
|
||||||
// If true, there has been at least one cell besides headers and
|
// If true, there has been at least one cell besides headers and
|
||||||
// footers. When false, footers at the end are forced to not repeat.
|
// footers. When false, footers at the end are forced to not repeat.
|
||||||
@ -1074,10 +1070,11 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
// automatically-positioned cell.
|
// automatically-positioned cell.
|
||||||
let mut auto_index: usize = 0;
|
let mut auto_index: usize = 0;
|
||||||
|
|
||||||
// The next header after the latest auto-positioned cell. This is used
|
// The next header and footer after the latest auto-positioned cell.
|
||||||
// to avoid checking for collision with headers that were already
|
// These are used to avoid checking for collision with headers that
|
||||||
// skipped.
|
// were already skipped.
|
||||||
let mut next_header = 0;
|
let mut next_header = 0;
|
||||||
|
let mut next_footer = 0;
|
||||||
|
|
||||||
// We have to rebuild the grid to account for fixed cell positions.
|
// We have to rebuild the grid to account for fixed cell positions.
|
||||||
//
|
//
|
||||||
@ -1100,10 +1097,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
&mut pending_hlines,
|
&mut pending_hlines,
|
||||||
&mut pending_vlines,
|
&mut pending_vlines,
|
||||||
&mut headers,
|
&mut headers,
|
||||||
&mut footer,
|
&mut footers,
|
||||||
&mut repeat_footer,
|
|
||||||
&mut auto_index,
|
&mut auto_index,
|
||||||
&mut next_header,
|
&mut next_header,
|
||||||
|
&mut next_footer,
|
||||||
&mut resolved_cells,
|
&mut resolved_cells,
|
||||||
&mut at_least_one_cell,
|
&mut at_least_one_cell,
|
||||||
child,
|
child,
|
||||||
@ -1121,11 +1118,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
row_amount,
|
row_amount,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let footer = self.finalize_headers_and_footers(
|
self.finalize_headers_and_footers(
|
||||||
has_gutter,
|
has_gutter,
|
||||||
&mut headers,
|
&mut headers,
|
||||||
footer,
|
&mut footers,
|
||||||
repeat_footer,
|
|
||||||
row_amount,
|
row_amount,
|
||||||
at_least_one_cell,
|
at_least_one_cell,
|
||||||
)?;
|
)?;
|
||||||
@ -1136,7 +1132,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
vlines,
|
vlines,
|
||||||
hlines,
|
hlines,
|
||||||
headers,
|
headers,
|
||||||
footer,
|
footers,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1156,10 +1152,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
pending_hlines: &mut Vec<(Span, Line, bool)>,
|
pending_hlines: &mut Vec<(Span, Line, bool)>,
|
||||||
pending_vlines: &mut Vec<(Span, Line)>,
|
pending_vlines: &mut Vec<(Span, Line)>,
|
||||||
headers: &mut Vec<Repeatable<Header>>,
|
headers: &mut Vec<Repeatable<Header>>,
|
||||||
footer: &mut Option<(usize, Span, Footer)>,
|
footers: &mut Vec<Repeatable<Footer>>,
|
||||||
repeat_footer: &mut bool,
|
|
||||||
auto_index: &mut usize,
|
auto_index: &mut usize,
|
||||||
next_header: &mut usize,
|
next_header: &mut usize,
|
||||||
|
next_footer: &mut usize,
|
||||||
resolved_cells: &mut Vec<Option<Entry<'x>>>,
|
resolved_cells: &mut Vec<Option<Entry<'x>>>,
|
||||||
at_least_one_cell: &mut bool,
|
at_least_one_cell: &mut bool,
|
||||||
child: ResolvableGridChild<T, I>,
|
child: ResolvableGridChild<T, I>,
|
||||||
@ -1212,6 +1208,12 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
&mut (*next_header).clone()
|
&mut (*next_header).clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let local_next_footer = if matches!(child, ResolvableGridChild::Item(_)) {
|
||||||
|
next_footer
|
||||||
|
} else {
|
||||||
|
&mut (*next_footer).clone()
|
||||||
|
};
|
||||||
|
|
||||||
// The first row in which this table group can fit.
|
// The first row in which this table group can fit.
|
||||||
//
|
//
|
||||||
// Within headers and footers, this will correspond to the first
|
// Within headers and footers, this will correspond to the first
|
||||||
@ -1249,10 +1251,6 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
(Some(items), None)
|
(Some(items), None)
|
||||||
}
|
}
|
||||||
ResolvableGridChild::Footer { repeat, span, items, .. } => {
|
ResolvableGridChild::Footer { repeat, span, items, .. } => {
|
||||||
if footer.is_some() {
|
|
||||||
bail!(span, "cannot have more than one footer");
|
|
||||||
}
|
|
||||||
|
|
||||||
row_group_data = Some(RowGroupData {
|
row_group_data = Some(RowGroupData {
|
||||||
range: None,
|
range: None,
|
||||||
span,
|
span,
|
||||||
@ -1396,10 +1394,11 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
colspan,
|
colspan,
|
||||||
rowspan,
|
rowspan,
|
||||||
headers,
|
headers,
|
||||||
footer.as_ref(),
|
footers,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
local_auto_index,
|
local_auto_index,
|
||||||
local_next_header,
|
local_next_header,
|
||||||
|
local_next_footer,
|
||||||
first_available_row,
|
first_available_row,
|
||||||
columns,
|
columns,
|
||||||
row_group_data.is_some(),
|
row_group_data.is_some(),
|
||||||
@ -1621,23 +1620,23 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
RowGroupKind::Footer => {
|
RowGroupKind::Footer => {
|
||||||
// Only check if the footer is at the end later, once we know
|
// Only check if the footer is at the end later, once we know
|
||||||
// the final amount of rows.
|
// the final amount of rows.
|
||||||
*footer = Some((
|
let data = Footer {
|
||||||
group_range.end,
|
// Later on, we have to correct this number in case there
|
||||||
row_group.span,
|
// is gutter, but only once all cells have been analyzed
|
||||||
Footer {
|
// and the header's and footer's exact boundaries are
|
||||||
// Later on, we have to correct this number in case there
|
// known. That is because the gutter row immediately
|
||||||
// is gutter, but only once all cells have been analyzed
|
// before the footer might not be included as part of
|
||||||
// and the header's and footer's exact boundaries are
|
// the footer if it is contained within the header.
|
||||||
// known. That is because the gutter row immediately
|
start: group_range.start,
|
||||||
// before the footer might not be included as part of
|
end: group_range.end,
|
||||||
// the footer if it is contained within the header.
|
level: row_group.repeatable_level.get(),
|
||||||
start: group_range.start,
|
};
|
||||||
end: group_range.end,
|
|
||||||
level: 1,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
*repeat_footer = row_group.repeat;
|
footers.push(if row_group.repeat {
|
||||||
|
Repeatable::Repeated(data)
|
||||||
|
} else {
|
||||||
|
Repeatable::NotRepeated(data)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1807,24 +1806,30 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
&self,
|
&self,
|
||||||
has_gutter: bool,
|
has_gutter: bool,
|
||||||
headers: &mut [Repeatable<Header>],
|
headers: &mut [Repeatable<Header>],
|
||||||
footer: Option<(usize, Span, Footer)>,
|
footers: &mut [Repeatable<Footer>],
|
||||||
repeat_footer: bool,
|
|
||||||
row_amount: usize,
|
row_amount: usize,
|
||||||
at_least_one_cell: bool,
|
at_least_one_cell: bool,
|
||||||
) -> SourceResult<Option<Repeatable<Footer>>> {
|
) -> SourceResult<()> {
|
||||||
// Mark consecutive headers right before the end of the table, or the
|
// Mark consecutive headers right before the end of the table, or the
|
||||||
// final footer, as short lived, given that there are no normal rows
|
// final footer, as short lived, given that there are no normal rows
|
||||||
// after them, so repeating them is pointless.
|
// after them, so repeating them is pointless.
|
||||||
//
|
//
|
||||||
// It is important to do this BEFORE we update header and footer ranges
|
|
||||||
// due to gutter below as 'row_amount' doesn't consider gutter.
|
|
||||||
//
|
|
||||||
// TODO(subfooters): take the last footer if it is at the end and
|
// TODO(subfooters): take the last footer if it is at the end and
|
||||||
// backtrack through consecutive footers until the first one in the
|
// backtrack through consecutive footers until the first one in the
|
||||||
// sequence is found. If there is no footer at the end, there are no
|
// sequence is found. If there is no footer at the end, there are no
|
||||||
// haeders to turn short-lived.
|
// haeders to turn short-lived.
|
||||||
let mut consecutive_header_start =
|
//
|
||||||
footer.as_ref().map(|(_, _, f)| f.start).unwrap_or(row_amount);
|
// TODO: interleaved headers and footers?
|
||||||
|
let mut first_end_footer = row_amount;
|
||||||
|
for end_footer in footers.iter().rev() {
|
||||||
|
if end_footer.end != first_end_footer {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
first_end_footer = end_footer.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut consecutive_header_start = first_end_footer;
|
||||||
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.range.end == consecutive_header_start;
|
let at_the_end = h.range.end == consecutive_header_start;
|
||||||
|
|
||||||
@ -1863,13 +1868,9 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
let row_amount = (2 * row_amount).saturating_sub(1);
|
let row_amount = (2 * row_amount).saturating_sub(1);
|
||||||
header.range.end = header.range.end.min(row_amount);
|
header.range.end = header.range.end.min(row_amount);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let footer = footer
|
for repeatable_footer in &mut *footers {
|
||||||
.map(|(footer_end, footer_span, mut footer)| {
|
let footer = repeatable_footer.unwrap_mut();
|
||||||
if footer_end != row_amount {
|
|
||||||
bail!(footer_span, "footer must end at the last row");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(subfooters): will need a global slice of headers and
|
// TODO(subfooters): will need a global slice of headers and
|
||||||
// footers for when we have multiple footers
|
// footers for when we have multiple footers
|
||||||
@ -1883,45 +1884,37 @@ impl<'x> CellGridResolver<'_, '_, 'x> {
|
|||||||
// 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.range.end);
|
let last_header_end = headers.last().map(|header| header.range.end);
|
||||||
|
|
||||||
if has_gutter {
|
// Convert the footer's start index to post-gutter coordinates.
|
||||||
// Convert the footer's start index to post-gutter coordinates.
|
footer.start *= 2;
|
||||||
footer.start *= 2;
|
|
||||||
|
|
||||||
// Include the gutter right before the footer, unless there is
|
// TODO: this probably has to change
|
||||||
// none, or the gutter is already included in the header (no
|
// Include the gutter right before the footer, unless there is
|
||||||
// rows between the header and the footer).
|
// none, or the gutter is already included in the header (no
|
||||||
if last_header_end != Some(footer.start) {
|
// rows between the header and the footer).
|
||||||
footer.start = footer.start.saturating_sub(1);
|
if last_header_end != Some(footer.start) {
|
||||||
}
|
footer.start = footer.start.saturating_sub(1);
|
||||||
|
|
||||||
// Adapt footer end but DO NOT include the gutter below it,
|
|
||||||
// if it exists. Calculation:
|
|
||||||
// - Starts as 'last y + 1'.
|
|
||||||
// - The result will be
|
|
||||||
// 2 * (last_y + 1) - 1 = 2 * last_y + 1,
|
|
||||||
// which is the new index of the last footer row plus one,
|
|
||||||
// meaning we do exclude any gutter below this way.
|
|
||||||
//
|
|
||||||
// It also keeps us within the total amount of rows, so we
|
|
||||||
// don't need to '.min()' later.
|
|
||||||
footer.end = (2 * footer.end).saturating_sub(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(footer)
|
// Adapt footer end but DO NOT include the gutter below it,
|
||||||
})
|
// if it exists. Calculation:
|
||||||
.transpose()?
|
// - Starts as 'last y + 1'.
|
||||||
.map(|footer| {
|
// - The result will be
|
||||||
// Don't repeat footers when the table only has headers and
|
// 2 * (last_y + 1) - 1 = 2 * last_y + 1,
|
||||||
// footers.
|
// which is the new index of the last footer row plus one,
|
||||||
// TODO(subfooters): Switch this to marking the last N
|
// meaning we do exclude any gutter below this way.
|
||||||
// consecutive footers as short lived.
|
//
|
||||||
Repeatable {
|
// It also keeps us within the total amount of rows, so we
|
||||||
inner: footer,
|
// don't need to '.min()' later.
|
||||||
repeated: repeat_footer && at_least_one_cell,
|
footer.end = (2 * footer.end).saturating_sub(1);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(footer)
|
if !at_least_one_cell {
|
||||||
|
// TODO: short-lived (and remove this?)
|
||||||
|
repeatable_footer.repeated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the cell's fields based on grid-wide properties.
|
/// Resolves the cell's fields based on grid-wide properties.
|
||||||
@ -2093,7 +2086,7 @@ fn expand_row_group(
|
|||||||
/// Check if a cell's fixed row would conflict with a header or footer.
|
/// Check if a cell's fixed row would conflict with a header or footer.
|
||||||
fn check_for_conflicting_cell_row(
|
fn check_for_conflicting_cell_row(
|
||||||
headers: &[Repeatable<Header>],
|
headers: &[Repeatable<Header>],
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footers: &[Repeatable<Footer>],
|
||||||
cell_y: usize,
|
cell_y: usize,
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
) -> HintedStrResult<()> {
|
) -> HintedStrResult<()> {
|
||||||
@ -2112,13 +2105,14 @@ fn check_for_conflicting_cell_row(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_, _, footer)) = footer {
|
if footers
|
||||||
if cell_y < footer.end && cell_y + rowspan > footer.start {
|
.iter()
|
||||||
bail!(
|
.any(|footer| cell_y < footer.end && cell_y + rowspan > footer.start)
|
||||||
"cell would conflict with footer spanning the same position";
|
{
|
||||||
hint: "try reducing the cell's rowspan or moving the footer"
|
bail!(
|
||||||
);
|
"cell would conflict with footer spanning the same position";
|
||||||
}
|
hint: "try reducing the cell's rowspan or moving the footer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -2140,10 +2134,11 @@ fn resolve_cell_position(
|
|||||||
colspan: usize,
|
colspan: usize,
|
||||||
rowspan: usize,
|
rowspan: usize,
|
||||||
headers: &[Repeatable<Header>],
|
headers: &[Repeatable<Header>],
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footers: &[Repeatable<Footer>],
|
||||||
resolved_cells: &[Option<Entry>],
|
resolved_cells: &[Option<Entry>],
|
||||||
auto_index: &mut usize,
|
auto_index: &mut usize,
|
||||||
next_header: &mut usize,
|
next_header: &mut usize,
|
||||||
|
next_footer: &mut usize,
|
||||||
first_available_row: usize,
|
first_available_row: usize,
|
||||||
columns: usize,
|
columns: usize,
|
||||||
in_row_group: bool,
|
in_row_group: bool,
|
||||||
@ -2166,11 +2161,12 @@ fn resolve_cell_position(
|
|||||||
// simply skipping existing cells, headers and footers.
|
// simply skipping existing cells, headers and footers.
|
||||||
let resolved_index = find_next_available_position(
|
let resolved_index = find_next_available_position(
|
||||||
headers,
|
headers,
|
||||||
footer,
|
footers,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
columns,
|
columns,
|
||||||
*auto_index,
|
*auto_index,
|
||||||
next_header,
|
next_header,
|
||||||
|
next_footer,
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -2207,7 +2203,7 @@ fn resolve_cell_position(
|
|||||||
// footer (but only if it isn't already in one, otherwise there
|
// footer (but only if it isn't already in one, otherwise there
|
||||||
// will already be a separate check).
|
// will already be a separate check).
|
||||||
if !in_row_group {
|
if !in_row_group {
|
||||||
check_for_conflicting_cell_row(headers, footer, cell_y, rowspan)?;
|
check_for_conflicting_cell_row(headers, footers, cell_y, rowspan)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
cell_index(cell_x, cell_y)
|
cell_index(cell_x, cell_y)
|
||||||
@ -2226,25 +2222,26 @@ fn resolve_cell_position(
|
|||||||
// cell in.
|
// cell in.
|
||||||
find_next_available_position(
|
find_next_available_position(
|
||||||
headers,
|
headers,
|
||||||
footer,
|
footers,
|
||||||
resolved_cells,
|
resolved_cells,
|
||||||
columns,
|
columns,
|
||||||
initial_index,
|
initial_index,
|
||||||
// Make our own copy of the 'next_header' counter, since it
|
// Make new copies of the 'next_header/footer' counters,
|
||||||
// should only be updated by auto cells. However, we cannot
|
// since they should only be updated by auto cells.
|
||||||
// start with the same value as we are searching from the
|
// However, we cannot start with the same values as we are
|
||||||
// start, and not from 'auto_index', so auto cells might
|
// searching from the start, and not from 'auto_index', so
|
||||||
// have skipped some headers already which this cell will
|
// auto cells might have skipped some headers and footers
|
||||||
// also need to skip.
|
// already which this cell will also need to skip.
|
||||||
//
|
//
|
||||||
// We could, in theory, keep a separate 'next_header'
|
// We could, in theory, keep separate 'next_header/footer'
|
||||||
// counter for cells with fixed columns. But then we would
|
// counters for cells with fixed columns. But then we would
|
||||||
// need one for every column, and much like how there isn't
|
// need one for every column, and much like how there isn't
|
||||||
// an index counter for each column either, the potential
|
// an index counter for each column either, the potential
|
||||||
// speed gain seems less relevant for a less used feature.
|
// speed gain seems less relevant for a less used feature.
|
||||||
// Still, it is something to consider for the future if
|
// Still, it is something to consider for the future if
|
||||||
// this turns out to be a bottleneck in important cases.
|
// this turns out to be a bottleneck in important cases.
|
||||||
&mut 0,
|
&mut 0,
|
||||||
|
&mut 0,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2255,7 +2252,7 @@ fn resolve_cell_position(
|
|||||||
// footer (but only if it isn't already in one, otherwise there
|
// footer (but only if it isn't already in one, otherwise there
|
||||||
// will already be a separate check).
|
// will already be a separate check).
|
||||||
if !in_row_group {
|
if !in_row_group {
|
||||||
check_for_conflicting_cell_row(headers, footer, cell_y, rowspan)?;
|
check_for_conflicting_cell_row(headers, footers, cell_y, rowspan)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's find the first column which has that row available.
|
// Let's find the first column which has that row available.
|
||||||
@ -2293,11 +2290,12 @@ fn resolve_cell_position(
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn find_next_available_position(
|
fn find_next_available_position(
|
||||||
headers: &[Repeatable<Header>],
|
headers: &[Repeatable<Header>],
|
||||||
footer: Option<&(usize, Span, Footer)>,
|
footers: &[Repeatable<Footer>],
|
||||||
resolved_cells: &[Option<Entry<'_>>],
|
resolved_cells: &[Option<Entry<'_>>],
|
||||||
columns: usize,
|
columns: usize,
|
||||||
initial_index: usize,
|
initial_index: usize,
|
||||||
next_header: &mut usize,
|
next_header: &mut usize,
|
||||||
|
next_footer: &mut usize,
|
||||||
skip_rows: bool,
|
skip_rows: bool,
|
||||||
) -> HintedStrResult<usize> {
|
) -> HintedStrResult<usize> {
|
||||||
let mut resolved_index = initial_index;
|
let mut resolved_index = initial_index;
|
||||||
@ -2341,15 +2339,20 @@ fn find_next_available_position(
|
|||||||
|
|
||||||
// From now on, only check the headers afterwards.
|
// From now on, only check the headers afterwards.
|
||||||
*next_header += 1;
|
*next_header += 1;
|
||||||
} else if let Some((footer_end, _, _)) = footer.filter(|(end, _, footer)| {
|
} else if let Some(footer) = footers
|
||||||
resolved_index >= footer.start * columns && resolved_index < *end * columns
|
.get(*next_footer)
|
||||||
}) {
|
.filter(|footer| resolved_index >= footer.start * columns)
|
||||||
|
{
|
||||||
// Skip footer, for the same reason.
|
// Skip footer, for the same reason.
|
||||||
resolved_index = *footer_end * columns;
|
if resolved_index < footer.end * columns {
|
||||||
|
resolved_index = footer.end * columns;
|
||||||
|
|
||||||
if skip_rows {
|
if skip_rows {
|
||||||
resolved_index += initial_index % columns;
|
resolved_index += initial_index % columns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*next_footer += 1;
|
||||||
} else {
|
} else {
|
||||||
return Ok(resolved_index);
|
return Ok(resolved_index);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,6 @@
|
|||||||
stroke: black,
|
stroke: black,
|
||||||
inset: 5pt,
|
inset: 5pt,
|
||||||
grid.cell(x: 1)[a],
|
grid.cell(x: 1)[a],
|
||||||
// Error: 3-56 footer must end at the last row
|
|
||||||
grid.footer(grid.cell(x: 0)[b1], grid.cell(x: 0)[b2]),
|
grid.footer(grid.cell(x: 0)[b1], grid.cell(x: 0)[b2]),
|
||||||
// This should skip the footer
|
// This should skip the footer
|
||||||
grid.cell(x: 1)[c]
|
grid.cell(x: 1)[c]
|
||||||
@ -141,14 +140,12 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
--- grid-footer-not-at-last-row ---
|
--- grid-footer-not-at-last-row ---
|
||||||
// Error: 2:3-2:19 footer must end at the last row
|
|
||||||
#grid(
|
#grid(
|
||||||
grid.footer([a]),
|
grid.footer([a]),
|
||||||
[b],
|
[b],
|
||||||
)
|
)
|
||||||
|
|
||||||
--- grid-footer-not-at-last-row-two-columns ---
|
--- grid-footer-not-at-last-row-two-columns ---
|
||||||
// Error: 3:3-3:19 footer must end at the last row
|
|
||||||
#grid(
|
#grid(
|
||||||
columns: 2,
|
columns: 2,
|
||||||
grid.footer([a]),
|
grid.footer([a]),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user