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_shared_tuples_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 compute_data_array_offset(&self) -> u32 {
169 let shared_tuples_len: u32 =
170 (array_len(&self.shared_tuples) * self.axis_count as usize * 2)
171 .try_into()
172 .unwrap();
173 self.compute_shared_tuples_offset() + shared_tuples_len
174 }
175
176 fn compile_variation_data(&self) -> GlyphDataWriter<'_> {
177 GlyphDataWriter {
178 long_offsets: self.compute_flags() == GvarFlags::LONG_OFFSETS,
179 shared_tuples: &self.shared_tuples,
180 data: &self.glyph_variation_data_offsets,
181 }
182 }
183}
184
185fn max_by_first_key<I, B, F>(iter: I, mut key: F) -> Option<I::Item>
192where
193 I: Iterator,
194 B: Ord,
195 F: FnMut(&I::Item) -> B,
196{
197 iter.fold(None, |max_elem: Option<(_, _)>, item| {
198 let item_key = key(&item);
199 match &max_elem {
200 Some((_, max_key)) if item_key <= *max_key => max_elem,
202 _ => Some((item, item_key)),
204 }
205 })
206 .map(|(item, _)| item)
207}
208
209impl GlyphVariations {
210 pub fn new(gid: GlyphId, variations: Vec<GlyphDeltas>) -> Self {
212 Self { gid, variations }
213 }
214
215 fn validate(&self) -> Result<(), GvarInputError> {
217 let (axis_count, delta_len) = self
218 .variations
219 .first()
220 .map(|var| (var.peak_tuple.len(), var.deltas.len()))
221 .unwrap_or_default();
222 for var in &self.variations {
223 if var.peak_tuple.len() != axis_count {
224 return Err(GvarInputError::InconsistentGlyphAxisCount(self.gid));
225 }
226 if let Some((start, end)) = var.intermediate_region.as_ref() {
227 if start.len() != axis_count || end.len() != axis_count {
228 return Err(GvarInputError::InconsistentTupleLengths(self.gid));
229 }
230 }
231 if var.deltas.len() != delta_len {
232 return Err(GvarInputError::InconsistentDeltaLength(self.gid));
233 }
234 }
235 Ok(())
236 }
237
238 pub fn axis_count(&self) -> Option<u16> {
240 self.variations.first().map(|var| var.peak_tuple.len())
241 }
242
243 fn count_peak_tuples<'a>(&'a self, counter: &mut IndexMap<&'a Tuple, usize>) {
244 for tuple in &self.variations {
245 *counter.entry(&tuple.peak_tuple).or_default() += 1;
246 }
247 }
248
249 fn compute_shared_points(&self) -> Option<PackedPointNumbers> {
273 let mut point_number_counts = IndexMap::new();
274 for deltas in &self.variations {
276 let (_, count) = point_number_counts
278 .entry(&deltas.best_point_packing)
279 .or_insert_with(|| {
280 let size = deltas.best_point_packing.compute_size();
281 (size as usize, 0usize)
282 });
283 *count += 1;
284 }
285 let (pts, _) = max_by_first_key(
289 point_number_counts
290 .into_iter()
291 .filter(|(_, (_, count))| *count > 1),
293 |(_, (size, count))| (*count - 1) * *size,
294 )?;
295
296 Some(pts.to_owned())
297 }
298
299 fn build(self, shared_tuple_map: &HashMap<&Tuple, u16>) -> GlyphVariationData {
300 let shared_points = self.compute_shared_points();
301
302 let (tuple_headers, tuple_data): (Vec<_>, Vec<_>) = self
303 .variations
304 .into_iter()
305 .map(|tup| tup.build(shared_tuple_map, shared_points.as_ref()))
306 .unzip();
307
308 let mut temp = GlyphVariationData {
309 tuple_variation_headers: tuple_headers,
310 shared_point_numbers: shared_points,
311 per_tuple_data: tuple_data,
312 length: 0,
313 };
314
315 temp.length = temp.compute_size();
316 temp
317 }
318}
319
320impl GlyphDelta {
321 pub fn new(x: i16, y: i16, required: bool) -> Self {
323 Self { x, y, required }
324 }
325
326 pub fn required(x: i16, y: i16) -> Self {
328 Self::new(x, y, true)
329 }
330
331 pub fn optional(x: i16, y: i16) -> Self {
333 Self::new(x, y, false)
334 }
335}
336
337#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
344#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
345pub struct Tent {
346 peak: F2Dot14,
347 min: F2Dot14,
348 max: F2Dot14,
349}
350
351impl Tent {
352 pub fn new(peak: F2Dot14, intermediate: Option<(F2Dot14, F2Dot14)>) -> Self {
358 let (min, max) = intermediate.unwrap_or_else(|| Tent::implied_intermediates_for_peak(peak));
359 Self { peak, min, max }
360 }
361
362 fn requires_intermediate(&self) -> bool {
363 (self.min, self.max) != Self::implied_intermediates_for_peak(self.peak)
364 }
365
366 fn implied_intermediates_for_peak(peak: F2Dot14) -> (F2Dot14, F2Dot14) {
367 (peak.min(F2Dot14::ZERO), peak.max(F2Dot14::ZERO))
368 }
369}
370
371impl GlyphDeltas {
372 pub fn new(tents: Vec<Tent>, deltas: Vec<GlyphDelta>) -> Self {
377 let peak_tuple = Tuple::new(tents.iter().map(|coords| coords.peak).collect());
378
379 let intermediate_region = if tents.iter().any(Tent::requires_intermediate) {
383 Some(tents.iter().map(|tent| (tent.min, tent.max)).unzip())
384 } else {
385 None
386 };
387
388 let best_point_packing = Self::pick_best_point_number_repr(&deltas);
393 GlyphDeltas {
394 peak_tuple,
395 intermediate_region,
396 deltas,
397 best_point_packing,
398 }
399 }
400
401 fn pick_best_point_number_repr(deltas: &[GlyphDelta]) -> PackedPointNumbers {
408 if deltas.iter().all(|d| d.required) {
409 return PackedPointNumbers::All;
410 }
411
412 let dense = Self::build_non_sparse_data(deltas);
413 let sparse = Self::build_sparse_data(deltas);
414 let dense_size = dense.compute_size();
415 let sparse_size = sparse.compute_size();
416 log::trace!("dense {dense_size}, sparse {sparse_size}");
417 if sparse_size < dense_size {
418 sparse.private_point_numbers.unwrap()
419 } else {
420 PackedPointNumbers::All
421 }
422 }
423
424 fn build_non_sparse_data(deltas: &[GlyphDelta]) -> GlyphTupleVariationData {
425 let (x_deltas, y_deltas) = deltas
426 .iter()
427 .map(|delta| (delta.x as i32, delta.y as i32))
428 .unzip();
429 GlyphTupleVariationData {
430 private_point_numbers: Some(PackedPointNumbers::All),
431 x_deltas: PackedDeltas::new(x_deltas),
432 y_deltas: PackedDeltas::new(y_deltas),
433 }
434 }
435
436 fn build_sparse_data(deltas: &[GlyphDelta]) -> GlyphTupleVariationData {
437 let (x_deltas, y_deltas) = deltas
438 .iter()
439 .filter_map(|delta| delta.required.then_some((delta.x as i32, delta.y as i32)))
440 .unzip();
441 let point_numbers = deltas
442 .iter()
443 .enumerate()
444 .filter_map(|(i, delta)| delta.required.then_some(i as u16))
445 .collect();
446 GlyphTupleVariationData {
447 private_point_numbers: Some(PackedPointNumbers::Some(point_numbers)),
448 x_deltas: PackedDeltas::new(x_deltas),
449 y_deltas: PackedDeltas::new(y_deltas),
450 }
451 }
452
453 fn build(
457 self,
458 shared_tuple_map: &HashMap<&Tuple, u16>,
459 shared_points: Option<&PackedPointNumbers>,
460 ) -> (TupleVariationHeader, GlyphTupleVariationData) {
461 let GlyphDeltas {
462 peak_tuple,
463 intermediate_region,
464 deltas,
465 best_point_packing: point_numbers,
466 } = self;
467
468 let (idx, peak_tuple) = match shared_tuple_map.get(&peak_tuple) {
469 Some(idx) => (Some(*idx), None),
470 None => (None, Some(peak_tuple)),
471 };
472
473 let has_private_points = Some(&point_numbers) != shared_points;
474 let (x_deltas, y_deltas) = match &point_numbers {
475 PackedPointNumbers::All => deltas.iter().map(|d| (d.x as i32, d.y as i32)).unzip(),
476 PackedPointNumbers::Some(pts) => pts
477 .iter()
478 .map(|idx| {
479 let delta = deltas[*idx as usize];
480 (delta.x as i32, delta.y as i32)
481 })
482 .unzip(),
483 };
484
485 let data = GlyphTupleVariationData {
486 private_point_numbers: has_private_points.then_some(point_numbers),
487 x_deltas: PackedDeltas::new(x_deltas),
488 y_deltas: PackedDeltas::new(y_deltas),
489 };
490 let data_size = data.compute_size();
491
492 let header = TupleVariationHeader::new(
493 data_size,
494 idx,
495 peak_tuple,
496 intermediate_region,
497 has_private_points,
498 );
499
500 (header, data)
501 }
502}
503
504#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
506#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
507pub struct GlyphVariationData {
508 tuple_variation_headers: Vec<TupleVariationHeader>,
509 shared_point_numbers: Option<PackedPointNumbers>,
511 per_tuple_data: Vec<GlyphTupleVariationData>,
512 length: u32,
517}
518
519#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
521#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
522struct GlyphTupleVariationData {
523 private_point_numbers: Option<PackedPointNumbers>,
525 x_deltas: PackedDeltas,
526 y_deltas: PackedDeltas,
527}
528
529impl GlyphTupleVariationData {
530 fn compute_size(&self) -> u16 {
531 self.private_point_numbers
532 .as_ref()
533 .map(PackedPointNumbers::compute_size)
534 .unwrap_or_default()
535 .checked_add(self.x_deltas.compute_size())
536 .unwrap()
537 .checked_add(self.y_deltas.compute_size())
538 .unwrap()
539 }
540}
541
542impl FontWrite for GlyphTupleVariationData {
543 fn write_into(&self, writer: &mut TableWriter) {
544 self.private_point_numbers.write_into(writer);
545 self.x_deltas.write_into(writer);
546 self.y_deltas.write_into(writer);
547 }
548}
549
550struct GlyphDataWriter<'a> {
551 long_offsets: bool,
552 shared_tuples: &'a SharedTuples,
553 data: &'a [GlyphVariationData],
554}
555
556impl FontWrite for GlyphDataWriter<'_> {
557 fn write_into(&self, writer: &mut TableWriter) {
558 if self.long_offsets {
559 let mut last = 0u32;
560 last.write_into(writer);
561
562 for glyph in self.data {
564 last += glyph.compute_size();
565 last.write_into(writer);
566 }
567 } else {
568 let mut last = 0u16;
571 last.write_into(writer);
572
573 for glyph in self.data {
575 let size = glyph.compute_size();
576 let short_size = (size / 2) + size % 2;
578 last += short_size as u16;
579 last.write_into(writer);
580 }
581 }
582 self.shared_tuples.write_into(writer);
586 for glyph in self.data {
588 if !glyph.is_empty() {
589 glyph.write_into(writer);
590 if !self.long_offsets {
591 writer.pad_to_2byte_aligned();
592 }
593 }
594 }
595 }
596}
597
598impl GlyphVariationData {
599 fn compute_tuple_variation_count(&self) -> TupleVariationCount {
600 assert!(self.tuple_variation_headers.len() <= 4095);
601 let mut bits = self.tuple_variation_headers.len() as u16;
602 if self.shared_point_numbers.is_some() {
603 bits |= TupleVariationCount::SHARED_POINT_NUMBERS;
604 }
605 TupleVariationCount::from_bits(bits)
606 }
607
608 fn is_empty(&self) -> bool {
609 self.tuple_variation_headers.is_empty()
610 }
611
612 fn compute_data_offset(&self) -> u16 {
613 let header_len = self
614 .tuple_variation_headers
615 .iter()
616 .fold(0usize, |acc, header| {
617 acc.checked_add(header.compute_size() as usize).unwrap()
618 });
619 (header_len + TupleVariationCount::RAW_BYTE_LEN + u16::RAW_BYTE_LEN)
620 .try_into()
621 .unwrap()
622 }
623
624 fn compute_size(&self) -> u32 {
625 if self.is_empty() {
626 return 0;
627 }
628
629 let data_start = self.compute_data_offset() as u32;
630 let shared_point_len = self
631 .shared_point_numbers
632 .as_ref()
633 .map(|pts| pts.compute_size())
634 .unwrap_or_default() as u32;
635 let tuple_data_len = self
636 .per_tuple_data
637 .iter()
638 .fold(0u32, |acc, tup| acc + tup.compute_size() as u32);
639 data_start + shared_point_len + tuple_data_len
640 }
641}
642
643impl Extend<F2Dot14> for Tuple {
644 fn extend<T: IntoIterator<Item = F2Dot14>>(&mut self, iter: T) {
645 self.values.extend(iter);
646 }
647}
648
649impl Validate for GlyphVariationData {
650 fn validate_impl(&self, ctx: &mut ValidationCtx) {
651 const MAX_TUPLE_VARIATIONS: usize = 4095;
652 if !(0..=MAX_TUPLE_VARIATIONS).contains(&self.tuple_variation_headers.len()) {
653 ctx.in_field("tuple_variation_headers", |ctx| {
654 ctx.report("expected 0-4095 tuple variation tables")
655 })
656 }
657 }
658}
659
660impl FontWrite for GlyphVariationData {
661 fn write_into(&self, writer: &mut TableWriter) {
662 self.compute_tuple_variation_count().write_into(writer);
663 self.compute_data_offset().write_into(writer);
664 self.tuple_variation_headers.write_into(writer);
665 self.shared_point_numbers.write_into(writer);
666 self.per_tuple_data.write_into(writer);
667 }
668}
669
670impl HasLen for SharedTuples {
671 fn len(&self) -> usize {
672 self.tuples.len()
673 }
674}
675
676impl FontWrite for TupleVariationCount {
677 fn write_into(&self, writer: &mut TableWriter) {
678 self.bits().write_into(writer)
679 }
680}
681
682impl std::fmt::Display for GvarInputError {
683 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
684 match self {
685 GvarInputError::UnexpectedAxisCount {
686 gid,
687 expected,
688 actual,
689 } => {
690 write!(
691 f,
692 "Expected {} axes for glyph {}, got {}",
693 expected, gid, actual
694 )
695 }
696 GvarInputError::InconsistentGlyphAxisCount(gid) => write!(
697 f,
698 "Glyph {gid} contains variations with inconsistent axis counts"
699 ),
700 GvarInputError::InconsistentDeltaLength(gid) => write!(
701 f,
702 "Glyph {gid} contains variations with inconsistent delta counts"
703 ),
704 GvarInputError::InconsistentTupleLengths(gid) => write!(
705 f,
706 "Glyph {gid} contains variations with inconsistent intermediate region sizes"
707 ),
708 }
709 }
710}
711
712impl std::error::Error for GvarInputError {}
713#[cfg(test)]
714mod tests {
715 use super::*;
716
717 fn peaks(peaks: Vec<F2Dot14>) -> Vec<Tent> {
719 peaks
720 .into_iter()
721 .map(|peak| Tent::new(peak, None))
722 .collect()
723 }
724
725 #[test]
726 fn gvar_smoke_test() {
727 let _ = env_logger::builder().is_test(true).try_init();
728 let table = Gvar::new(
729 vec![
730 GlyphVariations::new(GlyphId::new(0), vec![]),
731 GlyphVariations::new(
732 GlyphId::new(1),
733 vec![GlyphDeltas::new(
734 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
735 vec![
736 GlyphDelta::required(30, 31),
737 GlyphDelta::required(40, 41),
738 GlyphDelta::required(-50, -49),
739 GlyphDelta::required(101, 102),
740 GlyphDelta::required(10, 11),
741 ],
742 )],
743 ),
744 GlyphVariations::new(
745 GlyphId::new(2),
746 vec![
747 GlyphDeltas::new(
748 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
749 vec![
750 GlyphDelta::required(11, -20),
751 GlyphDelta::required(69, -41),
752 GlyphDelta::required(-69, 49),
753 GlyphDelta::required(168, 101),
754 GlyphDelta::required(1, 2),
755 ],
756 ),
757 GlyphDeltas::new(
758 peaks(vec![F2Dot14::from_f32(0.8), F2Dot14::from_f32(1.0)]),
759 vec![
760 GlyphDelta::required(3, -200),
761 GlyphDelta::required(4, -500),
762 GlyphDelta::required(5, -800),
763 GlyphDelta::required(6, -1200),
764 GlyphDelta::required(7, -1500),
765 ],
766 ),
767 ],
768 ),
769 ],
770 2,
771 )
772 .unwrap();
773
774 let g2 = &table.glyph_variation_data_offsets[1];
775 let computed = g2.compute_size();
776 let actual = crate::dump_table(g2).unwrap().len();
777 assert_eq!(computed as usize, actual);
778
779 let bytes = crate::dump_table(&table).unwrap();
780 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
781 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
782 assert_eq!(gvar.shared_tuple_count(), 1);
783 assert_eq!(gvar.glyph_count(), 3);
784 assert_eq!(gvar.shared_tuples_offset(), Offset32::new(28));
786 assert_eq!(gvar.glyph_variation_data_array_offset(), 32);
787
788 let g1 = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
789 let g1tup = g1.tuples().collect::<Vec<_>>();
790 assert_eq!(g1tup.len(), 1);
791
792 let (x, y): (Vec<_>, Vec<_>) = g1tup[0].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
793 assert_eq!(x, vec![30, 40, -50, 101, 10]);
794 assert_eq!(y, vec![31, 41, -49, 102, 11]);
795
796 let g2 = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
797 let g2tup = g2.tuples().collect::<Vec<_>>();
798 assert_eq!(g2tup.len(), 2);
799
800 let (x, y): (Vec<_>, Vec<_>) = g2tup[0].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
801 assert_eq!(x, vec![11, 69, -69, 168, 1]);
802 assert_eq!(y, vec![-20, -41, 49, 101, 2]);
803
804 let (x, y): (Vec<_>, Vec<_>) = g2tup[1].deltas().map(|d| (d.x_delta, d.y_delta)).unzip();
805
806 assert_eq!(x, vec![3, 4, 5, 6, 7]);
807 assert_eq!(y, vec![-200, -500, -800, -1200, -1500]);
808 }
809
810 #[test]
811 fn use_iup_when_appropriate() {
812 let _ = env_logger::builder().is_test(true).try_init();
814 let gid = GlyphId::new(0);
815 let table = Gvar::new(
816 vec![GlyphVariations::new(
817 gid,
818 vec![GlyphDeltas::new(
819 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
820 vec![
821 GlyphDelta::required(30, 31),
822 GlyphDelta::optional(30, 31),
823 GlyphDelta::optional(30, 31),
824 GlyphDelta::required(101, 102),
825 GlyphDelta::required(10, 11),
826 GlyphDelta::optional(10, 11),
827 ],
828 )],
829 )],
830 2,
831 )
832 .unwrap();
833
834 let bytes = crate::dump_table(&table).unwrap();
835 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
836 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
837 assert_eq!(gvar.shared_tuple_count(), 0);
838 assert_eq!(gvar.glyph_count(), 1);
839
840 let g1 = gvar.glyph_variation_data(gid).unwrap().unwrap();
841 let g1tup = g1.tuples().collect::<Vec<_>>();
842 assert_eq!(g1tup.len(), 1);
843 let tuple_variation = &g1tup[0];
844
845 assert!(!tuple_variation.has_deltas_for_all_points());
846 assert_eq!(
847 vec![0, 3, 4],
848 tuple_variation.point_numbers().collect::<Vec<_>>()
849 );
850
851 let points: Vec<_> = tuple_variation
852 .deltas()
853 .map(|d| (d.x_delta, d.y_delta))
854 .collect();
855 assert_eq!(points, vec![(30, 31), (101, 102), (10, 11)]);
856 }
857
858 #[test]
859 fn disregard_iup_when_appropriate() {
860 let points = vec![
863 GlyphDelta::required(1, 2),
864 GlyphDelta::required(3, 4),
865 GlyphDelta::required(5, 6),
866 GlyphDelta::optional(5, 6),
867 GlyphDelta::required(7, 8),
868 ];
869 let gid = GlyphId::new(0);
870 let table = Gvar::new(
871 vec![GlyphVariations::new(
872 gid,
873 vec![GlyphDeltas::new(
874 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
875 points,
876 )],
877 )],
878 2,
879 )
880 .unwrap();
881 let bytes = crate::dump_table(&table).unwrap();
882 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
883 assert_eq!(gvar.version(), MajorMinor::VERSION_1_0);
884 assert_eq!(gvar.shared_tuple_count(), 0);
885 assert_eq!(gvar.glyph_count(), 1);
886
887 let g1 = gvar.glyph_variation_data(gid).unwrap().unwrap();
888 let g1tup = g1.tuples().collect::<Vec<_>>();
889 assert_eq!(g1tup.len(), 1);
890 let tuple_variation = &g1tup[0];
891
892 assert!(tuple_variation.has_deltas_for_all_points());
893 let points: Vec<_> = tuple_variation
894 .deltas()
895 .map(|d| (d.x_delta, d.y_delta))
896 .collect();
897 assert_eq!(points, vec![(1, 2), (3, 4), (5, 6), (5, 6), (7, 8)]);
898 }
899
900 #[test]
901 fn share_points() {
902 let _ = env_logger::builder().is_test(true).try_init();
903 let variations = GlyphVariations::new(
904 GlyphId::new(0),
905 vec![
906 GlyphDeltas::new(
907 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
908 vec![
909 GlyphDelta::required(1, 2),
910 GlyphDelta::optional(3, 4),
911 GlyphDelta::required(5, 6),
912 GlyphDelta::optional(5, 6),
913 GlyphDelta::required(7, 8),
914 GlyphDelta::optional(7, 8),
915 ],
916 ),
917 GlyphDeltas::new(
918 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
919 vec![
920 GlyphDelta::required(10, 20),
921 GlyphDelta::optional(30, 40),
922 GlyphDelta::required(50, 60),
923 GlyphDelta::optional(50, 60),
924 GlyphDelta::required(70, 80),
925 GlyphDelta::optional(70, 80),
926 ],
927 ),
928 ],
929 );
930
931 assert_eq!(
932 variations.compute_shared_points(),
933 Some(PackedPointNumbers::Some(vec![0, 2, 4]))
934 )
935 }
936
937 #[test]
938 fn share_all_points() {
939 let variations = GlyphVariations::new(
940 GlyphId::new(0),
941 vec![
942 GlyphDeltas::new(
943 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
944 vec![
945 GlyphDelta::required(1, 2),
946 GlyphDelta::required(3, 4),
947 GlyphDelta::required(5, 6),
948 ],
949 ),
950 GlyphDeltas::new(
951 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
952 vec![
953 GlyphDelta::required(2, 4),
954 GlyphDelta::required(6, 8),
955 GlyphDelta::required(7, 9),
956 ],
957 ),
958 ],
959 );
960
961 let shared_tups = HashMap::new();
962 let built = variations.build(&shared_tups);
963 assert_eq!(built.shared_point_numbers, Some(PackedPointNumbers::All))
964 }
965
966 #[test]
969 fn dont_share_unique_points() {
970 let variations = GlyphVariations::new(
971 GlyphId::new(0),
972 vec![
973 GlyphDeltas::new(
974 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
975 vec![
976 GlyphDelta::required(1, 2),
977 GlyphDelta::optional(3, 4),
978 GlyphDelta::required(5, 6),
979 GlyphDelta::optional(5, 6),
980 GlyphDelta::required(7, 8),
981 GlyphDelta::optional(7, 8),
982 ],
983 ),
984 GlyphDeltas::new(
985 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
986 vec![
987 GlyphDelta::required(10, 20),
988 GlyphDelta::required(35, 40),
989 GlyphDelta::required(50, 60),
990 GlyphDelta::optional(50, 60),
991 GlyphDelta::required(70, 80),
992 GlyphDelta::optional(70, 80),
993 ],
994 ),
995 GlyphDeltas::new(
996 peaks(vec![F2Dot14::from_f32(0.5), F2Dot14::from_f32(1.0)]),
997 vec![
998 GlyphDelta::required(1, 2),
999 GlyphDelta::optional(3, 4),
1000 GlyphDelta::required(5, 6),
1001 GlyphDelta::optional(5, 6),
1002 GlyphDelta::optional(7, 8),
1003 GlyphDelta::optional(7, 8),
1004 ],
1005 ),
1006 ],
1007 );
1008
1009 let shared_tups = HashMap::new();
1010 let built = variations.build(&shared_tups);
1011 assert!(built.shared_point_numbers.is_none());
1012 }
1013
1014 #[test]
1016 #[allow(non_snake_case)]
1017 fn oswald_Lcaron() {
1018 let _ = env_logger::builder().is_test(true).try_init();
1019 let variations = GlyphVariations::new(
1023 GlyphId::new(0),
1024 vec![
1025 GlyphDeltas::new(
1026 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
1027 vec![
1028 GlyphDelta::optional(0, 0),
1029 GlyphDelta::required(35, 0),
1030 GlyphDelta::optional(0, 0),
1031 GlyphDelta::required(-24, 0),
1032 GlyphDelta::optional(0, 0),
1033 GlyphDelta::optional(0, 0),
1034 ],
1035 ),
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(26, 15),
1041 GlyphDelta::optional(0, 0),
1042 GlyphDelta::required(46, 0),
1043 GlyphDelta::optional(0, 0),
1044 GlyphDelta::optional(0, 0),
1045 ],
1046 ),
1047 ],
1048 );
1049 assert!(variations.compute_shared_points().is_none());
1050 let tups = HashMap::new();
1051 let built = variations.build(&tups);
1052 assert_eq!(
1053 built.per_tuple_data[0].private_point_numbers,
1054 Some(PackedPointNumbers::All)
1055 );
1056 assert_eq!(
1057 built.per_tuple_data[1].private_point_numbers,
1058 Some(PackedPointNumbers::Some(vec![1, 3]))
1059 );
1060 }
1061
1062 #[test]
1063 fn compute_shared_points_is_deterministic() {
1064 let _ = env_logger::builder().is_test(true).try_init();
1072 let variations = GlyphVariations::new(
1073 GlyphId::NOTDEF,
1074 vec![
1075 GlyphDeltas::new(
1076 peaks(vec![
1077 F2Dot14::from_f32(-1.0),
1078 F2Dot14::from_f32(0.0),
1079 F2Dot14::from_f32(0.0),
1080 ]),
1081 vec![
1082 GlyphDelta::optional(0, 0),
1083 GlyphDelta::required(-17, -4),
1084 GlyphDelta::optional(0, 0),
1085 GlyphDelta::required(-28, 0),
1086 GlyphDelta::optional(0, 0),
1087 GlyphDelta::optional(0, 0),
1088 ],
1089 ),
1090 GlyphDeltas::new(
1091 peaks(vec![
1092 F2Dot14::from_f32(0.0),
1093 F2Dot14::from_f32(1.0),
1094 F2Dot14::from_f32(0.0),
1095 ]),
1096 vec![
1097 GlyphDelta::optional(0, 0),
1098 GlyphDelta::required(0, -10),
1099 GlyphDelta::optional(0, 0),
1100 GlyphDelta::required(34, 0),
1101 GlyphDelta::optional(0, 0),
1102 GlyphDelta::optional(0, 0),
1103 ],
1104 ),
1105 GlyphDeltas::new(
1106 peaks(vec![
1107 F2Dot14::from_f32(0.0),
1108 F2Dot14::from_f32(0.0),
1109 F2Dot14::from_f32(-1.0),
1110 ]),
1111 vec![
1112 GlyphDelta::required(0, 0),
1113 GlyphDelta::optional(0, 0),
1114 GlyphDelta::optional(0, 0),
1115 GlyphDelta::optional(0, 0),
1116 GlyphDelta::optional(0, 0),
1117 GlyphDelta::optional(0, 0),
1118 ],
1119 ),
1120 GlyphDeltas::new(
1121 peaks(vec![
1122 F2Dot14::from_f32(0.0),
1123 F2Dot14::from_f32(0.0),
1124 F2Dot14::from_f32(1.0),
1125 ]),
1126 vec![
1127 GlyphDelta::required(0, 0),
1128 GlyphDelta::optional(0, 0),
1129 GlyphDelta::optional(0, 0),
1130 GlyphDelta::optional(0, 0),
1131 GlyphDelta::optional(0, 0),
1132 GlyphDelta::optional(0, 0),
1133 ],
1134 ),
1135 GlyphDeltas::new(
1136 peaks(vec![
1137 F2Dot14::from_f32(-1.0),
1138 F2Dot14::from_f32(1.0),
1139 F2Dot14::from_f32(0.0),
1140 ]),
1141 vec![
1142 GlyphDelta::optional(0, 0),
1143 GlyphDelta::required(-1, 10),
1144 GlyphDelta::optional(0, 0),
1145 GlyphDelta::required(-9, 0),
1146 GlyphDelta::optional(0, 0),
1147 GlyphDelta::optional(0, 0),
1148 ],
1149 ),
1150 GlyphDeltas::new(
1151 peaks(vec![
1152 F2Dot14::from_f32(-1.0),
1153 F2Dot14::from_f32(0.0),
1154 F2Dot14::from_f32(-1.0),
1155 ]),
1156 vec![
1157 GlyphDelta::required(0, 0),
1158 GlyphDelta::optional(0, 0),
1159 GlyphDelta::optional(0, 0),
1160 GlyphDelta::optional(0, 0),
1161 GlyphDelta::optional(0, 0),
1162 GlyphDelta::optional(0, 0),
1163 ],
1164 ),
1165 GlyphDeltas::new(
1166 peaks(vec![
1167 F2Dot14::from_f32(-1.0),
1168 F2Dot14::from_f32(0.0),
1169 F2Dot14::from_f32(1.0),
1170 ]),
1171 vec![
1172 GlyphDelta::required(0, 0),
1173 GlyphDelta::optional(0, 0),
1174 GlyphDelta::optional(0, 0),
1175 GlyphDelta::optional(0, 0),
1176 GlyphDelta::optional(0, 0),
1177 GlyphDelta::optional(0, 0),
1178 ],
1179 ),
1180 ],
1181 );
1182
1183 assert_eq!(
1184 variations.compute_shared_points(),
1185 Some(PackedPointNumbers::Some(vec![1, 3]))
1189 );
1190 }
1191
1192 fn make_31_bytes_of_variation_data() -> Vec<GlyphDeltas> {
1195 vec![
1196 GlyphDeltas::new(
1197 peaks(vec![F2Dot14::from_f32(-1.0), F2Dot14::from_f32(-1.0)]),
1198 vec![
1199 GlyphDelta::optional(0, 0),
1200 GlyphDelta::required(35, 0),
1201 GlyphDelta::optional(0, 0),
1202 GlyphDelta::required(-24, 0),
1203 GlyphDelta::optional(0, 0),
1204 GlyphDelta::optional(0, 0),
1205 ],
1206 ),
1207 GlyphDeltas::new(
1208 peaks(vec![F2Dot14::from_f32(1.0), F2Dot14::from_f32(1.0)]),
1209 vec![
1210 GlyphDelta::optional(0, 0),
1211 GlyphDelta::required(26, 15),
1212 GlyphDelta::optional(0, 0),
1213 GlyphDelta::required(46, 0),
1214 GlyphDelta::optional(0, 0),
1215 GlyphDelta::required(1, 0),
1216 ],
1217 ),
1218 ]
1219 }
1220
1221 #[test]
1223 fn who_tests_the_testers() {
1224 let variations = GlyphVariations::new(GlyphId::NOTDEF, make_31_bytes_of_variation_data());
1225 let mut tupl_map = HashMap::new();
1226
1227 assert_eq!(variations.clone().build(&tupl_map).length, 39);
1229
1230 tupl_map.insert(&variations.variations[0].peak_tuple, 1);
1232 tupl_map.insert(&variations.variations[1].peak_tuple, 2);
1233
1234 let built = variations.clone().build(&tupl_map);
1235 assert_eq!(built.length, 31);
1237 }
1238
1239 fn assert_test_offset_packing(n_glyphs: u16, should_be_short: bool) {
1240 let (offset_len, data_len, expected_flags) = if should_be_short {
1241 (u16::RAW_BYTE_LEN, 32, GvarFlags::empty())
1243 } else {
1244 (u32::RAW_BYTE_LEN, 31, GvarFlags::LONG_OFFSETS)
1245 };
1246
1247 let test_data = make_31_bytes_of_variation_data();
1248 let a_small_number_of_variations = (0..n_glyphs)
1249 .map(|i| GlyphVariations::new(GlyphId::from(i), test_data.clone()))
1250 .collect();
1251
1252 let gvar = Gvar::new(a_small_number_of_variations, 2).unwrap();
1253 assert_eq!(gvar.compute_flags(), expected_flags);
1254
1255 let writer = gvar.compile_variation_data();
1256 let mut sink = TableWriter::default();
1257 writer.write_into(&mut sink);
1258
1259 let bytes = sink.into_data().bytes;
1260 let expected_len = (n_glyphs + 1) as usize * offset_len + 8 + data_len * n_glyphs as usize; assert_eq!(bytes.len(), expected_len);
1264
1265 let dumped = crate::dump_table(&gvar).unwrap();
1266 let loaded = read_fonts::tables::gvar::Gvar::read(FontData::new(&dumped)).unwrap();
1267
1268 assert_eq!(loaded.glyph_count(), n_glyphs);
1269 assert_eq!(loaded.flags(), expected_flags);
1270 assert!(loaded
1271 .glyph_variation_data_offsets()
1272 .iter()
1273 .map(|off| off.unwrap().get())
1274 .enumerate()
1275 .all(|(i, off)| off as usize == i * data_len));
1276 }
1277
1278 #[test]
1279 fn prefer_short_offsets() {
1280 let _ = env_logger::builder().is_test(true).try_init();
1281 assert_test_offset_packing(5, true);
1282 }
1283
1284 #[test]
1285 fn use_long_offsets_when_necessary() {
1286 let _ = env_logger::builder().is_test(true).try_init();
1289 assert_test_offset_packing(4095, true);
1290 assert_test_offset_packing(4096, false);
1291 assert_test_offset_packing(4097, false);
1292 }
1293
1294 #[test]
1295 fn shared_tuples_stable_order() {
1296 let mut variations = Vec::new();
1299 for i in 0..2 {
1300 variations.push(GlyphVariations::new(
1301 GlyphId::new(i),
1302 vec![
1303 GlyphDeltas::new(
1304 peaks(vec![F2Dot14::from_f32(1.0)]),
1305 vec![GlyphDelta::required(10, 20)],
1306 ),
1307 GlyphDeltas::new(
1308 peaks(vec![F2Dot14::from_f32(-1.0)]),
1309 vec![GlyphDelta::required(-10, -20)],
1310 ),
1311 ],
1312 ))
1313 }
1314 for _ in 0..10 {
1315 let table = Gvar::new(variations.clone(), 1).unwrap();
1316 let bytes = crate::dump_table(&table).unwrap();
1317 let gvar = read_fonts::tables::gvar::Gvar::read(FontData::new(&bytes)).unwrap();
1318
1319 assert_eq!(gvar.shared_tuple_count(), 2);
1320 assert_eq!(
1321 gvar.shared_tuples()
1322 .unwrap()
1323 .tuples()
1324 .iter()
1325 .map(|t| t.unwrap().values.to_vec())
1326 .collect::<Vec<_>>(),
1327 vec![vec![F2Dot14::from_f32(1.0)], vec![F2Dot14::from_f32(-1.0)]]
1328 );
1329 }
1330 }
1331
1332 #[test]
1333 fn unexpected_axis_count() {
1334 let variations = GlyphVariations::new(
1335 GlyphId::NOTDEF,
1336 vec![
1337 GlyphDeltas::new(
1338 peaks(vec![F2Dot14::from_f32(1.0)]),
1339 vec![GlyphDelta::required(1, 2)],
1340 ),
1341 GlyphDeltas::new(
1342 peaks(vec![F2Dot14::from_f32(1.0)]),
1343 vec![GlyphDelta::required(1, 2)],
1344 ),
1345 ],
1346 );
1347 let gvar = Gvar::new(vec![variations], 2);
1348 assert!(matches!(
1349 gvar,
1350 Err(GvarInputError::UnexpectedAxisCount {
1351 gid: GlyphId::NOTDEF,
1352 expected: 2,
1353 actual: 1
1354 })
1355 ));
1356 }
1357
1358 #[test]
1359 fn empty_gvar_has_expected_axis_count() {
1360 let variations = GlyphVariations::new(GlyphId::NOTDEF, vec![]);
1361 let gvar = Gvar::new(vec![variations], 2).unwrap();
1362 assert_eq!(gvar.axis_count, 2);
1363 }
1364
1365 #[test]
1366 fn intermediates_only_when_explicit_needed() {
1369 let any_points = vec![]; let deltas = GlyphDeltas::new(
1373 vec![Tent::new(F2Dot14::from_f32(0.5), None)],
1374 any_points.clone(),
1375 );
1376 assert_eq!(deltas.intermediate_region, None);
1377
1378 let deltas = GlyphDeltas::new(
1381 vec![Tent::new(
1382 F2Dot14::from_f32(0.5),
1383 Some(Tent::implied_intermediates_for_peak(F2Dot14::from_f32(0.5))),
1384 )],
1385 any_points.clone(),
1386 );
1387 assert_eq!(deltas.intermediate_region, None);
1388
1389 let deltas = GlyphDeltas::new(
1392 vec![Tent::new(
1393 F2Dot14::from_f32(0.5),
1394 Some((F2Dot14::from_f32(-0.3), F2Dot14::from_f32(0.4))),
1395 )],
1396 any_points.clone(),
1397 );
1398 assert_eq!(
1399 deltas.intermediate_region,
1400 Some((
1401 Tuple::new(vec![F2Dot14::from_f32(-0.3)]),
1402 Tuple::new(vec![F2Dot14::from_f32(0.4)]),
1403 ))
1404 );
1405 }
1406
1407 #[test]
1408 fn intermediates_only_when_at_least_one_needed() {
1411 let any_points = vec![]; let deltas = GlyphDeltas::new(
1415 vec![
1416 Tent::new(F2Dot14::from_f32(0.5), None),
1417 Tent::new(F2Dot14::from_f32(0.5), None),
1418 ],
1419 any_points.clone(),
1420 );
1421 assert_eq!(deltas.intermediate_region, None);
1422
1423 let deltas = GlyphDeltas::new(
1425 vec![
1426 Tent::new(F2Dot14::from_f32(0.5), None),
1427 Tent::new(F2Dot14::from_f32(0.5), None),
1428 Tent::new(
1429 F2Dot14::from_f32(0.5),
1430 Some((F2Dot14::from_f32(-0.3), F2Dot14::from_f32(0.4))),
1431 ),
1432 ],
1433 any_points,
1434 );
1435 assert_eq!(
1436 deltas.intermediate_region,
1437 Some((
1438 Tuple::new(vec![
1439 F2Dot14::from_f32(0.0),
1440 F2Dot14::from_f32(0.0),
1441 F2Dot14::from_f32(-0.3)
1442 ]),
1443 Tuple::new(vec![
1444 F2Dot14::from_f32(0.5),
1445 F2Dot14::from_f32(0.5),
1446 F2Dot14::from_f32(0.4)
1447 ]),
1448 ))
1449 );
1450 }
1451}