1pub mod color;
4
5mod book;
6mod exceptions;
7mod info;
8mod metrics;
9mod tag;
10mod variant;
11mod variations;
12
13pub use self::book::FontBook;
14pub use self::info::{Coverage, FontFlags, FontInfo};
15pub use self::metrics::{
16 FontMetrics, LineMetrics, MathConstants, ScriptMetrics, TextEdgeBounds,
17 VerticalFontMetric,
18};
19pub use self::tag::Tag;
20pub use self::variant::{FontStretch, FontStyle, FontVariant, FontWeight};
21pub use self::variations::{AxisValue, FontAxis, FontVariations, StandardAxes};
22
23use std::cell::OnceCell;
24use std::fmt::{self, Debug, Formatter};
25use std::hash::{Hash, Hasher};
26use std::ops::Deref;
27use std::sync::Arc;
28
29use ttf_parser::{GlyphId, name_id};
30
31use self::exceptions::find_exception;
32use self::info::find_name;
33use crate::foundations::Bytes;
34use crate::layout::{Abs, Em};
35use crate::text::{BottomEdge, TopEdge};
36
37#[derive(Clone)]
41pub struct Font(Arc<FontInner>);
42
43struct FontInner {
45 index: u32,
47 info: FontInfo,
49 ttf: ttf_parser::Face<'static>,
55 data: Bytes,
59}
60
61impl Font {
62 pub fn new(data: Bytes, index: u32) -> Option<Self> {
64 let slice: &'static [u8] =
71 unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
72
73 let ttf = ttf_parser::Face::parse(slice, index).ok()?;
74 let info = FontInfo::from_ttf(&ttf)?;
75
76 Some(Self(Arc::new(FontInner { index, info, ttf, data })))
77 }
78
79 pub fn iter(data: Bytes) -> impl Iterator<Item = Self> {
81 let count = ttf_parser::fonts_in_collection(&data).unwrap_or(1);
82 (0..count).filter_map(move |index| Self::new(data.clone(), index))
83 }
84
85 pub fn data(&self) -> &Bytes {
87 &self.0.data
88 }
89
90 pub fn index(&self) -> u32 {
92 self.0.index
93 }
94
95 pub fn info(&self) -> &FontInfo {
97 &self.0.info
98 }
99
100 pub fn post_script_name(&self) -> Option<String> {
102 find_name(&self.0.ttf, name_id::POST_SCRIPT_NAME)
103 }
104
105 #[comemo::memoize]
108 pub fn instantiate(
109 self,
110 variant: FontVariant,
111 size: Abs,
112 custom: &FontVariations,
113 ) -> FontInstance {
114 let axes = &self.info().axes;
115 let automatic = FontVariations::resolve(axes, variant, size);
116 let full = automatic.chain(custom).normalized();
117 self.instantiate_impl(full)
118 }
119
120 #[comemo::memoize]
123 fn instantiate_impl(self, variations: FontVariations) -> FontInstance {
124 let data = self.data();
125 let index = self.index();
126
127 let slice: &'static [u8] =
129 unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) };
130
131 let mut rusty = rustybuzz::Face::from_slice(slice, index).unwrap();
132 for &(tag, value) in &variations.0 {
133 rusty.set_variation(tag.into(), value.0);
134 }
135
136 let metrics = FontMetrics::from_ttf(&rusty);
137
138 FontInstance(Arc::new(FontInstanceInner {
139 metrics,
140 rusty,
141 variations,
142 font: self,
143 }))
144 }
145}
146
147impl Debug for Font {
148 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
149 write!(f, "Font({}, {:?})", self.info().family, self.info().variant)
150 }
151}
152
153impl Hash for Font {
154 fn hash<H: Hasher>(&self, state: &mut H) {
155 self.0.data.hash(state);
156 self.0.index.hash(state);
157 }
158}
159
160impl Eq for Font {}
161
162impl PartialEq for Font {
163 fn eq(&self, other: &Self) -> bool {
164 self.0.data == other.0.data && self.0.index == other.0.index
165 }
166}
167
168#[derive(Clone)]
172pub struct FontInstance(Arc<FontInstanceInner>);
173
174struct FontInstanceInner {
176 metrics: FontMetrics,
178 rusty: rustybuzz::Face<'static>,
184 variations: FontVariations,
186 font: Font,
188}
189
190impl FontInstance {
191 pub fn font(&self) -> &Font {
193 &self.0.font
194 }
195
196 pub fn variations(&self) -> &FontVariations {
198 &self.0.variations
199 }
200
201 pub fn metrics(&self) -> &FontMetrics {
203 &self.0.metrics
204 }
205
206 #[inline]
208 pub fn math(&self) -> &MathConstants {
209 self.0.metrics.math.get_or_init(|| MathConstants::new(self))
210 }
211
212 pub fn units_per_em(&self) -> f64 {
214 self.0.metrics.units_per_em
215 }
216
217 pub fn to_em(&self, units: impl Into<f64>) -> Em {
219 Em::from_units(units, self.units_per_em())
220 }
221
222 pub fn x_advance(&self, glyph: u16) -> Option<Em> {
224 self.0
225 .rusty
226 .glyph_hor_advance(GlyphId(glyph))
227 .map(|units| self.to_em(units))
228 }
229
230 pub fn y_advance(&self, glyph: u16) -> Option<Em> {
232 self.0
233 .rusty
234 .glyph_ver_advance(GlyphId(glyph))
235 .map(|units| self.to_em(units))
236 }
237
238 pub fn ttf(&self) -> &ttf_parser::Face<'_> {
240 &self.0.rusty
243 }
244
245 pub fn rusty(&self) -> &rustybuzz::Face<'_> {
247 &self.0.rusty
250 }
251
252 pub fn edges(
254 &self,
255 top_edge: TopEdge,
256 bottom_edge: BottomEdge,
257 font_size: Abs,
258 bounds: TextEdgeBounds,
259 ) -> (Abs, Abs) {
260 let cell = OnceCell::new();
261 let bbox = |gid, f: fn(ttf_parser::Rect) -> i16| {
262 cell.get_or_init(|| self.ttf().glyph_bounding_box(GlyphId(gid)))
263 .map(|bbox| self.to_em(f(bbox)).at(font_size))
264 .unwrap_or_default()
265 };
266
267 let top = match top_edge {
268 TopEdge::Metric(metric) => match metric.try_into() {
269 Ok(metric) => self.metrics().vertical(metric).at(font_size),
270 Err(_) => match bounds {
271 TextEdgeBounds::Zero => Abs::zero(),
272 TextEdgeBounds::Frame(frame) => frame.ascent(),
273 TextEdgeBounds::Glyph(gid) => bbox(gid, |b| b.y_max),
274 },
275 },
276 TopEdge::Length(length) => length.at(font_size),
277 };
278
279 let bottom = match bottom_edge {
280 BottomEdge::Metric(metric) => match metric.try_into() {
281 Ok(metric) => -self.metrics().vertical(metric).at(font_size),
282 Err(_) => match bounds {
283 TextEdgeBounds::Zero => Abs::zero(),
284 TextEdgeBounds::Frame(frame) => frame.descent(),
285 TextEdgeBounds::Glyph(gid) => -bbox(gid, |b| b.y_min),
286 },
287 },
288 BottomEdge::Length(length) => -length.at(font_size),
289 };
290
291 (top, bottom)
292 }
293}
294
295impl Deref for FontInstance {
296 type Target = Font;
297
298 fn deref(&self) -> &Self::Target {
299 self.font()
300 }
301}
302
303impl Debug for FontInstance {
304 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
305 f.debug_struct("FontInstance")
306 .field("font", self.font())
307 .field("variations", self.variations())
308 .finish()
309 }
310}
311
312impl Hash for FontInstance {
313 fn hash<H: Hasher>(&self, state: &mut H) {
314 self.0.font.hash(state);
315 self.0.variations.hash(state);
316 }
317}
318
319impl Eq for FontInstance {}
320
321impl PartialEq for FontInstance {
322 fn eq(&self, other: &Self) -> bool {
323 self.0.font == other.0.font && self.0.variations == other.0.variations
324 }
325}