1use crate::LineMetrics;
5use crate::parser::{Stream, Fixed, LazyArray16};
6#[cfg(feature = "glyph-names")] use crate::GlyphId;
7
8const TABLE_SIZE: usize = 32;
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
279#[derive(Clone, Copy, Default)]
281pub struct Names<'a> {
282    indexes: LazyArray16<'a, u16>,
283    data: &'a [u8],
284}
285
286impl<'a> Names<'a> {
288    #[cfg(feature = "glyph-names")]
290    pub fn get(&self, glyph_id: GlyphId) -> Option<&'a str> {
291        let mut index = self.indexes.get(glyph_id.0)?;
292
293        if usize::from(index) < MACINTOSH_NAMES.len() {
296            Some(MACINTOSH_NAMES[usize::from(index)])
297        } else {
298            index -= MACINTOSH_NAMES.len() as u16;
301
302            let mut s = Stream::new(self.data);
303            let mut i = 0;
304            while !s.at_end() && i < core::u16::MAX {
305                let len = s.read::<u8>()?;
306
307                if i == index {
308                    if len == 0 {
309                        break;
311                    } else {
312                        let name = s.read_bytes(usize::from(len))?;
313                        return core::str::from_utf8(name).ok();
314                    }
315                } else {
316                    s.advance(usize::from(len));
317                }
318
319                i += 1;
320            }
321
322            None
323        }
324    }
325
326    #[inline]
328    pub fn len(&self) -> u16 {
329        self.indexes.len()
330    }
331}
332
333impl core::fmt::Debug for Names<'_> {
334    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
335        write!(f, "Names {{ ... }}")
336    }
337}
338
339
340#[derive(Clone, Copy, Debug)]
342pub struct Table<'a> {
343    pub italic_angle: f32,
345    pub underline_metrics: LineMetrics,
347    pub is_monospaced: bool,
349    pub names: Names<'a>,
351}
352
353
354impl<'a> Table<'a> {
355    pub fn parse(data: &'a [u8]) -> Option<Self> {
357        if data.len() < TABLE_SIZE {
358            return None;
359        }
360
361        let version = Stream::new(data).read::<u32>()?;
362        if !(version == 0x00010000 || version == 0x00020000 ||
363             version == 0x00025000 || version == 0x00030000 ||
364             version == 0x00040000)
365        {
366            return None;
367        }
368
369        let italic_angle = Stream::read_at::<Fixed>(data, ITALIC_ANGLE_OFFSET)?.0;
370
371        let underline_metrics = LineMetrics {
372            position: Stream::read_at::<i16>(data, UNDERLINE_POSITION_OFFSET)?,
373            thickness: Stream::read_at::<i16>(data, UNDERLINE_THICKNESS_OFFSET)?,
374        };
375
376        let is_monospaced = Stream::read_at::<u32>(data, IS_FIXED_PITCH_OFFSET)? != 0;
377
378        let mut names = Names::default();
379        if version == 0x00020000 {
381            let mut s = Stream::new_at(data, TABLE_SIZE)?;
382            let count = s.read::<u16>()?;
383            names.indexes = s.read_array16::<u16>(count)?;
384            names.data = s.tail()?;
385        }
386
387        Some(Table {
388            italic_angle,
389            underline_metrics,
390            is_monospaced,
391            names,
392        })
393    }
394}