diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/main.rs | 21 | ||||
| -rw-r--r-- | src/font/mod.rs | 69 | ||||
| -rw-r--r-- | src/lib.rs | 64 |
3 files changed, 74 insertions, 80 deletions
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), @@ -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")); } } |
