Create some benchmarks 📊

This commit is contained in:
Laurenz Mädje 2019-07-28 21:03:33 +02:00
parent 6c8b5caa9f
commit 19be053cc3
7 changed files with 238 additions and 80 deletions

View File

@ -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

19
benches/complete.rs Normal file
View File

@ -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);

45
benches/font.rs Normal file
View File

@ -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);

View File

@ -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)?;

View File

@ -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),

View File

@ -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"));
}
}

88
test/shakespeare.tps Normal file
View File

@ -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 ...