1 | //! An [SVG Table](https://docs.microsoft.com/en-us/typography/opentype/spec/svg) implementation. |
2 | |
3 | use crate::parser::{FromData, LazyArray16, NumFrom, Offset, Offset32, Stream}; |
4 | use crate::GlyphId; |
5 | |
6 | #[derive (Clone, Copy)] |
7 | struct SvgDocumentRecord { |
8 | start_glyph_id: GlyphId, |
9 | end_glyph_id: GlyphId, |
10 | svg_doc_offset: Option<Offset32>, |
11 | svg_doc_length: u32, |
12 | } |
13 | |
14 | impl FromData for SvgDocumentRecord { |
15 | const SIZE: usize = 12; |
16 | |
17 | #[inline ] |
18 | fn parse(data: &[u8]) -> Option<Self> { |
19 | let mut s: Stream<'_> = Stream::new(data); |
20 | Some(SvgDocumentRecord { |
21 | start_glyph_id: s.read::<GlyphId>()?, |
22 | end_glyph_id: s.read::<GlyphId>()?, |
23 | svg_doc_offset: s.read::<Option<Offset32>>()?, |
24 | svg_doc_length: s.read::<u32>()?, |
25 | }) |
26 | } |
27 | } |
28 | |
29 | /// A list of [SVG documents]( |
30 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/svg#svg-document-list). |
31 | #[derive (Clone, Copy)] |
32 | pub struct SvgDocumentsList<'a> { |
33 | data: &'a [u8], |
34 | records: LazyArray16<'a, SvgDocumentRecord>, |
35 | } |
36 | |
37 | impl<'a> SvgDocumentsList<'a> { |
38 | /// Returns SVG document data at index. |
39 | /// |
40 | /// `index` is not a GlyphId. You should use [`find()`](SvgDocumentsList::find) instead. |
41 | #[inline ] |
42 | pub fn get(&self, index: u16) -> Option<&'a [u8]> { |
43 | let record = self.records.get(index)?; |
44 | let offset = record.svg_doc_offset?.to_usize(); |
45 | self.data |
46 | .get(offset..offset + usize::num_from(record.svg_doc_length)) |
47 | } |
48 | |
49 | /// Returns a SVG document data by glyph ID. |
50 | #[inline ] |
51 | pub fn find(&self, glyph_id: GlyphId) -> Option<&'a [u8]> { |
52 | let index = self |
53 | .records |
54 | .into_iter() |
55 | .position(|v| (v.start_glyph_id..=v.end_glyph_id).contains(&glyph_id))?; |
56 | self.get(index as u16) |
57 | } |
58 | |
59 | /// Returns the number of SVG documents in the list. |
60 | pub fn len(&self) -> u16 { |
61 | self.records.len() |
62 | } |
63 | |
64 | /// Checks if the list is empty. |
65 | pub fn is_empty(&self) -> bool { |
66 | self.records.is_empty() |
67 | } |
68 | } |
69 | |
70 | impl core::fmt::Debug for SvgDocumentsList<'_> { |
71 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
72 | write!(f, "SvgDocumentsList {{ ... }}" ) |
73 | } |
74 | } |
75 | |
76 | impl<'a> IntoIterator for SvgDocumentsList<'a> { |
77 | type Item = &'a [u8]; |
78 | type IntoIter = SvgDocumentsListIter<'a>; |
79 | |
80 | #[inline ] |
81 | fn into_iter(self) -> Self::IntoIter { |
82 | SvgDocumentsListIter { |
83 | list: self, |
84 | index: 0, |
85 | } |
86 | } |
87 | } |
88 | |
89 | /// An iterator over [`SvgDocumentsList`] values. |
90 | #[derive (Clone, Copy)] |
91 | #[allow (missing_debug_implementations)] |
92 | pub struct SvgDocumentsListIter<'a> { |
93 | list: SvgDocumentsList<'a>, |
94 | index: u16, |
95 | } |
96 | |
97 | impl<'a> Iterator for SvgDocumentsListIter<'a> { |
98 | type Item = &'a [u8]; |
99 | |
100 | #[inline ] |
101 | fn next(&mut self) -> Option<Self::Item> { |
102 | if self.index < self.list.len() { |
103 | self.index += 1; |
104 | self.list.get(self.index - 1) |
105 | } else { |
106 | None |
107 | } |
108 | } |
109 | |
110 | #[inline ] |
111 | fn count(self) -> usize { |
112 | usize::from(self.list.len().saturating_sub(self.index)) |
113 | } |
114 | } |
115 | |
116 | /// An [SVG Table](https://docs.microsoft.com/en-us/typography/opentype/spec/svg). |
117 | #[derive (Clone, Copy, Debug)] |
118 | pub struct Table<'a> { |
119 | /// A list of SVG documents. |
120 | pub documents: SvgDocumentsList<'a>, |
121 | } |
122 | |
123 | impl<'a> Table<'a> { |
124 | /// Parses a table from raw data. |
125 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
126 | let mut s: Stream<'_> = Stream::new(data); |
127 | s.skip::<u16>(); // version |
128 | let doc_list_offset: Offset32 = s.read::<Option<Offset32>>()??; |
129 | |
130 | let mut s: Stream<'_> = Stream::new_at(data, offset:doc_list_offset.to_usize())?; |
131 | let count: u16 = s.read::<u16>()?; |
132 | let records: LazyArray16<'_, SvgDocumentRecord> = s.read_array16::<SvgDocumentRecord>(count)?; |
133 | |
134 | Some(Table { |
135 | documents: SvgDocumentsList { |
136 | data: &data[doc_list_offset.0 as usize..], |
137 | records, |
138 | }, |
139 | }) |
140 | } |
141 | } |
142 | |