//! OpenType font variations common tables. include!("../../generated/generated_variations.rs"); use super::{ glyf::{PointCoord, PointFlags, PointMarker}, gvar::GlyphDelta, }; pub const NO_VARIATION_INDEX: u32 = 0xFFFFFFFF; /// Outer and inner indices for reading from an [ItemVariationStore]. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeltaSetIndex { /// Outer delta set index. pub outer: u16, /// Inner delta set index. pub inner: u16, } impl DeltaSetIndex { pub const NO_VARIATION_INDEX: Self = Self { outer: (NO_VARIATION_INDEX >> 16) as u16, inner: (NO_VARIATION_INDEX & 0xFFFF) as u16, }; } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TupleIndex(u16); impl TupleIndex { /// Flag indicating that this tuple variation header includes an embedded /// peak tuple record, immediately after the tupleIndex field. /// /// If set, the low 12 bits of the tupleIndex value are ignored. /// /// Note that this must always be set within the 'cvar' table. pub const EMBEDDED_PEAK_TUPLE: u16 = 0x8000; /// Flag indicating that this tuple variation table applies to an /// intermediate region within the variation space. /// /// If set, the header includes the two intermediate-region, start and end /// tuple records, immediately after the peak tuple record (if present). pub const INTERMEDIATE_REGION: u16 = 0x4000; /// Flag indicating that the serialized data for this tuple variation table /// includes packed “point” number data. /// /// If set, this tuple variation table uses that number data; if clear, /// this tuple variation table uses shared number data found at the start /// of the serialized data for this glyph variation data or 'cvar' table. pub const PRIVATE_POINT_NUMBERS: u16 = 0x2000; //0x1000 Reserved Reserved for future use — set to 0. // /// Mask for the low 12 bits to give the shared tuple records index. pub const TUPLE_INDEX_MASK: u16 = 0x0FFF; #[inline(always)] fn tuple_len(self, axis_count: u16, flag: usize) -> usize { if flag == 0 { self.embedded_peak_tuple() as usize * axis_count as usize } else { self.intermediate_region() as usize * axis_count as usize } } pub fn bits(self) -> u16 { self.0 } pub fn from_bits(bits: u16) -> Self { TupleIndex(bits) } /// `true` if the header includes an embedded peak tuple. pub fn embedded_peak_tuple(self) -> bool { (self.0 & Self::EMBEDDED_PEAK_TUPLE) != 0 } /// `true` if the header includes the two intermediate region tuple records. pub fn intermediate_region(self) -> bool { (self.0 & Self::INTERMEDIATE_REGION) != 0 } /// `true` if the data for this table includes packed point number data. pub fn private_point_numbers(self) -> bool { (self.0 & Self::PRIVATE_POINT_NUMBERS) != 0 } pub fn tuple_records_index(self) -> Option { (!self.embedded_peak_tuple()).then_some(self.0 & Self::TUPLE_INDEX_MASK) } } impl types::Scalar for TupleIndex { type Raw = ::Raw; fn to_raw(self) -> Self::Raw { self.0.to_raw() } fn from_raw(raw: Self::Raw) -> Self { let t = ::from_raw(raw); Self(t) } } /// The 'tupleVariationCount' field of the [Tuple Variation Store Header][header] /// /// The high 4 bits are flags, and the low 12 bits are the number of tuple /// variation tables for this glyph. The count can be any number between 1 and 4095. /// /// [header]: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuple-variation-store-header #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TupleVariationCount(u16); impl TupleVariationCount { /// Flag indicating that some or all tuple variation tables reference a /// shared set of “point” numbers. /// /// These shared numbers are represented as packed point number data at the /// start of the serialized data. pub const SHARED_POINT_NUMBERS: u16 = 0x8000; /// Mask for the low 12 bits to give the shared tuple records index. pub const COUNT_MASK: u16 = 0x0FFF; pub fn bits(self) -> u16 { self.0 } pub fn from_bits(bits: u16) -> Self { Self(bits) } /// `true` if any tables reference a shared set of point numbers pub fn shared_point_numbers(self) -> bool { (self.0 & Self::SHARED_POINT_NUMBERS) != 0 } pub fn count(self) -> u16 { self.0 & Self::COUNT_MASK } } impl types::Scalar for TupleVariationCount { type Raw = ::Raw; fn to_raw(self) -> Self::Raw { self.0.to_raw() } fn from_raw(raw: Self::Raw) -> Self { let t = ::from_raw(raw); Self(t) } } impl<'a> TupleVariationHeader<'a> { #[cfg(feature = "experimental_traverse")] fn traverse_tuple_index(&self) -> traversal::FieldType<'a> { self.tuple_index().0.into() } /// Peak tuple record for this tuple variation table — optional, /// determined by flags in the tupleIndex value. Note that this /// must always be included in the 'cvar' table. #[inline(always)] pub fn peak_tuple(&self) -> Option> { self.tuple_index().embedded_peak_tuple().then(|| { let range = self.peak_tuple_byte_range(); Tuple { values: self.data.read_array(range).unwrap(), } }) } /// Intermediate start tuple record for this tuple variation table /// — optional, determined by flags in the tupleIndex value. #[inline(always)] pub fn intermediate_start_tuple(&self) -> Option> { self.tuple_index().intermediate_region().then(|| { let range = self.intermediate_start_tuple_byte_range(); Tuple { values: self.data.read_array(range).unwrap(), } }) } /// Intermediate end tuple record for this tuple variation table /// — optional, determined by flags in the tupleIndex value. #[inline(always)] pub fn intermediate_end_tuple(&self) -> Option> { self.tuple_index().intermediate_region().then(|| { let range = self.intermediate_end_tuple_byte_range(); Tuple { values: self.data.read_array(range).unwrap(), } }) } /// Intermediate tuple records for this tuple variation table /// — optional, determined by flags in the tupleIndex value. #[inline(always)] pub fn intermediate_tuples(&self) -> Option<(Tuple<'a>, Tuple<'a>)> { self.tuple_index().intermediate_region().then(|| { let start_range = self.intermediate_start_tuple_byte_range(); let end_range = self.intermediate_end_tuple_byte_range(); ( Tuple { values: self.data.read_array(start_range).unwrap(), }, Tuple { values: self.data.read_array(end_range).unwrap(), }, ) }) } /// Compute the actual length of this table in bytes #[inline(always)] fn byte_len(&self, axis_count: u16) -> usize { const FIXED_LEN: usize = u16::RAW_BYTE_LEN + TupleIndex::RAW_BYTE_LEN; let tuple_byte_len = F2Dot14::RAW_BYTE_LEN * axis_count as usize; let index = self.tuple_index(); FIXED_LEN + if index.embedded_peak_tuple() { tuple_byte_len } else { Default::default() } + if index.intermediate_region() { tuple_byte_len * 2 } else { Default::default() } } } impl Tuple<'_> { pub fn len(&self) -> usize { self.values().len() } pub fn is_empty(&self) -> bool { self.values.is_empty() } #[inline(always)] pub fn get(&self, idx: usize) -> Option { self.values.get(idx).map(BigEndian::get) } } //FIXME: add an #[extra_traits(..)] attribute! #[allow(clippy::derivable_impls)] impl Default for Tuple<'_> { fn default() -> Self { Self { values: Default::default(), } } } /// [Packed "Point" Numbers](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-point-numbers) #[derive(Clone, Default, Debug)] pub struct PackedPointNumbers<'a> { data: FontData<'a>, } impl<'a> PackedPointNumbers<'a> { /// read point numbers off the front of this data, returning the remaining data pub fn split_off_front(data: FontData<'a>) -> (Self, FontData<'a>) { let this = PackedPointNumbers { data }; let total_len = this.total_len(); let remainder = data.split_off(total_len).unwrap_or_default(); (this, remainder) } /// The number of points in this set pub fn count(&self) -> u16 { self.count_and_count_bytes().0 } /// compute the count, and the number of bytes used to store it fn count_and_count_bytes(&self) -> (u16, usize) { match self.data.read_at::(0).unwrap_or(0) { 0 => (0, 1), count @ 1..=127 => (count as u16, 1), _ => { // "If the high bit of the first byte is set, then a second byte is used. // The count is read from interpreting the two bytes as a big-endian // uint16 value with the high-order bit masked out." let count = self.data.read_at::(0).unwrap_or_default() & 0x7FFF; // a weird case where I'm following fonttools: if the 'use words' bit // is set, but the total count is still 0, treat it like 0 first byte if count == 0 { (0, 2) } else { (count & 0x7FFF, 2) } } } } /// the number of bytes to encode the packed point numbers #[inline(never)] fn total_len(&self) -> usize { let (n_points, mut n_bytes) = self.count_and_count_bytes(); if n_points == 0 { return n_bytes; } let mut cursor = self.data.cursor(); cursor.advance_by(n_bytes); let mut n_seen = 0; while n_seen < n_points { let Some((count, two_bytes)) = read_control_byte(&mut cursor) else { return n_bytes; }; let word_size = 1 + usize::from(two_bytes); let run_size = word_size * count as usize; n_bytes += run_size + 1; // plus the control byte; cursor.advance_by(run_size); n_seen += count as u16; } n_bytes } /// Iterate over the packed points pub fn iter(&self) -> PackedPointNumbersIter<'a> { let (count, n_bytes) = self.count_and_count_bytes(); let mut cursor = self.data.cursor(); cursor.advance_by(n_bytes); PackedPointNumbersIter::new(count, cursor) } } /// An iterator over the packed point numbers data. #[derive(Clone, Debug)] pub struct PackedPointNumbersIter<'a> { count: u16, seen: u16, last_val: u16, current_run: PointRunIter<'a>, } impl<'a> PackedPointNumbersIter<'a> { fn new(count: u16, cursor: Cursor<'a>) -> Self { PackedPointNumbersIter { count, seen: 0, last_val: 0, current_run: PointRunIter { remaining: 0, two_bytes: false, cursor, }, } } } /// Implements the logic for iterating over the individual runs #[derive(Clone, Debug)] struct PointRunIter<'a> { remaining: u8, two_bytes: bool, cursor: Cursor<'a>, } impl Iterator for PointRunIter<'_> { type Item = u16; fn next(&mut self) -> Option { // if no items remain in this run, start the next one. while self.remaining == 0 { (self.remaining, self.two_bytes) = read_control_byte(&mut self.cursor)?; } self.remaining -= 1; if self.two_bytes { self.cursor.read().ok() } else { self.cursor.read::().ok().map(|v| v as u16) } } } /// returns the count and the 'uses_two_bytes' flag from the control byte fn read_control_byte(cursor: &mut Cursor) -> Option<(u8, bool)> { let control: u8 = cursor.read().ok()?; let two_bytes = (control & 0x80) != 0; let count = (control & 0x7F) + 1; Some((count, two_bytes)) } impl Iterator for PackedPointNumbersIter<'_> { type Item = u16; fn next(&mut self) -> Option { // if our count is zero, we keep incrementing forever if self.count == 0 { let result = self.last_val; self.last_val = self.last_val.checked_add(1)?; return Some(result); } if self.count == self.seen { return None; } self.seen += 1; self.last_val = self.last_val.checked_add(self.current_run.next()?)?; Some(self.last_val) } fn size_hint(&self) -> (usize, Option) { (self.count as usize, Some(self.count as usize)) } } // completely unnecessary? impl ExactSizeIterator for PackedPointNumbersIter<'_> {} /// [Packed Deltas](https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas) #[derive(Clone, Debug)] pub struct PackedDeltas<'a> { data: FontData<'a>, // How many values we expect count: Option, } impl<'a> PackedDeltas<'a> { pub(crate) fn new(data: FontData<'a>, count: usize) -> Self { Self { data, count: Some(count), } } /// NOTE: this is unbounded, and assumes all of data is deltas. #[doc(hidden)] // used by tests in write-fonts pub fn consume_all(data: FontData<'a>) -> Self { Self { data, count: None } } pub fn count(&self) -> Option { self.count } pub fn count_or_compute(&self) -> usize { self.count.unwrap_or_else(|| count_all_deltas(self.data)) } pub fn iter(&self) -> DeltaRunIter<'a> { DeltaRunIter::new(self.data.cursor(), self.count) } pub fn fetcher(&self) -> PackedDeltaFetcher<'a> { PackedDeltaFetcher::new(self.data.as_bytes(), self.count) } fn x_deltas(&self) -> DeltaRunIter<'a> { let count = self.count_or_compute() / 2; DeltaRunIter::new(self.data.cursor(), Some(count)) } fn y_deltas(&self) -> DeltaRunIter<'a> { let count = self.count_or_compute(); DeltaRunIter::new(self.data.cursor(), Some(count)).skip_fast(count / 2) } } /// Flag indicating that this run contains no data, /// and that the deltas for this run are all zero. const DELTAS_ARE_ZERO: u8 = 0x80; /// Flag indicating the data type for delta values in the run. const DELTAS_ARE_WORDS: u8 = 0x40; /// Mask for the low 6 bits to provide the number of delta values in the run, minus one. const DELTA_RUN_COUNT_MASK: u8 = 0x3F; /// The type of values for a given delta run (influences the number of bytes per delta) /// /// The variants are intentionally set to the byte size of the type to allow usage /// as a multiplier when computing offsets. #[derive(Clone, Copy, Debug, PartialEq)] pub enum DeltaRunType { Zero = 0, I8 = 1, I16 = 2, I32 = 4, } impl DeltaRunType { /// The run type for a given control byte pub fn new(control: u8) -> Self { // if the top two bits of the control byte (DELTAS_ARE_ZERO and DELTAS_ARE_WORDS) are both set, // then the following values are 32-bit. // let are_zero = (control & DELTAS_ARE_ZERO) != 0; let are_words = (control & DELTAS_ARE_WORDS) != 0; match (are_zero, are_words) { (false, false) => Self::I8, (false, true) => Self::I16, (true, false) => Self::Zero, (true, true) => Self::I32, } } } /// Implements the logic for iterating over the individual runs #[derive(Clone, Debug)] pub struct DeltaRunIter<'a> { limit: Option, // when None, consume all available data remaining_in_run: u8, value_type: DeltaRunType, cursor: Cursor<'a>, } /// A decoding helper that adds packed deltas directly to an output slice. pub struct PackedDeltaFetcher<'a> { data: &'a [u8], pos: usize, end: usize, run_count: usize, value_type: DeltaRunType, remaining_total: Option, } impl<'a> PackedDeltaFetcher<'a> { fn new(data: &'a [u8], count: Option) -> Self { Self { data, pos: 0, end: data.len(), run_count: 0, value_type: DeltaRunType::I8, remaining_total: count, } } #[inline(always)] fn ensure_run(&mut self) -> Result<(), ReadError> { if self.run_count > 0 { return Ok(()); } if self.pos >= self.end { return Err(ReadError::OutOfBounds); } let control = self.data[self.pos]; self.pos += 1; self.run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1; self.value_type = DeltaRunType::new(control); let width = self.value_type as usize; let needed = self.run_count * width; if self.pos + needed > self.end { return Err(ReadError::OutOfBounds); } Ok(()) } pub fn skip(&mut self, mut n: usize) -> Result<(), ReadError> { if let Some(remaining_total) = self.remaining_total { if n > remaining_total { return Err(ReadError::OutOfBounds); } self.remaining_total = Some(remaining_total - n); } while n > 0 { self.ensure_run()?; let take = n.min(self.run_count); let width = self.value_type as usize; self.pos += take * width; self.run_count -= take; n -= take; } Ok(()) } pub fn add_to_f32_scaled(&mut self, out: &mut [f32], scale: f32) -> Result<(), ReadError> { let mut remaining = out.len(); if let Some(remaining_total) = self.remaining_total { if remaining > remaining_total { return Err(ReadError::OutOfBounds); } self.remaining_total = Some(remaining_total - remaining); } let mut idx = 0usize; while remaining > 0 { self.ensure_run()?; let take = remaining.min(self.run_count); match self.value_type { DeltaRunType::Zero => { // nothing to add idx += take; } DeltaRunType::I8 => { let bytes = &self.data[self.pos..self.pos + take]; for &b in bytes { out[idx] += b as i8 as f32 * scale; idx += 1; } self.pos += take; } DeltaRunType::I16 => { let bytes = &self.data[self.pos..self.pos + take * 2]; for chunk in bytes.chunks_exact(2) { let delta = i16::from_be_bytes([chunk[0], chunk[1]]) as f32; out[idx] += delta * scale; idx += 1; } self.pos += take * 2; } DeltaRunType::I32 => { let bytes = &self.data[self.pos..self.pos + take * 4]; for chunk in bytes.chunks_exact(4) { let delta = i32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) as f32; out[idx] += delta * scale; idx += 1; } self.pos += take * 4; } } self.run_count -= take; remaining -= take; } Ok(()) } } /// Counts the number of deltas available in the given data, avoiding /// excessive reads. fn count_all_deltas(data: FontData) -> usize { let mut count = 0; let mut offset = 0; while let Ok(control) = data.read_at::(offset) { let run_count = (control & DELTA_RUN_COUNT_MASK) as usize + 1; count += run_count; offset += run_count * DeltaRunType::new(control) as usize + 1; } count } impl<'a> DeltaRunIter<'a> { fn new(cursor: Cursor<'a>, limit: Option) -> Self { DeltaRunIter { limit, remaining_in_run: 0, value_type: DeltaRunType::I8, cursor, } } pub(crate) fn end(mut self) -> Cursor<'a> { if let Some(limit) = self.limit { return self.skip_fast(limit).cursor; } // No limit: jump over runs without decoding values. if self.remaining_in_run != 0 { if self.value_type != DeltaRunType::Zero { self.cursor .advance_by(self.remaining_in_run as usize * self.value_type as usize); } self.remaining_in_run = 0; } while self.read_next_control().is_some() { if self.value_type != DeltaRunType::Zero { self.cursor .advance_by(self.remaining_in_run as usize * self.value_type as usize); } self.remaining_in_run = 0; } self.cursor } /// Skips `n` deltas without reading the actual delta values. #[inline(always)] pub fn skip_fast(mut self, n: usize) -> Self { let mut wanted = n; let mut remaining = self.remaining_in_run as usize; let mut value_type = self.value_type; loop { if wanted > remaining { // Consume the rest of this run and move to the next. self.cursor.advance_by(remaining * value_type as usize); wanted -= remaining; if self.read_next_control().is_none() { self.limit = Some(0); break; } remaining = self.remaining_in_run as usize; value_type = self.value_type; continue; } let consumed = wanted; self.remaining_in_run -= consumed as u8; self.cursor.advance_by(consumed * value_type as usize); if let Some(limit) = self.limit.as_mut() { *limit = limit.saturating_sub(n); } break; } self } #[inline(always)] fn read_next_control(&mut self) -> Option<()> { self.remaining_in_run = 0; let control: u8 = self.cursor.read().ok()?; self.value_type = DeltaRunType::new(control); self.remaining_in_run = (control & DELTA_RUN_COUNT_MASK) + 1; Some(()) } } impl Iterator for DeltaRunIter<'_> { type Item = i32; #[inline(always)] fn next(&mut self) -> Option { if let Some(limit) = self.limit { if limit == 0 { return None; } self.limit = Some(limit - 1); } if self.remaining_in_run == 0 { self.read_next_control()?; } self.remaining_in_run -= 1; match self.value_type { DeltaRunType::Zero => Some(0), DeltaRunType::I8 => self.cursor.read::().ok().map(|v| v as i32), DeltaRunType::I16 => self.cursor.read::().ok().map(|v| v as i32), DeltaRunType::I32 => self.cursor.read::().ok(), } } } /// A helper type for iterating over [`TupleVariationHeader`]s. pub struct TupleVariationHeaderIter<'a> { data: FontData<'a>, n_headers: usize, current: usize, axis_count: u16, } impl<'a> TupleVariationHeaderIter<'a> { pub(crate) fn new(data: FontData<'a>, n_headers: usize, axis_count: u16) -> Self { Self { data, n_headers, current: 0, axis_count, } } } impl<'a> Iterator for TupleVariationHeaderIter<'a> { type Item = Result, ReadError>; #[inline(always)] fn next(&mut self) -> Option { if self.current == self.n_headers { return None; } self.current += 1; let next = TupleVariationHeader::read(self.data, self.axis_count); let next_len = next .as_ref() .map(|table| table.byte_len(self.axis_count)) .unwrap_or(0); self.data = self.data.split_off(next_len)?; Some(next) } } #[derive(Clone)] pub struct TupleVariationData<'a, T> { pub(crate) axis_count: u16, pub(crate) shared_tuples: Option>>, pub(crate) shared_point_numbers: Option>, pub(crate) tuple_count: TupleVariationCount, // the data for all the tuple variation headers pub(crate) header_data: FontData<'a>, // the data for all the tuple bodies pub(crate) serialized_data: FontData<'a>, pub(crate) _marker: std::marker::PhantomData T>, } impl<'a, T> TupleVariationData<'a, T> where T: TupleDelta, { pub fn tuples(&self) -> TupleVariationIter<'a, T> { TupleVariationIter { current: 0, parent: self.clone(), header_iter: TupleVariationHeaderIter::new( self.header_data, self.tuple_count.count() as usize, self.axis_count, ), serialized_data: self.serialized_data, _marker: std::marker::PhantomData, } } /// Returns an iterator over all of the pairs of (variation tuple, scalar) /// for this glyph that are active for the given set of normalized /// coordinates. pub fn active_tuples_at( &self, coords: &'a [F2Dot14], ) -> impl Iterator, Fixed)> + 'a { ActiveTupleVariationIter { coords, parent: self.clone(), header_iter: TupleVariationHeaderIter::new( self.header_data, self.tuple_count.count() as usize, self.axis_count, ), serialized_data: self.serialized_data, data_offset: 0, _marker: std::marker::PhantomData, } } pub(crate) fn tuple_count(&self) -> usize { self.tuple_count.count() as usize } } /// An iterator over the [`TupleVariation`]s for a specific glyph. pub struct TupleVariationIter<'a, T> { current: usize, parent: TupleVariationData<'a, T>, header_iter: TupleVariationHeaderIter<'a>, serialized_data: FontData<'a>, _marker: std::marker::PhantomData T>, } impl<'a, T> TupleVariationIter<'a, T> where T: TupleDelta, { #[inline(always)] fn next_tuple(&mut self) -> Option> { if self.parent.tuple_count() == self.current { return None; } self.current += 1; // FIXME: is it okay to discard an error here? let header = self.header_iter.next()?.ok()?; let data_len = header.variation_data_size() as usize; let var_data = self.serialized_data.take_up_to(data_len)?; Some(TupleVariation { axis_count: self.parent.axis_count, header, shared_tuples: self.parent.shared_tuples.clone(), serialized_data: var_data, shared_point_numbers: self.parent.shared_point_numbers.clone(), _marker: std::marker::PhantomData, }) } } impl<'a, T> Iterator for TupleVariationIter<'a, T> where T: TupleDelta, { type Item = TupleVariation<'a, T>; #[inline(always)] fn next(&mut self) -> Option { self.next_tuple() } } /// An iterator over the active [`TupleVariation`]s for a specific glyph /// for a given set of coordinates. struct ActiveTupleVariationIter<'a, T> { coords: &'a [F2Dot14], parent: TupleVariationData<'a, T>, header_iter: TupleVariationHeaderIter<'a>, serialized_data: FontData<'a>, data_offset: usize, _marker: std::marker::PhantomData T>, } impl<'a, T> Iterator for ActiveTupleVariationIter<'a, T> where T: TupleDelta, { type Item = (TupleVariation<'a, T>, Fixed); #[inline(always)] fn next(&mut self) -> Option { loop { let header = self.header_iter.next()?.ok()?; let data_len = header.variation_data_size() as usize; let data_start = self.data_offset; let data_end = data_start.checked_add(data_len)?; self.data_offset = data_end; if let Some(scalar) = compute_scalar( &header, self.parent.axis_count as usize, &self.parent.shared_tuples, self.coords, ) { let var_data = self.serialized_data.slice(data_start..data_end)?; return Some(( TupleVariation { axis_count: self.parent.axis_count, header, shared_tuples: self.parent.shared_tuples.clone(), serialized_data: var_data, shared_point_numbers: self.parent.shared_point_numbers.clone(), _marker: std::marker::PhantomData, }, scalar, )); } } } } /// A single set of tuple variation data #[derive(Clone)] pub struct TupleVariation<'a, T> { axis_count: u16, header: TupleVariationHeader<'a>, shared_tuples: Option>>, serialized_data: FontData<'a>, shared_point_numbers: Option>, _marker: std::marker::PhantomData T>, } impl<'a, T> TupleVariation<'a, T> where T: TupleDelta, { /// Returns true if this tuple provides deltas for all points in a glyph. pub fn has_deltas_for_all_points(&self) -> bool { if self.header.tuple_index().private_point_numbers() { PackedPointNumbers { data: self.serialized_data, } .count() == 0 } else if let Some(shared) = &self.shared_point_numbers { shared.count() == 0 } else { false } } pub fn point_numbers(&self) -> PackedPointNumbersIter<'a> { let (point_numbers, _) = self.point_numbers_and_packed_deltas(); point_numbers.iter() } /// Returns the 'peak' tuple for this variation pub fn peak(&self) -> Tuple<'a> { self.header .tuple_index() .tuple_records_index() .and_then(|idx| self.shared_tuples.as_ref()?.get(idx as usize).ok()) .or_else(|| self.header.peak_tuple()) .unwrap_or_default() } pub fn intermediate_start(&self) -> Option> { self.header.intermediate_start_tuple() } pub fn intermediate_end(&self) -> Option> { self.header.intermediate_end_tuple() } /// Compute the fixed point scalar for this tuple at the given location in /// variation space. /// /// The `coords` slice must be of lesser or equal length to the number of /// axes. If it is less, missing (trailing) axes will be assumed to have /// zero values. /// /// Returns `None` if this tuple is not applicable at the provided /// coordinates (e.g. if the resulting scalar is zero). pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Option { compute_scalar( &self.header, self.axis_count as usize, &self.shared_tuples, coords, ) } /// Compute the floating point scalar for this tuple at the given location /// in variation space. /// /// The `coords` slice must be of lesser or equal length to the number of /// axes. If it is less, missing (trailing) axes will be assumed to have /// zero values. /// /// Returns `None` if this tuple is not applicable at the provided /// coordinates (e.g. if the resulting scalar is zero). pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> Option { let mut scalar = 1.0; let peak = self.peak(); let inter_start = self.header.intermediate_start_tuple(); let inter_end = self.header.intermediate_end_tuple(); if peak.len() != self.axis_count as usize { return None; } for i in 0..self.axis_count { let i = i as usize; let coord = coords.get(i).copied().unwrap_or_default().to_bits() as i32; let peak = peak.get(i).unwrap_or_default().to_bits() as i32; if peak == 0 || peak == coord { continue; } if coord == 0 { return None; } if let (Some(inter_start), Some(inter_end)) = (&inter_start, &inter_end) { let start = inter_start.get(i).unwrap_or_default().to_bits() as i32; let end = inter_end.get(i).unwrap_or_default().to_bits() as i32; if start > peak || peak > end || (start < 0 && end > 0 && peak != 0) { continue; } if coord < start || coord > end { return None; } if coord < peak { if peak != start { scalar *= (coord - start) as f32 / (peak - start) as f32; } } else if peak != end { scalar *= (end - coord) as f32 / (end - peak) as f32; } } else { if coord < peak.min(0) || coord > peak.max(0) { return None; } scalar *= coord as f32 / peak as f32; } } Some(scalar) } /// Iterate over the deltas for this tuple. /// /// This does not account for scaling. Returns only explicitly encoded /// deltas, e.g. an omission by IUP will not be present. pub fn deltas(&self) -> TupleDeltaIter<'a, T> { let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas(); let count = point_numbers.count() as usize; let packed_deltas = if count == 0 { PackedDeltas::consume_all(packed_deltas) } else { PackedDeltas::new(packed_deltas, if T::is_point() { count * 2 } else { count }) }; TupleDeltaIter::new(&point_numbers, packed_deltas) } fn point_numbers_and_packed_deltas(&self) -> (PackedPointNumbers<'a>, FontData<'a>) { if self.header.tuple_index().private_point_numbers() { PackedPointNumbers::split_off_front(self.serialized_data) } else { ( self.shared_point_numbers.clone().unwrap_or_default(), self.serialized_data, ) } } } impl TupleVariation<'_, GlyphDelta> { /// Reads the set of deltas from this tuple variation. /// /// This is significantly faster than using the [`Self::deltas`] /// method but requires preallocated memory to store deltas and /// flags. /// /// This method should only be used when the tuple variation is dense, /// that is, [`Self::has_deltas_for_all_points`] returns true. /// /// The size of `deltas` must be the same as the target value set to /// which the variation is applied. For simple outlines, this is /// `num_points + 4` and for composites it is `num_components + 4` /// (where the `+ 4` is to accommodate phantom points). /// /// The `deltas` slice will not be zeroed before accumulation and each /// delta will be multiplied by the given `scalar`. pub fn accumulate_dense_deltas( &self, deltas: &mut [Point], scalar: Fixed, ) -> Result<(), ReadError> { let (_, packed_deltas) = self.point_numbers_and_packed_deltas(); let mut cursor = packed_deltas.cursor(); if scalar == Fixed::ONE { // scalar of 1.0 is common so avoid the costly conversions and // multiplications per coord read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { delta.x += D::from_i32(new_delta); })?; read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { delta.y += D::from_i32(new_delta); })?; } else { read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); })?; read_dense_deltas(&mut cursor, deltas, |delta, new_delta| { delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); })?; } Ok(()) } /// Reads the set of deltas from this tuple variation. /// /// This is significantly faster than using the [`Self::deltas`] /// method but requires preallocated memory to store deltas and /// flags. /// /// This method should only be used when the tuple variation is sparse, /// that is, [`Self::has_deltas_for_all_points`] returns false. /// /// The size of `deltas` must be the same as the target value set to /// which the variation is applied. For simple outlines, this is /// `num_points + 4` and for composites it is `num_components + 4` /// (where the `+ 4` is to accommodate phantom points). /// /// The `deltas` and `flags` slices must be the same size. Modifications /// to `deltas` will be sparse and for each entry that is modified, the /// [PointMarker::HAS_DELTA] marker will be set for the corresponding /// entry in the `flags` slice. /// /// The `deltas` slice will not be zeroed before accumulation and each /// delta will be multiplied by the given `scalar`. pub fn accumulate_sparse_deltas( &self, deltas: &mut [Point], flags: &mut [PointFlags], scalar: Fixed, ) -> Result<(), ReadError> { let (point_numbers, packed_deltas) = self.point_numbers_and_packed_deltas(); let mut cursor = packed_deltas.cursor(); let count = point_numbers.count() as usize; if scalar == Fixed::ONE { // scalar of 1.0 is common so avoid the costly conversions and // multiplications per coord read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { delta.x += D::from_i32(new_delta); flag.set_marker(PointMarker::HAS_DELTA); } })?; read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { if let Some(delta) = deltas.get_mut(ix) { delta.y += D::from_i32(new_delta); } })?; } else { read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { if let Some((delta, flag)) = deltas.get_mut(ix).zip(flags.get_mut(ix)) { delta.x += D::from_fixed(Fixed::from_i32(new_delta) * scalar); flag.set_marker(PointMarker::HAS_DELTA); } })?; read_sparse_deltas(&mut cursor, &point_numbers, count, |ix, new_delta| { if let Some(delta) = deltas.get_mut(ix) { delta.y += D::from_fixed(Fixed::from_i32(new_delta) * scalar); } })?; } Ok(()) } } /// This is basically a manually applied loop unswitching optimization /// for reading deltas. It reads each typed run into a slice for processing /// instead of handling each delta individually with all the necessary /// branching that implies. fn read_dense_deltas( cursor: &mut Cursor, deltas: &mut [T], mut f: impl FnMut(&mut T, i32), ) -> Result<(), ReadError> { let count = deltas.len(); let mut cur = 0; while cur < count { let control: u8 = cursor.read()?; let value_type = DeltaRunType::new(control); let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; let dest = deltas .get_mut(cur..cur + run_count) .ok_or(ReadError::OutOfBounds)?; match value_type { DeltaRunType::Zero => {} DeltaRunType::I8 => { let packed_deltas = cursor.read_array::(run_count)?; for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { f(delta, *new_delta as i32); } } DeltaRunType::I16 => { let packed_deltas = cursor.read_array::>(run_count)?; for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { f(delta, new_delta.get() as i32); } } DeltaRunType::I32 => { let packed_deltas = cursor.read_array::>(run_count)?; for (delta, new_delta) in dest.iter_mut().zip(packed_deltas) { f(delta, new_delta.get()); } } } cur += run_count; } Ok(()) } /// See [read_dense_deltas] docs. fn read_sparse_deltas( cursor: &mut Cursor, point_numbers: &PackedPointNumbers, count: usize, mut f: impl FnMut(usize, i32), ) -> Result<(), ReadError> { let mut cur = 0; let mut points_iter = point_numbers.iter().map(|ix| ix as usize); while cur < count { let control: u8 = cursor.read()?; let value_type = DeltaRunType::new(control); let run_count = ((control & DELTA_RUN_COUNT_MASK) + 1) as usize; match value_type { DeltaRunType::Zero => { for _ in 0..run_count { let point_ix = points_iter.next().ok_or(ReadError::OutOfBounds)?; f(point_ix, 0); } } DeltaRunType::I8 => { let packed_deltas = cursor.read_array::(run_count)?; for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { f(point_ix, *new_delta as i32); } } DeltaRunType::I16 => { let packed_deltas = cursor.read_array::>(run_count)?; for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { f(point_ix, new_delta.get() as i32); } } DeltaRunType::I32 => { let packed_deltas = cursor.read_array::>(run_count)?; for (new_delta, point_ix) in packed_deltas.iter().zip(points_iter.by_ref()) { f(point_ix, new_delta.get()); } } } cur += run_count; } Ok(()) } /// Compute the fixed point scalar for this tuple at the given location in /// variation space. /// /// The `coords` slice must be of lesser or equal length to the number of /// axes. If it is less, missing (trailing) axes will be assumed to have /// zero values. /// /// Returns `None` if this tuple is not applicable at the provided /// coordinates (e.g. if the resulting scalar is zero). #[inline(always)] fn compute_scalar<'a>( header: &TupleVariationHeader, axis_count: usize, shared_tuples: &Option>>, coords: &[F2Dot14], ) -> Option { let mut scalar = Fixed::ONE; let tuple_idx = header.tuple_index(); let peak = if let Some(shared_index) = tuple_idx.tuple_records_index() { shared_tuples.as_ref()?.get(shared_index as usize).ok()? } else { header.peak_tuple()? }; if peak.len() != axis_count { return None; } let intermediate = header.intermediate_tuples(); for (i, peak) in peak .values .iter() .enumerate() .filter(|(_, peak)| peak.get() != F2Dot14::ZERO) { let coord = coords.get(i).copied().unwrap_or_default(); if coord == F2Dot14::ZERO { return None; } let peak = peak.get(); if peak == coord { continue; } if let Some((inter_start, inter_end)) = &intermediate { let start = inter_start.get(i).unwrap_or_default(); let end = inter_end.get(i).unwrap_or_default(); if coord <= start || coord >= end { return None; } let coord = coord.to_fixed(); let peak = peak.to_fixed(); if coord < peak { let start = start.to_fixed(); scalar = scalar.mul_div(coord - start, peak - start); } else { let end = end.to_fixed(); scalar = scalar.mul_div(end - coord, end - peak); } } else { if coord < peak.min(F2Dot14::ZERO) || coord > peak.max(F2Dot14::ZERO) { return None; } let coord = coord.to_fixed(); let peak = peak.to_fixed(); scalar = scalar.mul_div(coord, peak); } } (scalar != Fixed::ZERO).then_some(scalar) } #[derive(Clone, Debug)] enum TupleDeltaValues<'a> { // Point deltas have separate runs for x and y coordinates. Points(DeltaRunIter<'a>, DeltaRunIter<'a>), Scalars(DeltaRunIter<'a>), } /// An iterator over the deltas for a glyph. #[derive(Clone, Debug)] pub struct TupleDeltaIter<'a, T> { pub cur: usize, // if None all points get deltas, if Some specifies subset of points that do points: Option>, next_point: usize, values: TupleDeltaValues<'a>, _marker: std::marker::PhantomData T>, } impl<'a, T> TupleDeltaIter<'a, T> where T: TupleDelta, { fn new(points: &PackedPointNumbers<'a>, deltas: PackedDeltas<'a>) -> TupleDeltaIter<'a, T> { let mut points = points.iter(); let next_point = points.next(); let values = if T::is_point() { TupleDeltaValues::Points(deltas.x_deltas(), deltas.y_deltas()) } else { TupleDeltaValues::Scalars(deltas.iter()) }; TupleDeltaIter { cur: 0, points: next_point.map(|_| points), next_point: next_point.unwrap_or_default() as usize, values, _marker: std::marker::PhantomData, } } } /// Trait for deltas that are computed in a tuple variation store. pub trait TupleDelta: Sized + Copy + 'static { /// Returns true if the delta is a point and requires reading two values /// from the packed delta stream. fn is_point() -> bool; /// Creates a new delta for the given position and coordinates. If /// the delta is not a point, the y value will always be zero. fn new(position: u16, x: i32, y: i32) -> Self; } impl Iterator for TupleDeltaIter<'_, T> where T: TupleDelta, { type Item = T; fn next(&mut self) -> Option { let (position, dx, dy) = loop { let position = if let Some(points) = &mut self.points { // if we have points then result is sparse; only some points have deltas if self.cur > self.next_point { self.next_point = points.next()? as usize; } self.next_point } else { // no points, every point has a delta. Just take the next one. self.cur }; if position == self.cur { let (dx, dy) = match &mut self.values { TupleDeltaValues::Points(x, y) => (x.next()?, y.next()?), TupleDeltaValues::Scalars(scalars) => (scalars.next()?, 0), }; break (position, dx, dy); } self.cur += 1; }; self.cur += 1; Some(T::new(position as u16, dx, dy)) } } impl EntryFormat { pub fn entry_size(self) -> u8 { ((self.bits() & Self::MAP_ENTRY_SIZE_MASK.bits()) >> 4) + 1 } pub fn bit_count(self) -> u8 { (self.bits() & Self::INNER_INDEX_BIT_COUNT_MASK.bits()) + 1 } // called from codegen pub(crate) fn map_size(self, map_count: impl Into) -> usize { self.entry_size() as usize * map_count.into() as usize } } impl DeltaSetIndexMap<'_> { /// Returns the delta set index for the specified value. pub fn get(&self, index: u32) -> Result { let (entry_format, map_count, data) = match self { Self::Format0(fmt) => (fmt.entry_format(), fmt.map_count() as u32, fmt.map_data()), Self::Format1(fmt) => (fmt.entry_format(), fmt.map_count(), fmt.map_data()), }; if map_count == 0 { return Ok(DeltaSetIndex { outer: (index >> 16) as u16, inner: index as u16, }); } let entry_size = entry_format.entry_size(); let data = FontData::new(data); // "if an index into the mapping array is used that is greater than or equal to // mapCount, then the last logical entry of the mapping array is used." // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats // #associating-target-items-to-variation-data let index = index.min(map_count.saturating_sub(1)); let offset = index as usize * entry_size as usize; let entry = match entry_size { 1 => data.read_at::(offset)? as u32, 2 => data.read_at::(offset)? as u32, 3 => data.read_at::(offset)?.into(), 4 => data.read_at::(offset)?, _ => { return Err(ReadError::MalformedData( "invalid entry size in DeltaSetIndexMap", )) } }; let bit_count = entry_format.bit_count(); Ok(DeltaSetIndex { outer: (entry >> bit_count) as u16, inner: (entry & ((1 << bit_count) - 1)) as u16, }) } } impl ItemVariationStore<'_> { /// Computes the delta value for the specified index and set of normalized /// variation coordinates. pub fn compute_delta( &self, index: DeltaSetIndex, coords: &[F2Dot14], ) -> Result { if coords.is_empty() || index == DeltaSetIndex::NO_VARIATION_INDEX { return Ok(0); } let data = match self.item_variation_data().get(index.outer as usize) { Some(data) => data?, None => return Ok(0), }; let regions = self.variation_region_list()?.variation_regions(); let region_indices = data.region_indexes(); // Compute deltas with 64-bit precision. // See let mut accum = 0i64; for (i, region_delta) in data.delta_set(index.inner).enumerate() { let region_index = region_indices .get(i) .ok_or(ReadError::MalformedData( "invalid delta sets in ItemVariationStore", ))? .get() as usize; let region = regions.get(region_index)?; let scalar = region.compute_scalar(coords); accum += region_delta as i64 * scalar.to_bits() as i64; } Ok(((accum + 0x8000) >> 16) as i32) } /// Computes the delta value in floating point for the specified index and set /// of normalized variation coordinates. pub fn compute_float_delta( &self, index: DeltaSetIndex, coords: &[F2Dot14], ) -> Result { if coords.is_empty() { return Ok(FloatItemDelta::ZERO); } let data = match self.item_variation_data().get(index.outer as usize) { Some(data) => data?, None => return Ok(FloatItemDelta::ZERO), }; let regions = self.variation_region_list()?.variation_regions(); let region_indices = data.region_indexes(); // Compute deltas in 64-bit floating point. let mut accum = 0f64; for (i, region_delta) in data.delta_set(index.inner).enumerate() { let region_index = region_indices .get(i) .ok_or(ReadError::MalformedData( "invalid delta sets in ItemVariationStore", ))? .get() as usize; let region = regions.get(region_index)?; let scalar = region.compute_scalar_f32(coords); accum += region_delta as f64 * scalar as f64; } Ok(FloatItemDelta(accum)) } } /// Floating point item delta computed by an item variation store. /// /// These can be applied to types that implement [`FloatItemDeltaTarget`]. #[derive(Copy, Clone, Default, Debug)] pub struct FloatItemDelta(f64); impl FloatItemDelta { pub const ZERO: Self = Self(0.0); /// Returns the (unrounded) delta value as a 64-bit float. pub fn to_f64(self) -> f64 { self.0 } } /// Trait for applying floating point item deltas to target values. pub trait FloatItemDeltaTarget { fn apply_float_delta(&self, delta: FloatItemDelta) -> f32; } impl FloatItemDeltaTarget for Fixed { fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 { const FIXED_TO_FLOAT: f64 = 1.0 / 65536.0; self.to_f32() + (delta.0 * FIXED_TO_FLOAT) as f32 } } impl FloatItemDeltaTarget for FWord { fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 { self.to_i16() as f32 + delta.0 as f32 } } impl FloatItemDeltaTarget for UfWord { fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 { self.to_u16() as f32 + delta.0 as f32 } } impl FloatItemDeltaTarget for F2Dot14 { fn apply_float_delta(&self, delta: FloatItemDelta) -> f32 { const F2DOT14_TO_FLOAT: f64 = 1.0 / 16384.0; self.to_f32() + (delta.0 * F2DOT14_TO_FLOAT) as f32 } } impl<'a> VariationRegion<'a> { /// Computes a scalar value for this region and the specified /// normalized variation coordinates. pub fn compute_scalar(&self, coords: &[F2Dot14]) -> Fixed { const ZERO: Fixed = Fixed::ZERO; let mut scalar = Fixed::ONE; for (i, peak, axis_coords) in self.active_region_axes() { let peak = peak.to_fixed(); let start = axis_coords.start_coord.get().to_fixed(); let end = axis_coords.end_coord.get().to_fixed(); if start > peak || peak > end || start < ZERO && end > ZERO { continue; } let coord = coords.get(i).map(|coord| coord.to_fixed()).unwrap_or(ZERO); if coord < start || coord > end { return ZERO; } else if coord == peak { continue; } else if coord < peak { scalar = scalar.mul_div(coord - start, peak - start); } else { scalar = scalar.mul_div(end - coord, end - peak); } } scalar } /// Computes a floating point scalar value for this region and the /// specified normalized variation coordinates. pub fn compute_scalar_f32(&self, coords: &[F2Dot14]) -> f32 { let mut scalar = 1.0; for (i, peak, axis_coords) in self.active_region_axes() { let peak = peak.to_f32(); let start = axis_coords.start_coord.get().to_f32(); let end = axis_coords.end_coord.get().to_f32(); if start > peak || peak > end || start < 0.0 && end > 0.0 { continue; } let coord = coords.get(i).map(|coord| coord.to_f32()).unwrap_or(0.0); if coord < start || coord > end { return 0.0; } else if coord == peak { continue; } else if coord < peak { scalar = (scalar * (coord - start)) / (peak - start); } else { scalar = (scalar * (end - coord)) / (end - peak); } } scalar } fn active_region_axes( &self, ) -> impl Iterator { self.region_axes() .iter() .enumerate() .filter_map(|(i, axis_coords)| { let peak = axis_coords.peak_coord(); if peak != F2Dot14::ZERO { Some((i, peak, axis_coords)) } else { None } }) } } impl<'a> ItemVariationData<'a> { /// Returns an iterator over the per-region delta values for the specified /// inner index. pub fn delta_set(&self, inner_index: u16) -> impl Iterator + 'a + Clone { let word_delta_count = self.word_delta_count(); let region_count = self.region_index_count(); let bytes_per_row = Self::delta_row_len(word_delta_count, region_count); let long_words = word_delta_count & 0x8000 != 0; let word_delta_count = word_delta_count & 0x7FFF; let offset = bytes_per_row * inner_index as usize; ItemDeltas { cursor: FontData::new(self.delta_sets()) .slice(offset..) .unwrap_or_default() .cursor(), word_delta_count, long_words, len: region_count, pos: 0, } } pub fn get_delta_row_len(&self) -> usize { let word_delta_count = self.word_delta_count(); let region_count = self.region_index_count(); Self::delta_row_len(word_delta_count, region_count) } /// the length of one delta set pub fn delta_row_len(word_delta_count: u16, region_index_count: u16) -> usize { let region_count = region_index_count as usize; let long_words = word_delta_count & 0x8000 != 0; let (word_size, small_size) = if long_words { (4, 2) } else { (2, 1) }; let long_delta_count = (word_delta_count & 0x7FFF) as usize; let short_delta_count = region_count.saturating_sub(long_delta_count); long_delta_count * word_size + short_delta_count * small_size } // called from generated code: compute the length in bytes of the delta_sets data pub fn delta_sets_len( item_count: u16, word_delta_count: u16, region_index_count: u16, ) -> usize { let bytes_per_row = Self::delta_row_len(word_delta_count, region_index_count); bytes_per_row * item_count as usize } } #[derive(Clone)] struct ItemDeltas<'a> { cursor: Cursor<'a>, word_delta_count: u16, long_words: bool, len: u16, pos: u16, } impl Iterator for ItemDeltas<'_> { type Item = i32; fn next(&mut self) -> Option { if self.pos >= self.len { return None; } let pos = self.pos; self.pos += 1; let value = match (pos >= self.word_delta_count, self.long_words) { (true, true) | (false, false) => self.cursor.read::().ok()? as i32, (true, false) => self.cursor.read::().ok()? as i32, (false, true) => self.cursor.read::().ok()?, }; Some(value) } } pub(crate) fn advance_delta( dsim: Option>, ivs: Result, glyph_id: GlyphId, coords: &[F2Dot14], ) -> Result { if coords.is_empty() { return Ok(Fixed::ZERO); } let gid = glyph_id.to_u32(); let ix = match dsim { Some(Ok(dsim)) => dsim.get(gid)?, _ => DeltaSetIndex { outer: 0, inner: gid as _, }, }; Ok(Fixed::from_i32(ivs?.compute_delta(ix, coords)?)) } pub(crate) fn item_delta( dsim: Option>, ivs: Result, glyph_id: GlyphId, coords: &[F2Dot14], ) -> Result { if coords.is_empty() { return Ok(Fixed::ZERO); } let gid = glyph_id.to_u32(); let ix = match dsim { Some(Ok(dsim)) => dsim.get(gid)?, _ => return Err(ReadError::NullOffset), }; Ok(Fixed::from_i32(ivs?.compute_delta(ix, coords)?)) } #[cfg(test)] mod tests { use font_test_data::bebuffer::BeBuffer; use super::*; use crate::{FontRef, TableProvider}; #[test] fn ivs_regions() { let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap(); let hvar = font.hvar().expect("missing HVAR table"); let ivs = hvar .item_variation_store() .expect("missing item variation store in HVAR"); let region_list = ivs.variation_region_list().expect("missing region list!"); let regions = region_list.variation_regions(); let expected = &[ // start_coord, peak_coord, end_coord vec![[-1.0f32, -1.0, 0.0]], vec![[0.0, 1.0, 1.0]], ][..]; let region_coords = regions .iter() .map(|region| { region .unwrap() .region_axes() .iter() .map(|coords| { [ coords.start_coord().to_f32(), coords.peak_coord().to_f32(), coords.end_coord().to_f32(), ] }) .collect::>() }) .collect::>(); assert_eq!(expected, ®ion_coords); } // adapted from https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Tests/ttLib/tables/TupleVariation_test.py#L492 #[test] fn packed_points() { fn decode_points(bytes: &[u8]) -> Option> { let data = FontData::new(bytes); let packed = PackedPointNumbers { data }; if packed.count() == 0 { None } else { Some(packed.iter().collect()) } } assert_eq!(decode_points(&[0]), None); // all points in glyph (in overly verbose encoding, not explicitly prohibited by spec) assert_eq!(decode_points(&[0x80, 0]), None); // 2 points; first run: [9, 9+6] assert_eq!(decode_points(&[0x02, 0x01, 0x09, 0x06]), Some(vec![9, 15])); // 2 points; first run: [0xBEEF, 0xCAFE]. (0x0C0F = 0xCAFE - 0xBEEF) assert_eq!( decode_points(&[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f]), Some(vec![0xbeef, 0xcafe]) ); // 1 point; first run: [7] assert_eq!(decode_points(&[0x01, 0, 0x07]), Some(vec![7])); // 1 point; first run: [7] in overly verbose encoding assert_eq!(decode_points(&[0x01, 0x80, 0, 0x07]), Some(vec![7])); // 1 point; first run: [65535]; requires words to be treated as unsigned numbers assert_eq!(decode_points(&[0x01, 0x80, 0xff, 0xff]), Some(vec![65535])); // 4 points; first run: [7, 8]; second run: [255, 257]. 257 is stored in delta-encoded bytes (0xFF + 2). assert_eq!( decode_points(&[0x04, 1, 7, 1, 1, 0xff, 2]), Some(vec![7, 8, 263, 265]) ); } #[test] fn packed_point_byte_len() { fn count_bytes(bytes: &[u8]) -> usize { let packed = PackedPointNumbers { data: FontData::new(bytes), }; packed.total_len() } static CASES: &[&[u8]] = &[ &[0], &[0x80, 0], &[0x02, 0x01, 0x09, 0x06], &[0x02, 0x81, 0xbe, 0xef, 0x0c, 0x0f], &[0x01, 0, 0x07], &[0x01, 0x80, 0, 0x07], &[0x01, 0x80, 0xff, 0xff], &[0x04, 1, 7, 1, 1, 0xff, 2], ]; for case in CASES { assert_eq!(count_bytes(case), case.len(), "{case:?}"); } } // https://github.com/fonttools/fonttools/blob/c30a6355ffdf7f09d31e7719975b4b59bac410af/Tests/ttLib/tables/TupleVariation_test.py#L670 #[test] fn packed_deltas() { static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); let deltas = PackedDeltas::consume_all(INPUT); assert_eq!(deltas.count_or_compute(), 7); assert_eq!( deltas.iter().collect::>(), &[0, 0, 0, 0, 258, -127, -128] ); assert_eq!( PackedDeltas::consume_all(FontData::new(&[0x81])) .iter() .collect::>(), &[0, 0,] ); } // https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#packed-deltas #[test] fn packed_deltas_spec() { static INPUT: FontData = FontData::new(&[ 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, ]); static EXPECTED: &[i32] = &[10, -105, 0, -58, 0, 0, 0, 0, 0, 0, 0, 0, 4130, -1228]; let deltas = PackedDeltas::consume_all(INPUT); assert_eq!(deltas.count_or_compute(), EXPECTED.len()); assert_eq!(deltas.iter().collect::>(), EXPECTED); } #[test] fn packed_delta_fetcher_skip_matches_iterator_suffix() { static INPUT: FontData = FontData::new(&[ 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, ]); let deltas = PackedDeltas::consume_all(INPUT); let expected = deltas.iter().collect::>(); for skip in 0..=expected.len() { let mut fetcher = deltas.fetcher(); fetcher.skip(skip).unwrap(); let mut out = vec![0.0; expected.len() - skip]; fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap(); let got = out.into_iter().map(|v| v as i32).collect::>(); assert_eq!(&got[..], &expected[skip..], "skip={skip}"); } let mut fetcher = deltas.fetcher(); assert!(matches!( fetcher.skip(expected.len() + 1), Err(ReadError::OutOfBounds) )); } #[test] fn packed_delta_fetcher_scaled_add_and_exhaustion() { static INPUT: FontData = FontData::new(&[ 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, ]); // First four deltas are [10, -105, 0, -58]. let deltas = PackedDeltas::new(INPUT, 4); let mut fetcher = deltas.fetcher(); let mut out = [1.0f32; 4]; fetcher.add_to_f32_scaled(&mut out, 0.5).unwrap(); assert_eq!(out, [6.0, -51.5, 1.0, -28.0]); // Bounded fetcher should now be exhausted. let mut extra = [0.0f32; 1]; assert!(matches!( fetcher.add_to_f32_scaled(&mut extra, 1.0), Err(ReadError::OutOfBounds) )); } #[test] fn packed_delta_fetcher_skip_then_add_bounded() { static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); // Full decoded stream: [0, 0, 0, 0, 258, -127, -128] let deltas = PackedDeltas::new(INPUT, 7); let mut fetcher = deltas.fetcher(); fetcher.skip(3).unwrap(); let mut out = [0.0f32; 4]; fetcher.add_to_f32_scaled(&mut out, 1.0).unwrap(); assert_eq!(out, [0.0, 258.0, -127.0, -128.0]); } #[test] fn delta_run_iter_end_exhausts_unbounded_data() { static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); let deltas = PackedDeltas::consume_all(INPUT); let end = deltas.iter().end(); assert_eq!(end.remaining_bytes(), 0); } #[test] fn delta_run_iter_end_respects_bounded_count() { static INPUT: FontData = FontData::new(&[0x83, 0x40, 0x01, 0x02, 0x01, 0x81, 0x80]); // Count is exactly the first run only (4 zeros), so end() should not consume past // the run header byte. let deltas = PackedDeltas::new(INPUT, 4); let end = deltas.iter().end(); assert_eq!(end.remaining_bytes(), INPUT.len() - 1); let end_via_skip = deltas.iter().skip_fast(4).cursor; assert_eq!(end_via_skip.remaining_bytes(), INPUT.len() - 1); } #[test] fn delta_run_iter_end_matches_manual_iteration_for_bounded_data() { static INPUT: FontData = FontData::new(&[ 0x03, 0x0A, 0x97, 0x00, 0xC6, 0x87, 0x41, 0x10, 0x22, 0xFB, 0x34, ]); let deltas = PackedDeltas::new(INPUT, 6); let iter_collected = deltas.iter().collect::>(); assert_eq!(iter_collected.len(), 6); let end = deltas.iter().end(); let end_via_skip = deltas.iter().skip_fast(6).cursor; assert_eq!(end.remaining_bytes(), end_via_skip.remaining_bytes()); } fn lcg_next(state: &mut u32) -> u32 { *state = state.wrapping_mul(1664525).wrapping_add(1013904223); *state } fn generated_delta_stream(seed: u32) -> (Vec, Vec) { let mut state = seed; let mut bytes = Vec::new(); let mut expected = Vec::new(); let run_count = (lcg_next(&mut state) % 6 + 1) as usize; for _ in 0..run_count { let run_type = (lcg_next(&mut state) % 4) as usize; let len = (lcg_next(&mut state) % 8 + 1) as usize; let control = match run_type { 0 => (len - 1) as u8, // i8 1 => 0x40 | (len - 1) as u8, // i16 2 => 0x80 | (len - 1) as u8, // zero _ => 0xC0 | (len - 1) as u8, // i32 }; bytes.push(control); match run_type { 0 => { for _ in 0..len { let v = ((lcg_next(&mut state) % 255) as i32 - 127) as i8; bytes.push(v as u8); expected.push(v as i32); } } 1 => { for _ in 0..len { let v = ((lcg_next(&mut state) % 65535) as i32 - 32767) as i16; bytes.extend(v.to_be_bytes()); expected.push(v as i32); } } 2 => { expected.resize(expected.len() + len, 0); } _ => { for _ in 0..len { let v = (lcg_next(&mut state) % 2_000_001) as i32 - 1_000_000; bytes.extend(v.to_be_bytes()); expected.push(v); } } } } (bytes, expected) } #[test] fn generated_packed_deltas_iter_matches_expected() { for seed in 1..=64 { let (bytes, expected) = generated_delta_stream(seed); let data = FontData::new(&bytes); let deltas = PackedDeltas::consume_all(data); assert_eq!(deltas.count_or_compute(), expected.len(), "seed={seed}"); assert_eq!(deltas.iter().collect::>(), expected, "seed={seed}"); } } #[test] fn generated_fetcher_skip_scaled_matches_expected() { for seed in 1..=64 { let (bytes, expected) = generated_delta_stream(seed); let data = FontData::new(&bytes); let deltas = PackedDeltas::new(data, expected.len()); let mut fetcher = deltas.fetcher(); let skip = (seed as usize * 7) % (expected.len() + 1); fetcher.skip(skip).unwrap(); let scale = if seed % 2 == 0 { 0.25 } else { -0.5 }; let mut out = vec![10.0f32; expected.len() - skip]; fetcher.add_to_f32_scaled(&mut out, scale).unwrap(); for (i, got) in out.iter().copied().enumerate() { let want = 10.0 + expected[skip + i] as f32 * scale; assert!( (got - want).abs() <= 1e-6, "seed={seed} i={i} got={got} want={want}" ); } // Bounded fetcher should be exhausted after consuming all remaining entries. let mut extra = [0.0f32; 1]; assert!(matches!( fetcher.add_to_f32_scaled(&mut extra, 1.0), Err(ReadError::OutOfBounds) )); } } #[test] fn packed_point_split() { static INPUT: FontData = FontData::new(&[2, 1, 1, 2, 1, 205, 143, 1, 8, 0, 1, 202, 59, 1, 255, 0]); let (points, data) = PackedPointNumbers::split_off_front(INPUT); assert_eq!(points.count(), 2); assert_eq!(points.iter().collect::>(), &[1, 3]); assert_eq!(points.total_len(), 4); assert_eq!(data.len(), INPUT.len() - 4); } #[test] fn packed_points_dont_panic() { // a single '0' byte means that there are deltas for all points static ALL_POINTS: FontData = FontData::new(&[0]); let (all_points, _) = PackedPointNumbers::split_off_front(ALL_POINTS); // in which case the iterator just keeps incrementing until u16::MAX assert_eq!(all_points.iter().count(), u16::MAX as usize); } /// Test that we split properly when the coordinate boundary doesn't align /// with a packed run boundary #[test] fn packed_delta_run_crosses_coord_boundary() { // 8 deltas with values 0..=7 with a run broken after the first 6; the // coordinate boundary occurs after the first 4 static INPUT: FontData = FontData::new(&[ // first run: 6 deltas as bytes 5, 0, 1, 2, 3, // coordinate boundary is here 4, 5, // second run: 2 deltas as words 1 | DELTAS_ARE_WORDS, 0, 6, 0, 7, ]); let deltas = PackedDeltas::consume_all(INPUT); assert_eq!(deltas.count_or_compute(), 8); let x_deltas = deltas.x_deltas().collect::>(); let y_deltas = deltas.y_deltas().collect::>(); assert_eq!(x_deltas, [0, 1, 2, 3]); assert_eq!(y_deltas, [4, 5, 6, 7]); } /// We don't have a reference for our float delta computation, so this is /// a sanity test to ensure that floating point deltas are within a /// reasonable margin of the same in fixed point. #[test] fn ivs_float_deltas_nearly_match_fixed_deltas() { let font = FontRef::new(font_test_data::COLRV0V1_VARIABLE).unwrap(); let axis_count = font.fvar().unwrap().axis_count() as usize; let colr = font.colr().unwrap(); let ivs = colr.item_variation_store().unwrap().unwrap(); // Generate a set of coords from -1 to 1 in 0.1 increments for coord in (0..=20).map(|x| F2Dot14::from_f32((x as f32) / 10.0 - 1.0)) { // For testing purposes, just splat the coord to all axes let coords = vec![coord; axis_count]; for (outer_ix, data) in ivs.item_variation_data().iter().enumerate() { let outer_ix = outer_ix as u16; let Some(Ok(data)) = data else { continue; }; for inner_ix in 0..data.item_count() { let delta_ix = DeltaSetIndex { outer: outer_ix, inner: inner_ix, }; // Check the deltas against all possible target values let orig_delta = ivs.compute_delta(delta_ix, &coords).unwrap(); let float_delta = ivs.compute_float_delta(delta_ix, &coords).unwrap(); // For font unit types, we need to accept both rounding and // truncation to account for the additional accumulation of // fractional bits in floating point assert!( orig_delta == float_delta.0.round() as i32 || orig_delta == float_delta.0.trunc() as i32 ); // For the fixed point types, check with an epsilon const EPSILON: f32 = 1e12; let fixed_delta = Fixed::ZERO.apply_float_delta(float_delta); assert!((Fixed::from_bits(orig_delta).to_f32() - fixed_delta).abs() < EPSILON); let f2dot14_delta = F2Dot14::ZERO.apply_float_delta(float_delta); assert!( (F2Dot14::from_bits(orig_delta as i16).to_f32() - f2dot14_delta).abs() < EPSILON ); } } } } #[test] fn ivs_data_len_short() { let data = BeBuffer::new() .push(2u16) // item_count .push(3u16) // word_delta_count .push(5u16) // region_index_count .extend([0u16, 1, 2, 3, 4]) // region_indices .extend([1u8; 128]); // this is much more data than we need! let ivs = ItemVariationData::read(data.data().into()).unwrap(); let row_len = (3 * u16::RAW_BYTE_LEN) + (2 * u8::RAW_BYTE_LEN); // 3 word deltas, 2 byte deltas let expected_len = 2 * row_len; assert_eq!(ivs.delta_sets().len(), expected_len); } #[test] fn ivs_data_len_long() { let data = BeBuffer::new() .push(2u16) // item_count .push(2u16 | 0x8000) // word_delta_count, long deltas .push(4u16) // region_index_count .extend([0u16, 1, 2]) // region_indices .extend([1u8; 128]); // this is much more data than we need! let ivs = ItemVariationData::read(data.data().into()).unwrap(); let row_len = (2 * u32::RAW_BYTE_LEN) + (2 * u16::RAW_BYTE_LEN); // 1 word (4-byte) delta, 2 short (2-byte) let expected_len = 2 * row_len; assert_eq!(ivs.delta_sets().len(), expected_len); } // Add with overflow when accumulating packed point numbers // https://issues.oss-fuzz.com/issues/378159154 #[test] fn packed_point_numbers_avoid_overflow() { // Lots of 1 bits triggers the behavior quite nicely let buf = vec![0xFF; 0xFFFF]; let iter = PackedPointNumbersIter::new(0xFFFF, FontData::new(&buf).cursor()); // Don't panic! let _ = iter.count(); } // Dense accumulator should match iterator #[test] fn accumulate_dense() { let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap(); let gvar = font.gvar().unwrap(); let gvar_data = gvar.glyph_variation_data(GlyphId::new(1)).unwrap().unwrap(); let mut count = 0; for tuple in gvar_data.tuples() { if !tuple.has_deltas_for_all_points() { continue; } let iter_deltas = tuple .deltas() .map(|delta| (delta.x_delta, delta.y_delta)) .collect::>(); let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); iter_deltas.len()]; tuple .accumulate_dense_deltas(&mut delta_buf, Fixed::ONE) .unwrap(); let accum_deltas = delta_buf .iter() .map(|delta| (delta.x.to_i32(), delta.y.to_i32())) .collect::>(); assert_eq!(iter_deltas, accum_deltas); count += iter_deltas.len(); } assert!(count != 0); } // Sparse accumulator should match iterator #[test] fn accumulate_sparse() { let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap(); let gvar = font.gvar().unwrap(); let gvar_data = gvar.glyph_variation_data(GlyphId::new(2)).unwrap().unwrap(); let mut count = 0; for tuple in gvar_data.tuples() { if tuple.has_deltas_for_all_points() { continue; } let iter_deltas = tuple.deltas().collect::>(); let max_modified_point = iter_deltas .iter() .max_by_key(|delta| delta.position) .unwrap() .position as usize; let mut delta_buf = vec![Point::broadcast(Fixed::ZERO); max_modified_point + 1]; let mut flags = vec![PointFlags::default(); delta_buf.len()]; tuple .accumulate_sparse_deltas(&mut delta_buf, &mut flags, Fixed::ONE) .unwrap(); let mut accum_deltas = vec![]; for (i, (delta, flag)) in delta_buf.iter().zip(flags).enumerate() { if flag.has_marker(PointMarker::HAS_DELTA) { accum_deltas.push(GlyphDelta::new( i as u16, delta.x.to_i32(), delta.y.to_i32(), )); } } assert_eq!(iter_deltas, accum_deltas); count += iter_deltas.len(); } assert!(count != 0); } #[test] fn delta_set_index_map_empty_is_identity() { let data = BeBuffer::new() .push(0u8) // format 0 .push(EntryFormat::empty()) .push(0u16); // map_count let map = DeltaSetIndexMap::read(data.data().into()).unwrap(); assert_eq!( map.get(0x0001_0002).unwrap(), DeltaSetIndex { outer: 1, inner: 2 } ); } }