Remove baseline weirdness
24
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<Length>,
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Length> {
|
||||
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.
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 206 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
BIN
tests/ref/text/baseline.png
Normal file
After Width: | Height: | Size: 886 B |
@ -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")
|
||||
|
@ -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.
|
||||
|
4
tests/typ/text/baseline.typ
Normal file
@ -0,0 +1,4 @@
|
||||
// Test text baseline.
|
||||
|
||||
---
|
||||
Hi #font(150%)[You], #font(75%)[how are you?]
|