1 | use 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. |
17 | pub 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 | |
248 | impl<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 | |