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}