Port to kurbo 🎋
This commit is contained in:
parent
8dbc5b60cc
commit
0fc25d732d
30
Cargo.toml
30
Cargo.toml
@ -4,27 +4,31 @@ version = "0.1.0"
|
||||
authors = ["The Typst Project Developers"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[workspace]
|
||||
members = ["main"]
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 2
|
||||
|
||||
[dependencies]
|
||||
fontdock = { path = "../fontdock", default-features = false }
|
||||
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"]
|
||||
|
||||
[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 }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
futures-executor = "0.3"
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
385
src/geom.rs
385
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 }
|
||||
}
|
||||
|
||||
/// Get the specificed component.
|
||||
pub fn get(self, axis: SpecAxis) -> T {
|
||||
match axis {
|
||||
SpecAxis::Horizontal => self.x,
|
||||
SpecAxis::Vertical => self.y,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the primary value of this specialized 2D-value.
|
||||
pub fn primary(self, sys: LayoutSystem) -> T {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
self.x
|
||||
} else {
|
||||
self.y
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the primary value of this specialized 2D-value mutably.
|
||||
pub fn primary_mut(&mut self, sys: LayoutSystem) -> &mut T {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
&mut self.x
|
||||
} else {
|
||||
&mut self.y
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the secondary value of this specialized 2D-value.
|
||||
pub fn secondary(self, sys: LayoutSystem) -> T {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
self.y
|
||||
} else {
|
||||
self.x
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the secondary value of this specialized 2D-value mutably.
|
||||
pub fn secondary_mut(&mut self, sys: LayoutSystem) -> &mut T {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
&mut self.y
|
||||
} else {
|
||||
&mut self.x
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the generalized version of a `Size2D` dependent on the layouting
|
||||
/// 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.
|
||||
pub fn generalized(self, sys: LayoutSystem) -> Self {
|
||||
match sys.primary.axis() {
|
||||
SpecAxis::Horizontal => self,
|
||||
SpecAxis::Vertical => Self { x: self.y, y: self.x },
|
||||
}
|
||||
}
|
||||
fn generalized(self, sys: LayoutSystem) -> Self;
|
||||
|
||||
/// Returns the specialized version of this generalized Size2D (inverse to
|
||||
/// `generalized`).
|
||||
pub 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
fn specialized(self, sys: LayoutSystem) -> Self;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
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.
|
||||
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),
|
||||
fn anchor(self, align: LayoutAlign, sys: LayoutSystem) -> Point;
|
||||
}
|
||||
|
||||
impl SizeExt for Size {
|
||||
fn primary(self, sys: LayoutSystem) -> f64 {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
self.width
|
||||
} else {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn primary_mut(&mut self, sys: LayoutSystem) -> &mut f64 {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
&mut self.width
|
||||
} else {
|
||||
&mut self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn secondary(self, sys: LayoutSystem) -> f64 {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
self.height
|
||||
} else {
|
||||
self.width
|
||||
}
|
||||
}
|
||||
|
||||
fn secondary_mut(&mut self, sys: LayoutSystem) -> &mut f64 {
|
||||
if sys.primary.axis() == SpecAxis::Horizontal {
|
||||
&mut self.height
|
||||
} else {
|
||||
&mut self.width
|
||||
}
|
||||
}
|
||||
|
||||
fn generalized(self, sys: LayoutSystem) -> Self {
|
||||
match sys.primary.axis() {
|
||||
SpecAxis::Horizontal => self,
|
||||
SpecAxis::Vertical => Self::new(self.height, self.width),
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn fits(self, other: Self) -> bool {
|
||||
self.width >= other.width && self.height >= other.height
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
Point::new(
|
||||
length_anchor(self.width, align.primary, sys.primary),
|
||||
length_anchor(self.height, align.secondary, sys.secondary),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Size {
|
||||
type Output = Size;
|
||||
|
||||
fn neg(self) -> Size {
|
||||
Size { x: -self.x, y: -self.y }
|
||||
}
|
||||
}
|
||||
|
||||
/// 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] }
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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)> {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
14
src/lib.rs
14
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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
27
src/paper.rs
27
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
src/style.rs
26
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user