read_fonts/tables/
hmtx.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! The [hmtx (Horizontal Metrics)](https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx) table

include!("../../generated/generated_hmtx.rs");

impl<'a> Hmtx<'a> {
    /// Returns the advance width for the given glyph identifier.
    pub fn advance(&self, glyph_id: GlyphId) -> Option<u16> {
        advance(self.h_metrics(), glyph_id)
    }

    /// Returns the left side bearing for the given glyph identifier.
    pub fn side_bearing(&self, glyph_id: GlyphId) -> Option<i16> {
        side_bearing(self.h_metrics(), self.left_side_bearings(), glyph_id)
    }
}

pub(super) fn advance(metrics: &[LongMetric], glyph_id: GlyphId) -> Option<u16> {
    metrics
        .get(glyph_id.to_u32() as usize)
        .or_else(|| metrics.last())
        .map(|metric| metric.advance())
}

pub(super) fn side_bearing(
    metrics: &[LongMetric],
    side_bearings: &[BigEndian<i16>],
    glyph_id: GlyphId,
) -> Option<i16> {
    let ix = glyph_id.to_u32() as usize;
    metrics
        .get(ix)
        .map(|metric| metric.side_bearing())
        .or_else(|| {
            side_bearings
                .get(ix.saturating_sub(metrics.len()))
                .map(|sb| sb.get())
        })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{FontRef, TableProvider};

    /// Test case where "long metric" array is short
    #[test]
    fn trimmed_advances() {
        let font = FontRef::new(font_test_data::CBDT).unwrap();
        let hmtx = font.hmtx().unwrap();
        assert!(
            !hmtx.left_side_bearings().is_empty(),
            "if this fails then the test is no longer accurate"
        );
        let expected_lsbs = [100, 0, 100, 0];
        for (i, lsb) in expected_lsbs.into_iter().enumerate() {
            let gid = GlyphId::new(i as _);
            // All glyphs have 800 advance width
            assert_eq!(hmtx.advance(gid), Some(800));
            assert_eq!(hmtx.side_bearing(gid), Some(lsb));
        }
    }

    #[test]
    fn metrics() {
        let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
        let hmtx = font.hmtx().unwrap();
        let expected = [(908, 100), (1336, 29), (1336, 29), (633, 57)];
        for (i, (advance, lsb)) in expected.into_iter().enumerate() {
            let gid = GlyphId::new(i as _);
            assert_eq!(hmtx.advance(gid), Some(advance));
            assert_eq!(hmtx.side_bearing(gid), Some(lsb));
        }
    }
}