ttf_parser/tables/
loca.rs

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