diff --git a/benches/oneshot.rs b/benches/oneshot.rs index 8a5cec1b1..e3b18390c 100644 --- a/benches/oneshot.rs +++ b/benches/oneshot.rs @@ -75,8 +75,7 @@ fn bench_eval(iai: &mut Iai) { fn bench_layout(iai: &mut Iai) { let (mut ctx, id) = context(); let module = ctx.evaluate(id).unwrap(); - let tree = module.into_root(); - iai.run(|| tree.layout(&mut ctx)); + iai.run(|| module.template.layout(&mut ctx)); } fn bench_highlight(iai: &mut Iai) { diff --git a/src/eval/mod.rs b/src/eval/mod.rs index aa75f8b70..22bea7d1a 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -40,7 +40,7 @@ use unicode_segmentation::UnicodeSegmentation; use crate::diag::{At, Error, StrResult, Trace, Tracepoint, TypResult}; use crate::geom::{Angle, Color, Fractional, Length, Paint, Relative}; use crate::image::ImageStore; -use crate::layout::RootNode; +use crate::layout::Layout; use crate::library::{self, DecoLine, TextNode}; use crate::loading::Loader; use crate::parse; @@ -66,13 +66,6 @@ pub struct Module { pub template: Template, } -impl Module { - /// Convert this module's template into a layout tree. - pub fn into_root(self) -> RootNode { - self.template.into_root() - } -} - /// Evaluate an expression. pub trait Eval { /// The output of evaluating the expression. diff --git a/src/eval/template.rs b/src/eval/template.rs index 6515c2713..90a751f33 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -8,11 +8,13 @@ use std::ops::{Add, AddAssign}; use super::{Property, StyleMap, Styled}; use crate::diag::StrResult; use crate::geom::SpecAxis; -use crate::layout::{Layout, PackedNode, RootNode}; +use crate::layout::{Layout, PackedNode}; +use crate::library::prelude::*; use crate::library::{ FlowChild, FlowNode, PageNode, ParChild, ParNode, PlaceNode, SpacingKind, TextNode, }; use crate::util::EcoString; +use crate::Context; /// Composable representation of styled content. /// @@ -89,6 +91,18 @@ impl Template { Self::Block(node.pack()) } + /// Layout this template into a collection of pages. + pub fn layout(&self, ctx: &mut Context) -> Vec<Arc<Frame>> { + let (mut ctx, styles) = LayoutContext::new(ctx); + let mut packer = Packer::new(true); + packer.walk(self.clone(), StyleMap::new()); + packer + .into_root() + .iter() + .flat_map(|styled| styled.item.layout(&mut ctx, styled.map.chain(&styles))) + .collect() + } + /// Style this template with a single property. pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self { if let Self::Styled(_, map) = &mut self { @@ -123,24 +137,6 @@ impl Template { Ok(Self::Sequence(vec![self.clone(); count])) } - - /// Convert to a type-erased block-level node. - pub fn pack(self) -> PackedNode { - if let Template::Block(packed) = self { - packed - } else { - let mut packer = Packer::new(false); - packer.walk(self, StyleMap::new()); - packer.into_block() - } - } - - /// Lift to a root layout tree node. - pub fn into_root(self) -> RootNode { - let mut packer = Packer::new(true); - packer.walk(self, StyleMap::new()); - packer.into_root() - } } impl Default for Template { @@ -185,6 +181,27 @@ impl Sum for Template { } } +impl Layout for Template { + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + styles: StyleChain, + ) -> Vec<Constrained<Arc<Frame>>> { + let mut packer = Packer::new(false); + packer.walk(self.clone(), StyleMap::new()); + packer.into_block().layout(ctx, regions, styles) + } + + fn pack(self) -> PackedNode { + if let Template::Block(packed) = self { + packed + } else { + PackedNode::new(self) + } + } +} + /// Packs a [`Template`] into a flow or root node. struct Packer { /// Whether this packer produces a root node. @@ -215,9 +232,9 @@ impl Packer { } /// Finish up and return the resulting root node. - fn into_root(mut self) -> RootNode { + fn into_root(mut self) -> Vec<Styled<PageNode>> { self.pagebreak(); - RootNode(self.pages) + self.pages } /// Consider a template with the given styles. diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 538bcd739..3cccee283 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -15,36 +15,14 @@ use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use crate::eval::{StyleChain, Styled}; +use crate::eval::StyleChain; use crate::font::FontStore; use crate::frame::{Element, Frame, Geometry, Shape, Stroke}; use crate::geom::{Align, Linear, Paint, Point, Sides, Size, Spec}; use crate::image::ImageStore; -use crate::library::{AlignNode, Move, PadNode, PageNode, TransformNode}; +use crate::library::{AlignNode, Move, PadNode, TransformNode}; use crate::Context; -/// The root layout node, a document consisting of top-level page runs. -#[derive(Hash)] -pub struct RootNode(pub Vec<Styled<PageNode>>); - -impl RootNode { - /// Layout the document into a sequence of frames, one per page. - pub fn layout(&self, ctx: &mut Context) -> Vec<Arc<Frame>> { - let (mut ctx, styles) = LayoutContext::new(ctx); - self.0 - .iter() - .flat_map(|styled| styled.item.layout(&mut ctx, styled.map.chain(&styles))) - .collect() - } -} - -impl Debug for RootNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("Root ")?; - f.debug_list().entries(&self.0).finish() - } -} - /// A node that can be layouted into a sequence of regions. /// /// Layout return one frame per used region alongside constraints that define @@ -63,11 +41,7 @@ pub trait Layout { where Self: Debug + Hash + Sized + Sync + Send + 'static, { - PackedNode { - #[cfg(feature = "layout-cache")] - hash: self.hash64(), - node: Arc::new(self), - } + PackedNode::new(self) } } @@ -86,8 +60,8 @@ pub struct LayoutContext<'a> { } impl<'a> LayoutContext<'a> { - /// Create a new layout context. - fn new(ctx: &'a mut Context) -> (Self, StyleChain<'a>) { + /// Create a new layout context and style chain. + pub fn new(ctx: &'a mut Context) -> (Self, StyleChain<'a>) { let this = Self { fonts: &mut ctx.fonts, images: &mut ctx.images, @@ -100,27 +74,7 @@ impl<'a> LayoutContext<'a> { } } -/// A layout node that produces an empty frame. -/// -/// The packed version of this is returned by [`PackedNode::default`]. -#[derive(Debug, Hash)] -pub struct EmptyNode; - -impl Layout for EmptyNode { - fn layout( - &self, - _: &mut LayoutContext, - regions: &Regions, - _: StyleChain, - ) -> Vec<Constrained<Arc<Frame>>> { - let size = regions.expand.select(regions.current, Size::zero()); - let mut cts = Constraints::new(regions.expand); - cts.exact = regions.current.filter(regions.expand); - vec![Frame::new(size).constrain(cts)] - } -} - -/// A packed layouting node with a precomputed hash. +/// A type-erased layouting node with a precomputed hash. #[derive(Clone)] pub struct PackedNode { /// The type-erased node. @@ -131,6 +85,18 @@ pub struct PackedNode { } impl PackedNode { + /// Pack any layoutable node. + pub fn new<T>(node: T) -> Self + where + T: Layout + Debug + Hash + Sync + Send + 'static, + { + Self { + #[cfg(feature = "layout-cache")] + hash: node.hash64(), + node: Arc::new(node), + } + } + /// Check whether the contained node is a specific layout node. pub fn is<T: 'static>(&self) -> bool { self.node.as_any().is::<T>() @@ -293,7 +259,7 @@ trait Bounds: Layout + Debug + Sync + Send + 'static { impl<T> Bounds for T where - T: Layout + Hash + Debug + Sync + Send + 'static, + T: Layout + Debug + Hash + Sync + Send + 'static, { fn as_any(&self) -> &dyn Any { self @@ -309,13 +275,33 @@ where } } +/// A layout node that produces an empty frame. +/// +/// The packed version of this is returned by [`PackedNode::default`]. +#[derive(Debug, Hash)] +struct EmptyNode; + +impl Layout for EmptyNode { + fn layout( + &self, + _: &mut LayoutContext, + regions: &Regions, + _: StyleChain, + ) -> Vec<Constrained<Arc<Frame>>> { + let size = regions.expand.select(regions.current, Size::zero()); + let mut cts = Constraints::new(regions.expand); + cts.exact = regions.current.filter(regions.expand); + vec![Frame::new(size).constrain(cts)] + } +} + /// Fix the size of a node. #[derive(Debug, Hash)] -pub struct SizedNode { +struct SizedNode { /// How to size the node horizontally and vertically. - pub sizing: Spec<Option<Linear>>, + sizing: Spec<Option<Linear>>, /// The node to be sized. - pub child: PackedNode, + child: PackedNode, } impl Layout for SizedNode { @@ -365,11 +351,11 @@ impl Layout for SizedNode { /// Fill the frames resulting from a node. #[derive(Debug, Hash)] -pub struct FillNode { +struct FillNode { /// How to fill the frames resulting from the `child`. - pub fill: Paint, + fill: Paint, /// The node to fill. - pub child: PackedNode, + child: PackedNode, } impl Layout for FillNode { @@ -390,11 +376,11 @@ impl Layout for FillNode { /// Stroke the frames resulting from a node. #[derive(Debug, Hash)] -pub struct StrokeNode { +struct StrokeNode { /// How to stroke the frames resulting from the `child`. - pub stroke: Stroke, + stroke: Stroke, /// The node to stroke. - pub child: PackedNode, + child: PackedNode, } impl Layout for StrokeNode { diff --git a/src/lib.rs b/src/lib.rs index 32938cdac..99958aaca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,14 +2,13 @@ //! //! # Steps //! - **Parsing:** The parsing step first transforms a plain string into an -//! [iterator of tokens][tokens]. This token stream is [parsed] into a -//! [green tree]. The green tree itself is untyped, but a typed layer over it -//! is provided in the [AST] module. +//! [iterator of tokens][tokens]. This token stream is [parsed] into a [green +//! tree]. The green tree itself is untyped, but a typed layer over it is +//! provided in the [AST] module. //! - **Evaluation:** The next step is to [evaluate] the markup. This produces a //! [module], consisting of a scope of values that were exported by the code -//! and a [template] with the contents of the module. This node can be -//! converted into a [layout tree], a hierarchical, styled representation of -//! the document. The nodes of this tree are well structured and +//! and a [template], a hierarchical, styled representation with the contents +//! of the module. The nodes of this tree are well structured and //! order-independent and thus much better suited for layouting than the raw //! markup. //! - **Layouting:** Next, the tree is [layouted] into a portable version of the @@ -26,8 +25,7 @@ //! [evaluate]: Context::evaluate //! [module]: eval::Module //! [template]: eval::Template -//! [layout tree]: layout::RootNode -//! [layouted]: layout::RootNode::layout +//! [layouted]: eval::Template::layout //! [cache]: layout::LayoutCache //! [PDF]: export::pdf @@ -127,8 +125,7 @@ impl Context { /// information. pub fn typeset(&mut self, id: SourceId) -> TypResult<Vec<Arc<Frame>>> { let module = self.evaluate(id)?; - let tree = module.into_root(); - let frames = tree.layout(self); + let frames = module.template.layout(self); Ok(frames) } diff --git a/tests/typeset.rs b/tests/typeset.rs index 31610ffc1..59a8425fe 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -23,7 +23,7 @@ use typst::Context; use { filedescriptor::{FileDescriptor, StdioDescriptor::*}, std::fs::File, - typst::layout::RootNode, + typst::eval::Template, }; const TYP_DIR: &str = "./typ"; @@ -266,7 +266,7 @@ fn test_part( let id = ctx.sources.provide(src_path, src); let source = ctx.sources.get(id); if debug { - println!("Syntax: {:#?}", source.root()) + println!("Syntax Tree: {:#?}", source.root()) } let (local_compare_ref, mut ref_errors) = parse_metadata(&source); @@ -276,15 +276,14 @@ fn test_part( let (frames, mut errors) = match ctx.evaluate(id) { Ok(module) => { - let tree = module.into_root(); if debug { - println!("Layout: {tree:#?}"); + println!("Template: {:#?}", module.template); } - let mut frames = tree.layout(ctx); + let mut frames = module.template.layout(ctx); #[cfg(feature = "layout-cache")] - (ok &= test_incremental(ctx, i, &tree, &frames)); + (ok &= test_incremental(ctx, i, &module.template, &frames)); if !compare_ref { frames.clear(); @@ -484,7 +483,7 @@ fn test_reparse(src: &str, i: usize, rng: &mut LinearShift) -> bool { fn test_incremental( ctx: &mut Context, i: usize, - tree: &RootNode, + template: &Template, frames: &[Arc<Frame>], ) -> bool { let mut ok = true; @@ -499,7 +498,7 @@ fn test_incremental( ctx.layout_cache.turnaround(); - let cached = silenced(|| tree.layout(ctx)); + let cached = silenced(|| template.layout(ctx)); let total = reference.levels() - 1; let misses = ctx .layout_cache