Remove baseline weirdness

This commit is contained in:
Laurenz 2021-12-02 14:48:12 +01:00
parent f3bdc9d3da
commit bfa9962632
16 changed files with 60 additions and 58 deletions

View File

@ -14,8 +14,9 @@ use crate::image::ImageId;
pub struct Frame { pub struct Frame {
/// The size of the frame. /// The size of the frame.
pub size: Size, pub size: Size,
/// The baseline of the frame measured from the top. /// The baseline of the frame measured from the top. If this is `None`, the
pub baseline: Length, /// frame's implicit baseline is at the bottom.
pub baseline: Option<Length>,
/// The elements composing this layout. /// The elements composing this layout.
pub elements: Vec<(Point, Element)>, pub elements: Vec<(Point, Element)>,
} }
@ -25,7 +26,12 @@ impl Frame {
#[track_caller] #[track_caller]
pub fn new(size: Size) -> Self { pub fn new(size: Size) -> Self {
assert!(size.is_finite()); 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. /// Add an element at a position in the background.
@ -64,15 +70,19 @@ impl Frame {
aligns.y.resolve(target.y - self.size.y), aligns.y.resolve(target.y - self.size.y),
); );
self.size = target; self.size = target;
self.baseline += offset.y;
self.translate(offset); 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) { pub fn translate(&mut self, offset: Point) {
for (point, _) in &mut self.elements { if !offset.is_zero() {
*point += offset; if let Some(baseline) = &mut self.baseline {
*baseline += offset.y;
}
for (point, _) in &mut self.elements {
*point += offset;
}
} }
} }

View File

@ -35,12 +35,9 @@ impl Point {
Self { x: Length::zero(), y } Self { x: Length::zero(), y }
} }
/// Convert to the generic representation. /// Whether both components are zero.
pub const fn to_gen(self, main: SpecAxis) -> Gen<Length> { pub fn is_zero(self) -> bool {
match main { self.x.is_zero() && self.y.is_zero()
SpecAxis::Horizontal => Gen::new(self.y, self.x),
SpecAxis::Vertical => Gen::new(self.x, self.y),
}
} }
/// Transform the point with the given transformation. /// Transform the point with the given transformation.

View File

@ -193,6 +193,11 @@ impl Size {
self.x.fits(other.x) && self.y.fits(other.y) 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. /// Whether both components are finite.
pub fn is_finite(self) -> bool { pub fn is_finite(self) -> bool {
self.x.is_finite() && self.y.is_finite() self.x.is_finite() && self.y.is_finite()

View File

@ -214,38 +214,28 @@ impl<'a> FlowLayouter<'a> {
} }
let mut output = Frame::new(size); let mut output = Frame::new(size);
let mut before = Length::zero(); let mut offset = Length::zero();
let mut ruler = Align::Top; let mut ruler = Align::Top;
let mut first = true;
// Place all frames. // Place all frames.
for item in self.items.drain(..) { for item in self.items.drain(..) {
match item { match item {
FlowItem::Absolute(v) => { FlowItem::Absolute(v) => {
before += v; offset += v;
} }
FlowItem::Fractional(v) => { FlowItem::Fractional(v) => {
before += v.resolve(self.fr, remaining); offset += v.resolve(self.fr, remaining);
} }
FlowItem::Frame(frame, aligns) => { FlowItem::Frame(frame, aligns) => {
ruler = ruler.max(aligns.y); ruler = ruler.max(aligns.y);
// Align horizontally and vertically.
let x = aligns.x.resolve(size.x - frame.size.x); 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); let pos = Point::new(x, y);
before += frame.size.y; offset += frame.size.y;
// The baseline of the flow is that of the first frame.
if first {
output.baseline = pos.y + frame.baseline;
first = false;
}
output.push_frame(pos, frame); output.push_frame(pos, frame);
} }
FlowItem::Placed(frame) => { FlowItem::Placed(frame) => {
output.push_frame(Point::with_y(before), frame); output.push_frame(Point::with_y(offset), frame);
} }
} }
} }

View File

@ -51,7 +51,6 @@ impl Layout for PadNode {
// Grow the frame and translate everything in the frame inwards. // Grow the frame and translate everything in the frame inwards.
let frame = Rc::make_mut(frame); let frame = Rc::make_mut(frame);
frame.size = padded; frame.size = padded;
frame.baseline += offset.y;
frame.translate(offset); frame.translate(offset);
// Set exact and base constraints if the child had them. Also set // Set exact and base constraints if the child had them. Also set

View File

@ -466,14 +466,18 @@ impl<'a> LineLayout<'a> {
// Measure the size of the line. // Measure the size of the line.
for item in first.iter().chain(items).chain(&last) { for item in first.iter().chain(items).chain(&last) {
match *item { match item {
ParItem::Absolute(v) => width += v, ParItem::Absolute(v) => width += *v,
ParItem::Fractional(v) => fr += v, ParItem::Fractional(v) => fr += *v,
ParItem::Text(ShapedText { size, baseline, .. }) ParItem::Text(shaped) => {
| ParItem::Frame(Frame { size, baseline, .. }) => { width += shaped.size.x;
width += size.x; top.set_max(shaped.baseline);
top.set_max(baseline); bottom.set_max(shaped.size.y - shaped.baseline);
bottom.set_max(size.y - 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 size = Size::new(self.size.x.max(width), self.size.y);
let remaining = size.x - self.size.x; let remaining = size.x - self.size.x;
let mut output = Frame::new(size);
let mut offset = Length::zero(); 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() { for (range, item) in self.reordered() {
let mut position = |mut frame: Frame| { let mut position = |mut frame: Frame| {
@ -510,7 +514,7 @@ impl<'a> LineLayout<'a> {
} }
let x = offset + self.par.align.resolve(remaining); let x = offset + self.par.align.resolve(remaining);
let y = self.baseline - frame.baseline; let y = self.baseline - frame.baseline();
offset += frame.size.x; offset += frame.size.x;
// Add to the line's frame. // Add to the line's frame.
@ -633,17 +637,10 @@ impl<'a> LineStack<'a> {
let mut output = Frame::new(self.size); let mut output = Frame::new(self.size);
let mut offset = Length::zero(); let mut offset = Length::zero();
let mut first = true;
for line in self.lines.drain(..) { for line in self.lines.drain(..) {
let frame = line.build(ctx, self.size.x); let frame = line.build(ctx, self.size.x);
let pos = Point::with_y(offset); let pos = Point::with_y(offset);
if first {
output.baseline = pos.y + frame.baseline;
first = false;
}
offset += frame.size.y + self.leading; offset += frame.size.y + self.leading;
output.merge_frame(pos, frame); output.merge_frame(pos, frame);
} }

View File

@ -305,9 +305,9 @@ pub struct ShapedGlyph {
impl<'a> ShapedText<'a> { impl<'a> ShapedText<'a> {
/// Build the shaped text's frame. /// Build the shaped text's frame.
pub fn build(&self) -> Frame { pub fn build(&self) -> Frame {
let mut frame = Frame::new(self.size);
let mut offset = Length::zero(); 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) { for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) {
let pos = Point::new(offset, self.baseline); let pos = Point::new(offset, self.baseline);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
tests/ref/text/baseline.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

View File

@ -42,6 +42,10 @@ Stuff \
Stuff Stuff
#image("../../res/rhino.png") #image("../../res/rhino.png")
---
// Test baseline.
A #image("../../res/tiger.jpg", height: 1cm, width: 80%) B
--- ---
// Error: 8-29 file not found // Error: 8-29 file not found
#image("path/does/not/exist") #image("path/does/not/exist")

View File

@ -11,14 +11,10 @@
--- ---
// Test alignment in automatically sized square and circle. // Test alignment in automatically sized square and circle.
#font(8pt) #font(8pt)
#grid( #square(padding: 4pt)[
columns: 2, Hey there, #align(center + bottom, rotate(180deg, [you!]))
gutter: 10pt, ]
square(padding: 4pt)[ #circle(align(center + horizon, [Hey.]))
Hey there, #align(center + bottom, rotate(180deg, [you!]))
],
circle(align(center + horizon, [Hey.])),
)
--- ---
// Test that maximum wins if both width and height are given. // Test that maximum wins if both width and height are given.

View File

@ -0,0 +1,4 @@
// Test text baseline.
---
Hi #font(150%)[You], #font(75%)[how are you?]