summaryrefslogtreecommitdiff
path: root/src/engine.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-03-13 18:42:33 +0100
committerLaurenz <laurmaedje@gmail.com>2019-03-13 18:42:33 +0100
commit0c87c0c5a5b7379e938ef9f37673f9c9c0bff051 (patch)
tree6310996dd92bcf50999ae2f3ce7641d08ef36986 /src/engine.rs
parent107450ee5ca8e7e0bc03cf6ce9f59268aa20e9f6 (diff)
Basic multiline support 📜
Diffstat (limited to 'src/engine.rs')
-rw-r--r--src/engine.rs93
1 files changed, 74 insertions, 19 deletions
diff --git a/src/engine.rs b/src/engine.rs
index 34f98766..ef50ecb4 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -3,59 +3,114 @@
use std::error;
use std::fmt;
use crate::syntax::{SyntaxTree, Node};
-use crate::doc::{Document, Style, Page, Text, TextCommand};
+use crate::doc::{Document, Style, Size, Page, Text, TextCommand};
use crate::font::Font;
/// The core typesetting engine, transforming an abstract syntax tree into a document.
#[derive(Debug, Clone)]
pub struct Engine<'s> {
- tree: SyntaxTree<'s>,
+ // Immutable
+ tree: &'s SyntaxTree<'s>,
+ style: Style,
+
+ // Mutable
+ fonts: Vec<Font>,
+ active_font: usize,
+ text_commands: Vec<TextCommand>,
+ current_line: String,
+ current_width: Size,
}
impl<'s> Engine<'s> {
/// Create a new generator from a syntax tree.
- pub fn new(tree: SyntaxTree<'s>) -> Engine<'s> {
- Engine { tree }
+ pub fn new(tree: &'s SyntaxTree<'s>) -> Engine<'s> {
+ Engine {
+ style: Style::default(),
+ tree,
+ fonts: Vec::new(),
+ active_font: 0,
+ text_commands: Vec::new(),
+ current_line: String::new(),
+ current_width: Size::zero(),
+ }
}
/// Generate the abstract document.
- pub fn typeset(&mut self) -> TypeResult<Document> {
- let style = Style::default();
-
+ pub fn typeset(mut self) -> TypeResult<Document> {
// Load font defined by style
- let font_family = style.font_families.first().unwrap();
+ 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 text = String::new();
+ self.fonts.push(font);
+ self.active_font = 0;
+
+ // Move cursor to top-left position
+ self.text_commands.push(TextCommand::Move(
+ self.style.margins[0],
+ self.style.paper_size[1] - self.style.margins[1])
+ );
+
+ // Set the current font
+ self.text_commands.push(TextCommand::SetFont(0, self.style.font_size));
+
+ // Iterate through the documents nodes.
for node in &self.tree.nodes {
match node {
- Node::Space if !text.is_empty() => text.push(' '),
- Node::Space | Node::Newline => (),
- Node::Word(word) => text.push_str(word),
+ Node::Word(word) => self.write_word(word),
+
+ Node::Space => self.write_space(),
+ Node::Newline => (),
Node::ToggleItalics | Node::ToggleBold | Node::ToggleMath => unimplemented!(),
Node::Func(_) => unimplemented!(),
}
}
+ // Create a page from the contents.
let page = Page {
- size: style.paper_size,
+ size: self.style.paper_size,
text: vec![Text {
- commands: vec![
- TextCommand::Move(style.margins[0], style.paper_size[1] - style.margins[1]),
- TextCommand::SetFont(0, style.font_size),
- TextCommand::Text(text)
- ]
+ commands: self.text_commands,
}],
};
Ok(Document {
pages: vec![page],
- fonts: vec![font],
+ fonts: self.fonts,
})
}
+
+ fn write_word(&mut self, word: &str) {
+ let max_width = self.style.paper_size[0] - 2 * self.style.margins[0];
+
+ let font = &self.fonts[self.active_font];
+ let width = word.chars()
+ .map(|c| font.widths[font.map(c) as usize] * self.style.font_size)
+ .sum();
+
+
+ if self.current_width + width > max_width {
+ let vertical_move = - self.style.font_size
+ * self.style.line_spacing
+ * font.metrics.ascender;
+ self.text_commands.push(TextCommand::Move(Size::zero(), vertical_move));
+
+ self.current_line.clear();
+ self.current_width = Size::zero();
+ }
+
+ self.text_commands.push(TextCommand::Text(word.to_owned()));
+ self.current_line.push_str(word);
+ self.current_width += width;
+ }
+
+ fn write_space(&mut self) {
+ if !self.current_line.is_empty() {
+ self.write_word(" ");
+ }
+ }
}
/// Result type used for parsing.