Skip to main content

read_fonts/ps/
num.rs

1//! Various types of number parsers.
2
3use super::error::Error;
4use crate::Cursor;
5use types::Fixed;
6
7#[inline]
8pub(crate) fn parse_int(cursor: &mut Cursor, b0: u8) -> Result<i32, Error> {
9    // Size   b0 range     Value range              Value calculation
10    //--------------------------------------------------------------------------------
11    // 1      32 to 246    -107 to +107             b0 - 139
12    // 2      247 to 250   +108 to +1131            (b0 - 247) * 256 + b1 + 108
13    // 2      251 to 254   -1131 to -108            -(b0 - 251) * 256 - b1 - 108
14    // 3      28           -32768 to +32767         b1 << 8 | b2
15    // 5      29           -(2^31) to +(2^31 - 1)   b1 << 24 | b2 << 16 | b3 << 8 | b4
16    // <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-3-operand-encoding>
17    Ok(match b0 {
18        32..=246 => b0 as i32 - 139,
19        247..=250 => (b0 as i32 - 247) * 256 + cursor.read::<u8>()? as i32 + 108,
20        251..=254 => -(b0 as i32 - 251) * 256 - cursor.read::<u8>()? as i32 - 108,
21        28 => cursor.read::<i16>()? as i32,
22        29 => cursor.read::<i32>()?,
23        _ => {
24            return Err(Error::InvalidNumber);
25        }
26    })
27}
28
29// Various unnamed constants inlined in FreeType's cff_parse_real function
30// <<https://gitlab.freedesktop.org/freetype/freetype/-/blob/82090e67c24259c343c83fd9cefe6ff0be7a7eca/src/cff/cffparse.c#L183>>
31
32// Value returned on overflow
33const BCD_OVERFLOW: Fixed = Fixed::from_bits(0x7FFFFFFF);
34// Value returned on underflow
35const BCD_UNDERFLOW: Fixed = Fixed::ZERO;
36// Limit at which we stop accumulating `number` and increase
37// the exponent instead
38const BCD_NUMBER_LIMIT: i32 = 0xCCCCCCC;
39// Limit for the integral part of the result
40const BCD_INTEGER_LIMIT: i32 = 0x7FFF;
41
42// <https://gitlab.freedesktop.org/freetype/freetype/-/blob/82090e67c24259c343c83fd9cefe6ff0be7a7eca/src/cff/cffparse.c#L150>
43pub(crate) const BCD_POWER_TENS: [i32; 10] = [
44    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
45];
46
47/// Components for computing a fixed point value for a binary coded decimal
48/// number.
49#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
50pub struct BcdComponents {
51    /// If overflow or underflow is detected early, then this
52    /// contains the resulting value and we skip further
53    /// processing.
54    error: Option<Fixed>,
55    number: i32,
56    sign: i32,
57    exponent: i32,
58    exponent_add: i32,
59    integer_len: i32,
60    fraction_len: i32,
61}
62
63impl BcdComponents {
64    /// Parse a binary coded decimal number.
65    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/82090e67c24259c343c83fd9cefe6ff0be7a7eca/src/cff/cffparse.c#L183>
66    pub(crate) fn parse(cursor: &mut Cursor) -> Result<Self, Error> {
67        enum Phase {
68            Integer,
69            Fraction,
70            Exponent,
71        }
72        let mut phase = Phase::Integer;
73        let mut sign = 1i32;
74        let mut exponent_sign = 1i32;
75        let mut number = 0i32;
76        let mut exponent = 0i32;
77        let mut exponent_add = 0i32;
78        let mut integer_len = 0;
79        let mut fraction_len = 0;
80        // Nibble value    Represents
81        //----------------------------------
82        // 0 to 9          0 to 9
83        // a               . (decimal point)
84        // b               E
85        // c               E-
86        // d               <reserved>
87        // e               - (minus)
88        // f               end of number
89        // <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions>
90        'outer: loop {
91            let b = cursor.read::<u8>()?;
92            for nibble in [(b >> 4) & 0xF, b & 0xF] {
93                match phase {
94                    Phase::Integer => match nibble {
95                        0x0..=0x9 => {
96                            if number >= BCD_NUMBER_LIMIT {
97                                exponent_add += 1;
98                            } else if nibble != 0 || number != 0 {
99                                number = number * 10 + nibble as i32;
100                                integer_len += 1;
101                            }
102                        }
103                        0xE => sign = -1,
104                        0xA => {
105                            phase = Phase::Fraction;
106                        }
107                        0xB => {
108                            phase = Phase::Exponent;
109                        }
110                        0xC => {
111                            phase = Phase::Exponent;
112                            exponent_sign = -1;
113                        }
114                        _ => break 'outer,
115                    },
116                    Phase::Fraction => match nibble {
117                        0x0..=0x9 => {
118                            if nibble == 0 && number == 0 {
119                                exponent_add -= 1;
120                            } else if number < BCD_NUMBER_LIMIT && fraction_len < 9 {
121                                number = number * 10 + nibble as i32;
122                                fraction_len += 1;
123                            }
124                        }
125                        0xB => {
126                            phase = Phase::Exponent;
127                        }
128                        0xC => {
129                            phase = Phase::Exponent;
130                            exponent_sign = -1;
131                        }
132                        _ => break 'outer,
133                    },
134                    Phase::Exponent => {
135                        match nibble {
136                            0x0..=0x9 => {
137                                // Arbitrarily limit exponent
138                                if exponent > 1000 {
139                                    return if exponent_sign == -1 {
140                                        Ok(BCD_UNDERFLOW.into())
141                                    } else {
142                                        Ok(BCD_OVERFLOW.into())
143                                    };
144                                } else {
145                                    exponent = exponent * 10 + nibble as i32;
146                                }
147                            }
148                            _ => break 'outer,
149                        }
150                    }
151                }
152            }
153        }
154        exponent *= exponent_sign;
155        Ok(Self {
156            error: None,
157            number,
158            sign,
159            exponent,
160            exponent_add,
161            integer_len,
162            fraction_len,
163        })
164    }
165
166    /// Returns the fixed point value for the precomputed components,
167    /// optionally using an internal scale factor of 1000 to
168    /// increase fractional precision.
169    pub fn value(&self, scale_by_1000: bool) -> Fixed {
170        if let Some(error) = self.error {
171            return error;
172        }
173        let mut number = self.number;
174        if number == 0 {
175            return Fixed::ZERO;
176        }
177        let mut exponent = self.exponent;
178        let mut integer_len = self.integer_len;
179        let mut fraction_len = self.fraction_len;
180        if scale_by_1000 {
181            exponent += 3 + self.exponent_add;
182        } else {
183            exponent += self.exponent_add;
184        }
185        integer_len += exponent;
186        fraction_len -= exponent;
187        if integer_len > 5 {
188            return BCD_OVERFLOW;
189        }
190        if integer_len < -5 {
191            return BCD_UNDERFLOW;
192        }
193        // Remove non-significant digits
194        if integer_len < 0 {
195            number /= BCD_POWER_TENS[(-integer_len) as usize];
196            fraction_len += integer_len;
197        }
198        // Can only happen if exponent was non-zero
199        if fraction_len == 10 {
200            number /= 10;
201            fraction_len -= 1;
202        }
203        // Convert to fixed
204        let mut result = if fraction_len > 0 {
205            let b = BCD_POWER_TENS[fraction_len as usize];
206            if number / b > BCD_INTEGER_LIMIT {
207                0
208            } else {
209                (Fixed::from_bits(number) / Fixed::from_bits(b)).to_bits()
210            }
211        } else {
212            number = number.wrapping_mul(BCD_POWER_TENS[-fraction_len as usize]);
213            if number > BCD_INTEGER_LIMIT {
214                return BCD_OVERFLOW;
215            } else {
216                number << 16
217            }
218        };
219        if scale_by_1000 {
220            // FreeType stores the scaled value and does a fixed division by
221            // 1000 when the blue metrics are requested. We just do it here
222            // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/psaux/psft.c#L554>
223            result = (Fixed::from_bits(result) / Fixed::from_i32(1000)).to_bits();
224        }
225        Fixed::from_bits(result * self.sign)
226    }
227
228    /// Returns the fixed point value for the components along with a
229    /// dynamically determined scale factor.
230    ///
231    /// Use for processing FontMatrix components.
232    ///
233    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/cff/cffparse.c#L332>
234    pub(crate) fn dynamically_scaled_value(&self) -> (Fixed, i32) {
235        if let Some(error) = self.error {
236            return (error, 0);
237        }
238        let mut number = self.number;
239        if number == 0 {
240            return (Fixed::ZERO, 0);
241        }
242        let mut exponent = self.exponent;
243        let integer_len = self.integer_len;
244        let mut fraction_len = self.fraction_len;
245        exponent += self.exponent_add;
246        fraction_len += integer_len;
247        exponent += integer_len;
248        let result;
249        let scaling;
250        if fraction_len <= 5 {
251            if number > BCD_INTEGER_LIMIT {
252                result = Fixed::from_bits(number) / Fixed::from_bits(10);
253                scaling = exponent - fraction_len + 1;
254            } else {
255                if exponent > 0 {
256                    // Make scaling as small as possible
257                    let new_fraction_len = exponent.min(5);
258                    let shift = new_fraction_len - fraction_len;
259                    if shift > 0 {
260                        exponent -= new_fraction_len;
261                        number *= BCD_POWER_TENS[shift as usize];
262                        if number > BCD_INTEGER_LIMIT {
263                            number /= 10;
264                            exponent += 1;
265                        }
266                    } else {
267                        exponent -= fraction_len;
268                    }
269                } else {
270                    exponent -= fraction_len;
271                }
272                result = Fixed::from_bits(number << 16);
273                scaling = exponent;
274            }
275        } else if (number / BCD_POWER_TENS[fraction_len as usize - 5]) > BCD_INTEGER_LIMIT {
276            result = Fixed::from_bits(number)
277                / Fixed::from_bits(BCD_POWER_TENS[fraction_len as usize - 4]);
278            scaling = exponent - 4;
279        } else {
280            result = Fixed::from_bits(number)
281                / Fixed::from_bits(BCD_POWER_TENS[fraction_len as usize - 5]);
282            scaling = exponent - 5;
283        }
284        (Fixed::from_bits(result.to_bits() * self.sign), scaling)
285    }
286}
287
288impl From<Fixed> for BcdComponents {
289    fn from(value: Fixed) -> Self {
290        Self {
291            error: Some(value),
292            ..Default::default()
293        }
294    }
295}
296
297/// Parse a fixed point value with a dynamic scaling factor.
298///
299/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/cff/cffparse.c#L580>
300pub(crate) fn parse_fixed_dynamic(cursor: &mut Cursor) -> Result<(Fixed, i32), Error> {
301    let b0 = cursor.read::<u8>()?;
302    match b0 {
303        30 => Ok(BcdComponents::parse(cursor)?.dynamically_scaled_value()),
304        28 | 29 | 32..=254 => {
305            let num = parse_int(cursor, b0)?;
306            let mut int_len = 10;
307            if num > BCD_INTEGER_LIMIT {
308                for (i, power_ten) in BCD_POWER_TENS.iter().enumerate().skip(5) {
309                    if num < *power_ten {
310                        int_len = i;
311                        break;
312                    }
313                }
314                let scaling = if (num - BCD_POWER_TENS[int_len - 5]) > BCD_INTEGER_LIMIT {
315                    int_len - 4
316                } else {
317                    int_len - 5
318                };
319                Ok((
320                    Fixed::from_bits(num) / Fixed::from_bits(BCD_POWER_TENS[scaling]),
321                    scaling as i32,
322                ))
323            } else {
324                Ok((Fixed::from_bits(num << 16), 0))
325            }
326        }
327        _ => Err(Error::InvalidNumber),
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334    use crate::FontData;
335
336    #[test]
337    fn int_operands() {
338        // Test the boundary conditions of the ranged int operators
339        let empty = FontData::new(&[]);
340        let min_byte = FontData::new(&[0]);
341        let max_byte = FontData::new(&[255]);
342        // 32..=246 => -107..=107
343        assert_eq!(parse_int(&mut empty.cursor(), 32).unwrap(), -107);
344        assert_eq!(parse_int(&mut empty.cursor(), 246).unwrap(), 107);
345        // 247..=250 => +108 to +1131
346        assert_eq!(parse_int(&mut min_byte.cursor(), 247).unwrap(), 108);
347        assert_eq!(parse_int(&mut max_byte.cursor(), 250).unwrap(), 1131);
348        // 251..=254 => -1131 to -108
349        assert_eq!(parse_int(&mut min_byte.cursor(), 251).unwrap(), -108);
350        assert_eq!(parse_int(&mut max_byte.cursor(), 254).unwrap(), -1131);
351    }
352
353    #[test]
354    fn binary_coded_decimal_operands() {
355        // From <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions>:
356        //
357        // "A real number is terminated by one (or two) 0xf nibbles so that it is always padded
358        // to a full byte. Thus, the value -2.25 is encoded by the byte sequence (1e e2 a2 5f)
359        // and the value 0.140541E-3 by the sequence (1e 0a 14 05 41 c3 ff)."
360        //
361        // The initial 1e byte in the examples above is the dictionary operator to trigger
362        // parsing of BCD so it is dropped in the tests here.
363        let bytes = FontData::new(&[0xe2, 0xa2, 0x5f]);
364        assert_eq!(
365            BcdComponents::parse(&mut bytes.cursor())
366                .unwrap()
367                .value(false),
368            Fixed::from_f64(-2.25)
369        );
370        let bytes = FontData::new(&[0x0a, 0x14, 0x05, 0x41, 0xc3, 0xff]);
371        assert_eq!(
372            BcdComponents::parse(&mut bytes.cursor())
373                .unwrap()
374                .value(false),
375            Fixed::from_f64(0.140541E-3)
376        );
377        // Check that we match FreeType for 375e-4.
378        // Note: we used to parse 0.0375... but the new FT matching code
379        // has less precision
380        let bytes = FontData::new(&[0x37, 0x5c, 0x4f]);
381        assert_eq!(
382            BcdComponents::parse(&mut bytes.cursor())
383                .unwrap()
384                .value(false),
385            Fixed::from_f64(0.0370025634765625)
386        );
387    }
388
389    #[test]
390    fn scaled_binary_coded_decimal_operands() {
391        // For blue scale, we compute values with an internal factor of 1000 to match
392        // FreeType, which gives us more precision for fractional bits
393        let bytes = FontData::new(&[0xA, 0x06, 0x25, 0xf]);
394        assert_eq!(
395            BcdComponents::parse(&mut bytes.cursor())
396                .unwrap()
397                .value(true),
398            Fixed::from_f64(0.0625)
399        );
400        // Just an additional check to test increased precision. Compare to
401        // the test above where this value generates 0.0370...
402        let bytes = FontData::new(&[0x37, 0x5c, 0x4f]);
403        assert_eq!(
404            BcdComponents::parse(&mut bytes.cursor())
405                .unwrap()
406                .value(true),
407            Fixed::from_f64(0.037506103515625)
408        );
409    }
410
411    #[test]
412    fn dynamically_scaled_binary_coded_decimal_operands() {
413        // 0.0625
414        let bytes = FontData::new(&[0xA, 0x06, 0x25, 0xf]);
415        assert_eq!(
416            BcdComponents::parse(&mut bytes.cursor())
417                .unwrap()
418                .dynamically_scaled_value(),
419            (Fixed::from_f64(6250.0), -5)
420        );
421        // 0.0375
422        let bytes = FontData::new(&[0x37, 0x5c, 0x4f]);
423        assert_eq!(
424            BcdComponents::parse(&mut bytes.cursor())
425                .unwrap()
426                .dynamically_scaled_value(),
427            (Fixed::from_f64(375.0), -4)
428        );
429        // .001953125
430        let bytes = FontData::new(&[0xa0, 0x1, 0x95, 0x31, 0x25, 0xff]);
431        assert_eq!(
432            BcdComponents::parse(&mut bytes.cursor())
433                .unwrap()
434                .dynamically_scaled_value(),
435            (Fixed::from_bits(1280000000), -7)
436        );
437    }
438
439    /// See <https://github.com/googlefonts/fontations/issues/1617>
440    #[test]
441    fn blue_scale_fraction_length_of_0() {
442        // 0.0037
443        let bytes = FontData::new(&[0x37, 0xC3, 0xFF]);
444        assert_eq!(
445            BcdComponents::parse(&mut bytes.cursor())
446                .unwrap()
447                .value(true),
448            Fixed::from_f64(0.0370025634765625)
449        );
450    }
451}