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