Do not create a block frame for out-of-flow items only (#2517)

This commit is contained in:
Malo 2023-12-18 11:37:30 +01:00 committed by GitHub
parent 41c0dae209
commit 356bdeba18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 107 additions and 12 deletions

View File

@ -82,7 +82,7 @@ impl Layout for FlowElem {
} else if child.is::<ColbreakElem>() { } else if child.is::<ColbreakElem>() {
if !layouter.regions.backlog.is_empty() || layouter.regions.last.is_some() if !layouter.regions.backlog.is_empty() || layouter.regions.last.is_some()
{ {
layouter.finish_region(engine)?; layouter.finish_region(engine, true)?;
} }
} else { } else {
bail!(child.span(), "unexpected flow child"); bail!(child.span(), "unexpected flow child");
@ -160,6 +160,19 @@ impl FlowItem {
Self::Frame { frame, .. } | Self::Footnote(frame) => frame.height(), Self::Frame { frame, .. } | Self::Footnote(frame) => frame.height(),
} }
} }
/// Whether this item is out-of-flow.
///
/// Out-of-flow items are guaranteed to have a [`Size::zero()`].
fn is_out_of_flow(&self) -> bool {
match self {
Self::Placed { float: false, .. } => true,
Self::Frame { frame, .. } => {
frame.items().all(|(_, item)| matches!(item, FrameItem::Meta(..)))
}
_ => false,
}
}
} }
impl<'a> FlowLayouter<'a> { impl<'a> FlowLayouter<'a> {
@ -243,7 +256,7 @@ impl<'a> FlowLayouter<'a> {
if let Some(first) = lines.first() { if let Some(first) = lines.first() {
if !self.regions.size.y.fits(first.height()) && !self.regions.in_last() { if !self.regions.size.y.fits(first.height()) && !self.regions.in_last() {
let carry: Vec<_> = self.items.drain(sticky..).collect(); let carry: Vec<_> = self.items.drain(sticky..).collect();
self.finish_region(engine)?; self.finish_region(engine, false)?;
for item in carry { for item in carry {
self.layout_item(engine, item)?; self.layout_item(engine, item)?;
} }
@ -323,7 +336,7 @@ impl<'a> FlowLayouter<'a> {
if self.regions.is_full() { if self.regions.is_full() {
// Skip directly if region is already full. // Skip directly if region is already full.
self.finish_region(engine)?; self.finish_region(engine, false)?;
} }
// How to align the block. // How to align the block.
@ -347,7 +360,7 @@ impl<'a> FlowLayouter<'a> {
} }
if i > 0 { if i > 0 {
self.finish_region(engine)?; self.finish_region(engine, false)?;
} }
let item = FlowItem::Frame { frame, align, sticky, movable: false }; let item = FlowItem::Frame { frame, align, sticky, movable: false };
@ -386,7 +399,7 @@ impl<'a> FlowLayouter<'a> {
FlowItem::Frame { ref frame, movable, .. } => { FlowItem::Frame { ref frame, movable, .. } => {
let height = frame.height(); let height = frame.height();
if !self.regions.size.y.fits(height) && !self.regions.in_last() { if !self.regions.size.y.fits(height) && !self.regions.in_last() {
self.finish_region(engine)?; self.finish_region(engine, false)?;
} }
self.regions.size.y -= height; self.regions.size.y -= height;
@ -396,7 +409,7 @@ impl<'a> FlowLayouter<'a> {
self.items.push(item); self.items.push(item);
if !self.handle_footnotes(engine, &mut notes, true, false)? { if !self.handle_footnotes(engine, &mut notes, true, false)? {
let item = self.items.pop(); let item = self.items.pop();
self.finish_region(engine)?; self.finish_region(engine, false)?;
self.items.extend(item); self.items.extend(item);
self.regions.size.y -= height; self.regions.size.y -= height;
self.handle_footnotes(engine, &mut notes, true, true)?; self.handle_footnotes(engine, &mut notes, true, true)?;
@ -454,7 +467,21 @@ impl<'a> FlowLayouter<'a> {
} }
/// Finish the frame for one region. /// Finish the frame for one region.
fn finish_region(&mut self, engine: &mut Engine) -> SourceResult<()> { ///
/// Set `force` to `true` to allow creating a frame for out-of-flow elements
/// only (this is used to force the creation of a frame in case the
/// remaining elements are all out-of-flow).
fn finish_region(&mut self, engine: &mut Engine, force: bool) -> SourceResult<()> {
if !force
&& !self.items.is_empty()
&& self.items.iter().all(FlowItem::is_out_of_flow)
{
self.finished.push(Frame::soft(self.initial));
self.regions.next();
self.initial = self.regions.size;
return Ok(());
}
// Trim weak spacing. // Trim weak spacing.
while self while self
.items .items
@ -591,13 +618,13 @@ impl<'a> FlowLayouter<'a> {
fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> { fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
if self.expand.y { if self.expand.y {
while !self.regions.backlog.is_empty() { while !self.regions.backlog.is_empty() {
self.finish_region(engine)?; self.finish_region(engine, true)?;
} }
} }
self.finish_region(engine)?; self.finish_region(engine, true)?;
while !self.items.is_empty() { while !self.items.is_empty() {
self.finish_region(engine)?; self.finish_region(engine, true)?;
} }
Ok(Fragment::frames(self.finished)) Ok(Fragment::frames(self.finished))
@ -611,7 +638,7 @@ impl FlowLayouter<'_> {
mut notes: Vec<FootnoteElem>, mut notes: Vec<FootnoteElem>,
) -> SourceResult<()> { ) -> SourceResult<()> {
if self.root && !self.handle_footnotes(engine, &mut notes, false, false)? { if self.root && !self.handle_footnotes(engine, &mut notes, false, false)? {
self.finish_region(engine)?; self.finish_region(engine, false)?;
self.handle_footnotes(engine, &mut notes, false, true)?; self.handle_footnotes(engine, &mut notes, false, true)?;
} }
Ok(()) Ok(())
@ -673,7 +700,7 @@ impl FlowLayouter<'_> {
for (i, frame) in frames.into_iter().enumerate() { for (i, frame) in frames.into_iter().enumerate() {
find_footnotes(notes, &frame); find_footnotes(notes, &frame);
if i > 0 { if i > 0 {
self.finish_region(engine)?; self.finish_region(engine, false)?;
self.layout_footnote_separator(engine)?; self.layout_footnote_separator(engine)?;
self.regions.size.y -= self.footnote_config.gap; self.regions.size.y -= self.footnote_config.gap;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -103,3 +103,10 @@ This is a normal page. Very normal.
// Test a page with zero columns. // Test a page with zero columns.
// Error: 49-50 number must be positive // Error: 49-50 number must be positive
#set page(height: auto, width: 7.05cm, columns: 0) #set page(height: auto, width: 7.05cm, columns: 0)
---
// Test colbreak after only out-of-flow elements.
#set page(width: 7.05cm, columns: 2)
#place[OOF]
#colbreak()
In flow.

View File

@ -0,0 +1,61 @@
// Test out-of-flow items (place, counter updates, etc.) at the
// beginning of a block not creating a frame just for them.
---
// No item in the first region.
#set page(height: 5cm, margin: 1cm)
No item in the first region.
#block(breakable: true, stroke: 1pt, inset: 0.5cm)[
#rect(height: 2cm, fill: gray)
]
---
// Counter update in the first region.
#set page(height: 5cm, margin: 1cm)
Counter update.
#block(breakable: true, stroke: 1pt, inset: 0.5cm)[
#counter("dummy").step()
#rect(height: 2cm, fill: gray)
]
---
// Placed item in the first region.
#set page(height: 5cm, margin: 1cm)
Placed item in the first region.
#block(breakable: true, above: 1cm, stroke: 1pt, inset: 0.5cm)[
#place(dx: -0.5cm, dy: -0.75cm, box(width: 200%)[OOF])
#rect(height: 2cm, fill: gray)
]
---
// In-flow item with size zero in the first region.
#set page(height: 5cm, margin: 1cm)
In-flow, zero-sized item.
#block(breakable: true, stroke: 1pt, inset: 0.5cm)[
#set block(spacing: 0pt)
#line(length: 0pt)
#rect(height: 2cm, fill: gray)
#line(length: 100%)
]
---
// Counter update and placed item in the first region.
#set page(height: 5cm, margin: 1cm)
Counter update + place.
#block(breakable: true, above: 1cm, stroke: 1pt, inset: 0.5cm)[
#counter("dummy").step()
#place(dx: -0.5cm, dy: -0.75cm, box([OOF]))
#rect(height: 2cm, fill: gray)
]
---
// Mix-and-match all the previous ones.
#set page(height: 5cm, margin: 1cm)
Mix-and-match all the previous tests.
#block(breakable: true, above: 1cm, stroke: 1pt, inset: 0.5cm)[
#counter("dummy").step()
#place(dx: -0.5cm, dy: -0.75cm, box(width: 200%)[OOF])
#line(length: 100%)
#place(dy: -0.8em)[OOF]
#rect(height: 2cm, fill: gray)
]