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