1 // https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx
2 
3 use crate::parser::{Stream, LazyArray};
4 use crate::{Font, TableName, GlyphId, HorizontalMetrics, Result, Error};
5 use crate::raw::hmtx as raw;
6 
7 impl<'a> Font<'a> {
8     /// Returns glyph's horizontal metrics.
glyph_hor_metrics(&self, glyph_id: GlyphId) -> Result<HorizontalMetrics>9     pub fn glyph_hor_metrics(&self, glyph_id: GlyphId) -> Result<HorizontalMetrics> {
10         self.check_glyph_id(glyph_id)?;
11         let data = self.hmtx.ok_or_else(|| Error::TableMissing(TableName::HorizontalMetrics))?;
12         let mut s = Stream::new(data);
13 
14         let number_of_hmetrics = self.number_of_hmetrics();
15         if number_of_hmetrics == 0 {
16             return Err(Error::NoHorizontalMetrics);
17         }
18 
19         let glyph_id = glyph_id.0;
20 
21         let array: LazyArray<raw::HorizontalMetrics> = s.read_array(number_of_hmetrics)?;
22 
23         if let Some(metrics) = array.get(glyph_id) {
24             Ok(HorizontalMetrics {
25                 advance: metrics.advance_width(),
26                 left_side_bearing: metrics.lsb(),
27             })
28         } else {
29             // 'If the number_of_hmetrics is less than the total number of glyphs,
30             // then that array is followed by an array for the left side bearing values
31             // of the remaining glyphs.'
32 
33             // Check for overflow.
34             if self.number_of_glyphs() < number_of_hmetrics {
35                 return Err(Error::NoHorizontalMetrics);
36             }
37 
38             let count = self.number_of_glyphs() - number_of_hmetrics;
39             let left_side_bearings: LazyArray<i16> = s.read_array(count)?;
40             let left_side_bearing = left_side_bearings.get(glyph_id)
41                 .ok_or_else(|| Error::NoHorizontalMetrics)?;
42 
43             // 'As an optimization, the number of records can be less than the number of glyphs,
44             // in which case the advance width value of the last record applies
45             // to all remaining glyph IDs.'
46             let last_metric = array.last().ok_or_else(|| Error::NoHorizontalMetrics)?;
47 
48             Ok(HorizontalMetrics {
49                 advance: last_metric.advance_width(),
50                 left_side_bearing,
51             })
52         }
53     }
54 }
55