diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-05-24 12:24:10 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-05-24 12:24:10 +0200 |
| commit | b3734bbc046fe7b14cff54e2dae7014a71014777 (patch) | |
| tree | d80e97d9f0694cb7e7f61869a1085e64e791a5b8 /src/layout | |
| parent | e3215fa3b92574e2087c28b1d494d397e6819236 (diff) | |
Restructure engine into modular layouter 🍂
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/mod.rs | 118 | ||||
| -rw-r--r-- | src/layout/size.rs | 147 |
2 files changed, 265 insertions, 0 deletions
diff --git a/src/layout/mod.rs b/src/layout/mod.rs new file mode 100644 index 00000000..7bc62bd3 --- /dev/null +++ b/src/layout/mod.rs @@ -0,0 +1,118 @@ +//! Layouting engine. + +use crate::doc::Document; +use crate::font::{Font, FontLoader, FontFamily, FontError}; +use crate::syntax::SyntaxTree; + +mod size; +pub use size::Size; + + +/// Layout a syntax tree given a context. +#[allow(unused_variables)] +pub fn layout(tree: &SyntaxTree, ctx: &LayoutContext) -> LayoutResult<Layout> { + Ok(Layout {}) +} + +/// A collection of layouted content. +pub struct Layout {} + +impl Layout { + /// Convert this layout into a document given the list of fonts referenced by it. + pub fn into_document(self, fonts: Vec<Font>) -> Document { + Document { + pages: vec![], + fonts, + } + } +} + +/// The context for layouting. +pub struct LayoutContext<'a, 'p> { + pub loader: &'a FontLoader<'p>, +} + +/// Default styles for pages. +#[derive(Debug, Clone, PartialEq)] +pub struct PageStyle { + /// The width of the paper. + pub width: Size, + /// The height of the paper. + pub height: Size, + + /// The left margin of the paper. + pub margin_left: Size, + /// The top margin of the paper. + pub margin_top: Size, + /// The right margin of the paper. + pub margin_right: Size, + /// The bottom margin of the paper. + pub margin_bottom: Size, +} + +impl Default for PageStyle { + fn default() -> PageStyle { + PageStyle { + // A4 paper. + width: Size::from_mm(210.0), + height: Size::from_mm(297.0), + + // Margins. A bit more on top and bottom. + margin_left: Size::from_cm(3.0), + margin_top: Size::from_cm(3.0), + margin_right: Size::from_cm(3.0), + margin_bottom: Size::from_cm(3.0), + } + } +} + +/// Default styles for texts. +#[derive(Debug, Clone, PartialEq)] +pub struct TextStyle { + /// A fallback list of font families to use. + pub font_families: Vec<FontFamily>, + /// The font size. + pub font_size: f32, + /// The line spacing (as a multiple of the font size). + pub line_spacing: f32, + /// The spacing for paragraphs (as a multiple of the line spacing). + pub paragraph_spacing: f32, +} + +impl Default for TextStyle { + fn default() -> TextStyle { + use FontFamily::*; + TextStyle { + // Default font family, font size and line spacing. + font_families: vec![SansSerif, Serif, Monospace], + font_size: 11.0, + line_spacing: 1.25, + paragraph_spacing: 1.5, + } + } +} + +/// The error type for layouting. +pub enum LayoutError { + /// There was no suitable font. + MissingFont, + /// An error occured while gathering font data. + Font(FontError), +} + +/// The result type for layouting. +pub type LayoutResult<T> = Result<T, LayoutError>; + +error_type! { + err: LayoutError, + show: f => match err { + LayoutError::MissingFont => write!(f, "missing font"), + LayoutError::Font(err) => write!(f, "font error: {}", err), + }, + source: match err { + LayoutError::Font(err) => Some(err), + _ => None, + }, + from: (std::io::Error, LayoutError::Font(FontError::Io(err))), + from: (FontError, LayoutError::Font(err)), +} diff --git a/src/layout/size.rs b/src/layout/size.rs new file mode 100644 index 00000000..bf79a3c4 --- /dev/null +++ b/src/layout/size.rs @@ -0,0 +1,147 @@ +use std::cmp::Ordering; +use std::fmt::{self, Display, Debug, Formatter}; +use std::iter::Sum; +use std::ops::*; + + +/// A general size (unit of length) type. +#[derive(Copy, Clone, PartialEq, Default)] +pub struct Size { + /// The size in typographic points (1/72 inches). + points: f32, +} + +impl Size { + /// Create an zeroed size. + #[inline] + pub fn zero() -> Size { Size { points: 0.0 } } + + /// Create a size from a number of points. + #[inline] + pub fn from_points(points: f32) -> Size { Size { points } } + + /// Create a size from a number of inches. + #[inline] + pub fn from_inches(inches: f32) -> Size { Size { points: 72.0 * inches } } + + /// Create a size from a number of millimeters. + #[inline] + pub fn from_mm(mm: f32) -> Size { Size { points: 2.83465 * mm } } + + /// Create a size from a number of centimeters. + #[inline] + pub fn from_cm(cm: f32) -> Size { Size { points: 28.3465 * cm } } + + /// Create a size from a number of points. + #[inline] + pub fn to_points(&self) -> f32 { self.points } + + /// Create a size from a number of inches. + #[inline] + pub fn to_inches(&self) -> f32 { self.points * 0.0138889 } + + /// Create a size from a number of millimeters. + #[inline] + pub fn to_mm(&self) -> f32 { self.points * 0.352778 } + + /// Create a size from a number of centimeters. + #[inline] + pub fn to_cm(&self) -> f32 { self.points * 0.0352778 } +} + +impl Display for Size { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}pt", self.points) + } +} + +impl Debug for Size { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +} + +impl PartialOrd for Size { + #[inline] + fn partial_cmp(&self, other: &Size) -> Option<Ordering> { + self.points.partial_cmp(&other.points) + } +} + +impl Neg for Size { + type Output = Size; + + #[inline] + fn neg(self) -> Size { + Size { points: -self.points } + } +} + +impl Sum for Size { + #[inline] + fn sum<I>(iter: I) -> Size where I: Iterator<Item=Size> { + iter.fold(Size::zero(), Add::add) + } +} + +macro_rules! impl_reflexive { + ($trait:ident, $func:ident, $assign_trait:ident, $assign_func:ident) => { + impl $trait for Size { + type Output = Size; + + #[inline] + fn $func(self, other: Size) -> Size { + Size { points: $trait::$func(self.points, other.points) } + } + } + + impl $assign_trait for Size { + #[inline] + fn $assign_func(&mut self, other: Size) { + $assign_trait::$assign_func(&mut self.points, other.points); + } + } + }; +} + +macro_rules! impl_num_back { + ($trait:ident, $func:ident, $assign_trait:ident, $assign_func:ident, $ty:ty) => { + impl $trait<$ty> for Size { + type Output = Size; + + #[inline] + fn $func(self, other: $ty) -> Size { + Size { points: $trait::$func(self.points, other as f32) } + } + } + + impl $assign_trait<$ty> for Size { + #[inline] + fn $assign_func(&mut self, other: $ty) { + $assign_trait::$assign_func(&mut self.points, other as f32); + } + } + }; +} + +macro_rules! impl_num_both { + ($trait:ident, $func:ident, $assign_trait:ident, $assign_func:ident, $ty:ty) => { + impl_num_back!($trait, $func, $assign_trait, $assign_func, $ty); + + impl $trait<Size> for $ty { + type Output = Size; + + #[inline] + fn $func(self, other: Size) -> Size { + Size { points: $trait::$func(self as f32, other.points) } + } + } + }; +} + +impl_reflexive!(Add, add, AddAssign, add_assign); +impl_reflexive!(Sub, sub, SubAssign, sub_assign); +impl_num_both!(Mul, mul, MulAssign, mul_assign, f32); +impl_num_both!(Mul, mul, MulAssign, mul_assign, i32); +impl_num_back!(Div, div, DivAssign, div_assign, f32); +impl_num_back!(Div, div, DivAssign, div_assign, i32); |
