diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/doc.rs | 27 | ||||
| -rw-r--r-- | src/export/pdf.rs | 7 | ||||
| -rw-r--r-- | src/layout/actions.rs | 131 | ||||
| -rw-r--r-- | src/layout/boxed.rs | 14 | ||||
| -rw-r--r-- | src/layout/flex.rs | 9 | ||||
| -rw-r--r-- | src/layout/mod.rs | 66 | ||||
| -rw-r--r-- | src/layout/text.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/macros.rs (renamed from src/error.rs) | 17 | ||||
| -rw-r--r-- | src/size.rs | 20 |
10 files changed, 176 insertions, 119 deletions
@@ -1,7 +1,7 @@ //! Representation of typesetted documents. -use std::io::{self, Write}; -use crate::size::{Size, Size2D}; +use crate::layout::LayoutAction; +use crate::size::Size; /// A complete typesetted document, which can be exported. @@ -21,26 +21,3 @@ pub struct Page { /// Layouting actions specifying how to draw content on the page. pub actions: Vec<LayoutAction>, } - -/// A layouting action. -#[derive(Debug, Clone)] -pub enum LayoutAction { - /// Move to an absolute position. - MoveAbsolute(Size2D), - /// Set the font by index and font size. - SetFont(usize, f32), - /// Write text starting at the current position. - WriteText(String), -} - -impl LayoutAction { - /// Serialize this layout action into a string representation. - pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { - use LayoutAction::*; - match self { - MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()), - SetFont(i, s) => write!(f, "f {} {}", i, s), - WriteText(s) => write!(f, "w {}", s), - } - } -} diff --git a/src/export/pdf.rs b/src/export/pdf.rs index d601f6b3..3a3f2a9c 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -14,7 +14,8 @@ use toddle::font::OwnedFont; use toddle::query::SharedFontLoader; use toddle::Error as FontError; -use crate::doc::{Document, Page as DocPage, LayoutAction}; +use crate::layout::LayoutAction; +use crate::doc::{Document, Page as DocPage}; use crate::size::{Size, Size2D}; @@ -192,16 +193,16 @@ impl<'d, W: Write> PdfEngine<'d, W> { } // Flush the position. - if let Some(pos) = next_pos { + if let Some(pos) = next_pos.take() { let x = pos.x.to_pt(); let y = (page.height - pos.y - Size::pt(active_font.1)).to_pt(); text.tm(1.0, 0.0, 0.0, 1.0, x, y); - next_pos = None; } // Write the text. text.tj(self.fonts[active_font.0].encode_text(&string)?); }, + LayoutAction::DebugBox(_, _) => {}, } } diff --git a/src/layout/actions.rs b/src/layout/actions.rs new file mode 100644 index 00000000..f76b61c3 --- /dev/null +++ b/src/layout/actions.rs @@ -0,0 +1,131 @@ +//! Drawing and cofiguration actions used by layouts. + +use std::fmt::{self, Display, Formatter}; +use std::io::{self, Write}; +use crate::size::Size2D; +use super::boxed::BoxLayout; +use LayoutAction::*; + + +/// A layouting action. +#[derive(Clone)] +pub enum LayoutAction { + /// Move to an absolute position. + MoveAbsolute(Size2D), + /// Set the font by index and font size. + SetFont(usize, f32), + /// Write text starting at the current position. + WriteText(String), + /// Visualize a box for debugging purposes. + /// Arguments are position and size. + DebugBox(Size2D, Size2D), +} + +impl LayoutAction { + /// Serialize this layout action into a string representation. + pub fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { + use LayoutAction::*; + match self { + MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()), + SetFont(i, s) => write!(f, "f {} {}", i, s), + WriteText(s) => write!(f, "w {}", s), + DebugBox(p, s) => write!(f, "b {} {} {} {}", + p.x.to_pt(), p.y.to_pt(), s.x.to_pt(), s.y.to_pt()) + } + } +} + +impl Display for LayoutAction { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use LayoutAction::*; + match self { + MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y), + SetFont(i, s) => write!(f, "font {} {}", i, s), + WriteText(s) => write!(f, "write \"{}\"", s), + DebugBox(p, s) => write!(f, "box {} {}", p, s), + } + } +} + +debug_display!(LayoutAction); + +/// Unifies and otimizes lists of actions. +#[derive(Debug, Clone)] +pub struct LayoutActionList { + pub origin: Size2D, + actions: Vec<LayoutAction>, + active_font: (usize, f32), + next_pos: Option<Size2D>, + next_font: Option<(usize, f32)>, +} + +impl LayoutActionList { + /// Create a new action list. + pub fn new() -> LayoutActionList { + LayoutActionList { + actions: vec![], + origin: Size2D::zero(), + active_font: (std::usize::MAX, 0.0), + next_pos: None, + next_font: None, + } + } + + /// Add an action to the list if it is not useless + /// (like changing to a font that is already active). + pub fn add(&mut self, action: LayoutAction) { + match action { + MoveAbsolute(pos) => self.next_pos = Some(self.origin + pos), + DebugBox(pos, size) => self.actions.push(DebugBox(self.origin + pos, size)), + + SetFont(index, size) if (index, size) != self.active_font => { + self.next_font = Some((index, size)); + }, + + _ => { + if let Some(target) = self.next_pos.take() { + self.actions.push(MoveAbsolute(target)); + } + if let Some((index, size)) = self.next_font.take() { + self.actions.push(SetFont(index, size)); + } + + self.actions.push(action); + }, + } + } + + /// Add a series of actions. + pub fn extend<I>(&mut self, actions: I) where I: IntoIterator<Item=LayoutAction> { + for action in actions.into_iter() { + self.add(action); + } + } + + /// Add all actions from a box layout at a position. A move to the position + /// is generated and all moves inside the box layout are translated as necessary. + pub fn add_box(&mut self, position: Size2D, layout: BoxLayout) { + if let Some(target) = self.next_pos.take() { + self.actions.push(MoveAbsolute(target)); + } + + self.next_pos = Some(position); + self.origin = position; + + if layout.debug_render { + self.actions.push(LayoutAction::DebugBox(position, layout.dimensions)); + } + + self.extend(layout.actions); + } + + /// Whether there are any actions in this list. + pub fn is_empty(&self) -> bool { + self.actions.is_empty() + } + + /// Return the list of actions as a vector. + pub fn into_vec(self) -> Vec<LayoutAction> { + self.actions + } +} diff --git a/src/layout/boxed.rs b/src/layout/boxed.rs index 5bd909d4..7ea9e4fb 100644 --- a/src/layout/boxed.rs +++ b/src/layout/boxed.rs @@ -1,9 +1,9 @@ //! Block-style layouting of boxes. use std::io::{self, Write}; -use crate::doc::{Document, Page, LayoutAction}; +use crate::doc::{Document, Page}; use crate::size::{Size, Size2D}; -use super::{ActionList, LayoutSpace, Alignment, LayoutResult, LayoutError}; +use super::*; /// A box layout has a fixed width and height and composes of actions. @@ -13,6 +13,8 @@ pub struct BoxLayout { pub dimensions: Size2D, /// The actions composing this layout. pub actions: Vec<LayoutAction>, + /// Whether to debug-render this box. + pub debug_render: bool, } impl BoxLayout { @@ -49,7 +51,7 @@ pub struct BoxContext { #[derive(Debug)] pub struct BoxLayouter { pub ctx: BoxContext, - actions: ActionList, + actions: LayoutActionList, dimensions: Size2D, usable: Size2D, cursor: Size2D, @@ -59,9 +61,10 @@ impl BoxLayouter { /// Create a new box layouter. pub fn new(ctx: BoxContext) -> BoxLayouter { let space = ctx.space; + BoxLayouter { ctx, - actions: ActionList::new(), + actions: LayoutActionList::new(), dimensions: match ctx.space.alignment { Alignment::Left => Size2D::zero(), Alignment::Right => Size2D::with_x(space.usable().x), @@ -109,7 +112,7 @@ impl BoxLayouter { /// Add a sublayout at an absolute position. pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) { - self.actions.add_box_absolute(position, layout); + self.actions.add_box(position, layout); } /// Add some space in between two boxes. @@ -148,6 +151,7 @@ impl BoxLayouter { self.ctx.space.dimensions }, actions: self.actions.into_vec(), + debug_render: true, } } diff --git a/src/layout/flex.rs b/src/layout/flex.rs index 8c099553..75674e8f 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -1,7 +1,7 @@ //! Flexible and lazy layouting of boxes. use crate::size::{Size, Size2D}; -use super::{BoxLayout, ActionList, LayoutSpace, Alignment, LayoutResult, LayoutError}; +use super::*; /// A flex layout consists of a yet unarranged list of boxes. @@ -76,7 +76,7 @@ pub struct FlexContext { struct FlexFinisher { units: Vec<FlexUnit>, ctx: FlexContext, - actions: ActionList, + actions: LayoutActionList, dimensions: Size2D, usable: Size2D, cursor: Size2D, @@ -92,7 +92,7 @@ impl FlexFinisher { FlexFinisher { units: layout.units, ctx, - actions: ActionList::new(), + actions: LayoutActionList::new(), dimensions: match ctx.space.alignment { Alignment::Left => Size2D::zero(), Alignment::Right => Size2D::with_x(space.usable().x), @@ -129,6 +129,7 @@ impl FlexFinisher { self.ctx.space.dimensions }, actions: self.actions.into_vec(), + debug_render: true, }) } @@ -187,7 +188,7 @@ impl FlexFinisher { }, }; - self.actions.add_box_absolute(position, layout); + self.actions.add_box(position, layout); } // Stretch the dimensions to at least the line width. diff --git a/src/layout/mod.rs b/src/layout/mod.rs index e5fdc42d..5cc8d8f2 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -6,7 +6,6 @@ use std::mem; use toddle::query::{SharedFontLoader, FontClass}; use toddle::Error as FontError; -use crate::doc::LayoutAction; use crate::size::{Size, Size2D, SizeBox}; use crate::syntax::{SyntaxTree, Node, FuncCall}; use crate::style::TextStyle; @@ -18,6 +17,9 @@ use self::text::TextContext; pub mod text; pub mod boxed; pub mod flex; +mod actions; + +pub use actions::{LayoutAction, LayoutActionList}; /// A collection of layouted content. @@ -159,6 +161,10 @@ impl<'a, 'p> Layouter<'a, 'p> { /// Finish the current flex run and return the resulting box. fn layout_flex(&mut self) -> LayoutResult<()> { + if self.flex_layout.is_empty() { + return Ok(()); + } + let mut layout = FlexLayout::new(); mem::swap(&mut layout, &mut self.flex_layout); @@ -204,64 +210,6 @@ impl<'a, 'p> Layouter<'a, 'p> { } } -/// Manipulates and optimizes a list of actions. -#[derive(Debug, Clone)] -pub struct ActionList { - pub origin: Size2D, - actions: Vec<LayoutAction>, - active_font: (usize, f32), -} - -impl ActionList { - /// Create a new action list. - pub fn new() -> ActionList { - ActionList { - actions: vec![], - origin: Size2D::zero(), - active_font: (std::usize::MAX, 0.0), - } - } - - /// Add an action to the list if it is not useless - /// (like changing to a font that is already active). - pub fn add(&mut self, action: LayoutAction) { - use LayoutAction::*; - match action { - MoveAbsolute(pos) => self.actions.push(MoveAbsolute(self.origin + pos)), - SetFont(index, size) => if (index, size) != self.active_font { - self.active_font = (index, size); - self.actions.push(action); - }, - _ => self.actions.push(action), - } - } - - /// Add a series of actions. - pub fn extend<I>(&mut self, actions: I) where I: IntoIterator<Item=LayoutAction> { - for action in actions.into_iter() { - self.add(action); - } - } - - /// Add all actions from a box layout at a position. A move to the position - /// is generated and all moves inside the box layout are translated as necessary. - pub fn add_box_absolute(&mut self, position: Size2D, layout: BoxLayout) { - self.actions.push(LayoutAction::MoveAbsolute(position)); - self.origin = position; - self.extend(layout.actions); - } - - /// Whether there are any actions in this list. - pub fn is_empty(&self) -> bool { - self.actions.is_empty() - } - - /// Return the list of actions as a vector. - pub fn into_vec(self) -> Vec<LayoutAction> { - self.actions - } -} - /// The error type for layouting. pub enum LayoutError { /// There is not enough space to add an item. diff --git a/src/layout/text.rs b/src/layout/text.rs index c1f4e464..ecec0220 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -3,7 +3,6 @@ use toddle::query::{FontQuery, SharedFontLoader}; use toddle::tables::{Header, CharMap, HorizontalMetrics}; -use crate::doc::LayoutAction; use crate::size::{Size, Size2D}; use super::*; @@ -92,5 +91,6 @@ pub fn layout(text: &str, ctx: TextContext) -> LayoutResult<BoxLayout> { Ok(BoxLayout { dimensions: Size2D::new(width, Size::pt(ctx.style.font_size)), actions, + debug_render: false, }) } @@ -26,7 +26,7 @@ use crate::style::{PageStyle, TextStyle}; use crate::syntax::SyntaxTree; #[macro_use] -mod error; +mod macros; pub mod doc; pub mod export; pub mod func; diff --git a/src/error.rs b/src/macros.rs index 514eb1a8..6aab95cf 100644 --- a/src/error.rs +++ b/src/macros.rs @@ -24,11 +24,7 @@ macro_rules! error_type { } } - impl std::fmt::Debug for $err { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - std::fmt::Display::fmt(self, f) - } - } + debug_display!($err); impl std::error::Error for $err { // The source method is only generated if an implementation was given. @@ -46,3 +42,14 @@ macro_rules! error_type { })* }; } + +/// Create a `Debug` implementation from a display implementation. +macro_rules! debug_display { + ($type:ident) => { + impl std::fmt::Debug for $type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } + } + }; +} diff --git a/src/size.rs b/src/size.rs index a6d38d63..b9db382e 100644 --- a/src/size.rs +++ b/src/size.rs @@ -1,7 +1,7 @@ //! General spacing types. use std::cmp::Ordering; -use std::fmt::{self, Display, Debug, Formatter}; +use std::fmt::{self, Display, Formatter}; use std::iter::Sum; use std::ops::*; use std::str::FromStr; @@ -133,11 +133,7 @@ impl Display for Size { } } -impl Debug for Size { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(self, f) - } -} +debug_display!(Size); /// An error which can be returned when parsing a size. pub struct ParseSizeError; @@ -254,11 +250,7 @@ impl Display for Size2D { } } -impl Debug for Size2D { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(self, f) - } -} +debug_display!(Size2D); impl Neg for Size2D { type Output = Size2D; @@ -349,8 +341,4 @@ impl Display for SizeBox { } } -impl Debug for SizeBox { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(self, f) - } -} +debug_display!(SizeBox); |
