1use crate::{
2 point, v2, Glyph, GlyphId, Outline, OutlinedGlyph, PxScale, PxScaleFont, Rect, ScaleFont,
3};
4
5/// Functionality required from font data.
6///
7/// See also [`FontArc`](struct.FontArc.html), [`FontRef`](struct.FontRef.html)
8/// and [`FontVec`](struct.FontVec.html).
9///
10/// ## Units
11///
12/// Units of unscaled accessors are "font units", which is an arbitrary unit
13/// defined by the font. See [`Font::units_per_em`].
14///
15/// ab_glyph uses a non-standard scale [`PxScale`] which is the pixel height
16/// of the text. See [`Font::pt_to_px_scale`] to convert standard point sizes.
17pub trait Font {
18 /// Get the size of the font unit
19 ///
20 /// This returns "font units per em", where 1em is a base unit of font scale
21 /// (typically the width of a capital 'M').
22 ///
23 /// Returns `None` in case the font unit size exceeds the expected range.
24 /// See [`Face::units_per_em`](https://docs.rs/ttf-parser/latest/ttf_parser/struct.Face.html#method.units_per_em).
25 ///
26 /// May be used to calculate [`PxScale`] from pt size, see [`Font::pt_to_px_scale`].
27 fn units_per_em(&self) -> Option<f32>;
28
29 /// Converts pt units into [`PxScale`].
30 ///
31 /// Note: To handle a screen scale factor multiply it to the `pt_size` argument.
32 ///
33 /// Returns `None` in case the [`Font::units_per_em`] unit size exceeds the expected range.
34 ///
35 /// ## Point size (pt)
36 ///
37 /// Font sizes are typically specified in "points". According to the modern
38 /// standard, 1pt = 1/72in. The "point size" of a font is the number of points
39 /// per em.
40 ///
41 /// The DPI (dots-per-inch) of a screen depends on the screen in question;
42 /// 96 DPI is often considered the "standard". For high-DPI displays the
43 /// DPI may be specified directly or one may multiply 96 by a scale-factor.
44 ///
45 /// Thus, for example, a 10pt font on a 96 pixels-per-inch display has
46 /// 10 / 72 * 96 = 13.333... pixels-per-em. If we divide this number by
47 /// `units_per_em` we then get a scaling factor: pixels-per-font-unit.
48 ///
49 /// Note however that since [`PxScale`] values are relative to the text height,
50 /// one further step is needed: multiply by [`Font::height_unscaled`].
51 fn pt_to_px_scale(&self, pt_size: f32) -> Option<PxScale> {
52 let px_per_em = pt_size * (96.0 / 72.0);
53 let units_per_em = self.units_per_em()?;
54 let height = self.height_unscaled();
55 Some(PxScale::from(px_per_em * height / units_per_em))
56 }
57
58 /// Unscaled glyph ascent.
59 ///
60 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
61 fn ascent_unscaled(&self) -> f32;
62
63 /// Unscaled glyph descent.
64 ///
65 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
66 fn descent_unscaled(&self) -> f32;
67
68 /// Unscaled height `ascent - descent`.
69 ///
70 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
71 #[inline]
72 fn height_unscaled(&self) -> f32 {
73 self.ascent_unscaled() - self.descent_unscaled()
74 }
75
76 /// Unscaled line gap.
77 ///
78 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
79 fn line_gap_unscaled(&self) -> f32;
80
81 /// Lookup a `GlyphId` matching a given `char`.
82 ///
83 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
84 fn glyph_id(&self, c: char) -> GlyphId;
85
86 /// Unscaled horizontal advance for a given glyph id.
87 ///
88 /// Returns `0.0` if the font does not define this value.
89 ///
90 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
91 fn h_advance_unscaled(&self, id: GlyphId) -> f32;
92
93 /// Unscaled horizontal side bearing for a given glyph id.
94 ///
95 /// Returns `0.0` if the font does not define this value.
96 ///
97 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
98 fn h_side_bearing_unscaled(&self, id: GlyphId) -> f32;
99
100 /// Unscaled vertical advance for a given glyph id.
101 ///
102 /// Returns `0.0` if the font does not define this value.
103 ///
104 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
105 fn v_advance_unscaled(&self, id: GlyphId) -> f32;
106
107 /// Unscaled vertical side bearing for a given glyph id.
108 ///
109 /// Returns `0.0` if the font does not define this value.
110 ///
111 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
112 fn v_side_bearing_unscaled(&self, id: GlyphId) -> f32;
113
114 /// Returns additional unscaled kerning to apply for a particular pair of glyph ids.
115 ///
116 /// Scaling can be done with [as_scaled](trait.Font.html#method.as_scaled).
117 fn kern_unscaled(&self, first: GlyphId, second: GlyphId) -> f32;
118
119 /// Compute unscaled glyph outline curves & bounding box.
120 fn outline(&self, id: GlyphId) -> Option<Outline>;
121
122 /// The number of glyphs present in this font. Glyph identifiers for this
123 /// font will always be in the range `0..self.glyph_count()`
124 fn glyph_count(&self) -> usize;
125
126 /// Returns an iterator of all distinct `(GlyphId, char)` pairs. Not ordered.
127 ///
128 /// # Example
129 /// ```
130 /// # use ab_glyph::{Font, FontRef, GlyphId};
131 /// # use std::collections::HashMap;
132 /// # fn main() -> Result<(), ab_glyph::InvalidFont> {
133 /// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf"))?;
134 ///
135 /// // Iterate over pairs, each id will appear at most once.
136 /// let mut codepoint_ids = font.codepoint_ids();
137 /// assert_eq!(codepoint_ids.next(), Some((GlyphId(408), '\r')));
138 /// assert_eq!(codepoint_ids.next(), Some((GlyphId(1), ' ')));
139 /// assert_eq!(codepoint_ids.next(), Some((GlyphId(75), '!')));
140 ///
141 /// // Build a lookup map for all ids
142 /// let map: HashMap<_, _> = font.codepoint_ids().collect();
143 /// assert_eq!(map.get(&GlyphId(75)), Some(&'!'));
144 /// # assert_eq!(map.len(), 908);
145 /// # Ok(()) }
146 /// ```
147 fn codepoint_ids(&self) -> crate::CodepointIdIter<'_>;
148
149 /// Returns a pre-rendered image of the glyph.
150 ///
151 /// This is normally only present when an outline is not sufficient to describe the glyph, such
152 /// as emojis (particularly color ones). The `pixel_size` parameter is in pixels per em, and will be
153 /// used to select between multiple possible images (if present); the returned image will
154 /// likely not match this value, requiring you to scale it to match the target resolution.
155 /// To get the largest image use `u16::MAX`.
156 #[allow(deprecated)]
157 #[deprecated(
158 since = "0.2.22",
159 note = "Deprecated in favor of `glyph_raster_image2`"
160 )]
161 fn glyph_raster_image(&self, id: GlyphId, pixel_size: u16) -> Option<crate::GlyphImage> {
162 self.glyph_raster_image2(id, pixel_size)
163 .map(|i| crate::GlyphImage {
164 origin: i.origin,
165 scale: i.pixels_per_em.into(),
166 data: i.data,
167 format: i.format,
168 })
169 }
170
171 /// Returns a pre-rendered image of the glyph.
172 ///
173 /// This is normally only present when an outline is not sufficient to describe the glyph, such
174 /// as emojis (particularly color ones). The `pixel_size` parameter is in pixels per em, and will be
175 /// used to select between multiple possible images (if present); the returned image will
176 /// likely not match this value, requiring you to scale it to match the target resolution.
177 /// To get the largest image use `u16::MAX`.
178 fn glyph_raster_image2(&self, id: GlyphId, pixel_size: u16) -> Option<v2::GlyphImage>;
179
180 /// Returns the layout bounds of this glyph. These are different to the outline `px_bounds()`.
181 ///
182 /// Horizontally: Glyph position +/- h_advance/h_side_bearing.
183 /// Vertically: Glyph position +/- ascent/descent.
184 #[inline]
185 fn glyph_bounds(&self, glyph: &Glyph) -> Rect
186 where
187 Self: Sized,
188 {
189 let sf = self.as_scaled(glyph.scale);
190 let pos = glyph.position;
191 Rect {
192 min: point(pos.x - sf.h_side_bearing(glyph.id), pos.y - sf.ascent()),
193 max: point(pos.x + sf.h_advance(glyph.id), pos.y - sf.descent()),
194 }
195 }
196
197 /// Compute glyph outline ready for drawing.
198 #[inline]
199 fn outline_glyph(&self, glyph: Glyph) -> Option<OutlinedGlyph>
200 where
201 Self: Sized,
202 {
203 let outline = self.outline(glyph.id)?;
204 let scale_factor = self.as_scaled(glyph.scale).scale_factor();
205 Some(OutlinedGlyph::new(glyph, outline, scale_factor))
206 }
207
208 /// Construct a [`PxScaleFontRef`](struct.PxScaleFontRef.html) by associating with the
209 /// given pixel `scale`.
210 ///
211 /// # Example
212 /// ```
213 /// # use ab_glyph::{Font, FontRef, PxScale, ScaleFont};
214 /// # fn main() -> Result<(), ab_glyph::InvalidFont> {
215 /// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf"))?;
216 ///
217 /// assert_eq!(font.descent_unscaled(), -201.0);
218 ///
219 /// assert_eq!(font.as_scaled(24.0).descent(), -4.02);
220 /// assert_eq!(font.as_scaled(50.0).descent(), -8.375);
221 /// # Ok(()) }
222 /// ```
223 #[inline]
224 fn as_scaled<S: Into<PxScale>>(&self, scale: S) -> PxScaleFont<&'_ Self>
225 where
226 Self: Sized,
227 {
228 PxScaleFont {
229 font: self,
230 scale: scale.into(),
231 }
232 }
233
234 /// Move into a [`PxScaleFont`](struct.PxScaleFont.html) associated with the
235 /// given pixel `scale`.
236 #[inline]
237 fn into_scaled<S: Into<PxScale>>(self, scale: S) -> PxScaleFont<Self>
238 where
239 Self: core::marker::Sized,
240 {
241 PxScaleFont {
242 font: self,
243 scale: scale.into(),
244 }
245 }
246}
247
248impl<F: Font> Font for &F {
249 #[inline]
250 fn units_per_em(&self) -> Option<f32> {
251 (*self).units_per_em()
252 }
253
254 #[inline]
255 fn ascent_unscaled(&self) -> f32 {
256 (*self).ascent_unscaled()
257 }
258
259 #[inline]
260 fn descent_unscaled(&self) -> f32 {
261 (*self).descent_unscaled()
262 }
263
264 #[inline]
265 fn line_gap_unscaled(&self) -> f32 {
266 (*self).line_gap_unscaled()
267 }
268
269 #[inline]
270 fn glyph_id(&self, c: char) -> GlyphId {
271 (*self).glyph_id(c)
272 }
273
274 #[inline]
275 fn h_advance_unscaled(&self, id: GlyphId) -> f32 {
276 (*self).h_advance_unscaled(id)
277 }
278
279 #[inline]
280 fn h_side_bearing_unscaled(&self, id: GlyphId) -> f32 {
281 (*self).h_side_bearing_unscaled(id)
282 }
283
284 #[inline]
285 fn v_advance_unscaled(&self, id: GlyphId) -> f32 {
286 (*self).v_advance_unscaled(id)
287 }
288
289 #[inline]
290 fn v_side_bearing_unscaled(&self, id: GlyphId) -> f32 {
291 (*self).v_side_bearing_unscaled(id)
292 }
293
294 #[inline]
295 fn kern_unscaled(&self, first: GlyphId, second: GlyphId) -> f32 {
296 (*self).kern_unscaled(first, second)
297 }
298
299 #[inline]
300 fn outline(&self, glyph: GlyphId) -> Option<Outline> {
301 (*self).outline(glyph)
302 }
303
304 #[inline]
305 fn glyph_count(&self) -> usize {
306 (*self).glyph_count()
307 }
308
309 #[inline]
310 fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
311 (*self).codepoint_ids()
312 }
313
314 #[inline]
315 fn glyph_raster_image2(&self, id: GlyphId, size: u16) -> Option<v2::GlyphImage> {
316 (*self).glyph_raster_image2(id, size)
317 }
318}
319