read_fonts/tables/
hmtx.rs

1//! The [hmtx (Horizontal Metrics)](https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx) table
2
3include!("../../generated/generated_hmtx.rs");
4
5impl Hmtx<'_> {
6    /// Returns the advance width for the given glyph identifier.
7    pub fn advance(&self, glyph_id: GlyphId) -> Option<u16> {
8        advance(self.h_metrics(), glyph_id)
9    }
10
11    /// Returns the left side bearing for the given glyph identifier.
12    pub fn side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
13        side_bearing(self.h_metrics(), self.left_side_bearings(), glyph_id)
14    }
15}
16
17pub(super) fn advance(metrics: &[LongMetric], glyph_id: GlyphId) -> Option<u16> {
18    metrics
19        .get(glyph_id.to_u32() as usize)
20        .or_else(|| metrics.last())
21        .map(|metric| metric.advance())
22}
23
24pub(super) fn side_bearing(
25    metrics: &[LongMetric],
26    side_bearings: &[BigEndian<i16>],
27    glyph_id: GlyphId,
28) -> Option<i16> {
29    let ix = glyph_id.to_u32() as usize;
30    metrics
31        .get(ix)
32        .map(|metric| metric.side_bearing())
33        .or_else(|| {
34            side_bearings
35                .get(ix.saturating_sub(metrics.len()))
36                .map(|sb| sb.get())
37        })
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use crate::{FontRef, TableProvider};
44
45    /// Test case where "long metric" array is short
46    #[test]
47    fn trimmed_advances() {
48        let font = FontRef::new(font_test_data::CBDT).unwrap();
49        let hmtx = font.hmtx().unwrap();
50        assert!(
51            !hmtx.left_side_bearings().is_empty(),
52            "if this fails then the test is no longer accurate"
53        );
54        let expected_lsbs = [100, 0, 100, 0];
55        for (i, lsb) in expected_lsbs.into_iter().enumerate() {
56            let gid = GlyphId::new(i as _);
57            // All glyphs have 800 advance width
58            assert_eq!(hmtx.advance(gid), Some(800));
59            assert_eq!(hmtx.side_bearing(gid), Some(lsb));
60        }
61    }
62
63    #[test]
64    fn metrics() {
65        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
66        let hmtx = font.hmtx().unwrap();
67        let expected = [(908, 100), (1336, 29), (1336, 29), (633, 57)];
68        for (i, (advance, lsb)) in expected.into_iter().enumerate() {
69            let gid = GlyphId::new(i as _);
70            assert_eq!(hmtx.advance(gid), Some(advance));
71            assert_eq!(hmtx.side_bearing(gid), Some(lsb));
72        }
73    }
74}