typst/src/layout/mod.rs
2020-10-04 23:31:35 +02:00

212 lines
6.4 KiB
Rust

//! Layouting of syntax trees.
pub mod primitive;
mod line;
mod stack;
mod tree;
pub use line::*;
pub use primitive::*;
pub use stack::*;
pub use tree::*;
use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt};
use crate::eval::{PageState, State, TextState};
use crate::font::SharedFontLoader;
use crate::shaping::Shaped;
use crate::syntax::SynTree;
use crate::{Feedback, Pass};
/// Layout a syntax tree and return the produced layout.
pub async fn layout(
tree: &SynTree,
state: State,
loader: SharedFontLoader,
) -> Pass<Vec<BoxLayout>> {
let space = LayoutSpace {
size: state.page.size,
insets: state.page.insets(),
expansion: LayoutExpansion::new(true, true),
};
let constraints = LayoutConstraints {
root: true,
base: space.usable(),
spaces: vec![space],
repeat: true,
};
let mut ctx = LayoutContext {
loader,
state,
constraints,
f: Feedback::new(),
};
let layouts = layout_tree(&tree, &mut ctx).await;
Pass::new(layouts, ctx.f)
}
/// A finished box with content at fixed positions.
#[derive(Debug, Clone, PartialEq)]
pub struct BoxLayout {
/// The size of the box.
pub size: Size,
/// How to align this box in a parent container.
pub align: LayoutAlign,
/// The elements composing this layout.
pub elements: Vec<(Point, LayoutElement)>,
}
impl BoxLayout {
/// Create an new empty collection.
pub fn new(size: Size, align: LayoutAlign) -> Self {
Self { size, align, elements: vec![] }
}
/// Add an element at a position.
pub fn push(&mut self, pos: Point, element: LayoutElement) {
self.elements.push((pos, element));
}
/// Add all elements of another collection, placing them relative to the
/// given position.
pub fn push_layout(&mut self, pos: Point, more: Self) {
for (subpos, element) in more.elements {
self.push(pos + subpos.to_vec2(), element);
}
}
}
/// A layout element, the basic building block layouts are composed of.
#[derive(Debug, Clone, PartialEq)]
pub enum LayoutElement {
/// Shaped text.
Text(Shaped),
}
/// The context for layouting.
#[derive(Debug, Clone)]
pub struct LayoutContext {
/// The font loader to query fonts from when typesetting text.
pub loader: SharedFontLoader,
/// The active state.
pub state: State,
/// The active constraints.
pub constraints: LayoutConstraints,
/// The accumulated feedback.
pub f: Feedback,
}
/// The constraints for layouting a single node.
#[derive(Debug, Clone)]
pub struct LayoutConstraints {
/// Whether this layouting process is the root page-building process.
pub root: bool,
/// The unpadded size of this container (the base 100% for relative sizes).
pub base: Size,
/// The spaces to layout into.
pub spaces: Vec<LayoutSpace>,
/// Whether to spill over into copies of the last space or finish layouting
/// when the last space is used up.
pub repeat: bool,
}
/// The space into which content is laid out.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LayoutSpace {
/// The maximum size of the rectangle to layout into.
pub size: Size,
/// Padding that should be respected on each side.
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 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 + self.insets.size()
}
/// The inner layout space with size reduced by the padding, zero padding of
/// its own and no layout expansion.
pub fn inner(&self) -> Self {
Self {
size: self.usable(),
insets: Insets::ZERO,
expansion: LayoutExpansion::new(false, false),
}
}
}
/// Commands executable by the layouting engine.
#[derive(Debug, Clone, PartialEq)]
pub enum Command {
/// Layout the given tree in the current context (i.e. not nested). The
/// content of the tree is not laid out into a separate box and then added,
/// but simply laid out flatly in the active layouting process.
///
/// This has the effect that the content fits nicely into the active line
/// layouting, enabling functions to e.g. change the style of some piece of
/// text while keeping it part of the current paragraph.
LayoutSyntaxTree(SynTree),
/// Add a finished layout.
Add(BoxLayout),
/// Add spacing of the given kind along the primary or secondary axis. The
/// kind defines how the spacing interacts with surrounding spacing.
AddSpacing(f64, SpacingKind, GenAxis),
/// Start a new line.
BreakLine,
/// Start a new page, which will be part of the finished layout even if it
/// stays empty (since the page break is a _hard_ space break).
BreakPage,
/// Update the text style.
SetTextState(TextState),
/// Update the page style.
SetPageState(PageState),
/// Update the layouting system along which future boxes will be laid
/// out. This ends the current line.
SetSystem(LayoutSystem),
/// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlign),
}
/// Defines how spacing interacts with surrounding spacing.
///
/// There are two options for interaction: Hard and soft spacing. Typically,
/// hard spacing is used when a fixed amount of space needs to be inserted no
/// matter what. In contrast, soft spacing can be used to insert a default
/// spacing between e.g. two words or paragraphs that can still be overridden by
/// a hard space.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SpacingKind {
/// Hard spaces are always laid out and consume surrounding soft space.
Hard,
/// Soft spaces are not laid out if they are touching a hard space and
/// consume neighbouring soft spaces with higher levels.
Soft(u32),
}
impl SpacingKind {
/// The standard spacing kind used for paragraph spacing.
pub const PARAGRAPH: Self = Self::Soft(1);
/// The standard spacing kind used for line spacing.
pub const LINE: Self = Self::Soft(2);
/// The standard spacing kind used for word spacing.
pub const WORD: Self = Self::Soft(1);
}