From 83dd762f67c6513e4073387c5f287fd2ce9ae767 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 14 Mar 2019 19:54:38 +0100 Subject: =?UTF-8?q?Font=20providers=20=F0=9F=9A=80+=20better=20docs=20?= =?UTF-8?q?=F0=9F=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine.rs | 103 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 28 deletions(-) (limited to 'src/engine.rs') 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, @@ -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 { // 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 = std::result::Result; /// 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 for TypesetError { + #[inline] + fn from(err: io::Error) -> TypesetError { + TypesetError::Io(err) + } +} + +impl From for TypesetError { + #[inline] + fn from(err: FontError) -> TypesetError { + TypesetError::Font(err) + } +} -- cgit v1.2.3