1pub mod color;
4
5mod book;
6mod exceptions;
7mod variant;
8
9pub use self::book::{Coverage, FontBook, FontFlags, FontInfo};
10pub use self::variant::{FontStretch, FontStyle, FontVariant, FontWeight};
11
12use std::cell::OnceCell;
13use std::fmt::{self, Debug, Formatter};
14use std::hash::{Hash, Hasher};
15use std::sync::Arc;
16
17use ttf_parser::GlyphId;
18
19use self::book::find_name;
20use crate::foundations::{Bytes, Cast};
21use crate::layout::{Abs, Em, Frame};
22use crate::text::{BottomEdge, TopEdge};
23
24#[derive(Clone)]
28pub struct Font(Arc<Repr>);
29
30struct Repr {
32 index: u32,
34 info: FontInfo,
36 metrics: FontMetrics,
38 ttf: ttf_parser::Face<'static>,
40 rusty: rustybuzz::Face<'static>,
42 data: Bytes,
50}
51
52impl Font {
53 pub fn new(data: Bytes, index: u32) -> Option<Self> {
55 let slice: &'static [u8] =
62 unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
63
64 let ttf = ttf_parser::Face::parse(slice, index).ok()?;
65 let rusty = rustybuzz::Face::from_slice(slice, index)?;
66 let metrics = FontMetrics::from_ttf(&ttf);
67 let info = FontInfo::from_ttf(&ttf)?;
68
69 Some(Self(Arc::new(Repr { data, index, info, metrics, ttf, rusty })))
70 }
71
72 pub fn iter(data: Bytes) -> impl Iterator<Item = Self> {
74 let count = ttf_parser::fonts_in_collection(&data).unwrap_or(1);
75 (0..count).filter_map(move |index| Self::new(data.clone(), index))
76 }
77
78 pub fn data(&self) -> &Bytes {
80 &self.0.data
81 }
82
83 pub fn index(&self) -> u32 {
85 self.0.index
86 }
87
88 pub fn info(&self) -> &FontInfo {
90 &self.0.info
91 }
92
93 pub fn metrics(&self) -> &FontMetrics {
95 &self.0.metrics
96 }
97
98 pub fn units_per_em(&self) -> f64 {
100 self.0.metrics.units_per_em
101 }
102
103 pub fn to_em(&self, units: impl Into<f64>) -> Em {
105 Em::from_units(units, self.units_per_em())
106 }
107
108 pub fn advance(&self, glyph: u16) -> Option<Em> {
110 self.0
111 .ttf
112 .glyph_hor_advance(GlyphId(glyph))
113 .map(|units| self.to_em(units))
114 }
115
116 pub fn find_name(&self, id: u16) -> Option<String> {
118 find_name(&self.0.ttf, id)
119 }
120
121 pub fn ttf(&self) -> &ttf_parser::Face<'_> {
123 &self.0.ttf
126 }
127
128 pub fn rusty(&self) -> &rustybuzz::Face<'_> {
130 &self.0.rusty
133 }
134
135 pub fn edges(
137 &self,
138 top_edge: TopEdge,
139 bottom_edge: BottomEdge,
140 font_size: Abs,
141 bounds: TextEdgeBounds,
142 ) -> (Abs, Abs) {
143 let cell = OnceCell::new();
144 let bbox = |gid, f: fn(ttf_parser::Rect) -> i16| {
145 cell.get_or_init(|| self.ttf().glyph_bounding_box(GlyphId(gid)))
146 .map(|bbox| self.to_em(f(bbox)).at(font_size))
147 .unwrap_or_default()
148 };
149
150 let top = match top_edge {
151 TopEdge::Metric(metric) => match metric.try_into() {
152 Ok(metric) => self.metrics().vertical(metric).at(font_size),
153 Err(_) => match bounds {
154 TextEdgeBounds::Zero => Abs::zero(),
155 TextEdgeBounds::Frame(frame) => frame.ascent(),
156 TextEdgeBounds::Glyph(gid) => bbox(gid, |b| b.y_max),
157 },
158 },
159 TopEdge::Length(length) => length.at(font_size),
160 };
161
162 let bottom = match bottom_edge {
163 BottomEdge::Metric(metric) => match metric.try_into() {
164 Ok(metric) => -self.metrics().vertical(metric).at(font_size),
165 Err(_) => match bounds {
166 TextEdgeBounds::Zero => Abs::zero(),
167 TextEdgeBounds::Frame(frame) => frame.descent(),
168 TextEdgeBounds::Glyph(gid) => -bbox(gid, |b| b.y_min),
169 },
170 },
171 BottomEdge::Length(length) => -length.at(font_size),
172 };
173
174 (top, bottom)
175 }
176}
177
178impl Hash for Font {
179 fn hash<H: Hasher>(&self, state: &mut H) {
180 self.0.data.hash(state);
181 self.0.index.hash(state);
182 }
183}
184
185impl Debug for Font {
186 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
187 write!(f, "Font({}, {:?})", self.info().family, self.info().variant)
188 }
189}
190
191impl Eq for Font {}
192
193impl PartialEq for Font {
194 fn eq(&self, other: &Self) -> bool {
195 self.0.data == other.0.data && self.0.index == other.0.index
196 }
197}
198
199#[derive(Debug, Copy, Clone)]
201pub struct FontMetrics {
202 pub units_per_em: f64,
204 pub ascender: Em,
206 pub cap_height: Em,
208 pub x_height: Em,
210 pub descender: Em,
212 pub strikethrough: LineMetrics,
214 pub underline: LineMetrics,
216 pub overline: LineMetrics,
218}
219
220impl FontMetrics {
221 pub fn from_ttf(ttf: &ttf_parser::Face) -> Self {
223 let units_per_em = f64::from(ttf.units_per_em());
224 let to_em = |units| Em::from_units(units, units_per_em);
225
226 let ascender = to_em(ttf.typographic_ascender().unwrap_or(ttf.ascender()));
227 let cap_height = ttf.capital_height().filter(|&h| h > 0).map_or(ascender, to_em);
228 let x_height = ttf.x_height().filter(|&h| h > 0).map_or(ascender, to_em);
229 let descender = to_em(ttf.typographic_descender().unwrap_or(ttf.descender()));
230 let strikeout = ttf.strikeout_metrics();
231 let underline = ttf.underline_metrics();
232
233 let strikethrough = LineMetrics {
234 position: strikeout.map_or(Em::new(0.25), |s| to_em(s.position)),
235 thickness: strikeout
236 .or(underline)
237 .map_or(Em::new(0.06), |s| to_em(s.thickness)),
238 };
239
240 let underline = LineMetrics {
241 position: underline.map_or(Em::new(-0.2), |s| to_em(s.position)),
242 thickness: underline
243 .or(strikeout)
244 .map_or(Em::new(0.06), |s| to_em(s.thickness)),
245 };
246
247 let overline = LineMetrics {
248 position: cap_height + Em::new(0.1),
249 thickness: underline.thickness,
250 };
251
252 Self {
253 units_per_em,
254 ascender,
255 cap_height,
256 x_height,
257 descender,
258 strikethrough,
259 underline,
260 overline,
261 }
262 }
263
264 pub fn vertical(&self, metric: VerticalFontMetric) -> Em {
266 match metric {
267 VerticalFontMetric::Ascender => self.ascender,
268 VerticalFontMetric::CapHeight => self.cap_height,
269 VerticalFontMetric::XHeight => self.x_height,
270 VerticalFontMetric::Baseline => Em::zero(),
271 VerticalFontMetric::Descender => self.descender,
272 }
273 }
274}
275
276#[derive(Debug, Copy, Clone)]
278pub struct LineMetrics {
279 pub position: Em,
282 pub thickness: Em,
284}
285
286#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
288pub enum VerticalFontMetric {
289 Ascender,
291 CapHeight,
293 XHeight,
295 Baseline,
297 Descender,
299}
300
301#[derive(Debug, Copy, Clone)]
303pub enum TextEdgeBounds<'a> {
304 Zero,
306 Glyph(u16),
308 Frame(&'a Frame),
310}