summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst/src/layout/flow.rs20
-rw-r--r--crates/typst/src/layout/mod.rs1
-rw-r--r--crates/typst/src/layout/place.rs51
-rw-r--r--crates/typst/src/realize/mod.rs5
-rw-r--r--tests/ref/place-figure-flush.pngbin0 -> 5197 bytes
-rw-r--r--tests/ref/place-float-flush.pngbin0 -> 2644 bytes
-rw-r--r--tests/suite/layout/place.typ27
7 files changed, 96 insertions, 8 deletions
diff --git a/crates/typst/src/layout/flow.rs b/crates/typst/src/layout/flow.rs
index 2f985f28..84a395cc 100644
--- a/crates/typst/src/layout/flow.rs
+++ b/crates/typst/src/layout/flow.rs
@@ -13,9 +13,9 @@ use crate::foundations::{
};
use crate::introspection::TagElem;
use crate::layout::{
- Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment, Fr,
- Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem, Point, Regions,
- Rel, Size, Spacing, VElem,
+ Abs, AlignElem, Axes, BlockElem, ColbreakElem, ColumnsElem, FixedAlignment,
+ FlushElem, Fr, Fragment, Frame, FrameItem, LayoutMultiple, LayoutSingle, PlaceElem,
+ Point, Regions, Rel, Size, Spacing, VElem,
};
use crate::model::{FootnoteElem, FootnoteEntry, ParElem};
use crate::utils::Numeric;
@@ -73,6 +73,8 @@ impl LayoutMultiple for Packed<FlowElem> {
if let Some(elem) = child.to_packed::<TagElem>() {
layouter.layout_tag(elem);
+ } else if child.is::<FlushElem>() {
+ layouter.flush(engine)?;
} else if let Some(elem) = child.to_packed::<VElem>() {
layouter.layout_spacing(engine, elem, styles)?;
} else if let Some(placed) = child.to_packed::<PlaceElem>() {
@@ -683,6 +685,18 @@ impl<'a> FlowLayouter<'a> {
Ok(())
}
+ /// Lays out all floating elements before continuing with other content.
+ fn flush(&mut self, engine: &mut Engine) -> SourceResult<()> {
+ for item in std::mem::take(&mut self.pending_floats) {
+ self.layout_item(engine, item)?;
+ }
+ while !self.pending_floats.is_empty() {
+ self.finish_region(engine, false)?;
+ }
+
+ Ok(())
+ }
+
/// Finish layouting and return the resulting fragment.
fn finish(mut self, engine: &mut Engine) -> SourceResult<Fragment> {
if self.expand.y {
diff --git a/crates/typst/src/layout/mod.rs b/crates/typst/src/layout/mod.rs
index 0fd8e6b1..444d5162 100644
--- a/crates/typst/src/layout/mod.rs
+++ b/crates/typst/src/layout/mod.rs
@@ -107,6 +107,7 @@ pub fn define(global: &mut Scope) {
global.define_elem::<ColumnsElem>();
global.define_elem::<ColbreakElem>();
global.define_elem::<PlaceElem>();
+ global.define_elem::<FlushElem>();
global.define_elem::<AlignElem>();
global.define_elem::<PadElem>();
global.define_elem::<RepeatElem>();
diff --git a/crates/typst/src/layout/place.rs b/crates/typst/src/layout/place.rs
index 7d41b037..f81af2b8 100644
--- a/crates/typst/src/layout/place.rs
+++ b/crates/typst/src/layout/place.rs
@@ -1,6 +1,6 @@
use crate::diag::{bail, At, Hint, SourceResult};
use crate::engine::Engine;
-use crate::foundations::{elem, Content, Packed, Smart, StyleChain};
+use crate::foundations::{elem, scope, Content, Packed, Smart, StyleChain, Unlabellable};
use crate::layout::{
Alignment, Axes, Em, Fragment, LayoutMultiple, Length, Regions, Rel, Size, VAlignment,
};
@@ -26,7 +26,7 @@ use crate::realize::{Behave, Behaviour};
/// ),
/// )
/// ```
-#[elem(Behave)]
+#[elem(scope, Behave)]
pub struct PlaceElem {
/// Relative to which position in the parent container to place the content.
///
@@ -43,7 +43,9 @@ pub struct PlaceElem {
/// Whether the placed element has floating layout.
///
/// Floating elements are positioned at the top or bottom of the page,
- /// displacing in-flow content.
+ /// displacing in-flow content. They are always placed in the in-flow
+ /// order relative to each other, as well as before any content following
+ /// a later [`flush`] element.
///
/// ```example
/// #set page(height: 150pt)
@@ -95,6 +97,12 @@ pub struct PlaceElem {
pub body: Content,
}
+#[scope]
+impl PlaceElem {
+ #[elem]
+ type FlushElem;
+}
+
impl Packed<PlaceElem> {
#[typst_macros::time(name = "place", span = self.span())]
pub fn layout(
@@ -136,3 +144,40 @@ impl Behave for Packed<PlaceElem> {
Behaviour::Ignorant
}
}
+
+/// Asks the layout algorithm to place pending floating elements before
+/// continuing with the content.
+///
+/// This is useful for preventing floating figures from spilling
+/// into the next section.
+///
+/// ```example
+/// #set page(height: 165pt, width: 150pt)
+///
+/// Some introductory text: #lorem(15)
+///
+/// #figure(
+/// rect(
+/// width: 100%,
+/// height: 64pt,
+/// [I float with a caption!],
+/// ),
+/// placement: auto,
+/// caption: [A self-describing figure],
+/// )
+///
+/// #place.flush()
+///
+/// Some conclusive text that must occur
+/// after the figure.
+/// ```
+#[elem(Behave, Unlabellable)]
+pub struct FlushElem {}
+
+impl Behave for Packed<FlushElem> {
+ fn behaviour(&self) -> Behaviour {
+ Behaviour::Invisible
+ }
+}
+
+impl Unlabellable for Packed<FlushElem> {}
diff --git a/crates/typst/src/realize/mod.rs b/crates/typst/src/realize/mod.rs
index 4679a61c..600df1bb 100644
--- a/crates/typst/src/realize/mod.rs
+++ b/crates/typst/src/realize/mod.rs
@@ -23,8 +23,8 @@ use crate::foundations::{
};
use crate::introspection::TagElem;
use crate::layout::{
- AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, HElem, LayoutMultiple,
- LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem,
+ AlignElem, BlockElem, BoxElem, ColbreakElem, FlowElem, FlushElem, HElem,
+ LayoutMultiple, LayoutSingle, PageElem, PagebreakElem, Parity, PlaceElem, VElem,
};
use crate::math::{EquationElem, LayoutMath};
use crate::model::{
@@ -381,6 +381,7 @@ impl<'a> FlowBuilder<'a> {
|| content.is::<ColbreakElem>()
|| content.is::<TagElem>()
|| content.is::<PlaceElem>()
+ || content.is::<FlushElem>()
{
self.0.push(content, styles);
return true;
diff --git a/tests/ref/place-figure-flush.png b/tests/ref/place-figure-flush.png
new file mode 100644
index 00000000..2e6e0310
--- /dev/null
+++ b/tests/ref/place-figure-flush.png
Binary files differ
diff --git a/tests/ref/place-float-flush.png b/tests/ref/place-float-flush.png
new file mode 100644
index 00000000..67c01c2c
--- /dev/null
+++ b/tests/ref/place-float-flush.png
Binary files differ
diff --git a/tests/suite/layout/place.typ b/tests/suite/layout/place.typ
index b8765e93..0922800d 100644
--- a/tests/suite/layout/place.typ
+++ b/tests/suite/layout/place.typ
@@ -121,6 +121,33 @@ Second
image("/assets/images/diagram.svg", width: 80%),
)
+--- place-float-flush ---
+#set page(height: 150pt, width: 150pt)
+
+#let floater = place(auto, float: true, rect(width: 100%, height: 90pt, text(size: 24pt)[I float!]))
+
+Some introductory text.
+
+#floater #floater #floater #floater
+
+Some additional text.
+
+#place.flush()
+
+Some conclusive text. // Should appear after all the floating figures
+
+--- place-figure-flush ---
+
+#set page(height: 165pt, width: 150pt)
+
+Some introductory text: #lorem(15)
+
+#figure(placement: auto, caption: [A self-describing figure], rect(width: 100%, height: 64pt, [I float with a caption!]))
+
+#place.flush()
+
+Some conclusive text that must occur after the figure.
+
--- place-bottom-in-box ---
#box(
fill: aqua,