summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-03-13 19:13:49 +0100
committerLaurenz <laurmaedje@gmail.com>2019-03-13 19:15:51 +0100
commit89205368c2788842abb13f1c174607d85dc4abe1 (patch)
treedce7fe4cbaf6c2ddeee6aa0d1126bf3fd2db1c95 /src
parent0c87c0c5a5b7379e938ef9f37673f9c9c0bff051 (diff)
Fix space handling for multiline 🔨
Diffstat (limited to 'src')
-rw-r--r--src/doc.rs35
-rw-r--r--src/engine.rs45
-rw-r--r--src/lib.rs20
-rw-r--r--src/pdf.rs8
4 files changed, 76 insertions, 32 deletions
diff --git a/src/doc.rs b/src/doc.rs
index 3e060f6e..a3bdfeb6 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -15,10 +15,19 @@ pub struct Document {
/// Default styles for a document.
#[derive(Debug, Clone, PartialEq)]
pub struct Style {
- /// The width and height of the paper.
- pub paper_size: [Size; 2],
- /// The [left, top, right, bottom] margins of the paper.
- pub margins: [Size; 4],
+ /// 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,
/// A fallback list of font families to use.
pub font_families: Vec<String>,
@@ -31,9 +40,15 @@ pub struct Style {
impl Default for Style {
fn default() -> Style {
Style {
- // A4 paper with 1.5 cm margins in all directions
- paper_size: [Size::from_mm(210.0), Size::from_mm(297.0)],
- margins: [Size::from_cm(2.5); 4],
+ // A4 paper
+ width: Size::from_mm(210.0),
+ height: Size::from_mm(297.0),
+
+ // A bit more on top and bottom
+ margin_left: Size::from_cm(2.5),
+ margin_top: Size::from_cm(3.0),
+ margin_right: Size::from_cm(2.5),
+ margin_bottom: Size::from_cm(3.0),
// Default font family
font_families: (&[
@@ -48,8 +63,10 @@ impl Default for Style {
/// A page with text contents in a document.
#[derive(Debug, Clone, PartialEq)]
pub struct Page {
- /// The width and height of the page.
- pub size: [Size; 2],
+ /// The width of the page.
+ pub width: Size,
+ /// The height of the page.
+ pub height: Size,
/// Text content on the page.
pub text: Vec<Text>,
}
diff --git a/src/engine.rs b/src/engine.rs
index ef50ecb4..2fbb830b 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -24,9 +24,9 @@ pub struct Engine<'s> {
impl<'s> Engine<'s> {
/// Create a new generator from a syntax tree.
- pub fn new(tree: &'s SyntaxTree<'s>) -> Engine<'s> {
+ pub fn new(tree: &'s SyntaxTree<'s>, style: Style) -> Engine<'s> {
Engine {
- style: Style::default(),
+ style,
tree,
fonts: Vec::new(),
active_font: 0,
@@ -48,9 +48,9 @@ impl<'s> Engine<'s> {
// 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])
- );
+ self.style.margin_left,
+ self.style.height - self.style.margin_top
+ ));
// Set the current font
self.text_commands.push(TextCommand::SetFont(0, self.style.font_size));
@@ -70,7 +70,8 @@ impl<'s> Engine<'s> {
// Create a page from the contents.
let page = Page {
- size: self.style.paper_size,
+ width: self.style.width,
+ height: self.style.height,
text: vec![Text {
commands: self.text_commands,
}],
@@ -83,15 +84,10 @@ impl<'s> Engine<'s> {
}
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 width = self.width(word);
+ if self.would_overflow(width) {
let vertical_move = - self.style.font_size
* self.style.line_spacing
* font.metrics.ascender;
@@ -107,10 +103,29 @@ impl<'s> Engine<'s> {
}
fn write_space(&mut self) {
- if !self.current_line.is_empty() {
- self.write_word(" ");
+ let space_width = self.width(" ");
+
+ if !self.would_overflow(space_width) && !self.current_line.is_empty() {
+ self.text_commands.push(TextCommand::Text(" ".to_owned()));
+ self.current_line.push_str(" ");
+ self.current_width += space_width;
}
}
+
+ 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)
+ .sum()
+ }
+
+ fn would_overflow(&self, width: Size) -> bool {
+ let max_width = self.style.width
+ - self.style.margin_left
+ - self.style.margin_right;
+
+ self.current_width + width > max_width
+ }
}
/// Result type used for parsing.
diff --git a/src/lib.rs b/src/lib.rs
index deb5b066..ae12bf8b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -37,20 +37,32 @@ use std::error;
use std::fmt;
use std::io::Write;
use crate::syntax::SyntaxTree;
-use crate::doc::Document;
+use crate::doc::{Document, Style};
/// Emits various compiled intermediates from source code.
pub struct Compiler<'s> {
/// The source code of the document.
source: &'s str,
+ /// Style for typesetting.
+ style: Style,
}
impl<'s> Compiler<'s> {
/// Create a new compiler from a document.
#[inline]
pub fn new(source: &'s str) -> Compiler<'s> {
- Compiler { source }
+ Compiler {
+ source,
+ style: Style::default(),
+ }
+ }
+
+ /// Set the default style for typesetting.
+ #[inline]
+ pub fn style(&mut self, style: Style) -> &mut Self {
+ self.style = style;
+ self
}
/// Return an iterator over the tokens of the document.
@@ -69,7 +81,7 @@ impl<'s> Compiler<'s> {
#[inline]
pub fn typeset(&self) -> Result<Document, Error> {
let tree = self.parse()?;
- Engine::new(&tree).typeset().map_err(Into::into)
+ Engine::new(&tree, self.style.clone()).typeset().map_err(Into::into)
}
/// Write the document as a _PDF_, returning how many bytes were written.
@@ -159,7 +171,7 @@ mod test {
}
#[test]
- fn long() {
+ fn long_styled() {
test("wikipedia", r#"
Typesetting is the composition of text by means of arranging physical types or the
digital equivalents. Stored letters and other symbols (called sorts in mechanical
diff --git a/src/pdf.rs b/src/pdf.rs
index d5d3b081..e5a2cf6b 100644
--- a/src/pdf.rs
+++ b/src/pdf.rs
@@ -122,11 +122,11 @@ impl<'a, W: Write> PdfCreator<'a, W> {
// The page objects
let mut id = self.offsets.pages.0;
for page in &self.doc.pages {
- let width = page.size[0].to_points();
- let height = page.size[1].to_points();
-
self.writer.write_obj(id, Page::new(self.offsets.page_tree)
- .media_box(Rect::new(0.0, 0.0, width, height))
+ .media_box(Rect::new(
+ 0.0, 0.0,
+ page.width.to_points(), page.height.to_points())
+ )
.contents(self.offsets.contents.0 ..= self.offsets.contents.1)
)?;