Create some benchmarks 📊
This commit is contained in:
parent
6c8b5caa9f
commit
19be053cc3
12
Cargo.toml
12
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
|
||||
|
19
benches/complete.rs
Normal file
19
benches/complete.rs
Normal 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
45
benches/font.rs
Normal 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);
|
@ -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)?;
|
||||
|
@ -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),
|
||||
|
64
src/lib.rs
64
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"));
|
||||
}
|
||||
}
|
||||
|
88
test/shakespeare.tps
Normal file
88
test/shakespeare.tps
Normal 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 ...
|
Loading…
x
Reference in New Issue
Block a user