diff options
Diffstat (limited to 'src/font/mod.rs')
| -rw-r--r-- | src/font/mod.rs | 174 |
1 files changed, 88 insertions, 86 deletions
diff --git a/src/font/mod.rs b/src/font/mod.rs index 5d257a9a..80d900dc 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -1,7 +1,7 @@ -//! Font loading and transforming. +//! Font loading and subsetting. //! //! # Font handling -//! To do the typesetting, the typesetting engine needs font data. To be highly portable the engine +//! To do the typesetting, the engine needs font data. However, to be highly portable the engine //! itself assumes nothing about the environment. To still work with fonts, the consumer of this //! library has to add _font providers_ to their typesetting instance. These can be queried for font //! data given flexible font filters specifying required font families and styles. A font provider @@ -19,28 +19,29 @@ use opentype::{Error as OpentypeError, OpenTypeReader}; use opentype::tables::{Header, Name, CharMap, HorizontalMetrics, Post, OS2}; use opentype::types::{MacStyleFlags, NameEntry}; -pub use self::loader::{FontLoader, FontQuery}; use self::subset::Subsetter; use crate::size::Size; mod loader; mod subset; +pub use loader::{FontLoader, FontQuery}; -/// A loaded and parsed font program. + +/// A parsed _OpenType_ font program. #[derive(Debug, Clone)] pub struct Font { - /// The base name of the font. + /// The name of the font. pub name: String, - /// The raw bytes of the font program. + /// The complete, raw bytes of the font program. pub program: Vec<u8>, - /// A mapping from character codes to glyph ids. + /// The mapping from character codes to glyph ids. pub mapping: HashMap<char, u16>, /// The widths of the glyphs indexed by glyph id. pub widths: Vec<Size>, - /// The fallback glyph. + /// The id of the fallback glyph. pub default_glyph: u16, - /// The typesetting-relevant metrics of this font. + /// The typesetting or exporting-relevant metrics of this font. pub metrics: FontMetrics, } @@ -51,32 +52,31 @@ pub struct FontMetrics { pub italic: bool, /// Whether font is monospace. pub monospace: bool, - /// The angle of text in italics. + /// The angle of text in italics (in counter-clockwise degrees from vertical). pub italic_angle: f32, - /// The glyph bounding box: [x_min, y_min, x_max, y_max], + /// The extremal values [x_min, y_min, x_max, y_max] for all glyph bounding boxes. pub bounding_box: [Size; 4], - /// The typographics ascender. + /// The typographic ascender. pub ascender: Size, - /// The typographics descender. + /// The typographic descender. pub descender: Size, /// The approximate height of capital letters. pub cap_height: Size, - /// The weight class of the font. + /// The weight class of the font (from 100 for thin to 900 for heavy). pub weight_class: u16, } impl Font { - /// Create a new font from a raw font program. + /// Create a `Font` from a raw font program. pub fn new(program: Vec<u8>) -> FontResult<Font> { - // Create an OpentypeReader to parse the font tables. let cursor = Cursor::new(&program); let mut reader = OpenTypeReader::new(cursor); - // Read the relevant tables - // (all of these are required by the OpenType specification, so we expect them). + // All of these tables are required by the OpenType specification, + // so we do not really have to handle the case that they are missing. let head = reader.read_table::<Header>()?; let name = reader.read_table::<Name>()?; - let os2 = reader.read_table::<OS2>()?; + let os2 = reader.read_table::<OS2>()?; let cmap = reader.read_table::<CharMap>()?; let hmtx = reader.read_table::<HorizontalMetrics>()?; let post = reader.read_table::<Post>()?; @@ -85,15 +85,13 @@ impl Font { let font_unit_ratio = 1.0 / (head.units_per_em as f32); let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); - // Find out the name of the font. - let font_name = name.get_decoded(NameEntry::PostScriptName) + let font_name = name + .get_decoded(NameEntry::PostScriptName) .unwrap_or_else(|| "unknown".to_owned()); - // Convert the widths from font units to sizes. let widths = hmtx.metrics.iter() .map(|m| font_unit_to_size(m.advance_width as f32)).collect(); - // Calculate the typesetting-relevant metrics. let metrics = FontMetrics { italic: head.mac_style.contains(MacStyleFlags::ITALIC), monospace: post.is_fixed_pitch, @@ -120,51 +118,82 @@ impl Font { }) } - /// Map a character to it's glyph index. + /// Encode a character into it's glyph id. #[inline] - pub fn map(&self, c: char) -> u16 { - self.mapping.get(&c).map(|&g| g).unwrap_or(self.default_glyph) + pub fn encode(&self, character: char) -> u16 { + self.mapping.get(&character).map(|&g| g).unwrap_or(self.default_glyph) } - /// Encode the given text for this font (into glyph ids). + /// Encode the given text into a vector of glyph ids. #[inline] - pub fn encode(&self, text: &str) -> Vec<u8> { - // Each glyph id takes two bytes that we encode in big endian. - let mut bytes = Vec::with_capacity(2 * text.len()); - for glyph in text.chars().map(|c| self.map(c)) { + pub fn encode_text(&self, text: &str) -> Vec<u8> { + const BYTES_PER_GLYPH: usize = 2; + let mut bytes = Vec::with_capacity(BYTES_PER_GLYPH * text.len()); + for c in text.chars() { + let glyph = self.encode(c); bytes.push((glyph >> 8) as u8); bytes.push((glyph & 0xff) as u8); } bytes } - /// Generate a subsetted version of this font including only the chars listed in `chars`. + /// Generate a subsetted version of this font. /// - /// The filter functions decides which tables to keep and which not based on their tag. + /// This version includes only the given `chars` and _OpenType_ `tables`. #[inline] pub fn subsetted<C, I, S>(&self, chars: C, tables: I) -> Result<Font, FontError> - where C: IntoIterator<Item=char>, I: IntoIterator<Item=S>, S: AsRef<str> { + where + C: IntoIterator<Item=char>, + I: IntoIterator<Item=S>, + S: AsRef<str> + { Subsetter::subset(self, chars, tables) } } -/// Categorizes a font. +/// A type that provides fonts. +pub trait FontProvider { + /// Returns a font with the given info if this provider has one. + fn get(&self, info: &FontInfo) -> Option<Box<dyn FontData>>; + + /// The available fonts this provider can serve. While these should generally + /// be retrievable through the `get` method, this is not guaranteed. + fn available<'p>(&'p self) -> &'p [FontInfo]; +} + +/// A wrapper trait around `Read + Seek`. +/// +/// This type is needed because currently you can't make a trait object with two traits, like +/// `Box<dyn Read + Seek>`. Automatically implemented for all types that are [`Read`] and [`Seek`]. +pub trait FontData: Read + Seek {} +impl<T> FontData for T where T: Read + Seek {} + +/// Classifies a font by listing the font classes it is part of. +/// +/// All fonts with the same [`FontInfo`] are part of the same intersection +/// of [font classes](FontClass). /// -/// Can be constructed conveniently with the [`font`] macro. +/// This structure can be constructed conveniently through the [`font`] macro. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct FontInfo { - /// The font families this font is part of. + /// The font classes this font is part of. pub classes: Vec<FontClass>, } impl FontInfo { - /// Create a new font info from an iterator of classes. + /// Create a new font info from a collection of classes. + #[inline] pub fn new<I>(classes: I) -> FontInfo where I: IntoIterator<Item=FontClass> { - FontInfo { classes: classes.into_iter().collect() } + FontInfo { + classes: classes.into_iter().collect() + } } } /// A class of fonts. +/// +/// The set of all fonts can be classified into subsets of font classes like +/// _serif_ or _bold_. This enum lists such subclasses. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub enum FontClass { Serif, @@ -183,27 +212,18 @@ pub enum FontClass { /// into custom `Family`-variants and others can be named directly. /// /// # Examples -/// The font _Noto Sans_ in regular typeface. /// ``` /// # use typeset::font; +/// // Noto Sans in regular typeface. /// font!["NotoSans", "Noto", Regular, SansSerif]; -/// ``` /// -/// The font _Noto Serif_ in italics and boldface. -/// ``` -/// # use typeset::font; +/// // Noto Serif in italics and boldface. /// font!["NotoSerif", "Noto", Bold, Italic, Serif]; -/// ``` /// -/// The font _Arial_ in italics. -/// ``` -/// # use typeset::font; +/// // Arial in italics. /// font!["Arial", Italic, SansSerif]; -/// ``` /// -/// The font _Noto Emoji_, which works with all base families. 🙂 -/// ``` -/// # use typeset::font; +/// // Noto Emoji, which works in sans-serif and serif contexts. /// font!["NotoEmoji", "Noto", Regular, SansSerif, Serif, Monospace]; /// ``` #[macro_export] @@ -229,37 +249,21 @@ macro_rules! font { }}; } -/// A type that provides fonts. -pub trait FontProvider { - /// Returns a font with the given info if this provider has one. - fn get(&self, info: &FontInfo) -> Option<Box<dyn FontData>>; - - /// The available fonts this provider can serve. While these should generally be retrievable - /// through the `get` method, it does not have to be guaranteed that a font info, that is - /// contained, here yields a `Some` value when passed into `get`. - fn available<'p>(&'p self) -> &'p [FontInfo]; -} - -/// A wrapper trait around `Read + Seek`. -/// -/// This type is needed because currently you can't make a trait object with two traits, like -/// `Box<dyn Read + Seek>`. Automatically implemented for all types that are [`Read`] and [`Seek`]. -pub trait FontData: Read + Seek {} -impl<T> FontData for T where T: Read + Seek {} - /// A font provider serving fonts from a folder on the local file system. #[derive(Debug)] pub struct FileSystemFontProvider { - /// The root folder. + /// The base folder all other paths are relative to. base: PathBuf, /// Paths of the fonts relative to the `base` path. paths: Vec<PathBuf>, - /// The information for the font with the same index in `paths`. + /// The info for the font with the same index in `paths`. infos: Vec<FontInfo>, } impl FileSystemFontProvider { - /// Create a new provider from a folder and an iterator of pairs of font paths and font infos. + /// Create a new provider serving fonts from a base path. The `fonts` iterator + /// should contain paths of fonts relative to the base alongside matching + /// infos for these fonts. /// /// # Example /// Serve the two fonts `NotoSans-Regular` and `NotoSans-Italic` from the local folder @@ -271,21 +275,20 @@ impl FileSystemFontProvider { /// ("NotoSans-Italic.ttf", font!["NotoSans", Italic, SansSerif]), /// ]); /// ``` - #[inline] - pub fn new<B, I, P>(base: B, infos: I) -> FileSystemFontProvider + pub fn new<B, I, P>(base: B, fonts: I) -> FileSystemFontProvider where B: Into<PathBuf>, I: IntoIterator<Item = (P, FontInfo)>, P: Into<PathBuf>, { - // Find out how long the iterator is at least, to reserve the correct capacity for the - // vectors. - let iter = infos.into_iter(); - let min = iter.size_hint().0; + let iter = fonts.into_iter(); - // Split the iterator into two seperated vectors. + // Find out how long the iterator is at least, to reserve the correct + // capacity for the vectors. + let min = iter.size_hint().0; let mut paths = Vec::with_capacity(min); let mut infos = Vec::with_capacity(min); + for (path, info) in iter { paths.push(path.into()); infos.push(info); @@ -302,12 +305,10 @@ impl FileSystemFontProvider { impl FontProvider for FileSystemFontProvider { #[inline] fn get(&self, info: &FontInfo) -> Option<Box<dyn FontData>> { - // Find the index of the font in both arrays (early exit if there is no match). - let index = self.infos.iter().position(|i| i == info)?; - - // Open the file and return a boxed reader operating on it. + let index = self.infos.iter().position(|c| c == info)?; let path = &self.paths[index]; - let file = File::open(self.base.join(path)).ok()?; + let full_path = self.base.join(path); + let file = File::open(full_path).ok()?; Some(Box::new(BufReader::new(file)) as Box<FontData>) } @@ -317,13 +318,14 @@ impl FontProvider for FileSystemFontProvider { } } + /// The error type for font operations. pub enum FontError { /// The font file is incorrect. InvalidFont(String), /// A character requested for subsetting was not present in the source font. MissingCharacter(char), - /// A requested table was not present. + /// A requested or required table was not present. MissingTable(String), /// The table is unknown to the subsetting engine. UnsupportedTable(String), |
