common/
font.rs

1//! Fonts and font repositories.
2
3use crate::{Glue, Scaled};
4
5/// Identifier for a font.
6///
7/// The zero value is the null font; real fonts start at 1.
8#[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Hash)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct Id(pub u32);
11
12impl Id {
13    /// The null font.
14    pub const NULL: Id = Id(0);
15
16    /// The first non-null font ID.
17    pub const ONE: Id = Id(1);
18}
19
20impl Default for Id {
21    /// The default font ID is the null font,
22    /// matching TeX's behavior for font variables that have not been set
23    /// (TeX.2021.222).
24    fn default() -> Self {
25        Id::NULL
26    }
27}
28
29impl std::fmt::Display for Id {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        write!(f, "{}", self.0)
32    }
33}
34
35/// A repository (repo) of fonts of a given type.
36pub struct Repo<Font> {
37    fonts: Vec<Font>,
38    next_id: Id,
39}
40
41impl<F: Format> Default for Repo<F> {
42    fn default() -> Self {
43        Self {
44            fonts: vec![F::null()],
45            next_id: Id(1),
46        }
47    }
48}
49
50impl<FontType> Repo<FontType> {
51    /// Register a new font in the repo.
52    pub fn register(&mut self, font: FontType) -> Id {
53        self.fonts.push(font);
54        let id = self.next_id;
55        self.next_id = Id(id.0.checked_add(1).expect("no more than 2^32 fonts"));
56        id
57    }
58
59    /// Get a font from the repository.
60    ///
61    /// Panics if the font has not been registered.
62    pub fn get(&self, id: Id) -> &FontType {
63        self.fonts
64            .get(id.0 as usize)
65            .expect("font has been registered")
66    }
67}
68
69/// Implementations of this trait provide the minimal set of features
70/// required by a font format.
71pub trait Format {
72    /// Return the null font in this format.
73    fn null() -> Self;
74
75    /// Get the width of the character, or [`None`] if the character is not defined in the font.
76    fn width(&self, c: char) -> Option<Scaled>;
77    /// Get the height of the character, or [`None`] if the character is not defined in the font.
78    fn height(&self, c: char) -> Option<Scaled>;
79    /// Get the depth of the character, or [`None`] if the character is not defined in the font.
80    fn depth(&self, c: char) -> Option<Scaled>;
81
82    /// Get the width, height and depth of the character, or [`None`] if the character is not
83    /// defined in the font.
84    fn width_height_depth(&self, c: char) -> Option<[Scaled; 3]> {
85        Some([
86            self.width(c)?,
87            self.height(c).unwrap_or(Scaled::ZERO),
88            self.depth(c).unwrap_or(Scaled::ZERO),
89        ])
90    }
91
92    fn default_space(&self) -> Glue;
93    fn extra_space(&self) -> Scaled;
94}
95
96#[derive(PartialEq, Debug)]
97pub enum TextItem {
98    Char(char),
99    Kern(Scaled),
100    Ligature {
101        c: char,
102        original: std::rc::Rc<str>,
103        includes_left_boundary: bool,
104        includes_right_boundary: bool,
105    },
106}
107
108pub trait TextIter: Iterator<Item = TextItem> {
109    fn is_separation_point(&self) -> bool;
110}
111
112pub trait TextBuilder {
113    type TextIter<'a, Word: Iterator<Item = char>>: TextIter
114    where
115        Self: 'a;
116    fn build_text<'a, Word: Iterator<Item = char>>(
117        &'a self,
118        word: Word,
119        options: BuildTextOptions,
120    ) -> Self::TextIter<'a, Word>;
121
122    fn has_replacement(&self, left: Option<char>, right: Option<char>) -> bool;
123}
124
125#[derive(Default)]
126pub struct BuildTextOptions {
127    pub disable_left_boundary: bool,
128    pub right_boundary_override: Option<char>,
129}
130
131pub struct DefaultTextIter<Word> {
132    word: Word,
133}
134
135impl<Word> DefaultTextIter<Word> {
136    pub fn new(word: Word) -> Self {
137        Self { word }
138    }
139}
140
141impl<Word: Iterator<Item = char>> Iterator for DefaultTextIter<Word> {
142    type Item = TextItem;
143
144    fn next(&mut self) -> Option<Self::Item> {
145        self.word.next().map(TextItem::Char)
146    }
147}
148
149impl<Word: Iterator<Item = char>> TextIter for DefaultTextIter<Word> {
150    fn is_separation_point(&self) -> bool {
151        true
152    }
153}