1 | //! A [Font Variations Table]( |
2 | //! https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) implementation. |
3 | |
4 | use core::num::NonZeroU16; |
5 | |
6 | use crate::parser::{f32_bound, Fixed, FromData, LazyArray16, Offset, Offset16, Stream}; |
7 | use crate::{NormalizedCoordinate, Tag}; |
8 | |
9 | /// A [variation axis](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar#variationaxisrecord). |
10 | #[repr (C)] |
11 | #[allow (missing_docs)] |
12 | #[derive (Clone, Copy, PartialEq, Debug)] |
13 | pub struct VariationAxis { |
14 | pub tag: Tag, |
15 | pub min_value: f32, |
16 | pub def_value: f32, |
17 | pub max_value: f32, |
18 | /// An axis name in the `name` table. |
19 | pub name_id: u16, |
20 | pub hidden: bool, |
21 | } |
22 | |
23 | impl FromData for VariationAxis { |
24 | const SIZE: usize = 20; |
25 | |
26 | fn parse(data: &[u8]) -> Option<Self> { |
27 | let mut s: Stream<'_> = Stream::new(data); |
28 | let tag: Tag = s.read::<Tag>()?; |
29 | let min_value: Fixed = s.read::<Fixed>()?; |
30 | let def_value: Fixed = s.read::<Fixed>()?; |
31 | let max_value: Fixed = s.read::<Fixed>()?; |
32 | let flags: u16 = s.read::<u16>()?; |
33 | let name_id: u16 = s.read::<u16>()?; |
34 | |
35 | Some(VariationAxis { |
36 | tag, |
37 | min_value: def_value.0.min(min_value.0), |
38 | def_value: def_value.0, |
39 | max_value: def_value.0.max(max_value.0), |
40 | name_id, |
41 | hidden: (flags >> 3) & 1 == 1, |
42 | }) |
43 | } |
44 | } |
45 | |
46 | impl VariationAxis { |
47 | /// Returns a normalized variation coordinate for this axis. |
48 | pub(crate) fn normalized_value(&self, mut v: f32) -> NormalizedCoordinate { |
49 | // Based on |
50 | // https://docs.microsoft.com/en-us/typography/opentype/spec/avar#overview |
51 | |
52 | v = f32_bound(self.min_value, val:v, self.max_value); |
53 | if v == self.def_value { |
54 | v = 0.0; |
55 | } else if v < self.def_value { |
56 | v = (v - self.def_value) / (self.def_value - self.min_value); |
57 | } else { |
58 | v = (v - self.def_value) / (self.max_value - self.def_value); |
59 | } |
60 | |
61 | NormalizedCoordinate::from(v) |
62 | } |
63 | } |
64 | |
65 | /// A [Font Variations Table]( |
66 | /// https://docs.microsoft.com/en-us/typography/opentype/spec/fvar). |
67 | #[derive (Clone, Copy, Debug)] |
68 | pub struct Table<'a> { |
69 | /// A list of variation axes. |
70 | pub axes: LazyArray16<'a, VariationAxis>, |
71 | } |
72 | |
73 | impl<'a> Table<'a> { |
74 | /// Parses a table from raw data. |
75 | pub fn parse(data: &'a [u8]) -> Option<Self> { |
76 | let mut s: Stream<'_> = Stream::new(data); |
77 | let version: u32 = s.read::<u32>()?; |
78 | if version != 0x00010000 { |
79 | return None; |
80 | } |
81 | |
82 | let axes_array_offset: Offset16 = s.read::<Offset16>()?; |
83 | s.skip::<u16>(); // reserved |
84 | let axis_count: u16 = s.read::<u16>()?; |
85 | |
86 | // 'If axisCount is zero, then the font is not functional as a variable font, |
87 | // and must be treated as a non-variable font; |
88 | // any variation-specific tables or data is ignored.' |
89 | let axis_count: NonZero = NonZeroU16::new(axis_count)?; |
90 | |
91 | let mut s: Stream<'_> = Stream::new_at(data, offset:axes_array_offset.to_usize())?; |
92 | let axes: LazyArray16<'_, VariationAxis> = s.read_array16::<VariationAxis>(count:axis_count.get())?; |
93 | |
94 | Some(Table { axes }) |
95 | } |
96 | } |
97 | |