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: u16) => 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(u128);
185
186impl UnicodeRanges {
187 /// Checks if ranges contain the specified character.
188 pub fn contains_char(&self, c: char) -> bool {
189 let range: i8 = 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 match n & 0xF {
428 0 => Some(Permissions::Installable),
429 2 => Some(Permissions::Restricted),
430 4 => Some(Permissions::PreviewAndPrint),
431 8 => Some(Permissions::Editable),
432 _ => None,
433 }
434 }
435
436 /// Checks if the face subsetting is allowed.
437 #[inline]
438 pub fn is_subsetting_allowed(&self) -> bool {
439 let n = Stream::read_at::<u16>(self.data, TYPE_OFFSET).unwrap_or(0);
440 n & 0x0100 == 0
441 }
442
443 /// Checks if the face bitmaps embedding is allowed.
444 #[inline]
445 pub fn is_bitmap_embedding_allowed(&self) -> bool {
446 let n = Stream::read_at::<u16>(self.data, TYPE_OFFSET).unwrap_or(0);
447 n & 0x0200 == 0
448 }
449
450 /// Returns subscript metrics.
451 #[inline]
452 pub fn subscript_metrics(&self) -> ScriptMetrics {
453 let mut s = Stream::new_at(self.data, Y_SUBSCRIPT_X_SIZE_OFFSET).unwrap_or_default();
454 ScriptMetrics {
455 x_size: s.read::<i16>().unwrap_or(0),
456 y_size: s.read::<i16>().unwrap_or(0),
457 x_offset: s.read::<i16>().unwrap_or(0),
458 y_offset: s.read::<i16>().unwrap_or(0),
459 }
460 }
461
462 /// Returns superscript metrics.
463 #[inline]
464 pub fn superscript_metrics(&self) -> ScriptMetrics {
465 let mut s = Stream::new_at(self.data, Y_SUPERSCRIPT_X_SIZE_OFFSET).unwrap_or_default();
466 ScriptMetrics {
467 x_size: s.read::<i16>().unwrap_or(0),
468 y_size: s.read::<i16>().unwrap_or(0),
469 x_offset: s.read::<i16>().unwrap_or(0),
470 y_offset: s.read::<i16>().unwrap_or(0),
471 }
472 }
473
474 /// Returns strikeout metrics.
475 #[inline]
476 pub fn strikeout_metrics(&self) -> LineMetrics {
477 LineMetrics {
478 thickness: Stream::read_at::<i16>(self.data, Y_STRIKEOUT_SIZE_OFFSET).unwrap_or(0),
479 position: Stream::read_at::<i16>(self.data, Y_STRIKEOUT_POSITION_OFFSET).unwrap_or(0),
480 }
481 }
482
483 /// Returns Unicode ranges.
484 #[inline]
485 pub fn unicode_ranges(&self) -> UnicodeRanges {
486 let mut s = Stream::new_at(self.data, UNICODE_RANGES_OFFSET).unwrap();
487 let n1 = s.read::<u32>().unwrap_or(0) as u128;
488 let n2 = s.read::<u32>().unwrap_or(0) as u128;
489 let n3 = s.read::<u32>().unwrap_or(0) as u128;
490 let n4 = s.read::<u32>().unwrap_or(0) as u128;
491 UnicodeRanges(n4 << 96 | n3 << 64 | n2 << 32 | n1)
492 }
493
494 #[inline]
495 fn fs_selection(&self) -> u16 {
496 Stream::read_at::<u16>(self.data, SELECTION_OFFSET).unwrap_or(0)
497 }
498
499 /// Returns style.
500 pub fn style(&self) -> Style {
501 let flags = SelectionFlags(self.fs_selection());
502 if flags.italic() {
503 Style::Italic
504 } else if self.version >= 4 && flags.oblique() {
505 Style::Oblique
506 } else {
507 Style::Normal
508 }
509 }
510
511 /// Checks if face is bold.
512 ///
513 /// Do not confuse with [`Weight::Bold`].
514 #[inline]
515 pub fn is_bold(&self) -> bool {
516 SelectionFlags(self.fs_selection()).bold()
517 }
518
519 /// Checks if typographic metrics should be used.
520 #[inline]
521 pub fn use_typographic_metrics(&self) -> bool {
522 if self.version < 4 {
523 false
524 } else {
525 SelectionFlags(self.fs_selection()).use_typo_metrics()
526 }
527 }
528
529 /// Returns typographic ascender.
530 #[inline]
531 pub fn typographic_ascender(&self) -> i16 {
532 Stream::read_at::<i16>(self.data, TYPO_ASCENDER_OFFSET).unwrap_or(0)
533 }
534
535 /// Returns typographic descender.
536 #[inline]
537 pub fn typographic_descender(&self) -> i16 {
538 Stream::read_at::<i16>(self.data, TYPO_DESCENDER_OFFSET).unwrap_or(0)
539 }
540
541 /// Returns typographic line gap.
542 #[inline]
543 pub fn typographic_line_gap(&self) -> i16 {
544 Stream::read_at::<i16>(self.data, TYPO_LINE_GAP_OFFSET).unwrap_or(0)
545 }
546
547 /// Returns Windows ascender.
548 #[inline]
549 pub fn windows_ascender(&self) -> i16 {
550 Stream::read_at::<i16>(self.data, WIN_ASCENT).unwrap_or(0)
551 }
552
553 /// Returns Windows descender.
554 #[inline]
555 pub fn windows_descender(&self) -> i16 {
556 // Should be negated.
557 -Stream::read_at::<i16>(self.data, WIN_DESCENT).unwrap_or(0)
558 }
559
560 /// Returns x height.
561 ///
562 /// Returns `None` version is < 2.
563 #[inline]
564 pub fn x_height(&self) -> Option<i16> {
565 if self.version < 2 {
566 None
567 } else {
568 Stream::read_at::<i16>(self.data, X_HEIGHT_OFFSET)
569 }
570 }
571
572 /// Returns capital height.
573 ///
574 /// Returns `None` version is < 2.
575 #[inline]
576 pub fn capital_height(&self) -> Option<i16> {
577 if self.version < 2 {
578 None
579 } else {
580 Stream::read_at::<i16>(self.data, CAP_HEIGHT_OFFSET)
581 }
582 }
583}
584
585impl core::fmt::Debug for Table<'_> {
586 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
587 write!(f, "Table {{ ... }}")
588 }
589}
590