read_fonts/tables/postscript/
stack.rs

1//! Operand stack for CFF/CFF2 parsing.
2
3use types::Fixed;
4
5use super::{BlendState, Error};
6
7/// Maximum size of the operand stack.
8///
9/// "Operators in Top DICT, Font DICTs, Private DICTs and CharStrings may be
10/// preceded by up to a maximum of 513 operands."
11///
12/// <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-9-top-dict-operator-entries>
13const MAX_STACK: usize = 513;
14
15/// Operand stack for DICTs and charstrings.
16///
17/// The operand stack can contain either 32-bit integers or 16.16 fixed point
18/// values. The type is known when pushing to the stack and the expected type
19/// is also known (based on the operator) when reading from the stack, so the
20/// conversion is performed on demand at read time.
21///
22/// Storing the entries as an enum would require 8 bytes each and since these
23/// objects are created on the _stack_, we reduce the required size by storing
24/// the entries in parallel arrays holding the raw 32-bit value and a flag that
25/// tracks which values are fixed point.
26pub struct Stack {
27    values: [i32; MAX_STACK],
28    value_is_fixed: [bool; MAX_STACK],
29    top: usize,
30}
31
32impl Stack {
33    pub fn new() -> Self {
34        Self {
35            values: [0; MAX_STACK],
36            value_is_fixed: [false; MAX_STACK],
37            top: 0,
38        }
39    }
40
41    pub fn is_empty(&self) -> bool {
42        self.top == 0
43    }
44
45    pub fn len(&self) -> usize {
46        self.top
47    }
48
49    pub fn verify_exact_len(&self, len: usize) -> Result<(), Error> {
50        if self.top != len {
51            Err(Error::StackUnderflow)
52        } else {
53            Ok(())
54        }
55    }
56
57    pub fn verify_at_least_len(&self, len: usize) -> Result<(), Error> {
58        if self.top < len {
59            Err(Error::StackUnderflow)
60        } else {
61            Ok(())
62        }
63    }
64
65    /// Returns true if the number of elements on the stack is odd.
66    ///
67    /// Used for processing some charstring operators where an odd
68    /// count represents the presence of the glyph advance width at the
69    /// bottom of the stack.
70    pub fn len_is_odd(&self) -> bool {
71        self.top & 1 != 0
72    }
73
74    pub fn clear(&mut self) {
75        self.top = 0;
76    }
77
78    /// Reverse the order of all elements on the stack.
79    ///
80    /// Some charstring operators are simpler to process on a reversed
81    /// stack.
82    pub fn reverse(&mut self) {
83        self.values[..self.top].reverse();
84        self.value_is_fixed[..self.top].reverse();
85    }
86
87    pub fn push(&mut self, number: impl Into<Number>) -> Result<(), Error> {
88        match number.into() {
89            Number::I32(value) => self.push_impl(value, false),
90            Number::Fixed(value) => self.push_impl(value.to_bits(), true),
91        }
92    }
93
94    /// Returns the 32-bit integer at the given index on the stack.
95    ///
96    /// Will return an error if the value at that index was not pushed as an
97    /// integer.
98    pub fn get_i32(&self, index: usize) -> Result<i32, Error> {
99        let value = *self
100            .values
101            .get(index)
102            .ok_or(Error::InvalidStackAccess(index))?;
103        if self.value_is_fixed[index] {
104            // FreeType returns an error here rather than converting
105            // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psstack.c#L145>
106            Err(Error::ExpectedI32StackEntry(index))
107        } else {
108            Ok(value)
109        }
110    }
111
112    /// Returns the 16.16 fixed point value at the given index on the stack.
113    ///
114    /// If the value was pushed as an integer, it will be automatically
115    /// converted to 16.16 fixed point.
116    pub fn get_fixed(&self, index: usize) -> Result<Fixed, Error> {
117        let value = *self
118            .values
119            .get(index)
120            .ok_or(Error::InvalidStackAccess(index))?;
121        Ok(if self.value_is_fixed[index] {
122            Fixed::from_bits(value)
123        } else {
124            Fixed::from_i32(value)
125        })
126    }
127
128    /// Pops a 32-bit integer from the top of stack.
129    ///
130    /// Will return an error if the top value on the stack was not pushed as an
131    /// integer.
132    pub fn pop_i32(&mut self) -> Result<i32, Error> {
133        let i = self.pop()?;
134        self.get_i32(i)
135    }
136
137    /// Pops a 16.16 fixed point value from the top of the stack.
138    ///
139    /// If the value was pushed as an integer, it will be automatically
140    /// converted to 16.16 fixed point.
141    pub fn pop_fixed(&mut self) -> Result<Fixed, Error> {
142        let i = self.pop()?;
143        self.get_fixed(i)
144    }
145
146    /// Returns an iterator yielding all elements on the stack
147    /// as 16.16 fixed point values.
148    ///
149    /// Used to read array style DICT entries such as blue values,
150    /// font matrix and font bounding box.
151    pub fn fixed_values(&self) -> impl Iterator<Item = Fixed> + '_ {
152        self.values[..self.top]
153            .iter()
154            .zip(&self.value_is_fixed)
155            .map(|(value, is_real)| {
156                if *is_real {
157                    Fixed::from_bits(*value)
158                } else {
159                    Fixed::from_i32(*value)
160                }
161            })
162    }
163
164    /// Returns an array of `N` 16.16 fixed point values starting at
165    /// `first_index`.
166    pub fn fixed_array<const N: usize>(&self, first_index: usize) -> Result<[Fixed; N], Error> {
167        let mut result = [Fixed::ZERO; N];
168        if first_index >= self.top {
169            return Err(Error::InvalidStackAccess(first_index));
170        }
171        let end = first_index + N;
172        if end > self.top {
173            return Err(Error::InvalidStackAccess(end - 1));
174        }
175        let range = first_index..end;
176        for ((src, is_fixed), dest) in self.values[range.clone()]
177            .iter()
178            .zip(&self.value_is_fixed[range])
179            .zip(&mut result)
180        {
181            let value = if *is_fixed {
182                Fixed::from_bits(*src)
183            } else {
184                Fixed::from_i32(*src)
185            };
186            *dest = value;
187        }
188        Ok(result)
189    }
190
191    /// Returns an iterator yielding all elements on the stack as number
192    /// values.
193    ///
194    /// This is useful for capturing the current state of the stack.
195    pub fn number_values(&self) -> impl Iterator<Item = Number> + '_ {
196        self.values[..self.top]
197            .iter()
198            .zip(&self.value_is_fixed)
199            .map(|(value, is_fixed)| Number::from_stack(*value, *is_fixed))
200    }
201
202    /// Apply a prefix sum to decode delta-encoded numbers.
203    ///
204    /// "The second and subsequent numbers in a delta are encoded as the
205    /// difference between successive values."
206    ///
207    /// Roughly equivalent to the FreeType logic at
208    /// <https://gitlab.freedesktop.org/freetype/freetype/-/blob/57617782464411201ce7bbc93b086c1b4d7d84a5/src/cff/cffparse.c#L1431>
209    ///
210    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-6-operand-types>
211    pub fn apply_delta_prefix_sum(&mut self) {
212        if self.top > 1 {
213            let mut sum = Fixed::ZERO;
214            for (value, is_fixed) in self.values[..self.top]
215                .iter_mut()
216                .zip(&mut self.value_is_fixed)
217            {
218                let fixed_value = if *is_fixed {
219                    // FreeType reads delta values using cff_parse_num which
220                    // which truncates the fractional parts of 16.16 values
221                    // See delta parsing:
222                    // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/cff/cffparse.c#L1427>
223                    // See cff_parse_num "binary-coded decimal is truncated to
224                    // integer":
225                    // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/cff/cffparse.c#L463>
226                    Fixed::from_bits(*value).floor()
227                } else {
228                    Fixed::from_i32(*value)
229                };
230                // See <https://github.com/googlefonts/fontations/issues/1193>
231                // The "DIN Alternate" font contains incorrect blue values
232                // that cause an overflow in this computation. FreeType does
233                // not use checked arithmetic so we need to explicitly use
234                // wrapping behavior to produce matching outlines.
235                sum = sum.wrapping_add(fixed_value);
236                *value = sum.to_bits();
237                *is_fixed = true;
238            }
239        }
240    }
241
242    /// Apply the `blend` operator.
243    ///
244    /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2charstr#syntax-for-font-variations-support-operators>
245    #[inline(never)]
246    pub fn apply_blend(&mut self, blend_state: &BlendState) -> Result<(), Error> {
247        // When the blend operator is invoked, the stack will contain a set
248        // of target values, followed by sets of deltas for those values for
249        // each variation region, followed by the count of target values.
250        //
251        // For example, if we're blending two target values across three
252        // variation regions, the stack would be setup as follows (parentheses
253        // added to signify grouping of deltas):
254        //
255        // value_0 value_1 (delta_0_0 delta_0_1 delta_0_2) (delta_1_0 delta_1_1 delta_1_2) 2
256        //
257        // where delta_i_j represents the delta for value i and region j.
258        //
259        // We compute the scalars for each region, multiply them by the
260        // associated deltas and add the result to the respective target value.
261        // Then the stack is popped so only the final target values remain.
262        let target_value_count = self.pop_i32()? as usize;
263        if target_value_count > self.top {
264            return Err(Error::StackUnderflow);
265        }
266        let region_count = blend_state.region_count()?;
267        // We expect at least `target_value_count * (region_count + 1)`
268        // elements on the stack.
269        let operand_count = target_value_count * (region_count + 1);
270        if self.len() < operand_count {
271            return Err(Error::StackUnderflow);
272        }
273        // The stack may contain more elements than necessary, so keep track of
274        // our active range.
275        let start = self.len() - operand_count;
276        let end = start + operand_count;
277        // For simplicity, convert all elements to fixed up front.
278        for (value, is_fixed) in self.values[start..end]
279            .iter_mut()
280            .zip(&mut self.value_is_fixed[start..])
281        {
282            if !*is_fixed {
283                *value = Fixed::from_i32(*value).to_bits();
284                *is_fixed = true;
285            }
286        }
287        let (values, deltas) = self.values[start..].split_at_mut(target_value_count);
288        // Note: we specifically loop over scalars in the outer loop to avoid
289        // computing them more than once in the case that we overflow our
290        // precomputed cache.
291        for (region_ix, maybe_scalar) in blend_state.scalars()?.enumerate() {
292            let scalar = maybe_scalar?;
293            // We could omit these in `BlendState::scalars()` but that would
294            // significantly reduce the clarity of the already complex
295            // chained iterator code there. Do the simple thing here instead.
296            if scalar == Fixed::ZERO {
297                continue;
298            }
299            for (value_ix, value) in values.iter_mut().enumerate() {
300                let delta_ix = (region_count * value_ix) + region_ix;
301                let delta = Fixed::from_bits(deltas[delta_ix]);
302                *value = (Fixed::from_bits(*value).wrapping_add(delta * scalar)).to_bits();
303            }
304        }
305        self.top = start + target_value_count;
306        Ok(())
307    }
308
309    fn push_impl(&mut self, value: i32, is_fixed: bool) -> Result<(), Error> {
310        if self.top == MAX_STACK {
311            return Err(Error::StackOverflow);
312        }
313        self.values[self.top] = value;
314        self.value_is_fixed[self.top] = is_fixed;
315        self.top += 1;
316        Ok(())
317    }
318
319    fn pop(&mut self) -> Result<usize, Error> {
320        if self.top > 0 {
321            self.top -= 1;
322            Ok(self.top)
323        } else {
324            Err(Error::StackUnderflow)
325        }
326    }
327}
328
329impl Default for Stack {
330    fn default() -> Self {
331        Self::new()
332    }
333}
334
335/// Either a signed 32-bit integer or a 16.16 fixed point number.
336///
337/// This represents the CFF "number" operand type.
338/// See "Table 6 Operand Types" at <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf>
339#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
340pub enum Number {
341    I32(i32),
342    Fixed(Fixed),
343}
344
345impl Number {
346    fn from_stack(raw: i32, is_fixed: bool) -> Self {
347        if is_fixed {
348            Self::Fixed(Fixed::from_bits(raw))
349        } else {
350            Self::I32(raw)
351        }
352    }
353}
354
355impl From<i32> for Number {
356    fn from(value: i32) -> Self {
357        Self::I32(value)
358    }
359}
360
361impl From<Fixed> for Number {
362    fn from(value: Fixed) -> Self {
363        Self::Fixed(value)
364    }
365}
366
367impl std::fmt::Display for Number {
368    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
369        match self {
370            Self::I32(value) => value.fmt(f),
371            Self::Fixed(value) => value.fmt(f),
372        }
373    }
374}
375
376#[cfg(test)]
377mod tests {
378    use types::{F2Dot14, Fixed};
379
380    use super::Stack;
381    use crate::{
382        tables::{postscript::BlendState, variations::ItemVariationStore},
383        FontData, FontRead,
384    };
385
386    #[test]
387    fn push_pop() {
388        let mut stack = Stack::new();
389        stack.push(20).unwrap();
390        stack.push(Fixed::from_f64(42.42)).unwrap();
391        assert!(!stack.len_is_odd());
392        stack.verify_exact_len(2).unwrap();
393        stack.verify_at_least_len(2).unwrap();
394        assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(42.42));
395        assert_eq!(stack.pop_i32().unwrap(), 20);
396    }
397
398    #[test]
399    fn push_fixed_pop_i32() {
400        let mut stack = Stack::new();
401        stack.push(Fixed::from_f64(42.42)).unwrap();
402        assert!(stack.pop_i32().is_err());
403    }
404
405    #[test]
406    fn push_i32_pop_fixed() {
407        let mut stack = Stack::new();
408        stack.push(123).unwrap();
409        assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(123.0));
410    }
411
412    #[test]
413    fn reverse() {
414        let mut stack = Stack::new();
415        stack.push(Fixed::from_f64(1.5)).unwrap();
416        stack.push(42).unwrap();
417        stack.push(Fixed::from_f64(4.2)).unwrap();
418        stack.reverse();
419        assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(1.5));
420        assert_eq!(stack.pop_i32().unwrap(), 42);
421        assert_eq!(stack.pop_fixed().unwrap(), Fixed::from_f64(4.2));
422    }
423
424    #[test]
425    fn delta_prefix_sum() {
426        let mut stack = Stack::new();
427        stack.push(Fixed::from_f64(1.5)).unwrap();
428        stack.push(42).unwrap();
429        stack.push(Fixed::from_f64(4.2)).unwrap();
430        stack.apply_delta_prefix_sum();
431        assert!(stack.len_is_odd());
432        let values: Vec<_> = stack.fixed_values().collect();
433        let expected = &[
434            Fixed::from_f64(1.0),
435            Fixed::from_f64(43.0),
436            Fixed::from_f64(47.0),
437        ];
438        assert_eq!(&values, expected);
439    }
440
441    #[test]
442    fn blend() {
443        let ivs_data = &font_test_data::cff2::EXAMPLE[18..];
444        let ivs = ItemVariationStore::read(FontData::new(ivs_data)).unwrap();
445        // This coordinate will generate scalars [0.5, 0.5]
446        let coords = &[F2Dot14::from_f32(-0.75)];
447        let blend_state = BlendState::new(ivs, coords, 0).unwrap();
448        let mut stack = Stack::new();
449        // Push our target values
450        stack.push(10).unwrap();
451        stack.push(20).unwrap();
452        // Push deltas for 2 regions for the first value
453        stack.push(4).unwrap();
454        stack.push(-8).unwrap();
455        // Push deltas for 2 regions for the second value
456        stack.push(-60).unwrap();
457        stack.push(2).unwrap();
458        // Push target value count
459        stack.push(2).unwrap();
460        stack.apply_blend(&blend_state).unwrap();
461        let result: Vec<_> = stack.fixed_values().collect();
462        // Expected values:
463        // 0: 10 + (4 * 0.5) + (-8 * 0.5) = 8
464        // 1: 20 + (-60 * 0.5) + (2 * 0.5) = -9
465        let expected = &[Fixed::from_f64(8.0), Fixed::from_f64(-9.0)];
466        assert_eq!(&result, expected);
467    }
468}