read_fonts/tables/postscript/
dict.rs

1//! Parsing for PostScript DICTs.
2
3use std::ops::Range;
4
5use super::{BlendState, Error, Number, Stack, StringId};
6use crate::{types::Fixed, Cursor, ReadError};
7
8/// PostScript DICT operator.
9///
10/// See "Table 9 Top DICT Operator Entries" and "Table 23 Private DICT
11/// Operators" at <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf>
12#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
13pub enum Operator {
14    Version,
15    Notice,
16    FullName,
17    FamilyName,
18    Weight,
19    FontBbox,
20    CharstringsOffset,
21    PrivateDictRange,
22    VariationStoreOffset,
23    Copyright,
24    IsFixedPitch,
25    ItalicAngle,
26    UnderlinePosition,
27    UnderlineThickness,
28    PaintType,
29    CharstringType,
30    FontMatrix,
31    StrokeWidth,
32    FdArrayOffset,
33    FdSelectOffset,
34    BlueValues,
35    OtherBlues,
36    FamilyBlues,
37    FamilyOtherBlues,
38    SubrsOffset,
39    VariationStoreIndex,
40    BlueScale,
41    BlueShift,
42    BlueFuzz,
43    LanguageGroup,
44    ExpansionFactor,
45    Encoding,
46    Charset,
47    UniqueId,
48    Xuid,
49    SyntheticBase,
50    PostScript,
51    BaseFontName,
52    BaseFontBlend,
53    Ros,
54    CidFontVersion,
55    CidFontRevision,
56    CidFontType,
57    CidCount,
58    UidBase,
59    FontName,
60    StdHw,
61    StdVw,
62    DefaultWidthX,
63    NominalWidthX,
64    Blend,
65    StemSnapH,
66    StemSnapV,
67    ForceBold,
68    InitialRandomSeed,
69}
70
71impl Operator {
72    fn from_opcode(opcode: u8) -> Option<Self> {
73        use Operator::*;
74        Some(match opcode {
75            // Top DICT operators
76            0 => Version,
77            1 => Notice,
78            2 => FullName,
79            3 => FamilyName,
80            4 => Weight,
81            5 => FontBbox,
82            13 => UniqueId,
83            14 => Xuid,
84            15 => Charset,
85            16 => Encoding,
86            17 => CharstringsOffset,
87            18 => PrivateDictRange,
88            24 => VariationStoreOffset,
89            // Private DICT operators
90            6 => BlueValues,
91            7 => OtherBlues,
92            8 => FamilyBlues,
93            9 => FamilyOtherBlues,
94            10 => StdHw,
95            11 => StdVw,
96            19 => SubrsOffset,
97            20 => DefaultWidthX,
98            21 => NominalWidthX,
99            22 => VariationStoreIndex,
100            23 => Blend,
101            // Font DICT only uses PrivateDictRange
102            _ => return None,
103        })
104    }
105
106    fn from_extended_opcode(opcode: u8) -> Option<Self> {
107        use Operator::*;
108        Some(match opcode {
109            // Top DICT operators
110            0 => Copyright,
111            1 => IsFixedPitch,
112            2 => ItalicAngle,
113            3 => UnderlinePosition,
114            4 => UnderlineThickness,
115            5 => PaintType,
116            6 => CharstringType,
117            7 => FontMatrix,
118            8 => StrokeWidth,
119            20 => SyntheticBase,
120            21 => PostScript,
121            22 => BaseFontName,
122            23 => BaseFontBlend,
123            30 => Ros,
124            31 => CidFontVersion,
125            32 => CidFontRevision,
126            33 => CidFontType,
127            34 => CidCount,
128            35 => UidBase,
129            36 => FdArrayOffset,
130            37 => FdSelectOffset,
131            38 => FontName,
132            // Private DICT operators
133            9 => BlueScale,
134            10 => BlueShift,
135            11 => BlueFuzz,
136            12 => StemSnapH,
137            13 => StemSnapV,
138            14 => ForceBold,
139            17 => LanguageGroup,
140            18 => ExpansionFactor,
141            19 => InitialRandomSeed,
142            _ => return None,
143        })
144    }
145}
146
147/// Either a PostScript DICT operator or a (numeric) operand.
148#[derive(Copy, Clone, PartialEq, Eq, Debug)]
149pub enum Token {
150    /// An operator parsed from a DICT.
151    Operator(Operator),
152    /// A number parsed from a DICT. If the source was in
153    /// binary coded decimal format, then the second field
154    /// contains the parsed components.
155    Operand(Number, Option<BcdComponents>),
156}
157
158impl From<Operator> for Token {
159    fn from(value: Operator) -> Self {
160        Self::Operator(value)
161    }
162}
163
164impl<T> From<T> for Token
165where
166    T: Into<Number>,
167{
168    fn from(value: T) -> Self {
169        Self::Operand(value.into(), None)
170    }
171}
172
173/// Given a byte slice containing DICT data, returns an iterator yielding
174/// raw operands and operators.
175///
176/// This does not perform any additional processing such as type conversion,
177/// delta decoding or blending.
178pub fn tokens(dict_data: &[u8]) -> impl Iterator<Item = Result<Token, Error>> + '_ + Clone {
179    let mut cursor = crate::FontData::new(dict_data).cursor();
180    std::iter::from_fn(move || {
181        if cursor.remaining_bytes() == 0 {
182            None
183        } else {
184            Some(parse_token(&mut cursor))
185        }
186    })
187}
188
189fn parse_token(cursor: &mut Cursor) -> Result<Token, Error> {
190    // Escape opcode for accessing extensions.
191    const ESCAPE: u8 = 12;
192    let b0 = cursor.read::<u8>()?;
193    Ok(if b0 == ESCAPE {
194        let b1 = cursor.read::<u8>()?;
195        Token::Operator(Operator::from_extended_opcode(b1).ok_or(Error::InvalidDictOperator(b1))?)
196    } else {
197        // See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-3-operand-encoding>
198        match b0 {
199            28 | 29 | 32..=254 => Token::Operand(parse_int(cursor, b0)?.into(), None),
200            30 => {
201                let components = BcdComponents::parse(cursor)?;
202                Token::Operand(components.value(false).into(), Some(components))
203            }
204            _ => Token::Operator(Operator::from_opcode(b0).ok_or(Error::InvalidDictOperator(b0))?),
205        }
206    })
207}
208
209/// Parse a fixed point value with a dynamic scaling factor.
210///
211/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/cff/cffparse.c#L580>
212fn parse_fixed_dynamic(cursor: &mut Cursor) -> Result<(Fixed, i32), Error> {
213    let b0 = cursor.read::<u8>()?;
214    match b0 {
215        30 => Ok(BcdComponents::parse(cursor)?.dynamically_scaled_value()),
216        28 | 29 | 32..=254 => {
217            let num = parse_int(cursor, b0)?;
218            let mut int_len = 10;
219            if num > BCD_INTEGER_LIMIT {
220                for (i, power_ten) in BCD_POWER_TENS.iter().enumerate().skip(5) {
221                    if num < *power_ten {
222                        int_len = i;
223                        break;
224                    }
225                }
226                let scaling = if (num - BCD_POWER_TENS[int_len - 5]) > BCD_INTEGER_LIMIT {
227                    int_len - 4
228                } else {
229                    int_len - 5
230                };
231                Ok((
232                    Fixed::from_bits(num) / Fixed::from_bits(BCD_POWER_TENS[scaling]),
233                    scaling as i32,
234                ))
235            } else {
236                Ok((Fixed::from_bits(num << 16), 0))
237            }
238        }
239        _ => Err(Error::InvalidNumber),
240    }
241}
242
243/// PostScript DICT Operator with its associated operands.
244#[derive(Clone, PartialEq, Eq, Debug)]
245pub enum Entry {
246    Version(StringId),
247    Notice(StringId),
248    FullName(StringId),
249    FamilyName(StringId),
250    Weight(StringId),
251    FontBbox([Fixed; 4]),
252    CharstringsOffset(usize),
253    PrivateDictRange(Range<usize>),
254    VariationStoreOffset(usize),
255    Copyright(StringId),
256    IsFixedPitch(bool),
257    ItalicAngle(Fixed),
258    UnderlinePosition(Fixed),
259    UnderlineThickness(Fixed),
260    PaintType(i32),
261    CharstringType(i32),
262    FontMatrix([Fixed; 6]),
263    StrokeWidth(Fixed),
264    FdArrayOffset(usize),
265    FdSelectOffset(usize),
266    BlueValues(Blues),
267    OtherBlues(Blues),
268    FamilyBlues(Blues),
269    FamilyOtherBlues(Blues),
270    SubrsOffset(usize),
271    VariationStoreIndex(u16),
272    BlueScale(Fixed),
273    BlueShift(Fixed),
274    BlueFuzz(Fixed),
275    LanguageGroup(i32),
276    ExpansionFactor(Fixed),
277    Encoding(usize),
278    Charset(usize),
279    UniqueId(i32),
280    Xuid,
281    SyntheticBase(i32),
282    PostScript(StringId),
283    BaseFontName(StringId),
284    BaseFontBlend,
285    Ros {
286        registry: StringId,
287        ordering: StringId,
288        supplement: Fixed,
289    },
290    CidFontVersion(Fixed),
291    CidFontRevision(Fixed),
292    CidFontType(i32),
293    CidCount(u32),
294    UidBase(i32),
295    FontName(StringId),
296    StdHw(Fixed),
297    StdVw(Fixed),
298    DefaultWidthX(Fixed),
299    NominalWidthX(Fixed),
300    StemSnapH(StemSnaps),
301    StemSnapV(StemSnaps),
302    ForceBold(bool),
303    InitialRandomSeed(i32),
304}
305
306/// Given a byte slice containing DICT data, returns an iterator yielding
307/// each operator with its associated operands.
308///
309/// This performs appropriate type conversions, decodes deltas and applies
310/// blending.
311///
312/// If processing a Private DICT from a CFF2 table and an item variation
313/// store is present, then `blend_state` must be provided.
314pub fn entries<'a>(
315    dict_data: &'a [u8],
316    mut blend_state: Option<BlendState<'a>>,
317) -> impl Iterator<Item = Result<Entry, Error>> + 'a {
318    let mut stack = Stack::new();
319    let mut last_bcd_components = None;
320    let mut cursor = crate::FontData::new(dict_data).cursor();
321    let mut cursor_pos = 0;
322    std::iter::from_fn(move || loop {
323        if cursor.remaining_bytes() == 0 {
324            return None;
325        }
326        let token = match parse_token(&mut cursor) {
327            Ok(token) => token,
328            Err(e) => return Some(Err(e)),
329        };
330        match token {
331            Token::Operand(number, bcd_components) => {
332                last_bcd_components = bcd_components;
333                match stack.push(number) {
334                    Ok(_) => continue,
335                    Err(e) => return Some(Err(e)),
336                }
337            }
338            Token::Operator(op) => {
339                if op == Operator::Blend || op == Operator::VariationStoreIndex {
340                    let state = match blend_state.as_mut() {
341                        Some(state) => state,
342                        None => return Some(Err(Error::MissingBlendState)),
343                    };
344                    if op == Operator::VariationStoreIndex {
345                        match stack
346                            .get_i32(0)
347                            .and_then(|ix| state.set_store_index(ix as u16))
348                        {
349                            Ok(_) => {}
350                            Err(e) => return Some(Err(e)),
351                        }
352                    }
353                    if op == Operator::Blend {
354                        match stack.apply_blend(state) {
355                            Ok(_) => continue,
356                            Err(e) => return Some(Err(e)),
357                        }
358                    }
359                }
360                if op == Operator::BlueScale {
361                    // FreeType parses BlueScale using a scaling factor of
362                    // 1000, presumably to capture more precision in the
363                    // fractional part. We do the same.
364                    // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/src/cff/cfftoken.h?ref_type=heads#L87>
365                    if let Some(bcd_components) = last_bcd_components.take() {
366                        // If the most recent numeric value was parsed as a
367                        // binary coded decimal then recompute the value using
368                        // the desired scaling and replace it on the stack
369                        stack.pop_fixed().ok()?;
370                        stack.push(bcd_components.value(true)).ok()?;
371                    }
372                }
373                if op == Operator::FontMatrix {
374                    // FontMatrix is also parsed specially... *sigh*
375                    // Redo the entire thing with special scaling factors
376                    // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/cff/cffparse.c#L623>
377                    // Dump the current values
378                    stack.clear();
379                    last_bcd_components = None;
380                    // Now reparse with dynamic scaling
381                    let mut cursor = crate::FontData::new(dict_data).cursor();
382                    cursor.advance_by(cursor_pos);
383                    if let Some((matrix, _upem)) = parse_font_matrix(&mut cursor) {
384                        // FreeType doesn't seem to make use of the upem
385                        // conversion factor. Seems like a bug?
386                        return Some(Ok(Entry::FontMatrix(matrix)));
387                    }
388                    continue;
389                }
390                last_bcd_components = None;
391                let entry = parse_entry(op, &mut stack);
392                stack.clear();
393                cursor_pos = cursor.position().unwrap_or_default();
394                return Some(entry);
395            }
396        }
397    })
398}
399
400fn parse_entry(op: Operator, stack: &mut Stack) -> Result<Entry, Error> {
401    use Operator::*;
402    Ok(match op {
403        Version => Entry::Version(stack.pop_i32()?.into()),
404        Notice => Entry::Notice(stack.pop_i32()?.into()),
405        FullName => Entry::FullName(stack.pop_i32()?.into()),
406        FamilyName => Entry::FamilyName(stack.pop_i32()?.into()),
407        Weight => Entry::Weight(stack.pop_i32()?.into()),
408        FontBbox => Entry::FontBbox([
409            stack.get_fixed(0)?,
410            stack.get_fixed(1)?,
411            stack.get_fixed(2)?,
412            stack.get_fixed(3)?,
413        ]),
414        CharstringsOffset => Entry::CharstringsOffset(stack.pop_i32()? as usize),
415        PrivateDictRange => {
416            let len = stack.get_i32(0)? as usize;
417            let start = stack.get_i32(1)? as usize;
418            let end = start.checked_add(len).ok_or(ReadError::OutOfBounds)?;
419            Entry::PrivateDictRange(start..end)
420        }
421        VariationStoreOffset => Entry::VariationStoreOffset(stack.pop_i32()? as usize),
422        Copyright => Entry::Copyright(stack.pop_i32()?.into()),
423        IsFixedPitch => Entry::IsFixedPitch(stack.pop_i32()? != 0),
424        ItalicAngle => Entry::ItalicAngle(stack.pop_fixed()?),
425        UnderlinePosition => Entry::UnderlinePosition(stack.pop_fixed()?),
426        UnderlineThickness => Entry::UnderlineThickness(stack.pop_fixed()?),
427        PaintType => Entry::PaintType(stack.pop_i32()?),
428        CharstringType => Entry::CharstringType(stack.pop_i32()?),
429        FontMatrix => unreachable!(),
430        StrokeWidth => Entry::StrokeWidth(stack.pop_fixed()?),
431        FdArrayOffset => Entry::FdArrayOffset(stack.pop_i32()? as usize),
432        FdSelectOffset => Entry::FdSelectOffset(stack.pop_i32()? as usize),
433        BlueValues => {
434            stack.apply_delta_prefix_sum();
435            Entry::BlueValues(Blues::new(stack.fixed_values()))
436        }
437        OtherBlues => {
438            stack.apply_delta_prefix_sum();
439            Entry::OtherBlues(Blues::new(stack.fixed_values()))
440        }
441        FamilyBlues => {
442            stack.apply_delta_prefix_sum();
443            Entry::FamilyBlues(Blues::new(stack.fixed_values()))
444        }
445        FamilyOtherBlues => {
446            stack.apply_delta_prefix_sum();
447            Entry::FamilyOtherBlues(Blues::new(stack.fixed_values()))
448        }
449        SubrsOffset => Entry::SubrsOffset(stack.pop_i32()? as usize),
450        VariationStoreIndex => Entry::VariationStoreIndex(stack.pop_i32()? as u16),
451        BlueScale => Entry::BlueScale(stack.pop_fixed()?),
452        BlueShift => Entry::BlueShift(stack.pop_fixed()?),
453        BlueFuzz => Entry::BlueFuzz(stack.pop_fixed()?),
454        LanguageGroup => Entry::LanguageGroup(stack.pop_i32()?),
455        ExpansionFactor => Entry::ExpansionFactor(stack.pop_fixed()?),
456        Encoding => Entry::Encoding(stack.pop_i32()? as usize),
457        Charset => Entry::Charset(stack.pop_i32()? as usize),
458        UniqueId => Entry::UniqueId(stack.pop_i32()?),
459        Xuid => Entry::Xuid,
460        SyntheticBase => Entry::SyntheticBase(stack.pop_i32()?),
461        PostScript => Entry::PostScript(stack.pop_i32()?.into()),
462        BaseFontName => Entry::BaseFontName(stack.pop_i32()?.into()),
463        BaseFontBlend => Entry::BaseFontBlend,
464        Ros => Entry::Ros {
465            registry: stack.get_i32(0)?.into(),
466            ordering: stack.get_i32(1)?.into(),
467            supplement: stack.get_fixed(2)?,
468        },
469        CidFontVersion => Entry::CidFontVersion(stack.pop_fixed()?),
470        CidFontRevision => Entry::CidFontRevision(stack.pop_fixed()?),
471        CidFontType => Entry::CidFontType(stack.pop_i32()?),
472        CidCount => Entry::CidCount(stack.pop_i32()? as u32),
473        UidBase => Entry::UidBase(stack.pop_i32()?),
474        FontName => Entry::FontName(stack.pop_i32()?.into()),
475        StdHw => Entry::StdHw(stack.pop_fixed()?),
476        StdVw => Entry::StdVw(stack.pop_fixed()?),
477        DefaultWidthX => Entry::DefaultWidthX(stack.pop_fixed()?),
478        NominalWidthX => Entry::NominalWidthX(stack.pop_fixed()?),
479        StemSnapH => {
480            stack.apply_delta_prefix_sum();
481            Entry::StemSnapH(StemSnaps::new(stack.fixed_values()))
482        }
483        StemSnapV => {
484            stack.apply_delta_prefix_sum();
485            Entry::StemSnapV(StemSnaps::new(stack.fixed_values()))
486        }
487        ForceBold => Entry::ForceBold(stack.pop_i32()? != 0),
488        InitialRandomSeed => Entry::InitialRandomSeed(stack.pop_i32()?),
489        // Blend is handled at the layer above
490        Blend => unreachable!(),
491    })
492}
493
494/// Parses a font matrix using dynamic scaling factors.
495///
496/// Returns the matrix and an adjusted upem factor.
497fn parse_font_matrix(cursor: &mut Cursor) -> Option<([Fixed; 6], i32)> {
498    let mut values = [Fixed::ZERO; 6];
499    let mut scalings = [0i32; 6];
500    let mut max_scaling = i32::MIN;
501    let mut min_scaling = i32::MAX;
502    for (value, scaling) in values.iter_mut().zip(&mut scalings) {
503        let (v, s) = parse_fixed_dynamic(cursor).ok()?;
504        if v != Fixed::ZERO {
505            max_scaling = max_scaling.max(s);
506            min_scaling = min_scaling.min(s);
507        }
508        *value = v;
509        *scaling = s;
510    }
511    if !(-9..=0).contains(&max_scaling)
512        || (max_scaling - min_scaling < 0)
513        || (max_scaling - min_scaling) > 9
514    {
515        return None;
516    }
517    for (value, scaling) in values.iter_mut().zip(scalings) {
518        if *value == Fixed::ZERO {
519            continue;
520        }
521        let divisor = BCD_POWER_TENS[(max_scaling - scaling) as usize];
522        let half_divisor = divisor >> 1;
523        if *value < Fixed::ZERO {
524            if i32::MIN + half_divisor < value.to_bits() {
525                *value = Fixed::from_bits((value.to_bits() - half_divisor) / divisor);
526            } else {
527                *value = Fixed::from_bits(i32::MIN / divisor);
528            }
529        } else if i32::MAX - half_divisor > value.to_bits() {
530            *value = Fixed::from_bits((value.to_bits() + half_divisor) / divisor);
531        } else {
532            *value = Fixed::from_bits(i32::MAX / divisor);
533        }
534    }
535    // Check for a degenerate matrix
536    if is_degenerate(&values) {
537        return None;
538    }
539    let mut upem = BCD_POWER_TENS[(-max_scaling) as usize];
540    // Now normalize the matrix so that the y scale factor is 1
541    // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/cff/cffobjs.c#L727>
542    let factor = if values[3] != Fixed::ZERO {
543        values[3].abs()
544    } else {
545        // Use yx if yy is zero
546        values[1].abs()
547    };
548    if factor != Fixed::ONE {
549        upem = (Fixed::from_bits(upem) / factor).to_bits();
550        for value in &mut values {
551            *value /= factor;
552        }
553    }
554    // FT shifts off the fractional parts of the translation?
555    for offset in values[4..6].iter_mut() {
556        *offset = Fixed::from_bits(offset.to_bits() >> 16);
557    }
558    Some((values, upem))
559}
560
561/// Check for a degenerate matrix.
562/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/base/ftcalc.c#L725>
563fn is_degenerate(matrix: &[Fixed; 6]) -> bool {
564    let [mut xx, mut yx, mut xy, mut yy, ..] = matrix.map(|x| x.to_bits() as i64);
565    let val = xx.abs() | yx.abs() | xy.abs() | yy.abs();
566    if val == 0 || val > 0x7FFFFFFF {
567        return true;
568    }
569    // Scale the matrix to avoid temp1 overflow
570    let msb = 32 - (val as i32).leading_zeros() - 1;
571    let shift = msb as i32 - 12;
572    if shift > 0 {
573        xx >>= shift;
574        xy >>= shift;
575        yx >>= shift;
576        yy >>= shift;
577    }
578    let temp1 = 32 * (xx * yy - xy * yx).abs();
579    let temp2 = (xx * xx) + (xy * xy) + (yx * yx) + (yy * yy);
580    if temp1 <= temp2 {
581        return true;
582    }
583    false
584}
585
586/// <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psblues.h#L141>
587const MAX_BLUE_VALUES: usize = 7;
588
589/// Operand for the `BlueValues`, `OtherBlues`, `FamilyBlues` and
590/// `FamilyOtherBlues` operators.
591///
592/// These are used to generate zones when applying hints.
593#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
594pub struct Blues {
595    values: [(Fixed, Fixed); MAX_BLUE_VALUES],
596    len: u32,
597}
598
599impl Blues {
600    pub fn new(values: impl Iterator<Item = Fixed>) -> Self {
601        let mut blues = Self::default();
602        let mut stash = Fixed::ZERO;
603        for (i, value) in values.take(MAX_BLUE_VALUES * 2).enumerate() {
604            if (i & 1) == 0 {
605                stash = value;
606            } else {
607                blues.values[i / 2] = (stash, value);
608                blues.len += 1;
609            }
610        }
611        blues
612    }
613
614    pub fn values(&self) -> &[(Fixed, Fixed)] {
615        &self.values[..self.len as usize]
616    }
617}
618
619/// Summary: older PostScript interpreters accept two values, but newer ones
620/// accept 12. We'll assume that as maximum.
621/// <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5049.StemSnap.pdf>
622const MAX_STEM_SNAPS: usize = 12;
623
624/// Operand for the `StemSnapH` and `StemSnapV` operators.
625///
626/// These are used for stem darkening when applying hints.
627#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
628pub struct StemSnaps {
629    values: [Fixed; MAX_STEM_SNAPS],
630    len: u32,
631}
632
633impl StemSnaps {
634    fn new(values: impl Iterator<Item = Fixed>) -> Self {
635        let mut snaps = Self::default();
636        for (value, target_value) in values.take(MAX_STEM_SNAPS).zip(&mut snaps.values) {
637            *target_value = value;
638            snaps.len += 1;
639        }
640        snaps
641    }
642
643    pub fn values(&self) -> &[Fixed] {
644        &self.values[..self.len as usize]
645    }
646}
647
648#[inline]
649pub(crate) fn parse_int(cursor: &mut Cursor, b0: u8) -> Result<i32, Error> {
650    // Size   b0 range     Value range              Value calculation
651    //--------------------------------------------------------------------------------
652    // 1      32 to 246    -107 to +107             b0 - 139
653    // 2      247 to 250   +108 to +1131            (b0 - 247) * 256 + b1 + 108
654    // 2      251 to 254   -1131 to -108            -(b0 - 251) * 256 - b1 - 108
655    // 3      28           -32768 to +32767         b1 << 8 | b2
656    // 5      29           -(2^31) to +(2^31 - 1)   b1 << 24 | b2 << 16 | b3 << 8 | b4
657    // <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-3-operand-encoding>
658    Ok(match b0 {
659        32..=246 => b0 as i32 - 139,
660        247..=250 => (b0 as i32 - 247) * 256 + cursor.read::<u8>()? as i32 + 108,
661        251..=254 => -(b0 as i32 - 251) * 256 - cursor.read::<u8>()? as i32 - 108,
662        28 => cursor.read::<i16>()? as i32,
663        29 => cursor.read::<i32>()?,
664        _ => {
665            return Err(Error::InvalidNumber);
666        }
667    })
668}
669
670// Various unnamed constants inlined in FreeType's cff_parse_real function
671// <<https://gitlab.freedesktop.org/freetype/freetype/-/blob/82090e67c24259c343c83fd9cefe6ff0be7a7eca/src/cff/cffparse.c#L183>>
672
673// Value returned on overflow
674const BCD_OVERFLOW: Fixed = Fixed::from_bits(0x7FFFFFFF);
675// Value returned on underflow
676const BCD_UNDERFLOW: Fixed = Fixed::ZERO;
677// Limit at which we stop accumulating `number` and increase
678// the exponent instead
679const BCD_NUMBER_LIMIT: i32 = 0xCCCCCCC;
680// Limit for the integral part of the result
681const BCD_INTEGER_LIMIT: i32 = 0x7FFF;
682
683// <https://gitlab.freedesktop.org/freetype/freetype/-/blob/82090e67c24259c343c83fd9cefe6ff0be7a7eca/src/cff/cffparse.c#L150>
684const BCD_POWER_TENS: [i32; 10] = [
685    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
686];
687
688/// Components for computing a fixed point value for a binary coded decimal
689/// number.
690#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
691pub struct BcdComponents {
692    /// If overflow or underflow is detected early, then this
693    /// contains the resulting value and we skip further
694    /// processing.
695    error: Option<Fixed>,
696    number: i32,
697    sign: i32,
698    exponent: i32,
699    exponent_add: i32,
700    integer_len: i32,
701    fraction_len: i32,
702}
703
704impl BcdComponents {
705    /// Parse a binary coded decimal number.
706    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/82090e67c24259c343c83fd9cefe6ff0be7a7eca/src/cff/cffparse.c#L183>
707    fn parse(cursor: &mut Cursor) -> Result<Self, Error> {
708        enum Phase {
709            Integer,
710            Fraction,
711            Exponent,
712        }
713        let mut phase = Phase::Integer;
714        let mut sign = 1i32;
715        let mut exponent_sign = 1i32;
716        let mut number = 0i32;
717        let mut exponent = 0i32;
718        let mut exponent_add = 0i32;
719        let mut integer_len = 0;
720        let mut fraction_len = 0;
721        // Nibble value    Represents
722        //----------------------------------
723        // 0 to 9          0 to 9
724        // a               . (decimal point)
725        // b               E
726        // c               E-
727        // d               <reserved>
728        // e               - (minus)
729        // f               end of number
730        // <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions>
731        'outer: loop {
732            let b = cursor.read::<u8>()?;
733            for nibble in [(b >> 4) & 0xF, b & 0xF] {
734                match phase {
735                    Phase::Integer => match nibble {
736                        0x0..=0x9 => {
737                            if number >= BCD_NUMBER_LIMIT {
738                                exponent_add += 1;
739                            } else if nibble != 0 || number != 0 {
740                                number = number * 10 + nibble as i32;
741                                integer_len += 1;
742                            }
743                        }
744                        0xE => sign = -1,
745                        0xA => {
746                            phase = Phase::Fraction;
747                        }
748                        0xB => {
749                            phase = Phase::Exponent;
750                        }
751                        0xC => {
752                            phase = Phase::Exponent;
753                            exponent_sign = -1;
754                        }
755                        _ => break 'outer,
756                    },
757                    Phase::Fraction => match nibble {
758                        0x0..=0x9 => {
759                            if nibble == 0 && number == 0 {
760                                exponent_add -= 1;
761                            } else if number < BCD_NUMBER_LIMIT && fraction_len < 9 {
762                                number = number * 10 + nibble as i32;
763                                fraction_len += 1;
764                            }
765                        }
766                        0xB => {
767                            phase = Phase::Exponent;
768                        }
769                        0xC => {
770                            phase = Phase::Exponent;
771                            exponent_sign = -1;
772                        }
773                        _ => break 'outer,
774                    },
775                    Phase::Exponent => {
776                        match nibble {
777                            0x0..=0x9 => {
778                                // Arbitrarily limit exponent
779                                if exponent > 1000 {
780                                    return if exponent_sign == -1 {
781                                        Ok(BCD_UNDERFLOW.into())
782                                    } else {
783                                        Ok(BCD_OVERFLOW.into())
784                                    };
785                                } else {
786                                    exponent = exponent * 10 + nibble as i32;
787                                }
788                            }
789                            _ => break 'outer,
790                        }
791                    }
792                }
793            }
794        }
795        exponent *= exponent_sign;
796        Ok(Self {
797            error: None,
798            number,
799            sign,
800            exponent,
801            exponent_add,
802            integer_len,
803            fraction_len,
804        })
805    }
806
807    /// Returns the fixed point value for the precomputed components,
808    /// optionally using an internal scale factor of 1000 to
809    /// increase fractional precision.
810    pub fn value(&self, scale_by_1000: bool) -> Fixed {
811        if let Some(error) = self.error {
812            return error;
813        }
814        let mut number = self.number;
815        if number == 0 {
816            return Fixed::ZERO;
817        }
818        let mut exponent = self.exponent;
819        let mut integer_len = self.integer_len;
820        let mut fraction_len = self.fraction_len;
821        if scale_by_1000 {
822            exponent += 3 + self.exponent_add;
823        } else {
824            exponent += self.exponent_add;
825        }
826        integer_len += exponent;
827        fraction_len -= exponent;
828        if integer_len > 5 {
829            return BCD_OVERFLOW;
830        }
831        if integer_len < -5 {
832            return BCD_UNDERFLOW;
833        }
834        // Remove non-significant digits
835        if integer_len < 0 {
836            number /= BCD_POWER_TENS[(-integer_len) as usize];
837            fraction_len += integer_len;
838        }
839        // Can only happen if exponent was non-zero
840        if fraction_len == 10 {
841            number /= 10;
842            fraction_len -= 1;
843        }
844        // Convert to fixed
845        let mut result = if fraction_len > 0 {
846            let b = BCD_POWER_TENS[fraction_len as usize];
847            if number / b > BCD_INTEGER_LIMIT {
848                0
849            } else {
850                (Fixed::from_bits(number) / Fixed::from_bits(b)).to_bits()
851            }
852        } else {
853            number = number.wrapping_mul(BCD_POWER_TENS[-fraction_len as usize]);
854            if number > BCD_INTEGER_LIMIT {
855                return BCD_OVERFLOW;
856            } else {
857                number << 16
858            }
859        };
860        if scale_by_1000 {
861            // FreeType stores the scaled value and does a fixed division by
862            // 1000 when the blue metrics are requested. We just do it here
863            // See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/psaux/psft.c#L554>
864            result = (Fixed::from_bits(result) / Fixed::from_i32(1000)).to_bits();
865        }
866        Fixed::from_bits(result * self.sign)
867    }
868
869    /// Returns the fixed point value for the components along with a
870    /// dynamically determined scale factor.
871    ///
872    /// Use for processing FontMatrix components.
873    ///
874    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/f1cd6dbfa0c98f352b698448f40ac27e8fb3832e/src/cff/cffparse.c#L332>
875    fn dynamically_scaled_value(&self) -> (Fixed, i32) {
876        if let Some(error) = self.error {
877            return (error, 0);
878        }
879        let mut number = self.number;
880        if number == 0 {
881            return (Fixed::ZERO, 0);
882        }
883        let mut exponent = self.exponent;
884        let integer_len = self.integer_len;
885        let mut fraction_len = self.fraction_len;
886        exponent += self.exponent_add;
887        fraction_len += integer_len;
888        exponent += integer_len;
889        let mut result = Fixed::ONE;
890        let mut scaling = 0;
891        if fraction_len <= 5 {
892            if number > BCD_INTEGER_LIMIT {
893                result = Fixed::from_bits(number) / Fixed::from_bits(10);
894                scaling = exponent - fraction_len + 1;
895            } else {
896                if exponent > 0 {
897                    // Make scaling as small as possible
898                    let new_fraction_len = exponent.min(5);
899                    let shift = new_fraction_len - fraction_len;
900                    if shift > 0 {
901                        exponent -= new_fraction_len;
902                        number *= BCD_POWER_TENS[shift as usize];
903                        if number > BCD_INTEGER_LIMIT {
904                            number /= 10;
905                            exponent += 1;
906                        }
907                    } else {
908                        exponent -= fraction_len;
909                    }
910                } else {
911                    exponent -= fraction_len;
912                }
913                result = Fixed::from_bits(number << 16);
914                scaling = exponent;
915            }
916        } else if (number / BCD_POWER_TENS[fraction_len as usize - 5]) > BCD_INTEGER_LIMIT {
917            result = Fixed::from_bits(number)
918                / Fixed::from_bits(BCD_POWER_TENS[fraction_len as usize - 4]);
919            scaling = exponent - 4;
920        }
921        (Fixed::from_bits(result.to_bits() * self.sign), scaling)
922    }
923}
924
925impl From<Fixed> for BcdComponents {
926    fn from(value: Fixed) -> Self {
927        Self {
928            error: Some(value),
929            ..Default::default()
930        }
931    }
932}
933
934#[cfg(test)]
935mod tests {
936    use font_test_data::bebuffer::BeBuffer;
937
938    use super::*;
939    use crate::{
940        tables::variations::ItemVariationStore, types::F2Dot14, FontData, FontRead, FontRef,
941        TableProvider,
942    };
943
944    #[test]
945    fn int_operands() {
946        // Test the boundary conditions of the ranged int operators
947        let empty = FontData::new(&[]);
948        let min_byte = FontData::new(&[0]);
949        let max_byte = FontData::new(&[255]);
950        // 32..=246 => -107..=107
951        assert_eq!(parse_int(&mut empty.cursor(), 32).unwrap(), -107);
952        assert_eq!(parse_int(&mut empty.cursor(), 246).unwrap(), 107);
953        // 247..=250 => +108 to +1131
954        assert_eq!(parse_int(&mut min_byte.cursor(), 247).unwrap(), 108);
955        assert_eq!(parse_int(&mut max_byte.cursor(), 250).unwrap(), 1131);
956        // 251..=254 => -1131 to -108
957        assert_eq!(parse_int(&mut min_byte.cursor(), 251).unwrap(), -108);
958        assert_eq!(parse_int(&mut max_byte.cursor(), 254).unwrap(), -1131);
959    }
960
961    #[test]
962    fn binary_coded_decimal_operands() {
963        // From <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions>:
964        //
965        // "A real number is terminated by one (or two) 0xf nibbles so that it is always padded
966        // to a full byte. Thus, the value -2.25 is encoded by the byte sequence (1e e2 a2 5f)
967        // and the value 0.140541E-3 by the sequence (1e 0a 14 05 41 c3 ff)."
968        //
969        // The initial 1e byte in the examples above is the dictionary operator to trigger
970        // parsing of BCD so it is dropped in the tests here.
971        let bytes = FontData::new(&[0xe2, 0xa2, 0x5f]);
972        assert_eq!(
973            BcdComponents::parse(&mut bytes.cursor())
974                .unwrap()
975                .value(false),
976            Fixed::from_f64(-2.25)
977        );
978        let bytes = FontData::new(&[0x0a, 0x14, 0x05, 0x41, 0xc3, 0xff]);
979        assert_eq!(
980            BcdComponents::parse(&mut bytes.cursor())
981                .unwrap()
982                .value(false),
983            Fixed::from_f64(0.140541E-3)
984        );
985        // Check that we match FreeType for 375e-4.
986        // Note: we used to parse 0.0375... but the new FT matching code
987        // has less precision
988        let bytes = FontData::new(&[0x37, 0x5c, 0x4f]);
989        assert_eq!(
990            BcdComponents::parse(&mut bytes.cursor())
991                .unwrap()
992                .value(false),
993            Fixed::from_f64(0.0370025634765625)
994        );
995    }
996
997    #[test]
998    fn scaled_binary_coded_decimal_operands() {
999        // For blue scale, we compute values with an internal factor of 1000 to match
1000        // FreeType, which gives us more precision for fractional bits
1001        let bytes = FontData::new(&[0xA, 0x06, 0x25, 0xf]);
1002        assert_eq!(
1003            BcdComponents::parse(&mut bytes.cursor())
1004                .unwrap()
1005                .value(true),
1006            Fixed::from_f64(0.0625)
1007        );
1008        // Just an additional check to test increased precision. Compare to
1009        // the test above where this value generates 0.0370...
1010        let bytes = FontData::new(&[0x37, 0x5c, 0x4f]);
1011        assert_eq!(
1012            BcdComponents::parse(&mut bytes.cursor())
1013                .unwrap()
1014                .value(true),
1015            Fixed::from_f64(0.037506103515625)
1016        );
1017    }
1018
1019    #[test]
1020    fn example_top_dict_tokens() {
1021        use Operator::*;
1022        let top_dict_data = &font_test_data::cff2::EXAMPLE[5..12];
1023        let tokens: Vec<_> = tokens(top_dict_data).map(|entry| entry.unwrap()).collect();
1024        let expected: &[Token] = &[
1025            68.into(),
1026            FdArrayOffset.into(),
1027            56.into(),
1028            CharstringsOffset.into(),
1029            16.into(),
1030            VariationStoreOffset.into(),
1031        ];
1032        assert_eq!(&tokens, expected);
1033    }
1034
1035    #[test]
1036    fn example_top_dict_entries() {
1037        use Entry::*;
1038        let top_dict_data = &font_test_data::cff2::EXAMPLE[0x5..=0xB];
1039        let entries: Vec<_> = entries(top_dict_data, None)
1040            .map(|entry| entry.unwrap())
1041            .collect();
1042        let expected: &[Entry] = &[
1043            FdArrayOffset(68),
1044            CharstringsOffset(56),
1045            VariationStoreOffset(16),
1046        ];
1047        assert_eq!(&entries, expected);
1048    }
1049
1050    #[test]
1051    fn example_private_dict_entries() {
1052        use Entry::*;
1053        let private_dict_data = &font_test_data::cff2::EXAMPLE[0x4f..=0xc0];
1054        let store =
1055            ItemVariationStore::read(FontData::new(&font_test_data::cff2::EXAMPLE[18..])).unwrap();
1056        let coords = &[F2Dot14::from_f32(0.0)];
1057        let blend_state = BlendState::new(store, coords, 0).unwrap();
1058        let entries: Vec<_> = entries(private_dict_data, Some(blend_state))
1059            .map(|entry| entry.unwrap())
1060            .collect();
1061        fn make_blues(values: &[f64]) -> Blues {
1062            Blues::new(values.iter().copied().map(Fixed::from_f64))
1063        }
1064        fn make_stem_snaps(values: &[f64]) -> StemSnaps {
1065            StemSnaps::new(values.iter().copied().map(Fixed::from_f64))
1066        }
1067        let expected: &[Entry] = &[
1068            BlueValues(make_blues(&[
1069                -20.0, 0.0, 472.0, 490.0, 525.0, 540.0, 645.0, 660.0, 670.0, 690.0, 730.0, 750.0,
1070            ])),
1071            OtherBlues(make_blues(&[-250.0, -240.0])),
1072            FamilyBlues(make_blues(&[
1073                -20.0, 0.0, 473.0, 491.0, 525.0, 540.0, 644.0, 659.0, 669.0, 689.0, 729.0, 749.0,
1074            ])),
1075            FamilyOtherBlues(make_blues(&[-249.0, -239.0])),
1076            BlueScale(Fixed::from_f64(0.037506103515625)),
1077            BlueFuzz(Fixed::ZERO),
1078            StdHw(Fixed::from_f64(55.0)),
1079            StdVw(Fixed::from_f64(80.0)),
1080            StemSnapH(make_stem_snaps(&[40.0, 55.0])),
1081            StemSnapV(make_stem_snaps(&[80.0, 90.0])),
1082            SubrsOffset(114),
1083        ];
1084        assert_eq!(&entries, expected);
1085    }
1086
1087    #[test]
1088    fn noto_serif_display_top_dict_entries() {
1089        use Entry::*;
1090        let top_dict_data = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED)
1091            .unwrap()
1092            .cff()
1093            .unwrap()
1094            .top_dicts()
1095            .get(0)
1096            .unwrap();
1097        let entries: Vec<_> = entries(top_dict_data, None)
1098            .map(|entry| entry.unwrap())
1099            .collect();
1100        let expected = &[
1101            Version(StringId::new(391)),
1102            Notice(StringId::new(392)),
1103            Copyright(StringId::new(393)),
1104            FullName(StringId::new(394)),
1105            FamilyName(StringId::new(395)),
1106            FontBbox([-693.0, -470.0, 2797.0, 1048.0].map(Fixed::from_f64)),
1107            Charset(517),
1108            PrivateDictRange(549..587),
1109            CharstringsOffset(521),
1110        ];
1111        assert_eq!(&entries, expected);
1112    }
1113
1114    // Fuzzer caught add with overflow when constructing private DICT
1115    // range.
1116    // See <https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=71746>
1117    // and <https://oss-fuzz.com/testcase?key=4591358306746368>
1118    #[test]
1119    fn private_dict_range_avoid_overflow() {
1120        // A Private DICT that tries to construct a range from -1..(-1 + -1)
1121        // which overflows when converted to usize
1122        let private_dict = BeBuffer::new()
1123            .push(29u8) // integer operator
1124            .push(-1i32) // integer value
1125            .push(29u8) // integer operator
1126            .push(-1i32) // integer value
1127            .push(18u8) // PrivateDICT operator
1128            .to_vec();
1129        // Just don't panic
1130        let _ = entries(&private_dict, None).count();
1131    }
1132
1133    #[test]
1134    fn dynamically_scaled_binary_coded_decimal_operands() {
1135        // 0.0625
1136        let bytes = FontData::new(&[0xA, 0x06, 0x25, 0xf]);
1137        assert_eq!(
1138            BcdComponents::parse(&mut bytes.cursor())
1139                .unwrap()
1140                .dynamically_scaled_value(),
1141            (Fixed::from_f64(6250.0), -5)
1142        );
1143        // 0.0375
1144        let bytes = FontData::new(&[0x37, 0x5c, 0x4f]);
1145        assert_eq!(
1146            BcdComponents::parse(&mut bytes.cursor())
1147                .unwrap()
1148                .dynamically_scaled_value(),
1149            (Fixed::from_f64(375.0), -4)
1150        );
1151    }
1152
1153    /// See <https://github.com/googlefonts/fontations/issues/1617>
1154    #[test]
1155    fn blue_scale_fraction_length_of_0() {
1156        // 0.0037
1157        let bytes = FontData::new(&[0x37, 0xC3, 0xFF]);
1158        assert_eq!(
1159            BcdComponents::parse(&mut bytes.cursor())
1160                .unwrap()
1161                .value(true),
1162            Fixed::from_f64(0.0370025634765625)
1163        );
1164    }
1165
1166    #[test]
1167    fn read_font_matrix() {
1168        let dict_data = [
1169            30u8, 10, 0, 31, 139, 30, 10, 0, 1, 103, 255, 30, 10, 0, 31, 139, 139, 12, 7,
1170        ];
1171        let Entry::FontMatrix(matrix) = entries(&dict_data, None).next().unwrap().unwrap() else {
1172            panic!("This was totally a font matrix");
1173        };
1174        // From ttx: <FontMatrix value="0.001 0 0.000167 0.001 0 0"/>
1175        // But scaled by 1000 because that's how FreeType does it
1176        assert_eq!(
1177            matrix,
1178            [
1179                Fixed::ONE,
1180                Fixed::ZERO,
1181                Fixed::from_f64(0.167007446289062),
1182                Fixed::ONE,
1183                Fixed::ZERO,
1184                Fixed::ZERO,
1185            ]
1186        );
1187    }
1188
1189    #[test]
1190    fn parse_degenerate_font_matrix() {
1191        let dict_data = [
1192            30u8, 0x0F, 30, 0x0F, 30, 0x0F, 30, 0x0F, 30, 0x0F, 30, 0x0F, 12, 7,
1193        ];
1194        // Don't return a degenerate matrix at all
1195        assert!(entries(&dict_data, None).next().is_none());
1196    }
1197
1198    /// See <https://github.com/googlefonts/fontations/issues/1595>
1199    #[test]
1200    fn degenerate_matrix_check_doesnt_overflow() {
1201        // Values taken from font in the above issue
1202        let matrix = [
1203            Fixed::from_bits(639999672),
1204            Fixed::ZERO,
1205            Fixed::ZERO,
1206            Fixed::from_bits(639999672),
1207            Fixed::ZERO,
1208            Fixed::ZERO,
1209        ];
1210        // Just don't panic with overflow
1211        is_degenerate(&matrix);
1212        // Try again with all max values
1213        is_degenerate(&[Fixed::MAX; 6]);
1214        // And all min values
1215        is_degenerate(&[Fixed::MIN; 6]);
1216    }
1217}