Remove baseline weirdness
24
src/frame.rs
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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()
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
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
|
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")
|
||||||
|
@ -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.
|
||||||
|
4
tests/typ/text/baseline.typ
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Test text baseline.
|
||||||
|
|
||||||
|
---
|
||||||
|
Hi #font(150%)[You], #font(75%)[how are you?]
|