mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +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>() {
|
} 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 |
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.
|
// 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.
|
||||||
|
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