1 | //! An [Anchor Point Table]( |
2 | //! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html) implementation. |
3 | |
4 | use core::num::NonZeroU16; |
5 | |
6 | use crate::aat; |
7 | use crate::parser::{FromData, LazyArray32, Offset, Offset32, Stream}; |
8 | use crate::GlyphId; |
9 | |
10 | /// An anchor point. |
11 | #[allow (missing_docs)] |
12 | #[derive (Clone, Copy, PartialEq, Eq, Default, Debug)] |
13 | pub struct Point { |
14 | pub x: i16, |
15 | pub y: i16, |
16 | } |
17 | |
18 | impl FromData for Point { |
19 | const SIZE: usize = 4; |
20 | |
21 | #[inline ] |
22 | fn parse(data: &[u8]) -> Option<Self> { |
23 | let mut s: Stream<'_> = Stream::new(data); |
24 | Some(Point { |
25 | x: s.read::<i16>()?, |
26 | y: s.read::<i16>()?, |
27 | }) |
28 | } |
29 | } |
30 | |
31 | /// An [Anchor Point Table]( |
32 | /// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html). |
33 | #[derive (Clone)] |
34 | pub struct Table<'a> { |
35 | lookup: aat::Lookup<'a>, |
36 | // Ideally, Glyphs Data can be represented as an array, |
37 | // but Apple's spec doesn't specify that Glyphs Data members have padding or not. |
38 | // Meaning we cannot simply iterate over them. |
39 | glyphs_data: &'a [u8], |
40 | } |
41 | |
42 | impl core::fmt::Debug for Table<'_> { |
43 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
44 | write!(f, "Table {{ ... }}" ) |
45 | } |
46 | } |
47 | |
48 | impl<'a> Table<'a> { |
49 | /// Parses a table from raw data. |
50 | /// |
51 | /// `number_of_glyphs` is from the `maxp` table. |
52 | pub fn parse(number_of_glyphs: NonZeroU16, data: &'a [u8]) -> Option<Self> { |
53 | let mut s = Stream::new(data); |
54 | |
55 | let version = s.read::<u16>()?; |
56 | if version != 0 { |
57 | return None; |
58 | } |
59 | |
60 | s.skip::<u16>(); // reserved |
61 | |
62 | // TODO: we should probably check that offset is larger than the header size (8) |
63 | let lookup_table = s.read_at_offset32(data)?; |
64 | let glyphs_data = s.read_at_offset32(data)?; |
65 | |
66 | Some(Table { |
67 | lookup: aat::Lookup::parse(number_of_glyphs, lookup_table)?, |
68 | glyphs_data, |
69 | }) |
70 | } |
71 | |
72 | /// Returns a list of anchor points for the specified glyph. |
73 | pub fn points(&self, glyph_id: GlyphId) -> Option<LazyArray32<'a, Point>> { |
74 | let offset = self.lookup.value(glyph_id)?; |
75 | |
76 | let mut s = Stream::new_at(self.glyphs_data, usize::from(offset))?; |
77 | let number_of_points = s.read::<u32>()?; |
78 | s.read_array32::<Point>(number_of_points) |
79 | } |
80 | } |
81 | |
82 | trait StreamExt<'a> { |
83 | fn read_at_offset32(&mut self, data: &'a [u8]) -> Option<&'a [u8]>; |
84 | } |
85 | |
86 | impl<'a> StreamExt<'a> for Stream<'a> { |
87 | fn read_at_offset32(&mut self, data: &'a [u8]) -> Option<&'a [u8]> { |
88 | let offset: usize = self.read::<Offset32>()?.to_usize(); |
89 | data.get(index:offset..) |
90 | } |
91 | } |
92 | |