1use crate::{Glue, Scaled};
4
5#[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 pub const NULL: Id = Id(0);
15
16 pub const ONE: Id = Id(1);
18}
19
20impl Default for Id {
21 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
35pub 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 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 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
69pub trait Format {
72 fn null() -> Self;
74
75 fn width(&self, c: char) -> Option<Scaled>;
77 fn height(&self, c: char) -> Option<Scaled>;
79 fn depth(&self, c: char) -> Option<Scaled>;
81
82 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}