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}