1 //! The [loca (Index to Location)][loca] table
2 //!
3 //! [loca]: https://docs.microsoft.com/en-us/typography/opentype/spec/loca
4 
5 use crate::{
6     read::{FontRead, FontReadWithArgs, ReadArgs, ReadError},
7     table_provider::TopLevelTable,
8     FontData,
9 };
10 use types::{BigEndian, GlyphId, Tag};
11 
12 #[cfg(feature = "traversal")]
13 use crate::traversal;
14 
15 /// The [loca] table.
16 ///
17 /// [loca]: https://docs.microsoft.com/en-us/typography/opentype/spec/loca
18 #[derive(Clone)]
19 pub enum Loca<'a> {
20     Short(&'a [BigEndian<u16>]),
21     Long(&'a [BigEndian<u32>]),
22 }
23 
24 impl TopLevelTable for Loca<'_> {
25     const TAG: Tag = Tag::new(b"loca");
26 }
27 
28 impl<'a> Loca<'a> {
read(data: FontData<'a>, is_long: bool) -> Result<Self, crate::ReadError>29     pub fn read(data: FontData<'a>, is_long: bool) -> Result<Self, crate::ReadError> {
30         Self::read_with_args(data, &is_long)
31     }
32 
len(&self) -> usize33     pub fn len(&self) -> usize {
34         match self {
35             Loca::Short(data) => data.len().saturating_sub(1),
36             Loca::Long(data) => data.len().saturating_sub(1),
37         }
38     }
39 
is_empty(&self) -> bool40     pub fn is_empty(&self) -> bool {
41         self.len() == 0
42     }
43 
44     /// Attempt to return the offset for a given glyph id.
get_raw(&self, idx: usize) -> Option<u32>45     pub fn get_raw(&self, idx: usize) -> Option<u32> {
46         match self {
47             Loca::Short(data) => data.get(idx).map(|x| x.get() as u32 * 2),
48             Loca::Long(data) => data.get(idx).map(|x| x.get()),
49         }
50     }
51 
get_glyf( &self, gid: GlyphId, glyf: &super::glyf::Glyf<'a>, ) -> Result<Option<super::glyf::Glyph<'a>>, ReadError>52     pub fn get_glyf(
53         &self,
54         gid: GlyphId,
55         glyf: &super::glyf::Glyf<'a>,
56     ) -> Result<Option<super::glyf::Glyph<'a>>, ReadError> {
57         let idx = gid.to_u16() as usize;
58         let start = self.get_raw(idx).ok_or(ReadError::OutOfBounds)?;
59         let end = self.get_raw(idx + 1).ok_or(ReadError::OutOfBounds)?;
60         if start == end {
61             return Ok(None);
62         }
63         let data = glyf
64             .offset_data()
65             .slice(start as usize..end as usize)
66             .ok_or(ReadError::OutOfBounds)?;
67         match super::glyf::Glyph::read(data) {
68             Ok(glyph) => Ok(Some(glyph)),
69             Err(e) => Err(e),
70         }
71     }
72 }
73 
74 impl ReadArgs for Loca<'_> {
75     type Args = bool;
76 }
77 
78 impl<'a> FontReadWithArgs<'a> for Loca<'a> {
read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, crate::ReadError>79     fn read_with_args(data: FontData<'a>, args: &Self::Args) -> Result<Self, crate::ReadError> {
80         let is_long = *args;
81         if is_long {
82             data.read_array(0..data.len()).map(Loca::Long)
83         } else {
84             data.read_array(0..data.len()).map(Loca::Short)
85         }
86     }
87 }
88 
89 #[cfg(feature = "traversal")]
90 impl<'a> traversal::SomeTable<'a> for Loca<'a> {
type_name(&self) -> &str91     fn type_name(&self) -> &str {
92         "loca"
93     }
94 
get_field(&self, idx: usize) -> Option<traversal::Field<'a>>95     fn get_field(&self, idx: usize) -> Option<traversal::Field<'a>> {
96         match idx {
97             0usize => Some(traversal::Field::new("offsets", self.clone())),
98             _ => None,
99         }
100     }
101 }
102 
103 #[cfg(feature = "traversal")]
104 impl<'a> traversal::SomeArray<'a> for Loca<'a> {
len(&self) -> usize105     fn len(&self) -> usize {
106         self.len()
107     }
108 
get(&self, idx: usize) -> Option<traversal::FieldType<'a>>109     fn get(&self, idx: usize) -> Option<traversal::FieldType<'a>> {
110         self.get_raw(idx).map(|off| off.into())
111     }
112 
type_name(&self) -> &str113     fn type_name(&self) -> &str {
114         "Offset32"
115     }
116 }
117 
118 #[cfg(feature = "traversal")]
119 impl<'a> std::fmt::Debug for Loca<'a> {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result120     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121         (self as &dyn traversal::SomeTable<'a>).fmt(f)
122     }
123 }
124