1include!("../../generated/generated_gvar.rs");
4
5use std::collections::HashMap;
6
7use indexmap::IndexMap;
8
9use crate::{collections::HasLen, OffsetMarker};
10
11use super::variations::{
12 PackedDeltas, PackedPointNumbers, Tuple, TupleVariationCount, TupleVariationHeader,
13};
14
15pub mod iup;
16
17#[derive(Clone, Debug)]
19pub struct GlyphVariations {
20 gid: GlyphId,
21 variations: Vec<GlyphDeltas>,
22}
23
24#[derive(Clone, Debug)]
26pub struct GlyphDeltas {
27 peak_tuple: Tuple,
28 intermediate_region: Option<(Tuple, Tuple)>,
30 deltas: Vec<GlyphDelta>,
32 best_point_packing: PackedPointNumbers,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub struct GlyphDelta {
46 pub x: i16,
47 pub y: i16,
48 pub required: bool,
50}
51
52#[derive(Clone, Debug)]
54pub enum GvarInputError {
55 UnexpectedAxisCount {
57 gid: GlyphId,
58 expected: u16,
59 actual: u16,
60 },
61 InconsistentGlyphAxisCount(GlyphId),
63 InconsistentDeltaLength(GlyphId),
65 InconsistentTupleLengths(GlyphId),
68}
69
70impl Gvar {
71 pub fn new(
77 mut variations: Vec<GlyphVariations>,
78 axis_count: u16,
79 ) -> Result<Self, GvarInputError> {
80 fn compute_shared_peak_tuples(glyphs: &[GlyphVariations]) -> Vec<Tuple> {
81 const MAX_SHARED_TUPLES: usize = 4095;
82 let mut peak_tuple_counts = IndexMap::new();
83 for glyph in glyphs {
84 glyph.count_peak_tuples(&mut peak_tuple_counts);
85 }
86 let mut to_share = peak_tuple_counts
87 .into_iter()
88 .filter(|(_, n)| *n > 1)
89 .collect::<Vec<_>>();
90 to_share.sort_by_key(|(_, n)| std::cmp::Reverse(*n));
94 to_share.truncate(MAX_SHARED_TUPLES);
95 to_share.into_iter().map(|(t, _)| t.to_owned()).collect()
96 }
97
98 for var in &variations {
99 var.validate()?;
100 }
101
102 if let Some(bad_var) = variations
103 .iter()
104 .find(|var| var.axis_count().is_some() && var.axis_count().unwrap() != axis_count)
105 {
106 return Err(GvarInputError::UnexpectedAxisCount {
107 gid: bad_var.gid,
108 expected: axis_count,
109 actual: bad_var.axis_count().unwrap(),
110 });
111 }
112
113 let shared = compute_shared_peak_tuples(&variations);
114 let shared_idx_map = shared
115 .iter()
116 .enumerate()
117 .map(|(i, x)| (x, i as u16))
118 .collect();
119 variations.sort_unstable_by_key(|g| g.gid);
120 let glyphs = variations
121 .into_iter()
122 .map(|raw_g| raw_g.build(&shared_idx_map))
123 .collect();
124
125 Ok(Gvar {
126 axis_count,
127 shared_tuples: SharedTuples::new(shared).into(),
128 glyph_variation_data_offsets: glyphs,
129 })
130 }
131
132 fn compute_flags(&self) -> GvarFlags {
133 let max_offset = self
134 .glyph_variation_data_offsets
135 .iter()
136 .fold(0, |acc, val| acc + val.length + val.length % 2);
137
138 if max_offset / 2 <= (u16::MAX as u32) {
139 GvarFlags::default()
140 } else {
141 GvarFlags::LONG_OFFSETS
142 }
143 }
144
145 fn compute_glyph_count(&self) -> u16 {
146 self.glyph_variation_data_offsets.len().try_into().unwrap()
147 }
148
149 fn compute_data_array_offset(&self) -> u32 {
150 const BASE_OFFSET: usize = MajorMinor::RAW_BYTE_LEN
151 + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN
154 + u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN + u32::RAW_BYTE_LEN; let bytes_per_offset = if self.compute_flags() == GvarFlags::LONG_OFFSETS {
158 u32::RAW_BYTE_LEN
159 } else {
160 u16::RAW_BYTE_LEN
161 };
162
163 let offsets_len = (self.glyph_variation_data_offsets.len() + 1) * bytes_per_offset;
164
165 (BASE_OFFSET + offsets_len).try_into().unwrap()
166 }
167
168 fn compile_variation_data(&self) -> GlyphDataWriter {
169 GlyphDataWriter {
170 long_offsets: self.compute_flags() == GvarFlags::LONG_OFFSETS,
171 data: &self.glyph_variation_data_offsets,
172 }
173 }
174}
175
176fn max_by_first_key<I, B, F>(iter: I, mut key: F) -> Option<I::Item>
183where
184 I: Iterator,
185 B: Ord,
186 F: FnMut(&I::Item) -> B,
187{
188 iter.fold(None, |max_elem: Option<(_, _)>, item| {
189 let item_key = key(&item);
190 match &max_elem {
191 Some((_, max_key)) if item_key <= *max_key => max_elem,
193 _ => Some((item, item_key)),
195 }
196 })
197 .map(|(item, _)| item)
198}
199
200impl GlyphVariations {
201 pub fn new(gid: GlyphId, variations: Vec<GlyphDeltas>) -> Self {
203 Self { gid, variations }
204 }
205
206 fn validate(&self) -> Result<(), GvarInputError> {
208 let (axis_count, delta_len) = self
209 .variations
210 .first()
211 .map(|var| (var.peak_tuple.len(), var.deltas.len()))
212 .unwrap_or_default();
213 for var in &self.variations {
214 if var.peak_tuple.len() != axis_count {
215 return Err(GvarInputError::InconsistentGlyphAxisCount(self.gid));
216 }
217 if let Some((start, end)) = var.intermediate_region.as_ref() {
218 if start.len() != axis_count || end.len() != axis_count {
219 return Err(GvarInputError::InconsistentTupleLengths(self.gid));
220 }
221 }
222 if var.deltas.len() != delta_len {
223 return Err(GvarInputError::InconsistentDeltaLength(self.gid));
224 }
225 }
226 Ok(())
227 }
228
229 pub fn axis_count(&self) -> Option<u16> {
231 self.variations.first().map(|var| var.peak_tuple.len())
232 }
233
234 fn count_peak_tuples<'a>(&'a self, counter: &mut IndexMap<&'a Tuple, usize>) {
235 for tuple in &self.variations {
236 *counter.entry(&tuple.peak_tuple).or_default() += 1;
237 }
238 }
239
240 fn compute_shared_points(&self) -> Option<PackedPointNumbers> {
264 let mut point_number_counts = IndexMap::new();
265 for deltas in &self.variations {
267 let (_, count) = point_number_counts
269 .entry(&deltas.best_point_packing)
270 .or_insert_with(|| {
271 let size = deltas.best_point_packing.compute_size();
272 (size as usize, 0usize)
273 });
274 *count += 1;
275 }
276 let (pts, _) = max_by_first_key(
280 point_number_counts
281 .into_iter()
282 .filter(|(_, (_, count))| *count > 1),
284 |(_, (size, count))| (*count - 1) * *size,
285 )?;
286
287 Some(pts.to_owned())
288 }
289
290 fn build(self, shared_tuple_map: &HashMap<&Tuple, u16>) -> GlyphVariationData {
291 let shared_points = self.compute_shared_points();
292
293 let (tuple_headers, tuple_data): (Vec<_>, Vec<_>) = self
294 .variations
295 .into_iter()
296 .map(|tup| tup.build(shared_tuple_map, shared_points.as_ref()))
297 .unzip();
298
299 let mut temp = GlyphVariationData {
300 tuple_variation_headers: tuple_headers,
301 shared_point_numbers: shared_points,
302 per_tuple_data: tuple_data,
303 length: 0,
304 };
305
306 temp.length = temp.compute_size();
307 temp
308 }
309}
310
311impl GlyphDelta {
312 pub fn new(x: i16, y: i16, required: bool) -> Self {
314 Self { x, y, required }
315 }
316
317 pub fn required(x: i16, y: i16) -> Self {
319 Self::new(x, y, true)
320 }
321
322 pub fn optional(x: i16, y: i16) -> Self {
324 Self::new(x, y, false)
325 }
326}
327
328#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
335#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
336pub struct Tent {
337 peak: F2Dot14,
338 min: F2Dot14,
339 max: F2Dot14,
340}
341
342impl Tent {
343 pub fn new(peak: F2Dot14, intermediate: Option<(F2Dot14, F2Dot14)>) -> Self {
349 let (min, max) = intermediate.unwrap_or_else(|| Tent::implied_intermediates_for_peak(peak));
350 Self { peak, min, max }
351 }
352
353 fn requires_intermediate(&self) -> bool {
354 (self.min, self.max) != Self::implied_intermediates_for_peak(self.peak)
355 }
356
357 fn implied_intermediates_for_peak(peak: F2Dot14) -> (F2Dot14, F2Dot14) {
358 (peak.min(F2Dot14::ZERO), peak.max(F2Dot14::ZERO))
359 }
360}
361
362impl GlyphDeltas {
363 pub fn new(tents: Vec<Tent>, deltas: Vec<GlyphDelta>) -> Self {
368 let peak_tuple = Tuple::new(tents.iter().map(|coords| coords.peak).collect());
369
370 let intermediate_region = if tents.iter().any(Tent::requires_intermediate) {
374 Some(tents.iter().map(|tent| (tent.min, tent.max)).unzip())
375 } else {
376 None
377 };
378
379 let best_point_packing = Self::pick_best_point_number_repr(&deltas);
384 GlyphDeltas {
385 peak_tuple,
386 intermediate_region,
387 deltas,
388 best_point_packing,
389 }
390 }
391
392 fn pick_best_point_number_repr(deltas: &[GlyphDelta]) -> PackedPointNumbers {
399 if deltas.iter().all(|d| d.required) {
400 return PackedPointNumbers::All;
401 }
402
403 let dense = Self::build_non_sparse_data(deltas);
404 let sparse = Self::build_sparse_data(deltas);
405 let dense_size = dense.compute_size();
406 let sparse_size = sparse.compute_size();
407 log::trace!("dense {dense_size}, sparse {sparse_size}");
408 if sparse_size < dense_size {
409 sparse.private_point_numbers.unwrap()
410 } else {
411 PackedPointNumbers::All
412 }
413 }
414
415 fn build_non_sparse_data(deltas: &[GlyphDelta]) -> GlyphTupleVariationData {
416 let (x_deltas, y_deltas) = deltas
417 .iter()
418 .map(|delta| (delta.x as i32, delta.y as i32))
419 .unzip();
420 GlyphTupleVariationData {
421 private_point_numbers: Some(PackedPointNumbers::All),
422 x_deltas: PackedDeltas::new(x_deltas),
423 y_deltas: PackedDeltas::new(y_deltas),
424 }
425 }
426
427 fn build_sparse_data(deltas: &[GlyphDelta]) -> GlyphTupleVariationData {
428 let (x_deltas, y_deltas) = deltas
429 .iter()
430 .filter_map(|delta| delta.required.then_some((delta.x as i32, delta.y as i32)))
431 .unzip();
432 let point_numbers = deltas
433 .iter()
434 .enumerate()
435 .filter_map(|(i, delta)| delta.required.then_some(i as u16))
436 .collect();
437 GlyphTupleVariationData {
438 private_point_numbers: Some(PackedPointNumbers::Some(point_numbers)),
439 x_deltas: PackedDeltas::new(x_deltas),
440 y_deltas: PackedDeltas::new(y_deltas),
441 }
442 }
443
444 fn build(
448 self,
449 shared_tuple_map: &HashMap<&Tuple, u16>,
450 shared_points: Option<&PackedPointNumbers>,
451 ) -> (TupleVariationHeader, GlyphTupleVariationData) {
452 let GlyphDeltas {
453 peak_tuple,
454 intermediate_region,
455 deltas,
456 best_point_packing: point_numbers,
457 } = self;
458
459 let (idx, peak_tuple) = match shared_tuple_map.get(&peak_tuple) {
460 Some(idx) => (Some(*idx), None),
461 None => (None, Some(peak_tuple)),
462 };
463
464 let has_private_points = Some(&point_numbers) != shared_points;
465 let (x_deltas, y_deltas) = match &point_numbers {
466 PackedPointNumbers::All => deltas.iter().map(|d| (d.x as i32, d.y as i32)).unzip(),
467 PackedPointNumbers::Some(pts) => pts
468 .iter()
469 .map(|idx| {
470 let delta = deltas[*idx as usize];
471 (delta.x as i32, delta.y as i32)
472 })
473 .unzip(),
474 };
475
476 let data = GlyphTupleVariationData {
477 private_point_numbers: has_private_points.then_some(point_numbers),
478 x_deltas: PackedDeltas::new(x_deltas),
479 y_deltas: PackedDeltas::new(y_deltas),
480 };
481 let data_size = data.compute_size();
482
483 let header = TupleVariationHeader::new(
484 data_size,
485 idx,
486 peak_tuple,
487 intermediate_region,
488 has_private_points,
489 );
490
491 (header, data)
492 }
493}
494
495#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
497#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
498pub struct GlyphVariationData {
499 tuple_variation_headers: Vec<TupleVariationHeader>,
500 shared_point_numbers: Option<PackedPointNumbers>,
502 per_tuple_data: Vec<GlyphTupleVariationData>,
503 length: u32,
508}
509
510#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
512#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
513struct GlyphTupleVariationData {
514 private_point_numbers: Option<PackedPointNumbers>,
516 x_deltas: PackedDeltas,
517 y_deltas: PackedDeltas,
518}
519
520impl GlyphTupleVariationData {
521 fn compute_size(&self) -> u16 {
522 self.private_point_numbers
523 .as_ref()
524 .map(PackedPointNumbers::compute_size)
525 .unwrap_or_default()
526 .checked_add(self.x_deltas.compute_size())
527 .unwrap()
528 .checked_add(self.y_deltas.compute_size())
529 .unwrap()
530 }
531}
532
533impl FontWrite for GlyphTupleVariationData {
534 fn write_into(&self, writer: &mut TableWriter) {
535 self.private_point_numbers.write_into(writer);
536 self.x_deltas.write_into(writer);
537 self.y_deltas.write_into(writer);
538 }
539}
540
541struct GlyphDataWriter<'a> {
542 long_offsets: bool,
543 data: &'a [GlyphVariationData],
544}
545
546impl FontWrite for GlyphDataWriter<'_> {
547 fn write_into(&self, writer: &mut TableWriter) {
548 if self.long_offsets {
549 let mut last = 0u32;
550 last.write_into(writer);
551
552 for glyph in self.data {
554 last += glyph.compute_size();
555 last.write_into(writer);
556 }
557 } else {
558 let mut last = 0u16;
561 last.write_into(writer);
562
563 for glyph in self.data {
565 let size = glyph.compute_size();
566 let short_size = (size / 2) + size % 2;
568 last += short_size as u16;
569 last.write_into(writer);
570 }
571 }
572 for glyph in self.data {
574 if !glyph.is_empty() {
575 glyph.write_into(writer);
576 if !self.long_offsets {
577 writer.pad_to_2byte_aligned();
578 }
579 }
580 }
581 }
582}
583
584impl GlyphVariationData {
585 fn compute_tuple_variation_count(&self) -> TupleVariationCount {
586 assert!(self.tuple_variation_headers.len() <= 4095);
587 let mut bits = self.tuple_variation_headers.len() as u16;
588 if self.shared_point_numbers.is_some() {
589 bits |= TupleVariationCount::SHARED_POINT_NUMBERS;
590 }
591 TupleVariationCount::from_bits(bits)
592 }
593
594 fn is_empty(&self) -> bool {
595 self.tuple_variation_headers.is_empty()
596 }
597
598 fn compute_data_offset(&self) -> u16 {
599 let header_len = self
600 .tuple_variation_headers
601 .iter()
602 .fold(0usize, |acc, header| {
603 acc.checked_add(header.compute_size() as usize).unwrap()
604 });
605 (header_len + TupleVariationCount::RAW_BYTE_LEN + u16::RAW_BYTE_LEN)
606 .try_into()
607 .unwrap()
608 }
609
610 fn compute_size(&self) -> u32 {
611 if self.is_empty() {
612 return 0;
613 }
614
615 let data_start = self.compute_data_offset() as u32;
616 let shared_point_len = self
617 .shared_point_numbers
618 .as_ref()
619 .map(|pts| pts.compute_size())
620 .unwrap_or_default() as u32;
621 let tuple_data_len = self
622 .per_tuple_data
623 .iter()
624 .fold(0u32, |acc, tup| acc + tup.compute_size() as u32);
625 data_start + shared_point_len + tuple_data_len
626 }
627}
628
629impl Extend<F2Dot14> for Tuple {
630 fn extend<T: IntoIterator<Item = F2Dot14>>(&mut self, iter: T) {
631 self.values.extend(iter);
632 }
633}
634
635impl Validate for GlyphVariationData {
636 fn validate_impl(&self, ctx: &mut ValidationCtx) {
637 const MAX_TUPLE_VARIATIONS: usize = 4095;
638 if !(0..=MAX_TUPLE_VARIATIONS).contains(&self.tuple_variation_headers.len()) {
639 ctx.in_field("tuple_variation_headers", |ctx| {
640 ctx.report("expected 0-4095 tuple variation tables")
641 })
642 }
643 }
644}
645
646impl FontWrite for GlyphVariationData {
647 fn write_into(&self, writer: &mut TableWriter) {
648 self.compute_tuple_variation_count().write_into(writer);
649 self.compute_data_offset().write_into(writer);
650 self.tuple_variation_headers.write_into(writer);
651 self.shared_point_numbers.write_into(writer);
652 self.per_tuple_data.write_into(writer);
653 }
654}
655
656impl HasLen for SharedTuples {
657 fn len(&self) -> usize {
658 self.tuples.len()
659 }
660}
661
662impl FontWrite for TupleVariationCount {
663 fn write_into(&self, writer: &mut TableWriter) {
664 self.bits().write_into(writer)
665 }
666}
667
668impl std::fmt::Display for GvarInputError {
669 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
670 match self {
671 GvarInputError::UnexpectedAxisCount {
672 gid,
673 expected,
674 actual,
675 } => {
676 write!(
677 f,
678 "Expected {} axes for glyph {}, got {}",
679 expected, gid, actual
680 )
681 }
682 GvarInputError::InconsistentGlyphAxisCount(gid) => write!(
683 f,
684 "Glyph {gid} contains variations with inconsistent axis counts"
685 ),
686 GvarInputError::InconsistentDeltaLength(gid) => write!(
687 f,
688 "Glyph {gid} contains variations with inconsistent delta counts"
689 ),
690 GvarInputError::InconsistentTupleLengths(gid) => write!(
691 f,
692 "Glyph {gid} contains variations with inconsistent intermediate region sizes"
693 ),
694 }
695 }
696}
697
698impl std::error::Error for GvarInputError {}
699#[cfg(test)]
700mod tests {
701 use super::*;
702
703 fn peaks(peaks: Vec<F2Dot14>) -> Vec<Tent> {
705 peaks
706 .into_iter()
707 .map(|peak| Tent::new(peak, None))
708 .collect()
709 }
710
711 #[test]
712 fn gvar_smoke_test() {
713 let _ = env_logger::builder().is_test(true).try_init();
714 let table = Gvar::new(
715 vec![
716 GlyphVariations::new(GlyphId::new(0), vec![]),
717 GlyphVariations::new(
718 GlyphId::new(1),
719 vec![GlyphDeltas::new(
720 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
721 vec![
722 GlyphDelta::required(30, 31),
723 GlyphDelta::required(40, 41),
724 GlyphDelta::required(-50, -49),
725 GlyphDelta::required(101, 102),
726 GlyphDelta::required(10, 11),
727 ],
728 )],
729 ),
730 GlyphVariations::new(
731 GlyphId::new(2),
732 vec![
733 GlyphDeltas::new(
734 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
735 vec![
736 GlyphDelta::required(11, -20),
737 GlyphDelta::required(69, -41),
738 GlyphDelta::required(-69, 49),
739 GlyphDelta::required(168, 101),
740 GlyphDelta::required(1, 2),
741 ],
742 ),
743 GlyphDeltas::new(
744 peaks(vec![F2Dot14::from_f32(0.8), F2Dot14::from_f32(1.0)]),
745 vec![
746 GlyphDelta::required(3, -200),
747 GlyphDelta::required(4, -500),
748 GlyphDelta::required(5, -800),
749 GlyphDelta::required(6, -1200),
750 GlyphDelta::required(7, -1500),
751 ],
752 ),
753 ],
754 ),
755 ],
756 2,
757 )
758 .unwrap();
759 let g2 = &table.glyph_variation_data_offsets[1];
760 let computed = g2.compute_size();
761 let actual = crate::dump_table(g2).unwrap().len();
762 assert_eq!(computed as usize, actual);
763
764 let bytes = crate::dump_table(&table).unwrap();
765 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
766 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
767 assert_eq!(gvar.shared_tuple_count(), 1);
768 assert_eq!(gvar.glyph_count(), 3);
769
770 let g1 = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
771 let g1tup = g1.tuples().collect::<Vec<_>>();
772 assert_eq!(g1tup.len(), 1);
773
774 let (x, y): (Vec<_>, Vec<_>) = g1tup[0].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
775 assert_eq!(x, vec![30, 40, -50, 101, 10]);
776 assert_eq!(y, vec![31, 41, -49, 102, 11]);
777
778 let g2 = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
779 let g2tup = g2.tuples().collect::<Vec<_>>();
780 assert_eq!(g2tup.len(), 2);
781
782 let (x, y): (Vec<_>, Vec<_>) = g2tup[0].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
783 assert_eq!(x, vec![11, 69, -69, 168, 1]);
784 assert_eq!(y, vec![-20, -41, 49, 101, 2]);
785
786 let (x, y): (Vec<_>, Vec<_>) = g2tup[1].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
787
788 assert_eq!(x, vec![3, 4, 5, 6, 7]);
789 assert_eq!(y, vec![-200, -500, -800, -1200, -1500]);
790 }
791
792 #[test]
793 fn use_iup_when_appropriate() {
794 let _ = env_logger::builder().is_test(true).try_init();
796 let gid = GlyphId::new(0);
797 let table = Gvar::new(
798 vec![GlyphVariations::new(
799 gid,
800 vec![GlyphDeltas::new(
801 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
802 vec![
803 GlyphDelta::required(30, 31),
804 GlyphDelta::optional(30, 31),
805 GlyphDelta::optional(30, 31),
806 GlyphDelta::required(101, 102),
807 GlyphDelta::required(10, 11),
808 GlyphDelta::optional(10, 11),
809 ],
810 )],
811 )],
812 2,
813 )
814 .unwrap();
815
816 let bytes = crate::dump_table(&table).unwrap();
817 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
818 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
819 assert_eq!(gvar.shared_tuple_count(), 0);
820 assert_eq!(gvar.glyph_count(), 1);
821
822 let g1 = gvar.glyph_variation_data(gid).unwrap().unwrap();
823 let g1tup = g1.tuples().collect::<Vec<_>>();
824 assert_eq!(g1tup.len(), 1);
825 let tuple_variation = &g1tup[0];
826
827 assert!(!tuple_variation.has_deltas_for_all_points());
828 assert_eq!(
829 vec![0, 3, 4],
830 tuple_variation.point_numbers().collect::<Vec<_>>()
831 );
832
833 let points: Vec<_> = tuple_variation
834 .deltas()
835 .map(|d| (d.x_delta, d.y_delta))
836 .collect();
837 assert_eq!(points, vec![(30, 31), (101, 102), (10, 11)]);
838 }
839
840 #[test]
841 fn disregard_iup_when_appropriate() {
842 let points = vec![
845 GlyphDelta::required(1, 2),
846 GlyphDelta::required(3, 4),
847 GlyphDelta::required(5, 6),
848 GlyphDelta::optional(5, 6),
849 GlyphDelta::required(7, 8),
850 ];
851 let gid = GlyphId::new(0);
852 let table = Gvar::new(
853 vec![GlyphVariations::new(
854 gid,
855 vec![GlyphDeltas::new(
856 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
857 points,
858 )],
859 )],
860 2,
861 )
862 .unwrap();
863 let bytes = crate::dump_table(&table).unwrap();
864 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
865 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
866 assert_eq!(gvar.shared_tuple_count(), 0);
867 assert_eq!(gvar.glyph_count(), 1);
868
869 let g1 = gvar.glyph_variation_data(gid).unwrap().unwrap();
870 let g1tup = g1.tuples().collect::<Vec<_>>();
871 assert_eq!(g1tup.len(), 1);
872 let tuple_variation = &g1tup[0];
873
874 assert!(tuple_variation.has_deltas_for_all_points());
875 let points: Vec<_> = tuple_variation
876 .deltas()
877 .map(|d| (d.x_delta, d.y_delta))
878 .collect();
879 assert_eq!(points, vec![(1, 2), (3, 4), (5, 6), (5, 6), (7, 8)]);
880 }
881
882 #[test]
883 fn share_points() {
884 let _ = env_logger::builder().is_test(true).try_init();
885 let variations = GlyphVariations::new(
886 GlyphId::new(0),
887 vec![
888 GlyphDeltas::new(
889 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
890 vec![
891 GlyphDelta::required(1, 2),
892 GlyphDelta::optional(3, 4),
893 GlyphDelta::required(5, 6),
894 GlyphDelta::optional(5, 6),
895 GlyphDelta::required(7, 8),
896 GlyphDelta::optional(7, 8),
897 ],
898 ),
899 GlyphDeltas::new(
900 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
901 vec![
902 GlyphDelta::required(10, 20),
903 GlyphDelta::optional(30, 40),
904 GlyphDelta::required(50, 60),
905 GlyphDelta::optional(50, 60),
906 GlyphDelta::required(70, 80),
907 GlyphDelta::optional(70, 80),
908 ],
909 ),
910 ],
911 );
912
913 assert_eq!(
914 variations.compute_shared_points(),
915 Some(PackedPointNumbers::Some(vec![0, 2, 4]))
916 )
917 }
918
919 #[test]
920 fn share_all_points() {
921 let variations = GlyphVariations::new(
922 GlyphId::new(0),
923 vec![
924 GlyphDeltas::new(
925 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
926 vec![
927 GlyphDelta::required(1, 2),
928 GlyphDelta::required(3, 4),
929 GlyphDelta::required(5, 6),
930 ],
931 ),
932 GlyphDeltas::new(
933 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
934 vec![
935 GlyphDelta::required(2, 4),
936 GlyphDelta::required(6, 8),
937 GlyphDelta::required(7, 9),
938 ],
939 ),
940 ],
941 );
942
943 let shared_tups = HashMap::new();
944 let built = variations.build(&shared_tups);
945 assert_eq!(built.shared_point_numbers, Some(PackedPointNumbers::All))
946 }
947
948 #[test]
951 fn dont_share_unique_points() {
952 let variations = GlyphVariations::new(
953 GlyphId::new(0),
954 vec![
955 GlyphDeltas::new(
956 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
957 vec![
958 GlyphDelta::required(1, 2),
959 GlyphDelta::optional(3, 4),
960 GlyphDelta::required(5, 6),
961 GlyphDelta::optional(5, 6),
962 GlyphDelta::required(7, 8),
963 GlyphDelta::optional(7, 8),
964 ],
965 ),
966 GlyphDeltas::new(
967 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
968 vec![
969 GlyphDelta::required(10, 20),
970 GlyphDelta::required(35, 40),
971 GlyphDelta::required(50, 60),
972 GlyphDelta::optional(50, 60),
973 GlyphDelta::required(70, 80),
974 GlyphDelta::optional(70, 80),
975 ],
976 ),
977 GlyphDeltas::new(
978 peaks(vec![F2Dot14::from_f32(0.5), F2Dot14::from_f32(1.0)]),
979 vec![
980 GlyphDelta::required(1, 2),
981 GlyphDelta::optional(3, 4),
982 GlyphDelta::required(5, 6),
983 GlyphDelta::optional(5, 6),
984 GlyphDelta::optional(7, 8),
985 GlyphDelta::optional(7, 8),
986 ],
987 ),
988 ],
989 );
990
991 let shared_tups = HashMap::new();
992 let built = variations.build(&shared_tups);
993 assert!(built.shared_point_numbers.is_none());
994 }
995
996 #[test]
998 #[allow(non_snake_case)]
999 fn oswald_Lcaron() {
1000 let _ = env_logger::builder().is_test(true).try_init();
1001 let variations = GlyphVariations::new(
1005 GlyphId::new(0),
1006 vec![
1007 GlyphDeltas::new(
1008 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
1009 vec![
1010 GlyphDelta::optional(0, 0),
1011 GlyphDelta::required(35, 0),
1012 GlyphDelta::optional(0, 0),
1013 GlyphDelta::required(-24, 0),
1014 GlyphDelta::optional(0, 0),
1015 GlyphDelta::optional(0, 0),
1016 ],
1017 ),
1018 GlyphDeltas::new(
1019 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
1020 vec![
1021 GlyphDelta::optional(0, 0),
1022 GlyphDelta::required(26, 15),
1023 GlyphDelta::optional(0, 0),
1024 GlyphDelta::required(46, 0),
1025 GlyphDelta::optional(0, 0),
1026 GlyphDelta::optional(0, 0),
1027 ],
1028 ),
1029 ],
1030 );
1031 assert!(variations.compute_shared_points().is_none());
1032 let tups = HashMap::new();
1033 let built = variations.build(&tups);
1034 assert_eq!(
1035 built.per_tuple_data[0].private_point_numbers,
1036 Some(PackedPointNumbers::All)
1037 );
1038 assert_eq!(
1039 built.per_tuple_data[1].private_point_numbers,
1040 Some(PackedPointNumbers::Some(vec![1, 3]))
1041 );
1042 }
1043
1044 #[test]
1045 fn compute_shared_points_is_deterministic() {
1046 let _ = env_logger::builder().is_test(true).try_init();
1054 let variations = GlyphVariations::new(
1055 GlyphId::NOTDEF,
1056 vec![
1057 GlyphDeltas::new(
1058 peaks(vec![
1059 F2Dot14::from_f32(-1.0),
1060 F2Dot14::from_f32(0.0),
1061 F2Dot14::from_f32(0.0),
1062 ]),
1063 vec![
1064 GlyphDelta::optional(0, 0),
1065 GlyphDelta::required(-17, -4),
1066 GlyphDelta::optional(0, 0),
1067 GlyphDelta::required(-28, 0),
1068 GlyphDelta::optional(0, 0),
1069 GlyphDelta::optional(0, 0),
1070 ],
1071 ),
1072 GlyphDeltas::new(
1073 peaks(vec![
1074 F2Dot14::from_f32(0.0),
1075 F2Dot14::from_f32(1.0),
1076 F2Dot14::from_f32(0.0),
1077 ]),
1078 vec![
1079 GlyphDelta::optional(0, 0),
1080 GlyphDelta::required(0, -10),
1081 GlyphDelta::optional(0, 0),
1082 GlyphDelta::required(34, 0),
1083 GlyphDelta::optional(0, 0),
1084 GlyphDelta::optional(0, 0),
1085 ],
1086 ),
1087 GlyphDeltas::new(
1088 peaks(vec![
1089 F2Dot14::from_f32(0.0),
1090 F2Dot14::from_f32(0.0),
1091 F2Dot14::from_f32(-1.0),
1092 ]),
1093 vec![
1094 GlyphDelta::required(0, 0),
1095 GlyphDelta::optional(0, 0),
1096 GlyphDelta::optional(0, 0),
1097 GlyphDelta::optional(0, 0),
1098 GlyphDelta::optional(0, 0),
1099 GlyphDelta::optional(0, 0),
1100 ],
1101 ),
1102 GlyphDeltas::new(
1103 peaks(vec![
1104 F2Dot14::from_f32(0.0),
1105 F2Dot14::from_f32(0.0),
1106 F2Dot14::from_f32(1.0),
1107 ]),
1108 vec![
1109 GlyphDelta::required(0, 0),
1110 GlyphDelta::optional(0, 0),
1111 GlyphDelta::optional(0, 0),
1112 GlyphDelta::optional(0, 0),
1113 GlyphDelta::optional(0, 0),
1114 GlyphDelta::optional(0, 0),
1115 ],
1116 ),
1117 GlyphDeltas::new(
1118 peaks(vec![
1119 F2Dot14::from_f32(-1.0),
1120 F2Dot14::from_f32(1.0),
1121 F2Dot14::from_f32(0.0),
1122 ]),
1123 vec![
1124 GlyphDelta::optional(0, 0),
1125 GlyphDelta::required(-1, 10),
1126 GlyphDelta::optional(0, 0),
1127 GlyphDelta::required(-9, 0),
1128 GlyphDelta::optional(0, 0),
1129 GlyphDelta::optional(0, 0),
1130 ],
1131 ),
1132 GlyphDeltas::new(
1133 peaks(vec![
1134 F2Dot14::from_f32(-1.0),
1135 F2Dot14::from_f32(0.0),
1136 F2Dot14::from_f32(-1.0),
1137 ]),
1138 vec![
1139 GlyphDelta::required(0, 0),
1140 GlyphDelta::optional(0, 0),
1141 GlyphDelta::optional(0, 0),
1142 GlyphDelta::optional(0, 0),
1143 GlyphDelta::optional(0, 0),
1144 GlyphDelta::optional(0, 0),
1145 ],
1146 ),
1147 GlyphDeltas::new(
1148 peaks(vec![
1149 F2Dot14::from_f32(-1.0),
1150 F2Dot14::from_f32(0.0),
1151 F2Dot14::from_f32(1.0),
1152 ]),
1153 vec![
1154 GlyphDelta::required(0, 0),
1155 GlyphDelta::optional(0, 0),
1156 GlyphDelta::optional(0, 0),
1157 GlyphDelta::optional(0, 0),
1158 GlyphDelta::optional(0, 0),
1159 GlyphDelta::optional(0, 0),
1160 ],
1161 ),
1162 ],
1163 );
1164
1165 assert_eq!(
1166 variations.compute_shared_points(),
1167 Some(PackedPointNumbers::Some(vec![1, 3]))
1171 );
1172 }
1173
1174 fn make_31_bytes_of_variation_data() -> Vec<GlyphDeltas> {
1177 vec![
1178 GlyphDeltas::new(
1179 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
1180 vec![
1181 GlyphDelta::optional(0, 0),
1182 GlyphDelta::required(35, 0),
1183 GlyphDelta::optional(0, 0),
1184 GlyphDelta::required(-24, 0),
1185 GlyphDelta::optional(0, 0),
1186 GlyphDelta::optional(0, 0),
1187 ],
1188 ),
1189 GlyphDeltas::new(
1190 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
1191 vec![
1192 GlyphDelta::optional(0, 0),
1193 GlyphDelta::required(26, 15),
1194 GlyphDelta::optional(0, 0),
1195 GlyphDelta::required(46, 0),
1196 GlyphDelta::optional(0, 0),
1197 GlyphDelta::required(1, 0),
1198 ],
1199 ),
1200 ]
1201 }
1202
1203 #[test]
1205 fn who_tests_the_testers() {
1206 let variations = GlyphVariations::new(GlyphId::NOTDEF, make_31_bytes_of_variation_data());
1207 let mut tupl_map = HashMap::new();
1208
1209 assert_eq!(variations.clone().build(&tupl_map).length, 39);
1211
1212 tupl_map.insert(&variations.variations[0].peak_tuple, 1);
1214 tupl_map.insert(&variations.variations[1].peak_tuple, 2);
1215
1216 let built = variations.clone().build(&tupl_map);
1217 assert_eq!(built.length, 31);
1219 }
1220
1221 fn assert_test_offset_packing(n_glyphs: u16, should_be_short: bool) {
1222 let (offset_len, data_len, expected_flags) = if should_be_short {
1223 (u16::RAW_BYTE_LEN, 32, GvarFlags::empty())
1225 } else {
1226 (u32::RAW_BYTE_LEN, 31, GvarFlags::LONG_OFFSETS)
1227 };
1228
1229 let test_data = make_31_bytes_of_variation_data();
1230 let a_small_number_of_variations = (0..n_glyphs)
1231 .map(|i| GlyphVariations::new(GlyphId::from(i), test_data.clone()))
1232 .collect();
1233
1234 let gvar = Gvar::new(a_small_number_of_variations, 2).unwrap();
1235 assert_eq!(gvar.compute_flags(), expected_flags);
1236
1237 let writer = gvar.compile_variation_data();
1238 let mut sink = TableWriter::default();
1239 writer.write_into(&mut sink);
1240
1241 let bytes = sink.into_data().bytes;
1242 let expected_len = (n_glyphs + 1) as usize * offset_len + data_len * n_glyphs as usize; assert_eq!(bytes.len(), expected_len);
1245
1246 let dumped = crate::dump_table(&gvar).unwrap();
1247 let loaded = read_fonts::tables::gvar::Gvar::read(FontData::new(&dumped)).unwrap();
1248
1249 assert_eq!(loaded.glyph_count(), n_glyphs);
1250 assert_eq!(loaded.flags(), expected_flags);
1251 assert!(loaded
1252 .glyph_variation_data_offsets()
1253 .iter()
1254 .map(|off| off.unwrap().get())
1255 .enumerate()
1256 .all(|(i, off)| off as usize == i * data_len));
1257 }
1258
1259 #[test]
1260 fn prefer_short_offsets() {
1261 let _ = env_logger::builder().is_test(true).try_init();
1262 assert_test_offset_packing(5, true);
1263 }
1264
1265 #[test]
1266 fn use_long_offsets_when_necessary() {
1267 let _ = env_logger::builder().is_test(true).try_init();
1270 assert_test_offset_packing(4095, true);
1271 assert_test_offset_packing(4096, false);
1272 assert_test_offset_packing(4097, false);
1273 }
1274
1275 #[test]
1276 fn shared_tuples_stable_order() {
1277 let mut variations = Vec::new();
1280 for i in 0..2 {
1281 variations.push(GlyphVariations::new(
1282 GlyphId::new(i),
1283 vec![
1284 GlyphDeltas::new(
1285 peaks(vec![F2Dot14::from_f32(1.0)]),
1286 vec![GlyphDelta::required(10, 20)],
1287 ),
1288 GlyphDeltas::new(
1289 peaks(vec![F2Dot14::from_f32(-1.0)]),
1290 vec![GlyphDelta::required(-10, -20)],
1291 ),
1292 ],
1293 ))
1294 }
1295 for _ in 0..10 {
1296 let table = Gvar::new(variations.clone(), 1).unwrap();
1297 let bytes = crate::dump_table(&table).unwrap();
1298 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
1299
1300 assert_eq!(gvar.shared_tuple_count(), 2);
1301 assert_eq!(
1302 gvar.shared_tuples()
1303 .unwrap()
1304 .tuples()
1305 .iter()
1306 .map(|t| t.unwrap().values.to_vec())
1307 .collect::<Vec<_>>(),
1308 vec![vec![F2Dot14::from_f32(1.0)], vec![F2Dot14::from_f32(-1.0)]]
1309 );
1310 }
1311 }
1312
1313 #[test]
1314 fn unexpected_axis_count() {
1315 let variations = GlyphVariations::new(
1316 GlyphId::NOTDEF,
1317 vec![
1318 GlyphDeltas::new(
1319 peaks(vec![F2Dot14::from_f32(1.0)]),
1320 vec![GlyphDelta::required(1, 2)],
1321 ),
1322 GlyphDeltas::new(
1323 peaks(vec![F2Dot14::from_f32(1.0)]),
1324 vec![GlyphDelta::required(1, 2)],
1325 ),
1326 ],
1327 );
1328 let gvar = Gvar::new(vec![variations], 2);
1329 assert!(matches!(
1330 gvar,
1331 Err(GvarInputError::UnexpectedAxisCount {
1332 gid: GlyphId::NOTDEF,
1333 expected: 2,
1334 actual: 1
1335 })
1336 ));
1337 }
1338
1339 #[test]
1340 fn empty_gvar_has_expected_axis_count() {
1341 let variations = GlyphVariations::new(GlyphId::NOTDEF, vec![]);
1342 let gvar = Gvar::new(vec![variations], 2).unwrap();
1343 assert_eq!(gvar.axis_count, 2);
1344 }
1345
1346 #[test]
1347 fn intermediates_only_when_explicit_needed() {
1350 let any_points = vec![]; let deltas = GlyphDeltas::new(
1354 vec![Tent::new(F2Dot14::from_f32(0.5), None)],
1355 any_points.clone(),
1356 );
1357 assert_eq!(deltas.intermediate_region, None);
1358
1359 let deltas = GlyphDeltas::new(
1362 vec![Tent::new(
1363 F2Dot14::from_f32(0.5),
1364 Some(Tent::implied_intermediates_for_peak(F2Dot14::from_f32(0.5))),
1365 )],
1366 any_points.clone(),
1367 );
1368 assert_eq!(deltas.intermediate_region, None);
1369
1370 let deltas = GlyphDeltas::new(
1373 vec![Tent::new(
1374 F2Dot14::from_f32(0.5),
1375 Some((F2Dot14::from_f32(-0.3), F2Dot14::from_f32(0.4))),
1376 )],
1377 any_points.clone(),
1378 );
1379 assert_eq!(
1380 deltas.intermediate_region,
1381 Some((
1382 Tuple::new(vec![F2Dot14::from_f32(-0.3)]),
1383 Tuple::new(vec![F2Dot14::from_f32(0.4)]),
1384 ))
1385 );
1386 }
1387
1388 #[test]
1389 fn intermediates_only_when_at_least_one_needed() {
1392 let any_points = vec![]; let deltas = GlyphDeltas::new(
1396 vec![
1397 Tent::new(F2Dot14::from_f32(0.5), None),
1398 Tent::new(F2Dot14::from_f32(0.5), None),
1399 ],
1400 any_points.clone(),
1401 );
1402 assert_eq!(deltas.intermediate_region, None);
1403
1404 let deltas = GlyphDeltas::new(
1406 vec![
1407 Tent::new(F2Dot14::from_f32(0.5), None),
1408 Tent::new(F2Dot14::from_f32(0.5), None),
1409 Tent::new(
1410 F2Dot14::from_f32(0.5),
1411 Some((F2Dot14::from_f32(-0.3), F2Dot14::from_f32(0.4))),
1412 ),
1413 ],
1414 any_points,
1415 );
1416 assert_eq!(
1417 deltas.intermediate_region,
1418 Some((
1419 Tuple::new(vec![
1420 F2Dot14::from_f32(0.0),
1421 F2Dot14::from_f32(0.0),
1422 F2Dot14::from_f32(-0.3)
1423 ]),
1424 Tuple::new(vec![
1425 F2Dot14::from_f32(0.5),
1426 F2Dot14::from_f32(0.5),
1427 F2Dot14::from_f32(0.4)
1428 ]),
1429 ))
1430 );
1431 }
1432}