1include!("../../generated/generated_gvar.rs");
4
5use std::collections::HashMap;
6
7use indexmap::IndexMap;
8
9use crate::{
10 collections::HasLen,
11 tables::variations::{Deltas, TupleVariationStoreInputError},
12 OffsetMarker,
13};
14
15use super::variations::{
16 compute_shared_points, compute_tuple_variation_count, compute_tuple_variation_data_offset,
17 PackedDeltas, PackedPointNumbers, Tuple, TupleVariationCount, TupleVariationHeader,
18};
19
20pub use super::variations::Tent;
21
22pub mod iup;
23
24#[derive(Clone, Debug)]
26pub struct GlyphVariations {
27 gid: GlyphId,
28 variations: Vec<GlyphDeltas>,
29}
30
31pub type GlyphDeltas = Deltas<GlyphDelta>;
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44pub struct GlyphDelta {
45 pub x: i16,
46 pub y: i16,
47 pub required: bool,
49}
50
51impl Gvar {
52 pub fn new(
58 mut variations: Vec<GlyphVariations>,
59 axis_count: u16,
60 ) -> Result<Self, TupleVariationStoreInputError<GlyphId>> {
61 fn compute_shared_peak_tuples(glyphs: &[GlyphVariations]) -> Vec<Tuple> {
62 const MAX_SHARED_TUPLES: usize = 4095;
63 let mut peak_tuple_counts = IndexMap::new();
64 for glyph in glyphs {
65 glyph.count_peak_tuples(&mut peak_tuple_counts);
66 }
67 let mut to_share = peak_tuple_counts
68 .into_iter()
69 .filter(|(_, n)| *n > 1)
70 .collect::<Vec<_>>();
71 to_share.sort_by_key(|(_, n)| std::cmp::Reverse(*n));
75 to_share.truncate(MAX_SHARED_TUPLES);
76 to_share.into_iter().map(|(t, _)| t.to_owned()).collect()
77 }
78
79 for var in &variations {
80 var.validate()?;
81 }
82
83 if let Some(bad_var) = variations
84 .iter()
85 .find(|var| var.axis_count().is_some() && var.axis_count().unwrap() != axis_count)
86 {
87 return Err(TupleVariationStoreInputError::UnexpectedAxisCount {
88 index: bad_var.gid,
89 expected: axis_count,
90 actual: bad_var.axis_count().unwrap(),
91 });
92 }
93
94 let shared = compute_shared_peak_tuples(&variations);
95 let shared_idx_map = shared
96 .iter()
97 .enumerate()
98 .map(|(i, x)| (x, i as u16))
99 .collect();
100 variations.sort_unstable_by_key(|g| g.gid);
101 let glyphs = variations
102 .into_iter()
103 .map(|raw_g| raw_g.build(&shared_idx_map))
104 .collect();
105
106 Ok(Gvar {
107 axis_count,
108 shared_tuples: SharedTuples::new(shared).into(),
109 glyph_variation_data_offsets: glyphs,
110 })
111 }
112
113 fn compute_flags(&self) -> GvarFlags {
114 let max_offset = self
115 .glyph_variation_data_offsets
116 .iter()
117 .fold(0, |acc, val| acc + val.length + val.length % 2);
118
119 if max_offset / 2 <= (u16::MAX as u32) {
120 GvarFlags::default()
121 } else {
122 GvarFlags::LONG_OFFSETS
123 }
124 }
125
126 fn compute_glyph_count(&self) -> u16 {
127 self.glyph_variation_data_offsets.len().try_into().unwrap()
128 }
129
130 fn compute_shared_tuples_offset(&self) -> u32 {
131 const BASE_OFFSET: usize = MajorMinor::RAW_BYTE_LEN
132 + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN
135 + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u32::RAW_BYTE_LEN; let bytes_per_offset = if self.compute_flags() == GvarFlags::LONG_OFFSETS {
139 u32::RAW_BYTE_LEN
140 } else {
141 u16::RAW_BYTE_LEN
142 };
143
144 let offsets_len = (self.glyph_variation_data_offsets.len() + 1) * bytes_per_offset;
145
146 (BASE_OFFSET + offsets_len).try_into().unwrap()
147 }
148
149 fn compute_data_array_offset(&self) -> u32 {
150 let shared_tuples_len: u32 =
151 (array_len(&self.shared_tuples) * self.axis_count as usize * 2)
152 .try_into()
153 .unwrap();
154 self.compute_shared_tuples_offset() + shared_tuples_len
155 }
156
157 fn compile_variation_data(&self) -> GlyphDataWriter<'_> {
158 GlyphDataWriter {
159 long_offsets: self.compute_flags() == GvarFlags::LONG_OFFSETS,
160 shared_tuples: &self.shared_tuples,
161 data: &self.glyph_variation_data_offsets,
162 }
163 }
164}
165
166impl GlyphVariations {
167 pub fn new(gid: GlyphId, variations: Vec<GlyphDeltas>) -> Self {
169 Self { gid, variations }
170 }
171
172 fn validate(&self) -> Result<(), TupleVariationStoreInputError<GlyphId>> {
174 let (axis_count, delta_len) = self
175 .variations
176 .first()
177 .map(|var| (var.peak_tuple.len(), var.deltas.len()))
178 .unwrap_or_default();
179 for var in &self.variations {
180 if var.peak_tuple.len() != axis_count {
181 return Err(TupleVariationStoreInputError::InconsistentAxisCount(
182 self.gid,
183 ));
184 }
185 if let Some((start, end)) = var.intermediate_region.as_ref() {
186 if start.len() != axis_count || end.len() != axis_count {
187 return Err(TupleVariationStoreInputError::InconsistentTupleLengths(
188 self.gid,
189 ));
190 }
191 }
192 if var.deltas.len() != delta_len {
193 return Err(TupleVariationStoreInputError::InconsistentDeltaLength(
194 self.gid,
195 ));
196 }
197 }
198 Ok(())
199 }
200
201 pub fn axis_count(&self) -> Option<u16> {
203 self.variations.first().map(|var| var.peak_tuple.len())
204 }
205
206 fn count_peak_tuples<'a>(&'a self, counter: &mut IndexMap<&'a Tuple, usize>) {
207 for tuple in &self.variations {
208 *counter.entry(&tuple.peak_tuple).or_default() += 1;
209 }
210 }
211
212 fn build(self, shared_tuple_map: &HashMap<&Tuple, u16>) -> GlyphVariationData {
213 let shared_points = compute_shared_points(&self.variations);
214
215 let (tuple_headers, tuple_data): (Vec<_>, Vec<_>) = self
216 .variations
217 .into_iter()
218 .map(|tup| tup.build(shared_tuple_map, shared_points.as_ref()))
219 .unzip();
220
221 let mut temp = GlyphVariationData {
222 tuple_variation_headers: tuple_headers,
223 shared_point_numbers: shared_points,
224 per_tuple_data: tuple_data,
225 length: 0,
226 };
227
228 temp.length = temp.compute_size();
229 temp
230 }
231}
232
233impl GlyphDelta {
234 pub fn new(x: i16, y: i16, required: bool) -> Self {
236 Self { x, y, required }
237 }
238
239 pub fn required(x: i16, y: i16) -> Self {
241 Self::new(x, y, true)
242 }
243
244 pub fn optional(x: i16, y: i16) -> Self {
246 Self::new(x, y, false)
247 }
248}
249
250impl GlyphDeltas {
251 pub fn new(tents: Vec<Tent>, deltas: Vec<GlyphDelta>) -> Self {
256 let peak_tuple = Tuple::new(tents.iter().map(Tent::peak).collect());
257
258 let intermediate_region = if tents.iter().any(Tent::requires_intermediate) {
262 Some(tents.iter().map(Tent::bounds).unzip())
263 } else {
264 None
265 };
266
267 let best_point_packing = Self::pick_best_point_number_repr(&deltas);
272 GlyphDeltas {
273 peak_tuple,
274 intermediate_region,
275 deltas,
276 best_point_packing,
277 }
278 }
279
280 fn pick_best_point_number_repr(deltas: &[GlyphDelta]) -> PackedPointNumbers {
287 if deltas.iter().all(|d| d.required) {
288 return PackedPointNumbers::All;
289 }
290
291 let dense = Self::build_non_sparse_data(deltas);
292 let sparse = Self::build_sparse_data(deltas);
293 let dense_size = dense.compute_size();
294 let sparse_size = sparse.compute_size();
295 log::trace!("dense {dense_size}, sparse {sparse_size}");
296 if sparse_size < dense_size {
297 sparse.private_point_numbers.unwrap()
298 } else {
299 PackedPointNumbers::All
300 }
301 }
302
303 fn build_non_sparse_data(deltas: &[GlyphDelta]) -> GlyphTupleVariationData {
304 let (x_deltas, y_deltas) = deltas
305 .iter()
306 .map(|delta| (delta.x as i32, delta.y as i32))
307 .unzip();
308 GlyphTupleVariationData {
309 private_point_numbers: Some(PackedPointNumbers::All),
310 x_deltas: PackedDeltas::new(x_deltas),
311 y_deltas: PackedDeltas::new(y_deltas),
312 }
313 }
314
315 fn build_sparse_data(deltas: &[GlyphDelta]) -> GlyphTupleVariationData {
316 let (x_deltas, y_deltas) = deltas
317 .iter()
318 .filter_map(|delta| delta.required.then_some((delta.x as i32, delta.y as i32)))
319 .unzip();
320 let point_numbers = deltas
321 .iter()
322 .enumerate()
323 .filter_map(|(i, delta)| delta.required.then_some(i as u16))
324 .collect();
325 GlyphTupleVariationData {
326 private_point_numbers: Some(PackedPointNumbers::Some(point_numbers)),
327 x_deltas: PackedDeltas::new(x_deltas),
328 y_deltas: PackedDeltas::new(y_deltas),
329 }
330 }
331
332 fn build(
336 self,
337 shared_tuple_map: &HashMap<&Tuple, u16>,
338 shared_points: Option<&PackedPointNumbers>,
339 ) -> (TupleVariationHeader, GlyphTupleVariationData) {
340 let GlyphDeltas {
341 peak_tuple,
342 intermediate_region,
343 deltas,
344 best_point_packing: point_numbers,
345 } = self;
346
347 let (idx, peak_tuple) = match shared_tuple_map.get(&peak_tuple) {
348 Some(idx) => (Some(*idx), None),
349 None => (None, Some(peak_tuple)),
350 };
351
352 let has_private_points = Some(&point_numbers) != shared_points;
353 let (x_deltas, y_deltas) = match &point_numbers {
354 PackedPointNumbers::All => deltas.iter().map(|d| (d.x as i32, d.y as i32)).unzip(),
355 PackedPointNumbers::Some(pts) => pts
356 .iter()
357 .map(|idx| {
358 let delta = deltas[*idx as usize];
359 (delta.x as i32, delta.y as i32)
360 })
361 .unzip(),
362 };
363
364 let data = GlyphTupleVariationData {
365 private_point_numbers: has_private_points.then_some(point_numbers),
366 x_deltas: PackedDeltas::new(x_deltas),
367 y_deltas: PackedDeltas::new(y_deltas),
368 };
369 let data_size = data.compute_size();
370
371 let header = TupleVariationHeader::new(
372 data_size,
373 idx,
374 peak_tuple,
375 intermediate_region,
376 has_private_points,
377 );
378
379 (header, data)
380 }
381}
382
383#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
385#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
386pub struct GlyphVariationData {
387 tuple_variation_headers: Vec<TupleVariationHeader>,
388 shared_point_numbers: Option<PackedPointNumbers>,
390 per_tuple_data: Vec<GlyphTupleVariationData>,
391 length: u32,
396}
397
398#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
400#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
401struct GlyphTupleVariationData {
402 private_point_numbers: Option<PackedPointNumbers>,
404 x_deltas: PackedDeltas,
405 y_deltas: PackedDeltas,
406}
407
408impl GlyphTupleVariationData {
409 fn compute_size(&self) -> u16 {
410 self.private_point_numbers
411 .as_ref()
412 .map(PackedPointNumbers::compute_size)
413 .unwrap_or_default()
414 .checked_add(self.x_deltas.compute_size())
415 .unwrap()
416 .checked_add(self.y_deltas.compute_size())
417 .unwrap()
418 }
419}
420
421impl FontWrite for GlyphTupleVariationData {
422 fn write_into(&self, writer: &mut TableWriter) {
423 self.private_point_numbers.write_into(writer);
424 self.x_deltas.write_into(writer);
425 self.y_deltas.write_into(writer);
426 }
427}
428
429struct GlyphDataWriter<'a> {
430 long_offsets: bool,
431 shared_tuples: &'a SharedTuples,
432 data: &'a [GlyphVariationData],
433}
434
435impl FontWrite for GlyphDataWriter<'_> {
436 fn write_into(&self, writer: &mut TableWriter) {
437 if self.long_offsets {
438 let mut last = 0u32;
439 last.write_into(writer);
440
441 for glyph in self.data {
443 last += glyph.compute_size();
444 last.write_into(writer);
445 }
446 } else {
447 let mut last = 0u16;
450 last.write_into(writer);
451
452 for glyph in self.data {
454 let size = glyph.compute_size();
455 let short_size = (size / 2) + size % 2;
457 last += short_size as u16;
458 last.write_into(writer);
459 }
460 }
461 self.shared_tuples.write_into(writer);
465 for glyph in self.data {
467 if !glyph.is_empty() {
468 glyph.write_into(writer);
469 if !self.long_offsets {
470 writer.pad_to_2byte_aligned();
471 }
472 }
473 }
474 }
475}
476
477impl GlyphVariationData {
478 fn compute_tuple_variation_count(&self) -> TupleVariationCount {
479 compute_tuple_variation_count(
480 self.tuple_variation_headers.len(),
481 self.shared_point_numbers.is_some(),
482 )
483 }
484
485 fn is_empty(&self) -> bool {
486 self.tuple_variation_headers.is_empty()
487 }
488
489 fn compute_data_offset(&self) -> u16 {
490 compute_tuple_variation_data_offset(
491 &self.tuple_variation_headers,
492 TupleVariationCount::RAW_BYTE_LEN + u16::RAW_BYTE_LEN,
493 )
494 }
495
496 fn compute_size(&self) -> u32 {
497 if self.is_empty() {
498 return 0;
499 }
500
501 let data_start = self.compute_data_offset() as u32;
502 let shared_point_len = self
503 .shared_point_numbers
504 .as_ref()
505 .map(|pts| pts.compute_size())
506 .unwrap_or_default() as u32;
507 let tuple_data_len = self
508 .per_tuple_data
509 .iter()
510 .fold(0u32, |acc, tup| acc + tup.compute_size() as u32);
511 data_start + shared_point_len + tuple_data_len
512 }
513}
514
515impl Extend<F2Dot14> for Tuple {
516 fn extend<T: IntoIterator<Item = F2Dot14>>(&mut self, iter: T) {
517 self.values.extend(iter);
518 }
519}
520
521impl Validate for GlyphVariationData {
522 fn validate_impl(&self, ctx: &mut ValidationCtx) {
523 const MAX_TUPLE_VARIATIONS: usize = 4095;
524 if !(0..=MAX_TUPLE_VARIATIONS).contains(&self.tuple_variation_headers.len()) {
525 ctx.in_field("tuple_variation_headers", |ctx| {
526 ctx.report("expected 0-4095 tuple variation tables")
527 })
528 }
529 }
530}
531
532impl FontWrite for GlyphVariationData {
533 fn write_into(&self, writer: &mut TableWriter) {
534 self.compute_tuple_variation_count().write_into(writer);
535 self.compute_data_offset().write_into(writer);
536 self.tuple_variation_headers.write_into(writer);
537 self.shared_point_numbers.write_into(writer);
538 self.per_tuple_data.write_into(writer);
539 }
540}
541
542impl HasLen for SharedTuples {
543 fn len(&self) -> usize {
544 self.tuples.len()
545 }
546}
547
548impl FontWrite for TupleVariationCount {
549 fn write_into(&self, writer: &mut TableWriter) {
550 self.bits().write_into(writer)
551 }
552}
553#[cfg(test)]
554mod tests {
555 use super::*;
556
557 fn peaks(peaks: Vec<F2Dot14>) -> Vec<Tent> {
559 peaks
560 .into_iter()
561 .map(|peak| Tent::new(peak, None))
562 .collect()
563 }
564
565 #[test]
566 fn gvar_smoke_test() {
567 let _ = env_logger::builder().is_test(true).try_init();
568 let table = Gvar::new(
569 vec![
570 GlyphVariations::new(GlyphId::new(0), vec![]),
571 GlyphVariations::new(
572 GlyphId::new(1),
573 vec![GlyphDeltas::new(
574 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
575 vec![
576 GlyphDelta::required(30, 31),
577 GlyphDelta::required(40, 41),
578 GlyphDelta::required(-50, -49),
579 GlyphDelta::required(101, 102),
580 GlyphDelta::required(10, 11),
581 ],
582 )],
583 ),
584 GlyphVariations::new(
585 GlyphId::new(2),
586 vec![
587 GlyphDeltas::new(
588 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
589 vec![
590 GlyphDelta::required(11, -20),
591 GlyphDelta::required(69, -41),
592 GlyphDelta::required(-69, 49),
593 GlyphDelta::required(168, 101),
594 GlyphDelta::required(1, 2),
595 ],
596 ),
597 GlyphDeltas::new(
598 peaks(vec![F2Dot14::from_f32(0.8), F2Dot14::from_f32(1.0)]),
599 vec![
600 GlyphDelta::required(3, -200),
601 GlyphDelta::required(4, -500),
602 GlyphDelta::required(5, -800),
603 GlyphDelta::required(6, -1200),
604 GlyphDelta::required(7, -1500),
605 ],
606 ),
607 ],
608 ),
609 ],
610 2,
611 )
612 .unwrap();
613
614 let g2 = &table.glyph_variation_data_offsets[1];
615 let computed = g2.compute_size();
616 let actual = crate::dump_table(g2).unwrap().len();
617 assert_eq!(computed as usize, actual);
618
619 let bytes = crate::dump_table(&table).unwrap();
620 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
621 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
622 assert_eq!(gvar.shared_tuple_count(), 1);
623 assert_eq!(gvar.glyph_count(), 3);
624 assert_eq!(gvar.shared_tuples_offset(), Offset32::new(28));
626 assert_eq!(gvar.glyph_variation_data_array_offset(), 32);
627
628 let g1 = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
629 let g1tup = g1.tuples().collect::<Vec<_>>();
630 assert_eq!(g1tup.len(), 1);
631
632 let (x, y): (Vec<_>, Vec<_>) = g1tup[0].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
633 assert_eq!(x, vec![30, 40, -50, 101, 10]);
634 assert_eq!(y, vec![31, 41, -49, 102, 11]);
635
636 let g2 = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
637 let g2tup = g2.tuples().collect::<Vec<_>>();
638 assert_eq!(g2tup.len(), 2);
639
640 let (x, y): (Vec<_>, Vec<_>) = g2tup[0].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
641 assert_eq!(x, vec![11, 69, -69, 168, 1]);
642 assert_eq!(y, vec![-20, -41, 49, 101, 2]);
643
644 let (x, y): (Vec<_>, Vec<_>) = g2tup[1].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
645
646 assert_eq!(x, vec![3, 4, 5, 6, 7]);
647 assert_eq!(y, vec![-200, -500, -800, -1200, -1500]);
648 }
649
650 #[test]
651 fn use_iup_when_appropriate() {
652 let _ = env_logger::builder().is_test(true).try_init();
654 let gid = GlyphId::new(0);
655 let table = Gvar::new(
656 vec![GlyphVariations::new(
657 gid,
658 vec![GlyphDeltas::new(
659 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
660 vec![
661 GlyphDelta::required(30, 31),
662 GlyphDelta::optional(30, 31),
663 GlyphDelta::optional(30, 31),
664 GlyphDelta::required(101, 102),
665 GlyphDelta::required(10, 11),
666 GlyphDelta::optional(10, 11),
667 ],
668 )],
669 )],
670 2,
671 )
672 .unwrap();
673
674 let bytes = crate::dump_table(&table).unwrap();
675 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
676 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
677 assert_eq!(gvar.shared_tuple_count(), 0);
678 assert_eq!(gvar.glyph_count(), 1);
679
680 let g1 = gvar.glyph_variation_data(gid).unwrap().unwrap();
681 let g1tup = g1.tuples().collect::<Vec<_>>();
682 assert_eq!(g1tup.len(), 1);
683 let tuple_variation = &g1tup[0];
684
685 assert!(!tuple_variation.has_deltas_for_all_points());
686 assert_eq!(
687 vec![0, 3, 4],
688 tuple_variation.point_numbers().collect::<Vec<_>>()
689 );
690
691 let points: Vec<_> = tuple_variation
692 .deltas()
693 .map(|d| (d.x_delta, d.y_delta))
694 .collect();
695 assert_eq!(points, vec![(30, 31), (101, 102), (10, 11)]);
696 }
697
698 #[test]
699 fn disregard_iup_when_appropriate() {
700 let points = vec![
703 GlyphDelta::required(1, 2),
704 GlyphDelta::required(3, 4),
705 GlyphDelta::required(5, 6),
706 GlyphDelta::optional(5, 6),
707 GlyphDelta::required(7, 8),
708 ];
709 let gid = GlyphId::new(0);
710 let table = Gvar::new(
711 vec![GlyphVariations::new(
712 gid,
713 vec![GlyphDeltas::new(
714 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
715 points,
716 )],
717 )],
718 2,
719 )
720 .unwrap();
721 let bytes = crate::dump_table(&table).unwrap();
722 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
723 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
724 assert_eq!(gvar.shared_tuple_count(), 0);
725 assert_eq!(gvar.glyph_count(), 1);
726
727 let g1 = gvar.glyph_variation_data(gid).unwrap().unwrap();
728 let g1tup = g1.tuples().collect::<Vec<_>>();
729 assert_eq!(g1tup.len(), 1);
730 let tuple_variation = &g1tup[0];
731
732 assert!(tuple_variation.has_deltas_for_all_points());
733 let points: Vec<_> = tuple_variation
734 .deltas()
735 .map(|d| (d.x_delta, d.y_delta))
736 .collect();
737 assert_eq!(points, vec![(1, 2), (3, 4), (5, 6), (5, 6), (7, 8)]);
738 }
739
740 #[test]
741 fn share_points() {
742 let _ = env_logger::builder().is_test(true).try_init();
743 let variations = GlyphVariations::new(
744 GlyphId::new(0),
745 vec![
746 GlyphDeltas::new(
747 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
748 vec![
749 GlyphDelta::required(1, 2),
750 GlyphDelta::optional(3, 4),
751 GlyphDelta::required(5, 6),
752 GlyphDelta::optional(5, 6),
753 GlyphDelta::required(7, 8),
754 GlyphDelta::optional(7, 8),
755 ],
756 ),
757 GlyphDeltas::new(
758 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
759 vec![
760 GlyphDelta::required(10, 20),
761 GlyphDelta::optional(30, 40),
762 GlyphDelta::required(50, 60),
763 GlyphDelta::optional(50, 60),
764 GlyphDelta::required(70, 80),
765 GlyphDelta::optional(70, 80),
766 ],
767 ),
768 ],
769 );
770
771 assert_eq!(
772 compute_shared_points(&variations.variations),
773 Some(PackedPointNumbers::Some(vec![0, 2, 4]))
774 )
775 }
776
777 #[test]
778 fn share_all_points() {
779 let variations = GlyphVariations::new(
780 GlyphId::new(0),
781 vec![
782 GlyphDeltas::new(
783 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
784 vec![
785 GlyphDelta::required(1, 2),
786 GlyphDelta::required(3, 4),
787 GlyphDelta::required(5, 6),
788 ],
789 ),
790 GlyphDeltas::new(
791 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
792 vec![
793 GlyphDelta::required(2, 4),
794 GlyphDelta::required(6, 8),
795 GlyphDelta::required(7, 9),
796 ],
797 ),
798 ],
799 );
800
801 let shared_tups = HashMap::new();
802 let built = variations.build(&shared_tups);
803 assert_eq!(built.shared_point_numbers, Some(PackedPointNumbers::All))
804 }
805
806 #[test]
809 fn dont_share_unique_points() {
810 let variations = GlyphVariations::new(
811 GlyphId::new(0),
812 vec![
813 GlyphDeltas::new(
814 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
815 vec![
816 GlyphDelta::required(1, 2),
817 GlyphDelta::optional(3, 4),
818 GlyphDelta::required(5, 6),
819 GlyphDelta::optional(5, 6),
820 GlyphDelta::required(7, 8),
821 GlyphDelta::optional(7, 8),
822 ],
823 ),
824 GlyphDeltas::new(
825 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
826 vec![
827 GlyphDelta::required(10, 20),
828 GlyphDelta::required(35, 40),
829 GlyphDelta::required(50, 60),
830 GlyphDelta::optional(50, 60),
831 GlyphDelta::required(70, 80),
832 GlyphDelta::optional(70, 80),
833 ],
834 ),
835 GlyphDeltas::new(
836 peaks(vec![F2Dot14::from_f32(0.5), F2Dot14::from_f32(1.0)]),
837 vec![
838 GlyphDelta::required(1, 2),
839 GlyphDelta::optional(3, 4),
840 GlyphDelta::required(5, 6),
841 GlyphDelta::optional(5, 6),
842 GlyphDelta::optional(7, 8),
843 GlyphDelta::optional(7, 8),
844 ],
845 ),
846 ],
847 );
848
849 let shared_tups = HashMap::new();
850 let built = variations.build(&shared_tups);
851 assert!(built.shared_point_numbers.is_none());
852 }
853
854 #[test]
856 #[allow(non_snake_case)]
857 fn oswald_Lcaron() {
858 let _ = env_logger::builder().is_test(true).try_init();
859 let variations = GlyphVariations::new(
863 GlyphId::new(0),
864 vec![
865 GlyphDeltas::new(
866 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
867 vec![
868 GlyphDelta::optional(0, 0),
869 GlyphDelta::required(35, 0),
870 GlyphDelta::optional(0, 0),
871 GlyphDelta::required(-24, 0),
872 GlyphDelta::optional(0, 0),
873 GlyphDelta::optional(0, 0),
874 ],
875 ),
876 GlyphDeltas::new(
877 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
878 vec![
879 GlyphDelta::optional(0, 0),
880 GlyphDelta::required(26, 15),
881 GlyphDelta::optional(0, 0),
882 GlyphDelta::required(46, 0),
883 GlyphDelta::optional(0, 0),
884 GlyphDelta::optional(0, 0),
885 ],
886 ),
887 ],
888 );
889 assert!(compute_shared_points(&variations.variations).is_none());
890 let tups = HashMap::new();
891 let built = variations.build(&tups);
892 assert_eq!(
893 built.per_tuple_data[0].private_point_numbers,
894 Some(PackedPointNumbers::All)
895 );
896 assert_eq!(
897 built.per_tuple_data[1].private_point_numbers,
898 Some(PackedPointNumbers::Some(vec![1, 3]))
899 );
900 }
901
902 #[test]
903 fn compute_shared_points_is_deterministic() {
904 let _ = env_logger::builder().is_test(true).try_init();
912 let variations = GlyphVariations::new(
913 GlyphId::NOTDEF,
914 vec![
915 GlyphDeltas::new(
916 peaks(vec![
917 F2Dot14::from_f32(-1.0),
918 F2Dot14::from_f32(0.0),
919 F2Dot14::from_f32(0.0),
920 ]),
921 vec![
922 GlyphDelta::optional(0, 0),
923 GlyphDelta::required(-17, -4),
924 GlyphDelta::optional(0, 0),
925 GlyphDelta::required(-28, 0),
926 GlyphDelta::optional(0, 0),
927 GlyphDelta::optional(0, 0),
928 ],
929 ),
930 GlyphDeltas::new(
931 peaks(vec![
932 F2Dot14::from_f32(0.0),
933 F2Dot14::from_f32(1.0),
934 F2Dot14::from_f32(0.0),
935 ]),
936 vec![
937 GlyphDelta::optional(0, 0),
938 GlyphDelta::required(0, -10),
939 GlyphDelta::optional(0, 0),
940 GlyphDelta::required(34, 0),
941 GlyphDelta::optional(0, 0),
942 GlyphDelta::optional(0, 0),
943 ],
944 ),
945 GlyphDeltas::new(
946 peaks(vec![
947 F2Dot14::from_f32(0.0),
948 F2Dot14::from_f32(0.0),
949 F2Dot14::from_f32(-1.0),
950 ]),
951 vec![
952 GlyphDelta::required(0, 0),
953 GlyphDelta::optional(0, 0),
954 GlyphDelta::optional(0, 0),
955 GlyphDelta::optional(0, 0),
956 GlyphDelta::optional(0, 0),
957 GlyphDelta::optional(0, 0),
958 ],
959 ),
960 GlyphDeltas::new(
961 peaks(vec![
962 F2Dot14::from_f32(0.0),
963 F2Dot14::from_f32(0.0),
964 F2Dot14::from_f32(1.0),
965 ]),
966 vec![
967 GlyphDelta::required(0, 0),
968 GlyphDelta::optional(0, 0),
969 GlyphDelta::optional(0, 0),
970 GlyphDelta::optional(0, 0),
971 GlyphDelta::optional(0, 0),
972 GlyphDelta::optional(0, 0),
973 ],
974 ),
975 GlyphDeltas::new(
976 peaks(vec![
977 F2Dot14::from_f32(-1.0),
978 F2Dot14::from_f32(1.0),
979 F2Dot14::from_f32(0.0),
980 ]),
981 vec![
982 GlyphDelta::optional(0, 0),
983 GlyphDelta::required(-1, 10),
984 GlyphDelta::optional(0, 0),
985 GlyphDelta::required(-9, 0),
986 GlyphDelta::optional(0, 0),
987 GlyphDelta::optional(0, 0),
988 ],
989 ),
990 GlyphDeltas::new(
991 peaks(vec![
992 F2Dot14::from_f32(-1.0),
993 F2Dot14::from_f32(0.0),
994 F2Dot14::from_f32(-1.0),
995 ]),
996 vec![
997 GlyphDelta::required(0, 0),
998 GlyphDelta::optional(0, 0),
999 GlyphDelta::optional(0, 0),
1000 GlyphDelta::optional(0, 0),
1001 GlyphDelta::optional(0, 0),
1002 GlyphDelta::optional(0, 0),
1003 ],
1004 ),
1005 GlyphDeltas::new(
1006 peaks(vec![
1007 F2Dot14::from_f32(-1.0),
1008 F2Dot14::from_f32(0.0),
1009 F2Dot14::from_f32(1.0),
1010 ]),
1011 vec![
1012 GlyphDelta::required(0, 0),
1013 GlyphDelta::optional(0, 0),
1014 GlyphDelta::optional(0, 0),
1015 GlyphDelta::optional(0, 0),
1016 GlyphDelta::optional(0, 0),
1017 GlyphDelta::optional(0, 0),
1018 ],
1019 ),
1020 ],
1021 );
1022
1023 assert_eq!(
1024 compute_shared_points(&variations.variations),
1025 Some(PackedPointNumbers::Some(vec![1, 3]))
1029 );
1030 }
1031
1032 fn make_31_bytes_of_variation_data() -> Vec<GlyphDeltas> {
1035 vec![
1036 GlyphDeltas::new(
1037 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
1038 vec![
1039 GlyphDelta::optional(0, 0),
1040 GlyphDelta::required(35, 0),
1041 GlyphDelta::optional(0, 0),
1042 GlyphDelta::required(-24, 0),
1043 GlyphDelta::optional(0, 0),
1044 GlyphDelta::optional(0, 0),
1045 ],
1046 ),
1047 GlyphDeltas::new(
1048 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
1049 vec![
1050 GlyphDelta::optional(0, 0),
1051 GlyphDelta::required(26, 15),
1052 GlyphDelta::optional(0, 0),
1053 GlyphDelta::required(46, 0),
1054 GlyphDelta::optional(0, 0),
1055 GlyphDelta::required(1, 0),
1056 ],
1057 ),
1058 ]
1059 }
1060
1061 #[test]
1063 fn who_tests_the_testers() {
1064 let variations = GlyphVariations::new(GlyphId::NOTDEF, make_31_bytes_of_variation_data());
1065 let mut tupl_map = HashMap::new();
1066
1067 assert_eq!(variations.clone().build(&tupl_map).length, 39);
1069
1070 tupl_map.insert(&variations.variations[0].peak_tuple, 1);
1072 tupl_map.insert(&variations.variations[1].peak_tuple, 2);
1073
1074 let built = variations.clone().build(&tupl_map);
1075 assert_eq!(built.length, 31);
1077 }
1078
1079 fn assert_test_offset_packing(n_glyphs: u16, should_be_short: bool) {
1080 let (offset_len, data_len, expected_flags) = if should_be_short {
1081 (u16::RAW_BYTE_LEN, 32, GvarFlags::empty())
1083 } else {
1084 (u32::RAW_BYTE_LEN, 31, GvarFlags::LONG_OFFSETS)
1085 };
1086
1087 let test_data = make_31_bytes_of_variation_data();
1088 let a_small_number_of_variations = (0..n_glyphs)
1089 .map(|i| GlyphVariations::new(GlyphId::from(i), test_data.clone()))
1090 .collect();
1091
1092 let gvar = Gvar::new(a_small_number_of_variations, 2).unwrap();
1093 assert_eq!(gvar.compute_flags(), expected_flags);
1094
1095 let writer = gvar.compile_variation_data();
1096 let mut sink = TableWriter::default();
1097 writer.write_into(&mut sink);
1098
1099 let bytes = sink.into_data().bytes;
1100 let expected_len = (n_glyphs + 1) as usize * offset_len + 8 + data_len * n_glyphs as usize; assert_eq!(bytes.len(), expected_len);
1104
1105 let dumped = crate::dump_table(&gvar).unwrap();
1106 let loaded = read_fonts::tables::gvar::Gvar::read(FontData::new(&dumped)).unwrap();
1107
1108 assert_eq!(loaded.glyph_count(), n_glyphs);
1109 assert_eq!(loaded.flags(), expected_flags);
1110 assert!(loaded
1111 .glyph_variation_data_offsets()
1112 .iter()
1113 .map(|off| off.unwrap().get())
1114 .enumerate()
1115 .all(|(i, off)| off as usize == i * data_len));
1116 }
1117
1118 #[test]
1119 fn prefer_short_offsets() {
1120 let _ = env_logger::builder().is_test(true).try_init();
1121 assert_test_offset_packing(5, true);
1122 }
1123
1124 #[test]
1125 fn use_long_offsets_when_necessary() {
1126 let _ = env_logger::builder().is_test(true).try_init();
1129 assert_test_offset_packing(4095, true);
1130 assert_test_offset_packing(4096, false);
1131 assert_test_offset_packing(4097, false);
1132 }
1133
1134 #[test]
1135 fn shared_tuples_stable_order() {
1136 let mut variations = Vec::new();
1139 for i in 0..2 {
1140 variations.push(GlyphVariations::new(
1141 GlyphId::new(i),
1142 vec![
1143 GlyphDeltas::new(
1144 peaks(vec![F2Dot14::from_f32(1.0)]),
1145 vec![GlyphDelta::required(10, 20)],
1146 ),
1147 GlyphDeltas::new(
1148 peaks(vec![F2Dot14::from_f32(-1.0)]),
1149 vec![GlyphDelta::required(-10, -20)],
1150 ),
1151 ],
1152 ))
1153 }
1154 for _ in 0..10 {
1155 let table = Gvar::new(variations.clone(), 1).unwrap();
1156 let bytes = crate::dump_table(&table).unwrap();
1157 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
1158
1159 assert_eq!(gvar.shared_tuple_count(), 2);
1160 assert_eq!(
1161 gvar.shared_tuples()
1162 .unwrap()
1163 .tuples()
1164 .iter()
1165 .map(|t| t.unwrap().values.to_vec())
1166 .collect::<Vec<_>>(),
1167 vec![vec![F2Dot14::from_f32(1.0)], vec![F2Dot14::from_f32(-1.0)]]
1168 );
1169 }
1170 }
1171
1172 #[test]
1173 fn unexpected_axis_count() {
1174 let variations = GlyphVariations::new(
1175 GlyphId::NOTDEF,
1176 vec![
1177 GlyphDeltas::new(
1178 peaks(vec![F2Dot14::from_f32(1.0)]),
1179 vec![GlyphDelta::required(1, 2)],
1180 ),
1181 GlyphDeltas::new(
1182 peaks(vec![F2Dot14::from_f32(1.0)]),
1183 vec![GlyphDelta::required(1, 2)],
1184 ),
1185 ],
1186 );
1187 let gvar = Gvar::new(vec![variations], 2);
1188 assert!(matches!(
1189 gvar,
1190 Err(TupleVariationStoreInputError::UnexpectedAxisCount {
1191 index: GlyphId::NOTDEF,
1192 expected: 2,
1193 actual: 1
1194 })
1195 ));
1196 }
1197
1198 #[test]
1199 fn empty_gvar_has_expected_axis_count() {
1200 let variations = GlyphVariations::new(GlyphId::NOTDEF, vec![]);
1201 let gvar = Gvar::new(vec![variations], 2).unwrap();
1202 assert_eq!(gvar.axis_count, 2);
1203 }
1204
1205 #[test]
1206 fn intermediates_only_when_explicit_needed() {
1209 let any_points = vec![]; let deltas = GlyphDeltas::new(
1213 vec![Tent::new(F2Dot14::from_f32(0.5), None)],
1214 any_points.clone(),
1215 );
1216 assert_eq!(deltas.intermediate_region, None);
1217
1218 let deltas = GlyphDeltas::new(
1221 vec![Tent::new(
1222 F2Dot14::from_f32(0.5),
1223 Some(Tent::implied_intermediates_for_peak(F2Dot14::from_f32(0.5))),
1224 )],
1225 any_points.clone(),
1226 );
1227 assert_eq!(deltas.intermediate_region, None);
1228
1229 let deltas = GlyphDeltas::new(
1232 vec![Tent::new(
1233 F2Dot14::from_f32(0.5),
1234 Some((F2Dot14::from_f32(-0.3), F2Dot14::from_f32(0.4))),
1235 )],
1236 any_points.clone(),
1237 );
1238 assert_eq!(
1239 deltas.intermediate_region,
1240 Some((
1241 Tuple::new(vec![F2Dot14::from_f32(-0.3)]),
1242 Tuple::new(vec![F2Dot14::from_f32(0.4)]),
1243 ))
1244 );
1245 }
1246
1247 #[test]
1248 fn intermediates_only_when_at_least_one_needed() {
1251 let any_points = vec![]; let deltas = GlyphDeltas::new(
1255 vec![
1256 Tent::new(F2Dot14::from_f32(0.5), None),
1257 Tent::new(F2Dot14::from_f32(0.5), None),
1258 ],
1259 any_points.clone(),
1260 );
1261 assert_eq!(deltas.intermediate_region, None);
1262
1263 let deltas = GlyphDeltas::new(
1265 vec![
1266 Tent::new(F2Dot14::from_f32(0.5), None),
1267 Tent::new(F2Dot14::from_f32(0.5), None),
1268 Tent::new(
1269 F2Dot14::from_f32(0.5),
1270 Some((F2Dot14::from_f32(-0.3), F2Dot14::from_f32(0.4))),
1271 ),
1272 ],
1273 any_points,
1274 );
1275 assert_eq!(
1276 deltas.intermediate_region,
1277 Some((
1278 Tuple::new(vec![
1279 F2Dot14::from_f32(0.0),
1280 F2Dot14::from_f32(0.0),
1281 F2Dot14::from_f32(-0.3)
1282 ]),
1283 Tuple::new(vec![
1284 F2Dot14::from_f32(0.5),
1285 F2Dot14::from_f32(0.5),
1286 F2Dot14::from_f32(0.4)
1287 ]),
1288 ))
1289 );
1290 }
1291}