1use crate::parser::{Fixed, LazyArray16, Stream};
5#[cfg(feature = "glyph-names")]
6use crate::GlyphId;
7use crate::LineMetrics;
8
9const ITALIC_ANGLE_OFFSET: usize = 4;
10const UNDERLINE_POSITION_OFFSET: usize = 8;
11const UNDERLINE_THICKNESS_OFFSET: usize = 10;
12const IS_FIXED_PITCH_OFFSET: usize = 12;
13
14#[cfg(feature = "glyph-names")]
17const MACINTOSH_NAMES: &[&str] = &[
18 ".notdef",
19 ".null",
20 "nonmarkingreturn",
21 "space",
22 "exclam",
23 "quotedbl",
24 "numbersign",
25 "dollar",
26 "percent",
27 "ampersand",
28 "quotesingle",
29 "parenleft",
30 "parenright",
31 "asterisk",
32 "plus",
33 "comma",
34 "hyphen",
35 "period",
36 "slash",
37 "zero",
38 "one",
39 "two",
40 "three",
41 "four",
42 "five",
43 "six",
44 "seven",
45 "eight",
46 "nine",
47 "colon",
48 "semicolon",
49 "less",
50 "equal",
51 "greater",
52 "question",
53 "at",
54 "A",
55 "B",
56 "C",
57 "D",
58 "E",
59 "F",
60 "G",
61 "H",
62 "I",
63 "J",
64 "K",
65 "L",
66 "M",
67 "N",
68 "O",
69 "P",
70 "Q",
71 "R",
72 "S",
73 "T",
74 "U",
75 "V",
76 "W",
77 "X",
78 "Y",
79 "Z",
80 "bracketleft",
81 "backslash",
82 "bracketright",
83 "asciicircum",
84 "underscore",
85 "grave",
86 "a",
87 "b",
88 "c",
89 "d",
90 "e",
91 "f",
92 "g",
93 "h",
94 "i",
95 "j",
96 "k",
97 "l",
98 "m",
99 "n",
100 "o",
101 "p",
102 "q",
103 "r",
104 "s",
105 "t",
106 "u",
107 "v",
108 "w",
109 "x",
110 "y",
111 "z",
112 "braceleft",
113 "bar",
114 "braceright",
115 "asciitilde",
116 "Adieresis",
117 "Aring",
118 "Ccedilla",
119 "Eacute",
120 "Ntilde",
121 "Odieresis",
122 "Udieresis",
123 "aacute",
124 "agrave",
125 "acircumflex",
126 "adieresis",
127 "atilde",
128 "aring",
129 "ccedilla",
130 "eacute",
131 "egrave",
132 "ecircumflex",
133 "edieresis",
134 "iacute",
135 "igrave",
136 "icircumflex",
137 "idieresis",
138 "ntilde",
139 "oacute",
140 "ograve",
141 "ocircumflex",
142 "odieresis",
143 "otilde",
144 "uacute",
145 "ugrave",
146 "ucircumflex",
147 "udieresis",
148 "dagger",
149 "degree",
150 "cent",
151 "sterling",
152 "section",
153 "bullet",
154 "paragraph",
155 "germandbls",
156 "registered",
157 "copyright",
158 "trademark",
159 "acute",
160 "dieresis",
161 "notequal",
162 "AE",
163 "Oslash",
164 "infinity",
165 "plusminus",
166 "lessequal",
167 "greaterequal",
168 "yen",
169 "mu",
170 "partialdiff",
171 "summation",
172 "product",
173 "pi",
174 "integral",
175 "ordfeminine",
176 "ordmasculine",
177 "Omega",
178 "ae",
179 "oslash",
180 "questiondown",
181 "exclamdown",
182 "logicalnot",
183 "radical",
184 "florin",
185 "approxequal",
186 "Delta",
187 "guillemotleft",
188 "guillemotright",
189 "ellipsis",
190 "nonbreakingspace",
191 "Agrave",
192 "Atilde",
193 "Otilde",
194 "OE",
195 "oe",
196 "endash",
197 "emdash",
198 "quotedblleft",
199 "quotedblright",
200 "quoteleft",
201 "quoteright",
202 "divide",
203 "lozenge",
204 "ydieresis",
205 "Ydieresis",
206 "fraction",
207 "currency",
208 "guilsinglleft",
209 "guilsinglright",
210 "fi",
211 "fl",
212 "daggerdbl",
213 "periodcentered",
214 "quotesinglbase",
215 "quotedblbase",
216 "perthousand",
217 "Acircumflex",
218 "Ecircumflex",
219 "Aacute",
220 "Edieresis",
221 "Egrave",
222 "Iacute",
223 "Icircumflex",
224 "Idieresis",
225 "Igrave",
226 "Oacute",
227 "Ocircumflex",
228 "apple",
229 "Ograve",
230 "Uacute",
231 "Ucircumflex",
232 "Ugrave",
233 "dotlessi",
234 "circumflex",
235 "tilde",
236 "macron",
237 "breve",
238 "dotaccent",
239 "ring",
240 "cedilla",
241 "hungarumlaut",
242 "ogonek",
243 "caron",
244 "Lslash",
245 "lslash",
246 "Scaron",
247 "scaron",
248 "Zcaron",
249 "zcaron",
250 "brokenbar",
251 "Eth",
252 "eth",
253 "Yacute",
254 "yacute",
255 "Thorn",
256 "thorn",
257 "minus",
258 "multiply",
259 "onesuperior",
260 "twosuperior",
261 "threesuperior",
262 "onehalf",
263 "onequarter",
264 "threequarters",
265 "franc",
266 "Gbreve",
267 "gbreve",
268 "Idotaccent",
269 "Scedilla",
270 "scedilla",
271 "Cacute",
272 "cacute",
273 "Ccaron",
274 "ccaron",
275 "dcroat",
276];
277
278#[derive(Clone, Copy, Default)]
283pub struct Names<'a> {
284 data: &'a [u8],
285 offset: usize,
286}
287
288impl core::fmt::Debug for Names<'_> {
289 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
290 write!(f, "Names {{ ... }}")
291 }
292}
293
294impl<'a> Iterator for Names<'a> {
295 type Item = &'a str;
296
297 fn next(&mut self) -> Option<Self::Item> {
298 if self.offset >= self.data.len() {
302 return None;
303 }
304
305 let len = self.data[self.offset];
306 self.offset += 1;
307
308 if len == 0 {
310 return None;
311 }
312
313 let name = self.data.get(self.offset..self.offset + usize::from(len))?;
314 self.offset += usize::from(len);
315 core::str::from_utf8(name).ok()
316 }
317}
318
319#[derive(Clone, Copy, Debug)]
321pub struct Table<'a> {
322 pub italic_angle: f32,
324 pub underline_metrics: LineMetrics,
326 pub is_monospaced: bool,
328 glyph_indexes: LazyArray16<'a, u16>,
329 names_data: &'a [u8],
330}
331
332impl<'a> Table<'a> {
333 pub fn parse(data: &'a [u8]) -> Option<Self> {
335 if data.len() < 32 {
338 return None;
339 }
340
341 let version = Stream::new(data).read::<u32>()?;
342 if !(version == 0x00010000
343 || version == 0x00020000
344 || version == 0x00025000
345 || version == 0x00030000
346 || version == 0x00040000)
347 {
348 return None;
349 }
350
351 let italic_angle = Stream::read_at::<Fixed>(data, ITALIC_ANGLE_OFFSET)?.0;
352
353 let underline_metrics = LineMetrics {
354 position: Stream::read_at::<i16>(data, UNDERLINE_POSITION_OFFSET)?,
355 thickness: Stream::read_at::<i16>(data, UNDERLINE_THICKNESS_OFFSET)?,
356 };
357
358 let is_monospaced = Stream::read_at::<u32>(data, IS_FIXED_PITCH_OFFSET)? != 0;
359
360 let mut names_data: &[u8] = &[];
361 let mut glyph_indexes = LazyArray16::default();
362 if version == 0x00020000 {
364 let mut s = Stream::new_at(data, 32)?;
365 let indexes_count = s.read::<u16>()?;
366 glyph_indexes = s.read_array16::<u16>(indexes_count)?;
367 names_data = s.tail()?;
368 }
369
370 Some(Table {
371 italic_angle,
372 underline_metrics,
373 is_monospaced,
374 names_data,
375 glyph_indexes,
376 })
377 }
378
379 #[cfg(feature = "glyph-names")]
381 pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&'a str> {
382 let mut index = self.glyph_indexes.get(glyph_id.0)?;
383
384 if usize::from(index) < MACINTOSH_NAMES.len() {
387 Some(MACINTOSH_NAMES[usize::from(index)])
388 } else {
389 index -= MACINTOSH_NAMES.len() as u16;
392 self.names().nth(usize::from(index))
393 }
394 }
395
396 #[cfg(feature = "glyph-names")]
398 pub fn glyph_index_by_name(&self, name: &str) -> Option<GlyphId> {
399 let id = if let Some(index) = MACINTOSH_NAMES.iter().position(|n| *n == name) {
400 self.glyph_indexes
401 .into_iter()
402 .position(|i| usize::from(i) == index)?
403 } else {
404 let mut index = self.names().position(|n| n == name)?;
405 index += MACINTOSH_NAMES.len();
406 self.glyph_indexes
407 .into_iter()
408 .position(|i| usize::from(i) == index)?
409 };
410
411 Some(GlyphId(id as u16))
412 }
413
414 pub fn names(&self) -> Names<'a> {
418 Names {
419 data: self.names_data,
420 offset: 0,
421 }
422 }
423}