summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz Mädje <laurmaedje@gmail.com>2019-07-28 21:03:33 +0200
committerLaurenz Mädje <laurmaedje@gmail.com>2019-07-28 21:03:33 +0200
commit19be053cc3465229a39a65cab2460ac61e18cd8c (patch)
treede130d294f54000f7db04becdb94fc992699aa21
parent6c8b5caa9fa731f16b2d9d232177c00de8f2b74b (diff)
Create some benchmarks 📊
-rw-r--r--Cargo.toml12
-rw-r--r--benches/complete.rs19
-rw-r--r--benches/font.rs45
-rw-r--r--src/bin/main.rs21
-rw-r--r--src/font/mod.rs69
-rw-r--r--src/lib.rs64
-rw-r--r--test/shakespeare.tps88
7 files changed, 238 insertions, 80 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 5aa6ffa5..76535302 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,7 +10,19 @@ opentype = { path = "../opentype" }
byteorder = "1"
smallvec = "0.6.10"
unicode-xid = "0.1.0"
+toml = "0.5"
+
+[dev-dependencies]
+bencher = "0.1"
[[bin]]
name = "typst"
path = "src/bin/main.rs"
+
+[[bench]]
+name = "font"
+harness = false
+
+[[bench]]
+name = "complete"
+harness = false
diff --git a/benches/complete.rs b/benches/complete.rs
new file mode 100644
index 00000000..8bc4acab
--- /dev/null
+++ b/benches/complete.rs
@@ -0,0 +1,19 @@
+use bencher::Bencher;
+use typeset::Typesetter;
+use typeset::font::FileSystemFontProvider;
+
+
+fn typesetting(b: &mut Bencher) {
+ let src = include_str!("../test/shakespeare.tps");
+
+ let mut typesetter = Typesetter::new();
+ let provider = FileSystemFontProvider::from_listing("../fonts/fonts.toml").unwrap();
+ typesetter.add_font_provider(provider);
+
+ b.iter(|| {
+ let _document = typesetter.typeset(&src).unwrap();
+ });
+}
+
+bencher::benchmark_group!(benches, typesetting);
+bencher::benchmark_main!(benches);
diff --git a/benches/font.rs b/benches/font.rs
new file mode 100644
index 00000000..cac264ec
--- /dev/null
+++ b/benches/font.rs
@@ -0,0 +1,45 @@
+use bencher::Bencher;
+use typeset::font::{*, FontClass::*};
+use typeset::style::TextStyle;
+
+
+fn font_loading(b: &mut Bencher) {
+ let provider = FileSystemFontProvider::from_listing("../fonts/fonts.toml").unwrap();
+ let providers = vec![Box::new(provider) as Box<dyn FontProvider>];
+ let font_loader = FontLoader::new(&providers);
+
+ let text = include_str!("../test/shakespeare.tps");
+
+ let mut style = TextStyle {
+ classes: vec![Regular],
+ fallback: vec![
+ Family("Helvetica".to_string()),
+ Family("Computer Modern".to_string()),
+ Serif,
+ Monospace,
+ ],
+ font_size: 12.0,
+ line_spacing: 1.0,
+ paragraph_spacing: 1.0,
+ };
+
+ b.iter(|| {
+ for character in text.chars() {
+ match character {
+ '_' => style.toggle_class(Italic),
+ '*' => style.toggle_class(Bold),
+ '\n' => {},
+ _ => {
+ let _font = font_loader.get(FontQuery {
+ character,
+ classes: style.classes.clone(),
+ fallback: style.fallback.clone(),
+ }).unwrap();
+ },
+ }
+ }
+ });
+}
+
+bencher::benchmark_group!(benches, font_loading);
+bencher::benchmark_main!(benches);
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 61237580..7ba61645 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use std::process;
use typeset::Typesetter;
-use typeset::{font::FileSystemFontProvider, font};
+use typeset::font::FileSystemFontProvider;
use typeset::export::pdf::PdfExporter;
@@ -18,7 +18,7 @@ fn main() {
}
/// The actual main function.
-fn run() -> Result<(), Box<Error>> {
+fn run() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 || args.len() > 3 {
help_and_quit();
@@ -45,21 +45,8 @@ fn run() -> Result<(), Box<Error>> {
// Create a typesetter with a font provider that provides the default fonts.
let mut typesetter = Typesetter::new();
- typesetter.add_font_provider(FileSystemFontProvider::new("../fonts", vec![
- ("CMU-SansSerif-Regular.ttf", font!["Computer Modern", Regular, SansSerif]),
- ("CMU-SansSerif-Italic.ttf", font!["Computer Modern", Italic, SansSerif]),
- ("CMU-SansSerif-Bold.ttf", font!["Computer Modern", Bold, SansSerif]),
- ("CMU-SansSerif-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, SansSerif]),
- ("CMU-Serif-Regular.ttf", font!["Computer Modern", Regular, Serif]),
- ("CMU-Serif-Italic.ttf", font!["Computer Modern", Italic, Serif]),
- ("CMU-Serif-Bold.ttf", font!["Computer Modern", Bold, Serif]),
- ("CMU-Serif-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif]),
- ("CMU-Typewriter-Regular.ttf", font!["Computer Modern", Regular, Serif, SansSerif, Monospace]),
- ("CMU-Typewriter-Italic.ttf", font!["Computer Modern", Italic, Serif, SansSerif, Monospace]),
- ("CMU-Typewriter-Bold.ttf", font!["Computer Modern", Bold, Serif, SansSerif, Monospace]),
- ("CMU-Typewriter-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif, SansSerif, Monospace]),
- ("NotoEmoji-Regular.ttf", font!["Noto", Regular, Bold, Italic, SansSerif, Serif, Monospace]),
- ]));
+ let provider = FileSystemFontProvider::from_listing("fonts/fonts.toml").unwrap();
+ typesetter.add_font_provider(provider);
// Typeset the source code.
let document = typesetter.typeset(&src)?;
diff --git a/src/font/mod.rs b/src/font/mod.rs
index 80d900dc..4efc624d 100644
--- a/src/font/mod.rs
+++ b/src/font/mod.rs
@@ -11,13 +11,15 @@
//! from a folder on the file system.
use std::collections::HashMap;
-use std::fs::File;
+use std::fs::{self, File};
use std::io::{self, Cursor, Read, Seek, BufReader};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use opentype::{Error as OpentypeError, OpenTypeReader};
use opentype::tables::{Header, Name, CharMap, HorizontalMetrics, Post, OS2};
use opentype::types::{MacStyleFlags, NameEntry};
+use toml::map::Map as TomlMap;
+use toml::value::Value as TomlValue;
use self::subset::Subsetter;
use crate::size::Size;
@@ -300,6 +302,64 @@ impl FileSystemFontProvider {
infos,
}
}
+
+ /// Create a new provider from a font listing file.
+ pub fn from_listing<P: AsRef<Path>>(file: P) -> FontResult<FileSystemFontProvider> {
+ fn inv<S: ToString>(message: S) -> FontError {
+ FontError::InvalidListing(message.to_string())
+ }
+
+ let file = file.as_ref();
+ let base = file.parent()
+ .ok_or_else(|| inv("expected listings file"))?;
+
+ let bytes = fs::read(file)?;
+ let map: TomlMap<String, toml::Value> = toml::de::from_slice(&bytes)
+ .map_err(|err| inv(err))?;
+
+ let mut paths = Vec::new();
+ let mut infos = Vec::new();
+
+ for value in map.values() {
+ if let TomlValue::Table(table) = value {
+ // Parse the string file key.
+ paths.push(match table.get("file") {
+ Some(TomlValue::String(s)) => PathBuf::from(s),
+ _ => return Err(inv("expected file name")),
+ });
+
+ // Parse the array<string> classes key.
+ infos.push(if let Some(TomlValue::Array(array)) = table.get("classes") {
+ let mut classes = Vec::with_capacity(array.len());
+ for class in array {
+ classes.push(match class {
+ TomlValue::String(class) => match class.as_str() {
+ "Serif" => FontClass::Serif,
+ "SansSerif" => FontClass::SansSerif,
+ "Monospace" => FontClass::Monospace,
+ "Regular" => FontClass::Regular,
+ "Bold" => FontClass::Bold,
+ "Italic" => FontClass::Italic,
+ _ => FontClass::Family(class.to_string()),
+ },
+ _ => return Err(inv("expect font class string")),
+ })
+ }
+ FontInfo { classes }
+ } else {
+ return Err(inv("expected font classes"));
+ });
+ } else {
+ return Err(inv("expected file/classes table"));
+ }
+ }
+
+ Ok(FileSystemFontProvider {
+ base: base.to_owned(),
+ paths,
+ infos,
+ })
+ }
}
impl FontProvider for FileSystemFontProvider {
@@ -309,7 +369,7 @@ impl FontProvider for FileSystemFontProvider {
let path = &self.paths[index];
let full_path = self.base.join(path);
let file = File::open(full_path).ok()?;
- Some(Box::new(BufReader::new(file)) as Box<FontData>)
+ Some(Box::new(BufReader::new(file)) as Box<dyn FontData>)
}
#[inline]
@@ -323,6 +383,8 @@ impl FontProvider for FileSystemFontProvider {
pub enum FontError {
/// The font file is incorrect.
InvalidFont(String),
+ /// The font listing is incorrect.
+ InvalidListing(String),
/// A character requested for subsetting was not present in the source font.
MissingCharacter(char),
/// A requested or required table was not present.
@@ -340,6 +402,7 @@ error_type! {
res: FontResult,
show: f => match err {
FontError::InvalidFont(message) => write!(f, "invalid font: {}", message),
+ FontError::InvalidListing(message) => write!(f, "invalid font listing: {}", message),
FontError::MissingCharacter(c) => write!(f, "missing character: '{}'", c),
FontError::MissingTable(table) => write!(f, "missing table: '{}'", table),
FontError::UnsupportedTable(table) => write!(f, "unsupported table: {}", table),
diff --git a/src/lib.rs b/src/lib.rs
index ed1e079e..9be3941d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -183,21 +183,8 @@ mod test {
/// Create a _PDF_ with a name from the source code.
fn test(name: &str, src: &str) {
let mut typesetter = Typesetter::new();
- typesetter.add_font_provider(FileSystemFontProvider::new("../fonts", vec![
- ("CMU-SansSerif-Regular.ttf", font!["Computer Modern", Regular, SansSerif]),
- ("CMU-SansSerif-Italic.ttf", font!["Computer Modern", Italic, SansSerif]),
- ("CMU-SansSerif-Bold.ttf", font!["Computer Modern", Bold, SansSerif]),
- ("CMU-SansSerif-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, SansSerif]),
- ("CMU-Serif-Regular.ttf", font!["Computer Modern", Regular, Serif]),
- ("CMU-Serif-Italic.ttf", font!["Computer Modern", Italic, Serif]),
- ("CMU-Serif-Bold.ttf", font!["Computer Modern", Bold, Serif]),
- ("CMU-Serif-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif]),
- ("CMU-Typewriter-Regular.ttf", font!["Computer Modern", Regular, Serif, SansSerif, Monospace]),
- ("CMU-Typewriter-Italic.ttf", font!["Computer Modern", Italic, Serif, SansSerif, Monospace]),
- ("CMU-Typewriter-Bold.ttf", font!["Computer Modern", Bold, Serif, SansSerif, Monospace]),
- ("CMU-Typewriter-Bold-Italic.ttf", font!["Computer Modern", Bold, Italic, Serif, SansSerif, Monospace]),
- ("NotoEmoji-Regular.ttf", font!["Noto", Regular, Bold, Italic, SansSerif, Serif, Monospace]),
- ]));
+ let provider = FileSystemFontProvider::from_listing("../fonts/fonts.toml").unwrap();
+ typesetter.add_font_provider(provider);
// Typeset into document.
let document = typesetter.typeset(src).unwrap();
@@ -231,50 +218,7 @@ mod test {
}
#[test]
- fn wikipedia() {
- 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
- systems and glyphs in digital systems) are retrieved and ordered according to a
- language's orthography for visual display. Typesetting requires one or more fonts
- (which are widely but erroneously confused with and substituted for typefaces). One
- significant effect of typesetting was that authorship of works could be spotted more
- easily, making it difficult for copiers who have not gained permission.
-
- During much of the letterpress era, movable type was composed by hand for each page.
- Cast metal sorts were composed into words, then lines, then paragraphs, then pages of
- text and tightly bound together to make up a form, with all letter faces exactly the
- same "height to paper", creating an even surface of type. The form was placed in a
- press, inked, and an impression made on paper.
-
- During typesetting, individual sorts are picked from a type case with the right hand,
- and set into a composing stick held in the left hand from left to right, and as viewed
- by the setter upside down. As seen in the photo of the composing stick, a lower case
- 'q' looks like a 'd', a lower case 'b' looks like a 'p', a lower case 'p' looks like a
- 'b' and a lower case 'd' looks like a 'q'. This is reputed to be the origin of the
- expression "mind your p's and q's". It might just as easily have been "mind your b's
- and d's".
-
- The diagram at right illustrates a cast metal sort: a face, b body or shank, c point
- size, 1 shoulder, 2 nick, 3 groove, 4 foot. Wooden printing sorts were in use for
- centuries in combination with metal type. Not shown, and more the concern of the
- casterman, is the “set”, or width of each sort. Set width, like body size, is measured
- in points.
-
- In order to extend the working life of type, and to account for the finite sorts in a
- case of type, copies of forms were cast when anticipating subsequent printings of a
- text, freeing the costly type for other work. This was particularly prevalent in book
- and newspaper work where rotary presses required type forms to wrap an impression
- cylinder rather than set in the bed of a press. In this process, called stereotyping,
- the entire form is pressed into a fine matrix such as plaster of Paris or papier mâché
- called a flong to create a positive, from which the stereotype form was electrotyped,
- cast of type metal.
-
- Advances such as the typewriter and computer would push the state of the art even
- farther ahead. Still, hand composition and letterpress printing have not fallen
- completely out of use, and since the introduction of digital typesetting, it has seen a
- revival as an artisanal pursuit. However, it is a very small niche within the larger
- typesetting market.
- "#);
+ fn shakespeare() {
+ test("shakespeare", include_str!("../test/shakespeare.tps"));
}
}
diff --git a/test/shakespeare.tps b/test/shakespeare.tps
new file mode 100644
index 00000000..51526711
--- /dev/null
+++ b/test/shakespeare.tps
@@ -0,0 +1,88 @@
+// -------------------------------------------------------------------------- //
+[bold][Scene 5: _The Tower of London_]
+
+[italic][Enter Mortimer, brought in a chair, and Gaolers.]
+
+*Mortimer.* Kind keepers of my weak decaying age,
+ Let dying Mortimer here rest himself.
+ Even like a man new haled from the rack,
+ So fare my limbs with long imprisonment;
+ And these grey locks, the pursuivants of death,
+ Nestor-like aged in an age of care,
+ Argue the end of Edmund Mortimer.
+ These eyes, like lamps whose wasting oil is spent,
+ Wax dim, as drawing to their exigent;
+ Weak shoulders, overborne with burdening grief,
+ And pithless arms, like to a withered vine
+ That droops his sapless branches to the ground.
+ Yet are these feet, whose strengthless stay is numb,
+ Unable to support this lump of clay,
+ Swift-winged with desire to get a grave,
+ As witting I no other comfort have.
+ But tell me, keeper, will my nephew come?
+
+*First Keeper.* Richard Plantagenet, my lord, will come.
+ We sent unto the Temple, unto his chamber;
+ And answer was return'd that he will come.
+
+*Mortimer.* Enough; my soul shall then be satisfied.
+ Poor gentleman! his wrong doth equal mine.
+ Since Henry Monmouth first began to reign,
+ Before whose glory I was great in arms,
+ This loathsome sequestration have I had;
+ And even since then hath Richard been obscur'd,
+ Depriv'd of honour and inheritance.
+ But now the arbitrator of despairs,
+ Just Death, kind umpire of men's miseries,
+ With sweet enlargement doth dismiss me hence.
+ I would his troubles likewise were expir'd,
+ That so he might recover what was lost.
+
+
+// -------------------------------------------------------------------------- //
+[italic][Enter Richard Plantagenet]
+
+*First Keeper.* My lord, your loving nephew now is come.
+
+*Mortimer.* Richard Plantagenet, my friend, is he come?
+
+*Plantagenet.* Ay, noble uncle, thus ignobly us'd,
+ Your nephew, late despised Richard, comes.
+
+*Mortimer.* Direct mine arms I may embrace his neck
+ And in his bosom spend my latter gasp.
+ O, tell me when my lips do touch his cheeks,
+ That I may kindly give one fainting kiss.
+ And now declare, sweet stem from York's great stock,
+ Why didst thou say of late thou wert despis'd?
+
+*Plantagenet.* First, lean thine aged back against mine arm;
+ And, in that ease, I'll tell thee my disease.
+ This day, in argument upon a case,
+ Some words there grew 'twixt Somerset and me;
+ Among which terms he us'd his lavish tongue
+ And did upbraid me with my father's death;
+ Which obloquy set bars before my tongue,
+ Else with the like I had requited him.
+ Therefore, good uncle, for my father's sake,
+ In honour of a true Plantagenet,
+ And for alliance sake, declare the cause
+ My father, Earl of Cambridge, lost his head.
+
+*Mortimer.* That cause, fair nephew, that imprison'd me
+ And hath detain'd me all my flow'ring youth
+ Within a loathsome dungeon, there to pine,
+ Was cursed instrument of his decease.
+
+*Plantagenet.* Discover more at large what cause that was,
+ For I am ignorant and cannot guess.
+
+*Mortimer.* I will, if that my fading breath permit
+ And death approach not ere my tale be done.
+ Henry the Fourth, grandfather to this king,
+ Depos'd his nephew Richard, Edward's son,
+ The first-begotten and the lawful heir
+ Of Edward king, the third of that descent;
+ During whose reign the Percies of the north,
+ Finding his usurpation most unjust,
+ Endeavour'd my advancement to the throne ...