mirror of
https://github.com/typst/typst
synced 2025-05-17 02:25:27 +08:00
finish region adjustments
- flush pending headers - properly layout headers at region start
This commit is contained in:
parent
63055c8c83
commit
e586cffa6c
@ -15,6 +15,7 @@ use typst_library::visualize::Geometry;
|
||||
use typst_syntax::Span;
|
||||
use typst_utils::{MaybeReverseIter, Numeric};
|
||||
|
||||
use super::repeated::HeadersToLayout;
|
||||
use super::{
|
||||
generate_line_segments, hline_stroke_at_column, layout_cell, vline_stroke_at_row,
|
||||
LineSegment, Rowspan, UnbreakableRowGroup,
|
||||
@ -1388,30 +1389,19 @@ impl<'a> GridLayouter<'a> {
|
||||
self.lrows.pop().unwrap();
|
||||
}
|
||||
|
||||
// If no rows other than the footer have been laid out so far, and
|
||||
// there are rows beside the footer, then don't lay it out at all.
|
||||
// This check doesn't apply, and is thus overridden, when there is a
|
||||
// header.
|
||||
let mut footer_would_be_orphan = self.lrows.is_empty()
|
||||
&& !in_last_with_offset(
|
||||
self.regions,
|
||||
self.header_height + self.footer_height,
|
||||
)
|
||||
&& self
|
||||
.grid
|
||||
.footer
|
||||
.as_ref()
|
||||
.and_then(Repeatable::as_repeated)
|
||||
.is_some_and(|footer| footer.start != 0);
|
||||
|
||||
if let Some(last_header) = self.repeating_headers.last() {
|
||||
let footer_would_be_widow = if let Some(last_header) = self
|
||||
.pending_headers
|
||||
.last()
|
||||
.map(Repeatable::unwrap)
|
||||
.or_else(|| self.repeating_headers.last().map(|h| *h))
|
||||
{
|
||||
if self.grid.rows.len() > last_header.end
|
||||
&& self
|
||||
.grid
|
||||
.footer
|
||||
.as_ref()
|
||||
.and_then(Repeatable::as_repeated)
|
||||
.is_none_or(|footer| footer.start != header.end)
|
||||
.is_none_or(|footer| footer.start != last_header.end)
|
||||
&& self.lrows.last().is_some_and(|row| row.index() < last_header.end)
|
||||
&& !in_last_with_offset(
|
||||
self.regions,
|
||||
@ -1421,19 +1411,41 @@ impl<'a> GridLayouter<'a> {
|
||||
// Header and footer would be alone in this region, but there are more
|
||||
// rows beyond the header and the footer. Push an empty region.
|
||||
self.lrows.clear();
|
||||
footer_would_be_orphan = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
} else if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
||||
// If no rows other than the footer have been laid out so far, and
|
||||
// there are rows beside the footer, then don't lay it out at all.
|
||||
// (Similar check from above, but for the case without headers.)
|
||||
// TODO: widow prevention for non-repeated footers with a similar
|
||||
// mechanism / when implementing multiple footers.
|
||||
self.lrows.is_empty()
|
||||
&& !in_last_with_offset(
|
||||
self.regions,
|
||||
self.header_height + self.footer_height,
|
||||
)
|
||||
&& footer.start != 0
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut laid_out_footer_start = None;
|
||||
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
||||
// Don't layout the footer if it would be alone with the header in
|
||||
// the page, and don't layout it twice.
|
||||
if !footer_would_be_orphan
|
||||
&& self.lrows.iter().all(|row| row.index() < footer.start)
|
||||
{
|
||||
laid_out_footer_start = Some(footer.start);
|
||||
self.layout_footer(footer, engine, self.finished.len())?;
|
||||
if !footer_would_be_widow {
|
||||
// Did not trigger automatic header orphan / footer widow check.
|
||||
// This means pending headers have successfully been placed once
|
||||
// without hitting orphan prevention, so they may now be moved into
|
||||
// repeating headers.
|
||||
self.flush_pending_headers();
|
||||
|
||||
if let Some(Repeatable::Repeated(footer)) = &self.grid.footer {
|
||||
// Don't layout the footer if it would be alone with the header in
|
||||
// the page (hence the widow check), and don't layout it twice.
|
||||
if self.lrows.iter().all(|row| row.index() < footer.start) {
|
||||
laid_out_footer_start = Some(footer.start);
|
||||
self.layout_footer(footer, engine, self.finished.len())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1569,13 +1581,16 @@ impl<'a> GridLayouter<'a> {
|
||||
self.prepare_footer(footer, engine, disambiguator)?;
|
||||
}
|
||||
|
||||
if !self.repeating_headers.is_empty() {
|
||||
// Add a header to the new region.
|
||||
self.layout_headers(engine, disambiguator)?;
|
||||
}
|
||||
|
||||
// Ensure rows don't try to overrun the footer.
|
||||
// Note that header layout will only subtract this again if it has
|
||||
// to skip regions to fit headers, so there is no risk of
|
||||
// subtracting this twice.
|
||||
self.regions.size.y -= self.footer_height;
|
||||
|
||||
if !self.repeating_headers.is_empty() || !self.pending_headers.is_empty() {
|
||||
// Add headers to the new region.
|
||||
self.layout_headers(HeadersToLayout::RepeatingAndPending, engine)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -96,23 +96,16 @@ impl<'a> GridLayouter<'a> {
|
||||
}
|
||||
|
||||
pub fn flush_pending_headers(&mut self) {
|
||||
debug_assert!(!self.upcoming_headers.is_empty());
|
||||
debug_assert!(self.pending_header_end > 0);
|
||||
let headers = self.pending_headers();
|
||||
|
||||
let [first_header, ..] = headers else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.repeating_headers.truncate(
|
||||
self.repeating_headers
|
||||
.partition_point(|h| h.level < first_header.unwrap().level),
|
||||
);
|
||||
|
||||
for header in self.pending_headers() {
|
||||
for header in self.pending_headers {
|
||||
if let Repeatable::Repeated(header) = header {
|
||||
// Vector remains sorted by increasing levels:
|
||||
// - It was sorted before, so the truncation above only keeps
|
||||
// - 'pending_headers' themselves are sorted, since we only
|
||||
// push non-mutually-conflicting headers at a time.
|
||||
// - Before pushing new pending headers in
|
||||
// 'layout_new_pending_headers', we truncate repeating headers
|
||||
// to remove anything with the same or higher levels as the
|
||||
// first pending header.
|
||||
// - Assuming it was sorted before, that truncation only keeps
|
||||
// elements with a lower level.
|
||||
// - Therefore, by pushing this header to the end, it will have
|
||||
// a level larger than all the previous headers, and is thus
|
||||
@ -121,12 +114,7 @@ impl<'a> GridLayouter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
self.upcoming_headers = self
|
||||
.upcoming_headers
|
||||
.get(self.pending_header_end..)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.pending_header_end = 0;
|
||||
self.pending_headers = Default::default();
|
||||
}
|
||||
|
||||
pub fn bump_repeating_headers(&mut self) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user