ab_glyph/ttfp.rs
1//! ttf-parser crate specific code. ttf-parser types should not be leaked publicly.
2mod outliner;
3#[cfg(feature = "variable-fonts")]
4mod variable;
5
6use crate::{point, v2, Font, GlyphId, GlyphImageFormat, GlyphSvg, InvalidFont, Outline, Rect};
7use alloc::boxed::Box;
8#[cfg(not(feature = "std"))]
9use alloc::vec::Vec;
10use core::fmt;
11use owned_ttf_parser::{self as ttfp, AsFaceRef};
12
13impl From<GlyphId> for ttfp::GlyphId {
14 #[inline]
15 fn from(id: GlyphId) -> Self {
16 Self(id.0)
17 }
18}
19
20/// Font data handle stored as a `&[u8]` + parsed data.
21/// See [`Font`] for more methods.
22///
23/// Also see the owned version [`FontVec`].
24///
25/// # Example
26/// ```
27/// use ab_glyph::{Font, FontRef};
28///
29/// # fn main() -> Result<(), ab_glyph::InvalidFont> {
30/// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf"))?;
31///
32/// assert_eq!(font.glyph_id('s'), ab_glyph::GlyphId(56));
33/// # Ok(()) }
34/// ```
35#[derive(Clone)]
36pub struct FontRef<'font>(ttfp::PreParsedSubtables<'font, ttfp::Face<'font>>);
37
38impl fmt::Debug for FontRef<'_> {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "FontRef")
41 }
42}
43
44impl<'font> FontRef<'font> {
45 /// Creates an `FontRef` from a byte-slice.
46 ///
47 /// For font collections see
48 /// [`FontRef::try_from_slice_and_index`].
49 ///
50 /// # Example
51 /// ```
52 /// # use ab_glyph::*;
53 /// # fn main() -> Result<(), InvalidFont> {
54 /// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf"))?;
55 /// # Ok(()) }
56 /// ```
57 #[inline]
58 pub fn try_from_slice(data: &'font [u8]) -> Result<Self, InvalidFont> {
59 Self::try_from_slice_and_index(data, 0)
60 }
61
62 /// Creates an `FontRef` from byte-slice.
63 ///
64 /// You can set index for font collections. For simple fonts use `0` or
65 /// [`FontRef::try_from_slice`].
66 ///
67 /// # Example
68 /// ```
69 /// # use ab_glyph::*;
70 /// # fn main() -> Result<(), InvalidFont> {
71 /// let font =
72 /// FontRef::try_from_slice_and_index(include_bytes!("../../dev/fonts/Exo2-Light.otf"), 0)?;
73 /// # Ok(()) }
74 /// ```
75 #[inline]
76 pub fn try_from_slice_and_index(data: &'font [u8], index: u32) -> Result<Self, InvalidFont> {
77 Ok(Self(ttfp::PreParsedSubtables::from(
78 ttfp::Face::parse(data, index).map_err(|_| InvalidFont)?,
79 )))
80 }
81}
82
83/// Font data handle stored in a `Vec<u8>` + parsed data.
84/// See [`Font`] for more methods.
85///
86/// Also see [`FontRef`].
87///
88/// # Example
89/// ```
90/// use ab_glyph::{Font, FontVec};
91///
92/// # fn main() -> Result<(), ab_glyph::InvalidFont> {
93/// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf").to_vec();
94/// let font = FontVec::try_from_vec_and_index(owned_font_data, 0)?;
95///
96/// assert_eq!(font.glyph_id('s'), ab_glyph::GlyphId(56));
97/// # Ok(()) }
98/// ```
99pub struct FontVec(ttfp::PreParsedSubtables<'static, ttfp::OwnedFace>);
100
101impl fmt::Debug for FontVec {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "FontVec")
104 }
105}
106
107impl FontVec {
108 /// Creates an `FontVec` from owned data.
109 ///
110 /// For font collections see
111 /// [`FontVec::try_from_vec_and_index`].
112 ///
113 /// # Example
114 /// ```
115 /// # use ab_glyph::*;
116 /// # fn main() -> Result<(), InvalidFont> {
117 /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf").to_vec();
118 /// let font = FontVec::try_from_vec(owned_font_data)?;
119 /// # Ok(()) }
120 /// ```
121 #[inline]
122 pub fn try_from_vec(data: Vec<u8>) -> Result<Self, InvalidFont> {
123 Self::try_from_vec_and_index(data, 0)
124 }
125
126 /// Creates an `FontVec` from owned data.
127 ///
128 /// You can set index for font collections. For simple fonts use `0` or
129 /// [`FontVec::try_from_vec`].
130 ///
131 /// # Example
132 /// ```
133 /// # use ab_glyph::*;
134 /// # fn main() -> Result<(), InvalidFont> {
135 /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf").to_vec();
136 /// let font = FontVec::try_from_vec_and_index(owned_font_data, 0)?;
137 /// # Ok(()) }
138 /// ```
139 #[inline]
140 pub fn try_from_vec_and_index(data: Vec<u8>, index: u32) -> Result<Self, InvalidFont> {
141 Ok(Self(ttfp::PreParsedSubtables::from(
142 ttfp::OwnedFace::from_vec(data, index).map_err(|_| InvalidFont)?,
143 )))
144 }
145
146 /// Extracts a slice containing the data passed into e.g. [`FontVec::try_from_vec`].
147 ///
148 /// # Example
149 /// ```
150 /// # use ab_glyph::*;
151 /// # fn main() -> Result<(), InvalidFont> {
152 /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf").to_vec();
153 /// let font_data_clone = owned_font_data.clone();
154 /// let font = FontVec::try_from_vec(owned_font_data)?;
155 /// assert_eq!(font.as_slice(), font_data_clone);
156 /// # Ok(()) }
157 /// ```
158 #[inline]
159 pub fn as_slice(&self) -> &[u8] {
160 self.0.face.as_slice()
161 }
162
163 /// Unwraps the data passed into e.g. [`FontVec::try_from_vec`].
164 ///
165 /// # Example
166 /// ```
167 /// # use ab_glyph::*;
168 /// # fn main() -> Result<(), InvalidFont> {
169 /// # let owned_font_data = include_bytes!("../../dev/fonts/Exo2-Light.otf").to_vec();
170 /// let font_data_clone = owned_font_data.clone();
171 /// let font = FontVec::try_from_vec(owned_font_data)?;
172 /// assert_eq!(font.into_vec(), font_data_clone);
173 /// # Ok(()) }
174 /// ```
175 #[inline]
176 pub fn into_vec(self) -> Vec<u8> {
177 self.0.face.into_vec()
178 }
179}
180
181/// Implement `Font` for `Self(AsFontRef)` types.
182macro_rules! impl_font {
183 ($font:ty) => {
184 impl Font for $font {
185 #[inline]
186 fn units_per_em(&self) -> Option<f32> {
187 // TODO unwrap signature when making next breaking change
188 Some(self.0.as_face_ref().units_per_em().into())
189 }
190
191 #[inline]
192 fn ascent_unscaled(&self) -> f32 {
193 self.0.as_face_ref().ascender().into()
194 }
195
196 #[inline]
197 fn descent_unscaled(&self) -> f32 {
198 self.0.as_face_ref().descender().into()
199 }
200
201 #[inline]
202 fn line_gap_unscaled(&self) -> f32 {
203 self.0.as_face_ref().line_gap().into()
204 }
205
206 #[inline]
207 fn italic_angle(&self) -> f32 {
208 self.0.as_face_ref().italic_angle()
209 }
210
211 #[inline]
212 fn glyph_id(&self, c: char) -> GlyphId {
213 // Note: Using `PreParsedSubtables` method for better performance.
214 let index = self.0.glyph_index(c).map(|id| id.0).unwrap_or(0);
215 GlyphId(index)
216 }
217
218 #[inline]
219 fn h_advance_unscaled(&self, id: GlyphId) -> f32 {
220 self.0
221 .as_face_ref()
222 .glyph_hor_advance(id.into())
223 .unwrap_or_default()
224 .into()
225 }
226
227 #[inline]
228 fn h_side_bearing_unscaled(&self, id: GlyphId) -> f32 {
229 self.0
230 .as_face_ref()
231 .glyph_hor_side_bearing(id.into())
232 .unwrap_or_default()
233 .into()
234 }
235
236 #[inline]
237 fn v_advance_unscaled(&self, id: GlyphId) -> f32 {
238 self.0
239 .as_face_ref()
240 .glyph_ver_advance(id.into())
241 .unwrap_or_default()
242 .into()
243 }
244
245 #[inline]
246 fn v_side_bearing_unscaled(&self, id: GlyphId) -> f32 {
247 self.0
248 .as_face_ref()
249 .glyph_ver_side_bearing(id.into())
250 .unwrap_or_default()
251 .into()
252 }
253
254 #[inline]
255 fn kern_unscaled(&self, first: GlyphId, second: GlyphId) -> f32 {
256 // Note: Using `PreParsedSubtables` method for better performance.
257 self.0
258 .glyphs_hor_kerning(first.into(), second.into())
259 .map(f32::from)
260 .unwrap_or_default()
261 }
262
263 fn outline(&self, id: GlyphId) -> Option<Outline> {
264 let mut outliner = outliner::OutlineCurveBuilder::default();
265
266 let ttfp::Rect {
267 x_min,
268 x_max,
269 y_min,
270 y_max,
271 } = self
272 .0
273 .as_face_ref()
274 .outline_glyph(id.into(), &mut outliner)
275 // invalid bounds are treated as having no outline
276 .filter(|b| b.x_min < b.x_max && b.y_min < b.y_max)?;
277
278 let curves = outliner.take_outline();
279
280 let bounds = Rect {
281 min: point(x_min.into(), y_max.into()),
282 max: point(x_max.into(), y_min.into()),
283 };
284
285 Some(Outline { bounds, curves })
286 }
287
288 #[inline]
289 fn glyph_count(&self) -> usize {
290 self.0.as_face_ref().number_of_glyphs() as _
291 }
292
293 fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
294 let face_ref = self.0.as_face_ref();
295
296 #[cfg(feature = "std")]
297 let mut used_indices =
298 std::collections::HashSet::with_capacity(face_ref.number_of_glyphs() as _);
299 #[cfg(not(feature = "std"))]
300 let mut used_indices = alloc::collections::BTreeSet::new();
301
302 let inner = Box::new(
303 face_ref
304 .tables()
305 .cmap
306 .iter()
307 .flat_map(|c| c.subtables)
308 .filter(|s| s.is_unicode())
309 .flat_map(move |subtable| {
310 let mut pairs = Vec::new();
311 subtable.codepoints(|c| {
312 if let Ok(ch) = char::try_from(c) {
313 if let Some(idx) = subtable.glyph_index(c).filter(|i| i.0 > 0) {
314 if used_indices.insert(idx.0) {
315 pairs.push((GlyphId(idx.0), ch));
316 }
317 }
318 }
319 });
320 pairs
321 }),
322 );
323
324 crate::CodepointIdIter { inner }
325 }
326
327 fn glyph_raster_image2(&self, id: GlyphId, size: u16) -> Option<v2::GlyphImage> {
328 use GlyphImageFormat::*;
329
330 let img = self.0.as_face_ref().glyph_raster_image(id.into(), size)?;
331 Some(v2::GlyphImage {
332 origin: point(img.x.into(), img.y.into()),
333 width: img.width,
334 height: img.height,
335 pixels_per_em: img.pixels_per_em,
336 data: img.data,
337 format: match img.format {
338 ttfp::RasterImageFormat::PNG => Png,
339 ttfp::RasterImageFormat::BitmapMono => BitmapMono,
340 ttfp::RasterImageFormat::BitmapMonoPacked => BitmapMonoPacked,
341 ttfp::RasterImageFormat::BitmapGray2 => BitmapGray2,
342 ttfp::RasterImageFormat::BitmapGray2Packed => BitmapGray2Packed,
343 ttfp::RasterImageFormat::BitmapGray4 => BitmapGray4,
344 ttfp::RasterImageFormat::BitmapGray4Packed => BitmapGray4Packed,
345 ttfp::RasterImageFormat::BitmapGray8 => BitmapGray8,
346 ttfp::RasterImageFormat::BitmapPremulBgra32 => BitmapPremulBgra32,
347 },
348 })
349 }
350
351 fn glyph_svg_image(&self, id: GlyphId) -> Option<GlyphSvg> {
352 let img = self.0.as_face_ref().glyph_svg_image(id.into())?;
353
354 Some(GlyphSvg {
355 data: img.data,
356 start_glyph_id: GlyphId(img.start_glyph_id.0),
357 end_glyph_id: GlyphId(img.end_glyph_id.0),
358 })
359 }
360
361 #[inline]
362 fn font_data(&self) -> &[u8] {
363 self.0.as_face_ref().raw_face().data
364 }
365 }
366 };
367}
368
369impl_font!(FontRef<'_>);
370impl_font!(FontVec);