ttf_parser/tables/
post.rs

1//! A [PostScript Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/post) implementation.
3
4use 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// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html
15/// A list of Macintosh glyph names.
16#[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/// An iterator over glyph names.
279///
280/// The `post` table doesn't provide the glyph names count,
281/// so we have to simply iterate over all of them to find it out.
282#[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        // Glyph names are stored as Pascal Strings.
299        // Meaning u8 (len) + [u8] (data).
300
301        if self.offset >= self.data.len() {
302            return None;
303        }
304
305        let len = self.data[self.offset];
306        self.offset += 1;
307
308        // An empty name is an error.
309        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/// A [PostScript Table](https://docs.microsoft.com/en-us/typography/opentype/spec/post).
320#[derive(Clone, Copy, Debug)]
321pub struct Table<'a> {
322    /// Italic angle in counter-clockwise degrees from the vertical.
323    pub italic_angle: f32,
324    /// Underline metrics.
325    pub underline_metrics: LineMetrics,
326    /// Flag that indicates that the font is monospaced.
327    pub is_monospaced: bool,
328    glyph_indexes: LazyArray16<'a, u16>,
329    names_data: &'a [u8],
330}
331
332impl<'a> Table<'a> {
333    /// Parses a table from raw data.
334    pub fn parse(data: &'a [u8]) -> Option<Self> {
335        // Do not check the exact length, because some fonts include
336        // padding in table's length in table records, which is incorrect.
337        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        // Only version 2.0 of the table has data at the end.
363        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    /// Returns a glyph name by ID.
380    #[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 the name index is between 0 and 257, treat the name index
385        // as a glyph index in the Macintosh standard order.'
386        if usize::from(index) < MACINTOSH_NAMES.len() {
387            Some(MACINTOSH_NAMES[usize::from(index)])
388        } else {
389            // 'If the name index is between 258 and 65535, then subtract 258 and use that
390            // to index into the list of Pascal strings at the end of the table.'
391            index -= MACINTOSH_NAMES.len() as u16;
392            self.names().nth(usize::from(index))
393        }
394    }
395
396    /// Returns a glyph ID by a name.
397    #[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    /// Returns an iterator over glyph names.
415    ///
416    /// Default/predefined names are not included. Just the one in the font file.
417    pub fn names(&self) -> Names<'a> {
418        Names {
419            data: self.names_data,
420            offset: 0,
421        }
422    }
423}