ttf_parser/tables/
post.rs

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