ttf_parser/tables/
os2.rs

1//! A [OS/2 and Windows Metrics Table](https://docs.microsoft.com/en-us/typography/opentype/spec/os2)
2//! implementation.
3
4use crate::parser::Stream;
5use crate::LineMetrics;
6
7const WEIGHT_CLASS_OFFSET: usize = 4;
8const WIDTH_CLASS_OFFSET: usize = 6;
9const TYPE_OFFSET: usize = 8;
10const Y_SUBSCRIPT_X_SIZE_OFFSET: usize = 10;
11const Y_SUPERSCRIPT_X_SIZE_OFFSET: usize = 18;
12const Y_STRIKEOUT_SIZE_OFFSET: usize = 26;
13const Y_STRIKEOUT_POSITION_OFFSET: usize = 28;
14const UNICODE_RANGES_OFFSET: usize = 42;
15const SELECTION_OFFSET: usize = 62;
16const TYPO_ASCENDER_OFFSET: usize = 68;
17const TYPO_DESCENDER_OFFSET: usize = 70;
18const TYPO_LINE_GAP_OFFSET: usize = 72;
19const WIN_ASCENT: usize = 74;
20const WIN_DESCENT: usize = 76;
21const X_HEIGHT_OFFSET: usize = 86;
22const CAP_HEIGHT_OFFSET: usize = 88;
23
24/// A face [weight](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass).
25#[allow(missing_docs)]
26#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
27pub enum Weight {
28    Thin,
29    ExtraLight,
30    Light,
31    Normal,
32    Medium,
33    SemiBold,
34    Bold,
35    ExtraBold,
36    Black,
37    Other(u16),
38}
39
40impl Weight {
41    /// Returns a numeric representation of a weight.
42    #[inline]
43    pub fn to_number(self) -> u16 {
44        match self {
45            Weight::Thin => 100,
46            Weight::ExtraLight => 200,
47            Weight::Light => 300,
48            Weight::Normal => 400,
49            Weight::Medium => 500,
50            Weight::SemiBold => 600,
51            Weight::Bold => 700,
52            Weight::ExtraBold => 800,
53            Weight::Black => 900,
54            Weight::Other(n) => n,
55        }
56    }
57}
58
59impl From<u16> for Weight {
60    #[inline]
61    fn from(value: u16) -> Self {
62        match value {
63            100 => Weight::Thin,
64            200 => Weight::ExtraLight,
65            300 => Weight::Light,
66            400 => Weight::Normal,
67            500 => Weight::Medium,
68            600 => Weight::SemiBold,
69            700 => Weight::Bold,
70            800 => Weight::ExtraBold,
71            900 => Weight::Black,
72            _ => Weight::Other(value),
73        }
74    }
75}
76
77impl Default for Weight {
78    #[inline]
79    fn default() -> Self {
80        Weight::Normal
81    }
82}
83
84/// A face [width](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass).
85#[allow(missing_docs)]
86#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
87pub enum Width {
88    UltraCondensed,
89    ExtraCondensed,
90    Condensed,
91    SemiCondensed,
92    Normal,
93    SemiExpanded,
94    Expanded,
95    ExtraExpanded,
96    UltraExpanded,
97}
98
99impl Width {
100    /// Returns a numeric representation of a width.
101    #[inline]
102    pub fn to_number(self) -> u16 {
103        match self {
104            Width::UltraCondensed => 1,
105            Width::ExtraCondensed => 2,
106            Width::Condensed => 3,
107            Width::SemiCondensed => 4,
108            Width::Normal => 5,
109            Width::SemiExpanded => 6,
110            Width::Expanded => 7,
111            Width::ExtraExpanded => 8,
112            Width::UltraExpanded => 9,
113        }
114    }
115}
116
117impl Default for Width {
118    #[inline]
119    fn default() -> Self {
120        Width::Normal
121    }
122}
123
124/// Face [permissions](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fst).
125#[allow(missing_docs)]
126#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
127pub enum Permissions {
128    Installable,
129    Restricted,
130    PreviewAndPrint,
131    Editable,
132}
133
134/// A face style.
135#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
136pub enum Style {
137    /// A face that is neither italic not obliqued.
138    Normal,
139    /// A form that is generally cursive in nature.
140    Italic,
141    /// A typically-sloped version of the regular face.
142    Oblique,
143}
144
145impl Default for Style {
146    #[inline]
147    fn default() -> Style {
148        Style::Normal
149    }
150}
151
152/// A script metrics used by subscript and superscript.
153#[repr(C)]
154#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
155pub struct ScriptMetrics {
156    /// Horizontal face size.
157    pub x_size: i16,
158
159    /// Vertical face size.
160    pub y_size: i16,
161
162    /// X offset.
163    pub x_offset: i16,
164
165    /// Y offset.
166    pub y_offset: i16,
167}
168
169// https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
170#[derive(Clone, Copy)]
171struct SelectionFlags(u16);
172
173#[rustfmt::skip]
174impl SelectionFlags {
175    #[inline] fn italic(self) -> bool { self.0 & (1 << 0) != 0 }
176    #[inline] fn bold(self) -> bool { self.0 & (1 << 5) != 0 }
177    // #[inline] fn regular(self) -> bool { self.0 & (1 << 6) != 0 }
178    #[inline] fn use_typo_metrics(self) -> bool { self.0 & (1 << 7) != 0 }
179    #[inline] fn oblique(self) -> bool { self.0 & (1 << 9) != 0 }
180}
181
182/// [Unicode Ranges](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur).
183#[derive(Clone, Copy, Default, Debug)]
184pub struct UnicodeRanges(pub u128);
185
186impl UnicodeRanges {
187    /// Checks if ranges contain the specified character.
188    pub fn contains_char(&self, c: char) -> bool {
189        let range = char_range_index(c);
190        if range >= 0 {
191            self.0 & (1 << range as u128) != 0
192        } else {
193            false
194        }
195    }
196}
197
198fn char_range_index(c: char) -> i8 {
199    match c as u32 {
200        0x0000..=0x007F => 0,
201        0x0080..=0x00FF => 1,
202        0x0100..=0x017F => 2,
203        0x0180..=0x024F => 3,
204        0x0250..=0x02AF => 4,
205        0x1D00..=0x1DBF => 4,
206        0x02B0..=0x02FF => 5,
207        0xA700..=0xA71F => 5,
208        0x0300..=0x036F => 6,
209        0x1DC0..=0x1DFF => 6,
210        0x0370..=0x03FF => 7,
211        0x2C80..=0x2CFF => 8,
212        0x0400..=0x052F => 9,
213        0x2DE0..=0x2DFF => 9,
214        0xA640..=0xA69F => 9,
215        0x0530..=0x058F => 10,
216        0x0590..=0x05FF => 11,
217        0xA500..=0xA63F => 12,
218        0x0600..=0x06FF => 13,
219        0x0750..=0x077F => 13,
220        0x07C0..=0x07FF => 14,
221        0x0900..=0x097F => 15,
222        0x0980..=0x09FF => 16,
223        0x0A00..=0x0A7F => 17,
224        0x0A80..=0x0AFF => 18,
225        0x0B00..=0x0B7F => 19,
226        0x0B80..=0x0BFF => 20,
227        0x0C00..=0x0C7F => 21,
228        0x0C80..=0x0CFF => 22,
229        0x0D00..=0x0D7F => 23,
230        0x0E00..=0x0E7F => 24,
231        0x0E80..=0x0EFF => 25,
232        0x10A0..=0x10FF => 26,
233        0x2D00..=0x2D2F => 26,
234        0x1B00..=0x1B7F => 27,
235        0x1100..=0x11FF => 28,
236        0x1E00..=0x1EFF => 29,
237        0x2C60..=0x2C7F => 29,
238        0xA720..=0xA7FF => 29,
239        0x1F00..=0x1FFF => 30,
240        0x2000..=0x206F => 31,
241        0x2E00..=0x2E7F => 31,
242        0x2070..=0x209F => 32,
243        0x20A0..=0x20CF => 33,
244        0x20D0..=0x20FF => 34,
245        0x2100..=0x214F => 35,
246        0x2150..=0x218F => 36,
247        0x2190..=0x21FF => 37,
248        0x27F0..=0x27FF => 37,
249        0x2900..=0x297F => 37,
250        0x2B00..=0x2BFF => 37,
251        0x2200..=0x22FF => 38,
252        0x2A00..=0x2AFF => 38,
253        0x27C0..=0x27EF => 38,
254        0x2980..=0x29FF => 38,
255        0x2300..=0x23FF => 39,
256        0x2400..=0x243F => 40,
257        0x2440..=0x245F => 41,
258        0x2460..=0x24FF => 42,
259        0x2500..=0x257F => 43,
260        0x2580..=0x259F => 44,
261        0x25A0..=0x25FF => 45,
262        0x2600..=0x26FF => 46,
263        0x2700..=0x27BF => 47,
264        0x3000..=0x303F => 48,
265        0x3040..=0x309F => 49,
266        0x30A0..=0x30FF => 50,
267        0x31F0..=0x31FF => 50,
268        0x3100..=0x312F => 51,
269        0x31A0..=0x31BF => 51,
270        0x3130..=0x318F => 52,
271        0xA840..=0xA87F => 53,
272        0x3200..=0x32FF => 54,
273        0x3300..=0x33FF => 55,
274        0xAC00..=0xD7AF => 56,
275        // Ignore Non-Plane 0 (57), since this is not a real range.
276        0x10900..=0x1091F => 58,
277        0x4E00..=0x9FFF => 59,
278        0x2E80..=0x2FDF => 59,
279        0x2FF0..=0x2FFF => 59,
280        0x3400..=0x4DBF => 59,
281        0x20000..=0x2A6DF => 59,
282        0x3190..=0x319F => 59,
283        0xE000..=0xF8FF => 60,
284        0x31C0..=0x31EF => 61,
285        0xF900..=0xFAFF => 61,
286        0x2F800..=0x2FA1F => 61,
287        0xFB00..=0xFB4F => 62,
288        0xFB50..=0xFDFF => 63,
289        0xFE20..=0xFE2F => 64,
290        0xFE10..=0xFE1F => 65,
291        0xFE30..=0xFE4F => 65,
292        0xFE50..=0xFE6F => 66,
293        0xFE70..=0xFEFF => 67,
294        0xFF00..=0xFFEF => 68,
295        0xFFF0..=0xFFFF => 69,
296        0x0F00..=0x0FFF => 70,
297        0x0700..=0x074F => 71,
298        0x0780..=0x07BF => 72,
299        0x0D80..=0x0DFF => 73,
300        0x1000..=0x109F => 74,
301        0x1200..=0x139F => 75,
302        0x2D80..=0x2DDF => 75,
303        0x13A0..=0x13FF => 76,
304        0x1400..=0x167F => 77,
305        0x1680..=0x169F => 78,
306        0x16A0..=0x16FF => 79,
307        0x1780..=0x17FF => 80,
308        0x19E0..=0x19FF => 80,
309        0x1800..=0x18AF => 81,
310        0x2800..=0x28FF => 82,
311        0xA000..=0xA48F => 83,
312        0xA490..=0xA4CF => 83,
313        0x1700..=0x177F => 84,
314        0x10300..=0x1032F => 85,
315        0x10330..=0x1034F => 86,
316        0x10400..=0x1044F => 87,
317        0x1D000..=0x1D24F => 88,
318        0x1D400..=0x1D7FF => 89,
319        0xF0000..=0xFFFFD => 90,
320        0x100000..=0x10FFFD => 90,
321        0xFE00..=0xFE0F => 91,
322        0xE0100..=0xE01EF => 91,
323        0xE0000..=0xE007F => 92,
324        0x1900..=0x194F => 93,
325        0x1950..=0x197F => 94,
326        0x1980..=0x19DF => 95,
327        0x1A00..=0x1A1F => 96,
328        0x2C00..=0x2C5F => 97,
329        0x2D30..=0x2D7F => 98,
330        0x4DC0..=0x4DFF => 99,
331        0xA800..=0xA82F => 100,
332        0x10000..=0x1013F => 101,
333        0x10140..=0x1018F => 102,
334        0x10380..=0x1039F => 103,
335        0x103A0..=0x103DF => 104,
336        0x10450..=0x1047F => 105,
337        0x10480..=0x104AF => 106,
338        0x10800..=0x1083F => 107,
339        0x10A00..=0x10A5F => 108,
340        0x1D300..=0x1D35F => 109,
341        0x12000..=0x123FF => 110,
342        0x12400..=0x1247F => 110,
343        0x1D360..=0x1D37F => 111,
344        0x1B80..=0x1BBF => 112,
345        0x1C00..=0x1C4F => 113,
346        0x1C50..=0x1C7F => 114,
347        0xA880..=0xA8DF => 115,
348        0xA900..=0xA92F => 116,
349        0xA930..=0xA95F => 117,
350        0xAA00..=0xAA5F => 118,
351        0x10190..=0x101CF => 119,
352        0x101D0..=0x101FF => 120,
353        0x102A0..=0x102DF => 121,
354        0x10280..=0x1029F => 121,
355        0x10920..=0x1093F => 121,
356        0x1F030..=0x1F09F => 122,
357        0x1F000..=0x1F02F => 122,
358        _ => -1,
359    }
360}
361
362/// A [OS/2 and Windows Metrics Table](https://docs.microsoft.com/en-us/typography/opentype/spec/os2).
363#[derive(Clone, Copy)]
364pub struct Table<'a> {
365    /// Table version.
366    pub version: u8,
367    data: &'a [u8],
368}
369
370impl<'a> Table<'a> {
371    /// Parses a table from raw data.
372    pub fn parse(data: &'a [u8]) -> Option<Self> {
373        let mut s = Stream::new(data);
374        let version = s.read::<u16>()?;
375
376        let table_len = match version {
377            0 => 78,
378            1 => 86,
379            2 => 96,
380            3 => 96,
381            4 => 96,
382            5 => 100,
383            _ => return None,
384        };
385
386        // Do not check the exact length, because some fonts include
387        // padding in table's length in table records, which is incorrect.
388        if data.len() < table_len {
389            return None;
390        }
391
392        Some(Table {
393            version: version as u8,
394            data,
395        })
396    }
397
398    /// Returns weight class.
399    #[inline]
400    pub fn weight(&self) -> Weight {
401        Weight::from(Stream::read_at::<u16>(self.data, WEIGHT_CLASS_OFFSET).unwrap_or(0))
402    }
403
404    /// Returns face width.
405    #[inline]
406    pub fn width(&self) -> Width {
407        match Stream::read_at::<u16>(self.data, WIDTH_CLASS_OFFSET).unwrap_or(0) {
408            1 => Width::UltraCondensed,
409            2 => Width::ExtraCondensed,
410            3 => Width::Condensed,
411            4 => Width::SemiCondensed,
412            5 => Width::Normal,
413            6 => Width::SemiExpanded,
414            7 => Width::Expanded,
415            8 => Width::ExtraExpanded,
416            9 => Width::UltraExpanded,
417            _ => Width::Normal,
418        }
419    }
420
421    /// Returns face permissions.
422    ///
423    /// Returns `None` in case of a malformed value.
424    #[inline]
425    pub fn permissions(&self) -> Option<Permissions> {
426        let n = Stream::read_at::<u16>(self.data, TYPE_OFFSET).unwrap_or(0);
427        if self.version <= 2 {
428            // Version 2 and prior, applications are allowed to take
429            // the most permissive of provided flags
430            let permission = if n & 0xF == 0 {
431                Permissions::Installable
432            } else if n & 8 != 0 {
433                Permissions::Editable
434            } else if n & 4 != 0 {
435                Permissions::PreviewAndPrint
436            } else {
437                Permissions::Restricted
438            };
439
440            Some(permission)
441        } else {
442            // Version 3 onwards, flags must be mutually exclusive.
443            match n & 0xF {
444                0 => Some(Permissions::Installable),
445                2 => Some(Permissions::Restricted),
446                4 => Some(Permissions::PreviewAndPrint),
447                8 => Some(Permissions::Editable),
448                _ => None,
449            }
450        }
451    }
452
453    /// Checks if the face allows embedding a subset, further restricted by [`Self::permissions`].
454    #[inline]
455    pub fn is_subsetting_allowed(&self) -> bool {
456        if self.version <= 1 {
457            // Flag introduced in version 2
458            true
459        } else {
460            let n = Stream::read_at::<u16>(self.data, TYPE_OFFSET).unwrap_or(0);
461            n & 0x0100 == 0
462        }
463    }
464
465    /// Checks if the face allows outline data to be embedded.
466    ///
467    /// If false, only bitmaps may be embedded in accordance with [`Self::permissions`].
468    ///
469    /// If the font contains no bitmaps and this flag is not set, it implies no embedding is allowed.
470    #[inline]
471    pub fn is_outline_embedding_allowed(&self) -> bool {
472        if self.version <= 1 {
473            // Flag introduced in version 2
474            true
475        } else {
476            let n = Stream::read_at::<u16>(self.data, TYPE_OFFSET).unwrap_or(0);
477            n & 0x0200 == 0
478        }
479    }
480
481    /// Returns subscript metrics.
482    #[inline]
483    pub fn subscript_metrics(&self) -> ScriptMetrics {
484        let mut s = Stream::new_at(self.data, Y_SUBSCRIPT_X_SIZE_OFFSET).unwrap_or_default();
485        ScriptMetrics {
486            x_size: s.read::<i16>().unwrap_or(0),
487            y_size: s.read::<i16>().unwrap_or(0),
488            x_offset: s.read::<i16>().unwrap_or(0),
489            y_offset: s.read::<i16>().unwrap_or(0),
490        }
491    }
492
493    /// Returns superscript metrics.
494    #[inline]
495    pub fn superscript_metrics(&self) -> ScriptMetrics {
496        let mut s = Stream::new_at(self.data, Y_SUPERSCRIPT_X_SIZE_OFFSET).unwrap_or_default();
497        ScriptMetrics {
498            x_size: s.read::<i16>().unwrap_or(0),
499            y_size: s.read::<i16>().unwrap_or(0),
500            x_offset: s.read::<i16>().unwrap_or(0),
501            y_offset: s.read::<i16>().unwrap_or(0),
502        }
503    }
504
505    /// Returns strikeout metrics.
506    #[inline]
507    pub fn strikeout_metrics(&self) -> LineMetrics {
508        LineMetrics {
509            thickness: Stream::read_at::<i16>(self.data, Y_STRIKEOUT_SIZE_OFFSET).unwrap_or(0),
510            position: Stream::read_at::<i16>(self.data, Y_STRIKEOUT_POSITION_OFFSET).unwrap_or(0),
511        }
512    }
513
514    /// Returns Unicode ranges.
515    #[inline]
516    pub fn unicode_ranges(&self) -> UnicodeRanges {
517        let mut s = Stream::new_at(self.data, UNICODE_RANGES_OFFSET).unwrap();
518        let n1 = s.read::<u32>().unwrap_or(0) as u128;
519        let n2 = s.read::<u32>().unwrap_or(0) as u128;
520        let n3 = s.read::<u32>().unwrap_or(0) as u128;
521        let n4 = s.read::<u32>().unwrap_or(0) as u128;
522        UnicodeRanges(n4 << 96 | n3 << 64 | n2 << 32 | n1)
523    }
524
525    #[inline]
526    fn fs_selection(&self) -> u16 {
527        Stream::read_at::<u16>(self.data, SELECTION_OFFSET).unwrap_or(0)
528    }
529
530    /// Returns style.
531    pub fn style(&self) -> Style {
532        let flags = SelectionFlags(self.fs_selection());
533        if flags.italic() {
534            Style::Italic
535        } else if self.version >= 4 && flags.oblique() {
536            Style::Oblique
537        } else {
538            Style::Normal
539        }
540    }
541
542    /// Checks if face is bold.
543    ///
544    /// Do not confuse with [`Weight::Bold`].
545    #[inline]
546    pub fn is_bold(&self) -> bool {
547        SelectionFlags(self.fs_selection()).bold()
548    }
549
550    /// Checks if typographic metrics should be used.
551    #[inline]
552    pub fn use_typographic_metrics(&self) -> bool {
553        if self.version < 4 {
554            false
555        } else {
556            SelectionFlags(self.fs_selection()).use_typo_metrics()
557        }
558    }
559
560    /// Returns typographic ascender.
561    #[inline]
562    pub fn typographic_ascender(&self) -> i16 {
563        Stream::read_at::<i16>(self.data, TYPO_ASCENDER_OFFSET).unwrap_or(0)
564    }
565
566    /// Returns typographic descender.
567    #[inline]
568    pub fn typographic_descender(&self) -> i16 {
569        Stream::read_at::<i16>(self.data, TYPO_DESCENDER_OFFSET).unwrap_or(0)
570    }
571
572    /// Returns typographic line gap.
573    #[inline]
574    pub fn typographic_line_gap(&self) -> i16 {
575        Stream::read_at::<i16>(self.data, TYPO_LINE_GAP_OFFSET).unwrap_or(0)
576    }
577
578    /// Returns Windows ascender.
579    #[inline]
580    pub fn windows_ascender(&self) -> i16 {
581        Stream::read_at::<i16>(self.data, WIN_ASCENT).unwrap_or(0)
582    }
583
584    /// Returns Windows descender.
585    #[inline]
586    pub fn windows_descender(&self) -> i16 {
587        // Should be negated.
588        -Stream::read_at::<i16>(self.data, WIN_DESCENT).unwrap_or(0)
589    }
590
591    /// Returns x height.
592    ///
593    /// Returns `None` version is < 2.
594    #[inline]
595    pub fn x_height(&self) -> Option<i16> {
596        if self.version < 2 {
597            None
598        } else {
599            Stream::read_at::<i16>(self.data, X_HEIGHT_OFFSET)
600        }
601    }
602
603    /// Returns capital height.
604    ///
605    /// Returns `None` version is < 2.
606    #[inline]
607    pub fn capital_height(&self) -> Option<i16> {
608        if self.version < 2 {
609            None
610        } else {
611            Stream::read_at::<i16>(self.data, CAP_HEIGHT_OFFSET)
612        }
613    }
614}
615
616impl core::fmt::Debug for Table<'_> {
617    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
618        write!(f, "Table {{ ... }}")
619    }
620}