mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Tidy up layouting code
This commit is contained in:
parent
24c4a746bc
commit
c975d0d5e9
@ -167,7 +167,7 @@ struct StackBuilder {
|
||||
impl StackBuilder {
|
||||
fn new(state: &State) -> Self {
|
||||
Self {
|
||||
dirs: Gen::new(Dir::TTB, state.lang.dir),
|
||||
dirs: Gen::new(state.lang.dir, Dir::TTB),
|
||||
children: vec![],
|
||||
last: Last::None,
|
||||
par: ParBuilder::new(state),
|
||||
|
@ -29,7 +29,7 @@ impl Default for State {
|
||||
page: PageState::default(),
|
||||
par: ParState::default(),
|
||||
font: FontState::default(),
|
||||
aligns: Gen::new(Align::Start, Align::Start),
|
||||
aligns: Gen::splat(Align::Start),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,7 @@ impl PageState {
|
||||
Self {
|
||||
class: paper.class,
|
||||
size: paper.size(),
|
||||
margins: Sides::uniform(None),
|
||||
margins: Sides::splat(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,24 +3,24 @@ use super::*;
|
||||
/// A container with a main and cross component.
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Gen<T> {
|
||||
/// The main component.
|
||||
pub main: T,
|
||||
/// The cross component.
|
||||
pub cross: T,
|
||||
/// The main component.
|
||||
pub main: T,
|
||||
}
|
||||
|
||||
impl<T> Gen<T> {
|
||||
/// Create a new instance from the two components.
|
||||
pub fn new(main: T, cross: T) -> Self {
|
||||
Self { main, cross }
|
||||
pub fn new(cross: T, main: T) -> Self {
|
||||
Self { cross, main }
|
||||
}
|
||||
|
||||
/// Create a new instance with two equal components.
|
||||
pub fn uniform(value: T) -> Self
|
||||
pub fn splat(value: T) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
Self { main: value.clone(), cross: value }
|
||||
Self { cross: value.clone(), main: value }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,11 +79,21 @@ impl Length {
|
||||
Self { raw: self.raw.min(other.raw) }
|
||||
}
|
||||
|
||||
/// Set to the minimum of this and another length.
|
||||
pub fn set_min(&mut self, other: Self) {
|
||||
*self = self.min(other);
|
||||
}
|
||||
|
||||
/// The maximum of this and another length.
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
Self { raw: self.raw.max(other.raw) }
|
||||
}
|
||||
|
||||
/// Set to the maximum of this and another length.
|
||||
pub fn set_max(&mut self, other: Self) {
|
||||
*self = self.max(other);
|
||||
}
|
||||
|
||||
/// Whether the other length fits into this one (i.e. is smaller).
|
||||
pub fn fits(self, other: Self) -> bool {
|
||||
self.raw + 1e-6 >= other.raw
|
||||
|
@ -22,6 +22,25 @@ impl Path {
|
||||
Self(vec![])
|
||||
}
|
||||
|
||||
/// Create a path that approximates an axis-aligned ellipse.
|
||||
pub fn ellipse(size: Size) -> Self {
|
||||
// https://stackoverflow.com/a/2007782
|
||||
let rx = size.width / 2.0;
|
||||
let ry = size.height / 2.0;
|
||||
let m = 0.551784;
|
||||
let mx = m * rx;
|
||||
let my = m * ry;
|
||||
let z = Length::ZERO;
|
||||
let point = Point::new;
|
||||
let mut path = Self::new();
|
||||
path.move_to(point(-rx, z));
|
||||
path.cubic_to(point(-rx, my), point(-mx, ry), point(z, ry));
|
||||
path.cubic_to(point(mx, ry), point(rx, my), point(rx, z));
|
||||
path.cubic_to(point(rx, -my), point(mx, -ry), point(z, -ry));
|
||||
path.cubic_to(point(-mx, -ry), point(-rx, -my), point(z - rx, z));
|
||||
path
|
||||
}
|
||||
|
||||
/// Push a [`MoveTo`](PathElement::MoveTo) element.
|
||||
pub fn move_to(&mut self, p: Point) {
|
||||
self.0.push(PathElement::MoveTo(p));
|
||||
@ -42,22 +61,3 @@ impl Path {
|
||||
self.0.push(PathElement::ClosePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a path that approximates an axis-aligned ellipse.
|
||||
pub fn ellipse_path(size: Size) -> Path {
|
||||
// https://stackoverflow.com/a/2007782
|
||||
let rx = size.width / 2.0;
|
||||
let ry = size.height / 2.0;
|
||||
let m = 0.551784;
|
||||
let mx = m * rx;
|
||||
let my = m * ry;
|
||||
let z = Length::ZERO;
|
||||
let point = Point::new;
|
||||
let mut path = Path::new();
|
||||
path.move_to(point(-rx, z));
|
||||
path.cubic_to(point(-rx, my), point(-mx, ry), point(z, ry));
|
||||
path.cubic_to(point(mx, ry), point(rx, my), point(rx, z));
|
||||
path.cubic_to(point(rx, -my), point(mx, -ry), point(z, -ry));
|
||||
path.cubic_to(point(-mx, -ry), point(-rx, -my), point(z - rx, z));
|
||||
path
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ impl Point {
|
||||
}
|
||||
|
||||
/// Create an instance with two equal components.
|
||||
pub fn uniform(value: Length) -> Self {
|
||||
pub fn splat(value: Length) -> Self {
|
||||
Self { x: value, y: value }
|
||||
}
|
||||
}
|
||||
@ -49,8 +49,8 @@ impl Switch for Point {
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.x, self.y),
|
||||
SpecAxis::Vertical => Gen::new(self.y, self.x),
|
||||
SpecAxis::Horizontal => Gen::new(self.y, self.x),
|
||||
SpecAxis::Vertical => Gen::new(self.x, self.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ impl<T> Sides<T> {
|
||||
}
|
||||
|
||||
/// Create an instance with four equal components.
|
||||
pub fn uniform(value: T) -> Self
|
||||
pub fn splat(value: T) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ impl Size {
|
||||
}
|
||||
|
||||
/// Create an instance with two equal components.
|
||||
pub fn uniform(value: Length) -> Self {
|
||||
pub fn splat(value: Length) -> Self {
|
||||
Self { width: value, height: value }
|
||||
}
|
||||
|
||||
@ -77,8 +77,8 @@ impl Switch for Size {
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.width, self.height),
|
||||
SpecAxis::Vertical => Gen::new(self.height, self.width),
|
||||
SpecAxis::Horizontal => Gen::new(self.height, self.width),
|
||||
SpecAxis::Vertical => Gen::new(self.width, self.height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ impl<T> Spec<T> {
|
||||
}
|
||||
|
||||
/// Create a new instance with two equal components.
|
||||
pub fn uniform(value: T) -> Self
|
||||
pub fn splat(value: T) -> Self
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
@ -68,8 +68,8 @@ impl<T> Switch for Spec<T> {
|
||||
|
||||
fn switch(self, main: SpecAxis) -> Self::Other {
|
||||
match main {
|
||||
SpecAxis::Horizontal => Gen::new(self.horizontal, self.vertical),
|
||||
SpecAxis::Vertical => Gen::new(self.vertical, self.horizontal),
|
||||
SpecAxis::Horizontal => Gen::new(self.vertical, self.horizontal),
|
||||
SpecAxis::Vertical => Gen::new(self.horizontal, self.vertical),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,14 +13,14 @@ pub struct FixedNode {
|
||||
|
||||
impl Layout for FixedNode {
|
||||
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
|
||||
let Areas { current, full, .. } = areas;
|
||||
let Areas { current, base, .. } = areas;
|
||||
let size = Size::new(
|
||||
self.width.map_or(current.width, |w| w.resolve(full.width)),
|
||||
self.height.map_or(current.height, |h| h.resolve(full.height)),
|
||||
self.width.map_or(current.width, |w| w.resolve(base.width)),
|
||||
self.height.map_or(current.height, |h| h.resolve(base.height)),
|
||||
);
|
||||
|
||||
let fixed = Spec::new(self.width.is_some(), self.height.is_some());
|
||||
let areas = Areas::once(size, size, fixed);
|
||||
let areas = Areas::once(size, fixed);
|
||||
self.child.layout(ctx, &areas)
|
||||
}
|
||||
}
|
||||
|
@ -144,67 +144,63 @@ pub struct LayoutContext<'a> {
|
||||
pub struct Areas {
|
||||
/// The remaining size of the current area.
|
||||
pub current: Size,
|
||||
/// The full size the current area once had (used for relative sizing).
|
||||
pub full: Size,
|
||||
/// A stack of followup areas (the next area is the last element).
|
||||
pub backlog: Vec<Size>,
|
||||
/// The final area that is repeated when the backlog is empty.
|
||||
pub last: Option<Size>,
|
||||
/// Whether the frames resulting from layouting into this areas should
|
||||
/// expand to the fixed size defined by `current`.
|
||||
/// The base size for relative sizing.
|
||||
pub base: Size,
|
||||
/// A stack of followup areas.
|
||||
///
|
||||
/// If this is false, the frame will shrink to fit its content.
|
||||
/// Note that this is a stack and not a queue! The size of the next area is
|
||||
/// `backlog.last()`.
|
||||
pub backlog: Vec<Size>,
|
||||
/// The final area that is repeated once the backlog is drained.
|
||||
pub last: Option<Size>,
|
||||
/// Whether layouting into these areas should produce frames of the exact
|
||||
/// size of `current` instead of shrinking to fit the content.
|
||||
///
|
||||
/// This property is only handled by nodes that have the ability to control
|
||||
/// their own size.
|
||||
pub fixed: Spec<bool>,
|
||||
}
|
||||
|
||||
impl Areas {
|
||||
/// Create a new length-1 sequence of areas with just one `area`.
|
||||
pub fn once(size: Size, full: Size, fixed: Spec<bool>) -> Self {
|
||||
/// Create a new area sequence of length one.
|
||||
pub fn once(size: Size, fixed: Spec<bool>) -> Self {
|
||||
Self {
|
||||
current: size,
|
||||
full,
|
||||
base: size,
|
||||
backlog: vec![],
|
||||
last: None,
|
||||
fixed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new sequence of areas that repeats `area` indefinitely.
|
||||
/// Create a new sequence of same-size areas that repeats indefinitely.
|
||||
pub fn repeat(size: Size, fixed: Spec<bool>) -> Self {
|
||||
Self {
|
||||
current: size,
|
||||
full: size,
|
||||
base: size,
|
||||
backlog: vec![],
|
||||
last: Some(size),
|
||||
fixed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Map all areas.
|
||||
/// Map the size of all areas.
|
||||
pub fn map<F>(&self, mut f: F) -> Self
|
||||
where
|
||||
F: FnMut(Size) -> Size,
|
||||
{
|
||||
Self {
|
||||
current: f(self.current),
|
||||
full: f(self.full),
|
||||
base: f(self.base),
|
||||
backlog: self.backlog.iter().copied().map(|s| f(s)).collect(),
|
||||
last: self.last.map(f),
|
||||
fixed: self.fixed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Advance to the next area if there is any.
|
||||
pub fn next(&mut self) {
|
||||
if let Some(size) = self.backlog.pop().or(self.last) {
|
||||
self.current = size;
|
||||
self.full = size;
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `current` is a fully sized (untouched) copy of the last area.
|
||||
///
|
||||
/// If this is false calling `next()` will have no effect.
|
||||
/// If this is true, calling `next()` will have no effect.
|
||||
pub fn in_full_last(&self) -> bool {
|
||||
self.backlog.is_empty()
|
||||
&& self.last.map_or(true, |size| {
|
||||
@ -212,9 +208,18 @@ impl Areas {
|
||||
})
|
||||
}
|
||||
|
||||
/// Advance to the next area if there is any.
|
||||
pub fn next(&mut self) {
|
||||
if let Some(size) = self.backlog.pop().or(self.last) {
|
||||
self.current = size;
|
||||
self.base = size;
|
||||
}
|
||||
}
|
||||
|
||||
/// Shrink `current` to ensure that the aspect ratio can be satisfied.
|
||||
pub fn apply_aspect_ratio(&mut self, ratio: f64) {
|
||||
let Size { width, height } = self.current;
|
||||
self.current = Size::new(width.min(ratio * height), height.min(width / ratio));
|
||||
pub fn apply_aspect_ratio(&mut self, aspect: f64) {
|
||||
let width = self.current.width.min(aspect * self.current.height);
|
||||
let height = width / aspect;
|
||||
self.current = Size::new(width, height);
|
||||
}
|
||||
}
|
||||
|
@ -11,40 +11,26 @@ pub struct PadNode {
|
||||
|
||||
impl Layout for PadNode {
|
||||
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
|
||||
let areas = shrink(areas, self.padding);
|
||||
let areas = areas.map(|size| size - self.padding.resolve(size).size());
|
||||
|
||||
let mut frames = self.child.layout(ctx, &areas);
|
||||
for frame in &mut frames {
|
||||
pad(frame, self.padding);
|
||||
let padded = solve(self.padding, frame.size);
|
||||
let padding = self.padding.resolve(padded);
|
||||
let origin = Point::new(padding.left, padding.top);
|
||||
|
||||
frame.size = padded;
|
||||
frame.baseline += origin.y;
|
||||
|
||||
for (point, _) in &mut frame.elements {
|
||||
*point += origin;
|
||||
}
|
||||
}
|
||||
|
||||
frames
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PadNode> for AnyNode {
|
||||
fn from(pad: PadNode) -> Self {
|
||||
Self::new(pad)
|
||||
}
|
||||
}
|
||||
|
||||
/// Shrink all areas by the padding.
|
||||
fn shrink(areas: &Areas, padding: Sides<Linear>) -> Areas {
|
||||
areas.map(|size| size - padding.resolve(size).size())
|
||||
}
|
||||
|
||||
/// Pad the frame and move all elements inwards.
|
||||
fn pad(frame: &mut Frame, padding: Sides<Linear>) {
|
||||
let padded = solve(padding, frame.size);
|
||||
let padding = padding.resolve(padded);
|
||||
let origin = Point::new(padding.left, padding.top);
|
||||
|
||||
frame.size = padded;
|
||||
frame.baseline += origin.y;
|
||||
|
||||
for (point, _) in &mut frame.elements {
|
||||
*point += origin;
|
||||
}
|
||||
}
|
||||
|
||||
/// Solve for the size `padded` that satisfies (approximately):
|
||||
/// `padded - padding.resolve(padded).size() == size`
|
||||
fn solve(padding: Sides<Linear>, size: Size) -> Size {
|
||||
@ -57,3 +43,9 @@ fn solve(padding: Sides<Linear>, size: Size) -> Size {
|
||||
solve_axis(size.height, padding.top + padding.bottom),
|
||||
)
|
||||
}
|
||||
|
||||
impl From<PadNode> for AnyNode {
|
||||
fn from(pad: PadNode) -> Self {
|
||||
Self::new(pad)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::mem;
|
||||
|
||||
use unicode_bidi::{BidiInfo, Level};
|
||||
use xi_unicode::LineBreakIterator;
|
||||
@ -142,10 +141,9 @@ impl<'a> ParLayout<'a> {
|
||||
}
|
||||
}
|
||||
ParChild::Any(ref node, align) => {
|
||||
let frames = node.layout(ctx, areas);
|
||||
assert_eq!(frames.len(), 1);
|
||||
|
||||
let frame = frames.into_iter().next().unwrap();
|
||||
let mut frames = node.layout(ctx, areas).into_iter();
|
||||
let frame = frames.next().unwrap();
|
||||
assert!(frames.next().is_none());
|
||||
items.push(ParItem::Frame(frame, align));
|
||||
ranges.push(range);
|
||||
}
|
||||
@ -184,16 +182,16 @@ impl<'a> ParLayout<'a> {
|
||||
}
|
||||
|
||||
// If the line does not fit vertically, we start a new area.
|
||||
if !stack.areas.current.height.fits(line.size.height)
|
||||
while !stack.areas.current.height.fits(line.size.height)
|
||||
&& !stack.areas.in_full_last()
|
||||
{
|
||||
stack.finish_area(ctx);
|
||||
}
|
||||
|
||||
// If the line does not fit horizontally or we have a mandatory
|
||||
// line break (i.e. due to "\n"), we push the line into the
|
||||
// stack.
|
||||
if mandatory || !stack.areas.current.width.fits(line.size.width) {
|
||||
// If the line does not fit horizontally or we have a mandatory
|
||||
// line break (i.e. due to "\n"), we push the line into the
|
||||
// stack.
|
||||
stack.push(line);
|
||||
start = end;
|
||||
last = None;
|
||||
@ -289,14 +287,13 @@ impl<'a> LineStack<'a> {
|
||||
}
|
||||
|
||||
fn push(&mut self, line: LineLayout<'a>) {
|
||||
self.areas.current.height -= line.size.height + self.line_spacing;
|
||||
|
||||
self.size.width = self.size.width.max(line.size.width);
|
||||
self.size.height += line.size.height;
|
||||
self.size.width.set_max(line.size.width);
|
||||
if !self.lines.is_empty() {
|
||||
self.size.height += self.line_spacing;
|
||||
}
|
||||
|
||||
self.areas.current.height -= line.size.height + self.line_spacing;
|
||||
self.lines.push(line);
|
||||
}
|
||||
|
||||
@ -306,22 +303,20 @@ impl<'a> LineStack<'a> {
|
||||
}
|
||||
|
||||
let mut output = Frame::new(self.size, self.size.height);
|
||||
let mut first = true;
|
||||
let mut offset = Length::ZERO;
|
||||
let mut first = true;
|
||||
|
||||
for line in mem::take(&mut self.lines) {
|
||||
for line in std::mem::take(&mut self.lines) {
|
||||
let frame = line.build(ctx, self.size.width);
|
||||
let Frame { size, baseline, .. } = frame;
|
||||
|
||||
let pos = Point::new(Length::ZERO, offset);
|
||||
output.push_frame(pos, frame);
|
||||
|
||||
if first {
|
||||
output.baseline = offset + baseline;
|
||||
output.baseline = pos.y + frame.baseline;
|
||||
first = false;
|
||||
}
|
||||
|
||||
offset += size.height + self.line_spacing;
|
||||
offset += frame.size.height + self.line_spacing;
|
||||
output.push_frame(pos, frame);
|
||||
}
|
||||
|
||||
self.finished.push(output);
|
||||
@ -430,8 +425,8 @@ impl<'a> LineLayout<'a> {
|
||||
let size = item.size();
|
||||
let baseline = item.baseline();
|
||||
width += size.width;
|
||||
top = top.max(baseline);
|
||||
bottom = bottom.max(size.height - baseline);
|
||||
top.set_max(baseline);
|
||||
bottom.set_max(size.height - baseline);
|
||||
}
|
||||
|
||||
Self {
|
||||
@ -448,13 +443,12 @@ impl<'a> LineLayout<'a> {
|
||||
|
||||
/// Build the line's frame.
|
||||
fn build(&self, ctx: &mut LayoutContext, width: Length) -> Frame {
|
||||
let full_width = self.size.width.max(width);
|
||||
let full_size = Size::new(full_width, self.size.height);
|
||||
let free_width = full_width - self.size.width;
|
||||
let size = Size::new(self.size.width.max(width), self.size.height);
|
||||
let free = size.width - self.size.width;
|
||||
|
||||
let mut output = Frame::new(full_size, self.baseline);
|
||||
let mut ruler = Align::Start;
|
||||
let mut output = Frame::new(size, self.baseline);
|
||||
let mut offset = Length::ZERO;
|
||||
let mut ruler = Align::Start;
|
||||
|
||||
self.reordered(|item| {
|
||||
let frame = match *item {
|
||||
@ -472,14 +466,14 @@ impl<'a> LineLayout<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let Frame { size, baseline, .. } = frame;
|
||||
// FIXME: Ruler alignment for RTL.
|
||||
let pos = Point::new(
|
||||
ruler.resolve(self.par.dir, offset .. free_width + offset),
|
||||
self.baseline - baseline,
|
||||
ruler.resolve(self.par.dir, offset .. free + offset),
|
||||
self.baseline - frame.baseline,
|
||||
);
|
||||
|
||||
offset += frame.size.width;
|
||||
output.push_frame(pos, frame);
|
||||
offset += size.width;
|
||||
});
|
||||
|
||||
output
|
||||
|
@ -336,9 +336,8 @@ fn measure(
|
||||
let mut top = Length::ZERO;
|
||||
let mut bottom = Length::ZERO;
|
||||
let mut expand_vertical = |face: &Face| {
|
||||
top = top.max(face.vertical_metric(props.top_edge).to_length(props.size));
|
||||
bottom =
|
||||
bottom.max(-face.vertical_metric(props.bottom_edge).to_length(props.size));
|
||||
top.set_max(face.vertical_metric(props.top_edge).to_length(props.size));
|
||||
bottom.set_max(-face.vertical_metric(props.bottom_edge).to_length(props.size));
|
||||
};
|
||||
|
||||
if glyphs.is_empty() {
|
||||
|
@ -61,6 +61,7 @@ struct StackLayouter {
|
||||
areas: Areas,
|
||||
finished: Vec<Frame>,
|
||||
frames: Vec<(Length, Frame, Gen<Align>)>,
|
||||
full: Size,
|
||||
size: Gen<Length>,
|
||||
ruler: Align,
|
||||
}
|
||||
@ -75,18 +76,19 @@ impl StackLayouter {
|
||||
dirs,
|
||||
aspect,
|
||||
main: dirs.main.axis(),
|
||||
areas,
|
||||
finished: vec![],
|
||||
frames: vec![],
|
||||
full: areas.current,
|
||||
size: Gen::ZERO,
|
||||
ruler: Align::Start,
|
||||
areas,
|
||||
}
|
||||
}
|
||||
|
||||
fn push_spacing(&mut self, amount: Length) {
|
||||
let main_rest = self.areas.current.get_mut(self.main);
|
||||
let capped = amount.min(*main_rest);
|
||||
*main_rest -= capped;
|
||||
let remaining = self.areas.current.get_mut(self.main);
|
||||
let capped = amount.min(*remaining);
|
||||
*remaining -= capped;
|
||||
self.size.main += capped;
|
||||
}
|
||||
|
||||
@ -95,71 +97,64 @@ impl StackLayouter {
|
||||
self.finish_area();
|
||||
}
|
||||
|
||||
while !self.areas.current.fits(frame.size) {
|
||||
if self.areas.in_full_last() {
|
||||
// TODO: Diagnose once the necessary spans exist.
|
||||
break;
|
||||
} else {
|
||||
self.finish_area();
|
||||
}
|
||||
while !self.areas.current.fits(frame.size) && !self.areas.in_full_last() {
|
||||
self.finish_area();
|
||||
}
|
||||
|
||||
let offset = self.size.main;
|
||||
let size = frame.size.switch(self.main);
|
||||
self.frames.push((self.size.main, frame, aligns));
|
||||
self.ruler = aligns.main;
|
||||
self.size.main += size.main;
|
||||
self.size.cross = self.size.cross.max(size.cross);
|
||||
self.size.cross.set_max(size.cross);
|
||||
self.ruler = aligns.main;
|
||||
*self.areas.current.get_mut(self.main) -= size.main;
|
||||
self.frames.push((offset, frame, aligns));
|
||||
}
|
||||
|
||||
fn finish_area(&mut self) {
|
||||
let full_size = {
|
||||
let Areas { current, full, fixed, .. } = self.areas;
|
||||
let fixed = self.areas.fixed;
|
||||
|
||||
let used = self.size.switch(self.main).to_size();
|
||||
let mut size = Size::new(
|
||||
if fixed.horizontal { full.width } else { used.width },
|
||||
if fixed.vertical { full.height } else { used.height },
|
||||
);
|
||||
let used = self.size.switch(self.main).to_size();
|
||||
let mut size = Size::new(
|
||||
if fixed.horizontal { self.full.width } else { used.width },
|
||||
if fixed.vertical { self.full.height } else { used.height },
|
||||
);
|
||||
|
||||
if let Some(aspect) = self.aspect {
|
||||
let width = size
|
||||
.width
|
||||
.max(aspect * size.height)
|
||||
.min(current.width)
|
||||
.min((current.height + used.height) / aspect);
|
||||
if let Some(aspect) = self.aspect {
|
||||
let width = size
|
||||
.width
|
||||
.max(aspect * size.height)
|
||||
.min(self.full.width)
|
||||
.min(aspect * self.full.height);
|
||||
|
||||
size = Size::new(width, width / aspect);
|
||||
}
|
||||
size = Size::new(width, width / aspect);
|
||||
}
|
||||
|
||||
size
|
||||
};
|
||||
|
||||
let mut output = Frame::new(full_size, full_size.height);
|
||||
let mut output = Frame::new(size, size.height);
|
||||
let mut first = true;
|
||||
|
||||
let full_size = full_size.switch(self.main);
|
||||
for (before, frame, aligns) in std::mem::take(&mut self.frames) {
|
||||
let child_size = frame.size.switch(self.main);
|
||||
let used = self.size;
|
||||
let size = size.switch(self.main);
|
||||
|
||||
for (offset, frame, aligns) in std::mem::take(&mut self.frames) {
|
||||
let child = frame.size.switch(self.main);
|
||||
|
||||
// Align along the cross axis.
|
||||
let cross = aligns
|
||||
.cross
|
||||
.resolve(self.dirs.cross, Length::ZERO .. size.cross - child.cross);
|
||||
|
||||
// Align along the main axis.
|
||||
let main = aligns.main.resolve(
|
||||
self.dirs.main,
|
||||
if self.dirs.main.is_positive() {
|
||||
before .. before + full_size.main - self.size.main
|
||||
offset .. size.main - used.main + offset
|
||||
} else {
|
||||
self.size.main - (before + child_size.main)
|
||||
.. full_size.main - (before + child_size.main)
|
||||
let offset_with_self = offset + child.main;
|
||||
used.main - offset_with_self .. size.main - offset_with_self
|
||||
},
|
||||
);
|
||||
|
||||
// Align along the cross axis.
|
||||
let cross = aligns.cross.resolve(
|
||||
self.dirs.cross,
|
||||
Length::ZERO .. full_size.cross - child_size.cross,
|
||||
);
|
||||
|
||||
let pos = Gen::new(main, cross).switch(self.main).to_point();
|
||||
let pos = Gen::new(cross, main).switch(self.main).to_point();
|
||||
if first {
|
||||
output.baseline = pos.y + frame.baseline;
|
||||
first = false;
|
||||
@ -168,14 +163,14 @@ impl StackLayouter {
|
||||
output.push_frame(pos, frame);
|
||||
}
|
||||
|
||||
self.finished.push(output);
|
||||
self.areas.next();
|
||||
self.ruler = Align::Start;
|
||||
self.size = Gen::ZERO;
|
||||
|
||||
self.ruler = Align::Start;
|
||||
self.areas.next();
|
||||
if let Some(aspect) = self.aspect {
|
||||
self.areas.apply_aspect_ratio(aspect);
|
||||
}
|
||||
|
||||
self.finished.push(output);
|
||||
}
|
||||
|
||||
fn finish(mut self) -> Vec<Frame> {
|
||||
|
@ -46,15 +46,14 @@ struct ImageNode {
|
||||
|
||||
impl Layout for ImageNode {
|
||||
fn layout(&self, _: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
|
||||
let Areas { current, full, .. } = areas;
|
||||
let Areas { current, base, .. } = areas;
|
||||
let width = self.width.map(|w| w.resolve(base.width));
|
||||
let height = self.height.map(|w| w.resolve(base.height));
|
||||
|
||||
let pixel_width = self.dimensions.0 as f64;
|
||||
let pixel_height = self.dimensions.1 as f64;
|
||||
let pixel_ratio = pixel_width / pixel_height;
|
||||
|
||||
let width = self.width.map(|w| w.resolve(full.width));
|
||||
let height = self.height.map(|w| w.resolve(full.height));
|
||||
|
||||
let size = match (width, height) {
|
||||
(Some(width), Some(height)) => Size::new(width, height),
|
||||
(Some(width), None) => Size::new(width, width / pixel_ratio),
|
||||
@ -75,7 +74,6 @@ impl Layout for ImageNode {
|
||||
|
||||
let mut frame = Frame::new(size, size.height);
|
||||
frame.push(Point::ZERO, Element::Image(self.id, size));
|
||||
|
||||
vec![frame]
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||
}
|
||||
|
||||
if let Some(margins) = margins {
|
||||
ctx.state.page.margins = Sides::uniform(Some(margins));
|
||||
ctx.state.page.margins = Sides::splat(Some(margins));
|
||||
}
|
||||
|
||||
if let Some(left) = left {
|
||||
|
@ -142,7 +142,7 @@ fn ellipse_impl(
|
||||
width,
|
||||
height,
|
||||
child: PadNode {
|
||||
padding: Sides::uniform(Relative::new(PAD).into()),
|
||||
padding: Sides::splat(Relative::new(PAD).into()),
|
||||
child: stack.into(),
|
||||
}
|
||||
.into(),
|
||||
|
@ -177,7 +177,7 @@ impl<'a> PdfExporter<'a> {
|
||||
}
|
||||
|
||||
Shape::Ellipse(size) => {
|
||||
let path = geom::ellipse_path(size);
|
||||
let path = geom::Path::ellipse(size);
|
||||
write_path(&mut content, x, y, &path, false, true);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ Auto-sized square. \
|
||||
#align(center)
|
||||
#pad(5pt)[
|
||||
#font(color: #fff, weight: bold)
|
||||
Typst \
|
||||
Typst
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -220,7 +220,7 @@ fn test_part(
|
||||
// large and fit them to match their content.
|
||||
let mut state = State::default();
|
||||
state.page.size = Size::new(Length::pt(120.0), Length::raw(f64::INFINITY));
|
||||
state.page.margins = Sides::uniform(Some(Length::pt(10.0).into()));
|
||||
state.page.margins = Sides::splat(Some(Length::pt(10.0).into()));
|
||||
|
||||
let Pass { output: mut frames, diags } = typeset(env, &src, &scope, state);
|
||||
if !compare_ref {
|
||||
@ -368,7 +368,7 @@ fn draw(env: &Env, frames: &[Frame], dpi: f32) -> Pixmap {
|
||||
let ts = Transform::from_scale(dpi, dpi);
|
||||
canvas.fill(Color::BLACK);
|
||||
|
||||
let mut origin = Point::new(pad, pad);
|
||||
let mut origin = Point::splat(pad);
|
||||
for frame in frames {
|
||||
let mut paint = Paint::default();
|
||||
paint.set_color(Color::WHITE);
|
||||
@ -469,7 +469,7 @@ fn draw_geometry(canvas: &mut Pixmap, ts: Transform, shape: &Shape, fill: Fill)
|
||||
canvas.fill_rect(rect, &paint, ts, None);
|
||||
}
|
||||
Shape::Ellipse(size) => {
|
||||
let path = convert_typst_path(&geom::ellipse_path(size));
|
||||
let path = convert_typst_path(&geom::Path::ellipse(size));
|
||||
canvas.fill_path(&path, &paint, rule, ts, None);
|
||||
}
|
||||
Shape::Path(ref path) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user