diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-01-19 21:50:20 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-01-19 21:53:24 +0100 |
| commit | 95e6b078fecddeaa3d6f2c920b617201b74bf01e (patch) | |
| tree | 1c03b0b16d614a5a2350dccf71a1eb1e34f9a812 /src/layout | |
| parent | 277f2d2176f5e98305870f90b16af3feae1bb3d1 (diff) | |
Move to non-fatal errors 🪂 [WIP]
- Dynamic models instead of SyntaxTrees
- No more ParseResult/LayoutResult
- Errors and Decorations which are propagated to parent contexts
- Models are finally clonable
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/line.rs | 72 | ||||
| -rw-r--r-- | src/layout/mod.rs | 42 | ||||
| -rw-r--r-- | src/layout/model.rs | 193 | ||||
| -rw-r--r-- | src/layout/stack.rs | 55 | ||||
| -rw-r--r-- | src/layout/text.rs | 59 | ||||
| -rw-r--r-- | src/layout/tree.rs | 163 |
6 files changed, 310 insertions, 274 deletions
diff --git a/src/layout/line.rs b/src/layout/line.rs index c4205e81..f7777ae0 100644 --- a/src/layout/line.rs +++ b/src/layout/line.rs @@ -67,18 +67,21 @@ impl LineLayouter { } /// Add a layout to the run. - pub fn add(&mut self, layout: Layout) -> LayoutResult<()> { + pub fn add(&mut self, layout: Layout) { let axes = self.ctx.axes; if let Some(alignment) = self.run.alignment { if layout.alignment.secondary != alignment.secondary { - if self.stack.is_fitting_alignment(layout.alignment) { - self.finish_line()?; + // TODO: Issue warning for non-fitting alignment in + // non-repeating context. + let fitting = self.stack.is_fitting_alignment(layout.alignment); + if !fitting && self.ctx.repeat { + self.finish_space(true); } else { - self.finish_space(true)?; + self.finish_line(); } } else if layout.alignment.primary < alignment.primary { - self.finish_line()?; + self.finish_line(); } else if layout.alignment.primary > alignment.primary { let mut rest_run = LineRun::new(); @@ -92,7 +95,7 @@ impl LineLayouter { rest_run.size.y = self.run.size.y; - self.finish_line()?; + self.finish_line(); self.stack.add_spacing(-rest_run.size.y, SpacingKind::Hard); self.run = rest_run; @@ -105,16 +108,14 @@ impl LineLayouter { let size = layout.dimensions.generalized(axes); - while !self.usable().fits(size) { + if !self.usable().fits(size) { if !self.line_is_empty() { - self.finish_line()?; - } else { - if self.stack.space_is_last() && self.stack.space_is_empty() { - error!("cannot fit box of size {} into usable size of {}", - layout.dimensions, self.usable()); - } + self.finish_line(); + } - self.finish_space(true)?; + // TODO: Issue warning about overflow if there is overflow. + if !self.usable().fits(size) { + self.stack.skip_to_fitting_space(layout.dimensions); } } @@ -124,18 +125,15 @@ impl LineLayouter { self.run.size.x += size.x; self.run.size.y.max_eq(size.y); self.run.last_spacing = LastSpacing::None; - - Ok(()) } /// Add multiple layouts to the run. /// /// This function simply calls `add` repeatedly for each layout. - pub fn add_multiple(&mut self, layouts: MultiLayout) -> LayoutResult<()> { + pub fn add_multiple(&mut self, layouts: MultiLayout) { for layout in layouts { - self.add(layout)?; + self.add(layout); } - Ok(()) } /// The remaining usable size in the run. @@ -180,20 +178,16 @@ impl LineLayouter { } /// Finish the run and add secondary spacing to the underlying stack. - pub fn add_secondary_spacing( - &mut self, - spacing: Size, - kind: SpacingKind - ) -> LayoutResult<()> { - self.finish_line_if_not_empty()?; - Ok(self.stack.add_spacing(spacing, kind)) + pub fn add_secondary_spacing(&mut self, spacing: Size, kind: SpacingKind) { + self.finish_line_if_not_empty(); + self.stack.add_spacing(spacing, kind) } /// Change the layouting axes used by this layouter. - pub fn set_axes(&mut self, axes: LayoutAxes) -> LayoutResult<()> { - self.finish_line_if_not_empty()?; + pub fn set_axes(&mut self, axes: LayoutAxes) { + self.finish_line_if_not_empty(); self.ctx.axes = axes; - Ok(self.stack.set_axes(axes)) + self.stack.set_axes(axes) } /// Change the layouting spaces to use. @@ -224,19 +218,19 @@ impl LineLayouter { } /// Finish the last line and compute the final multi-layout. - pub fn finish(mut self) -> LayoutResult<MultiLayout> { - self.finish_line_if_not_empty()?; + pub fn finish(mut self) -> MultiLayout { + self.finish_line_if_not_empty(); self.stack.finish() } /// Finish the currently active space and start a new one. - pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> { - self.finish_line_if_not_empty()?; + pub fn finish_space(&mut self, hard: bool) { + self.finish_line_if_not_empty(); self.stack.finish_space(hard) } /// Add the current line to the stack and start a new line. - pub fn finish_line(&mut self) -> LayoutResult<()> { + pub fn finish_line(&mut self) { let mut actions = LayoutActions::new(); let layouts = std::mem::replace(&mut self.run.layouts, vec![]); @@ -257,21 +251,17 @@ impl LineLayouter { alignment: self.run.alignment .unwrap_or(LayoutAlignment::new(Origin, Origin)), actions: actions.to_vec(), - })?; + }); self.run = LineRun::new(); - self.stack.add_spacing(self.ctx.line_spacing, LINE_KIND); - - Ok(()) + self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE); } /// Finish the current line if it is not empty. - fn finish_line_if_not_empty(&mut self) -> LayoutResult<()> { + fn finish_line_if_not_empty(&mut self) { if !self.line_is_empty() { self.finish_line() - } else { - Ok(()) } } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 49551945..75d34409 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -4,23 +4,20 @@ use std::io::{self, Write}; use smallvec::SmallVec; use toddle::query::{SharedFontLoader, FontIndex}; +use crate::error::Error; +use crate::syntax::SpanVec; use crate::size::{Size, Size2D, SizeBox}; use crate::style::LayoutStyle; mod actions; -mod tree; +mod model; mod line; mod stack; mod text; /// Common types for layouting. pub mod prelude { - pub use super::{ - layout, LayoutResult, - MultiLayout, Layout, LayoutContext, LayoutSpaces, LayoutSpace, - LayoutExpansion, LayoutAxes, GenericAxis, SpecificAxis, Direction, - LayoutAlignment, Alignment, SpacingKind, - }; + pub use super::*; pub use GenericAxis::*; pub use SpecificAxis::*; pub use Direction::*; @@ -29,7 +26,7 @@ pub mod prelude { /// Different kinds of layouters (fully re-exported). pub mod layouters { - pub use super::tree::layout; + pub use super::model::layout; pub use super::line::{LineLayouter, LineContext}; pub use super::stack::{StackLayouter, StackContext}; pub use super::text::{layout_text, TextContext}; @@ -40,8 +37,19 @@ pub use self::layouters::*; pub use self::prelude::*; -/// The result type for layouting. -pub type LayoutResult<T> = crate::TypesetResult<T>; +pub struct Layouted<T> { + pub output: T, + pub errors: SpanVec<Error>, +} + +impl<T> Layouted<T> { + pub fn map<F, U>(self, f: F) -> Layouted<U> where F: FnOnce(T) -> U { + Layouted { + output: f(self.output), + errors: self.errors, + } + } +} /// A collection of layouts. pub type MultiLayout = Vec<Layout>; @@ -361,14 +369,16 @@ pub enum SpacingKind { Soft(u32), } -/// The standard spacing kind used for paragraph spacing. -const PARAGRAPH_KIND: SpacingKind = SpacingKind::Soft(1); +impl SpacingKind { + /// The standard spacing kind used for paragraph spacing. + pub const PARAGRAPH: SpacingKind = SpacingKind::Soft(1); -/// The standard spacing kind used for line spacing. -const LINE_KIND: SpacingKind = SpacingKind::Soft(2); + /// The standard spacing kind used for line spacing. + pub const LINE: SpacingKind = SpacingKind::Soft(2); -/// The standard spacing kind used for word spacing. -const WORD_KIND: SpacingKind = SpacingKind::Soft(1); + /// The standard spacing kind used for word spacing. + pub const WORD: SpacingKind = SpacingKind::Soft(1); +} /// The last appeared spacing. #[derive(Debug, Copy, Clone, PartialEq)] diff --git a/src/layout/model.rs b/src/layout/model.rs new file mode 100644 index 00000000..bcec5ceb --- /dev/null +++ b/src/layout/model.rs @@ -0,0 +1,193 @@ +use std::pin::Pin; +use std::future::Future; +use smallvec::smallvec; + +use crate::error::Error; +use crate::func::Command; +use crate::syntax::{Model, DynFuture, SyntaxModel, Node}; +use crate::syntax::{SpanVec, Spanned, Span, offset_spans}; +use super::*; + + +pub async fn layout( + model: &SyntaxModel, + ctx: LayoutContext<'_, '_> +) -> Layouted<MultiLayout> { + let mut layouter = ModelLayouter::new(ctx); + layouter.layout_syntax_model(model).await; + layouter.finish() +} + +#[derive(Debug, Clone)] +struct ModelLayouter<'a, 'p> { + ctx: LayoutContext<'a, 'p>, + layouter: LineLayouter, + style: LayoutStyle, + errors: SpanVec<Error>, +} + +impl<'a, 'p> ModelLayouter<'a, 'p> { + /// Create a new syntax tree layouter. + fn new(ctx: LayoutContext<'a, 'p>) -> ModelLayouter<'a, 'p> { + ModelLayouter { + layouter: LineLayouter::new(LineContext { + spaces: ctx.spaces.clone(), + axes: ctx.axes, + alignment: ctx.alignment, + repeat: ctx.repeat, + debug: ctx.debug, + line_spacing: ctx.style.text.line_spacing(), + }), + style: ctx.style.clone(), + ctx, + errors: vec![], + } + } + + fn layout<'r>( + &'r mut self, + model: Spanned<&'r dyn Model> + ) -> DynFuture<'r, ()> { Box::pin(async move { + let layouted = model.v.layout(LayoutContext { + style: &self.style, + spaces: self.layouter.remaining(), + nested: true, + debug: false, + .. self.ctx + }).await; + + let commands = layouted.output; + self.errors.extend(offset_spans(layouted.errors, model.span.start)); + + for command in commands { + self.execute_command(command, model.span); + } + }) } + + fn execute_command<'r>( + &'r mut self, + command: Command<'r>, + model_span: Span, + ) -> DynFuture<'r, ()> { Box::pin(async move { + use Command::*; + + match command { + LayoutSyntaxModel(model) => self.layout_syntax_model(model).await, + + Add(layout) => self.layouter.add(layout), + AddMultiple(layouts) => self.layouter.add_multiple(layouts), + SpacingFunc(space, kind, axis) => match axis { + Primary => self.layouter.add_primary_spacing(space, kind), + Secondary => self.layouter.add_secondary_spacing(space, kind), + } + + FinishLine => self.layouter.finish_line(), + FinishSpace => self.layouter.finish_space(true), + BreakParagraph => self.layout_paragraph(), + BreakPage => { + if self.ctx.nested { + self.errors.push(Spanned::new( + Error::new( "page break cannot be issued from nested context"), + model_span, + )); + } else { + self.layouter.finish_space(true) + } + } + + SetTextStyle(style) => { + self.layouter.set_line_spacing(style.line_spacing()); + self.style.text = style; + } + SetPageStyle(style) => { + if self.ctx.nested { + self.errors.push(Spanned::new( + Error::new("page style cannot be changed from nested context"), + model_span, + )); + } else { + self.style.page = style; + + let margins = style.margins(); + self.ctx.base = style.dimensions.unpadded(margins); + self.layouter.set_spaces(smallvec![ + LayoutSpace { + dimensions: style.dimensions, + padding: margins, + expansion: LayoutExpansion::new(true, true), + } + ], true); + } + } + + SetAlignment(alignment) => self.ctx.alignment = alignment, + SetAxes(axes) => { + self.layouter.set_axes(axes); + self.ctx.axes = axes; + } + } + }) } + + fn layout_syntax_model<'r>( + &'r mut self, + model: &'r SyntaxModel + ) -> DynFuture<'r, ()> { Box::pin(async move { + use Node::*; + + for node in &model.nodes { + match &node.v { + Space => self.layout_space(), + Newline => self.layout_paragraph(), + Text(text) => self.layout_text(text).await, + + ToggleItalic => self.style.text.variant.style.toggle(), + ToggleBolder => { + let fac = if self.style.text.bolder { -1 } else { 1 }; + self.style.text.variant.weight.0 += 300 * fac; + self.style.text.bolder = !self.style.text.bolder; + } + ToggleMonospace => { + let list = &mut self.style.text.fallback.list; + match list.get(0).map(|s| s.as_str()) { + Some("monospace") => { list.remove(0); }, + _ => list.insert(0, "monospace".to_string()), + } + } + + Node::Model(model) => { + self.layout(Spanned::new(model.as_ref(), node.span)).await; + } + } + } + }) } + + async fn layout_text(&mut self, text: &str) { + self.layouter.add(layout_text(text, TextContext { + loader: &self.ctx.loader, + style: &self.style.text, + axes: self.ctx.axes, + alignment: self.ctx.alignment, + }).await) + } + + fn layout_space(&mut self) { + self.layouter.add_primary_spacing( + self.style.text.word_spacing(), + SpacingKind::WORD, + ); + } + + fn layout_paragraph(&mut self) { + self.layouter.add_secondary_spacing( + self.style.text.paragraph_spacing(), + SpacingKind::PARAGRAPH, + ); + } + + fn finish(self) -> Layouted<MultiLayout> { + Layouted { + output: self.layouter.finish(), + errors: self.errors, + } + } +} diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 80d57424..96b44d04 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -68,10 +68,12 @@ impl StackLayouter { } /// Add a layout to the stack. - pub fn add(&mut self, layout: Layout) -> LayoutResult<()> { - // If the alignment cannot be fit in this space, finish it. - if !self.update_rulers(layout.alignment) { - self.finish_space(true)?; + pub fn add(&mut self, layout: Layout) { + // If the alignment cannot be fitted in this space, finish it. + // TODO: Issue warning for non-fitting alignment in + // non-repeating context. + if !self.update_rulers(layout.alignment) && self.ctx.repeat { + self.finish_space(true); } // Now, we add a possibly cached soft space. If the secondary alignment @@ -81,14 +83,9 @@ impl StackLayouter { self.add_spacing(spacing, SpacingKind::Hard); } - // Find the first space that fits the layout. - while !self.space.usable.fits(layout.dimensions) { - if self.space_is_last() && self.space_is_empty() { - error!("cannot fit box of size {} into usable size of {}", - layout.dimensions, self.space.usable); - } - - self.finish_space(true)?; + // TODO: Issue warning about overflow if there is overflow. + if !self.space.usable.fits(layout.dimensions) && self.ctx.repeat { + self.skip_to_fitting_space(layout.dimensions); } // Change the usable space and size of the space. @@ -98,18 +95,15 @@ impl StackLayouter { // again. self.space.layouts.push((self.ctx.axes, layout)); self.space.last_spacing = LastSpacing::None; - - Ok(()) } /// Add multiple layouts to the stack. /// /// This function simply calls `add` repeatedly for each layout. - pub fn add_multiple(&mut self, layouts: MultiLayout) -> LayoutResult<()> { + pub fn add_multiple(&mut self, layouts: MultiLayout) { for layout in layouts { - self.add(layout)?; + self.add(layout); } - Ok(()) } /// Add secondary spacing to the stack. @@ -215,6 +209,19 @@ impl StackLayouter { } } + /// Move to the first space that can fit the given dimensions or do nothing + /// if no space is capable of that. + pub fn skip_to_fitting_space(&mut self, dimensions: Size2D) { + let start = self.next_space(); + for (index, space) in self.ctx.spaces[start..].iter().enumerate() { + if space.usable().fits(dimensions) { + self.finish_space(true); + self.start_space(start + index, true); + return; + } + } + } + /// The remaining unpadded, unexpanding spaces. If a multi-layout is laid /// out into these spaces, it will fit into this stack. pub fn remaining(&self) -> LayoutSpaces { @@ -251,19 +258,15 @@ impl StackLayouter { } /// Compute the finished multi-layout. - pub fn finish(mut self) -> LayoutResult<MultiLayout> { + pub fn finish(mut self) -> MultiLayout { if self.space.hard || !self.space_is_empty() { - self.finish_space(false)?; + self.finish_space(false); } - Ok(self.layouts) + self.layouts } /// Finish the current space and start a new one. - pub fn finish_space(&mut self, hard: bool) -> LayoutResult<()> { - if !self.ctx.repeat && hard { - error!("cannot create new space in a non-repeating context"); - } - + pub fn finish_space(&mut self, hard: bool) { let space = self.ctx.spaces[self.space.index]; // ------------------------------------------------------------------ // @@ -376,7 +379,7 @@ impl StackLayouter { // ------------------------------------------------------------------ // // Step 5: Start the next space. - Ok(self.start_space(self.next_space(), hard)) + self.start_space(self.next_space(), hard) } /// Start a new space with the given index. diff --git a/src/layout/text.rs b/src/layout/text.rs index a66e04a2..16ae93da 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -6,6 +6,14 @@ use crate::style::TextStyle; use super::*; +/// Layouts text into a box. +/// +/// There is no complex layout involved. The text is simply laid out left- +/// to-right using the correct font for each character. +pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> Layout { + TextLayouter::new(text, ctx).layout().await +} + /// The context for text layouting. /// /// See [`LayoutContext`] for details about the fields. @@ -17,14 +25,6 @@ pub struct TextContext<'a, 'p> { pub alignment: LayoutAlignment, } -/// Layouts text into a box. -/// -/// There is no complex layout involved. The text is simply laid out left- -/// to-right using the correct font for each character. -pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> LayoutResult<Layout> { - TextLayouter::new(text, ctx).layout().await -} - /// Layouts text into boxes. struct TextLayouter<'a, 'p> { ctx: TextContext<'a, 'p>, @@ -49,14 +49,14 @@ impl<'a, 'p> TextLayouter<'a, 'p> { } /// Layout the text - async fn layout(mut self) -> LayoutResult<Layout> { + async fn layout(mut self) -> Layout { if self.ctx.axes.primary.is_positive() { for c in self.text.chars() { - self.layout_char(c).await?; + self.layout_char(c).await; } } else { for c in self.text.chars().rev() { - self.layout_char(c).await?; + self.layout_char(c).await; } } @@ -64,16 +64,20 @@ impl<'a, 'p> TextLayouter<'a, 'p> { self.actions.add(LayoutAction::WriteText(self.buffer)); } - Ok(Layout { + Layout { dimensions: Size2D::new(self.width, self.ctx.style.font_size()), alignment: self.ctx.alignment, actions: self.actions.to_vec(), - }) + } } /// Layout an individual character. - async fn layout_char(&mut self, c: char) -> LayoutResult<()> { - let (index, char_width) = self.select_font(c).await?; + async fn layout_char(&mut self, c: char) { + let (index, char_width) = match self.select_font(c).await { + Some(selected) => selected, + // TODO: Issue warning about missing character. + None => return, + }; self.width += char_width; @@ -88,13 +92,11 @@ impl<'a, 'p> TextLayouter<'a, 'p> { } self.buffer.push(c); - - Ok(()) } /// Select the best font for a character and return its index along with /// the width of the char in the font. - async fn select_font(&mut self, c: char) -> LayoutResult<(FontIndex, Size)> { + async fn select_font(&mut self, c: char) -> Option<(FontIndex, Size)> { let mut loader = self.ctx.loader.borrow_mut(); let query = FontQuery { @@ -104,26 +106,27 @@ impl<'a, 'p> TextLayouter<'a, 'p> { }; if let Some((font, index)) = loader.get(query).await { - let font_unit_ratio = 1.0 / (font.read_table::<Header>()?.units_per_em as f32); + let header = font.read_table::<Header>().ok()?; + let font_unit_ratio = 1.0 / (header.units_per_em as f32); let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); let glyph = font - .read_table::<CharMap>()? - .get(c) - .expect("select_font: font should have char"); + .read_table::<CharMap>() + .ok()? + .get(c)?; let glyph_width = font - .read_table::<HorizontalMetrics>()? - .get(glyph) - .expect("select_font: font should have glyph") + .read_table::<HorizontalMetrics>() + .ok()? + .get(glyph)? .advance_width as f32; let char_width = font_unit_to_size(glyph_width) * self.ctx.style.font_size().to_pt(); - return Ok((index, char_width)); + Some((index, char_width)) + } else { + None } - - error!("no suitable font for character `{}`", c); } } diff --git a/src/layout/tree.rs b/src/layout/tree.rs deleted file mode 100644 index 86b00f22..00000000 --- a/src/layout/tree.rs +++ /dev/null @@ -1,163 +0,0 @@ -use std::pin::Pin; -use std::future::Future; -use smallvec::smallvec; - -use crate::func::Command; -use crate::syntax::{SyntaxTree, Node, FuncCall}; -use super::*; - - -type RecursiveResult<'a, T> = Pin<Box<dyn Future<Output=LayoutResult<T>> + 'a>>; - -/// Layout a syntax tree into a multibox. -pub async fn layout(tree: &SyntaxTree, ctx: LayoutContext<'_, '_>) -> LayoutResult<MultiLayout> { - let mut layouter = TreeLayouter::new(ctx); - layouter.layout(tree).await?; - layouter.finish() -} - -#[derive(Debug, Clone)] -struct TreeLayouter<'a, 'p> { - ctx: LayoutContext<'a, 'p>, - layouter: LineLayouter, - style: LayoutStyle, -} - -impl<'a, 'p> TreeLayouter<'a, 'p> { - /// Create a new syntax tree layouter. - fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> { - TreeLayouter { - layouter: LineLayouter::new(LineContext { - spaces: ctx.spaces.clone(), - axes: ctx.axes, - alignment: ctx.alignment, - repeat: ctx.repeat, - debug: ctx.debug, - line_spacing: ctx.style.text.line_spacing(), - }), - style: ctx.style.clone(), - ctx, - } - } - - fn layout<'b>(&'b mut self, tree: &'b SyntaxTree) -> RecursiveResult<'b, ()> { - Box::pin(async move { - for node in &tree.nodes { - match &node.v { - Node::Text(text) => self.layout_text(text).await?, - - Node::Space => self.layout_space(), - Node::Newline => self.layout_paragraph()?, - - Node::ToggleItalic => self.style.text.variant.style.toggle(), - Node::ToggleBolder => { - self.style.text.variant.weight.0 += 300 * - if self.style.text.bolder { -1 } else { 1 }; - self.style.text.bolder = !self.style.text.bolder; - } - Node::ToggleMonospace => { - let list = &mut self.style.text.fallback.list; - match list.get(0).map(|s| s.as_str()) { - Some("monospace") => { list.remove(0); }, - _ => list.insert(0, "monospace".to_string()), - } - } - - Node::Func(func) => self.layout_func(func).await?, - } - } - - Ok(()) - }) - } - - async fn layout_text(&mut self, text: &str) -> LayoutResult<()> { - let layout = layout_text(text, TextContext { - loader: &self.ctx.loader, - style: &self.style.text, - axes: self.ctx.axes, - alignment: self.ctx.alignment, - }).await?; - - self.layouter.add(layout) - } - - fn layout_space(&mut self) { - self.layouter.add_primary_spacing(self.style.text.word_spacing(), WORD_KIND); - } - - fn layout_paragraph(&mut self) -> LayoutResult<()> { - self.layouter.add_secondary_spacing(self.style.text.paragraph_spacing(), PARAGRAPH_KIND) - } - - fn layout_func<'b>(&'b mut self, func: &'b FuncCall) -> RecursiveResult<'b, ()> { - Box::pin(async move { - let commands = func.0.layout(LayoutContext { - style: &self.style, - spaces: self.layouter.remaining(), - nested: true, - debug: false, - .. self.ctx - }).await?; - - for command in commands { - use Command::*; - - match command { - LayoutTree(tree) => self.layout(tree).await?, - - Add(layout) => self.layouter.add(layout)?, - AddMultiple(layouts) => self.layouter.add_multiple(layouts)?, - SpacingFunc(space, kind, axis) => match axis { - Primary => self.layouter.add_primary_spacing(space, kind), - Secondary => self.layouter.add_secondary_spacing(space, kind)?, - } - - FinishLine => self.layouter.finish_line()?, - FinishSpace => self.layouter.finish_space(true)?, - BreakParagraph => self.layout_paragraph()?, - BreakPage => { - if self.ctx.nested { - error!("page break cannot be issued from nested context"); - } - - self.layouter.finish_space(true)? - } - - SetTextStyle(style) => { - self.layouter.set_line_spacing(style.line_spacing()); - self.style.text = style; - } - SetPageStyle(style) => { - if self.ctx.nested { - error!("page style cannot be altered in nested context"); - } - - self.style.page = style; - - let margins = style.margins(); - self.ctx.base = style.dimensions.unpadded(margins); - self.layouter.set_spaces(smallvec![ - LayoutSpace { - dimensions: style.dimensions, - padding: margins, - expansion: LayoutExpansion::new(true, true), - } - ], true); - } - SetAlignment(alignment) => self.ctx.alignment = alignment, - SetAxes(axes) => { - self.layouter.set_axes(axes)?; - self.ctx.axes = axes; - } - } - } - - Ok(()) - }) - } - - fn finish(self) -> LayoutResult<MultiLayout> { - self.layouter.finish() - } -} |
