diff --git a/crates/typst-library/src/layout/flow.rs b/crates/typst-library/src/layout/flow.rs index ce276f6d3..930857e75 100644 --- a/crates/typst-library/src/layout/flow.rs +++ b/crates/typst-library/src/layout/flow.rs @@ -66,6 +66,8 @@ impl Layout for FlowElem { sticky: true, movable: false, }); + } else if let Some(placed) = child.to::() { + layouter.layout_placed(vt, placed, styles)?; } else if child.can::() { layouter.layout_multiple(vt, child, styles)?; } else if child.is::() { @@ -128,7 +130,13 @@ enum FlowItem { /// (to keep it together with its footnotes). Frame { frame: Frame, aligns: Axes, sticky: bool, movable: bool }, /// An absolutely placed frame. - Placed { frame: Frame, y_align: Smart>, float: bool, clearance: Abs }, + Placed { + frame: Frame, + x_align: Align, + y_align: Smart>, + float: bool, + clearance: Abs, + }, /// A footnote frame (can also be the separator). Footnote(Frame), } @@ -258,6 +266,25 @@ impl<'a> FlowLayouter<'a> { Ok(()) } + /// Layout a placed element. + fn layout_placed( + &mut self, + vt: &mut Vt, + placed: &PlaceElem, + styles: StyleChain, + ) -> SourceResult<()> { + let float = placed.float(styles); + let clearance = placed.clearance(styles); + let alignment = placed.alignment(styles); + let x_align = alignment.map_or(Align::Center, |aligns| { + aligns.x.unwrap_or(GenAlign::Start).resolve(styles) + }); + let y_align = alignment.map(|align| align.y.resolve(styles)); + let frame = placed.layout(vt, styles, self.regions)?.into_frame(); + let item = FlowItem::Placed { frame, x_align, y_align, float, clearance }; + self.layout_item(vt, item) + } + /// Layout into multiple regions. fn layout_multiple( &mut self, @@ -265,16 +292,6 @@ impl<'a> FlowLayouter<'a> { block: &Content, styles: StyleChain, ) -> SourceResult<()> { - // Handle placed elements. - if let Some(placed) = block.to::() { - let float = placed.float(styles); - let clearance = placed.clearance(styles); - let y_align = placed.alignment(styles).map(|align| align.y.resolve(styles)); - let frame = placed.layout_inner(vt, styles, self.regions)?.into_frame(); - let item = FlowItem::Placed { frame, y_align, float, clearance }; - return self.layout_item(vt, item); - } - // Temporarily delegerate rootness to the columns. let is_root = self.root; if is_root && block.is::() { @@ -491,7 +508,8 @@ impl<'a> FlowLayouter<'a> { offset += frame.height(); output.push_frame(pos, frame); } - FlowItem::Placed { frame, y_align, float, .. } => { + FlowItem::Placed { frame, x_align, y_align, float, .. } => { + let x = x_align.position(size.x - frame.width()); let y = if float { match y_align { Smart::Custom(Some(Align::Top)) => { @@ -505,7 +523,7 @@ impl<'a> FlowLayouter<'a> { float_bottom_offset += frame.height(); y } - _ => offset + ruler.position(size.y - used.y), + _ => unreachable!("float must be y aligned"), } } else { match y_align { @@ -516,7 +534,7 @@ impl<'a> FlowLayouter<'a> { } }; - output.push_frame(Point::with_y(y), frame); + output.push_frame(Point::new(x, y), frame); } FlowItem::Footnote(frame) => { let y = size.y - footnote_height + footnote_offset; diff --git a/crates/typst-library/src/layout/mod.rs b/crates/typst-library/src/layout/mod.rs index 41490eb8f..ce728ccb7 100644 --- a/crates/typst-library/src/layout/mod.rs +++ b/crates/typst-library/src/layout/mod.rs @@ -266,6 +266,8 @@ fn realize_block<'a>( content: &'a Content, styles: StyleChain<'a>, ) -> SourceResult<(Content, StyleChain<'a>)> { + // These elements implement `Layout` but still require a flow for + // proper layout. if content.can::() && !content.is::() && !content.is::() @@ -275,6 +277,7 @@ fn realize_block<'a>( && !content.is::() && !content.is::() && !content.is::() + && !content.is::() && !applicable(content, styles) { return Ok((content.clone(), styles)); diff --git a/crates/typst-library/src/layout/place.rs b/crates/typst-library/src/layout/place.rs index 6f2681c10..115e5107a 100644 --- a/crates/typst-library/src/layout/place.rs +++ b/crates/typst-library/src/layout/place.rs @@ -91,36 +91,13 @@ impl Layout for PlaceElem { vt: &mut Vt, styles: StyleChain, regions: Regions, - ) -> SourceResult { - let mut frame = self.layout_inner(vt, styles, regions)?.into_frame(); - - // If expansion is off, zero all sizes so that we don't take up any - // space in our parent. Otherwise, respect the expand settings. - let target = regions.expand.select(regions.size, Size::zero()); - frame.resize(target, Align::LEFT_TOP); - - Ok(Fragment::frame(frame)) - } -} - -impl PlaceElem { - /// Layout without zeroing the frame size. - pub fn layout_inner( - &self, - vt: &mut Vt, - styles: StyleChain, - regions: Regions, ) -> SourceResult { // The pod is the base area of the region because for absolute // placement we don't really care about the already used area. let base = regions.base(); - let expand = - Axes::new(base.x.is_finite(), base.y.is_finite() && !self.float(styles)); - - let pod = Regions::one(base, expand); - let float = self.float(styles); let alignment = self.alignment(styles); + if float && !matches!( alignment, @@ -145,7 +122,9 @@ impl PlaceElem { alignment.unwrap_or_else(|| Axes::with_x(Some(Align::Center.into()))), ); - child.layout(vt, styles, pod) + let pod = Regions::one(base, Axes::splat(false)); + let frame = child.layout(vt, styles, pod)?.into_frame(); + Ok(Fragment::frame(frame)) } } diff --git a/tests/ref/bugs/place-nested.png b/tests/ref/bugs/place-nested.png new file mode 100644 index 000000000..b59dc5d3d Binary files /dev/null and b/tests/ref/bugs/place-nested.png differ diff --git a/tests/ref/layout/place-nested.png b/tests/ref/layout/place-nested.png new file mode 100644 index 000000000..ccd55ee4d Binary files /dev/null and b/tests/ref/layout/place-nested.png differ diff --git a/tests/typ/layout/place-nested.typ b/tests/typ/layout/place-nested.typ new file mode 100644 index 000000000..c979176a7 --- /dev/null +++ b/tests/typ/layout/place-nested.typ @@ -0,0 +1,33 @@ +// Test vertical alignment with nested placement. + +--- +#box( + fill: aqua, + width: 30pt, + height: 30pt, + place(bottom, + place(line(start: (0pt, 0pt), end: (20pt, 0pt), stroke: red + 3pt)) + ) +) + +--- +#box( + fill: aqua, + width: 30pt, + height: 30pt, + { + box(fill: yellow, { + [Hello] + place(horizon, line(start: (0pt, 0pt), end: (20pt, 0pt), stroke: red + 2pt)) + }) + place(horizon, line(start: (0pt, 0pt), end: (20pt, 0pt), stroke: green + 3pt)) + } +) + +--- +#box(fill: aqua)[ + #place(bottom + right)[Hi] + Hello World \ + How are \ + you? +]