swash/
metrics.rs

1/*!
2Font and glyph metrics.
3*/
4
5use super::internal::*;
6use super::{FontRef, GlyphId, NormalizedCoord};
7
8/// Proxy for rematerializing metrics.
9#[derive(Copy, Clone, Default)]
10pub struct MetricsProxy {
11    units_per_em: u16,
12    glyph_count: u16,
13    is_monospace: bool,
14    has_vertical_metrics: bool,
15    ascent: i16,
16    descent: i16,
17    leading: i16,
18    vertical_ascent: i16,
19    vertical_descent: i16,
20    vertical_leading: i16,
21    cap_height: i16,
22    x_height: i16,
23    average_width: u16,
24    max_width: u16,
25    underline_offset: i16,
26    strikeout_offset: i16,
27    stroke_size: i16,
28    mvar: u32,
29    hmtx: u32,
30    hvar: u32,
31    hmtx_count: u16,
32    has_vvar: bool,
33    vertical: Vertical,
34}
35
36impl MetricsProxy {
37    /// Creates a metrics proxy for the specified font.
38    pub fn from_font(font: &FontRef) -> Self {
39        let mut metadata = Self {
40            units_per_em: 1,
41            ..Self::default()
42        };
43        metadata.fill(font);
44        metadata
45    }
46
47    /// Materializes font metrics for the specified font and
48    /// normalized variation coordinates. This proxy must have been created
49    /// from the same font.
50    pub fn materialize_metrics(&self, font: &FontRef, coords: &[NormalizedCoord]) -> Metrics {
51        let data = font.data;
52        let mut m = Metrics {
53            units_per_em: self.units_per_em,
54            glyph_count: self.glyph_count,
55            is_monospace: self.is_monospace,
56            has_vertical_metrics: self.has_vertical_metrics,
57            ascent: self.ascent as f32,
58            descent: self.descent as f32,
59            leading: self.leading as f32,
60            vertical_ascent: self.vertical_ascent as f32,
61            vertical_descent: self.vertical_descent as f32,
62            vertical_leading: self.vertical_leading as f32,
63            cap_height: self.cap_height as f32,
64            x_height: self.x_height as f32,
65            average_width: self.average_width as f32,
66            max_width: self.max_width as f32,
67            underline_offset: self.underline_offset as f32,
68            strikeout_offset: self.strikeout_offset as f32,
69            stroke_size: self.stroke_size as f32,
70        };
71        if self.mvar != 0 && !coords.is_empty() {
72            if let Some(v) = var::Mvar::new(data, self.mvar, coords) {
73                use var::mvar_tags::*;
74                m.ascent += v.delta(HASC);
75                m.descent += v.delta(HDSC);
76                m.leading += v.delta(HLGP);
77                if self.has_vertical_metrics {
78                    m.vertical_ascent += v.delta(VASC);
79                    m.vertical_descent += v.delta(VDSC);
80                    m.vertical_leading += v.delta(VLGP);
81                }
82                m.cap_height += v.delta(CPHT);
83                m.x_height += v.delta(XHGT);
84                m.underline_offset += v.delta(UNDO);
85                m.strikeout_offset += v.delta(STRO);
86                m.stroke_size += v.delta(UNDS);
87            }
88        }
89        m
90    }
91
92    /// Materializes glyph metrics for the specified font and
93    /// normalized variation coordinates. This proxy must have been created
94    /// from the same font.
95    pub fn materialize_glyph_metrics<'a>(
96        &self,
97        font: &FontRef<'a>,
98        coords: &'a [NormalizedCoord],
99    ) -> GlyphMetrics<'a> {
100        let data = font.data;
101        let mut vertical = self.vertical;
102        if !coords.is_empty() {
103            if let Vertical::Synthesized {
104                mvar,
105                advance,
106                origin,
107            } = &mut vertical
108            {
109                if *mvar != 0 {
110                    if let Some(v) = var::Mvar::new(data, *mvar, coords) {
111                        use var::mvar_tags::*;
112                        let ascent_delta = v.delta(HASC);
113                        let descent_delta = v.delta(HDSC);
114                        *advance += ascent_delta + descent_delta;
115                        *origin += ascent_delta;
116                    }
117                }
118            }
119        }
120        GlyphMetrics {
121            data,
122            coords,
123            units_per_em: self.units_per_em,
124            glyph_count: self.glyph_count,
125            hmtx: self.hmtx,
126            hvar: self.hvar,
127            hmtx_count: self.hmtx_count,
128            has_vvar: self.has_vvar,
129            vertical,
130            scale: 1.,
131        }
132    }
133
134    /// Returns the number of font design units per em unit.
135    pub fn units_per_em(&self) -> u16 {
136        self.units_per_em
137    }
138
139    /// Returns the number of glyphs in the font.
140    pub fn glyph_count(&self) -> u16 {
141        self.glyph_count
142    }
143
144    fn fill(&mut self, font: &FontRef) -> Option<()> {
145        let head = font.head()?;
146        self.units_per_em = head.units_per_em();
147        self.glyph_count = font.maxp()?.glyph_count();
148        let mut have_line_metrics = false;
149        let os2 = font.os2();
150        if let Some(os2) = os2 {
151            let flags = os2.selection_flags();
152            self.average_width = os2.average_char_width() as u16;
153            self.strikeout_offset = os2.strikeout_position();
154            self.stroke_size = os2.strikeout_size();
155            self.x_height = os2.x_height();
156            self.cap_height = os2.cap_height();
157            if flags.use_typographic_metrics() {
158                self.ascent = os2.typographic_ascender();
159                self.descent = -os2.typographic_descender();
160                self.leading = os2.typographic_line_gap();
161                have_line_metrics = self.ascent != 0;
162            }
163        }
164        let hhea = font.hhea();
165        if let Some(hhea) = hhea {
166            self.max_width = hhea.max_advance();
167            if !have_line_metrics {
168                self.ascent = hhea.ascender();
169                self.descent = -hhea.descender();
170                self.leading = hhea.line_gap();
171            }
172        }
173        let vhea = font.vhea();
174        if let Some(vhea) = vhea {
175            self.has_vertical_metrics = true;
176            self.vertical_ascent = vhea.ascender();
177            self.vertical_descent = -vhea.descender();
178            self.vertical_leading = vhea.line_gap();
179        } else {
180            self.vertical_ascent = (self.units_per_em / 2) as i16;
181            self.vertical_descent = self.vertical_ascent;
182        }
183        if let Some(post) = font.post() {
184            self.underline_offset = post.underline_position();
185            self.stroke_size = post.underline_size();
186            self.is_monospace = post.is_fixed_pitch();
187        }
188        self.mvar = font.table_offset(var::MVAR);
189        self.hmtx_count = hhea.map(|t| t.num_long_metrics()).unwrap_or(1);
190        self.hmtx = font.table_offset(xmtx::HMTX);
191        self.hvar = font.table_offset(var::HVAR);
192        let mut vmtx = 0;
193        if vhea.is_some() {
194            vmtx = font.table_offset(xmtx::VMTX);
195        }
196        if vmtx != 0 {
197            let long_count = vhea.unwrap().num_long_metrics();
198            let vvar = font.table_offset(var::VVAR);
199            self.has_vvar = vvar != 0;
200            let vorg = font.table_offset(vorg::VORG);
201            if vorg != 0 {
202                self.vertical = Vertical::VmtxVorg {
203                    long_count,
204                    vmtx,
205                    vvar,
206                    vorg,
207                };
208            } else {
209                let glyf = font.table_offset(glyf::GLYF);
210                let loca = font.table_offset(glyf::LOCA);
211                let loca_fmt = font
212                    .head()
213                    .map(|t| t.index_to_location_format() as u8)
214                    .unwrap_or(0xFF);
215                if glyf != 0 && loca != 0 && loca_fmt != 0xFF {
216                    self.vertical = Vertical::VmtxGlyf {
217                        loca_fmt,
218                        long_count,
219                        vmtx,
220                        vvar,
221                        glyf,
222                        loca,
223                    }
224                }
225            }
226        } else {
227            self.vertical = Vertical::Synthesized {
228                mvar: self.mvar,
229                advance: self.ascent as f32 + self.descent as f32,
230                origin: self.ascent as f32,
231            };
232        }
233        Some(())
234    }
235}
236
237/// Global font metrics.
238#[derive(Copy, Clone, Default, Debug)]
239pub struct Metrics {
240    /// Number of font design units per em unit.
241    pub units_per_em: u16,
242    /// Number of glyphs in the font.
243    pub glyph_count: u16,
244    /// True if the font is monospace.
245    pub is_monospace: bool,
246    /// True if the font provides canonical vertical metrics.
247    pub has_vertical_metrics: bool,
248    /// Distance from the baseline to the top of the alignment box.
249    pub ascent: f32,
250    /// Distance from the baseline to the bottom of the alignment box.
251    pub descent: f32,
252    /// Recommended additional spacing between lines.
253    pub leading: f32,
254    /// Distance from the vertical center baseline to the right edge of
255    /// the design space.
256    pub vertical_ascent: f32,
257    /// Distance from the vertical center baseline to the left edge of
258    /// the design space.
259    pub vertical_descent: f32,
260    /// Recommended additional spacing between columns.
261    pub vertical_leading: f32,
262    /// Distance from the baseline to the top of a typical English capital.
263    pub cap_height: f32,
264    /// Distance from the baseline to the top of the lowercase "x" or
265    /// similar character.
266    pub x_height: f32,
267    /// Average width of all non-zero characters in the font.
268    pub average_width: f32,
269    /// Maximum advance width of all characters in the font.
270    pub max_width: f32,
271    /// Recommended distance from the baseline to the top of an underline
272    /// stroke.
273    pub underline_offset: f32,
274    /// Recommended distance from the baseline to the top of a strikeout
275    /// stroke.
276    pub strikeout_offset: f32,
277    /// Recommended thickness of an underline or strikeout stroke.
278    pub stroke_size: f32,
279}
280
281impl Metrics {
282    /// Creates a new set of metrics from the specified font and
283    /// normalized variation coordinates.
284    pub(crate) fn from_font(font: &FontRef, coords: &[i16]) -> Self {
285        let meta = MetricsProxy::from_font(font);
286        meta.materialize_metrics(font, coords)
287    }
288
289    /// Creates a new set of metrics scaled for the specified pixels
290    /// per em unit.
291    pub fn scale(&self, ppem: f32) -> Self {
292        self.linear_scale(if self.units_per_em != 0 {
293            ppem / self.units_per_em as f32
294        } else {
295            1.
296        })
297    }
298
299    /// Creates a new set of metrics scaled by the specified factor.
300    pub fn linear_scale(&self, s: f32) -> Self {
301        let mut m = *self;
302        m.ascent *= s;
303        m.descent *= s;
304        m.leading *= s;
305        m.vertical_ascent *= s;
306        m.vertical_descent *= s;
307        m.vertical_leading *= s;
308        m.cap_height *= s;
309        m.x_height *= s;
310        m.average_width *= s;
311        m.max_width *= s;
312        m.underline_offset *= s;
313        m.strikeout_offset *= s;
314        m.stroke_size *= s;
315        m
316    }
317}
318
319/// Glyph advances, side bearings and vertical origins.
320#[derive(Copy, Clone)]
321pub struct GlyphMetrics<'a> {
322    data: &'a [u8],
323    coords: &'a [i16],
324    units_per_em: u16,
325    glyph_count: u16,
326    hmtx: u32,
327    hvar: u32,
328    hmtx_count: u16,
329    has_vvar: bool,
330    vertical: Vertical,
331    scale: f32,
332}
333
334impl<'a> GlyphMetrics<'a> {
335    /// Creates a new set of glyph metrics from the specified font and
336    /// normalized variation coordinates.
337    pub(crate) fn from_font(font: &FontRef<'a>, coords: &'a [NormalizedCoord]) -> Self {
338        let proxy = MetricsProxy::from_font(font);
339        proxy.materialize_glyph_metrics(font, coords)
340    }
341
342    /// Returns the number of font design units per em unit.
343    pub fn units_per_em(&self) -> u16 {
344        self.units_per_em
345    }
346
347    /// Returns the number of glyphs in the font.
348    pub fn glyph_count(&self) -> u16 {
349        self.glyph_count
350    }
351
352    /// Returns true if the font provides canonical vertical glyph metrics.
353    pub fn has_vertical_metrics(&self) -> bool {
354        !matches!(self.vertical, Vertical::Synthesized { .. })
355    }
356
357    /// Returns true if variations are supported.
358    pub fn has_variations(&self) -> bool {
359        self.hvar != 0 || self.has_vvar
360    }
361
362    /// Creates a new set of metrics scaled for the specified pixels
363    /// per em unit.
364    pub fn scale(&self, ppem: f32) -> Self {
365        self.linear_scale(if self.units_per_em() != 0 {
366            ppem / self.units_per_em() as f32
367        } else {
368            1.
369        })
370    }
371
372    /// Creates a new set of metrics scaled by the specified factor.
373    pub fn linear_scale(&self, scale: f32) -> Self {
374        let mut copy = *self;
375        copy.scale = scale;
376        copy
377    }
378
379    /// Returns the horizontal advance for the specified glyph.
380    pub fn advance_width(&self, glyph_id: GlyphId) -> f32 {
381        let mut v = xmtx::advance(self.data, self.hmtx, self.hmtx_count, glyph_id) as f32;
382        if self.hvar != 0 {
383            v += var::advance_delta(self.data, self.hvar, glyph_id, self.coords);
384        }
385        v * self.scale
386    }
387
388    /// Returns the left side bearing for the specified glyph.
389    pub fn lsb(&self, glyph_id: GlyphId) -> f32 {
390        let mut v = xmtx::sb(self.data, self.hmtx, self.hmtx_count, glyph_id) as f32;
391        if self.hvar != 0 {
392            v += var::sb_delta(self.data, self.hvar, glyph_id, self.coords);
393        }
394        v * self.scale
395    }
396
397    /// Returns the vertical advance for the specified glyph.
398    pub fn advance_height(&self, glyph_id: GlyphId) -> f32 {
399        self.scale
400            * match self.vertical {
401                Vertical::VmtxGlyf {
402                    vmtx,
403                    vvar,
404                    long_count,
405                    ..
406                }
407                | Vertical::VmtxVorg {
408                    vmtx,
409                    vvar,
410                    long_count,
411                    ..
412                } => {
413                    let mut v = xmtx::advance(self.data, vmtx, long_count, glyph_id) as f32;
414                    if vvar != 0 {
415                        v += var::advance_delta(self.data, vvar, glyph_id, self.coords);
416                    }
417                    v
418                }
419                Vertical::Synthesized { advance, .. } => advance,
420            }
421    }
422
423    /// Returns the top side bearing for the specified glyph.
424    pub fn tsb(&self, glyph_id: GlyphId) -> f32 {
425        self.scale
426            * match self.vertical {
427                Vertical::VmtxGlyf {
428                    vmtx,
429                    vvar,
430                    long_count,
431                    ..
432                }
433                | Vertical::VmtxVorg {
434                    vmtx,
435                    vvar,
436                    long_count,
437                    ..
438                } => {
439                    let mut v = xmtx::sb(self.data, vmtx, long_count, glyph_id) as f32;
440                    if vvar != 0 {
441                        v += var::sb_delta(self.data, vvar, glyph_id, self.coords);
442                    }
443                    v
444                }
445                Vertical::Synthesized { .. } => 0.,
446            }
447    }
448
449    /// Returns the vertical origin for the specified glyph id.
450    pub fn vertical_origin(&self, glyph_id: GlyphId) -> f32 {
451        self.scale
452            * match self.vertical {
453                Vertical::VmtxGlyf {
454                    loca_fmt,
455                    loca,
456                    glyf,
457                    ..
458                } => {
459                    if let Some(max_y) = glyf::ymax(self.data, loca_fmt, loca, glyf, glyph_id) {
460                        max_y as f32 + self.tsb(glyph_id)
461                    } else {
462                        self.units_per_em as f32
463                    }
464                }
465                Vertical::VmtxVorg { vorg, .. } => vorg::origin(self.data, vorg, glyph_id)
466                    .unwrap_or(self.units_per_em as i16)
467                    as f32,
468                Vertical::Synthesized { origin, .. } => origin,
469            }
470    }
471}
472
473#[derive(Copy, Clone)]
474#[repr(u8)]
475enum Vertical {
476    VmtxGlyf {
477        loca_fmt: u8,
478        long_count: u16,
479        vmtx: u32,
480        vvar: u32,
481        glyf: u32,
482        loca: u32,
483    },
484    VmtxVorg {
485        long_count: u16,
486        vmtx: u32,
487        vvar: u32,
488        vorg: u32,
489    },
490    Synthesized {
491        mvar: u32,
492        advance: f32,
493        origin: f32,
494    },
495}
496
497impl Default for Vertical {
498    fn default() -> Self {
499        Self::Synthesized {
500            mvar: 0,
501            advance: 0.,
502            origin: 0.,
503        }
504    }
505}