1//! A [Font Variations Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) implementation.
3
4use core::num::NonZeroU16;
5
6use crate::parser::{f32_bound, Fixed, FromData, LazyArray16, Offset, Offset16, Stream};
7use 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)]
13pub 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
23impl 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
46impl 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)]
68pub struct Table<'a> {
69 /// A list of variation axes.
70 pub axes: LazyArray16<'a, VariationAxis>,
71}
72
73impl<'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