summaryrefslogtreecommitdiff
path: root/src/engine.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-03-14 19:54:38 +0100
committerLaurenz <laurmaedje@gmail.com>2019-03-14 19:54:38 +0100
commit83dd762f67c6513e4073387c5f287fd2ce9ae767 (patch)
tree691bb772fe6a49353f173282024e217513151a9b /src/engine.rs
parent22ea09d9c1fd342dcee13d153fedaf49a62db044 (diff)
Font providers 🚀+ better docs 📜
Diffstat (limited to 'src/engine.rs')
-rw-r--r--src/engine.rs103
1 files changed, 75 insertions, 28 deletions
diff --git a/src/engine.rs b/src/engine.rs
index 5a6e27b0..1d249172 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -1,18 +1,19 @@
//! Core typesetting engine.
+use std::io;
use std::error;
use std::fmt;
use crate::syntax::{SyntaxTree, Node};
-use crate::doc::{Document, Style, Size, Page, Text, TextCommand};
-use crate::font::Font;
+use crate::doc::{Document, Size, Page, Text, TextCommand};
+use crate::font::{Font, FontConfig, FontError};
+use crate::Context;
/// The core typesetting engine, transforming an abstract syntax tree into a document.
-#[derive(Debug, Clone)]
-pub struct Engine<'s> {
+pub(crate) struct Engine<'a> {
// Immutable
- tree: &'s SyntaxTree<'s>,
- style: Style,
+ tree: &'a SyntaxTree<'a>,
+ ctx: &'a Context<'a>,
// Mutable
fonts: Vec<Font>,
@@ -22,12 +23,12 @@ pub struct Engine<'s> {
current_width: Size,
}
-impl<'s> Engine<'s> {
+impl<'a> Engine<'a> {
/// Create a new generator from a syntax tree.
- pub fn new(tree: &'s SyntaxTree<'s>, style: Style) -> Engine<'s> {
+ pub fn new(tree: &'a SyntaxTree<'a>, context: &'a Context<'a>) -> Engine<'a> {
Engine {
- style,
tree,
+ ctx: context,
fonts: Vec::new(),
active_font: 0,
text_commands: Vec::new(),
@@ -39,21 +40,33 @@ impl<'s> Engine<'s> {
/// Generate the abstract document.
pub fn typeset(mut self) -> TypeResult<Document> {
// Load font defined by style
- let font_family = self.style.font_families.first().unwrap();
- let program = std::fs::read(format!("../fonts/{}-Regular.ttf", font_family)).unwrap();
- let font = Font::new(program).unwrap();
+ let mut font = None;
+ let config = FontConfig::new(self.ctx.style.font_families.clone());
+ for provider in &self.ctx.font_providers {
+ if let Some(mut source) = provider.provide(&config) {
+ let mut program = Vec::new();
+ source.read_to_end(&mut program)?;
+ font = Some(Font::new(program)?);
+ break;
+ }
+ }
+
+ let font = match font {
+ Some(font) => font,
+ None => return Err(TypesetError::MissingFont),
+ };
self.fonts.push(font);
self.active_font = 0;
// Move cursor to top-left position
self.text_commands.push(TextCommand::Move(
- self.style.margin_left,
- self.style.height - self.style.margin_top
+ self.ctx.style.margin_left,
+ self.ctx.style.height - self.ctx.style.margin_top
));
// Set the current font
- self.text_commands.push(TextCommand::SetFont(0, self.style.font_size));
+ self.text_commands.push(TextCommand::SetFont(0, self.ctx.style.font_size));
// Iterate through the documents nodes.
for node in &self.tree.nodes {
@@ -70,8 +83,8 @@ impl<'s> Engine<'s> {
// Create a page from the contents.
let page = Page {
- width: self.style.width,
- height: self.style.height,
+ width: self.ctx.style.width,
+ height: self.ctx.style.height,
text: vec![Text {
commands: self.text_commands,
}],
@@ -88,8 +101,8 @@ impl<'s> Engine<'s> {
let width = self.width(word);
if self.would_overflow(width) {
- let vertical_move = - self.style.font_size
- * self.style.line_spacing
+ let vertical_move = - self.ctx.style.font_size
+ * self.ctx.style.line_spacing
* font.metrics.ascender;
self.text_commands.push(TextCommand::Move(Size::zero(), vertical_move));
@@ -115,31 +128,51 @@ impl<'s> Engine<'s> {
fn width(&self, word: &str) -> Size {
let font = &self.fonts[self.active_font];
word.chars()
- .map(|c| font.widths[font.map(c) as usize] * self.style.font_size)
+ .map(|c| font.widths[font.map(c) as usize] * self.ctx.style.font_size)
.sum()
}
fn would_overflow(&self, width: Size) -> bool {
- let max_width = self.style.width
- - self.style.margin_left
- - self.style.margin_right;
+ let max_width = self.ctx.style.width
+ - self.ctx.style.margin_left
+ - self.ctx.style.margin_right;
self.current_width + width > max_width
}
}
-/// Result type used for parsing.
+/// Result type used for typesetting.
type TypeResult<T> = std::result::Result<T, TypesetError>;
/// The error type for typesetting.
-pub enum TypesetError {}
+pub enum TypesetError {
+ /// There was no suitable font.
+ MissingFont,
+ /// An error occured while gathering font data.
+ Font(FontError),
+ /// An I/O Error on occured while reading a font.
+ Io(io::Error),
+}
-impl error::Error for TypesetError {}
+impl error::Error for TypesetError {
+ #[inline]
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match self {
+ TypesetError::Font(err) => Some(err),
+ TypesetError::Io(err) => Some(err),
+ _ => None,
+ }
+ }
+}
impl fmt::Display for TypesetError {
#[inline]
- fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
- Ok(())
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ TypesetError::MissingFont => write!(f, "missing font"),
+ TypesetError::Font(err) => write!(f, "font error: {}", err),
+ TypesetError::Io(err) => write!(f, "io error: {}", err),
+ }
}
}
@@ -149,3 +182,17 @@ impl fmt::Debug for TypesetError {
fmt::Display::fmt(self, f)
}
}
+
+impl From<io::Error> for TypesetError {
+ #[inline]
+ fn from(err: io::Error) -> TypesetError {
+ TypesetError::Io(err)
+ }
+}
+
+impl From<FontError> for TypesetError {
+ #[inline]
+ fn from(err: FontError) -> TypesetError {
+ TypesetError::Font(err)
+ }
+}