summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/doc.rs27
-rw-r--r--src/export/pdf.rs7
-rw-r--r--src/layout/actions.rs131
-rw-r--r--src/layout/boxed.rs14
-rw-r--r--src/layout/flex.rs9
-rw-r--r--src/layout/mod.rs66
-rw-r--r--src/layout/text.rs2
-rw-r--r--src/lib.rs2
-rw-r--r--src/macros.rs (renamed from src/error.rs)17
-rw-r--r--src/size.rs20
10 files changed, 176 insertions, 119 deletions
diff --git a/src/doc.rs b/src/doc.rs
index d83ae635..9a287913 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -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,
})
}
diff --git a/src/lib.rs b/src/lib.rs
index 26543b1d..2fe2f94f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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);