/src/ttf-parser/src/tables/loca.rs
Line | Count | Source |
1 | | //! An [Index to Location Table](https://docs.microsoft.com/en-us/typography/opentype/spec/loca) |
2 | | //! implementation. |
3 | | |
4 | | use core::convert::TryFrom; |
5 | | use core::num::NonZeroU16; |
6 | | use core::ops::Range; |
7 | | |
8 | | use crate::parser::{LazyArray32, NumFrom, Stream}; |
9 | | use crate::{GlyphId, IndexToLocationFormat}; |
10 | | |
11 | | /// An [Index to Location Table](https://docs.microsoft.com/en-us/typography/opentype/spec/loca). |
12 | | #[derive(Clone, Copy, Debug)] |
13 | | pub enum Table<'a> { |
14 | | /// Short offsets. |
15 | | Short(LazyArray32<'a, u16>), |
16 | | /// Long offsets. |
17 | | Long(LazyArray32<'a, u32>), |
18 | | } |
19 | | |
20 | | impl<'a> Table<'a> { |
21 | | /// Parses a table from raw data. |
22 | | /// |
23 | | /// - `number_of_glyphs` is from the `maxp` table. |
24 | | /// - `format` is from the `head` table. |
25 | 7.53k | pub fn parse( |
26 | 7.53k | number_of_glyphs: NonZeroU16, |
27 | 7.53k | format: IndexToLocationFormat, |
28 | 7.53k | data: &'a [u8], |
29 | 7.53k | ) -> Option<Self> { |
30 | | // The number of ranges is `maxp.numGlyphs + 1`. |
31 | | // |
32 | | // Check for overflow first. |
33 | 7.53k | let mut total: u32 = number_of_glyphs.get() as u32 + 1; |
34 | | |
35 | | // By the spec, the number of `loca` offsets is `maxp.numGlyphs + 1`. |
36 | | // But some malformed fonts can have less glyphs than that. |
37 | | // In which case we try to parse only the available offsets |
38 | | // and do not return an error, since the expected data length |
39 | | // would go beyond table's length. |
40 | | // |
41 | | // In case when `loca` has more data than needed we simply ignore the rest. |
42 | 7.53k | let actual_total = match format { |
43 | 6.32k | IndexToLocationFormat::Short => data.len() / 2, |
44 | 1.21k | IndexToLocationFormat::Long => data.len() / 4, |
45 | | }; |
46 | 7.53k | let actual_total = u32::try_from(actual_total).ok()?; |
47 | 7.53k | total = total.min(actual_total); |
48 | | |
49 | 7.53k | let mut s = Stream::new(data); |
50 | 7.53k | match format { |
51 | 6.32k | IndexToLocationFormat::Short => Some(Table::Short(s.read_array32::<u16>(total)?)), |
52 | 1.21k | IndexToLocationFormat::Long => Some(Table::Long(s.read_array32::<u32>(total)?)), |
53 | | } |
54 | 7.53k | } |
55 | | |
56 | | /// Returns the number of offsets. |
57 | | #[inline] |
58 | 75.3M | pub fn len(&self) -> u32 { |
59 | 75.3M | match self { |
60 | 68.8M | Table::Short(array) => array.len(), |
61 | 6.50M | Table::Long(array) => array.len(), |
62 | | } |
63 | 75.3M | } <ttf_parser::tables::loca::Table>::len Line | Count | Source | 58 | 12.5M | pub fn len(&self) -> u32 { | 59 | 12.5M | match self { | 60 | 10.1M | Table::Short(array) => array.len(), | 61 | 2.42M | Table::Long(array) => array.len(), | 62 | | } | 63 | 12.5M | } |
Unexecuted instantiation: <ttf_parser::tables::loca::Table>::len <ttf_parser::tables::loca::Table>::len Line | Count | Source | 58 | 13.4M | pub fn len(&self) -> u32 { | 59 | 13.4M | match self { | 60 | 10.6M | Table::Short(array) => array.len(), | 61 | 2.86M | Table::Long(array) => array.len(), | 62 | | } | 63 | 13.4M | } |
<ttf_parser::tables::loca::Table>::len Line | Count | Source | 58 | 4 | pub fn len(&self) -> u32 { | 59 | 4 | match self { | 60 | 4 | Table::Short(array) => array.len(), | 61 | 0 | Table::Long(array) => array.len(), | 62 | | } | 63 | 4 | } |
<ttf_parser::tables::loca::Table>::len Line | Count | Source | 58 | 89.2k | pub fn len(&self) -> u32 { | 59 | 89.2k | match self { | 60 | 82.7k | Table::Short(array) => array.len(), | 61 | 6.45k | Table::Long(array) => array.len(), | 62 | | } | 63 | 89.2k | } |
<ttf_parser::tables::loca::Table>::len Line | Count | Source | 58 | 49.2M | pub fn len(&self) -> u32 { | 59 | 49.2M | match self { | 60 | 48.0M | Table::Short(array) => array.len(), | 61 | 1.20M | Table::Long(array) => array.len(), | 62 | | } | 63 | 49.2M | } |
|
64 | | |
65 | | /// Checks if there are any offsets. |
66 | 0 | pub fn is_empty(&self) -> bool { |
67 | 0 | self.len() == 0 |
68 | 0 | } |
69 | | |
70 | | /// Returns glyph's range in the `glyf` table. |
71 | | #[inline] |
72 | 75.3M | pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> { |
73 | 75.3M | let glyph_id = glyph_id.0 as u32; |
74 | | |
75 | | // Glyph ID must be smaller than total number of values in a `loca` array. |
76 | 75.3M | if glyph_id + 1 >= self.len() { |
77 | 36.3M | return None; |
78 | 39.0M | } |
79 | | |
80 | 39.0M | let range = match self { |
81 | 37.9M | Table::Short(array) => { |
82 | | // 'The actual local offset divided by 2 is stored.' |
83 | 37.9M | usize::from(array.get(glyph_id)?) * 2..usize::from(array.get(glyph_id + 1)?) * 2 |
84 | | } |
85 | 1.03M | Table::Long(array) => { |
86 | 1.03M | usize::num_from(array.get(glyph_id)?)..usize::num_from(array.get(glyph_id + 1)?) |
87 | | } |
88 | | }; |
89 | | |
90 | 39.0M | if range.start >= range.end { |
91 | | // 'The offsets must be in ascending order.' |
92 | | // And range cannot be empty. |
93 | 8.10M | None |
94 | | } else { |
95 | 30.9M | Some(range) |
96 | | } |
97 | 75.3M | } <ttf_parser::tables::loca::Table>::glyph_range Line | Count | Source | 72 | 12.5M | pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> { | 73 | 12.5M | let glyph_id = glyph_id.0 as u32; | 74 | | | 75 | | // Glyph ID must be smaller than total number of values in a `loca` array. | 76 | 12.5M | if glyph_id + 1 >= self.len() { | 77 | 10.4M | return None; | 78 | 2.05M | } | 79 | | | 80 | 2.05M | let range = match self { | 81 | 1.63M | Table::Short(array) => { | 82 | | // 'The actual local offset divided by 2 is stored.' | 83 | 1.63M | usize::from(array.get(glyph_id)?) * 2..usize::from(array.get(glyph_id + 1)?) * 2 | 84 | | } | 85 | 423k | Table::Long(array) => { | 86 | 423k | usize::num_from(array.get(glyph_id)?)..usize::num_from(array.get(glyph_id + 1)?) | 87 | | } | 88 | | }; | 89 | | | 90 | 2.05M | if range.start >= range.end { | 91 | | // 'The offsets must be in ascending order.' | 92 | | // And range cannot be empty. | 93 | 1.10M | None | 94 | | } else { | 95 | 953k | Some(range) | 96 | | } | 97 | 12.5M | } |
Unexecuted instantiation: <ttf_parser::tables::loca::Table>::glyph_range <ttf_parser::tables::loca::Table>::glyph_range Line | Count | Source | 72 | 13.4M | pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> { | 73 | 13.4M | let glyph_id = glyph_id.0 as u32; | 74 | | | 75 | | // Glyph ID must be smaller than total number of values in a `loca` array. | 76 | 13.4M | if glyph_id + 1 >= self.len() { | 77 | 12.5M | return None; | 78 | 918k | } | 79 | | | 80 | 918k | let range = match self { | 81 | 584k | Table::Short(array) => { | 82 | | // 'The actual local offset divided by 2 is stored.' | 83 | 584k | usize::from(array.get(glyph_id)?) * 2..usize::from(array.get(glyph_id + 1)?) * 2 | 84 | | } | 85 | 334k | Table::Long(array) => { | 86 | 334k | usize::num_from(array.get(glyph_id)?)..usize::num_from(array.get(glyph_id + 1)?) | 87 | | } | 88 | | }; | 89 | | | 90 | 918k | if range.start >= range.end { | 91 | | // 'The offsets must be in ascending order.' | 92 | | // And range cannot be empty. | 93 | 499k | None | 94 | | } else { | 95 | 419k | Some(range) | 96 | | } | 97 | 13.4M | } |
<ttf_parser::tables::loca::Table>::glyph_range Line | Count | Source | 72 | 4 | pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> { | 73 | 4 | let glyph_id = glyph_id.0 as u32; | 74 | | | 75 | | // Glyph ID must be smaller than total number of values in a `loca` array. | 76 | 4 | if glyph_id + 1 >= self.len() { | 77 | 4 | return None; | 78 | 0 | } | 79 | | | 80 | 0 | let range = match self { | 81 | 0 | Table::Short(array) => { | 82 | | // 'The actual local offset divided by 2 is stored.' | 83 | 0 | usize::from(array.get(glyph_id)?) * 2..usize::from(array.get(glyph_id + 1)?) * 2 | 84 | | } | 85 | 0 | Table::Long(array) => { | 86 | 0 | usize::num_from(array.get(glyph_id)?)..usize::num_from(array.get(glyph_id + 1)?) | 87 | | } | 88 | | }; | 89 | | | 90 | 0 | if range.start >= range.end { | 91 | | // 'The offsets must be in ascending order.' | 92 | | // And range cannot be empty. | 93 | 0 | None | 94 | | } else { | 95 | 0 | Some(range) | 96 | | } | 97 | 4 | } |
<ttf_parser::tables::loca::Table>::glyph_range Line | Count | Source | 72 | 89.2k | pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> { | 73 | 89.2k | let glyph_id = glyph_id.0 as u32; | 74 | | | 75 | | // Glyph ID must be smaller than total number of values in a `loca` array. | 76 | 89.2k | if glyph_id + 1 >= self.len() { | 77 | 53.5k | return None; | 78 | 35.6k | } | 79 | | | 80 | 35.6k | let range = match self { | 81 | 33.5k | Table::Short(array) => { | 82 | | // 'The actual local offset divided by 2 is stored.' | 83 | 33.5k | usize::from(array.get(glyph_id)?) * 2..usize::from(array.get(glyph_id + 1)?) * 2 | 84 | | } | 85 | 2.14k | Table::Long(array) => { | 86 | 2.14k | usize::num_from(array.get(glyph_id)?)..usize::num_from(array.get(glyph_id + 1)?) | 87 | | } | 88 | | }; | 89 | | | 90 | 35.6k | if range.start >= range.end { | 91 | | // 'The offsets must be in ascending order.' | 92 | | // And range cannot be empty. | 93 | 5.06k | None | 94 | | } else { | 95 | 30.5k | Some(range) | 96 | | } | 97 | 89.2k | } |
<ttf_parser::tables::loca::Table>::glyph_range Line | Count | Source | 72 | 49.2M | pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> { | 73 | 49.2M | let glyph_id = glyph_id.0 as u32; | 74 | | | 75 | | // Glyph ID must be smaller than total number of values in a `loca` array. | 76 | 49.2M | if glyph_id + 1 >= self.len() { | 77 | 13.2M | return None; | 78 | 36.0M | } | 79 | | | 80 | 36.0M | let range = match self { | 81 | 35.7M | Table::Short(array) => { | 82 | | // 'The actual local offset divided by 2 is stored.' | 83 | 35.7M | usize::from(array.get(glyph_id)?) * 2..usize::from(array.get(glyph_id + 1)?) * 2 | 84 | | } | 85 | 278k | Table::Long(array) => { | 86 | 278k | usize::num_from(array.get(glyph_id)?)..usize::num_from(array.get(glyph_id + 1)?) | 87 | | } | 88 | | }; | 89 | | | 90 | 36.0M | if range.start >= range.end { | 91 | | // 'The offsets must be in ascending order.' | 92 | | // And range cannot be empty. | 93 | 6.50M | None | 94 | | } else { | 95 | 29.5M | Some(range) | 96 | | } | 97 | 49.2M | } |
|
98 | | } |