diff --git a/src/frame.rs b/src/frame.rs index e02fdf46a..b0442a06d 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -14,8 +14,9 @@ use crate::image::ImageId; pub struct Frame { /// The size of the frame. pub size: Size, - /// The baseline of the frame measured from the top. - pub baseline: Length, + /// The baseline of the frame measured from the top. If this is `None`, the + /// frame's implicit baseline is at the bottom. + pub baseline: Option, /// The elements composing this layout. pub elements: Vec<(Point, Element)>, } @@ -25,7 +26,12 @@ impl Frame { #[track_caller] pub fn new(size: Size) -> Self { assert!(size.is_finite()); - Self { size, baseline: size.y, elements: vec![] } + Self { size, baseline: None, elements: vec![] } + } + + /// The baseline of the frame. + pub fn baseline(&self) -> Length { + self.baseline.unwrap_or(self.size.y) } /// Add an element at a position in the background. @@ -64,15 +70,19 @@ impl Frame { aligns.y.resolve(target.y - self.size.y), ); self.size = target; - self.baseline += offset.y; self.translate(offset); } } - /// Move the contents of the frame by an offset. + /// Move the baseline and contents of the frame by an offset. pub fn translate(&mut self, offset: Point) { - for (point, _) in &mut self.elements { - *point += offset; + if !offset.is_zero() { + if let Some(baseline) = &mut self.baseline { + *baseline += offset.y; + } + for (point, _) in &mut self.elements { + *point += offset; + } } } diff --git a/src/geom/point.rs b/src/geom/point.rs index 30e3f9b6b..ab8f4439f 100644 --- a/src/geom/point.rs +++ b/src/geom/point.rs @@ -35,12 +35,9 @@ impl Point { Self { x: Length::zero(), y } } - /// Convert to the generic representation. - pub const fn to_gen(self, main: SpecAxis) -> Gen { - match main { - SpecAxis::Horizontal => Gen::new(self.y, self.x), - SpecAxis::Vertical => Gen::new(self.x, self.y), - } + /// Whether both components are zero. + pub fn is_zero(self) -> bool { + self.x.is_zero() && self.y.is_zero() } /// Transform the point with the given transformation. diff --git a/src/geom/spec.rs b/src/geom/spec.rs index 5d89c894f..40d7386bb 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -193,6 +193,11 @@ impl Size { self.x.fits(other.x) && self.y.fits(other.y) } + /// Whether both components are zero. + pub fn is_zero(self) -> bool { + self.x.is_zero() && self.y.is_zero() + } + /// Whether both components are finite. pub fn is_finite(self) -> bool { self.x.is_finite() && self.y.is_finite() diff --git a/src/library/flow.rs b/src/library/flow.rs index 9494d6c06..98b518b71 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -214,38 +214,28 @@ impl<'a> FlowLayouter<'a> { } let mut output = Frame::new(size); - let mut before = Length::zero(); + let mut offset = Length::zero(); let mut ruler = Align::Top; - let mut first = true; // Place all frames. for item in self.items.drain(..) { match item { FlowItem::Absolute(v) => { - before += v; + offset += v; } FlowItem::Fractional(v) => { - before += v.resolve(self.fr, remaining); + offset += v.resolve(self.fr, remaining); } FlowItem::Frame(frame, aligns) => { ruler = ruler.max(aligns.y); - - // Align horizontally and vertically. let x = aligns.x.resolve(size.x - frame.size.x); - let y = before + ruler.resolve(size.y - self.used.y); + let y = offset + ruler.resolve(size.y - self.used.y); let pos = Point::new(x, y); - before += frame.size.y; - - // The baseline of the flow is that of the first frame. - if first { - output.baseline = pos.y + frame.baseline; - first = false; - } - + offset += frame.size.y; output.push_frame(pos, frame); } FlowItem::Placed(frame) => { - output.push_frame(Point::with_y(before), frame); + output.push_frame(Point::with_y(offset), frame); } } } diff --git a/src/library/pad.rs b/src/library/pad.rs index 05cc30b7f..681da73eb 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -51,7 +51,6 @@ impl Layout for PadNode { // Grow the frame and translate everything in the frame inwards. let frame = Rc::make_mut(frame); frame.size = padded; - frame.baseline += offset.y; frame.translate(offset); // Set exact and base constraints if the child had them. Also set diff --git a/src/library/par.rs b/src/library/par.rs index c29e5f9ed..6abfa7af1 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -466,14 +466,18 @@ impl<'a> LineLayout<'a> { // Measure the size of the line. for item in first.iter().chain(items).chain(&last) { - match *item { - ParItem::Absolute(v) => width += v, - ParItem::Fractional(v) => fr += v, - ParItem::Text(ShapedText { size, baseline, .. }) - | ParItem::Frame(Frame { size, baseline, .. }) => { - width += size.x; - top.set_max(baseline); - bottom.set_max(size.y - baseline); + match item { + ParItem::Absolute(v) => width += *v, + ParItem::Fractional(v) => fr += *v, + ParItem::Text(shaped) => { + width += shaped.size.x; + top.set_max(shaped.baseline); + bottom.set_max(shaped.size.y - shaped.baseline); + } + ParItem::Frame(frame) => { + width += frame.size.x; + top.set_max(frame.baseline()); + bottom.set_max(frame.size.y - frame.baseline()); } } } @@ -496,9 +500,9 @@ impl<'a> LineLayout<'a> { let size = Size::new(self.size.x.max(width), self.size.y); let remaining = size.x - self.size.x; - let mut output = Frame::new(size); let mut offset = Length::zero(); - output.baseline = self.baseline; + let mut output = Frame::new(size); + output.baseline = Some(self.baseline); for (range, item) in self.reordered() { let mut position = |mut frame: Frame| { @@ -510,7 +514,7 @@ impl<'a> LineLayout<'a> { } let x = offset + self.par.align.resolve(remaining); - let y = self.baseline - frame.baseline; + let y = self.baseline - frame.baseline(); offset += frame.size.x; // Add to the line's frame. @@ -633,17 +637,10 @@ impl<'a> LineStack<'a> { let mut output = Frame::new(self.size); let mut offset = Length::zero(); - let mut first = true; for line in self.lines.drain(..) { let frame = line.build(ctx, self.size.x); - let pos = Point::with_y(offset); - if first { - output.baseline = pos.y + frame.baseline; - first = false; - } - offset += frame.size.y + self.leading; output.merge_frame(pos, frame); } diff --git a/src/library/text.rs b/src/library/text.rs index aac99e8a0..0790196d5 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -305,9 +305,9 @@ pub struct ShapedGlyph { impl<'a> ShapedText<'a> { /// Build the shaped text's frame. pub fn build(&self) -> Frame { - let mut frame = Frame::new(self.size); let mut offset = Length::zero(); - frame.baseline = self.baseline; + let mut frame = Frame::new(self.size); + frame.baseline = Some(self.baseline); for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) { let pos = Point::new(offset, self.baseline); diff --git a/tests/ref/elements/circle.png b/tests/ref/elements/circle.png index e5a38e280..931debd07 100644 Binary files a/tests/ref/elements/circle.png and b/tests/ref/elements/circle.png differ diff --git a/tests/ref/elements/image.png b/tests/ref/elements/image.png index 2385d8200..92c68e291 100644 Binary files a/tests/ref/elements/image.png and b/tests/ref/elements/image.png differ diff --git a/tests/ref/elements/square.png b/tests/ref/elements/square.png index 9663015f9..86bc1ff5d 100644 Binary files a/tests/ref/elements/square.png and b/tests/ref/elements/square.png differ diff --git a/tests/ref/layout/aspect.png b/tests/ref/layout/aspect.png index 62f17ba4f..f76b49802 100644 Binary files a/tests/ref/layout/aspect.png and b/tests/ref/layout/aspect.png differ diff --git a/tests/ref/layout/containers.png b/tests/ref/layout/containers.png index f8977f1de..f6981fe55 100644 Binary files a/tests/ref/layout/containers.png and b/tests/ref/layout/containers.png differ diff --git a/tests/ref/text/baseline.png b/tests/ref/text/baseline.png new file mode 100644 index 000000000..6890236fe Binary files /dev/null and b/tests/ref/text/baseline.png differ diff --git a/tests/typ/elements/image.typ b/tests/typ/elements/image.typ index 0fce4295c..5fd121e3a 100644 --- a/tests/typ/elements/image.typ +++ b/tests/typ/elements/image.typ @@ -42,6 +42,10 @@ Stuff \ Stuff #image("../../res/rhino.png") +--- +// Test baseline. +A #image("../../res/tiger.jpg", height: 1cm, width: 80%) B + --- // Error: 8-29 file not found #image("path/does/not/exist") diff --git a/tests/typ/layout/aspect.typ b/tests/typ/layout/aspect.typ index c0db17e09..c9b8ee41e 100644 --- a/tests/typ/layout/aspect.typ +++ b/tests/typ/layout/aspect.typ @@ -11,14 +11,10 @@ --- // Test alignment in automatically sized square and circle. #font(8pt) -#grid( - columns: 2, - gutter: 10pt, - square(padding: 4pt)[ - Hey there, #align(center + bottom, rotate(180deg, [you!])) - ], - circle(align(center + horizon, [Hey.])), -) +#square(padding: 4pt)[ + Hey there, #align(center + bottom, rotate(180deg, [you!])) +] +#circle(align(center + horizon, [Hey.])) --- // Test that maximum wins if both width and height are given. diff --git a/tests/typ/text/baseline.typ b/tests/typ/text/baseline.typ new file mode 100644 index 000000000..f22fce089 --- /dev/null +++ b/tests/typ/text/baseline.typ @@ -0,0 +1,4 @@ +// Test text baseline. + +--- +Hi #font(150%)[You], #font(75%)[how are you?]