Implement bold and italics 📜

This commit is contained in:
Laurenz 2019-04-01 12:25:31 +02:00
parent a34d725000
commit 3b4b55c59e
4 changed files with 53 additions and 28 deletions

View File

@ -30,6 +30,8 @@ pub struct Engine<'t> {
current_text: String, current_text: String,
current_line_width: Size, current_line_width: Size,
current_max_vertical_move: Size, current_max_vertical_move: Size,
bold: bool,
italic: bool,
} }
impl<'t> Engine<'t> { impl<'t> Engine<'t> {
@ -44,6 +46,8 @@ impl<'t> Engine<'t> {
current_text: String::new(), current_text: String::new(),
current_line_width: Size::zero(), current_line_width: Size::zero(),
current_max_vertical_move: Size::zero(), current_max_vertical_move: Size::zero(),
italic: false,
bold: false,
} }
} }
@ -57,8 +61,12 @@ impl<'t> Engine<'t> {
match node { match node {
Node::Word(word) => self.write_word(word)?, Node::Word(word) => self.write_word(word)?,
Node::Space => self.write_space()?, Node::Space => self.write_space()?,
Node::Newline => (), Node::Newline => {},
Node::ToggleItalics | Node::ToggleBold | Node::ToggleMath => unimplemented!(),
Node::ToggleItalics => self.italic = !self.italic,
Node::ToggleBold => self.bold = !self.bold,
Node::ToggleMath => unimplemented!(),
Node::Func(_) => unimplemented!(), Node::Func(_) => unimplemented!(),
} }
} }
@ -177,8 +185,8 @@ impl<'t> Engine<'t> {
fn get_font_for(&self, character: char) -> TypeResult<(usize, Ref<Font>)> { fn get_font_for(&self, character: char) -> TypeResult<(usize, Ref<Font>)> {
self.font_loader.get(FontQuery { self.font_loader.get(FontQuery {
families: &self.ctx.style.font_families, families: &self.ctx.style.font_families,
italic: false, italic: self.italic,
bold: false, bold: self.bold,
character, character,
}).ok_or_else(|| TypesetError::MissingFont) }).ok_or_else(|| TypesetError::MissingFont)
} }

View File

@ -13,7 +13,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use std::io::{self, Cursor, Read, Seek, SeekFrom}; use std::io::{self, Cursor, Read, Seek, SeekFrom, BufReader};
use byteorder::{BE, ReadBytesExt, WriteBytesExt}; use byteorder::{BE, ReadBytesExt, WriteBytesExt};
use opentype::{Error as OpentypeError, OpenTypeReader, Outlines, TableRecord, Tag}; use opentype::{Error as OpentypeError, OpenTypeReader, Outlines, TableRecord, Tag};
use opentype::tables::{Header, Name, CharMap, MaximumProfile, HorizontalMetrics, Post, OS2}; use opentype::tables::{Header, Name, CharMap, MaximumProfile, HorizontalMetrics, Post, OS2};
@ -339,7 +339,7 @@ impl FontProvider for FileSystemFontProvider {
let index = self.infos.iter().position(|i| i == info)?; let index = self.infos.iter().position(|i| i == info)?;
let path = &self.paths[index]; let path = &self.paths[index];
let file = File::open(self.base.join(path)).ok()?; let file = File::open(self.base.join(path)).ok()?;
Some(Box::new(file) as Box<FontData>) Some(Box::new(BufReader::new(file)) as Box<FontData>)
} }
#[inline] #[inline]

View File

@ -20,7 +20,7 @@
//! use typeset::export::pdf::PdfExporter; //! use typeset::export::pdf::PdfExporter;
//! //!
//! // Simple example source code. //! // Simple example source code.
//! let src = "Hello World from Typeset! 🌍"; //! let src = "Hello World from __Typeset__! 🌍";
//! //!
//! // Create a compiler with a font provider that provides three fonts //! // Create a compiler with a font provider that provides three fonts
//! // (the default sans-serif fonts and a fallback for the emoji). //! // (the default sans-serif fonts and a fallback for the emoji).
@ -38,7 +38,7 @@
//! # /* //! # /*
//! let file = File::create("hello-typeset.pdf").unwrap(); //! let file = File::create("hello-typeset.pdf").unwrap();
//! # */ //! # */
//! # let file = File::create("../target/typeset-hello.pdf").unwrap(); //! # let file = File::create("../target/typeset-doc-hello.pdf").unwrap();
//! let exporter = PdfExporter::new(); //! let exporter = PdfExporter::new();
//! exporter.export(&document, file).unwrap(); //! exporter.export(&document, file).unwrap();
//! ``` //! ```
@ -167,16 +167,16 @@ mod test {
let document = compiler.typeset(src).unwrap(); let document = compiler.typeset(src).unwrap();
// Write to file // Write to file
let path = format!("../target/typeset-pdf-{}.pdf", name); let path = format!("../target/typeset-unit-{}.pdf", name);
let file = BufWriter::new(File::create(path).unwrap()); let file = BufWriter::new(File::create(path).unwrap());
let exporter = PdfExporter::new(); let exporter = PdfExporter::new();
exporter.export(&document, file).unwrap(); exporter.export(&document, file).unwrap();
} }
#[test] #[test]
fn small() { fn simple() {
test("parentheses", "Text with ) and ( or (enclosed) works."); test("parentheses", "Text with ) and ( or (enclosed) works.");
test("multiline"," test("multiline-lorem","
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
@ -184,21 +184,22 @@ mod test {
"); ");
} }
#[test]
fn unicode() {
test("unicode", "∑mbe∂∂ed font with Unicode!");
}
#[test] #[test]
fn composite_glyph() { fn composite_glyph() {
test("composite-glyph", "Composite character‼"); test("composite-glyph", "Composite character‼");
} }
#[test] #[test]
fn mixed_emoji() { fn unicode() {
test("unicode", "∑mbe∂∂ed font with Unicode!");
test("mixed-emoji", "Hello World 🌍!") test("mixed-emoji", "Hello World 🌍!")
} }
#[test]
fn styled() {
test("styled", "**Hello World**. That's __great__!");
}
#[test] #[test]
fn long_wikipedia() { fn long_wikipedia() {
test("wikipedia", r#" test("wikipedia", r#"

View File

@ -249,7 +249,12 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
PS::Body => match token { PS::Body => match token {
// Whitespace // Whitespace
Token::Space => self.append(Node::Space), Token::Space => self.append(Node::Space),
Token::Newline => self.append(Node::Newline), Token::Newline => {
self.append(Node::Newline);
if self.tokens.peek() != Some(&Token::Space) {
self.append(Node::Space);
}
},
// Words // Words
Token::Word(word) => self.append(Node::Word(word)), Token::Word(word) => self.append(Node::Word(word)),
@ -382,6 +387,17 @@ mod token_tests {
test("\n", vec![N]); test("\n", vec![N]);
} }
/// This test looks if LF- and CRLF-style newlines get both identified correctly
#[test]
fn tokenize_whitespace_newlines() {
test(" \t", vec![S]);
test("First line\r\nSecond line\nThird line\n",
vec![W("First"), S, W("line"), N, W("Second"), S, W("line"), N,
W("Third"), S, W("line"), N]);
test("Hello \n ", vec![W("Hello"), S, N, S]);
test("Dense\nTimes", vec![W("Dense"), N, W("Times")]);
}
/// Tests if escaping with backslash works as it should. /// Tests if escaping with backslash works as it should.
#[test] #[test]
fn tokenize_escape() { fn tokenize_escape() {
@ -454,21 +470,12 @@ mod token_tests {
vec![L, W("document"), R, L, W("Hello"), S, W("🌍"), W("!"), R]); vec![L, W("document"), R, L, W("Hello"), S, W("🌍"), W("!"), R]);
test("[f]⺐.", vec![L, W("f"), R, W(""), W(".")]); test("[f]⺐.", vec![L, W("f"), R, W(""), W(".")]);
} }
/// This test looks if LF- and CRLF-style newlines get both identified correctly.
#[test]
fn tokenize_whitespace_newlines() {
test(" \t", vec![S]);
test("First line\r\nSecond line\nThird line\n",
vec![W("First"), S, W("line"), N, W("Second"), S, W("line"), N,
W("Third"), S, W("line"), N]);
}
} }
#[cfg(test)] #[cfg(test)]
mod parse_tests { mod parse_tests {
use super::*; use super::*;
use Node::{Space as S, Word as W, Func as F}; use Node::{Space as S, Word as W, Newline as N, Func as F};
/// Test if the source code parses into the syntax tree. /// Test if the source code parses into the syntax tree.
fn test(src: &str, tree: SyntaxTree) { fn test(src: &str, tree: SyntaxTree) {
@ -496,6 +503,15 @@ mod parse_tests {
test("Hello World!", tree! { W("Hello"), S, W("World"), W("!")}); test("Hello World!", tree! { W("Hello"), S, W("World"), W("!")});
} }
/// Test whether newlines generate the correct whitespace.
#[test]
fn parse_newlines_whitespace() {
test("Hello \n World", tree! { W("Hello"), S, N, S, W("World") });
test("Hello\nWorld", tree! { W("Hello"), N, S, W("World") });
test("Hello\n World", tree! { W("Hello"), N, S, W("World") });
test("Hello \nWorld", tree! { W("Hello"), S, N, S, W("World") });
}
/// Parse things dealing with functions. /// Parse things dealing with functions.
#[test] #[test]
fn parse_functions() { fn parse_functions() {