1use super::{
4 types::{Cursor, Fixed},
5 NameTable,
6};
7use crate::{
8 alloc::{String, Vec},
9 write::{VecExt, WriteTable},
10 ParseError, TableTag,
11};
12
13#[derive(Clone, Copy, PartialEq, Eq, Hash)]
15pub struct VariationAxisTag(pub(crate) [u8; 4]);
16
17impl_tag!(VariationAxisTag);
18
19impl VariationAxisTag {
20 pub const WEIGHT: Self = Self(*b"wght");
22 pub const WIDTH: Self = Self(*b"wdth");
24}
25
26#[derive(Debug, Clone)]
28#[cfg_attr(test, derive(PartialEq))]
29pub struct VariationAxis {
30 pub tag: VariationAxisTag,
32 pub min_value: Fixed,
34 pub max_value: Fixed,
36 pub default_value: Fixed,
38 pub name: Option<String>,
40 pub(super) name_id: u16,
41 flags: u16,
42}
43
44impl VariationAxis {
45 fn parse(cursor: &mut Cursor<'_>) -> Result<Self, ParseError> {
46 let tag = VariationAxisTag(cursor.read_byte_array::<4>()?);
47 let min_value = Fixed(cursor.read_i32()?);
48 let default_value = Fixed(cursor.read_i32()?);
49 let max_value = Fixed(cursor.read_i32()?);
50 let flags = cursor.read_u16()?;
51 let name_id = cursor.read_u16()?;
52
53 #[cfg(feature = "tracing")]
54 tracing::debug!(
55 ?tag,
56 ?min_value,
57 ?max_value,
58 ?default_value,
59 flags,
60 name_id,
61 "parsed variation axis"
62 );
63
64 Ok(Self {
65 tag,
66 min_value,
67 max_value,
68 default_value,
69 flags,
70 name: None,
71 name_id,
72 })
73 }
74
75 fn write_to_vec(&self, buffer: &mut Vec<u8>) {
76 buffer.extend_from_slice(&self.tag.0);
77 buffer.write_i32(self.min_value.0);
78 buffer.write_i32(self.default_value.0);
79 buffer.write_i32(self.max_value.0);
80 buffer.write_u16(self.flags);
81 buffer.write_u16(self.name_id);
82 }
83}
84
85#[derive(Debug, Clone)]
86pub(crate) struct FvarTable<'a> {
87 axes: Vec<VariationAxis>,
88 all_bytes: Option<&'a [u8]>,
89}
90
91impl<'a> FvarTable<'a> {
92 const VERSION: u32 = 0x0001_0000;
93 const AXIS_SIZE: u16 = 20;
94
95 #[cfg_attr(
96 feature = "tracing",
97 tracing::instrument(level = "debug", err, skip_all, fields(range = ?cursor.range()))
98 )]
99 pub(super) fn parse(mut cursor: Cursor<'a>) -> Result<Self, ParseError> {
100 let all_bytes = cursor;
101
102 cursor.read_u32_checked(|version| check_exact!(version, Self::VERSION))?;
103 let axes_array_offset = cursor.read_u16()?;
104 cursor.skip(2)?; let axis_count = cursor.read_u16()?;
106 cursor.read_u16_checked(|axis_size| check_exact!(axis_size, Self::AXIS_SIZE))?;
107 #[cfg(feature = "tracing")]
108 tracing::debug!(axis_count, "read basic info");
109
110 let mut axes_cursor = all_bytes;
111 axes_cursor.skip(axes_array_offset.into())?;
112 let axes = (0..axis_count)
113 .map(|_| VariationAxis::parse(&mut axes_cursor))
114 .collect::<Result<Vec<_>, _>>()?;
115 Ok(Self {
116 axes,
117 all_bytes: Some(all_bytes.bytes()),
118 })
119 }
120
121 pub(super) fn axis_name_ids(&self) -> Vec<u16> {
122 self.axes.iter().map(|axis| axis.name_id).collect()
123 }
124
125 pub(super) fn resolve_axe_names(&mut self, name: &NameTable<'_>) {
126 for axis in &mut self.axes {
127 axis.name = name.parsed_names.get(&axis.name_id).cloned();
128 }
129 }
130
131 pub(crate) fn axes(&self) -> &[VariationAxis] {
132 &self.axes
133 }
134
135 pub(crate) fn subset(&mut self) {
136 self.all_bytes = None;
137 }
138}
139
140impl WriteTable for FvarTable<'_> {
141 fn tag(&self) -> TableTag {
142 TableTag::FVAR
143 }
144
145 fn write_to_vec(&self, buffer: &mut Vec<u8>) {
146 const AXES_ARRAY_OFFSET: u16 = 16;
147
148 if let Some(bytes) = self.all_bytes {
149 buffer.extend_from_slice(bytes);
150 return;
151 }
152
153 let initial_offset = buffer.len();
154 buffer.write_u32(Self::VERSION);
155 buffer.write_u16(AXES_ARRAY_OFFSET);
156 buffer.write_u16(2); let axis_count = u16::try_from(self.axes.len()).unwrap();
158 buffer.write_u16(axis_count);
159 buffer.write_u16(Self::AXIS_SIZE);
160 buffer.write_u16(0); let instance_size = axis_count * 4 + 4;
162 buffer.write_u16(instance_size);
163
164 debug_assert_eq!(
165 usize::from(AXES_ARRAY_OFFSET),
166 buffer.len() - initial_offset
167 );
168 for axis in &self.axes {
169 axis.write_to_vec(buffer);
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::{testonly::TestFont, OpenTypeReader, TableTag};
178
179 #[test]
180 fn reading_fvar_table_with_2_axes() {
181 let reader = OpenTypeReader::new(TestFont::ROBOTO.bytes).unwrap();
182 let fvar = reader.table(TableTag::FVAR);
183 let fvar = FvarTable::parse(fvar).unwrap();
184
185 assert_eq!(fvar.axes.len(), 2);
186 assert_eq!(fvar.axes[0].tag, VariationAxisTag::WEIGHT);
187 assert_eq!(fvar.axes[0].min_value, 100_i16.into());
188 assert_eq!(fvar.axes[0].max_value, 900_i16.into());
189 assert_eq!(fvar.axes[0].default_value, 400_i16.into());
190 assert_eq!(fvar.axes[1].tag, VariationAxisTag::WIDTH);
191 assert_eq!(fvar.axes[1].min_value, 75_i16.into());
192 assert_eq!(fvar.axes[1].max_value, 100_i16.into());
193 assert_eq!(fvar.axes[1].default_value, 100_i16.into());
194 }
195
196 #[test]
197 fn reading_fvar_table_with_1_axe() {
198 let reader = OpenTypeReader::new(TestFont::ROBOTO_MONO.bytes).unwrap();
199 let fvar = reader.table(TableTag::FVAR);
200 let fvar = FvarTable::parse(fvar).unwrap();
201
202 assert_eq!(fvar.axes.len(), 1);
203 assert_eq!(fvar.axes[0].tag, VariationAxisTag::WEIGHT);
204 assert_eq!(fvar.axes[0].min_value, 100_i16.into());
205 assert_eq!(fvar.axes[0].max_value, 700_i16.into());
206 assert_eq!(fvar.axes[0].default_value, 400_i16.into());
207 }
208
209 #[test]
210 fn subset_roundtrip() {
211 let reader = OpenTypeReader::new(TestFont::ROBOTO_MONO.bytes).unwrap();
212 let fvar = reader.table(TableTag::FVAR);
213 let mut fvar = FvarTable::parse(fvar).unwrap();
214 fvar.subset();
215
216 let mut buffer = vec![];
217 fvar.write_to_vec(&mut buffer);
218 let subset_fvar = FvarTable::parse(Cursor::new(&buffer)).unwrap();
219 assert_eq!(fvar.axes, subset_fvar.axes);
220 }
221}