/src/ttf-parser/src/tables/svg.rs
Line | Count | Source |
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 | | /// An [SVG documents]( |
7 | | /// https://docs.microsoft.com/en-us/typography/opentype/spec/svg#svg-document-list). |
8 | | #[derive(Clone, Copy, Debug)] |
9 | | pub struct SvgDocument<'a> { |
10 | | /// The SVG document data. |
11 | | /// |
12 | | /// Can be stored as a string or as a gzip compressed data, aka SVGZ. |
13 | | pub data: &'a [u8], |
14 | | /// The first glyph ID for the range covered by this record. |
15 | | pub start_glyph_id: GlyphId, |
16 | | /// The last glyph ID, *inclusive*, for the range covered by this record. |
17 | | pub end_glyph_id: GlyphId, |
18 | | } |
19 | | |
20 | | impl SvgDocument<'_> { |
21 | | /// Returns the glyphs range. |
22 | 392 | pub fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> { |
23 | 392 | self.start_glyph_id..=self.end_glyph_id |
24 | 392 | } |
25 | | } |
26 | | |
27 | | #[derive(Clone, Copy)] |
28 | | struct SvgDocumentRecord { |
29 | | start_glyph_id: GlyphId, |
30 | | end_glyph_id: GlyphId, |
31 | | svg_doc_offset: Option<Offset32>, |
32 | | svg_doc_length: u32, |
33 | | } |
34 | | |
35 | | impl SvgDocumentRecord { |
36 | 813 | fn glyphs_range(&self) -> core::ops::RangeInclusive<GlyphId> { |
37 | 813 | self.start_glyph_id..=self.end_glyph_id |
38 | 813 | } |
39 | | } |
40 | | |
41 | | impl FromData for SvgDocumentRecord { |
42 | | const SIZE: usize = 12; |
43 | | |
44 | | #[inline] |
45 | 145k | fn parse(data: &[u8]) -> Option<Self> { |
46 | 145k | let mut s = Stream::new(data); |
47 | | Some(SvgDocumentRecord { |
48 | 145k | start_glyph_id: s.read::<GlyphId>()?, |
49 | 145k | end_glyph_id: s.read::<GlyphId>()?, |
50 | 145k | svg_doc_offset: s.read::<Option<Offset32>>()?, |
51 | 145k | svg_doc_length: s.read::<u32>()?, |
52 | | }) |
53 | 145k | } <ttf_parser::tables::svg::SvgDocumentRecord as ttf_parser::parser::FromData>::parse Line | Count | Source | 45 | 144k | fn parse(data: &[u8]) -> Option<Self> { | 46 | 144k | let mut s = Stream::new(data); | 47 | | Some(SvgDocumentRecord { | 48 | 144k | start_glyph_id: s.read::<GlyphId>()?, | 49 | 144k | end_glyph_id: s.read::<GlyphId>()?, | 50 | 144k | svg_doc_offset: s.read::<Option<Offset32>>()?, | 51 | 144k | svg_doc_length: s.read::<u32>()?, | 52 | | }) | 53 | 144k | } |
<ttf_parser::tables::svg::SvgDocumentRecord as ttf_parser::parser::FromData>::parse Line | Count | Source | 45 | 902 | fn parse(data: &[u8]) -> Option<Self> { | 46 | 902 | let mut s = Stream::new(data); | 47 | | Some(SvgDocumentRecord { | 48 | 902 | start_glyph_id: s.read::<GlyphId>()?, | 49 | 902 | end_glyph_id: s.read::<GlyphId>()?, | 50 | 902 | svg_doc_offset: s.read::<Option<Offset32>>()?, | 51 | 902 | svg_doc_length: s.read::<u32>()?, | 52 | | }) | 53 | 902 | } |
|
54 | | } |
55 | | |
56 | | /// A list of [SVG documents]( |
57 | | /// https://docs.microsoft.com/en-us/typography/opentype/spec/svg#svg-document-list). |
58 | | #[derive(Clone, Copy)] |
59 | | pub struct SvgDocumentsList<'a> { |
60 | | data: &'a [u8], |
61 | | records: LazyArray16<'a, SvgDocumentRecord>, |
62 | | } |
63 | | |
64 | | impl<'a> SvgDocumentsList<'a> { |
65 | | /// Returns SVG document data at index. |
66 | | /// |
67 | | /// `index` is not a GlyphId. You should use [`find()`](SvgDocumentsList::find) instead. |
68 | | #[inline] |
69 | 144k | pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> { |
70 | 144k | let record = self.records.get(index)?; |
71 | 144k | let offset = record.svg_doc_offset?.to_usize(); |
72 | 124k | self.data |
73 | 124k | .get(offset..offset + usize::num_from(record.svg_doc_length)) |
74 | 124k | .map(|data| SvgDocument { |
75 | 393 | data, |
76 | 393 | start_glyph_id: record.start_glyph_id, |
77 | 393 | end_glyph_id: record.end_glyph_id, |
78 | 393 | }) <ttf_parser::tables::svg::SvgDocumentsList>::get::{closure#0}Line | Count | Source | 75 | 392 | data, | 76 | 392 | start_glyph_id: record.start_glyph_id, | 77 | 392 | end_glyph_id: record.end_glyph_id, | 78 | 392 | }) |
<ttf_parser::tables::svg::SvgDocumentsList>::get::{closure#0}Line | Count | Source | 75 | 1 | data, | 76 | 1 | start_glyph_id: record.start_glyph_id, | 77 | 1 | end_glyph_id: record.end_glyph_id, | 78 | 1 | }) |
|
79 | 144k | } <ttf_parser::tables::svg::SvgDocumentsList>::get Line | Count | Source | 69 | 144k | pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> { | 70 | 144k | let record = self.records.get(index)?; | 71 | 144k | let offset = record.svg_doc_offset?.to_usize(); | 72 | 124k | self.data | 73 | 124k | .get(offset..offset + usize::num_from(record.svg_doc_length)) | 74 | 124k | .map(|data| SvgDocument { | 75 | | data, | 76 | | start_glyph_id: record.start_glyph_id, | 77 | | end_glyph_id: record.end_glyph_id, | 78 | | }) | 79 | 144k | } |
<ttf_parser::tables::svg::SvgDocumentsList>::get Line | Count | Source | 69 | 89 | pub fn get(&self, index: u16) -> Option<SvgDocument<'a>> { | 70 | 89 | let record = self.records.get(index)?; | 71 | 89 | let offset = record.svg_doc_offset?.to_usize(); | 72 | 82 | self.data | 73 | 82 | .get(offset..offset + usize::num_from(record.svg_doc_length)) | 74 | 82 | .map(|data| SvgDocument { | 75 | | data, | 76 | | start_glyph_id: record.start_glyph_id, | 77 | | end_glyph_id: record.end_glyph_id, | 78 | | }) | 79 | 89 | } |
|
80 | | |
81 | | /// Returns a SVG document data by glyph ID. |
82 | | #[inline] |
83 | 98 | pub fn find(&self, glyph_id: GlyphId) -> Option<SvgDocument<'a>> { |
84 | 98 | let index = self |
85 | 98 | .records |
86 | 98 | .into_iter() |
87 | 813 | .position(|v| v.glyphs_range().contains(&glyph_id))?; |
88 | 89 | self.get(index as u16) |
89 | 98 | } |
90 | | |
91 | | /// Returns the number of SVG documents in the list. |
92 | 4 | pub fn len(&self) -> u16 { |
93 | 4 | self.records.len() |
94 | 4 | } |
95 | | |
96 | | /// Checks if the list is empty. |
97 | 0 | pub fn is_empty(&self) -> bool { |
98 | 0 | self.records.is_empty() |
99 | 0 | } |
100 | | } |
101 | | |
102 | | impl core::fmt::Debug for SvgDocumentsList<'_> { |
103 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
104 | 0 | write!(f, "SvgDocumentsList {{ ... }}") |
105 | 0 | } |
106 | | } |
107 | | |
108 | | impl<'a> IntoIterator for SvgDocumentsList<'a> { |
109 | | type Item = SvgDocument<'a>; |
110 | | type IntoIter = SvgDocumentsListIter<'a>; |
111 | | |
112 | | #[inline] |
113 | | fn into_iter(self) -> Self::IntoIter { |
114 | | SvgDocumentsListIter { |
115 | | list: self, |
116 | | index: 0, |
117 | | } |
118 | | } |
119 | | } |
120 | | |
121 | | /// An iterator over [`SvgDocumentsList`] values. |
122 | | #[derive(Clone, Copy)] |
123 | | #[allow(missing_debug_implementations)] |
124 | | pub struct SvgDocumentsListIter<'a> { |
125 | | list: SvgDocumentsList<'a>, |
126 | | index: u16, |
127 | | } |
128 | | |
129 | | impl<'a> Iterator for SvgDocumentsListIter<'a> { |
130 | | type Item = SvgDocument<'a>; |
131 | | |
132 | | #[inline] |
133 | | fn next(&mut self) -> Option<Self::Item> { |
134 | | if self.index < self.list.len() { |
135 | | self.index += 1; |
136 | | self.list.get(self.index - 1) |
137 | | } else { |
138 | | None |
139 | | } |
140 | | } |
141 | | |
142 | | #[inline] |
143 | | fn count(self) -> usize { |
144 | | usize::from(self.list.len().saturating_sub(self.index)) |
145 | | } |
146 | | } |
147 | | |
148 | | /// An [SVG Table](https://docs.microsoft.com/en-us/typography/opentype/spec/svg). |
149 | | #[derive(Clone, Copy, Debug)] |
150 | | pub struct Table<'a> { |
151 | | /// A list of SVG documents. |
152 | | pub documents: SvgDocumentsList<'a>, |
153 | | } |
154 | | |
155 | | impl<'a> Table<'a> { |
156 | | /// Parses a table from raw data. |
157 | 363 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
158 | 363 | let mut s = Stream::new(data); |
159 | 363 | s.skip::<u16>(); // version |
160 | 363 | let doc_list_offset = s.read::<Option<Offset32>>()??; |
161 | | |
162 | 352 | let mut s = Stream::new_at(data, doc_list_offset.to_usize())?; |
163 | 198 | let count = s.read::<u16>()?; |
164 | 191 | let records = s.read_array16::<SvgDocumentRecord>(count)?; |
165 | | |
166 | 120 | Some(Table { |
167 | 120 | documents: SvgDocumentsList { |
168 | 120 | data: &data[doc_list_offset.0 as usize..], |
169 | 120 | records, |
170 | 120 | }, |
171 | 120 | }) |
172 | 363 | } |
173 | | } |