typst/src/layout/mod.rs
2021-02-06 12:30:44 +01:00

269 lines
6.9 KiB
Rust

//! Layouting.
mod fixed;
mod node;
mod pad;
mod par;
mod background;
mod spacing;
mod stack;
mod text;
use crate::color::Color;
use crate::env::{Env, ResourceId};
use crate::geom::*;
use crate::shaping::Shaped;
pub use fixed::*;
pub use node::*;
pub use pad::*;
pub use par::*;
pub use background::*;
pub use spacing::*;
pub use stack::*;
pub use text::*;
/// Layout a tree into a collection of frames.
pub fn layout(tree: &Tree, env: &mut Env) -> Vec<Frame> {
tree.layout(&mut LayoutContext { env })
}
/// A tree of layout nodes.
#[derive(Debug, Clone, PartialEq)]
pub struct Tree {
/// Runs of pages with the same properties.
pub runs: Vec<NodePages>,
}
impl Tree {
/// Layout the tree into a collection of frames.
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
self.runs.iter().flat_map(|run| run.layout(ctx)).collect()
}
}
/// A run of pages that all have the same properties.
#[derive(Debug, Clone, PartialEq)]
pub struct NodePages {
/// The size of each page.
pub size: Size,
/// The layout node that produces the actual pages (typically a
/// [`NodeStack`]).
pub child: Node,
}
impl NodePages {
/// Layout the page run.
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
let areas = Areas::repeat(self.size);
let layouted = self.child.layout(ctx, &areas);
layouted.into_frames()
}
}
/// Layout a node.
pub trait Layout {
/// Layout the node into the given areas.
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted;
}
/// The context for layouting.
#[derive(Debug)]
pub struct LayoutContext<'a> {
/// The environment from which fonts are gathered.
pub env: &'a mut Env,
}
/// A collection of areas to layout into.
#[derive(Debug, Clone, PartialEq)]
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>,
}
impl Areas {
/// Create a new length-1 sequence of areas with just one `area`.
pub fn once(size: Size) -> Self {
Self {
current: size,
full: size,
backlog: vec![],
last: None,
}
}
/// Create a new sequence of areas that repeats `area` indefinitely.
pub fn repeat(size: Size) -> Self {
Self {
current: size,
full: size,
backlog: vec![],
last: Some(size),
}
}
/// 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.
pub fn in_full_last(&self) -> bool {
self.backlog.is_empty()
&& self.last.map_or(true, |size| {
self.current.is_nan() || size.is_nan() || self.current == size
})
}
}
/// Whether to expand or shrink a node along an axis.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Expansion {
/// Fit the content.
Fit,
/// Fill the available space.
Fill,
}
impl Expansion {
/// Resolve the expansion to either the `fit` or `fill` length.
///
/// Prefers `fit` if `fill` is infinite.
pub fn resolve(self, fit: Length, fill: Length) -> Length {
match self {
Self::Fill if fill.is_finite() => fill,
_ => fit,
}
}
}
/// The result of layouting a node.
#[derive(Debug, Clone, PartialEq)]
pub enum Layouted {
/// Spacing that should be added to the parent.
Spacing(Length),
/// A layout that should be added to and aligned in the parent.
Frame(Frame, ChildAlign),
/// Multiple layouts.
Frames(Vec<Frame>, ChildAlign),
}
impl Layouted {
/// Return a reference to all frames contained in this variant (zero, one or
/// arbitrarily many).
pub fn frames(&self) -> &[Frame] {
match self {
Self::Spacing(_) => &[],
Self::Frame(frame, _) => std::slice::from_ref(frame),
Self::Frames(frames, _) => frames,
}
}
/// Return a mutable reference to all frames contained in this variant.
pub fn frames_mut(&mut self) -> &mut [Frame] {
match self {
Self::Spacing(_) => &mut [],
Self::Frame(frame, _) => std::slice::from_mut(frame),
Self::Frames(frames, _) => frames,
}
}
/// Return all frames contained in this varian.
pub fn into_frames(self) -> Vec<Frame> {
match self {
Self::Spacing(_) => vec![],
Self::Frame(frame, _) => vec![frame],
Self::Frames(frames, _) => frames,
}
}
}
/// A finished layout with elements at fixed positions.
#[derive(Debug, Clone, PartialEq)]
pub struct Frame {
/// The size of the frame.
pub size: Size,
/// The elements composing this layout.
pub elements: Vec<(Point, Element)>,
}
impl Frame {
/// Create a new, empty frame.
pub fn new(size: Size) -> Self {
Self { size, elements: vec![] }
}
/// Add an element at a position.
pub fn push(&mut self, pos: Point, element: Element) {
self.elements.push((pos, element));
}
/// Add all elements of another frame, placing them relative to the given
/// position.
pub fn push_frame(&mut self, pos: Point, subframe: Self) {
for (subpos, element) in subframe.elements {
self.push(pos + subpos, element);
}
}
}
/// The building block frames are composed of.
#[derive(Debug, Clone, PartialEq)]
pub enum Element {
/// Shaped text.
Text(Shaped),
/// An image.
Image(Image),
/// Some shape that could hold another frame.
Geometry(Geometry),
}
/// The kind of graphic fill to be applied to a [`Shape`].
#[derive(Debug, Clone, PartialEq)]
pub enum Fill {
/// The fill is a color.
Color(Color),
/// The fill is an image.
Image(Image),
}
/// A shape with some kind of fill.
#[derive(Debug, Clone, PartialEq)]
pub struct Geometry {
/// The shape to draw.
pub shape: Shape,
/// How the shape looks on the inside.
//
// TODO: This could be made into a Vec<Fill> or something such that
// the user can compose multiple fills with alpha values less
// than one to achieve cool effects.
pub fill: Fill,
}
/// Some shape.
#[derive(Debug, Clone, PartialEq)]
pub enum Shape {
/// A rectangle.
Rect(Size),
}
/// An image element.
#[derive(Debug, Clone, PartialEq)]
pub struct Image {
/// The image resource.
pub res: ResourceId,
/// The size of the image in the document.
pub size: Size,
}