1include!("../../generated/generated_gvar.rs");
5
6use super::{
7 glyf::{CompositeGlyphFlags, Glyf, Glyph, PointCoord},
8 loca::Loca,
9 variations::{
10 PackedPointNumbers, Tuple, TupleDelta, TupleVariationCount, TupleVariationData,
11 TupleVariationHeader,
12 },
13};
14
15pub type GlyphVariationData<'a> = TupleVariationData<'a, GlyphDelta>;
17
18#[derive(Clone, Copy, Debug)]
19pub struct U16Or32(u32);
20
21impl ReadArgs for U16Or32 {
22 type Args = GvarFlags;
23}
24
25impl ComputeSize for U16Or32 {
26 fn compute_size(args: &GvarFlags) -> Result<usize, ReadError> {
27 Ok(if args.contains(GvarFlags::LONG_OFFSETS) {
28 4
29 } else {
30 2
31 })
32 }
33}
34
35impl FontReadWithArgs<'_> for U16Or32 {
36 fn read_with_args(data: FontData<'_>, args: &Self::Args) -> Result<Self, ReadError> {
37 if args.contains(GvarFlags::LONG_OFFSETS) {
38 data.read_at::<u32>(0).map(Self)
39 } else {
40 data.read_at::<u16>(0).map(|v| Self(v as u32 * 2))
41 }
42 }
43}
44
45impl U16Or32 {
46 #[inline]
47 pub fn get(self) -> u32 {
48 self.0
49 }
50}
51
52impl<'a> GlyphVariationDataHeader<'a> {
53 fn raw_tuple_header_data(&self) -> FontData<'a> {
54 let range = self.shape.tuple_variation_headers_byte_range();
55 self.data.split_off(range.start).unwrap()
56 }
57}
58
59impl<'a> Gvar<'a> {
60 pub fn data_for_gid(&self, gid: GlyphId) -> Result<Option<FontData<'a>>, ReadError> {
64 let range = self.data_range_for_gid(gid)?;
65 if range.is_empty() {
66 return Ok(None);
67 }
68 match self.data.slice(range) {
69 Some(data) => Ok(Some(data)),
70 None => Err(ReadError::OutOfBounds),
71 }
72 }
73
74 pub fn glyph_variation_data_for_range(
75 &self,
76 offset_range: Range<usize>,
77 ) -> Result<FontData<'a>, ReadError> {
78 let base = self.glyph_variation_data_array_offset() as usize;
79 let start = base
80 .checked_add(offset_range.start)
81 .ok_or(ReadError::OutOfBounds)?;
82 let end = base
83 .checked_add(offset_range.end)
84 .ok_or(ReadError::OutOfBounds)?;
85 self.data.slice(start..end).ok_or(ReadError::OutOfBounds)
86 }
87
88 pub fn as_bytes(&self) -> &[u8] {
89 self.data.as_bytes()
90 }
91
92 fn data_range_for_gid(&self, gid: GlyphId) -> Result<Range<usize>, ReadError> {
93 let start_idx = gid.to_u32() as usize;
94 let end_idx = start_idx + 1;
95 let data_start = self.glyph_variation_data_array_offset();
96 let start =
97 data_start.checked_add(self.glyph_variation_data_offsets().get(start_idx)?.get());
98 let end = data_start.checked_add(self.glyph_variation_data_offsets().get(end_idx)?.get());
99 let (Some(start), Some(end)) = (start, end) else {
100 return Err(ReadError::OutOfBounds);
101 };
102 Ok(start as usize..end as usize)
103 }
104
105 pub fn glyph_variation_data(
110 &self,
111 gid: GlyphId,
112 ) -> Result<Option<GlyphVariationData<'a>>, ReadError> {
113 let shared_tuples = self.shared_tuples()?;
114 let axis_count = self.axis_count();
115 let data = self.data_for_gid(gid)?;
116 data.map(|data| GlyphVariationData::new(data, axis_count, shared_tuples))
117 .transpose()
118 }
119
120 pub fn phantom_point_deltas(
126 &self,
127 glyf: &Glyf,
128 loca: &Loca,
129 coords: &[F2Dot14],
130 glyph_id: GlyphId,
131 ) -> Result<Option<[Point<Fixed>; 4]>, ReadError> {
132 let (glyph_id, point_count) = find_glyph_and_point_count(glyf, loca, glyph_id, 0)?;
143 let mut phantom_deltas = [Point::default(); 4];
144 let phantom_range = point_count..point_count + 4;
145 let Some(var_data) = self.glyph_variation_data(glyph_id)? else {
146 return Ok(None);
147 };
148 for (tuple, scalar) in var_data.active_tuples_at(coords) {
151 for tuple_delta in tuple.deltas() {
152 let ix = tuple_delta.position as usize;
153 if phantom_range.contains(&ix) {
154 phantom_deltas[ix - phantom_range.start] += tuple_delta.apply_scalar(scalar);
155 }
156 }
157 }
158 Ok(Some(phantom_deltas))
159 }
160}
161
162impl<'a> GlyphVariationData<'a> {
163 pub(crate) fn new(
164 data: FontData<'a>,
165 axis_count: u16,
166 shared_tuples: SharedTuples<'a>,
167 ) -> Result<Self, ReadError> {
168 let header = GlyphVariationDataHeader::read(data)?;
169
170 let header_data = header.raw_tuple_header_data();
171 let count = header.tuple_variation_count();
172 let data = header.serialized_data()?;
173
174 let (shared_point_numbers, serialized_data) =
176 if header.tuple_variation_count().shared_point_numbers() {
177 let (packed, data) = PackedPointNumbers::split_off_front(data);
178 (Some(packed), data)
179 } else {
180 (None, data)
181 };
182
183 Ok(GlyphVariationData {
184 tuple_count: count,
185 axis_count,
186 shared_tuples: Some(shared_tuples.tuples()),
187 shared_point_numbers,
188 header_data,
189 serialized_data,
190 _marker: std::marker::PhantomData,
191 })
192 }
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq)]
197pub struct GlyphDelta {
198 pub position: u16,
200 pub x_delta: i32,
202 pub y_delta: i32,
204}
205
206impl GlyphDelta {
207 pub fn apply_scalar<D: PointCoord>(self, scalar: Fixed) -> Point<D> {
209 let scalar = D::from_fixed(scalar);
210 Point::new(self.x_delta, self.y_delta).map(D::from_i32) * scalar
211 }
212}
213
214impl TupleDelta for GlyphDelta {
215 fn is_point() -> bool {
216 true
217 }
218
219 fn new(position: u16, x: i32, y: i32) -> Self {
220 Self {
221 position,
222 x_delta: x,
223 y_delta: y,
224 }
225 }
226}
227
228fn find_glyph_and_point_count(
238 glyf: &Glyf,
239 loca: &Loca,
240 glyph_id: GlyphId,
241 recurse_depth: usize,
242) -> Result<(GlyphId, usize), ReadError> {
243 const RECURSION_LIMIT: usize = 64;
245 if recurse_depth > RECURSION_LIMIT {
246 return Err(ReadError::MalformedData(
247 "nesting too deep in composite glyph",
248 ));
249 }
250 let glyph = loca.get_glyf(glyph_id, glyf)?;
251 let Some(glyph) = glyph else {
252 return Ok((glyph_id, 0));
255 };
256 match glyph {
257 Glyph::Simple(simple) => {
258 Ok((glyph_id, simple.num_points()))
260 }
261 Glyph::Composite(composite) => {
262 let (count, inherit_metrics) = composite.component_glyphs_and_flags().fold(
268 (0, None),
269 |(count, inherit_metrics), (component_id, flags)| {
270 let has_flag = flags.contains(CompositeGlyphFlags::USE_MY_METRICS);
271 let preferred = has_flag.then_some(component_id).or(inherit_metrics);
272
273 (count + 1, preferred)
274 },
275 );
276
277 if let Some(component) = inherit_metrics {
278 find_glyph_and_point_count(glyf, loca, component.into(), recurse_depth + 1)
279 } else {
280 Ok((glyph_id, count))
281 }
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use std::collections::HashMap;
289
290 use font_test_data::bebuffer::BeBuffer;
291
292 use super::*;
293 use crate::{FontRef, TableProvider};
294
295 static SKIA_GVAR_SHARED_TUPLES_DATA: FontData = FontData::new(&[
299 0x40, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xC0,
300 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x40, 0x00, 0x40, 0x00, 0xC0, 0x00,
301 0x40, 0x00,
302 ]);
303
304 static SKIA_GVAR_I_DATA: FontData = FontData::new(&[
305 0x00, 0x08, 0x00, 0x24, 0x00, 0x33, 0x20, 0x00, 0x00, 0x15, 0x20, 0x01, 0x00, 0x1B, 0x20,
306 0x02, 0x00, 0x24, 0x20, 0x03, 0x00, 0x15, 0x20, 0x04, 0x00, 0x26, 0x20, 0x07, 0x00, 0x0D,
307 0x20, 0x06, 0x00, 0x1A, 0x20, 0x05, 0x00, 0x40, 0x01, 0x01, 0x01, 0x81, 0x80, 0x43, 0xFF,
308 0x7E, 0xFF, 0x7E, 0xFF, 0x7E, 0xFF, 0x7E, 0x00, 0x81, 0x45, 0x01, 0x01, 0x01, 0x03, 0x01,
309 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 0x02, 0x80, 0x40, 0x00, 0x82, 0x81, 0x81, 0x04, 0x3A,
310 0x5A, 0x3E, 0x43, 0x20, 0x81, 0x04, 0x0E, 0x40, 0x15, 0x45, 0x7C, 0x83, 0x00, 0x0D, 0x9E,
311 0xF3, 0xF2, 0xF0, 0xF0, 0xF0, 0xF0, 0xF3, 0x9E, 0xA0, 0xA1, 0xA1, 0xA1, 0x9F, 0x80, 0x00,
312 0x91, 0x81, 0x91, 0x00, 0x0D, 0x0A, 0x0A, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
313 0x0A, 0x0A, 0x0A, 0x0B, 0x80, 0x00, 0x15, 0x81, 0x81, 0x00, 0xC4, 0x89, 0x00, 0xC4, 0x83,
314 0x00, 0x0D, 0x80, 0x99, 0x98, 0x96, 0x96, 0x96, 0x96, 0x99, 0x80, 0x82, 0x83, 0x83, 0x83,
315 0x81, 0x80, 0x40, 0xFF, 0x18, 0x81, 0x81, 0x04, 0xE6, 0xF9, 0x10, 0x21, 0x02, 0x81, 0x04,
316 0xE8, 0xE5, 0xEB, 0x4D, 0xDA, 0x83, 0x00, 0x0D, 0xCE, 0xD3, 0xD4, 0xD3, 0xD3, 0xD3, 0xD5,
317 0xD2, 0xCE, 0xCC, 0xCD, 0xCD, 0xCD, 0xCD, 0x80, 0x00, 0xA1, 0x81, 0x91, 0x00, 0x0D, 0x07,
318 0x03, 0x04, 0x02, 0x02, 0x02, 0x03, 0x03, 0x07, 0x07, 0x08, 0x08, 0x08, 0x07, 0x80, 0x00,
319 0x09, 0x81, 0x81, 0x00, 0x28, 0x40, 0x00, 0xA4, 0x02, 0x24, 0x24, 0x66, 0x81, 0x04, 0x08,
320 0xFA, 0xFA, 0xFA, 0x28, 0x83, 0x00, 0x82, 0x02, 0xFF, 0xFF, 0xFF, 0x83, 0x02, 0x01, 0x01,
321 0x01, 0x84, 0x91, 0x00, 0x80, 0x06, 0x07, 0x08, 0x08, 0x08, 0x08, 0x0A, 0x07, 0x80, 0x03,
322 0xFE, 0xFF, 0xFF, 0xFF, 0x81, 0x00, 0x08, 0x81, 0x82, 0x02, 0xEE, 0xEE, 0xEE, 0x8B, 0x6D,
323 0x00,
324 ]);
325
326 #[test]
327 fn test_shared_tuples() {
328 #[allow(overflowing_literals)]
329 const MINUS_ONE: F2Dot14 = F2Dot14::from_bits(0xC000);
330 assert_eq!(MINUS_ONE, F2Dot14::from_f32(-1.0));
331
332 static EXPECTED: &[(F2Dot14, F2Dot14)] = &[
333 (F2Dot14::ONE, F2Dot14::ZERO),
334 (MINUS_ONE, F2Dot14::ZERO),
335 (F2Dot14::ZERO, F2Dot14::ONE),
336 (F2Dot14::ZERO, MINUS_ONE),
337 (MINUS_ONE, MINUS_ONE),
338 (F2Dot14::ONE, MINUS_ONE),
339 (F2Dot14::ONE, F2Dot14::ONE),
340 (MINUS_ONE, F2Dot14::ONE),
341 ];
342
343 const N_AXES: u16 = 2;
344
345 let tuples =
346 SharedTuples::read(SKIA_GVAR_SHARED_TUPLES_DATA, EXPECTED.len() as u16, N_AXES)
347 .unwrap();
348 let tuple_vec: Vec<_> = tuples
349 .tuples()
350 .iter()
351 .map(|tup| {
352 let values = tup.unwrap().values();
353 assert_eq!(values.len(), N_AXES as usize);
354 (values[0].get(), values[1].get())
355 })
356 .collect();
357
358 assert_eq!(tuple_vec, EXPECTED);
359 }
360
361 #[test]
363 fn smoke_test() {
364 let header = GlyphVariationDataHeader::read(SKIA_GVAR_I_DATA).unwrap();
365 assert_eq!(header.serialized_data_offset(), 36);
366 assert_eq!(header.tuple_variation_count().count(), 8);
367 let shared_tuples = SharedTuples::read(SKIA_GVAR_SHARED_TUPLES_DATA, 8, 2).unwrap();
368
369 let vardata = GlyphVariationData::new(SKIA_GVAR_I_DATA, 2, shared_tuples).unwrap();
370 assert_eq!(vardata.tuple_count(), 8);
371 let deltas = vardata
372 .tuples()
373 .next()
374 .unwrap()
375 .deltas()
376 .collect::<Vec<_>>();
377 assert_eq!(deltas.len(), 18);
378 static EXPECTED: &[(i32, i32)] = &[
379 (257, 0),
380 (-127, 0),
381 (-128, 58),
382 (-130, 90),
383 (-130, 62),
384 (-130, 67),
385 (-130, 32),
386 (-127, 0),
387 (257, 0),
388 (259, 14),
389 (260, 64),
390 (260, 21),
391 (260, 69),
392 (258, 124),
393 (0, 0),
394 (130, 0),
395 (0, 0),
396 (0, 0),
397 ];
398 let expected = EXPECTED
399 .iter()
400 .copied()
401 .enumerate()
402 .map(|(pos, (x_delta, y_delta))| GlyphDelta {
403 position: pos as _,
404 x_delta,
405 y_delta,
406 })
407 .collect::<Vec<_>>();
408
409 for (a, b) in deltas.iter().zip(expected.iter()) {
410 assert_eq!(a, b);
411 }
412 }
413
414 #[test]
415 fn vazirmatn_var_a() {
416 let gvar = FontRef::new(font_test_data::VAZIRMATN_VAR)
417 .unwrap()
418 .gvar()
419 .unwrap();
420 let a_glyph_var = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap();
421 assert_eq!(a_glyph_var.axis_count, 1);
422 let mut tuples = a_glyph_var.tuples();
423 let tup1 = tuples.next().unwrap();
424 assert_eq!(tup1.peak().values(), &[F2Dot14::from_f32(-1.0)]);
425 assert_eq!(tup1.deltas().count(), 18);
426 let x_vals = &[
427 -90, -134, 4, -6, -81, 18, -25, -33, -109, -121, -111, -111, -22, -22, 0, -113, 0, 0,
428 ];
429 let y_vals = &[
430 83, 0, 0, 0, 0, 0, 83, 0, 0, 0, -50, 54, 54, -50, 0, 0, -21, 0,
431 ];
432 assert_eq!(tup1.deltas().map(|d| d.x_delta).collect::<Vec<_>>(), x_vals);
433 assert_eq!(tup1.deltas().map(|d| d.y_delta).collect::<Vec<_>>(), y_vals);
434 let tup2 = tuples.next().unwrap();
435 assert_eq!(tup2.peak().values(), &[F2Dot14::from_f32(1.0)]);
436 let x_vals = &[
437 20, 147, -33, -53, 59, -90, 37, -6, 109, 90, -79, -79, -8, -8, 0, 59, 0, 0,
438 ];
439 let y_vals = &[
440 -177, 0, 0, 0, 0, 0, -177, 0, 0, 0, 4, -109, -109, 4, 0, 0, 9, 0,
441 ];
442
443 assert_eq!(tup2.deltas().map(|d| d.x_delta).collect::<Vec<_>>(), x_vals);
444 assert_eq!(tup2.deltas().map(|d| d.y_delta).collect::<Vec<_>>(), y_vals);
445 assert!(tuples.next().is_none());
446 }
447
448 #[test]
449 fn vazirmatn_var_agrave() {
450 let gvar = FontRef::new(font_test_data::VAZIRMATN_VAR)
451 .unwrap()
452 .gvar()
453 .unwrap();
454 let agrave_glyph_var = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap();
455 let mut tuples = agrave_glyph_var.tuples();
456 let tup1 = tuples.next().unwrap();
457 assert_eq!(
458 tup1.deltas()
459 .map(|d| (d.position, d.x_delta, d.y_delta))
460 .collect::<Vec<_>>(),
461 &[(1, -51, 8), (3, -113, 0)]
462 );
463 let tup2 = tuples.next().unwrap();
464 assert_eq!(
465 tup2.deltas()
466 .map(|d| (d.position, d.x_delta, d.y_delta))
467 .collect::<Vec<_>>(),
468 &[(1, -54, -1), (3, 59, 0)]
469 );
470 }
471
472 #[test]
473 fn vazirmatn_var_grave() {
474 let gvar = FontRef::new(font_test_data::VAZIRMATN_VAR)
475 .unwrap()
476 .gvar()
477 .unwrap();
478 let grave_glyph_var = gvar.glyph_variation_data(GlyphId::new(3)).unwrap().unwrap();
479 let mut tuples = grave_glyph_var.tuples();
480 let tup1 = tuples.next().unwrap();
481 let tup2 = tuples.next().unwrap();
482 assert!(tuples.next().is_none());
483 assert_eq!(tup1.deltas().count(), 8);
484 assert_eq!(
485 tup2.deltas().map(|d| d.y_delta).collect::<Vec<_>>(),
486 &[0, -20, -20, 0, 0, 0, 0, 0]
487 );
488 }
489
490 #[test]
491 fn phantom_point_deltas() {
492 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
493 #[rustfmt::skip]
494 let a_cases = [
495 (&[0.0], [(0.0, 0.0); 4]),
497 (&[1.0], [(0.0, 0.0), (59.0, 0.0), (0.0, 9.0), (0.0, 0.0)]),
498 (&[-1.0], [(0.0, 0.0), (-113.0, 0.0), (0.0, -21.0), (0.0, 0.0)]),
499 (&[0.5], [(0.0, 0.0), (29.5, 0.0), (0.0, 4.5), (0.0, 0.0)]),
500 (&[-0.5], [(0.0, 0.0), (-56.5, 0.0), (0.0, -10.5), (0.0, 0.0)]),
501 ];
502 for (coords, deltas) in a_cases {
503 assert_eq!(
505 compute_phantom_deltas(&font, coords, GlyphId::new(1)),
506 deltas
507 );
508 assert_eq!(
511 compute_phantom_deltas(&font, coords, GlyphId::new(2)),
512 deltas
513 );
514 }
515 #[rustfmt::skip]
516 let grave_cases = [
517 (&[0.0], [(0.0, 0.0); 4]),
519 (&[1.0], [(0.0, 0.0), (63.0, 0.0), (0.0, 0.0), (0.0, 0.0)]),
520 (&[-1.0], [(0.0, 0.0), (-96.0, 0.0), (0.0, 0.0), (0.0, 0.0)]),
521 (&[0.5], [(0.0, 0.0), (31.5, 0.0), (0.0, 0.0), (0.0, 0.0)]),
522 (&[-0.5], [(0.0, 0.0), (-48.0, 0.0), (0.0, 0.0), (0.0, 0.0)]),
523 ];
524 for (coords, deltas) in grave_cases {
526 assert_eq!(
527 compute_phantom_deltas(&font, coords, GlyphId::new(3)),
528 deltas
529 );
530 }
531 }
532
533 fn compute_phantom_deltas(
534 font: &FontRef,
535 coords: &[f32],
536 glyph_id: GlyphId,
537 ) -> [(f32, f32); 4] {
538 let loca = font.loca(None).unwrap();
539 let glyf = font.glyf().unwrap();
540 let gvar = font.gvar().unwrap();
541 let coords = coords
542 .iter()
543 .map(|coord| F2Dot14::from_f32(*coord))
544 .collect::<Vec<_>>();
545 gvar.phantom_point_deltas(&glyf, &loca, &coords, glyph_id)
546 .unwrap()
547 .unwrap()
548 .map(|delta| delta.map(Fixed::to_f32))
549 .map(|p| (p.x, p.y))
550 }
551
552 #[test]
555 fn avoid_data_range_overflow() {
556 let mut buf = BeBuffer::new();
559 buf = buf.push(1u16).push(0u16);
561 buf = buf.push(0u16);
563 buf = buf.push(0u16).push(0u32);
565 buf = buf.push(1u16);
567 buf = buf.push(1u16);
569 buf = buf.push(u32::MAX - 10);
571 buf = buf.push(0u32).push(11u32);
573 let gvar = Gvar::read(buf.data().into()).unwrap();
574 let _ = gvar.data_range_for_gid(GlyphId::new(0));
576 }
577
578 #[test]
581 fn follow_use_my_metrics() {
582 let font = FontRef::new(font_test_data::gvar::USE_MY_METRICS).unwrap();
584 let glyf = font.glyf().unwrap();
585 let loca = font.loca(None).unwrap();
586 let post = font.post().unwrap();
587
588 let gids = (0..post.num_glyphs().unwrap())
590 .map(GlyphId16::new)
591 .map(|gid| (post.glyph_name(gid).unwrap(), gid))
592 .collect::<HashMap<_, _>>();
593
594 let (source, _) =
596 find_glyph_and_point_count(&glyf, &loca, gids["neither"].into(), 5).unwrap();
597 assert_eq!(
598 source, gids["neither"],
599 "a composite without any USE_MY_METRICS components should use its own metrics"
600 );
601
602 let (source, _) =
603 find_glyph_and_point_count(&glyf, &loca, gids["firstonly"].into(), 5).unwrap();
604 assert_eq!(
605 source, gids["first"],
606 "a composite with a single USE_MY_METRICS component should use that component's metrics"
607 );
608
609 let (source, _) = find_glyph_and_point_count(&glyf, &loca, gids["both"].into(), 5).unwrap();
610 assert_eq!(
611 source, gids["second"],
612 "a composite with multiple USE_MY_METRICS components should use the last flagged component's metrics"
613 );
614 }
615}