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