we (web engine): Experimental web browser project to understand the limits of Claude
1//! `OS/2` — OS/2 and Windows Metrics table.
2//!
3//! Contains font-wide metrics used for line spacing, weight classification,
4//! and typographic alignment.
5//! Reference: <https://learn.microsoft.com/en-us/typography/opentype/spec/os2>
6
7use crate::font::parse::Reader;
8use crate::font::FontError;
9
10/// Parsed `OS/2` table.
11#[derive(Debug)]
12pub struct Os2Table {
13 /// Table version (0–5).
14 pub version: u16,
15 /// Average weighted advance width of lowercase letters and space.
16 pub x_avg_char_width: i16,
17 /// Visual weight class (100–900).
18 pub us_weight_class: u16,
19 /// Visual width class (1–9).
20 pub us_width_class: u16,
21 /// Font embedding licensing rights.
22 pub fs_type: u16,
23 /// Typographic ascender.
24 pub s_typo_ascender: i16,
25 /// Typographic descender (typically negative).
26 pub s_typo_descender: i16,
27 /// Typographic line gap.
28 pub s_typo_line_gap: i16,
29 /// Strikeout stroke size in font design units.
30 pub y_strikeout_size: i16,
31 /// Strikeout stroke position above baseline.
32 pub y_strikeout_position: i16,
33 /// Subscript vertical offset.
34 pub y_subscript_y_offset: i16,
35 /// Superscript vertical offset.
36 pub y_superscript_y_offset: i16,
37 /// Height of lowercase 'x' (version >= 2, else 0).
38 pub sx_height: i16,
39 /// Height of uppercase letters (version >= 2, else 0).
40 pub s_cap_height: i16,
41}
42
43impl Os2Table {
44 /// Parse the `OS/2` table from raw bytes.
45 pub fn parse(data: &[u8]) -> Result<Os2Table, FontError> {
46 let r = Reader::new(data);
47
48 // Minimum OS/2 table is 78 bytes (version 0).
49 if r.len() < 78 {
50 return Err(FontError::MalformedTable("OS/2: too short"));
51 }
52
53 let version = r.u16(0)?;
54 let x_avg_char_width = r.i16(2)?;
55 let us_weight_class = r.u16(4)?;
56 let us_width_class = r.u16(6)?;
57 let fs_type = r.u16(8)?;
58
59 // Subscript/superscript offsets: offsets 14 and 18 respectively
60 // ySubscriptXSize(10), ySubscriptYSize(12), ySubscriptXOffset(14)
61 let y_subscript_y_offset = r.i16(16)?;
62 // ySuperscriptXSize(18), ySuperscriptYSize(20), ySuperscriptXOffset(22)
63 let y_superscript_y_offset = r.i16(24)?;
64
65 let y_strikeout_size = r.i16(26)?;
66 let y_strikeout_position = r.i16(28)?;
67
68 // sTypoAscender is at offset 68, sTypoDescender at 70, sTypoLineGap at 72
69 let s_typo_ascender = r.i16(68)?;
70 let s_typo_descender = r.i16(70)?;
71 let s_typo_line_gap = r.i16(72)?;
72
73 // sxHeight and sCapHeight are available in version >= 2 at offsets 86 and 88.
74 let (sx_height, s_cap_height) = if version >= 2 && r.len() >= 96 {
75 (r.i16(86)?, r.i16(88)?)
76 } else {
77 (0, 0)
78 };
79
80 Ok(Os2Table {
81 version,
82 x_avg_char_width,
83 us_weight_class,
84 us_width_class,
85 fs_type,
86 s_typo_ascender,
87 s_typo_descender,
88 s_typo_line_gap,
89 y_strikeout_size,
90 y_strikeout_position,
91 y_subscript_y_offset,
92 y_superscript_y_offset,
93 sx_height,
94 s_cap_height,
95 })
96 }
97}