diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-12-02 13:17:07 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-12-02 13:21:36 +0100 |
| commit | 5110a41de1ca2236739ace2d37a1af912bb029f1 (patch) | |
| tree | 22cc223140052bd7ec10798f5ecbffaae7c934a8 /library/src/layout | |
| parent | 33ab1fdbdda4e95e48b767a3f7f8f66413b6de0e (diff) | |
Introduce virtual typesetter
Diffstat (limited to 'library/src/layout')
| -rw-r--r-- | library/src/layout/align.rs | 4 | ||||
| -rw-r--r-- | library/src/layout/columns.rs | 6 | ||||
| -rw-r--r-- | library/src/layout/container.rs | 8 | ||||
| -rw-r--r-- | library/src/layout/flow.rs | 25 | ||||
| -rw-r--r-- | library/src/layout/grid.rs | 22 | ||||
| -rw-r--r-- | library/src/layout/hide.rs | 4 | ||||
| -rw-r--r-- | library/src/layout/mod.rs | 85 | ||||
| -rw-r--r-- | library/src/layout/pad.rs | 4 | ||||
| -rw-r--r-- | library/src/layout/page.rs | 16 | ||||
| -rw-r--r-- | library/src/layout/par.rs | 117 | ||||
| -rw-r--r-- | library/src/layout/place.rs | 4 | ||||
| -rw-r--r-- | library/src/layout/repeat.rs | 4 | ||||
| -rw-r--r-- | library/src/layout/stack.rs | 8 | ||||
| -rw-r--r-- | library/src/layout/transform.rs | 8 |
14 files changed, 178 insertions, 137 deletions
diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs index a06f7edb..42bf86e1 100644 --- a/library/src/layout/align.rs +++ b/library/src/layout/align.rs @@ -29,7 +29,7 @@ impl AlignNode { impl Layout for AlignNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { @@ -44,7 +44,7 @@ impl Layout for AlignNode { } // Layout the child. - let mut fragment = self.child.layout(world, styles.chain(&map), &pod)?; + let mut fragment = self.child.layout(vt, styles.chain(&map), &pod)?; for (region, frame) in regions.iter().zip(&mut fragment) { // Align in the target size. The target size depends on whether we // should expand. diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs index 257cc62f..a5d5aff5 100644 --- a/library/src/layout/columns.rs +++ b/library/src/layout/columns.rs @@ -29,14 +29,14 @@ impl ColumnsNode { impl Layout for ColumnsNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { // Separating the infinite space into infinite columns does not make // much sense. if !regions.first.x.is_finite() { - return self.child.layout(world, styles, regions); + return self.child.layout(vt, styles, regions); } // Determine the width of the gutter and each column. @@ -58,7 +58,7 @@ impl Layout for ColumnsNode { }; // Layout the children. - let mut frames = self.child.layout(world, styles, &pod)?.into_iter(); + let mut frames = self.child.layout(vt, styles, &pod)?.into_iter(); let mut finished = vec![]; let dir = styles.get(TextNode::DIR); diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs index a77e0249..5790aae6 100644 --- a/library/src/layout/container.rs +++ b/library/src/layout/container.rs @@ -23,7 +23,7 @@ impl BoxNode { impl Layout for BoxNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { @@ -47,7 +47,7 @@ impl Layout for BoxNode { }; // Layout the child. - let mut frame = self.child.layout(world, styles, &pod)?.into_frame(); + let mut frame = self.child.layout(vt, styles, &pod)?.into_frame(); // Ensure frame size matches regions size if expansion is on. let target = regions.expand.select(regions.first, frame.size()); @@ -95,10 +95,10 @@ impl BlockNode { impl Layout for BlockNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { - self.0.layout(world, styles, regions) + self.0.layout(vt, styles, regions) } } diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index b644d73f..2994d9c6 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -20,7 +20,7 @@ impl FlowNode { impl Layout for FlowNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { @@ -33,9 +33,9 @@ impl Layout for FlowNode { } else if let Some(node) = child.to::<ParNode>() { let barrier = Style::Barrier(child.id()); let styles = styles.chain_one(&barrier); - layouter.layout_par(world, node, styles)?; + layouter.layout_par(vt, node, styles)?; } else if child.has::<dyn Layout>() { - layouter.layout_block(world, child, styles)?; + layouter.layout_block(vt, child, styles)?; } else if child.is::<ColbreakNode>() { layouter.finish_region(false); } else { @@ -122,16 +122,23 @@ impl FlowLayouter { /// Layout a paragraph. fn layout_par( &mut self, - world: Tracked<dyn World>, + vt: &mut Vt, par: &ParNode, styles: StyleChain, ) -> SourceResult<()> { let aligns = Axes::new(styles.get(ParNode::ALIGN), Align::Top); let leading = styles.get(ParNode::LEADING); let consecutive = self.last_was_par; - let fragment = par.layout(world, styles, &self.regions, consecutive)?; - let len = fragment.len(); + let fragment = par.layout( + vt, + styles, + consecutive, + self.regions.first.x, + self.regions.base, + self.regions.expand.x, + )?; + let len = fragment.len(); for (i, frame) in fragment.into_iter().enumerate() { if i > 0 { self.layout_item(FlowItem::Leading(leading)); @@ -151,7 +158,7 @@ impl FlowLayouter { /// Layout a block. fn layout_block( &mut self, - world: Tracked<dyn World>, + vt: &mut Vt, block: &Content, styles: StyleChain, ) -> SourceResult<()> { @@ -159,7 +166,7 @@ impl FlowLayouter { // aligned later. if let Some(placed) = block.to::<PlaceNode>() { if placed.out_of_flow() { - let frame = block.layout(world, styles, &self.regions)?.into_frame(); + let frame = block.layout(vt, styles, &self.regions)?.into_frame(); self.layout_item(FlowItem::Placed(frame)); return Ok(()); } @@ -180,7 +187,7 @@ impl FlowLayouter { // Layout the block itself. let sticky = styles.get(BlockNode::STICKY); - let fragment = block.layout(world, styles, &self.regions)?; + let fragment = block.layout(vt, styles, &self.regions)?; for frame in fragment { self.layout_item(FlowItem::Frame(frame, aligns, sticky)); } diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index a848a650..ab189423 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -36,13 +36,13 @@ impl GridNode { impl Layout for GridNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { // Prepare grid layout by unifying content and gutter tracks. let layouter = GridLayouter::new( - world, + vt, self.tracks.as_deref(), self.gutter.as_deref(), &self.cells, @@ -110,9 +110,9 @@ castable! { } /// Performs grid layout. -struct GridLayouter<'a> { +struct GridLayouter<'a, 'v> { /// The core context. - world: Tracked<'a, dyn World>, + vt: &'a mut Vt<'v>, /// The grid cells. cells: &'a [Content], /// The column tracks including gutter tracks. @@ -147,12 +147,12 @@ enum Row { Fr(Fr, usize), } -impl<'a> GridLayouter<'a> { +impl<'a, 'v> GridLayouter<'a, 'v> { /// Create a new grid layouter. /// /// This prepares grid layout by unifying content and gutter tracks. fn new( - world: Tracked<'a, dyn World>, + vt: &'a mut Vt<'v>, tracks: Axes<&[TrackSizing]>, gutter: Axes<&[TrackSizing]>, cells: &'a [Content], @@ -206,7 +206,7 @@ impl<'a> GridLayouter<'a> { regions.expand = Axes::new(true, false); Self { - world, + vt, cells, cols, rows, @@ -318,7 +318,7 @@ impl<'a> GridLayouter<'a> { v.resolve(self.styles).relative_to(self.regions.base.y); } - let frame = cell.layout(self.world, self.styles, &pod)?.into_frame(); + let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame(); resolved.set_max(frame.width()); } } @@ -395,7 +395,7 @@ impl<'a> GridLayouter<'a> { } let mut sizes = cell - .layout(self.world, self.styles, &pod)? + .layout(self.vt, self.styles, &pod)? .into_iter() .map(|frame| frame.height()); @@ -483,7 +483,7 @@ impl<'a> GridLayouter<'a> { .select(self.regions.base, size); let pod = Regions::one(size, base, Axes::splat(true)); - let frame = cell.layout(self.world, self.styles, &pod)?.into_frame(); + let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame(); output.push_frame(pos, frame); } @@ -519,7 +519,7 @@ impl<'a> GridLayouter<'a> { } // Push the layouted frames into the individual output frames. - let fragment = cell.layout(self.world, self.styles, &pod)?; + let fragment = cell.layout(self.vt, self.styles, &pod)?; for (output, frame) in outputs.iter_mut().zip(fragment) { output.push_frame(pos, frame); } diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs index 64cbee64..136dae2f 100644 --- a/library/src/layout/hide.rs +++ b/library/src/layout/hide.rs @@ -14,11 +14,11 @@ impl HideNode { impl Layout for HideNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { - let mut fragment = self.0.layout(world, styles, regions)?; + let mut fragment = self.0.layout(vt, styles, regions)?; for frame in &mut fragment { frame.clear(); } diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index 8f9337ba..e3691732 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -32,7 +32,6 @@ pub use self::transform::*; use std::mem; -use comemo::Tracked; use typed_arena::Arena; use typst::diag::SourceResult; use typst::geom::*; @@ -40,7 +39,6 @@ use typst::model::{ applicable, capability, realize, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder, StyledNode, }; -use typst::World; use crate::basics::{DescNode, EnumNode, ListItem, ListNode, DESC, ENUM, LIST}; use crate::meta::DocumentNode; @@ -52,23 +50,27 @@ use crate::text::{LinebreakNode, SmartQuoteNode, SpaceNode, TextNode}; #[capability] pub trait LayoutRoot { /// Layout into one frame per page. - fn layout_root( - &self, - world: Tracked<dyn World>, - styles: StyleChain, - ) -> SourceResult<Document>; + fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document>; } impl LayoutRoot for Content { - #[comemo::memoize] - fn layout_root( - &self, - world: Tracked<dyn World>, - styles: StyleChain, - ) -> SourceResult<Document> { - let scratch = Scratch::default(); - let (realized, styles) = realize_root(world, &scratch, self, styles)?; - realized.with::<dyn LayoutRoot>().unwrap().layout_root(world, styles) + fn layout_root(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Document> { + #[comemo::memoize] + fn cached( + node: &Content, + world: comemo::Tracked<dyn World>, + styles: StyleChain, + ) -> SourceResult<Document> { + let mut vt = Vt { world }; + let scratch = Scratch::default(); + let (realized, styles) = realize_root(&mut vt, &scratch, node, styles)?; + realized + .with::<dyn LayoutRoot>() + .unwrap() + .layout_root(&mut vt, styles) + } + + cached(self, vt.world, styles) } } @@ -78,25 +80,38 @@ pub trait Layout { /// Layout into one frame per region. fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment>; } impl Layout for Content { - #[comemo::memoize] fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { - let scratch = Scratch::default(); - let (realized, styles) = realize_block(world, &scratch, self, styles)?; - let barrier = Style::Barrier(realized.id()); - let styles = styles.chain_one(&barrier); - realized.with::<dyn Layout>().unwrap().layout(world, styles, regions) + #[comemo::memoize] + fn cached( + node: &Content, + world: comemo::Tracked<dyn World>, + styles: StyleChain, + regions: &Regions, + ) -> SourceResult<Fragment> { + let mut vt = Vt { world }; + let scratch = Scratch::default(); + let (realized, styles) = realize_block(&mut vt, &scratch, node, styles)?; + let barrier = Style::Barrier(realized.id()); + let styles = styles.chain_one(&barrier); + realized + .with::<dyn Layout>() + .unwrap() + .layout(&mut vt, styles, regions) + } + + cached(self, vt.world, styles, regions) } } @@ -199,7 +214,7 @@ impl Regions { /// Realize into a node that is capable of root-level layout. fn realize_root<'a>( - world: Tracked<'a, dyn World>, + vt: &mut Vt, scratch: &'a Scratch<'a>, content: &'a Content, styles: StyleChain<'a>, @@ -208,7 +223,7 @@ fn realize_root<'a>( return Ok((content.clone(), styles)); } - let mut builder = Builder::new(world, &scratch, true); + let mut builder = Builder::new(vt, &scratch, true); builder.accept(content, styles)?; builder.interrupt_page(Some(styles))?; let (pages, shared) = builder.doc.unwrap().pages.finish(); @@ -217,7 +232,7 @@ fn realize_root<'a>( /// Realize into a node that is capable of block-level layout. fn realize_block<'a>( - world: Tracked<'a, dyn World>, + vt: &mut Vt, scratch: &'a Scratch<'a>, content: &'a Content, styles: StyleChain<'a>, @@ -226,7 +241,7 @@ fn realize_block<'a>( return Ok((content.clone(), styles)); } - let mut builder = Builder::new(world, &scratch, false); + let mut builder = Builder::new(vt, &scratch, false); builder.accept(content, styles)?; builder.interrupt_par()?; let (children, shared) = builder.flow.0.finish(); @@ -234,9 +249,9 @@ fn realize_block<'a>( } /// Builds a document or a flow node from content. -struct Builder<'a> { - /// The core context. - world: Tracked<'a, dyn World>, +struct Builder<'a, 'v, 't> { + /// The virtual typesetter. + vt: &'v mut Vt<'t>, /// Scratch arenas for building. scratch: &'a Scratch<'a>, /// The current document building state. @@ -258,10 +273,10 @@ struct Scratch<'a> { content: Arena<Content>, } -impl<'a> Builder<'a> { - fn new(world: Tracked<'a, dyn World>, scratch: &'a Scratch<'a>, top: bool) -> Self { +impl<'a, 'v, 't> Builder<'a, 'v, 't> { + fn new(vt: &'v mut Vt<'t>, scratch: &'a Scratch<'a>, top: bool) -> Self { Self { - world, + vt, scratch, doc: top.then(|| DocBuilder::default()), flow: FlowBuilder::default(), @@ -286,7 +301,7 @@ impl<'a> Builder<'a> { return Ok(()); } - if let Some(realized) = realize(self.world, content, styles)? { + if let Some(realized) = realize(self.vt, content, styles)? { let stored = self.scratch.content.alloc(realized); return self.accept(stored, styles); } diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs index c688dd47..5b36c8c3 100644 --- a/library/src/layout/pad.rs +++ b/library/src/layout/pad.rs @@ -28,14 +28,14 @@ impl PadNode { impl Layout for PadNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { // Layout child into padded regions. let padding = self.padding.resolve(styles); let pod = regions.map(|size| shrink(size, padding)); - let mut fragment = self.child.layout(world, styles, &pod)?; + let mut fragment = self.child.layout(vt, styles, &pod)?; for frame in &mut fragment { // Apply the padding inversely such that the grown size padded diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index 9fe608ee..08411ad1 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -57,7 +57,7 @@ impl PageNode { /// Layout the page run into a sequence of frames, one per page. pub fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, mut page: usize, styles: StyleChain, ) -> SourceResult<Fragment> { @@ -97,7 +97,7 @@ impl PageNode { // Layout the child. let regions = Regions::repeat(size, size, size.map(Abs::is_finite)); - let mut fragment = child.layout(world, styles, ®ions)?; + let mut fragment = child.layout(vt, styles, ®ions)?; let header = styles.get(Self::HEADER); let footer = styles.get(Self::FOOTER); @@ -116,9 +116,9 @@ impl PageNode { (foreground, Point::zero(), size), (background, Point::zero(), size), ] { - if let Some(content) = marginal.resolve(world, page)? { + if let Some(content) = marginal.resolve(vt, page)? { let pod = Regions::one(area, area, Axes::splat(true)); - let sub = content.layout(world, styles, &pod)?.into_frame(); + let sub = content.layout(vt, styles, &pod)?.into_frame(); if std::ptr::eq(marginal, background) { frame.prepend_frame(pos, sub); } else { @@ -169,17 +169,13 @@ pub enum Marginal { impl Marginal { /// Resolve the marginal based on the page number. - pub fn resolve( - &self, - world: Tracked<dyn World>, - page: usize, - ) -> SourceResult<Option<Content>> { + pub fn resolve(&self, vt: &Vt, page: usize) -> SourceResult<Option<Content>> { Ok(match self { Self::None => None, Self::Content(content) => Some(content.clone()), Self::Func(func, span) => { let args = Args::new(*span, [Value::Int(page as i64)]); - Some(func.call_detached(world, args)?.display()) + Some(func.call_detached(vt.world(), args)?.display()) } }) } diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index 82bea1b5..e96845dd 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -44,27 +44,43 @@ impl ParNode { impl ParNode { /// Layout the paragraph into a collection of lines. - #[comemo::memoize] pub fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, - regions: &Regions, consecutive: bool, + width: Abs, + base: Size, + expand: bool, ) -> SourceResult<Fragment> { - // Collect all text into one string for BiDi analysis. - let (text, segments) = collect(self, &styles, consecutive); - - // Perform BiDi analysis and then prepare paragraph layout by building a - // representation on which we can do line breaking without layouting - // each and every line from scratch. - let p = prepare(world, self, &text, segments, regions, styles)?; - - // Break the paragraph into lines. - let lines = linebreak(&p, regions.first.x); + #[comemo::memoize] + fn cached( + par: &ParNode, + world: Tracked<dyn World>, + styles: StyleChain, + consecutive: bool, + width: Abs, + base: Size, + expand: bool, + ) -> SourceResult<Fragment> { + let mut vt = Vt { world }; + + // Collect all text into one string for BiDi analysis. + let (text, segments) = collect(par, &styles, consecutive); + + // Perform BiDi analysis and then prepare paragraph layout by building a + // representation on which we can do line breaking without layouting + // each and every line from scratch. + let p = prepare(&mut vt, par, &text, segments, styles, width, base)?; + + // Break the paragraph into lines. + let lines = linebreak(&vt, &p, width); + + // Stack the lines into one frame per region. + finalize(&mut vt, &p, &lines, width, base, expand) + } - // Stack the lines into one frame per region. - finalize(&p, &lines, regions) + cached(self, vt.world, styles, consecutive, width, base, expand) } } @@ -143,8 +159,6 @@ const NODE_REPLACE: char = '\u{FFFC}'; // Object Replacement Character /// Only when a line break falls onto a text index that is not safe-to-break per /// rustybuzz, we have to reshape that portion. struct Preparation<'a> { - /// The compilation environment. - world: Tracked<'a, dyn World>, /// Bidirectional text embedding levels for the paragraph. bidi: BidiInfo<'a>, /// Text runs, spacing and layouted nodes. @@ -470,12 +484,13 @@ fn collect<'a>( /// Prepare paragraph layout by shaping the whole paragraph and layouting all /// contained inline-level content. fn prepare<'a>( - world: Tracked<'a, dyn World>, + vt: &mut Vt, par: &'a ParNode, text: &'a str, segments: Vec<(Segment<'a>, StyleChain<'a>)>, - regions: &Regions, styles: StyleChain<'a>, + width: Abs, + base: Size, ) -> SourceResult<Preparation<'a>> { let bidi = BidiInfo::new( text, @@ -494,11 +509,11 @@ fn prepare<'a>( let end = cursor + segment.len(); match segment { Segment::Text(_) => { - shape_range(&mut items, world, &bidi, cursor..end, styles); + shape_range(&mut items, vt, &bidi, cursor..end, styles); } Segment::Spacing(spacing) => match spacing { Spacing::Relative(v) => { - let resolved = v.resolve(styles).relative_to(regions.base.x); + let resolved = v.resolve(styles).relative_to(base.x); items.push(Item::Absolute(resolved)); } Spacing::Fractional(v) => { @@ -509,9 +524,9 @@ fn prepare<'a>( if let Some(repeat) = inline.to::<RepeatNode>() { items.push(Item::Repeat(repeat, styles)); } else { - let size = Size::new(regions.first.x, regions.base.y); - let pod = Regions::one(size, regions.base, Axes::splat(false)); - let mut frame = inline.layout(world, styles, &pod)?.into_frame(); + let size = Size::new(width, base.y); + let pod = Regions::one(size, base, Axes::splat(false)); + let mut frame = inline.layout(vt, styles, &pod)?.into_frame(); frame.translate(Point::with_y(styles.get(TextNode::BASELINE))); items.push(Item::Frame(frame)); } @@ -522,7 +537,6 @@ fn prepare<'a>( } Ok(Preparation { - world, bidi, items, styles, @@ -537,14 +551,14 @@ fn prepare<'a>( /// items for them. fn shape_range<'a>( items: &mut Vec<Item<'a>>, - world: Tracked<dyn World>, + vt: &Vt, bidi: &BidiInfo<'a>, range: Range, styles: StyleChain<'a>, ) { let mut process = |text, level: BidiLevel| { let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL }; - let shaped = shape(world, text, styles, dir); + let shaped = shape(vt, text, styles, dir); items.push(Item::Text(shaped)); }; @@ -601,7 +615,7 @@ fn shared_get<'a, K: Key>( } /// Find suitable linebreaks. -fn linebreak<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { +fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { let linebreaks = p.styles.get(ParNode::LINEBREAKS).unwrap_or_else(|| { if p.styles.get(ParNode::JUSTIFY) { Linebreaks::Optimized @@ -611,22 +625,22 @@ fn linebreak<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { }); match linebreaks { - Linebreaks::Simple => linebreak_simple(p, width), - Linebreaks::Optimized => linebreak_optimized(p, width), + Linebreaks::Simple => linebreak_simple(vt, p, width), + Linebreaks::Optimized => linebreak_optimized(vt, p, width), } } /// Perform line breaking in simple first-fit style. This means that we build /// lines greedily, always taking the longest possible line. This may lead to /// very unbalanced line, but is fast and simple. -fn linebreak_simple<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { +fn linebreak_simple<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { let mut lines = vec![]; let mut start = 0; let mut last = None; for (end, mandatory, hyphen) in breakpoints(p) { // Compute the line and its size. - let mut attempt = line(p, start..end, mandatory, hyphen); + let mut attempt = line(vt, p, start..end, mandatory, hyphen); // If the line doesn't fit anymore, we push the last fitting attempt // into the stack and rebuild the line from the attempt's end. The @@ -635,7 +649,7 @@ fn linebreak_simple<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { if let Some((last_attempt, last_end)) = last.take() { lines.push(last_attempt); start = last_end; - attempt = line(p, start..end, mandatory, hyphen); + attempt = line(vt, p, start..end, mandatory, hyphen); } } @@ -675,7 +689,7 @@ fn linebreak_simple<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { /// computed and stored in dynamic programming table) is minimal. The final /// result is simply the layout determined for the last breakpoint at the end of /// text. -fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { +fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> { /// The cost of a line or paragraph layout. type Cost = f64; @@ -698,7 +712,7 @@ fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> let mut table = vec![Entry { pred: 0, total: 0.0, - line: line(p, 0..0, false, false), + line: line(vt, p, 0..0, false, false), }]; let em = p.styles.get(TextNode::SIZE); @@ -712,7 +726,7 @@ fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> for (i, pred) in table.iter_mut().enumerate().skip(active) { // Layout the line. let start = pred.line.end; - let attempt = line(p, start..end, mandatory, hyphen); + let attempt = line(vt, p, start..end, mandatory, hyphen); // Determine how much the line's spaces would need to be stretched // to make it the desired width. @@ -787,7 +801,7 @@ fn linebreak_optimized<'a>(p: &'a Preparation<'a>, width: Abs) -> Vec<Line<'a>> /// Returns for each breakpoint the text index, whether the break is mandatory /// (after `\n`) and whether a hyphen is required (when breaking inside of a /// word). -fn breakpoints<'a>(p: &'a Preparation) -> Breakpoints<'a> { +fn breakpoints<'a>(p: &'a Preparation<'a>) -> Breakpoints<'a> { Breakpoints { p, linebreaks: LineBreakIterator::new(p.bidi.text), @@ -885,6 +899,7 @@ impl Breakpoints<'_> { /// Create a line which spans the given range. fn line<'a>( + vt: &Vt, p: &'a Preparation, mut range: Range, mandatory: bool, @@ -940,9 +955,9 @@ fn line<'a>( if hyphen || start + shaped.text.len() > range.end { if hyphen || start < range.end || before.is_empty() { let shifted = start - base..range.end - base; - let mut reshaped = shaped.reshape(p.world, shifted); + let mut reshaped = shaped.reshape(vt, shifted); if hyphen || shy { - reshaped.push_hyphen(p.world); + reshaped.push_hyphen(vt); } width += reshaped.width; last = Some(Item::Text(reshaped)); @@ -963,7 +978,7 @@ fn line<'a>( if range.start + shaped.text.len() > end { if range.start < end { let shifted = range.start - base..end - base; - let reshaped = shaped.reshape(p.world, shifted); + let reshaped = shaped.reshape(vt, shifted); width += reshaped.width; first = Some(Item::Text(reshaped)); } @@ -992,27 +1007,35 @@ fn line<'a>( /// Combine layouted lines into one frame per region. fn finalize( + vt: &mut Vt, p: &Preparation, lines: &[Line], - regions: &Regions, + mut width: Abs, + base: Size, + expand: bool, ) -> SourceResult<Fragment> { // Determine the paragraph's width: Full width of the region if we // should expand or there's fractional spacing, fit-to-width otherwise. - let mut width = regions.first.x; - if !regions.expand.x && lines.iter().all(|line| line.fr().is_zero()) { + if !expand && lines.iter().all(|line| line.fr().is_zero()) { width = lines.iter().map(|line| line.width).max().unwrap_or_default(); } // Stack the lines into one frame per region. lines .iter() - .map(|line| commit(p, line, regions.base, width)) + .map(|line| commit(vt, p, line, base, width)) .collect::<SourceResult<_>>() .map(Fragment::frames) } /// Commit to a line and build its frame. -fn commit(p: &Preparation, line: &Line, base: Size, width: Abs) -> SourceResult<Frame> { +fn commit( + vt: &mut Vt, + p: &Preparation, + line: &Line, + base: Size, + width: Abs, +) -> SourceResult<Frame> { let mut remaining = width - line.width; let mut offset = Abs::zero(); @@ -1079,7 +1102,7 @@ fn commit(p: &Preparation, line: &Line, base: Size, width: Abs) -> SourceResult< offset += v.share(fr, remaining); } Item::Text(shaped) => { - let frame = shaped.build(p.world, justification); + let frame = shaped.build(vt, justification); push(&mut offset, frame); } Item::Frame(frame) => { @@ -1090,7 +1113,7 @@ fn commit(p: &Preparation, line: &Line, base: Size, width: Abs) -> SourceResult< let fill = Fr::one().share(fr, remaining); let size = Size::new(fill, base.y); let pod = Regions::one(size, base, Axes::new(false, false)); - let frame = repeat.layout(p.world, *styles, &pod)?.into_frame(); + let frame = repeat.layout(vt, *styles, &pod)?.into_frame(); let width = frame.width(); let count = (fill / width).floor(); let remaining = fill % width; diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs index 215b5b9f..f95aff6a 100644 --- a/library/src/layout/place.rs +++ b/library/src/layout/place.rs @@ -19,7 +19,7 @@ impl PlaceNode { impl Layout for PlaceNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { @@ -33,7 +33,7 @@ impl Layout for PlaceNode { Regions::one(regions.base, regions.base, expand) }; - let mut frame = self.0.layout(world, styles, &pod)?.into_frame(); + let mut frame = self.0.layout(vt, styles, &pod)?.into_frame(); // If expansion is off, zero all sizes so that we don't take up any // space in our parent. Otherwise, respect the expand settings. diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs index d9323e1d..a0fceee9 100644 --- a/library/src/layout/repeat.rs +++ b/library/src/layout/repeat.rs @@ -14,11 +14,11 @@ impl RepeatNode { impl Layout for RepeatNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { - self.0.layout(world, styles, regions) + self.0.layout(vt, styles, regions) } } diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index c1073b26..5d0b072f 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -29,7 +29,7 @@ impl StackNode { impl Layout for StackNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { @@ -49,7 +49,7 @@ impl Layout for StackNode { layouter.layout_spacing(kind); } - layouter.layout_block(world, block, styles)?; + layouter.layout_block(vt, block, styles)?; deferred = self.spacing; } } @@ -170,7 +170,7 @@ impl<'a> StackLayouter<'a> { /// Layout an arbitrary block. fn layout_block( &mut self, - world: Tracked<dyn World>, + vt: &mut Vt, block: &Content, styles: StyleChain, ) -> SourceResult<()> { @@ -195,7 +195,7 @@ impl<'a> StackLayouter<'a> { self.dir.start().into() }); - let fragment = block.layout(world, styles, &self.regions)?; + let fragment = block.layout(vt, styles, &self.regions)?; let len = fragment.len(); for (i, frame) in fragment.into_iter().enumerate() { // Grow our size, shrink the region and save the frame for later. diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs index cfc4ac83..16ee3316 100644 --- a/library/src/layout/transform.rs +++ b/library/src/layout/transform.rs @@ -27,11 +27,11 @@ impl MoveNode { impl Layout for MoveNode { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { - let mut fragment = self.child.layout(world, styles, regions)?; + let mut fragment = self.child.layout(vt, styles, regions)?; for frame in &mut fragment { let delta = self.delta.resolve(styles); let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s)); @@ -85,11 +85,11 @@ impl<const T: TransformKind> TransformNode<T> { impl<const T: TransformKind> Layout for TransformNode<T> { fn layout( &self, - world: Tracked<dyn World>, + vt: &mut Vt, styles: StyleChain, regions: &Regions, ) -> SourceResult<Fragment> { - let mut fragment = self.child.layout(world, styles, regions)?; + let mut fragment = self.child.layout(vt, styles, regions)?; for frame in &mut fragment { let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); |
