mirror of https://github.com/stelzo/typst.git
274 lines
6.9 KiB
Rust
274 lines
6.9 KiB
Rust
//! Layouting.
|
|
|
|
mod background;
|
|
mod fixed;
|
|
mod frame;
|
|
mod grid;
|
|
mod image;
|
|
mod incremental;
|
|
mod pad;
|
|
mod par;
|
|
mod shaping;
|
|
mod stack;
|
|
|
|
pub use self::image::*;
|
|
pub use background::*;
|
|
pub use fixed::*;
|
|
pub use frame::*;
|
|
pub use grid::*;
|
|
pub use incremental::*;
|
|
pub use pad::*;
|
|
pub use par::*;
|
|
pub use shaping::*;
|
|
pub use stack::*;
|
|
|
|
use std::any::Any;
|
|
use std::fmt::{self, Debug, Formatter};
|
|
use std::hash::{Hash, Hasher};
|
|
use std::rc::Rc;
|
|
|
|
use fxhash::FxHasher64;
|
|
|
|
use crate::cache::Cache;
|
|
use crate::geom::*;
|
|
use crate::loading::Loader;
|
|
|
|
/// Layout a tree into a collection of frames.
|
|
pub fn layout(loader: &mut dyn Loader, cache: &mut Cache, tree: &Tree) -> Vec<Rc<Frame>> {
|
|
tree.layout(&mut LayoutContext { loader, cache, level: 0 })
|
|
}
|
|
|
|
/// A tree of layout nodes.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct Tree {
|
|
/// Runs of pages with the same properties.
|
|
pub runs: Vec<PageRun>,
|
|
}
|
|
|
|
impl Tree {
|
|
/// Layout the tree into a collection of frames.
|
|
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<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 PageRun {
|
|
/// The size of each page.
|
|
pub size: Size,
|
|
/// The layout node that produces the actual pages (typically a
|
|
/// [`StackNode`]).
|
|
pub child: AnyNode,
|
|
}
|
|
|
|
impl PageRun {
|
|
/// Layout the page run.
|
|
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
|
|
// When one of the lengths is infinite the page fits its content along
|
|
// that axis.
|
|
let Size { width, height } = self.size;
|
|
let expand = Spec::new(width.is_finite(), height.is_finite());
|
|
let regions = Regions::repeat(self.size, expand);
|
|
self.child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect()
|
|
}
|
|
}
|
|
|
|
/// A wrapper around a dynamic layouting node.
|
|
pub struct AnyNode {
|
|
node: Box<dyn Bounds>,
|
|
hash: u64,
|
|
}
|
|
|
|
impl AnyNode {
|
|
/// Create a new instance from any node that satisifies the required bounds.
|
|
pub fn new<T>(node: T) -> Self
|
|
where
|
|
T: Layout + Debug + Clone + PartialEq + Hash + 'static,
|
|
{
|
|
let mut state = FxHasher64::default();
|
|
node.type_id().hash(&mut state);
|
|
node.hash(&mut state);
|
|
let hash = state.finish();
|
|
|
|
Self { node: Box::new(node), hash }
|
|
}
|
|
}
|
|
|
|
impl Layout for AnyNode {
|
|
fn layout(
|
|
&self,
|
|
ctx: &mut LayoutContext,
|
|
regions: &Regions,
|
|
) -> Vec<Constrained<Rc<Frame>>> {
|
|
ctx.level += 1;
|
|
let frames =
|
|
ctx.cache.layout.get(self.hash, regions.clone()).unwrap_or_else(|| {
|
|
let frames = self.node.layout(ctx, regions);
|
|
ctx.cache.layout.insert(self.hash, frames.clone(), ctx.level - 1);
|
|
frames
|
|
});
|
|
ctx.level -= 1;
|
|
frames
|
|
}
|
|
}
|
|
|
|
impl Clone for AnyNode {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
node: self.node.dyn_clone(),
|
|
hash: self.hash,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for AnyNode {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.node.dyn_eq(other.node.as_ref())
|
|
}
|
|
}
|
|
|
|
impl Hash for AnyNode {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
state.write_u64(self.hash);
|
|
}
|
|
}
|
|
|
|
impl Debug for AnyNode {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
self.node.fmt(f)
|
|
}
|
|
}
|
|
|
|
trait Bounds: Layout + Debug + 'static {
|
|
fn as_any(&self) -> &dyn Any;
|
|
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
|
|
fn dyn_clone(&self) -> Box<dyn Bounds>;
|
|
}
|
|
|
|
impl<T> Bounds for T
|
|
where
|
|
T: Layout + Debug + PartialEq + Clone + 'static,
|
|
{
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn dyn_eq(&self, other: &dyn Bounds) -> bool {
|
|
if let Some(other) = other.as_any().downcast_ref::<Self>() {
|
|
self == other
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn dyn_clone(&self) -> Box<dyn Bounds> {
|
|
Box::new(self.clone())
|
|
}
|
|
}
|
|
|
|
/// Layout a node.
|
|
pub trait Layout {
|
|
/// Layout the node into the given regions.
|
|
fn layout(
|
|
&self,
|
|
ctx: &mut LayoutContext,
|
|
regions: &Regions,
|
|
) -> Vec<Constrained<Rc<Frame>>>;
|
|
}
|
|
|
|
/// The context for layouting.
|
|
pub struct LayoutContext<'a> {
|
|
/// The loader from which fonts are loaded.
|
|
pub loader: &'a mut dyn Loader,
|
|
/// A cache for loaded fonts and artifacts from past layouting.
|
|
pub cache: &'a mut Cache,
|
|
/// How deeply nested the current layout tree position is.
|
|
pub level: usize,
|
|
}
|
|
|
|
/// A sequence of regions to layout into.
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct Regions {
|
|
/// The remaining size of the current region.
|
|
pub current: Size,
|
|
/// The base size for relative sizing.
|
|
pub base: Size,
|
|
/// A stack of followup regions.
|
|
///
|
|
/// Note that this is a stack and not a queue! The size of the next region is
|
|
/// `backlog.last()`.
|
|
pub backlog: Vec<Size>,
|
|
/// The final region that is repeated once the backlog is drained.
|
|
pub last: Option<Size>,
|
|
/// Whether nodes should expand to fill the regions instead of shrinking to
|
|
/// fit the content.
|
|
///
|
|
/// This property is only handled by nodes that have the ability to control
|
|
/// their own size.
|
|
pub expand: Spec<bool>,
|
|
}
|
|
|
|
impl Regions {
|
|
/// Create a new region sequence with exactly one region.
|
|
pub fn one(size: Size, expand: Spec<bool>) -> Self {
|
|
Self {
|
|
current: size,
|
|
base: size,
|
|
backlog: vec![],
|
|
last: None,
|
|
expand,
|
|
}
|
|
}
|
|
|
|
/// Create a new sequence of same-size regions that repeats indefinitely.
|
|
pub fn repeat(size: Size, expand: Spec<bool>) -> Self {
|
|
Self {
|
|
current: size,
|
|
base: size,
|
|
backlog: vec![],
|
|
last: Some(size),
|
|
expand,
|
|
}
|
|
}
|
|
|
|
/// Create new regions where all sizes are mapped with `f`.
|
|
pub fn map<F>(&self, mut f: F) -> Self
|
|
where
|
|
F: FnMut(Size) -> Size,
|
|
{
|
|
let mut regions = self.clone();
|
|
regions.mutate(|s| *s = f(*s));
|
|
regions
|
|
}
|
|
|
|
/// Whether `current` is a fully sized (untouched) copy of the last region.
|
|
///
|
|
/// If this is true, calling `next()` will have no effect.
|
|
pub fn in_full_last(&self) -> bool {
|
|
self.backlog.is_empty() && self.last.map_or(true, |size| self.current == size)
|
|
}
|
|
|
|
/// Advance to the next region if there is any.
|
|
pub fn next(&mut self) -> bool {
|
|
if let Some(size) = self.backlog.pop().or(self.last) {
|
|
self.current = size;
|
|
self.base = size;
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
/// Mutate all contained sizes in place.
|
|
pub fn mutate<F>(&mut self, mut f: F)
|
|
where
|
|
F: FnMut(&mut Size),
|
|
{
|
|
f(&mut self.current);
|
|
f(&mut self.base);
|
|
self.last.as_mut().map(|x| f(x));
|
|
self.backlog.iter_mut().for_each(f);
|
|
}
|
|
}
|