write_fonts/tables/variations/
mivs_builder.rs1use std::collections::HashMap;
11
12use indexmap::IndexMap;
13use types::F2Dot14;
14
15use crate::{
16 error::Error,
17 tables::{
18 postscript::Index2,
19 varc::{
20 MultiItemVariationData, MultiItemVariationStore, SparseRegionAxisCoordinates,
21 SparseVariationRegion, SparseVariationRegionList,
22 },
23 variations::{
24 common_builder::{TemporaryDeltaSetId, VarStoreRemapping, NO_VARIATION_INDEX},
25 PackedDeltas,
26 },
27 },
28};
29
30pub type MultiVariationIndexRemapping = VarStoreRemapping<u32>;
31
32#[derive(Clone, Debug, Hash, PartialEq, Eq)]
37pub struct SparseRegion(Vec<(u16, F2Dot14, F2Dot14, F2Dot14)>);
38
39impl SparseRegion {
40 pub fn new(mut axes: Vec<(u16, F2Dot14, F2Dot14, F2Dot14)>) -> Self {
45 axes.sort_by_key(|(idx, _, _, _)| *idx);
47 axes.retain(|(_, _, peak, _)| peak.to_f32() != 0.0);
49 Self(axes)
50 }
51
52 pub fn is_empty(&self) -> bool {
54 self.0.is_empty()
55 }
56
57 fn to_sparse_variation_region(&self) -> SparseVariationRegion {
58 let region_axis_offsets = self
59 .0
60 .iter()
61 .map(|(axis_index, start, peak, end)| {
62 SparseRegionAxisCoordinates::new(*axis_index, *start, *peak, *end)
63 })
64 .collect::<Vec<_>>();
65 SparseVariationRegion::new(region_axis_offsets.len() as u16, region_axis_offsets)
66 }
67}
68
69#[derive(Clone, Debug, Hash, PartialEq, Eq)]
74struct MultiDeltaSet {
75 tuple_len: usize,
77 deltas: Vec<(u16, Vec<i32>)>,
80}
81
82impl MultiDeltaSet {
83 fn new(tuple_len: usize, mut deltas: Vec<(u16, Vec<i32>)>) -> Result<Self, Error> {
84 deltas.sort_by_key(|(idx, _)| *idx);
86 if !deltas.iter().all(|(_, tuple)| tuple.len() == tuple_len) {
88 return Err(Error::InvalidInput(
89 "all delta tuples in MultiDeltaSet must have the same length",
90 ));
91 };
92 deltas.retain(|(_, tuple)| tuple.iter().any(|v| *v != 0));
94 Ok(Self { tuple_len, deltas })
95 }
96
97 fn is_empty(&self) -> bool {
98 self.deltas.is_empty()
99 }
100}
101
102#[derive(Clone, Debug, Default)]
107pub struct MultiItemVariationStoreBuilder {
108 all_regions: HashMap<SparseRegion, usize>,
110 delta_sets: IndexMap<MultiDeltaSet, TemporaryDeltaSetId>,
113 next_id: TemporaryDeltaSetId,
115}
116
117impl MultiItemVariationStoreBuilder {
118 pub fn new() -> Self {
120 Self::default()
121 }
122
123 pub fn is_empty(&self) -> bool {
125 self.delta_sets.is_empty()
126 }
127
128 pub fn add_deltas<T: Into<i32>>(
142 &mut self,
143 deltas: Vec<(SparseRegion, Vec<T>)>,
144 ) -> Result<TemporaryDeltaSetId, Error> {
145 let tuple_len = deltas
147 .iter()
148 .map(|(_, tuple)| tuple.len())
149 .next()
150 .unwrap_or(0);
151
152 let mut indexed_deltas = Vec::with_capacity(deltas.len());
154 for (region, tuple) in deltas {
155 assert_eq!(
156 tuple.len(),
157 tuple_len,
158 "all delta tuples must have the same length"
159 );
160 if region.is_empty() {
161 continue;
162 }
163 let region_idx = self.canonical_index_for_region(region) as u16;
164 let converted_tuple: Vec<i32> = tuple.into_iter().map(|v| v.into()).collect();
165 indexed_deltas.push((region_idx, converted_tuple));
166 }
167
168 let delta_set = MultiDeltaSet::new(tuple_len, indexed_deltas)?;
169
170 if delta_set.is_empty() {
172 return Ok(NO_VARIATION_INDEX);
173 }
174
175 if let Some(&existing_id) = self.delta_sets.get(&delta_set) {
177 return Ok(existing_id);
178 }
179
180 let id = self.next_id;
181 self.next_id += 1;
182 self.delta_sets.insert(delta_set, id);
183 Ok(id)
184 }
185
186 fn canonical_index_for_region(&mut self, region: SparseRegion) -> usize {
187 let next_idx = self.all_regions.len();
188 *self.all_regions.entry(region).or_insert(next_idx)
189 }
190
191 pub fn build(self) -> (MultiItemVariationStore, MultiVariationIndexRemapping) {
196 if self.delta_sets.is_empty() {
197 let region_list = SparseVariationRegionList::new(0, vec![]);
199 let store = MultiItemVariationStore::new(region_list, 0, vec![]);
200 return (store, MultiVariationIndexRemapping::default());
201 }
202
203 let mut var_data_groups: IndexMap<Vec<u16>, Vec<(&MultiDeltaSet, TemporaryDeltaSetId)>> =
205 IndexMap::new();
206
207 for (delta_set, temp_id) in &self.delta_sets {
208 let region_indices: Vec<u16> = delta_set.deltas.iter().map(|(idx, _)| *idx).collect();
209 var_data_groups
210 .entry(region_indices)
211 .or_default()
212 .push((delta_set, *temp_id));
213 }
214
215 let region_list = self.build_region_list();
217
218 let mut key_map = MultiVariationIndexRemapping::default();
220 let mut var_data_tables = Vec::new();
221
222 for (outer, (region_indices, delta_sets)) in var_data_groups.into_iter().enumerate() {
223 for chunk in delta_sets.chunks(0xFFFF) {
225 let subtable =
226 self.build_var_data(®ion_indices, chunk, outer as u16, &mut key_map);
227 var_data_tables.push(subtable);
228 }
229 }
230
231 let store = MultiItemVariationStore::new(
232 region_list,
233 var_data_tables.len() as u16,
234 var_data_tables,
235 );
236
237 (store, key_map)
238 }
239
240 fn build_region_list(&self) -> SparseVariationRegionList {
241 let mut regions: Vec<_> = self.all_regions.iter().collect();
243 regions.sort_by_key(|(_, idx)| *idx);
244
245 let sparse_regions: Vec<SparseVariationRegion> = regions
246 .into_iter()
247 .map(|(region, _)| region.to_sparse_variation_region())
248 .collect();
249
250 SparseVariationRegionList::new(sparse_regions.len() as u16, sparse_regions)
251 }
252
253 fn build_var_data(
254 &self,
255 region_indices: &[u16],
256 delta_sets: &[(&MultiDeltaSet, TemporaryDeltaSetId)],
257 outer: u16,
258 key_map: &mut MultiVariationIndexRemapping,
259 ) -> MultiItemVariationData {
260 let mut items = Vec::new();
262
263 for (inner, (delta_set, temp_id)) in delta_sets.iter().enumerate() {
264 let flattened = self.flatten_deltas(delta_set, region_indices);
266
267 let packed = PackedDeltas::new(flattened);
269 let mut encoded = Vec::new();
270 encode_packed_deltas(&packed, &mut encoded);
272
273 items.push(encoded);
274
275 let var_idx = ((outer as u32) << 16) | (inner as u32);
277 key_map.set(*temp_id, var_idx);
278 }
279
280 let index2 = Index2::from_items(items);
281 let raw_delta_sets = crate::dump_table(&index2).expect("Index2 serialization failed");
282
283 MultiItemVariationData::new(
284 region_indices.len() as u16,
285 region_indices.to_vec(),
286 raw_delta_sets,
287 )
288 }
289
290 fn flatten_deltas(&self, delta_set: &MultiDeltaSet, region_indices: &[u16]) -> Vec<i32> {
295 let tuple_len = delta_set.tuple_len;
296 let mut result = Vec::with_capacity(region_indices.len() * tuple_len);
297
298 let delta_map: HashMap<u16, &Vec<i32>> = delta_set
300 .deltas
301 .iter()
302 .map(|(idx, tuple)| (*idx, tuple))
303 .collect();
304
305 for ®ion_idx in region_indices {
306 if let Some(tuple) = delta_map.get(®ion_idx) {
307 result.extend(tuple.iter().copied());
308 } else {
309 result.extend(std::iter::repeat_n(0, tuple_len));
311 }
312 }
313
314 result
315 }
316}
317
318fn encode_packed_deltas(packed: &PackedDeltas, output: &mut Vec<u8>) {
322 use crate::write::FontWrite;
323
324 let mut writer = crate::write::TableWriter::default();
326 packed.write_into(&mut writer);
327
328 let data = writer.into_data();
330 output.extend(&data.bytes);
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 fn f2dot14(val: f32) -> F2Dot14 {
338 F2Dot14::from_f32(val)
339 }
340
341 #[test]
342 fn empty_builder() {
343 let builder = MultiItemVariationStoreBuilder::new();
344 assert!(builder.is_empty());
345
346 let (store, _remap) = builder.build();
347 assert_eq!(store.variation_data_count, 0);
348 }
349
350 #[test]
351 fn single_delta_set() {
352 let mut builder = MultiItemVariationStoreBuilder::new();
353
354 let region = SparseRegion::new(vec![(0, f2dot14(0.0), f2dot14(1.0), f2dot14(1.0))]);
355
356 let temp_id = builder.add_deltas(vec![(region, vec![10, 20])]).unwrap();
358
359 assert!(temp_id != NO_VARIATION_INDEX);
360 assert!(!builder.is_empty());
361
362 let (store, remap) = builder.build();
363
364 assert_eq!(store.region_list.region_count, 1);
366
367 assert_eq!(store.variation_data_count, 1);
369
370 let var_idx = remap.get(temp_id).unwrap();
372 assert_eq!(var_idx >> 16, 0); assert_eq!(var_idx & 0xFFFF, 0); }
375
376 #[test]
377 fn deduplication() {
378 let mut builder = MultiItemVariationStoreBuilder::new();
379
380 let region = SparseRegion::new(vec![(0, f2dot14(0.0), f2dot14(1.0), f2dot14(1.0))]);
381
382 let id1 = builder
384 .add_deltas(vec![(region.clone(), vec![10, 20])])
385 .unwrap();
386 let id2 = builder.add_deltas(vec![(region, vec![10, 20])]).unwrap();
387
388 assert_eq!(id1, id2);
390 }
391
392 #[test]
393 fn empty_delta_returns_no_variation_index() {
394 let mut builder = MultiItemVariationStoreBuilder::new();
395
396 let id = builder.add_deltas::<i32>(vec![]).unwrap();
398 assert_eq!(id, NO_VARIATION_INDEX);
399
400 let region = SparseRegion::new(vec![(0, f2dot14(0.0), f2dot14(1.0), f2dot14(1.0))]);
402 let id = builder.add_deltas(vec![(region, vec![0, 0])]).unwrap();
403 assert_eq!(id, NO_VARIATION_INDEX);
404 }
405
406 #[test]
407 fn multiple_regions() {
408 let mut builder = MultiItemVariationStoreBuilder::new();
409
410 let region1 = SparseRegion::new(vec![(0, f2dot14(0.0), f2dot14(1.0), f2dot14(1.0))]);
411 let region2 = SparseRegion::new(vec![(1, f2dot14(0.0), f2dot14(1.0), f2dot14(1.0))]);
412
413 let temp_id = builder
414 .add_deltas(vec![(region1, vec![10, 20]), (region2, vec![30, 40])])
415 .unwrap();
416
417 assert!(temp_id != NO_VARIATION_INDEX);
418
419 let (store, _remap) = builder.build();
420
421 assert_eq!(store.region_list.region_count, 2);
423 }
424}