1use super::parser::Stream;
5
6const WEIGHT_CLASS_OFFSET: usize = 4;
7const WIDTH_CLASS_OFFSET: usize = 6;
8const SELECTION_OFFSET: usize = 62;
9
10#[allow(missing_docs)]
12#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
13pub enum Weight {
14 Thin,
15 ExtraLight,
16 Light,
17 Normal,
18 Medium,
19 SemiBold,
20 Bold,
21 ExtraBold,
22 Black,
23 Other(u16),
24}
25
26impl Weight {
27 #[inline]
29 pub fn to_number(self) -> u16 {
30 match self {
31 Weight::Thin => 100,
32 Weight::ExtraLight => 200,
33 Weight::Light => 300,
34 Weight::Normal => 400,
35 Weight::Medium => 500,
36 Weight::SemiBold => 600,
37 Weight::Bold => 700,
38 Weight::ExtraBold => 800,
39 Weight::Black => 900,
40 Weight::Other(n) => n,
41 }
42 }
43}
44
45impl From<u16> for Weight {
46 #[inline]
47 fn from(value: u16) -> Self {
48 match value {
49 100 => Weight::Thin,
50 200 => Weight::ExtraLight,
51 300 => Weight::Light,
52 400 => Weight::Normal,
53 500 => Weight::Medium,
54 600 => Weight::SemiBold,
55 700 => Weight::Bold,
56 800 => Weight::ExtraBold,
57 900 => Weight::Black,
58 _ => Weight::Other(value),
59 }
60 }
61}
62
63impl Default for Weight {
64 #[inline]
65 fn default() -> Self {
66 Weight::Normal
67 }
68}
69
70#[allow(missing_docs)]
72#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
73pub enum Width {
74 UltraCondensed,
75 ExtraCondensed,
76 Condensed,
77 SemiCondensed,
78 Normal,
79 SemiExpanded,
80 Expanded,
81 ExtraExpanded,
82 UltraExpanded,
83}
84
85impl Width {
86 #[inline]
88 pub fn to_number(self) -> u16 {
89 match self {
90 Width::UltraCondensed => 1,
91 Width::ExtraCondensed => 2,
92 Width::Condensed => 3,
93 Width::SemiCondensed => 4,
94 Width::Normal => 5,
95 Width::SemiExpanded => 6,
96 Width::Expanded => 7,
97 Width::ExtraExpanded => 8,
98 Width::UltraExpanded => 9,
99 }
100 }
101}
102
103impl Default for Width {
104 #[inline]
105 fn default() -> Self {
106 Width::Normal
107 }
108}
109
110#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
112pub enum Style {
113 Normal,
115 Italic,
117 Oblique,
119}
120
121impl Default for Style {
122 #[inline]
123 fn default() -> Style {
124 Style::Normal
125 }
126}
127
128#[derive(Clone, Copy)]
130struct SelectionFlags(u16);
131
132#[rustfmt::skip]
133impl SelectionFlags {
134 #[inline] fn italic(self) -> bool { self.0 & (1 << 0) != 0 }
135 #[inline] fn oblique(self) -> bool { self.0 & (1 << 9) != 0 }
136}
137
138#[derive(Clone, Copy)]
140pub struct Table<'a> {
141 pub version: u8,
143 data: &'a [u8],
144}
145
146impl<'a> Table<'a> {
147 pub fn parse(data: &'a [u8]) -> Option<Self> {
149 let mut s = Stream::new(data);
150 let version = s.read::<u16>()?;
151
152 let table_len = match version {
153 0 => 78,
154 1 => 86,
155 2 => 96,
156 3 => 96,
157 4 => 96,
158 5 => 100,
159 _ => return None,
160 };
161
162 if data.len() < table_len {
165 return None;
166 }
167
168 Some(Table {
169 version: version as u8,
170 data,
171 })
172 }
173
174 #[inline]
176 pub fn weight(&self) -> Weight {
177 Weight::from(Stream::read_at::<u16>(self.data, WEIGHT_CLASS_OFFSET).unwrap_or(0))
178 }
179
180 #[inline]
182 pub fn width(&self) -> Width {
183 match Stream::read_at::<u16>(self.data, WIDTH_CLASS_OFFSET).unwrap_or(0) {
184 1 => Width::UltraCondensed,
185 2 => Width::ExtraCondensed,
186 3 => Width::Condensed,
187 4 => Width::SemiCondensed,
188 5 => Width::Normal,
189 6 => Width::SemiExpanded,
190 7 => Width::Expanded,
191 8 => Width::ExtraExpanded,
192 9 => Width::UltraExpanded,
193 _ => Width::Normal,
194 }
195 }
196
197 #[inline]
198 fn fs_selection(&self) -> u16 {
199 Stream::read_at::<u16>(self.data, SELECTION_OFFSET).unwrap_or(0)
200 }
201
202 pub fn style(&self) -> Style {
204 let flags = SelectionFlags(self.fs_selection());
205 if flags.italic() {
206 Style::Italic
207 } else if self.version >= 4 && flags.oblique() {
208 Style::Oblique
209 } else {
210 Style::Normal
211 }
212 }
213}