| 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 | |