Improve region progression handling

Fixes #5044
This commit is contained in:
Laurenz 2024-10-01 14:26:51 +02:00
parent d03af848eb
commit 9a71e7263d
9 changed files with 39 additions and 24 deletions

View File

@ -925,18 +925,22 @@ fn breakable_pod<'a>(
/// height and a new backlog.
fn distribute<'a>(
height: Abs,
regions: Regions,
mut regions: Regions,
buf: &'a mut SmallVec<[Abs; 2]>,
) -> (Abs, &'a mut [Abs]) {
// Build new region heights from old regions.
let mut remaining = height;
for region in regions.iter() {
let limited = region.y.min(remaining);
loop {
let limited = regions.size.y.clamp(Abs::zero(), remaining);
buf.push(limited);
remaining -= limited;
if remaining.approx_empty() {
if remaining.approx_empty()
|| !regions.may_break()
|| (!regions.may_progress() && limited.approx_empty())
{
break;
}
regions.next();
}
// If there is still something remaining, apply it to the

View File

@ -282,7 +282,7 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> {
let need = frame.height() + clearance;
// If the float doesn't fit, queue it for the next region.
if !remaining.fits(need) && !regions.in_last() {
if !remaining.fits(need) && regions.may_progress() {
self.work.floats.push(placed);
return Ok(());
}
@ -343,7 +343,7 @@ impl<'a, 'b> Composer<'a, 'b, '_, '_> {
let mut relayout = false;
let mut regions = *regions;
let mut migratable = !breakable && !regions.in_last();
let mut migratable = !breakable && regions.may_progress();
for (y, elem) in notes {
// The amount of space used by the in-flow content that contains the

View File

@ -194,9 +194,9 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
/// Processes a line of a paragraph.
fn line(&mut self, line: &'b LineChild) -> FlowResult<()> {
// If the line doesn't fit and we're allowed to break, finish the
// region.
if !self.regions.size.y.fits(line.frame.height()) && !self.regions.in_last() {
// If the line doesn't fit and a followup region may improve things,
// finish the region.
if !self.regions.size.y.fits(line.frame.height()) && self.regions.may_progress() {
return Err(Stop::Finish(false));
}
@ -228,9 +228,9 @@ impl<'a, 'b> Distributor<'a, 'b, '_, '_, '_> {
// Lay out the block.
let frame = single.layout(self.composer.engine, self.regions.base())?;
// If the block doesn't fit and we're allowed to break, finish the
// region.
if !self.regions.size.y.fits(frame.height()) && !self.regions.in_last() {
// If the block doesn't fit and a followup region may improve things,
// finish the region.
if !self.regions.size.y.fits(frame.height()) && self.regions.may_progress() {
return Err(Stop::Finish(false));
}

View File

@ -57,7 +57,7 @@ impl<'a> GridLayouter<'a> {
let mut skipped_region = false;
while self.unbreakable_rows_left == 0
&& !self.regions.size.y.fits(header_rows.height + self.footer_height)
&& !self.regions.in_last()
&& self.regions.may_progress()
{
// Advance regions without any output until we can place the
// header and the footer.
@ -124,7 +124,7 @@ impl<'a> GridLayouter<'a> {
let mut skipped_region = false;
while self.unbreakable_rows_left == 0
&& !self.regions.size.y.fits(footer_height)
&& !self.regions.in_last()
&& self.regions.may_progress()
{
// Advance regions without any output until we can place the
// footer.

View File

@ -1147,7 +1147,7 @@ impl<'a> RowspanSimulator<'a> {
// Skip until we reach a fitting region for both header and footer.
while !self.regions.size.y.fits(header_height + footer_height)
&& !self.regions.in_last()
&& self.regions.may_progress()
{
self.regions.next();
self.finished += 1;

View File

@ -96,14 +96,18 @@ impl Regions<'_> {
/// Whether the first region is full and a region break is called for.
pub fn is_full(&self) -> bool {
Abs::zero().fits(self.size.y) && !self.in_last()
Abs::zero().fits(self.size.y) && self.may_progress()
}
/// Whether the first region is the last usable region.
///
/// If this is true, calling `next()` will have no effect.
pub fn in_last(&self) -> bool {
self.backlog.is_empty() && self.last.map_or(true, |height| self.size.y == height)
/// Whether a region break is permitted.
pub fn may_break(&self) -> bool {
!self.backlog.is_empty() || self.last.is_some()
}
/// Whether calling `next()` may improve a situation where there is a lack
/// of space.
pub fn may_progress(&self) -> bool {
!self.backlog.is_empty() || self.last.is_some_and(|height| self.size.y != height)
}
/// Advance to the next region if there is any.

View File

@ -327,8 +327,9 @@ fn layout_equation_block(
let mut rows = full_equation_builder.frames.into_iter().peekable();
let mut equation_builders = vec![];
let mut last_first_pos = Point::zero();
let mut regions = regions;
for region in regions.iter() {
loop {
// Keep track of the position of the first row in this region,
// so that the offset can be reverted later.
let Some(&(_, first_pos)) = rows.peek() else { break };
@ -344,8 +345,9 @@ fn layout_equation_block(
// we placed at least one line _or_ we still have non-last
// regions. Crucially, we don't want to infinitely create
// new regions which are too small.
if !region.y.fits(sub.height() + pos.y)
&& (!frames.is_empty() || !regions.in_last())
if !regions.size.y.fits(sub.height() + pos.y)
&& (regions.may_progress()
|| (regions.may_break() && !frames.is_empty()))
{
break;
}
@ -357,6 +359,7 @@ fn layout_equation_block(
equation_builders
.push(MathRunFrameBuilder { frames, size: Size::new(width, height) });
regions.next();
}
// Append remaining rows to the equation builder of the last region.

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View File

@ -28,3 +28,7 @@ Hi #box(pad(left: 10pt)[A]) there
--- pad-adding-to-100-percent ---
// Test that padding adding up to 100% does not panic.
#pad(50%)[]
--- issue-5044-pad-100-percent ---
#set page(width: 30pt, height: 30pt)
#pad(100%, block(width: 1cm, height: 1cm, fill: red))