mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Do not create a block frame for out-of-flow items only (#2517)
This commit is contained in:
parent
41c0dae209
commit
356bdeba18
@ -82,7 +82,7 @@ impl Layout for FlowElem {
|
||||
} else if child.is::<ColbreakElem>() {
|
||||
if !layouter.regions.backlog.is_empty() || layouter.regions.last.is_some()
|
||||
{
|
||||
layouter.finish_region(engine)?;
|
||||
layouter.finish_region(engine, true)?;
|
||||
}
|
||||
} else {
|
||||
bail!(child.span(), "unexpected flow child");
|
||||
@ -160,6 +160,19 @@ impl FlowItem {
|
||||
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> {
|
||||
@ -243,7 +256,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
if let Some(first) = lines.first() {
|
||||
if !self.regions.size.y.fits(first.height()) && !self.regions.in_last() {
|
||||
let carry: Vec<_> = self.items.drain(sticky..).collect();
|
||||
self.finish_region(engine)?;
|
||||
self.finish_region(engine, false)?;
|
||||
for item in carry {
|
||||
self.layout_item(engine, item)?;
|
||||
}
|
||||
@ -323,7 +336,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
|
||||
if self.regions.is_full() {
|
||||
// Skip directly if region is already full.
|
||||
self.finish_region(engine)?;
|
||||
self.finish_region(engine, false)?;
|
||||
}
|
||||
|
||||
// How to align the block.
|
||||
@ -347,7 +360,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
self.finish_region(engine)?;
|
||||
self.finish_region(engine, false)?;
|
||||
}
|
||||
|
||||
let item = FlowItem::Frame { frame, align, sticky, movable: false };
|
||||
@ -386,7 +399,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
FlowItem::Frame { ref frame, movable, .. } => {
|
||||
let height = frame.height();
|
||||
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;
|
||||
@ -396,7 +409,7 @@ impl<'a> FlowLayouter<'a> {
|
||||
self.items.push(item);
|
||||
if !self.handle_footnotes(engine, &mut notes, true, false)? {
|
||||
let item = self.items.pop();
|
||||
self.finish_region(engine)?;
|
||||
self.finish_region(engine, false)?;
|
||||
self.items.extend(item);
|
||||
self.regions.size.y -= height;
|
||||
self.handle_footnotes(engine, &mut notes, true, true)?;
|
||||
@ -454,7 +467,21 @@ impl<'a> FlowLayouter<'a> {
|
||||
}
|
||||
|
||||
/// 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.
|
||||
while self
|
||||
.items
|
||||
@ -591,13 +618,13 @@ impl<'a> FlowLayouter<'a> {
|
||||
fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
|
||||
if self.expand.y {
|
||||
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() {
|
||||
self.finish_region(engine)?;
|
||||
self.finish_region(engine, true)?;
|
||||
}
|
||||
|
||||
Ok(Fragment::frames(self.finished))
|
||||
@ -611,7 +638,7 @@ impl FlowLayouter<'_> {
|
||||
mut notes: Vec<FootnoteElem>,
|
||||
) -> SourceResult<()> {
|
||||
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)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -673,7 +700,7 @@ impl FlowLayouter<'_> {
|
||||
for (i, frame) in frames.into_iter().enumerate() {
|
||||
find_footnotes(notes, &frame);
|
||||
if i > 0 {
|
||||
self.finish_region(engine)?;
|
||||
self.finish_region(engine, false)?;
|
||||
self.layout_footnote_separator(engine)?;
|
||||
self.regions.size.y -= self.footnote_config.gap;
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 94 KiB |
BIN
tests/ref/layout/out-of-flow-in-block.png
Normal file
BIN
tests/ref/layout/out-of-flow-in-block.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
@ -103,3 +103,10 @@ This is a normal page. Very normal.
|
||||
// Test a page with zero columns.
|
||||
// Error: 49-50 number must be positive
|
||||
#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.
|
||||
|
61
tests/typ/layout/out-of-flow-in-block.typ
Normal file
61
tests/typ/layout/out-of-flow-in-block.typ
Normal 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)
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user