summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml20
-rw-r--r--benches/benchmarks.rs15
-rw-r--r--src/export/pdf.rs6
-rw-r--r--src/geom.rs333
-rw-r--r--src/layout/elements.rs15
-rw-r--r--src/layout/line.rs30
-rw-r--r--src/layout/mod.rs16
-rw-r--r--src/layout/shaping.rs11
-rw-r--r--src/layout/stack.rs55
-rw-r--r--src/layout/tree.rs17
-rw-r--r--src/lib.rs14
-rw-r--r--src/library/boxed.rs20
-rw-r--r--src/library/page.rs19
-rw-r--r--src/paper.rs27
-rw-r--r--src/style.rs26
-rw-r--r--tests/coma.typ5
-rw-r--r--tests/test_typeset.rs29
17 files changed, 250 insertions, 408 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 15eea562..d1f7f946 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,27 +4,31 @@ version = "0.1.0"
authors = ["The Typst Project Developers"]
edition = "2018"
-[lib]
-bench = false
-
[workspace]
members = ["main"]
+[features]
+default = ["serialize", "fs"]
+serialize = ["serde"]
+fs = ["fontdock/fs"]
+
+[lib]
+bench = false
+
[profile.dev.package."*"]
opt-level = 2
+[profile.release]
+lto = true
+
[dependencies]
fontdock = { path = "../fontdock", default-features = false }
+kurbo = "0.6.3"
tide = { path = "../tide" }
ttf-parser = "0.8.2"
unicode-xid = "0.2"
serde = { version = "1", features = ["derive"], optional = true }
-[features]
-default = ["serialize", "fs"]
-serialize = ["serde"]
-fs = ["fontdock/fs"]
-
[dev-dependencies]
criterion = "0.3"
futures-executor = "0.3"
diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs
index 52200e66..a6364055 100644
--- a/benches/benchmarks.rs
+++ b/benches/benchmarks.rs
@@ -9,15 +9,10 @@ use typstc::parse::parse;
use typstc::Typesetter;
const FONT_DIR: &str = "fonts";
-
-// 28 not too dense lines.
const COMA: &str = include_str!("../tests/coma.typ");
fn parsing_benchmark(c: &mut Criterion) {
- c.bench_function("parse-coma-28-lines", |b| b.iter(|| parse(COMA)));
-
- let long = COMA.repeat(100);
- c.bench_function("parse-coma-2800-lines", |b| b.iter(|| parse(&long)));
+ c.bench_function("parse-coma", |b| b.iter(|| parse(COMA)));
}
fn typesetting_benchmark(c: &mut Criterion) {
@@ -29,15 +24,9 @@ fn typesetting_benchmark(c: &mut Criterion) {
let loader = FontLoader::new(Box::new(provider), descriptors);
let loader = Rc::new(RefCell::new(loader));
let typesetter = Typesetter::new(loader.clone());
-
- c.bench_function("typeset-coma-28-lines", |b| {
+ c.bench_function("typeset-coma", |b| {
b.iter(|| futures_executor::block_on(typesetter.typeset(COMA)))
});
-
- let long = COMA.repeat(100);
- c.bench_function("typeset-coma-2800-lines", |b| {
- b.iter(|| futures_executor::block_on(typesetter.typeset(&long)))
- });
}
criterion_group!(benches, parsing_benchmark, typesetting_benchmark);
diff --git a/src/export/pdf.rs b/src/export/pdf.rs
index b87b3181..1bed2188 100644
--- a/src/export/pdf.rs
+++ b/src/export/pdf.rs
@@ -111,8 +111,8 @@ impl<'a, W: Write> PdfExporter<'a, W> {
let rect = Rect::new(
0.0,
0.0,
- Length::raw(page.size.x).as_pt() as f32,
- Length::raw(page.size.y).as_pt() as f32,
+ Length::raw(page.size.width).as_pt() as f32,
+ Length::raw(page.size.height).as_pt() as f32,
);
self.writer.write_obj(
@@ -152,7 +152,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
}
let x = Length::raw(pos.x).as_pt();
- let y = Length::raw(page.size.y - pos.y - size).as_pt();
+ let y = Length::raw(page.size.height - pos.y - size).as_pt();
text.tm(1.0, 0.0, 0.0, 1.0, x as f32, y as f32);
text.tj(shaped.encode_glyphs_be());
}
diff --git a/src/geom.rs b/src/geom.rs
index f897c08f..6aa875f1 100644
--- a/src/geom.rs
+++ b/src/geom.rs
@@ -1,209 +1,161 @@
//! Geometrical types.
-use std::fmt::{self, Debug, Formatter};
-use std::ops::*;
+#[doc(no_inline)]
+pub use kurbo::*;
-use crate::layout::primitive::*;
+use crate::layout::primitive::{Dir, GenAlign, LayoutAlign, LayoutSystem, SpecAxis};
-/// A value in two dimensions.
-#[derive(Default, Copy, Clone, Eq, PartialEq)]
-pub struct Value2<T> {
- /// The horizontal component.
- pub x: T,
- /// The vertical component.
- pub y: T,
-}
+/// Additional methods for [sizes].
+///
+/// [sizes]: ../../kurbo/struct.Size.html
+pub trait SizeExt {
+ /// Return the primary component of this specialized size.
+ fn primary(self, sys: LayoutSystem) -> f64;
-impl<T: Clone> Value2<T> {
- /// Create a new 2D-value from two values.
- pub fn new(x: T, y: T) -> Self {
- Self { x, y }
- }
+ /// Borrow the primary component of this specialized size mutably.
+ fn primary_mut(&mut self, sys: LayoutSystem) -> &mut f64;
- /// Create a new 2D-value with `x` set to a value and `y` to default.
- pub fn with_x(x: T) -> Self
- where
- T: Default,
- {
- Self { x, y: T::default() }
- }
+ /// Return the secondary component of this specialized size.
+ fn secondary(self, sys: LayoutSystem) -> f64;
- /// Create a new 2D-value with `y` set to a value and `x` to default.
- pub fn with_y(y: T) -> Self
- where
- T: Default,
- {
- Self { x: T::default(), y }
- }
+ /// Borrow the secondary component of this specialized size mutably.
+ fn secondary_mut(&mut self, sys: LayoutSystem) -> &mut f64;
- /// Create a 2D-value with `x` and `y` set to the same value `s`.
- pub fn with_all(s: T) -> Self {
- Self { x: s.clone(), y: s }
- }
+ /// Returns the generalized version of a `Size` based on the layouting
+ /// system, that is:
+ /// - `x` describes the primary axis instead of the horizontal one.
+ /// - `y` describes the secondary axis instead of the vertical one.
+ fn generalized(self, sys: LayoutSystem) -> Self;
- /// Get the specificed component.
- pub fn get(self, axis: SpecAxis) -> T {
- match axis {
- SpecAxis::Horizontal => self.x,
- SpecAxis::Vertical => self.y,
- }
- }
+ /// Returns the specialized version of this generalized Size2D (inverse to
+ /// `generalized`).
+ fn specialized(self, sys: LayoutSystem) -> Self;
- /// Borrow the specificed component mutably.
- pub fn get_mut(&mut self, axis: SpecAxis) -> &mut T {
- match axis {
- SpecAxis::Horizontal => &mut self.x,
- SpecAxis::Vertical => &mut self.y,
- }
- }
+ /// Whether the given size fits into this one, that is, both coordinate
+ /// values are smaller or equal.
+ fn fits(self, other: Self) -> bool;
+
+ /// The anchor position along the given axis for an item with the given
+ /// alignment in a container with this size.
+ ///
+ /// This assumes the size to be generalized such that `x` corresponds to the
+ /// primary axis.
+ fn anchor(self, align: LayoutAlign, sys: LayoutSystem) -> Point;
+}
- /// Return the primary value of this specialized 2D-value.
- pub fn primary(self, sys: LayoutSystem) -> T {
+impl SizeExt for Size {
+ fn primary(self, sys: LayoutSystem) -> f64 {
if sys.primary.axis() == SpecAxis::Horizontal {
- self.x
+ self.width
} else {
- self.y
+ self.height
}
}
- /// Borrow the primary value of this specialized 2D-value mutably.
- pub fn primary_mut(&mut self, sys: LayoutSystem) -> &mut T {
+ fn primary_mut(&mut self, sys: LayoutSystem) -> &mut f64 {
if sys.primary.axis() == SpecAxis::Horizontal {
- &mut self.x
+ &mut self.width
} else {
- &mut self.y
+ &mut self.height
}
}
- /// Return the secondary value of this specialized 2D-value.
- pub fn secondary(self, sys: LayoutSystem) -> T {
+ fn secondary(self, sys: LayoutSystem) -> f64 {
if sys.primary.axis() == SpecAxis::Horizontal {
- self.y
+ self.height
} else {
- self.x
+ self.width
}
}
- /// Borrow the secondary value of this specialized 2D-value mutably.
- pub fn secondary_mut(&mut self, sys: LayoutSystem) -> &mut T {
+ fn secondary_mut(&mut self, sys: LayoutSystem) -> &mut f64 {
if sys.primary.axis() == SpecAxis::Horizontal {
- &mut self.y
+ &mut self.height
} else {
- &mut self.x
+ &mut self.width
}
}
- /// Returns the generalized version of a `Size2D` dependent on the layouting
- /// system, that is:
- /// - `x` describes the primary axis instead of the horizontal one.
- /// - `y` describes the secondary axis instead of the vertical one.
- pub fn generalized(self, sys: LayoutSystem) -> Self {
+ fn generalized(self, sys: LayoutSystem) -> Self {
match sys.primary.axis() {
SpecAxis::Horizontal => self,
- SpecAxis::Vertical => Self { x: self.y, y: self.x },
+ SpecAxis::Vertical => Self::new(self.height, self.width),
}
}
- /// Returns the specialized version of this generalized Size2D (inverse to
- /// `generalized`).
- pub fn specialized(self, sys: LayoutSystem) -> Self {
+ fn specialized(self, sys: LayoutSystem) -> Self {
// In fact, generalized is its own inverse. For reasons of clarity
// at the call site, we still have this second function.
self.generalized(sys)
}
- /// Swap the `x` and `y` values.
- pub fn swap(&mut self) {
- std::mem::swap(&mut self.x, &mut self.y);
+ fn fits(self, other: Self) -> bool {
+ self.width >= other.width && self.height >= other.height
}
-}
-impl<T: Debug> Debug for Value2<T> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.debug_list().entry(&self.x).entry(&self.y).finish()
- }
-}
-
-/// A position or extent in 2-dimensional space.
-pub type Size = Value2<f64>;
-
-impl Size {
- /// The zeroed size.
- pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
-
- /// Whether the given size fits into this one, that is, both coordinate
- /// values are smaller or equal.
- pub fn fits(self, other: Self) -> bool {
- self.x >= other.x && self.y >= other.y
- }
-
- /// Return a size padded by the paddings of the given box.
- pub fn padded(self, padding: Margins) -> Self {
- Size {
- x: self.x + padding.left + padding.right,
- y: self.y + padding.top + padding.bottom,
+ fn anchor(self, align: LayoutAlign, sys: LayoutSystem) -> Point {
+ fn length_anchor(length: f64, align: GenAlign, dir: Dir) -> f64 {
+ match (dir.is_positive(), align) {
+ (true, GenAlign::Start) | (false, GenAlign::End) => 0.0,
+ (_, GenAlign::Center) => length / 2.0,
+ (true, GenAlign::End) | (false, GenAlign::Start) => length,
+ }
}
- }
- /// Return a size reduced by the paddings of the given box.
- pub fn unpadded(self, padding: Margins) -> Self {
- Size {
- x: self.x - padding.left - padding.right,
- y: self.y - padding.top - padding.bottom,
- }
- }
-
- /// The anchor position along the given axis for an item with the given
- /// alignment in a container with this size.
- ///
- /// This assumes the size to be generalized such that `x` corresponds to the
- /// primary axis.
- pub fn anchor(self, align: LayoutAlign, sys: LayoutSystem) -> Self {
- Size {
- x: anchor(self.x, align.primary, sys.primary),
- y: anchor(self.y, align.secondary, sys.secondary),
- }
+ Point::new(
+ length_anchor(self.width, align.primary, sys.primary),
+ length_anchor(self.height, align.secondary, sys.secondary),
+ )
}
}
-fn anchor(length: f64, align: GenAlign, dir: Dir) -> f64 {
- match (dir.is_positive(), align) {
- (true, GenAlign::Start) | (false, GenAlign::End) => 0.0,
- (_, GenAlign::Center) => length / 2.0,
- (true, GenAlign::End) | (false, GenAlign::Start) => length,
- }
+/// Additional methods for [rectangles].
+///
+/// [rectangles]: ../../kurbo/struct.Rect.html
+pub trait RectExt {
+ /// Get a mutable reference to the value for the specified direction at the
+ /// alignment.
+ ///
+ /// Center alignment is treated the same as origin alignment.
+ fn get_mut(&mut self, dir: Dir, align: GenAlign) -> &mut f64;
}
-impl Neg for Size {
- type Output = Size;
-
- fn neg(self) -> Size {
- Size { x: -self.x, y: -self.y }
+impl RectExt for Rect {
+ fn get_mut(&mut self, dir: Dir, align: GenAlign) -> &mut f64 {
+ match if align == GenAlign::End { dir.inv() } else { dir } {
+ Dir::LTR => &mut self.x0,
+ Dir::TTB => &mut self.y0,
+ Dir::RTL => &mut self.x1,
+ Dir::BTT => &mut self.y1,
+ }
}
}
-/// A value in four dimensions.
+/// A generic container for `[left, top, right, bottom]` values.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
-pub struct Value4<T> {
- /// The left extent.
+pub struct Sides<T> {
+ /// The value for the left side.
pub left: T,
- /// The top extent.
+ /// The value for the top side.
pub top: T,
- /// The right extent.
+ /// The value for the right side.
pub right: T,
- /// The bottom extent.
+ /// The value for the bottom side.
pub bottom: T,
}
-impl<T: Clone> Value4<T> {
+impl<T> Sides<T> {
/// Create a new box from four sizes.
pub fn new(left: T, top: T, right: T, bottom: T) -> Self {
- Value4 { left, top, right, bottom }
+ Self { left, top, right, bottom }
}
- /// Create a box with all four fields set to the same value `s`.
- pub fn with_all(value: T) -> Self {
- Value4 {
+ /// Create an instance with all four components set to the same `value`.
+ pub fn uniform(value: T) -> Self
+ where
+ T: Clone,
+ {
+ Self {
left: value.clone(),
top: value.clone(),
right: value.clone(),
@@ -215,105 +167,12 @@ impl<T: Clone> Value4<T> {
/// alignment.
///
/// Center alignment is treated the same as origin alignment.
- pub fn get_mut(&mut self, mut dir: Dir, align: GenAlign) -> &mut T {
- if align == GenAlign::End {
- dir = dir.inv();
- }
-
- match dir {
+ pub fn get_mut(&mut self, dir: Dir, align: GenAlign) -> &mut T {
+ match if align == GenAlign::End { dir.inv() } else { dir } {
Dir::LTR => &mut self.left,
Dir::RTL => &mut self.right,
Dir::TTB => &mut self.top,
Dir::BTT => &mut self.bottom,
}
}
-
- /// Set all values to the given value.
- pub fn set_all(&mut self, value: T) {
- *self = Value4::with_all(value);
- }
-}
-
-/// A length in four dimensions.
-pub type Margins = Value4<f64>;
-
-impl Margins {
- /// The zero margins.
- pub const ZERO: Margins = Margins {
- left: 0.0,
- top: 0.0,
- right: 0.0,
- bottom: 0.0,
- };
}
-
-macro_rules! implement_traits {
- ($ty:ident, $t:ident, $o:ident
- reflexive {$(
- ($tr:ident($tf:ident), $at:ident($af:ident), [$($f:ident),*])
- )*}
- numbers { $(($w:ident: $($rest:tt)*))* }
- ) => {
- $(impl $tr for $ty {
- type Output = $ty;
- fn $tf($t, $o: $ty) -> $ty {
- $ty { $($f: $tr::$tf($t.$f, $o.$f),)* }
- }
- }
-
- impl $at for $ty {
- fn $af(&mut $t, $o: $ty) { $($at::$af(&mut $t.$f, $o.$f);)* }
- })*
-
- $(implement_traits!(@$w f64, $ty $t $o $($rest)*);)*
- };
-
- (@front $num:ty, $ty:ident $t:ident $o:ident
- $tr:ident($tf:ident),
- [$($f:ident),*]
- ) => {
- impl $tr<$ty> for $num {
- type Output = $ty;
- fn $tf($t, $o: $ty) -> $ty {
- $ty { $($f: $tr::$tf($t as f64, $o.$f),)* }
- }
- }
- };
-
- (@back $num:ty, $ty:ident $t:ident $o:ident
- $tr:ident($tf:ident), $at:ident($af:ident),
- [$($f:ident),*]
- ) => {
- impl $tr<$num> for $ty {
- type Output = $ty;
- fn $tf($t, $o: $num) -> $ty {
- $ty { $($f: $tr::$tf($t.$f, $o as f64),)* }
- }
- }
-
- impl $at<$num> for $ty {
- fn $af(&mut $t, $o: $num) { $($at::$af(&mut $t.$f, $o as f64);)* }
- }
- };
-}
-
-macro_rules! implement_size {
- ($ty:ident($t:ident, $o:ident) [$($f:ident),*]) => {
- implement_traits! {
- $ty, $t, $o
-
- reflexive {
- (Add(add), AddAssign(add_assign), [$($f),*])
- (Sub(sub), SubAssign(sub_assign), [$($f),*])
- }
-
- numbers {
- (front: Mul(mul), [$($f),*])
- (back: Mul(mul), MulAssign(mul_assign), [$($f),*])
- (back: Div(div), DivAssign(div_assign), [$($f),*])
- }
- }
- };
-}
-
-implement_size! { Size(self, other) [x, y] }
diff --git a/src/layout/elements.rs b/src/layout/elements.rs
index dd148a24..b5f83bfe 100644
--- a/src/layout/elements.rs
+++ b/src/layout/elements.rs
@@ -5,11 +5,11 @@ use std::fmt::{self, Debug, Formatter};
use fontdock::FaceId;
use ttf_parser::GlyphId;
-use crate::geom::Size;
+use crate::geom::Point;
/// A collection of absolutely positioned layout elements.
#[derive(Debug, Default, Clone, PartialEq)]
-pub struct LayoutElements(pub Vec<(Size, LayoutElement)>);
+pub struct LayoutElements(pub Vec<(Point, LayoutElement)>);
impl LayoutElements {
/// Create an new empty collection.
@@ -18,16 +18,15 @@ impl LayoutElements {
}
/// Add an element at a position.
- pub fn push(&mut self, pos: Size, element: LayoutElement) {
+ pub fn push(&mut self, pos: Point, element: LayoutElement) {
self.0.push((pos, element));
}
- /// Add all elements of another collection, offsetting each by the given
- /// `offset`. This can be used to place a sublayout at a position in another
- /// layout.
- pub fn extend_offset(&mut self, offset: Size, more: Self) {
+ /// Add all elements of another collection, placing them relative to the
+ /// given position.
+ pub fn push_elements(&mut self, pos: Point, more: Self) {
for (subpos, element) in more.0 {
- self.0.push((subpos + offset, element));
+ self.0.push((pos + subpos.to_vec2(), element));
}
}
}
diff --git a/src/layout/line.rs b/src/layout/line.rs
index d8e97df3..86531f30 100644
--- a/src/layout/line.rs
+++ b/src/layout/line.rs
@@ -95,14 +95,14 @@ impl LineLayouter {
let usable = self.stack.usable().primary(sys);
rest_run.usable = Some(match layout.align.primary {
GenAlign::Start => unreachable!("start > x"),
- GenAlign::Center => usable - 2.0 * self.run.size.x,
- GenAlign::End => usable - self.run.size.x,
+ GenAlign::Center => usable - 2.0 * self.run.size.width,
+ GenAlign::End => usable - self.run.size.width,
});
- rest_run.size.y = self.run.size.y;
+ rest_run.size.height = self.run.size.height;
self.finish_line();
- self.stack.add_spacing(-rest_run.size.y, SpacingKind::Hard);
+ self.stack.add_spacing(-rest_run.size.height, SpacingKind::Hard);
self.run = rest_run;
}
@@ -126,10 +126,10 @@ impl LineLayouter {
}
self.run.align = Some(layout.align);
- self.run.layouts.push((self.run.size.x, layout));
+ self.run.layouts.push((self.run.size.width, layout));
- self.run.size.x += size.x;
- self.run.size.y = self.run.size.y.max(size.y);
+ self.run.size.width += size.width;
+ self.run.size.height = self.run.size.height.max(size.height);
self.run.last_spacing = LastSpacing::None;
}
@@ -152,10 +152,10 @@ impl LineLayouter {
// If there was another run already, override the stack's size.
if let Some(primary) = self.run.usable {
- usable.x = primary;
+ usable.width = primary;
}
- usable.x -= self.run.size.x;
+ usable.width -= self.run.size.width;
usable
}
@@ -163,8 +163,8 @@ impl LineLayouter {
pub fn add_primary_spacing(&mut self, mut spacing: f64, kind: SpacingKind) {
match kind {
SpacingKind::Hard => {
- spacing = spacing.min(self.usable().x);
- self.run.size.x += spacing;
+ spacing = spacing.min(self.usable().width);
+ self.run.size.width += spacing;
self.run.last_spacing = LastSpacing::Hard;
}
@@ -215,7 +215,7 @@ impl LineLayouter {
/// it will fit into this layouter's underlying stack.
pub fn remaining(&self) -> LayoutSpaces {
let mut spaces = self.stack.remaining();
- *spaces[0].size.secondary_mut(self.ctx.sys) -= self.run.size.y;
+ *spaces[0].size.secondary_mut(self.ctx.sys) -= self.run.size.height;
spaces
}
@@ -246,11 +246,11 @@ impl LineLayouter {
for (offset, layout) in layouts {
let x = match self.ctx.sys.primary.is_positive() {
true => offset,
- false => self.run.size.x - offset - layout.size.primary(self.ctx.sys),
+ false => self.run.size.width - offset - layout.size.primary(self.ctx.sys),
};
- let pos = Size::with_x(x);
- elements.extend_offset(pos, layout.elements);
+ let pos = Point::new(x, 0.0);
+ elements.push_elements(pos, layout.elements);
}
self.stack.add(BoxLayout {
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index fcaab372..11c03b0b 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -10,9 +10,10 @@ mod tree;
pub use primitive::*;
pub use tree::layout_tree as layout;
+use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt};
+
use crate::eval::Scope;
use crate::font::SharedFontLoader;
-use crate::geom::{Margins, Size};
use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::SynTree;
@@ -67,22 +68,21 @@ pub struct LayoutSpace {
/// The maximum size of the rectangle to layout into.
pub size: Size,
/// Padding that should be respected on each side.
- pub padding: Margins,
+ pub insets: Insets,
/// Whether to expand the size of the resulting layout to the full size of
/// this space or to shrink it to fit the content.
pub expansion: LayoutExpansion,
}
impl LayoutSpace {
- /// The offset from the origin to the start of content, i.e.
- /// `(padding.left, padding.top)`.
- pub fn start(&self) -> Size {
- Size::new(self.padding.left, self.padding.top)
+ /// The position of the padded start in the space.
+ pub fn start(&self) -> Point {
+ Point::new(-self.insets.x0, -self.insets.y0)
}
/// The actually usable area (size minus padding).
pub fn usable(&self) -> Size {
- self.size.unpadded(self.padding)
+ self.size + self.insets.size()
}
/// The inner layout space with size reduced by the padding, zero padding of
@@ -90,7 +90,7 @@ impl LayoutSpace {
pub fn inner(&self) -> Self {
Self {
size: self.usable(),
- padding: Margins::ZERO,
+ insets: Insets::ZERO,
expansion: LayoutExpansion::new(false, false),
}
}
diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs
index 0f42d4bb..4de30a9e 100644
--- a/src/layout/shaping.rs
+++ b/src/layout/shaping.rs
@@ -11,7 +11,6 @@ use super::elements::{LayoutElement, Shaped};
use super::BoxLayout as Layout;
use super::*;
use crate::font::FontLoader;
-use crate::geom::Size;
use crate::style::TextStyle;
/// Shape text into a box.
@@ -74,7 +73,7 @@ impl<'a> Shaper<'a> {
// Flush the last buffered parts of the word.
if !self.shaped.text.is_empty() {
- let pos = Size::new(self.offset, 0.0);
+ let pos = Point::new(self.offset, 0.0);
self.layout.elements.push(pos, LayoutElement::Text(self.shaped));
}
@@ -97,9 +96,9 @@ impl<'a> Shaper<'a> {
Shaped::new(FaceId::MAX, self.opts.style.font_size()),
);
- let pos = Size::new(self.offset, 0.0);
+ let pos = Point::new(self.offset, 0.0);
self.layout.elements.push(pos, LayoutElement::Text(shaped));
- self.offset = self.layout.size.x;
+ self.offset = self.layout.size.width;
}
self.shaped.face = index;
@@ -107,9 +106,9 @@ impl<'a> Shaper<'a> {
self.shaped.text.push(c);
self.shaped.glyphs.push(glyph);
- self.shaped.offsets.push(self.layout.size.x - self.offset);
+ self.shaped.offsets.push(self.layout.size.width - self.offset);
- self.layout.size.x += char_width;
+ self.layout.size.width += char_width;
}
async fn select_font(&mut self, c: char) -> Option<(FaceId, GlyphId, f64)> {
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 3e63f5e5..a68fbac0 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -20,7 +20,6 @@
//! sentence in the second box.
use super::*;
-use crate::geom::Value4;
/// Performs the stack layouting.
pub struct StackLayouter {
@@ -64,7 +63,7 @@ struct Space {
/// Dictate which alignments for new boxes are still allowed and which
/// require a new space to be started. For example, after an `End`-aligned
/// item, no `Start`-aligned one can follow.
- rulers: Value4<GenAlign>,
+ rulers: Sides<GenAlign>,
/// The spacing state. This influences how new spacing is handled, e.g. hard
/// spacing may override soft spacing.
last_spacing: LastSpacing,
@@ -127,7 +126,7 @@ impl StackLayouter {
SpacingKind::Hard => {
// Reduce the spacing such that it definitely fits.
spacing = spacing.min(self.space.usable.secondary(self.ctx.sys));
- let size = Size::with_y(spacing);
+ let size = Size::new(0.0, spacing);
self.update_metrics(size);
self.space.layouts.push((self.ctx.sys, BoxLayout {
@@ -161,15 +160,15 @@ impl StackLayouter {
let mut size = self.space.size.generalized(sys);
let mut extra = self.space.extra.generalized(sys);
- size.x += (added.x - extra.x).max(0.0);
- size.y += (added.y - extra.y).max(0.0);
+ size.width += (added.width - extra.width).max(0.0);
+ size.height += (added.height - extra.height).max(0.0);
- extra.x = extra.x.max(added.x);
- extra.y = (extra.y - added.y).max(0.0);
+ extra.width = extra.width.max(added.width);
+ extra.height = (extra.height - added.height).max(0.0);
self.space.size = size.specialized(sys);
self.space.extra = extra.specialized(sys);
- *self.space.usable.secondary_mut(sys) -= added.y;
+ *self.space.usable.secondary_mut(sys) -= added.height;
}
/// Returns true if a space break is necessary.
@@ -239,7 +238,7 @@ impl StackLayouter {
let mut spaces = vec![LayoutSpace {
size,
- padding: Margins::ZERO,
+ insets: Insets::ZERO,
expansion: LayoutExpansion::new(false, false),
}];
@@ -253,7 +252,7 @@ impl StackLayouter {
/// The remaining usable size.
pub fn usable(&self) -> Size {
self.space.usable
- - Size::with_y(self.space.last_spacing.soft_or_zero())
+ - Size::new(0.0, self.space.last_spacing.soft_or_zero())
.specialized(self.ctx.sys)
}
@@ -286,13 +285,13 @@ impl StackLayouter {
let usable = space.usable();
if space.expansion.horizontal {
- self.space.size.x = usable.x;
+ self.space.size.width = usable.width;
}
if space.expansion.vertical {
- self.space.size.y = usable.y;
+ self.space.size.height = usable.height;
}
- let size = self.space.size.padded(space.padding);
+ let size = self.space.size - space.insets.size();
// ------------------------------------------------------------------ //
// Step 2: Forward pass. Create a bounding box for each layout in which
@@ -302,11 +301,11 @@ impl StackLayouter {
let start = space.start();
let mut bounds = vec![];
- let mut bound = Margins {
- left: start.x,
- top: start.y,
- right: start.x + self.space.size.x,
- bottom: start.y + self.space.size.y,
+ let mut bound = Rect {
+ x0: start.x,
+ y0: start.y,
+ x1: start.x + self.space.size.width,
+ y1: start.y + self.space.size.height,
};
for (sys, layout) in &self.space.layouts {
@@ -340,8 +339,8 @@ impl StackLayouter {
// is thus stored in `extent.y`. The primary extent is reset for
// this new axis-aligned run.
if rotation != sys.secondary.axis() {
- extent.y = extent.x;
- extent.x = 0.0;
+ extent.height = extent.width;
+ extent.width = 0.0;
rotation = sys.secondary.axis();
}
@@ -349,12 +348,12 @@ impl StackLayouter {
// accumulated secondary extent of all layouts we have seen so far,
// which are the layouts after this one since we iterate reversed.
*bound.get_mut(sys.secondary, GenAlign::End) -=
- sys.secondary.factor() * extent.y;
+ sys.secondary.factor() * extent.height;
// Then, we add this layout's secondary extent to the accumulator.
let size = layout.size.generalized(*sys);
- extent.x = extent.x.max(size.x);
- extent.y += size.y;
+ extent.width = extent.width.max(size.width);
+ extent.height += size.height;
}
// ------------------------------------------------------------------ //
@@ -370,13 +369,11 @@ impl StackLayouter {
// The space in which this layout is aligned is given by the
// distances between the borders of its bounding box.
- let usable = Size::new(bound.right - bound.left, bound.bottom - bound.top)
- .generalized(sys);
-
+ let usable = bound.size().generalized(sys);
let local = usable.anchor(align, sys) - size.anchor(align, sys);
- let pos = Size::new(bound.left, bound.top) + local.specialized(sys);
+ let pos = bound.origin() + local.to_size().specialized(sys).to_vec2();
- elements.extend_offset(pos, layout.elements);
+ elements.push_elements(pos, layout.elements);
}
self.layouts.push(BoxLayout { size, align: self.ctx.align, elements });
@@ -406,7 +403,7 @@ impl Space {
size: Size::ZERO,
usable,
extra: Size::ZERO,
- rulers: Value4::with_all(GenAlign::Start),
+ rulers: Sides::uniform(GenAlign::Start),
last_spacing: LastSpacing::Hard,
}
}
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index b43ef089..58774215 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -202,16 +202,13 @@ impl<'a> TreeLayouter<'a> {
// The line layouter has no idea of page styles and thus we
// need to recompute the layouting space resulting of the
// new page style and update it within the layouter.
- let margins = style.margins();
- self.ctx.base = style.size.unpadded(margins);
- self.layouter.set_spaces(
- vec![LayoutSpace {
- size: style.size,
- padding: margins,
- expansion: LayoutExpansion::new(true, true),
- }],
- true,
- );
+ let space = LayoutSpace {
+ size: style.size,
+ insets: style.insets(),
+ expansion: LayoutExpansion::new(true, true),
+ };
+ self.ctx.base = space.usable();
+ self.layouter.set_spaces(vec![space], true);
} else {
error!(
@self.feedback, span,
diff --git a/src/lib.rs b/src/lib.rs
index 8bf5398d..3be97873 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -93,17 +93,17 @@ impl Typesetter {
/// Layout a syntax tree and return the produced layout.
pub async fn layout(&self, tree: &SynTree) -> Pass<MultiLayout> {
- let margins = self.style.page.margins();
+ let space = LayoutSpace {
+ size: self.style.page.size,
+ insets: self.style.page.insets(),
+ expansion: LayoutExpansion::new(true, true),
+ };
layout(&tree, LayoutContext {
loader: &self.loader,
scope: &self.std,
style: &self.style,
- base: self.style.page.size.unpadded(margins),
- spaces: vec![LayoutSpace {
- size: self.style.page.size,
- padding: margins,
- expansion: LayoutExpansion::new(true, true),
- }],
+ base: space.usable(),
+ spaces: vec![space],
repeat: true,
sys: LayoutSystem::new(Dir::LTR, Dir::TTB),
align: LayoutAlign::new(GenAlign::Start, GenAlign::Start),
diff --git a/src/library/boxed.rs b/src/library/boxed.rs
index 85025264..ac0bc19e 100644
--- a/src/library/boxed.rs
+++ b/src/library/boxed.rs
@@ -4,8 +4,8 @@ use crate::length::ScaleLength;
/// `box`: Layouts its contents into a box.
///
/// # Keyword arguments
-/// - `width`: The width of the box (length of relative to parent's width).
-/// - `height`: The height of the box (length of relative to parent's height).
+/// - `width`: The width of the box (length or relative to parent's width).
+/// - `height`: The height of the box (length or relative to parent's height).
pub async fn boxed(
_: Span,
mut args: DictValue,
@@ -19,17 +19,17 @@ pub async fn boxed(
ctx.spaces.truncate(1);
ctx.repeat = false;
- if let Some(w) = args.take_key::<ScaleLength>("width", &mut f) {
- let length = w.raw_scaled(ctx.base.x);
- ctx.base.x = length;
- ctx.spaces[0].size.x = length;
+ if let Some(width) = args.take_key::<ScaleLength>("width", &mut f) {
+ let length = width.raw_scaled(ctx.base.width);
+ ctx.base.width = length;
+ ctx.spaces[0].size.width = length;
ctx.spaces[0].expansion.horizontal = true;
}
- if let Some(h) = args.take_key::<ScaleLength>("height", &mut f) {
- let length = h.raw_scaled(ctx.base.y);
- ctx.base.y = length;
- ctx.spaces[0].size.y = length;
+ if let Some(height) = args.take_key::<ScaleLength>("height", &mut f) {
+ let length = height.raw_scaled(ctx.base.height);
+ ctx.base.height = length;
+ ctx.spaces[0].size.height = length;
ctx.spaces[0].expansion.vertical = true;
}
diff --git a/src/library/page.rs b/src/library/page.rs
index b4f74e48..8188bb67 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -1,4 +1,7 @@
+use std::mem;
+
use super::*;
+use crate::geom::Sides;
use crate::length::{Length, ScaleLength};
use crate::paper::{Paper, PaperClass};
@@ -27,36 +30,36 @@ pub async fn page(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
if let Some(width) = args.take_key::<Length>("width", &mut f) {
style.class = PaperClass::Custom;
- style.size.x = width.as_raw();
+ style.size.width = width.as_raw();
}
if let Some(height) = args.take_key::<Length>("height", &mut f) {
style.class = PaperClass::Custom;
- style.size.y = height.as_raw();
+ style.size.height = height.as_raw();
}
if let Some(margins) = args.take_key::<ScaleLength>("margins", &mut f) {
- style.margins.set_all(Some(margins));
+ style.margins = Sides::uniform(Some(margins));
}
if let Some(left) = args.take_key::<ScaleLength>("left", &mut f) {
style.margins.left = Some(left);
}
- if let Some(right) = args.take_key::<ScaleLength>("right", &mut f) {
- style.margins.right = Some(right);
- }
-
if let Some(top) = args.take_key::<ScaleLength>("top", &mut f) {
style.margins.top = Some(top);
}
+ if let Some(right) = args.take_key::<ScaleLength>("right", &mut f) {
+ style.margins.right = Some(right);
+ }
+
if let Some(bottom) = args.take_key::<ScaleLength>("bottom", &mut f) {
style.margins.bottom = Some(bottom);
}
if args.take_key::<bool>("flip", &mut f).unwrap_or(false) {
- style.size.swap();
+ mem::swap(&mut style.size.width, &mut style.size.height);
}
args.unexpected(&mut f);
diff --git a/src/paper.rs b/src/paper.rs
index 266f22d1..8f855d5a 100644
--- a/src/paper.rs
+++ b/src/paper.rs
@@ -1,6 +1,6 @@
//! Predefined papers.
-use crate::geom::{Size, Value4};
+use crate::geom::{Sides, Size};
use crate::length::{Length, ScaleLength};
/// Specification of a paper.
@@ -37,23 +37,16 @@ pub enum PaperClass {
}
impl PaperClass {
- /// The default margins for this page class.
- pub fn default_margins(self) -> Value4<ScaleLength> {
- let values = |l, t, r, b| {
- Value4::new(
- ScaleLength::Scaled(l),
- ScaleLength::Scaled(t),
- ScaleLength::Scaled(r),
- ScaleLength::Scaled(b),
- )
- };
-
+ /// The default margin ratios for this page class.
+ pub fn default_margins(self) -> Sides<ScaleLength> {
+ let s = ScaleLength::Scaled;
+ let f = |l, r, t, b| Sides::new(s(l), s(r), s(t), s(b));
match self {
- Self::Custom => values(0.1190, 0.0842, 0.1190, 0.0842),
- Self::Base => values(0.1190, 0.0842, 0.1190, 0.0842),
- Self::US => values(0.1760, 0.1092, 0.1760, 0.0910),
- Self::Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294),
- Self::Book => values(0.1200, 0.0852, 0.1500, 0.0965),
+ Self::Custom => f(0.1190, 0.0842, 0.1190, 0.0842),
+ Self::Base => f(0.1190, 0.0842, 0.1190, 0.0842),
+ Self::US => f(0.1760, 0.1092, 0.1760, 0.0910),
+ Self::Newspaper => f(0.0455, 0.0587, 0.0455, 0.0294),
+ Self::Book => f(0.1200, 0.0852, 0.1500, 0.0965),
}
}
}
diff --git a/src/style.rs b/src/style.rs
index 7da7f0c6..b6c1a278 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -2,7 +2,7 @@
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
-use crate::geom::{Margins, Size, Value4};
+use crate::geom::{Insets, Sides, Size};
use crate::length::{Length, ScaleLength};
use crate::paper::{Paper, PaperClass, PAPER_A4};
@@ -101,9 +101,9 @@ pub struct PageStyle {
pub class: PaperClass,
/// The width and height of the page.
pub size: Size,
- /// The amount of white space on each side. If a side is set to `None`, the
- /// default for the paper class is used.
- pub margins: Value4<Option<ScaleLength>>,
+ /// The amount of white space in the order [left, top, right, bottom]. If a
+ /// side is set to `None`, the default for the paper class is used.
+ pub margins: Sides<Option<ScaleLength>>,
}
impl PageStyle {
@@ -112,19 +112,19 @@ impl PageStyle {
Self {
class: paper.class,
size: paper.size(),
- margins: Value4::with_all(None),
+ margins: Sides::uniform(None),
}
}
- /// The absolute margins.
- pub fn margins(&self) -> Margins {
- let size = self.size;
+ /// The absolute insets.
+ pub fn insets(&self) -> Insets {
+ let Size { width, height } = self.size;
let default = self.class.default_margins();
- Margins {
- left: self.margins.left.unwrap_or(default.left).raw_scaled(size.x),
- top: self.margins.top.unwrap_or(default.top).raw_scaled(size.y),
- right: self.margins.right.unwrap_or(default.right).raw_scaled(size.x),
- bottom: self.margins.bottom.unwrap_or(default.bottom).raw_scaled(size.y),
+ Insets {
+ x0: -self.margins.left.unwrap_or(default.left).raw_scaled(width),
+ y0: -self.margins.top.unwrap_or(default.top).raw_scaled(height),
+ x1: -self.margins.right.unwrap_or(default.right).raw_scaled(width),
+ y1: -self.margins.bottom.unwrap_or(default.bottom).raw_scaled(height),
}
}
}
diff --git a/tests/coma.typ b/tests/coma.typ
index e1bf9f5b..839335b7 100644
--- a/tests/coma.typ
+++ b/tests/coma.typ
@@ -7,10 +7,7 @@
Dr. Max Mustermann \
Ola Nordmann, John Doe
]
-[align: right >> box][
- *WiSe 2019/2020* \
- Woche 3
-]
+[align: right >> box][*WiSe 2019/2020* \ Woche 3]
[v: 6mm]
diff --git a/tests/test_typeset.rs b/tests/test_typeset.rs
index b81cecf6..eb1b8ad0 100644
--- a/tests/test_typeset.rs
+++ b/tests/test_typeset.rs
@@ -13,7 +13,7 @@ use ttf_parser::OutlineBuilder;
use typstc::export::pdf;
use typstc::font::{FontLoader, SharedFontLoader};
-use typstc::geom::{Size, Value4};
+use typstc::geom::{Point, Sides, Size, Vec2};
use typstc::layout::elements::{LayoutElement, Shaped};
use typstc::layout::MultiLayout;
use typstc::length::Length;
@@ -66,10 +66,11 @@ fn main() {
let loader = Rc::new(RefCell::new(loader));
let mut typesetter = Typesetter::new(loader.clone());
+ let edge = Length::pt(250.0).as_raw();
typesetter.set_page_style(PageStyle {
class: PaperClass::Custom,
- size: Size::with_all(Length::pt(250.0).as_raw()),
- margins: Value4::with_all(None),
+ size: Size::new(edge, edge),
+ margins: Sides::uniform(None),
});
for (name, path, src) in filtered {
@@ -156,24 +157,28 @@ fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget
let width = 2.0 * pad
+ layouts
.iter()
- .map(|l| scale * l.size.x)
+ .map(|l| scale * l.size.width)
.max_by(|a, b| a.partial_cmp(&b).unwrap())
.unwrap()
.round();
- let height =
- pad + layouts.iter().map(|l| scale * l.size.y + pad).sum::<f64>().round();
+ let height = pad
+ + layouts
+ .iter()
+ .map(|l| scale * l.size.height + pad)
+ .sum::<f64>()
+ .round();
let mut surface = DrawTarget::new(width as i32, height as i32);
surface.clear(BLACK);
- let mut offset = Size::new(pad, pad);
+ let mut offset = Vec2::new(pad, pad);
for layout in layouts {
surface.fill_rect(
offset.x as f32,
offset.y as f32,
- (scale * layout.size.x) as f32,
- (scale * layout.size.y) as f32,
+ (scale * layout.size.width) as f32,
+ (scale * layout.size.height) as f32,
&Source::Solid(WHITE),
&Default::default(),
);
@@ -184,13 +189,13 @@ fn render(layouts: &MultiLayout, loader: &FontLoader, scale: f64) -> DrawTarget
&mut surface,
loader,
shaped,
- scale * pos + offset,
+ (scale * pos.to_vec2() + offset).to_point(),
scale,
),
}
}
- offset.y += scale * layout.size.y + pad;
+ offset.y += scale * layout.size.height + pad;
}
surface
@@ -200,7 +205,7 @@ fn render_shaped(
surface: &mut DrawTarget,
loader: &FontLoader,
shaped: &Shaped,
- pos: Size,
+ pos: Point,
scale: f64,
) {
let face = loader.get_loaded(shaped.face).get();