Coverage Report

Created: 2026-04-04 06:51

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