//! Operand stack for CFF/CFF2 parsing. use super::{super::error::Error, blend::BlendState}; use types::Fixed; /// Maximum size of the operand stack. /// /// "Operators in Top DICT, Font DICTs, Private DICTs and CharStrings may be /// preceded by up to a maximum of 513 operands." /// /// const MAX_STACK: usize = 513; /// Operand stack for DICTs and charstrings. /// /// The operand stack can contain either 32-bit integers or 16.16 fixed point /// values. The type is known when pushing to the stack and the expected type /// is also known (based on the operator) when reading from the stack, so the /// conversion is performed on demand at read time. /// /// Storing the entries as an enum would require 8 bytes each and since these /// objects are created on the _stack_, we reduce the required size by storing /// the entries in parallel arrays holding the raw 32-bit value and a flag that /// tracks which values are fixed point. pub struct Stack { values: [i32; MAX_STACK], value_is_fixed: [bool; MAX_STACK], top: usize, } impl Stack { pub fn new() -> Self { Self { values: [0; MAX_STACK], value_is_fixed: [false; MAX_STACK], top: 0, } } pub fn is_empty(&self) -> bool { self.top == 0 } pub fn len(&self) -> usize { self.top } pub fn verify_exact_len(&self, len: usize) -> Result<(), Error> { if self.top != len { Err(Error::StackUnderflow) } else { Ok(()) } } pub fn verify_at_least_len(&self, len: usize) -> Result<(), Error> { if self.top < len { Err(Error::StackUnderflow) } else { Ok(()) } } /// Returns true if the number of elements on the stack is odd. /// /// Used for processing some charstring operators where an odd /// count represents the presence of the glyph advance width at the /// bottom of the stack. pub fn len_is_odd(&self) -> bool { self.top & 1 != 0 } pub fn clear(&mut self) { self.top = 0; } /// Reverse the order of all elements on the stack. /// /// Some charstring operators are simpler to process on a reversed /// stack. pub fn reverse(&mut self) { self.values[..self.top].reverse(); self.value_is_fixed[..self.top].reverse(); } /// Swaps the top two elements. pub(crate) fn exch(&mut self) -> Result<(), Error> { if self.top < 2 { return Err(Error::StackUnderflow); } let a = self.top - 1; let b = a - 1; self.values.swap(a, b); self.value_is_fixed.swap(a, b); Ok(()) } pub fn push(&mut self, number: impl Into) -> Result<(), Error> { match number.into() { Number::I32(value) => self.push_impl(value, false), Number::Fixed(value) => self.push_impl(value.to_bits(), true), } } /// Sets the element at `index` to the given `number`. pub fn set(&mut self, index: usize, number: impl Into) -> Result<(), Error> { if index >= self.top { return Err(Error::StackOverflow); } match number.into() { Number::I32(value) => { self.value_is_fixed[index] = false; self.values[index] = value; } Number::Fixed(value) => { self.value_is_fixed[index] = true; self.values[index] = value.to_bits(); } } Ok(()) } /// Drops `n` elements from the top of the stack. /// /// If `n` is greater than the length of the stack, this simply acts as /// a clear operation. pub fn drop(&mut self, n: usize) { self.top = self.top.saturating_sub(n); } /// Returns the 32-bit integer at the given index on the stack. /// /// Will return an error if the value at that index was not pushed as an /// integer. pub fn get_i32(&self, index: usize) -> Result { // FreeType just returns 0 for OOB access // let Some(value) = self.values.get(index).copied() else { return Ok(0); }; if self.value_is_fixed[index] { // FreeType returns an error here rather than converting // Err(Error::ExpectedI32StackEntry(index)) } else { Ok(value) } } /// Returns the 16.16 fixed point value at the given index on the stack. /// /// If the value was pushed as an integer, it will be automatically /// converted to 16.16 fixed point. pub fn get_fixed(&self, index: usize) -> Result { // FreeType just returns 0 for OOB access // let Some(value) = self.values.get(index).copied() else { return Ok(Fixed::ZERO); }; Ok(if self.value_is_fixed[index] { Fixed::from_bits(value) } else { Fixed::from_i32(value) }) } /// Pops a 32-bit integer from the top of stack. /// /// Will return an error if the top value on the stack was not pushed as an /// integer. pub fn pop_i32(&mut self) -> Result { let i = self.pop()?; self.get_i32(i) } /// Pops a 16.16 fixed point value from the top of the stack. /// /// If the value was pushed as an integer, it will be automatically /// converted to 16.16 fixed point. pub fn pop_fixed(&mut self) -> Result { let i = self.pop()?; self.get_fixed(i) } /// Returns an iterator yielding all elements on the stack /// as 16.16 fixed point values. /// /// Used to read array style DICT entries such as blue values, /// font matrix and font bounding box. pub fn fixed_values(&self) -> impl Iterator + '_ { self.values[..self.top] .iter() .zip(&self.value_is_fixed) .map(|(value, is_real)| { if *is_real { Fixed::from_bits(*value) } else { Fixed::from_i32(*value) } }) } /// Returns an array of `N` 16.16 fixed point values starting at /// `first_index`. pub fn fixed_array(&self, first_index: usize) -> Result<[Fixed; N], Error> { let mut result = [Fixed::ZERO; N]; let end = first_index + N; // FreeType doesn't have an equivalent to this function but always // returns 0 on failure so we take the maximum valid range and // pad the remainder with zeros let range = first_index.min(self.top)..end.min(self.top); for ((src, is_fixed), dest) in self.values[range.clone()] .iter() .zip(&self.value_is_fixed[range]) .zip(&mut result) { let value = if *is_fixed { Fixed::from_bits(*src) } else { Fixed::from_i32(*src) }; *dest = value; } Ok(result) } /// Returns an iterator yielding all elements on the stack as number /// values. /// /// This is useful for capturing the current state of the stack. pub fn number_values(&self) -> impl Iterator + '_ { self.values[..self.top] .iter() .zip(&self.value_is_fixed) .map(|(value, is_fixed)| Number::from_stack(*value, *is_fixed)) } /// Apply a prefix sum to decode delta-encoded numbers. /// /// "The second and subsequent numbers in a delta are encoded as the /// difference between successive values." /// /// Roughly equivalent to the FreeType logic at /// /// /// See pub fn apply_delta_prefix_sum(&mut self) { if self.top > 1 { let mut sum = Fixed::ZERO; for (value, is_fixed) in self.values[..self.top] .iter_mut() .zip(&mut self.value_is_fixed) { let fixed_value = if *is_fixed { Fixed::from_bits(*value) } else { Fixed::from_i32(*value) }; // See // The "DIN Alternate" font contains incorrect blue values // that cause an overflow in this computation. FreeType does // not use checked arithmetic so we need to explicitly use // wrapping behavior to produce matching outlines. sum = sum.wrapping_add(fixed_value); *value = sum.to_bits(); *is_fixed = true; } } } /// Apply the `blend` operator. /// /// See #[inline(never)] pub fn apply_blend(&mut self, blend_state: &BlendState) -> Result<(), Error> { // When the blend operator is invoked, the stack will contain a set // of target values, followed by sets of deltas for those values for // each variation region, followed by the count of target values. // // For example, if we're blending two target values across three // variation regions, the stack would be setup as follows (parentheses // added to signify grouping of deltas): // // value_0 value_1 (delta_0_0 delta_0_1 delta_0_2) (delta_1_0 delta_1_1 delta_1_2) 2 // // where delta_i_j represents the delta for value i and region j. // // We compute the scalars for each region, multiply them by the // associated deltas and add the result to the respective target value. // Then the stack is popped so only the final target values remain. let target_value_count = self.pop_i32()? as usize; if target_value_count > self.top { return Err(Error::StackUnderflow); } let region_count = blend_state.region_count()?; // We expect at least `target_value_count * (region_count + 1)` // elements on the stack. let operand_count = target_value_count * (region_count + 1); if self.len() < operand_count { return Err(Error::StackUnderflow); } // The stack may contain more elements than necessary, so keep track of // our active range. let start = self.len() - operand_count; let end = start + operand_count; // For simplicity, convert all elements to fixed up front. for (value, is_fixed) in self.values[start..end] .iter_mut() .zip(&mut self.value_is_fixed[start..]) { if !*is_fixed { *value = Fixed::from_i32(*value).to_bits(); *is_fixed = true; } } let (values, deltas) = self.values[start..].split_at_mut(target_value_count); // Note: we specifically loop over scalars in the outer loop to avoid // computing them more than once in the case that we overflow our // precomputed cache. for (region_ix, maybe_scalar) in blend_state.scalars()?.enumerate() { let scalar = maybe_scalar?; // We could omit these in `BlendState::scalars()` but that would // significantly reduce the clarity of the already complex // chained iterator code there. Do the simple thing here instead. if scalar == Fixed::ZERO { continue; } for (value_ix, value) in values.iter_mut().enumerate() { let delta_ix = (region_count * value_ix) + region_ix; let delta = Fixed::from_bits(deltas[delta_ix]); *value = (Fixed::from_bits(*value).wrapping_add(delta * scalar)).to_bits(); } } self.top = start + target_value_count; Ok(()) } /// Pops the top two elements from the stack and pushes the quotient. /// /// Implements special behavior for Type1 fonts to handle the case of /// integers greater than 32k per FreeType. pub(crate) fn div(&mut self, is_type1: bool) -> Result<(), Error> { let b_idx = self.pop()?; let a_idx = self.pop()?; let (a, b) = if self.value_is_fixed[a_idx] { (self.get_fixed(a_idx)?, self.get_fixed(b_idx)?) } else { // In type1 fonts, we can end up with an integer greater than // 32k. In this case, FT just reinterprets both as fixed point // and divides: let a = self.get_i32(a_idx)?; if is_type1 && !(-32000..=32000).contains(&a) { (Fixed::from_bits(a), Fixed::from_bits(self.get_i32(b_idx)?)) } else { (self.get_fixed(a_idx)?, self.get_fixed(b_idx)?) } }; self.push(a / b) } fn push_impl(&mut self, value: i32, is_fixed: bool) -> Result<(), Error> { if self.top == MAX_STACK { return Err(Error::StackOverflow); } self.values[self.top] = value; self.value_is_fixed[self.top] = is_fixed; self.top += 1; Ok(()) } fn pop(&mut self) -> Result { if self.top > 0 { self.top -= 1; Ok(self.top) } else { Ok(0) } } } impl Default for Stack { fn default() -> Self { Self::new() } } /// Either a signed 32-bit integer or a 16.16 fixed point number. /// /// This represents the CFF "number" operand type. /// See "Table 6 Operand Types" at #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum Number { I32(i32), Fixed(Fixed), } impl Number { fn from_stack(raw: i32, is_fixed: bool) -> Self { if is_fixed { Self::Fixed(Fixed::from_bits(raw)) } else { Self::I32(raw) } } } impl From for Number { fn from(value: i32) -> Self { Self::I32(value) } } impl From for Number { fn from(value: Fixed) -> Self { Self::Fixed(value) } } impl std::fmt::Display for Number { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::I32(value) => value.fmt(f), Self::Fixed(value) => value.fmt(f), } } } #[cfg(test)] mod tests { use super::*; use crate::{tables::variations::ItemVariationStore, FontData, FontRead}; use types::{F2Dot14, Fixed}; #[test] fn push_pop() { let mut stack = Stack::new(); stack.push(20).unwrap(); stack.push(Fixed::from_f64(42.42)).unwrap(); assert!(!stack.len_is_odd()); stack.verify_exact_len(2).unwrap(); stack.verify_at_least_len(2).unwrap(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(42.42)); assert_eq!(stack.pop_i32().unwrap(), 20); } #[test] fn push_fixed_pop_i32() { let mut stack = Stack::new(); stack.push(Fixed::from_f64(42.42)).unwrap(); assert!(stack.pop_i32().is_err()); } #[test] fn push_i32_pop_fixed() { let mut stack = Stack::new(); stack.push(123).unwrap(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(123.0)); } #[test] fn reverse() { let mut stack = Stack::new(); stack.push(Fixed::from_f64(1.5)).unwrap(); stack.push(42).unwrap(); stack.push(Fixed::from_f64(4.2)).unwrap(); stack.reverse(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(1.5)); assert_eq!(stack.pop_i32().unwrap(), 42); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(4.2)); } #[test] fn delta_prefix_sum() { let mut stack = Stack::new(); stack.push(Fixed::from_f64(1.5)).unwrap(); stack.push(42).unwrap(); stack.push(Fixed::from_f64(4.2)).unwrap(); stack.apply_delta_prefix_sum(); assert!(stack.len_is_odd()); let values: Vec<_> = stack.fixed_values().collect(); let expected = &[ Fixed::from_f64(1.5), Fixed::from_f64(43.5), Fixed::from_f64(47.69999694824219), ]; assert_eq!(&values, expected); } #[test] fn blend() { let ivs_data = &font_test_data::cff2::EXAMPLE[18..]; let ivs = ItemVariationStore::read(FontData::new(ivs_data)).unwrap(); // This coordinate will generate scalars [0.5, 0.5] let coords = &[F2Dot14::from_f32(-0.75)]; let blend_state = BlendState::new(ivs, coords, 0).unwrap(); let mut stack = Stack::new(); // Push our target values stack.push(10).unwrap(); stack.push(20).unwrap(); // Push deltas for 2 regions for the first value stack.push(4).unwrap(); stack.push(-8).unwrap(); // Push deltas for 2 regions for the second value stack.push(-60).unwrap(); stack.push(2).unwrap(); // Push target value count stack.push(2).unwrap(); stack.apply_blend(&blend_state).unwrap(); let result: Vec<_> = stack.fixed_values().collect(); // Expected values: // 0: 10 + (4 * 0.5) + (-8 * 0.5) = 8 // 1: 20 + (-60 * 0.5) + (2 * 0.5) = -9 let expected = &[Fixed::from_f64(8.0), Fixed::from_f64(-9.0)]; assert_eq!(&result, expected); } #[test] fn invalid_access_yields_zero() { let mut stack = Stack::new(); assert_eq!(stack.pop_i32().unwrap(), 0); assert_eq!(stack.pop_fixed().unwrap(), Fixed::ZERO); assert_eq!(stack.get_i32(10).unwrap(), 0); assert_eq!(stack.get_fixed(10).unwrap(), Fixed::ZERO); // In this case, we get the first valid value followed by the // remainder of the requested array size padded with zeros stack.push(5).unwrap(); assert_eq!( stack.fixed_array::<3>(0).unwrap(), [Fixed::from_i32(5), Fixed::ZERO, Fixed::ZERO] ); } #[test] fn exch() { let mut stack = Stack::new(); stack.push(4).unwrap(); stack.push(Fixed::from_f64(2.5)).unwrap(); stack.exch().unwrap(); assert_eq!(stack.pop_i32().unwrap(), 4); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(2.5)); // Error when we don't have at least two elements stack.clear(); stack.push(1).unwrap(); assert!(stack.exch().is_err()); } #[test] fn div() { let mut stack = Stack::new(); // Simple division stack.push(200).unwrap(); stack.push(50).unwrap(); stack.div(false).unwrap(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_i32(4)); stack.push(Fixed::from_f64(151.0)).unwrap(); stack.push(2).unwrap(); stack.div(false).unwrap(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(75.5)); // Handle large numbers (magnitude greater than 32k) stack.push(40000).unwrap(); stack.push(20).unwrap(); stack.div(true).unwrap(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_i32(2000)); stack.push(-40000).unwrap(); stack.push(20).unwrap(); stack.div(true).unwrap(); assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_i32(-2000)); } #[test] fn set() { let mut stack = Stack::new(); stack.push(0).unwrap(); stack.push(0).unwrap(); stack.push(0).unwrap(); stack.set(0, Fixed::from_f64(-4.2)).unwrap(); stack.set(2, 25).unwrap(); assert_eq!( stack.fixed_array(0).unwrap(), [Fixed::from_f64(-4.2), Fixed::ZERO, Fixed::from_f64(25.0)] ); } #[test] fn drop() { let mut stack = Stack::new(); for _ in 0..20 { stack.push(0).unwrap(); } assert_eq!(stack.len(), 20); stack.drop(4); assert_eq!(stack.len(), 16); stack.drop(10); assert_eq!(stack.len(), 6); // Only 6 left, but be lenient because processing fonts is often // best effort stack.drop(7); assert_eq!(stack.len(), 0); } }