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