ttf_parser/tables/
loca.rs

1// https://docs.microsoft.com/en-us/typography/opentype/spec/loca
2
3use core::num::NonZeroU16;
4use core::ops::Range;
5
6use crate::{GlyphId, IndexToLocationFormat};
7use crate::parser::{Stream, LazyArray16, NumFrom};
8
9#[derive(Clone, Copy)]
10pub(crate) enum Table<'a> {
11    Short(LazyArray16<'a, u16>),
12    Long(LazyArray16<'a, u32>),
13}
14
15impl<'a> Table<'a> {
16    pub fn parse(
17        data: &'a [u8],
18        number_of_glyphs: NonZeroU16,
19        format: IndexToLocationFormat,
20    ) -> Option<Self> {
21        // The number of ranges is `maxp.numGlyphs + 1`.
22        //
23        // Check for overflow first.
24        let total = if number_of_glyphs.get() == core::u16::MAX {
25            number_of_glyphs.get()
26        } else {
27            number_of_glyphs.get() + 1
28        };
29
30        let mut s = Stream::new(data);
31        match format {
32            IndexToLocationFormat::Short => {
33                Some(Table::Short(s.read_array16::<u16>(total)?))
34            }
35            IndexToLocationFormat::Long => {
36                Some(Table::Long(s.read_array16::<u32>(total)?))
37            }
38        }
39    }
40
41    #[inline]
42    fn len(&self) -> u16 {
43        match self {
44            Table::Short(ref array) => array.len(),
45            Table::Long(ref array) => array.len(),
46        }
47    }
48
49    #[inline]
50    pub fn glyph_range(&self, glyph_id: GlyphId) -> Option<Range<usize>> {
51        let glyph_id = glyph_id.0;
52        if glyph_id == core::u16::MAX {
53            return None;
54        }
55
56        // Glyph ID must be smaller than total number of values in a `loca` array.
57        if glyph_id + 1 >= self.len() {
58            return None;
59        }
60
61        let range = match self {
62            Table::Short(ref array) => {
63                // 'The actual local offset divided by 2 is stored.'
64                usize::from(array.get(glyph_id)?) * 2 .. usize::from(array.get(glyph_id + 1)?) * 2
65            }
66            Table::Long(ref array) => {
67                usize::num_from(array.get(glyph_id)?) .. usize::num_from(array.get(glyph_id + 1)?)
68            }
69        };
70
71        if range.start >= range.end {
72            // 'The offsets must be in ascending order.'
73            // And range cannot be empty.
74            None
75        } else {
76            Some(range)
77        }
78    }
79}