summaryrefslogtreecommitdiff
path: root/src/font/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/font/mod.rs')
-rw-r--r--src/font/mod.rs174
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),