Skip to main content

read_fonts/ps/
type1.rs

1//! Type1 fonts.
2
3use super::{
4    charmap::Charmap,
5    cs::{self, CharstringContext, CharstringKind, CommandSink, NopFilterSink, TransformSink},
6    encoding::PredefinedEncoding,
7    error::Error,
8    transform::{self, FontMatrix, ScaledFontMatrix, Transform},
9};
10use crate::{
11    model::pen::OutlinePen,
12    types::{BoundingBox, Fixed, GlyphId},
13    ReadError,
14};
15use alloc::{string::String, vec::Vec};
16use core::ops::Range;
17
18/// A Type1 font.
19pub struct Type1Font {
20    name: Option<String>,
21    full_name: Option<String>,
22    family_name: Option<String>,
23    weight: Option<String>,
24    bbox: BoundingBox<Fixed>,
25    italic_angle: i32,
26    is_fixed_pitch: bool,
27    underline_position: i32,
28    underline_thickness: i32,
29    matrix: ScaledFontMatrix,
30    charstrings: Charstrings,
31    subrs: Subrs,
32    encoding: Option<RawEncoding>,
33    weight_vector: Vec<Fixed>,
34    unicode_charmap: Charmap,
35}
36
37impl Type1Font {
38    /// Creates a new Type1 font from the given data.
39    pub fn new(data: &[u8]) -> Result<Self, Error> {
40        // Any failure to parse is simply represented by an invalid font format
41        // error
42        Self::new_impl(data).ok_or(Error::InvalidFontFormat)
43    }
44
45    fn new_impl(data: &[u8]) -> Option<Self> {
46        let raw_dicts = RawDicts::new(data)?;
47        Self::from_dicts(raw_dicts.base, &raw_dicts.private)
48    }
49
50    fn empty() -> Self {
51        Self {
52            name: None,
53            full_name: None,
54            family_name: None,
55            weight: None,
56            italic_angle: 0,
57            is_fixed_pitch: false,
58            underline_position: 0,
59            underline_thickness: 0,
60            matrix: ScaledFontMatrix {
61                matrix: FontMatrix::IDENTITY,
62                scale: 1000,
63            },
64            bbox: BoundingBox::default(),
65            charstrings: Charstrings::default(),
66            subrs: Subrs::default(),
67            encoding: None,
68            weight_vector: Vec::new(),
69            unicode_charmap: Charmap::default(),
70        }
71    }
72
73    fn from_dicts(base: &[u8], private: &[u8]) -> Option<Self> {
74        let mut font = Self::empty();
75        // Read base dict entries
76        let mut encoding_offset = None;
77        let mut parser = Parser::new(base);
78        while let Some(token) = parser.next() {
79            match token {
80                Token::Name(b"FontName") => font.name = parser.read_string(),
81                Token::Name(b"FullName") => font.full_name = parser.read_string(),
82                Token::Name(b"FamilyName") => font.family_name = parser.read_string(),
83                Token::Name(b"Weight") => {
84                    // The /Weight token can appear elsewhere in MM fonts so take
85                    // the first one
86                    if font.weight.is_none() {
87                        font.weight = parser.read_string();
88                    }
89                }
90                Token::Name(b"ItalicAngle") => {
91                    font.italic_angle = parser.read_num_as_int().unwrap_or(0)
92                }
93                Token::Name(b"IsFixedPitch") => {
94                    font.is_fixed_pitch = parser.next() == Some(Token::Raw(b"true"))
95                }
96                Token::Name(b"UnderlinePosition") => {
97                    font.underline_position = parser.read_num_as_int().unwrap_or(0);
98                }
99                Token::Name(b"UnderlineThickness") => {
100                    font.underline_thickness = parser.read_num_as_int().unwrap_or(0);
101                }
102                Token::Name(b"FontBBox") => {
103                    if let Some([x_min, y_min, x_max, y_max]) = parser.read_font_bbox() {
104                        font.bbox = BoundingBox {
105                            x_min,
106                            y_min,
107                            x_max,
108                            y_max,
109                        };
110                    }
111                }
112                Token::Name(b"FontMatrix") => font.matrix = parser.read_font_matrix()?,
113                // Simply save the encoding offset. We'll parse it after
114                // we have read charstrings so we have an accurate mapping
115                // if we've synthesized or remapped a notdef glyph
116                Token::Name(b"Encoding") => encoding_offset = Some(parser.pos),
117                Token::Name(b"WeightVector") => {
118                    // Gated on a successful read because some fonts reference
119                    // the /WeightVector name outside of a proc, leading to
120                    // spurious field reads
121                    if let Some(weights) = parser.read_weight_vector() {
122                        font.weight_vector = weights;
123                    }
124                }
125                _ => {}
126            }
127        }
128        // Read private dict entries
129        let mut parser = Parser::new(private);
130        // Default value if not present
131        let mut len_iv = 4;
132        while let Some(token) = parser.next() {
133            match token {
134                Token::Name(b"lenIV") => len_iv = parser.read_int()?,
135                Token::Name(b"Subrs") => {
136                    // With synthetic fonts, it's possible to read subroutines
137                    // twice. FreeType ignores the second copy.
138                    // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1855>
139                    if font.subrs.index.is_empty() {
140                        font.subrs = parser.read_subrs(len_iv)?;
141                    }
142                }
143                Token::Name(b"CharStrings") => {
144                    // Some non-standard fonts provide multiple copies of
145                    // outlines for different resolutions and FreeType only
146                    // retains the first copy, so skip parsing if we've
147                    // already read some charstrings.
148                    // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L2058>
149                    if font.charstrings.index.is_empty() {
150                        font.charstrings = parser.read_charstrings(len_iv)?;
151                    }
152                }
153                _ => {}
154            }
155        }
156        // Reject fonts that are missing a /CharStrings array
157        // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L2665>
158        if font.charstrings.index.is_empty() {
159            return None;
160        }
161        if let Some(encoding_offset) = encoding_offset {
162            let mut parser = Parser::new(base.get(encoding_offset..)?);
163            font.encoding = Some(parser.read_encoding(&font.charstrings)?);
164        }
165        // We can only generate a Unicode cmap if we have the AGL available
166        #[cfg(feature = "agl")]
167        {
168            font.unicode_charmap = Charmap::from_glyph_names(font.glyph_names());
169        }
170        Some(font)
171    }
172
173    /// Returns the PostScript name.
174    pub fn name(&self) -> Option<&str> {
175        self.name.as_deref()
176    }
177
178    /// Returns the full font name.
179    pub fn full_name(&self) -> Option<&str> {
180        self.full_name.as_deref()
181    }
182
183    /// Returns the font family name.
184    pub fn family_name(&self) -> Option<&str> {
185        self.family_name.as_deref()
186    }
187
188    /// Returns the weight or style name.
189    pub fn weight(&self) -> Option<&str> {
190        self.weight.as_deref()
191    }
192
193    /// Returns the italic angle.
194    pub fn italic_angle(&self) -> i32 {
195        self.italic_angle
196    }
197
198    /// Returns true if the glyphs in this font have the same width.
199    pub fn is_fixed_pitch(&self) -> bool {
200        self.is_fixed_pitch
201    }
202
203    /// Returns the position of the top of an underline decoration.
204    pub fn underline_position(&self) -> i32 {
205        self.underline_position
206    }
207
208    /// Returns the suggested size for an underline decoration.
209    pub fn underline_thickness(&self) -> i32 {
210        self.underline_thickness
211    }
212
213    /// Returns the font bounding box.
214    pub fn bbox(&self) -> BoundingBox<Fixed> {
215        self.bbox
216    }
217
218    /// Returns the number of glyphs in the Type1 font.
219    pub fn num_glyphs(&self) -> u32 {
220        self.charstrings.num_glyphs()
221    }
222
223    /// Returns the units per em.
224    pub fn upem(&self) -> i32 {
225        self.matrix.scale
226    }
227
228    /// Returns the top level font matrix.
229    pub fn matrix(&self) -> FontMatrix {
230        self.matrix.matrix
231    }
232
233    /// Returns the appropriate transform for adjusting points and metrics.
234    pub fn transform(&self, ppem: Option<f32>) -> Transform {
235        let scale = ppem.map(|ppem| Transform::compute_scale(ppem, self.upem()));
236        Transform {
237            matrix: self.matrix(),
238            scale,
239        }
240    }
241
242    /// Returns the character encoding.
243    pub fn encoding(&self) -> Option<Encoding<'_>> {
244        self.encoding.as_ref().map(|enc| Encoding {
245            encoding: enc,
246            charstrings: &self.charstrings,
247        })
248    }
249
250    /// Returns the Unicode charmap for this font.
251    ///
252    /// Note that this is an empty mapping if the `agl` feature is not enabled.
253    pub fn unicode_charmap(&self) -> &Charmap {
254        &self.unicode_charmap
255    }
256
257    /// Returns the glyph name for the given id.
258    pub fn glyph_name(&self, gid: GlyphId) -> Option<&str> {
259        self.charstrings.name(gid.to_u32())
260    }
261
262    /// Returns an iterator over the pairs of glyph ids and associated names in
263    /// the Type1 font.
264    pub fn glyph_names(&self) -> impl Iterator<Item = (GlyphId, &str)> {
265        (0..self.num_glyphs())
266            .filter_map(|idx| Some((GlyphId::new(idx), self.charstrings.name(idx)?)))
267    }
268
269    /// Given a glyph identifier in the original glyph order, returns the
270    /// possibly remapped identifier.
271    ///
272    /// This occurs if we remap or synthesize a `.notdef` glyph.
273    pub fn remapped_gid(&self, original_gid: GlyphId) -> GlyphId {
274        if let Some(orig_notdef) = self.charstrings.orig_notdef_index {
275            if original_gid == GlyphId::NOTDEF {
276                GlyphId::new(orig_notdef as u32)
277            } else if orig_notdef == original_gid.to_u32() as usize {
278                GlyphId::NOTDEF
279            } else {
280                original_gid
281            }
282        } else {
283            original_gid
284        }
285    }
286
287    /// Evaluates the charstring for the requested glyph and sends the results
288    /// to the given sink.
289    ///
290    /// Returns the advance with of the glyph in font units if the charstring
291    /// provides one.
292    pub fn evaluate_charstring(
293        &self,
294        gid: GlyphId,
295        sink: &mut impl CommandSink,
296    ) -> Result<Option<Fixed>, Error> {
297        let charstring_data = self
298            .charstrings
299            .get(gid.to_u32())
300            .ok_or(ReadError::OutOfBounds)?;
301        cs::evaluate(self, None, charstring_data, sink)
302    }
303
304    /// Draws the glyph with an optional size in ppem to the given pen.
305    ///
306    /// Returns the advance width of the glyph if the charstring provides
307    /// one.
308    pub fn draw(
309        &self,
310        gid: GlyphId,
311        ppem: Option<f32>,
312        pen: &mut impl OutlinePen,
313    ) -> Result<Option<f32>, Error> {
314        let mut nop_filter = NopFilterSink::new(pen);
315        let transform = self.transform(ppem);
316        let mut transformer = TransformSink::new(&mut nop_filter, transform);
317        let width = self.evaluate_charstring(gid, &mut transformer)?;
318        Ok(width.map(|w| transform.transform_h_metric(w).to_f32().max(0.0)))
319    }
320}
321
322impl CharstringContext for Type1Font {
323    fn kind(&self) -> CharstringKind {
324        CharstringKind::Type1
325    }
326
327    fn seac_components(&self, base_code: i32, accent_code: i32) -> Result<[&[u8]; 2], Error> {
328        let decode = |code: i32| {
329            let name = PredefinedEncoding::Standard
330                .name(code.try_into().map_err(|_| Error::InvalidSeacCode(code))?);
331            self.charstrings
332                .index_for_name(name)
333                .and_then(|idx| self.charstrings.get(idx))
334                .ok_or(Error::InvalidSeacCode(code))
335        };
336        let base = decode(base_code)?;
337        let accent = decode(accent_code)?;
338        Ok([base, accent])
339    }
340
341    fn subr(&self, index: i32) -> Result<&[u8], Error> {
342        Ok(self.subrs.get(index as u32).ok_or(ReadError::OutOfBounds)?)
343    }
344
345    fn global_subr(&self, _index: i32) -> Result<&[u8], Error> {
346        // Type1 fonts don't have global subroutines
347        Err(Error::MissingSubroutines)
348    }
349
350    fn weight_vector(&self) -> &[Fixed] {
351        &self.weight_vector
352    }
353}
354
355/// Associates character codes with glyph names and ids.
356#[derive(Clone)]
357pub struct Encoding<'a> {
358    encoding: &'a RawEncoding,
359    charstrings: &'a Charstrings,
360}
361
362impl<'a> Encoding<'a> {
363    /// Returns the predefined encoding, if any.
364    pub fn predefined(&self) -> Option<PredefinedEncoding> {
365        if let RawEncoding::Predefined(pre) = self.encoding {
366            Some(*pre)
367        } else {
368            None
369        }
370    }
371
372    /// Returns the glyph name for the given character code.
373    pub fn glyph_name(&self, code: u8) -> Option<&'a str> {
374        match self.encoding {
375            RawEncoding::Predefined(pre) => Some(pre.name(code)),
376            RawEncoding::Custom(custom) => {
377                self.charstrings.name(custom.get(code as usize)?.to_u32())
378            }
379        }
380    }
381
382    /// Maps a character code to a glyph identifier.
383    pub fn map(&self, code: u8) -> Option<GlyphId> {
384        match self.encoding {
385            RawEncoding::Predefined(pre) => self
386                .charstrings
387                .index_for_name(pre.name(code))
388                .map(GlyphId::new),
389            RawEncoding::Custom(custom) => custom.get(code as usize).copied(),
390        }
391    }
392}
393
394/// Raw dictionary data for a Type1 font.
395struct RawDicts<'a> {
396    /// Data containing the base dicitionary.
397    base: &'a [u8],
398    /// Data containing the decrypted private dictionary.
399    private: Vec<u8>,
400}
401
402impl<'a> RawDicts<'a> {
403    fn new(data: &'a [u8]) -> Option<Self> {
404        if let Some((PFB_TEXT_SEGMENT_TAG, base_size)) = decode_pfb_tag(data, 0) {
405            // We have a PFB; skip the tag
406            let data = data.get(6..)?;
407            verify_header(data)?;
408            let (base_dict, raw_private_dict) = data.split_at_checked(base_size as usize)?;
409            // Decrypt private dict segments
410            let private_dict = decrypt(
411                decode_pfb_binary_segments(raw_private_dict)
412                    .flat_map(|segment| segment.iter().copied()),
413                EEXEC_SEED,
414            )
415            // First four bytes are random garbage
416            .skip(4)
417            .collect::<Vec<_>>();
418            Some(Self {
419                base: base_dict,
420                private: private_dict,
421            })
422        } else {
423            // We have a PFA
424            verify_header(data)?;
425            // Now find the start of the private dictionary
426            let start = find_eexec_data(data)?;
427            let (base_dict, raw_private_dict) = data.split_at_checked(start)?;
428            let private_dict = if raw_private_dict.len() > 3
429                && raw_private_dict[..4].iter().all(|b| b.is_ascii_hexdigit())
430            {
431                // Hex decode and then decrypt
432                decrypt(decode_hex(raw_private_dict.iter().copied()), EEXEC_SEED)
433                    .skip(4)
434                    .collect::<Vec<_>>()
435            } else {
436                // Just decrypt
437                decrypt(raw_private_dict.iter().copied(), EEXEC_SEED)
438                    .skip(4)
439                    .collect::<Vec<_>>()
440            };
441            Some(Self {
442                base: base_dict,
443                private: private_dict,
444            })
445        }
446    }
447}
448
449fn verify_header(data: &[u8]) -> Option<()> {
450    (data.starts_with(b"%!PS-AdobeFont") || data.starts_with(b"%!FontType")).then_some(())
451}
452
453const PFB_TEXT_SEGMENT_TAG: u16 = 0x8001;
454const PFB_BINARY_SEGMENT_TAG: u16 = 0x8002;
455
456/// Returns the PFB tag and segment size.
457///
458/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1parse.c#L69>
459fn decode_pfb_tag(data: &[u8], start: usize) -> Option<(u16, u32)> {
460    let header: [u8; 6] = data.get(start..start + 6)?.try_into().ok()?;
461    let tag = ((header[0] as u16) << 8) | header[1] as u16;
462    if matches!(tag, PFB_BINARY_SEGMENT_TAG | PFB_TEXT_SEGMENT_TAG) {
463        let size = u32::from_le_bytes(header[2..].try_into().unwrap());
464        Some((tag, size))
465    } else {
466        None
467    }
468}
469
470/// Returns an iterator over the sequence of PFB binary segments.
471fn decode_pfb_binary_segments(data: &[u8]) -> impl Iterator<Item = &[u8]> + '_ {
472    let mut pos = 0usize;
473    core::iter::from_fn(move || {
474        let (tag, len) = decode_pfb_tag(data, pos)?;
475        // FT only decodes the sequence of binary segments here
476        // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1parse.c#L286>
477        if tag != PFB_BINARY_SEGMENT_TAG {
478            return None;
479        }
480        // Skip tag and size bytes
481        let start = pos + 6;
482        let end = start + len as usize;
483        let segment = data.get(start..end)?;
484        pos = end;
485        Some(segment)
486    })
487}
488
489/// Helper to find the position of the data following the 'eexec' token.
490///
491/// Unsurprisingly, more complicated than it should be.
492fn find_eexec_data(data: &[u8]) -> Option<usize> {
493    // Use a parser to avoid catching "eexec" in a comment or string
494    // which apparently occurs in some fonts.
495    let mut parser = Parser::new(data);
496    while let Some(token) = parser.next() {
497        if token != Token::Raw(b"eexec") {
498            continue;
499        }
500        let mut start = parser.pos;
501        // FreeType has some unfun logic for skipping whitespace
502        // after the eexec token
503        // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1parse.c#L382>
504        let mut linefeed_pos = None;
505        while start < data.len() {
506            match data[start] {
507                b' ' | b'\t' => {}
508                b'\n' => linefeed_pos = Some(start),
509                b'\r' => {
510                    // If we've already seen \n or there is not a \n later
511                    // in the data, then stop at this \r
512                    if *linefeed_pos.get_or_insert_with(|| {
513                        data[start..]
514                            .iter()
515                            .position(|b| *b == b'\n')
516                            .map(|pos| pos + start)
517                            .unwrap_or(0)
518                    }) < start
519                    {
520                        break;
521                    }
522                }
523                _ => break,
524            }
525            start += 1;
526        }
527        if start == data.len() {
528            // eexec not properly terminated
529            return None;
530        }
531        return Some(start);
532    }
533    None
534}
535
536/// Converts hex formatted data to associated bytes.
537///
538/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psconv.c#L464>
539fn decode_hex(mut bytes: impl Iterator<Item = u8>) -> impl Iterator<Item = u8> {
540    /// Converts digits (as ASCII characters) into integer values.
541    const DIGIT_TO_NUM: [i8; 128] = [
542        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
543        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
544        -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15,
545        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1,
546        -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
547        30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
548    ];
549    let mut pad = 0x1_u32;
550    core::iter::from_fn(move || {
551        loop {
552            let Some(c) = bytes.next() else {
553                break;
554            };
555            if is_whitespace(c) {
556                continue;
557            }
558            if c >= 0x80 {
559                break;
560            }
561            let c = DIGIT_TO_NUM[(c & 0x7F) as usize] as u32;
562            if c >= 16 {
563                break;
564            }
565            pad = (pad << 4) | c;
566            if pad & 0x100 != 0 {
567                let res = pad as u8;
568                pad = 0x1;
569                return Some(res);
570            } else {
571                continue;
572            }
573        }
574        if pad != 0x1 {
575            let res = (pad << 4) as u8;
576            pad = 0x1;
577            return Some(res);
578        }
579        None
580    })
581}
582
583/// Decryption seed for eexec segment.
584const EEXEC_SEED: u32 = 55665;
585
586/// Decryption seed for charstring (and subroutine) data.
587const CHARSTRING_SEED: u32 = 4330;
588
589/// Returns an iterator yielding the decrypted bytes.
590///
591/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psconv.c#L557>
592fn decrypt(bytes: impl Iterator<Item = u8>, mut seed: u32) -> impl Iterator<Item = u8> {
593    bytes.map(move |b| {
594        let b = b as u32;
595        let plain = b ^ (seed >> 8);
596        seed = b.wrapping_add(seed).wrapping_mul(52845).wrapping_add(22719) & 0xFFFF;
597        plain as u8
598    })
599}
600
601fn is_whitespace(c: u8) -> bool {
602    if c <= 32 {
603        return matches!(c, b' ' | b'\n' | b'\r' | b'\t' | b'\0' | 0x0C);
604    }
605    false
606}
607
608/// Characters that always delimit tokens.
609///
610/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/include/freetype/internal/psaux.h#L1398>
611fn is_special(c: u8) -> bool {
612    matches!(
613        c,
614        b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
615    )
616}
617
618fn is_special_or_whitespace(c: u8) -> bool {
619    is_special(c) || is_whitespace(c)
620}
621
622#[derive(Copy, Clone, PartialEq, Eq, Debug)]
623enum Token<'a> {
624    /// Integers
625    Int(i64),
626    /// Literal strings, delimited by ()
627    LitString(&'a [u8]),
628    /// Hex strings, delimited by <>
629    HexString(&'a [u8]),
630    /// Procedures, delimited by {}
631    Proc(&'a [u8]),
632    /// Binary blobs
633    Binary(&'a [u8]),
634    /// Names, preceded by /
635    Name(&'a [u8]),
636    /// All other raw tokens (identifiers and self-delimiting punctuation)
637    Raw(&'a [u8]),
638}
639
640/// Collection of subroutines.
641#[derive(Default)]
642struct Subrs {
643    /// Packed data for all subroutines.
644    data: Vec<u8>,
645    /// Index mapping subroutine number to range in the packed data. Sorted
646    /// by subroutine number.
647    index: Vec<(u32, Range<usize>)>,
648    /// If true, subroutine number == index so we don't need to
649    /// bsearch.
650    is_dense: bool,
651}
652
653impl Subrs {
654    fn get(&self, index: u32) -> Option<&[u8]> {
655        let entry_idx = if self.is_dense {
656            index as usize
657        } else {
658            self.index.binary_search_by_key(&index, |e| e.0).ok()?
659        };
660        self.data.get(self.index.get(entry_idx)?.1.clone())
661    }
662}
663
664struct CharstringEntry {
665    name: Range<usize>,
666    data: Range<usize>,
667}
668
669/// Collection of charstrings.
670#[derive(Default)]
671struct Charstrings {
672    /// Packed data for all charstrings.
673    data: Vec<u8>,
674    /// Packed data for all glyph names.
675    names: Vec<u8>,
676    /// Index containing all charstrings.
677    index: Vec<CharstringEntry>,
678    /// If notdef was remapped, holds the original index of the notdef
679    /// charstring.
680    orig_notdef_index: Option<usize>,
681}
682
683impl Charstrings {
684    fn num_glyphs(&self) -> u32 {
685        self.index.len() as u32
686    }
687
688    fn get(&self, index: u32) -> Option<&[u8]> {
689        self.data.get(self.index.get(index as usize)?.data.clone())
690    }
691
692    fn name(&self, index: u32) -> Option<&str> {
693        core::str::from_utf8(
694            self.names
695                .get(self.index.get(index as usize)?.name.clone())?,
696        )
697        .ok()
698    }
699
700    fn index_for_name(&self, name: &str) -> Option<u32> {
701        let name = name.as_bytes();
702        for (idx, entry) in self.index.iter().enumerate() {
703            if self.names.get(entry.name.clone()) == Some(name) {
704                return Some(idx as u32);
705            }
706        }
707        None
708    }
709
710    fn push(&mut self, name: &[u8], data: &[u8], len_iv: i64) {
711        let start = self.data.len();
712        if len_iv >= 0 {
713            // use decryption; skip first len_iv bytes
714            self.data
715                .extend(decrypt(data.iter().copied(), CHARSTRING_SEED).skip(len_iv as usize));
716        } else {
717            // just add the data
718            self.data.extend_from_slice(data);
719        }
720        let end = self.data.len();
721        let name_start = self.names.len();
722        self.names.extend_from_slice(name);
723        let name_end = self.names.len();
724        self.index.push(CharstringEntry {
725            name: name_start..name_end,
726            data: start..end,
727        });
728    }
729}
730
731/// Encoding that maps characters to glyph identifiers.
732#[derive(PartialEq, Debug)]
733enum RawEncoding {
734    Predefined(PredefinedEncoding),
735    Custom(Vec<GlyphId>),
736}
737
738/// Simulated .notdef glyph, same as FreeType:
739///
740/// 0 333 hsbw endchar
741///
742/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L2192>
743const NOTDEF_GLYPH: &[u8] = &[0x8B, 0xF7, 0xE1, 0x0D, 0x0E];
744
745#[derive(Clone)]
746struct Parser<'a> {
747    data: &'a [u8],
748    pos: usize,
749}
750
751impl<'a> Parser<'a> {
752    fn new(data: &'a [u8]) -> Self {
753        Self { data, pos: 0 }
754    }
755
756    fn next(&mut self) -> Option<Token<'a>> {
757        // Roughly follows the logic of ps_parser_skip_PS_token
758        // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psobjs.c#L482>
759        loop {
760            self.skip_whitespace()?;
761            let start = self.pos;
762            let c = self.next_byte()?;
763            match c {
764                // Line comment
765                b'%' => self.skip_line(),
766                // Procedures
767                b'{' => return self.read_proc(start),
768                // Literal strings
769                b'(' => return self.read_lit_string(start),
770                b'<' => {
771                    if self.peek_byte() == Some(b'<') {
772                        // Just ignore these
773                        self.pos += 1;
774                        continue;
775                    }
776                    // Hex string: hex digits and whitespace
777                    return self.read_hex_string(start);
778                }
779                b'>' => {
780                    // We consume single '>' when parsing hex strings so a
781                    // double >> is expected here
782                    if self.next_byte()? != b'>' {
783                        return None;
784                    }
785                }
786                // Name
787                b'/' => {
788                    if let Some(c) = self.peek_byte() {
789                        if is_whitespace(c) || is_special(c) {
790                            if !is_special(c) {
791                                self.pos += 1;
792                            }
793                            return Some(Token::Name(&[]));
794                        } else {
795                            let count = self.skip_until(|c| is_whitespace(c) || is_special(c));
796                            return self.data.get(start + 1..start + count).map(Token::Name);
797                        }
798                    }
799                }
800                // Brackets
801                b'[' | b']' => {
802                    let data = self.data.get(start..start + 1)?;
803                    return Some(Token::Raw(data));
804                }
805                _ => {
806                    let count = self.skip_until(is_special_or_whitespace);
807                    let content = self.data.get(start..start + count)?;
808                    // Look for numbers but don't try to parse fractional
809                    // values since we want to handle those with special
810                    // precision
811                    if (c.is_ascii_digit() || c == b'-') && !content.contains(&b'.') {
812                        if let Some(int) = decode_int(content) {
813                            // HACK: if we have an int followed by RD or -|
814                            // then is a binary blob in Type1. Hack because
815                            // this is not actually how PostScript works
816                            // but Type1 fonts define /RD procs and this
817                            // pattern is used by FreeType.
818                            // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1351>
819                            if self.accept_blob_start() {
820                                // skip a single space
821                                self.pos += 1;
822                                // read the internal data
823                                let data = self.read_bytes(int as usize)?;
824                                // there's often some form of terminator here
825                                // but let the calling code handle it because
826                                // some buggy fonts may omit it
827                                return Some(Token::Binary(data));
828                            }
829                            return Some(Token::Int(int));
830                        }
831                    }
832                    return Some(Token::Raw(content));
833                }
834            }
835        }
836    }
837
838    /// Special lookahead case for RD or -| which indicates a binary blob in
839    /// Type1 fonts.
840    fn accept_blob_start(&mut self) -> bool {
841        let mut p = self.clone();
842        p.skip_whitespace();
843        let start = p.pos;
844        p.skip_until(is_special_or_whitespace);
845        let end = p.pos;
846        if matches!(self.data.get(start..end), Some(b"RD") | Some(b"-|")) {
847            self.pos = end;
848            true
849        } else {
850            false
851        }
852    }
853
854    fn accept(&mut self, token: Token) -> bool {
855        let mut p = self.clone();
856        if p.next() == Some(token) {
857            self.pos = p.pos;
858            true
859        } else {
860            false
861        }
862    }
863
864    fn expect(&mut self, token: Token) -> Option<()> {
865        (self.next()? == token).then_some(())
866    }
867
868    fn next_byte(&mut self) -> Option<u8> {
869        let byte = self.peek_byte()?;
870        self.pos += 1;
871        Some(byte)
872    }
873
874    fn peek_byte(&self) -> Option<u8> {
875        self.data.get(self.pos).copied()
876    }
877
878    fn read_bytes(&mut self, len: usize) -> Option<&'a [u8]> {
879        let end = self.pos.checked_add(len)?;
880        let content = self.data.get(self.pos..end)?;
881        self.pos = end;
882        Some(content)
883    }
884
885    fn skip_whitespace(&mut self) -> Option<()> {
886        while is_whitespace(*self.data.get(self.pos)?) {
887            self.pos += 1;
888        }
889        Some(())
890    }
891
892    fn skip_line(&mut self) {
893        while let Some(c) = self.next_byte() {
894            if c == b'\n' || c == b'\r' {
895                break;
896            }
897        }
898    }
899
900    fn skip_until(&mut self, f: impl Fn(u8) -> bool) -> usize {
901        let mut count = 0;
902        while let Some(byte) = self.peek_byte() {
903            if f(byte) {
904                break;
905            }
906            self.pos += 1;
907            count += 1;
908        }
909        count + 1
910    }
911
912    fn read_proc(&mut self, start: usize) -> Option<Token<'a>> {
913        let mut nest_depth = 1i32;
914        while let Some(c) = self.next_byte() {
915            match c {
916                b'{' => nest_depth = nest_depth.checked_add(1)?,
917                b'}' => {
918                    nest_depth -= 1;
919                    if nest_depth == 0 {
920                        break;
921                    }
922                }
923                // Skip over comments
924                b'%' => self.skip_line(),
925                // Skip over literal strings since they can contain braces
926                b'(' => {
927                    self.read_lit_string(self.pos - 1)?;
928                }
929                _ => {}
930            }
931        }
932        if nest_depth != 0 {
933            // unterminated procedure
934            return None;
935        }
936        let end = self.pos;
937        Some(Token::Proc(self.data.get(start + 1..end - 1)?))
938    }
939
940    fn read_lit_string(&mut self, start: usize) -> Option<Token<'a>> {
941        let mut nest_depth = 1i32;
942        while let Some(c) = self.next_byte() {
943            match c {
944                b'(' => nest_depth = nest_depth.checked_add(1)?,
945                b')' => {
946                    nest_depth -= 1;
947                    if nest_depth == 0 {
948                        break;
949                    }
950                }
951                // Escape sequence
952                b'\\' => {
953                    // Just eat the next byte. We only care
954                    // about avoiding \( and \) anyway.
955                    self.next_byte()?;
956                }
957                _ => {}
958            }
959        }
960        if nest_depth != 0 {
961            // unterminated string
962            return None;
963        }
964        let end = self.pos;
965        Some(Token::LitString(self.data.get(start + 1..end - 1)?))
966    }
967
968    fn read_hex_string(&mut self, start: usize) -> Option<Token<'a>> {
969        while let Some(c) = self.next_byte() {
970            if !is_whitespace(c) && !c.is_ascii_hexdigit() {
971                break;
972            }
973        }
974        let end = self.pos;
975        if self.data.get(end - 1) != Some(&b'>') {
976            // unterminated hex string
977            return None;
978        }
979        Some(Token::HexString(self.data.get(start + 1..end - 1)?))
980    }
981
982    fn read_int(&mut self) -> Option<i64> {
983        self.next().and_then(|t| match t {
984            Token::Int(n) => Some(n),
985            _ => None,
986        })
987    }
988
989    fn read_num_as_int(&mut self) -> Option<i32> {
990        match self.next()? {
991            Token::Int(n) => Some(n as i32),
992            // Note, FT calls PS_Conv_ToInt for fields that might contain
993            // fractional bits but just ignores everything after the
994            // initial integer, so we do the same
995            Token::Raw(bytes) => decode_int_prefix(bytes, 0).map(|n| n.0 as i32),
996            _ => None,
997        }
998    }
999
1000    fn read_string(&mut self) -> Option<String> {
1001        use alloc::borrow::ToOwned;
1002        let bytes = match self.next()? {
1003            // FreeType accepts a name or a string here
1004            // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psobjs.c#L1123>
1005            Token::Name(bytes) | Token::LitString(bytes) => bytes,
1006            _ => return None,
1007        };
1008        core::str::from_utf8(bytes).ok().map(|s| s.to_owned())
1009    }
1010}
1011
1012impl Parser<'_> {
1013    /// Parse a font matrix.
1014    ///
1015    /// Like FreeType, this is designed assuming a upem of 1000 and produces
1016    /// an identity matrix in that case. This is, the result is scaled such
1017    /// that 0.001 yields a value of 1.0.
1018    ///
1019    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1403>
1020    fn read_font_matrix(&mut self) -> Option<ScaledFontMatrix> {
1021        let mut components = [Fixed::ZERO; 6];
1022        // accept [ or { to match FreeType
1023        if !self.accept(Token::Raw(b"[")) {
1024            self.expect(Token::Raw(b"{"))?;
1025        }
1026        // read all components
1027        for component in &mut components {
1028            *component = match self.next()? {
1029                Token::Int(int) => Fixed::from_i32((int as i32).checked_mul(1000)?),
1030                Token::Raw(bytes) => decode_fixed(bytes, 3)?,
1031                _ => return None,
1032            }
1033        }
1034        // FreeType doesn't validate the closing delimiter, so just skip
1035        self.next()?;
1036        let temp_scale = components[3].abs();
1037        if temp_scale == Fixed::ZERO {
1038            return None;
1039        }
1040        let mut upem = 1000;
1041        if temp_scale != Fixed::ONE {
1042            upem = (Fixed::from_bits(1000) / temp_scale).to_bits();
1043            components[0] /= temp_scale;
1044            components[1] /= temp_scale;
1045            components[2] /= temp_scale;
1046            // don't scale components[3]
1047            components[4] /= temp_scale;
1048            components[5] /= temp_scale;
1049            if components[3] < Fixed::ZERO {
1050                components[3] = -Fixed::ONE;
1051            } else {
1052                components[3] = Fixed::ONE;
1053            }
1054        }
1055        // offsets must be expressed in integer font units
1056        for offset in components.iter_mut().skip(4) {
1057            *offset = Fixed::from_bits(offset.to_bits() >> 16);
1058        }
1059        let matrix = FontMatrix::from_elements(components);
1060        if transform::is_degenerate(&matrix) {
1061            return None;
1062        }
1063        Some(ScaledFontMatrix {
1064            matrix,
1065            scale: upem,
1066        })
1067    }
1068
1069    /// Parse the set of subroutines.
1070    ///
1071    /// The `len_iv` parameter defines the number of prefix padding bytes for
1072    /// encrypted data. If < 0, then the data is not encrypted.
1073    ///
1074    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1720>
1075    fn read_subrs(&mut self, len_iv: i64) -> Option<Subrs> {
1076        let mut subrs = Subrs::default();
1077        let _: usize = match self.next()? {
1078            Token::Raw(b"[") => {
1079                // Just an empty array
1080                self.expect(Token::Raw(b"]"))?;
1081                return Some(subrs);
1082            }
1083            Token::Int(n) => n.try_into().ok()?,
1084            _ => return None,
1085        };
1086        self.expect(Token::Raw(b"array"))?;
1087        let mut is_dense = true;
1088        // The pattern for each subroutine is `dup <subr_num> <data>`
1089        while self.accept(Token::Raw(b"dup")) {
1090            let (Token::Int(n), Token::Binary(data)) = (self.next()?, self.next()?) else {
1091                return None;
1092            };
1093            // Skip the NP, | or noaccess. FreeType just skips whatever happens
1094            // to be here
1095            self.next();
1096            // There might be an additional put token following the binary data
1097            self.accept(Token::Raw(b"put"));
1098            let subr_num: u32 = n.try_into().ok()?;
1099            if subr_num as usize != subrs.index.len() {
1100                is_dense = false;
1101            }
1102            let start = subrs.data.len();
1103            if len_iv >= 0 {
1104                // use decryption; skip first len_iv bytes
1105                subrs
1106                    .data
1107                    .extend(decrypt(data.iter().copied(), CHARSTRING_SEED).skip(len_iv as usize));
1108            } else {
1109                // just add the data
1110                subrs.data.extend_from_slice(data);
1111            }
1112            let end = subrs.data.len();
1113            subrs.index.push((subr_num, start..end));
1114        }
1115        // If we don't have a dense set, sort the index by number
1116        if !is_dense {
1117            subrs.index.sort_unstable_by_key(|(n, ..)| *n);
1118        }
1119        subrs.is_dense = is_dense;
1120        subrs.data.shrink_to_fit();
1121        subrs.index.shrink_to_fit();
1122        Some(subrs)
1123    }
1124
1125    /// Parse the set of charstrings.
1126    ///
1127    /// The `len_iv` parameter defines the number of prefix padding bytes for
1128    /// encrypted data. If < 0, then the data is not encrypted.
1129    ///
1130    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1919>
1131    fn read_charstrings(&mut self, len_iv: i64) -> Option<Charstrings> {
1132        let mut charstrings = Charstrings::default();
1133        let _: usize = match self.next()? {
1134            Token::Int(n) => n.try_into().ok()?,
1135            _ => return None,
1136        };
1137        let mut notdef_idx = None;
1138        while let Some(token) = self.next() {
1139            let name = match token {
1140                // Stop when we find a `def` or `end` keyword.
1141                // The ugliness matches the FT logic to handle some malformed
1142                // fonts:
1143                // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L2006>
1144                Token::Raw(b"end") => {
1145                    if self
1146                        .peek_byte()
1147                        .map(is_special_or_whitespace)
1148                        .unwrap_or_default()
1149                    {
1150                        break;
1151                    } else {
1152                        continue;
1153                    }
1154                }
1155                Token::Raw(b"def") => {
1156                    // but ignore `def` if no charstring has been seen
1157                    if self
1158                        .peek_byte()
1159                        .map(is_special_or_whitespace)
1160                        .unwrap_or_default()
1161                        && !charstrings.index.is_empty()
1162                    {
1163                        break;
1164                    } else {
1165                        continue;
1166                    }
1167                }
1168                Token::Name(name) => name,
1169                _ => continue,
1170            };
1171            if name == b".notdef" {
1172                notdef_idx = Some(charstrings.index.len());
1173            }
1174            let Token::Binary(data) = self.next()? else {
1175                return None;
1176            };
1177            charstrings.push(name, data, len_iv);
1178        }
1179        match notdef_idx {
1180            Some(0) => {
1181                // .notdef found and at correct location
1182            }
1183            Some(idx) => {
1184                // .notdef found but at incorrect location. Swap with the
1185                // glyph at 0
1186                charstrings.index.swap(0, idx);
1187                charstrings.orig_notdef_index = Some(idx);
1188            }
1189            None => {
1190                // .notdef not found. Add it to the end and then swap with
1191                // the glyph at 0
1192                let idx = charstrings.index.len();
1193                charstrings.push(b".notdef", NOTDEF_GLYPH, -1);
1194                charstrings.index.swap(0, idx);
1195                charstrings.orig_notdef_index = Some(idx);
1196            }
1197        }
1198        charstrings.data.shrink_to_fit();
1199        charstrings.names.shrink_to_fit();
1200        charstrings.index.shrink_to_fit();
1201        Some(charstrings)
1202    }
1203
1204    fn read_font_bbox(&mut self) -> Option<[Fixed; 4]> {
1205        let mut bbox = [Fixed::ZERO; 4];
1206        // accept [ or { to match FreeType
1207        // Note that we parse { as a procedure so this needs some special
1208        // handling
1209        let mut parser;
1210        let parser = if self.accept(Token::Raw(b"[")) {
1211            self
1212        } else if let Token::Proc(proc) = self.next()? {
1213            parser = Parser::new(proc);
1214            &mut parser
1215        } else {
1216            return None;
1217        };
1218        // read all components
1219        for component in &mut bbox {
1220            *component = match parser.next()? {
1221                Token::Int(int) => Fixed::from_i32(int as i32),
1222                Token::Raw(bytes) => decode_fixed(bytes, 0)?,
1223                _ => return None,
1224            }
1225        }
1226        Some(bbox)
1227    }
1228
1229    fn read_weight_vector(&mut self) -> Option<Vec<Fixed>> {
1230        self.accept(Token::Raw(b"["));
1231        let mut weights = Vec::new();
1232        while let Some(token) = self.next() {
1233            match token {
1234                Token::Raw(b"]") => break,
1235                Token::Int(val) => weights.push(Fixed::from_i32(val as _)),
1236                Token::Raw(raw) => weights.push(decode_fixed(raw, 0)?),
1237                _ => return None,
1238            }
1239        }
1240        Some(weights)
1241    }
1242
1243    /// Parse the encoding.
1244    ///
1245    /// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1474>
1246    fn read_encoding(&mut self, charstrings: &Charstrings) -> Option<RawEncoding> {
1247        match self.next()? {
1248            // Array of names where index == character code
1249            Token::Raw(b"[") => {
1250                let mut map = Vec::new();
1251                // Should always be 256 entries but preset values to notdef
1252                map.resize(256, GlyphId::NOTDEF);
1253                self.read_dense_encoding(|idx, name| {
1254                    if let Some((slot, gid)) = map
1255                        .get_mut(idx as usize)
1256                        .zip(charstrings.index_for_name(name))
1257                    {
1258                        *slot = gid.into();
1259                    }
1260                });
1261                Some(RawEncoding::Custom(map))
1262            }
1263            // Map of index to glyph name
1264            Token::Int(count) => {
1265                // We're limited to 256 character codes
1266                // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1518>
1267                let count: usize = count.clamp(0, 256) as usize;
1268                let mut map = Vec::new();
1269                // Start with all glyphs mapped to notdef
1270                map.resize(count, GlyphId::NOTDEF);
1271                self.read_sparse_encoding(|idx, name| {
1272                    if let Some((slot, gid)) = map
1273                        .get_mut(idx as usize)
1274                        .zip(charstrings.index_for_name(name))
1275                    {
1276                        *slot = gid.into();
1277                    }
1278                });
1279                Some(RawEncoding::Custom(map))
1280            }
1281            Token::Raw(b"StandardEncoding") => {
1282                Some(RawEncoding::Predefined(PredefinedEncoding::Standard))
1283            }
1284            Token::Raw(b"ExpertEncoding") => {
1285                Some(RawEncoding::Predefined(PredefinedEncoding::Expert))
1286            }
1287            Token::Raw(b"ISOLatin1Encoding") => {
1288                Some(RawEncoding::Predefined(PredefinedEncoding::IsoLatin1))
1289            }
1290            _ => None,
1291        }
1292    }
1293
1294    /// Returns a custom encoding defined by an array of `/<name>`, where
1295    /// the index represents the character code, and invokes the given
1296    /// callback for each.
1297    fn read_dense_encoding(&mut self, mut f: impl FnMut(i64, &str)) -> Option<()> {
1298        // Eat the opening brace if present
1299        self.accept(Token::Raw(b"["));
1300        // Always expect 256 entries
1301        // <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/type1/t1load.c#L1510>
1302        let mut idx = 0;
1303        while let Some(token) = self.next() {
1304            match token {
1305                Token::Raw(b"]") => break,
1306                Token::Name(name) => {
1307                    let code = idx;
1308                    idx += 1;
1309                    let Ok(name) = core::str::from_utf8(name) else {
1310                        continue;
1311                    };
1312                    f(code, name);
1313                }
1314                _ => {
1315                    // FreeType fails if missing a literal name here
1316                    return None;
1317                }
1318            }
1319        }
1320        Some(())
1321    }
1322
1323    /// Reads a custom encoding defined by a map of `<charcode> /<name>`
1324    /// and invokes the given callback for each.
1325    fn read_sparse_encoding(&mut self, mut f: impl FnMut(i64, &str)) -> Option<()> {
1326        while let Some(token) = self.next() {
1327            match token {
1328                // The 'def' keyword ends the mapping
1329                Token::Raw(b"def") => break,
1330                Token::Int(code) => {
1331                    // read the name
1332                    let Some(Token::Name(name)) = self.next() else {
1333                        continue;
1334                    };
1335                    let Ok(name) = core::str::from_utf8(name) else {
1336                        continue;
1337                    };
1338                    f(code, name);
1339                }
1340                _ => {}
1341            }
1342        }
1343        Some(())
1344    }
1345}
1346
1347/// Decode an integer, optionally with a base.
1348///
1349/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psconv.c#L161>
1350fn decode_int(bytes: &[u8]) -> Option<i64> {
1351    let s = std::str::from_utf8(bytes).ok()?;
1352    if let Some(hash_idx) = s.find('#') {
1353        if hash_idx == 1 || hash_idx == 2 {
1354            // It's a radix number, like 8#40.
1355            let radix_str = s.get(0..hash_idx)?;
1356            let number_str = s.get(hash_idx + 1..)?;
1357            let radix = radix_str
1358                .parse::<u32>()
1359                .ok()
1360                .filter(|n| (2..=36).contains(n))?;
1361            i64::from_str_radix(number_str, radix).ok()
1362        } else {
1363            s.parse::<i64>().ok()
1364        }
1365    } else {
1366        s.parse::<i64>().ok()
1367    }
1368}
1369
1370/// Decode an integer at the given position, returning the value and the
1371/// index of the position following the decoded integer.
1372fn decode_int_prefix(bytes: &[u8], start: usize) -> Option<(i64, usize)> {
1373    let tail = bytes.get(start..)?;
1374    let end = tail
1375        .iter()
1376        .position(|c| *c != b'-' && !c.is_ascii_digit())
1377        .unwrap_or(tail.len());
1378    let int = decode_int(tail.get(..end)?)?;
1379    Some((int, start + end))
1380}
1381
1382/// Decode a fixed point value, scaling to a specific power of
1383/// ten.
1384///
1385/// See <https://gitlab.freedesktop.org/freetype/freetype/-/blob/80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef/src/psaux/psconv.c#L195>
1386fn decode_fixed(bytes: &[u8], mut power_ten: i32) -> Option<Fixed> {
1387    const LIMIT: i32 = 0xCCCCCCC;
1388    let mut idx = 0;
1389    let &first = bytes.get(idx)?;
1390    let sign = if first == b'-' || first == b'+' {
1391        idx += 1;
1392        if first == b'-' {
1393            -1
1394        } else {
1395            1
1396        }
1397    } else {
1398        1
1399    };
1400    let overflow = || Some(Fixed::from_bits(0x7FFFFFFF * sign));
1401    let mut integral = 0;
1402    if *bytes.get(idx)? != b'.' {
1403        let (int, end_idx) = decode_int_prefix(bytes, idx)?;
1404        if int > 0x7FFF {
1405            return overflow();
1406        }
1407        integral = (int << 16) as i32;
1408        idx = end_idx;
1409    }
1410    let mut decimal = 0;
1411    let mut divider = 1;
1412    if bytes.get(idx) == Some(&b'.') {
1413        idx += 1;
1414        while let Some(byte) = bytes.get(idx).copied() {
1415            if !byte.is_ascii_digit() {
1416                break;
1417            }
1418            let digit = (byte - b'0') as i32;
1419            if divider < LIMIT && decimal < LIMIT {
1420                decimal = decimal * 10 + digit;
1421                if integral == 0 && power_ten > 0 {
1422                    power_ten -= 1;
1423                } else {
1424                    divider *= 10;
1425                }
1426            }
1427            idx += 1;
1428        }
1429    }
1430    if bytes.get(idx).map(|b| b.to_ascii_lowercase()) == Some(b'e') {
1431        idx += 1;
1432        let (exponent, _) = decode_int_prefix(bytes, idx)?;
1433        if exponent > 1000 {
1434            return overflow();
1435        } else if exponent < -1000 {
1436            // underflow
1437            return Some(Fixed::ZERO);
1438        } else {
1439            power_ten = power_ten.checked_add(exponent as i32)?;
1440        }
1441    }
1442    if integral == 0 && decimal == 0 {
1443        return Some(Fixed::ZERO);
1444    }
1445    while power_ten > 0 {
1446        if integral >= LIMIT {
1447            return overflow();
1448        }
1449        integral *= 10;
1450        if decimal >= LIMIT {
1451            if divider == 1 {
1452                return overflow();
1453            }
1454            divider /= 10;
1455        } else {
1456            decimal *= 10;
1457        }
1458        power_ten -= 1;
1459    }
1460    while power_ten < 0 {
1461        integral /= 10;
1462        if divider < LIMIT {
1463            divider *= 10;
1464        } else {
1465            decimal /= 10;
1466        }
1467        if integral == 0 && decimal == 0 {
1468            return Some(Fixed::ZERO);
1469        }
1470        power_ten += 1;
1471    }
1472    if decimal != 0 {
1473        decimal = (Fixed::from_bits(decimal) / Fixed::from_bits(divider)).to_bits();
1474        integral += decimal;
1475    }
1476    Some(Fixed::from_bits(integral * sign))
1477}
1478
1479#[cfg(test)]
1480mod tests {
1481    use super::*;
1482    use cs::test_helpers::*;
1483
1484    #[test]
1485    fn pfb_tags() {
1486        // Text segment tag
1487        let data = [0x80, 0x01, 0x01, 0x02, 0x00, 0x00];
1488        let (tag, len) = decode_pfb_tag(&data, 0).unwrap();
1489        assert_eq!(tag, PFB_TEXT_SEGMENT_TAG);
1490        assert_eq!(len, 513);
1491        // Binary segment tag
1492        let data = [0x80, 0x02, 0x01, 0x03, 0x00, 0x00];
1493        let (tag, len) = decode_pfb_tag(&data, 0).unwrap();
1494        assert_eq!(tag, PFB_BINARY_SEGMENT_TAG);
1495        assert_eq!(len, 769);
1496        // Invalid tag
1497        let data = [0x00; 6];
1498        assert!(decode_pfb_tag(&data, 0).is_none());
1499        // Not enough data
1500        let data = [0x00; 5];
1501        assert!(decode_pfb_tag(&data, 0).is_none());
1502    }
1503
1504    #[test]
1505    fn pfb_segments() {
1506        let segments = [
1507            vec![0x01; 8],
1508            vec![0x02; 10],
1509            vec![0x03; 4],
1510            vec![0x04; 255],
1511        ];
1512        // Write each segment to a buffer
1513        let mut buf = vec![];
1514        for segment in &segments {
1515            buf.push(0x80);
1516            buf.push(0x02);
1517            buf.push(segment.len() as u8);
1518            buf.extend_from_slice(&[0; 3]);
1519            for byte in segment {
1520                buf.push(*byte);
1521            }
1522        }
1523        // Now parse and compare
1524        let mut parsed_count = 0;
1525        for (parsed, expected) in decode_pfb_binary_segments(&buf).zip(&segments) {
1526            assert_eq!(parsed, expected);
1527            parsed_count += 1;
1528        }
1529        assert_eq!(parsed_count, segments.len());
1530    }
1531
1532    #[test]
1533    fn hex_decode() {
1534        check_hex_decode(
1535            b"743F8413F3636CA85A9FFEFB50B4BB27",
1536            &[
1537                116, 63, 132, 19, 243, 99, 108, 168, 90, 159, 254, 251, 80, 180, 187, 39,
1538            ],
1539        );
1540    }
1541
1542    #[test]
1543    fn hex_decode_ignores_whitespace() {
1544        check_hex_decode(
1545            b"743F 8413F3636C\nA85A9FFEF\tB50B     4BB27",
1546            &[
1547                116, 63, 132, 19, 243, 99, 108, 168, 90, 159, 254, 251, 80, 180, 187, 39,
1548            ],
1549        );
1550    }
1551
1552    #[test]
1553    fn hex_decode_truncate() {
1554        check_hex_decode(b"743F.8413F3636CA85A9FFEFB50B4BB27", &[116, 63]);
1555    }
1556
1557    #[test]
1558    fn hex_decode_odd_chars() {
1559        check_hex_decode(b"743", &[116, 48]);
1560    }
1561
1562    #[track_caller]
1563    fn check_hex_decode(hex: &[u8], expected: &[u8]) {
1564        let decoded = decode_hex(hex.iter().copied()).collect::<Vec<_>>();
1565        assert_eq!(decoded, expected);
1566    }
1567
1568    #[test]
1569    fn decrypt_bytes() {
1570        let cipher = [
1571            0x74, 0x3f, 0x84, 0x13, 0xf3, 0x63, 0x6c, 0xa8, 0x5a, 0x9f, 0xfe, 0xfb, 0x50, 0xb4,
1572            0xbb, 0x27,
1573        ];
1574        let plain = decrypt(cipher.iter().copied(), EEXEC_SEED).collect::<Vec<_>>();
1575        // First 4 bytes are random garbage
1576        assert_eq!(&plain[4..], b"dup\n/Private");
1577    }
1578
1579    #[test]
1580    fn find_eexec() {
1581        // Just a space
1582        assert_eq!(
1583            find_eexec_data(b"dup\n/Private\ncurrentfile eexec *&&FW"),
1584            Some(31)
1585        );
1586        // Multiple spaces
1587        assert_eq!(
1588            find_eexec_data(b"dup\n/Private\ncurrentfile eexec     *&&FW"),
1589            Some(35)
1590        );
1591        // New lines
1592        assert_eq!(
1593            find_eexec_data(b"dup\n/Private\ncurrentfile eexec\n\n*&&FW"),
1594            Some(32)
1595        );
1596        // Only skip \r when it precedes \n
1597        assert_eq!(
1598            find_eexec_data(b"dup\n/Private\ncurrentfile eexec\r\n\r*&&FW"),
1599            Some(32)
1600        );
1601        // Skip eexec in comments and strings
1602        assert_eq!(
1603            find_eexec_data(b"% eexec in comment\n(eexec in string) currentfile eexec $$$$"),
1604            Some(55)
1605        );
1606        // No eexec
1607        assert!(find_eexec_data(b"% eexec in comment\n(eexec in string) currentfile").is_none());
1608    }
1609
1610    #[test]
1611    fn read_pfb_raw_dicts() {
1612        let dicts = RawDicts::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFB).unwrap();
1613        check_noto_serif_base(dicts.base);
1614        check_noto_serif_private(&dicts.private);
1615    }
1616
1617    #[test]
1618    fn read_pfa_raw_dicts() {
1619        let dicts = RawDicts::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
1620        check_noto_serif_base(dicts.base);
1621        check_noto_serif_private(&dicts.private);
1622    }
1623
1624    fn check_noto_serif_base(base: &[u8]) {
1625        const EXPECTED_PREFIX: &str = r#"%!PS-AdobeFont-1.0: NotoSerif-Regular 2.007; ttfautohint (v1.8) -l 8 -r 50 -G 200 -x 14 -D latn -f none -a qsq -X ""
1626%%Title: NotoSerif-Regular
1627%Version: 2.007; ttfautohint (v1.8) -l 8 -r 50 -G 200 -x 14 -D latn -f none -a qsq -X ""
1628%%CreationDate: Tue Feb 10 16:07:25 2026
1629%%Creator: www-data
1630%Copyright: Copyright 2015-2021 Google LLC. All Rights Reserved.
1631% Generated by FontForge 20190801 (http://fontforge.sf.net/)
1632%%EndComments
1633
163410 dict begin
1635/FontType 1 def
1636/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def
1637/FontName /NotoSerif-Regular def
1638/FontBBox {5 0 989 775 }readonly def
1639"#;
1640        // git will replace \r\n with \n in pfa files on windows so strip
1641        // \r out to ensure a robust comparison
1642        let mut base = base.to_vec();
1643        base.retain(|&b| b != b'\r');
1644        assert!(base.starts_with(EXPECTED_PREFIX.as_bytes()));
1645    }
1646
1647    fn check_noto_serif_private(private: &[u8]) {
1648        const EXPECTED_PREFIX: &str = r#"dup
1649/Private 8 dict dup begin
1650/RD{string currentfile exch readstring pop}executeonly def
1651/ND{noaccess def}executeonly def
1652/NP{noaccess put}executeonly def
1653/MinFeature{16 16}ND
1654/password 5839 def
1655/BlueValues [0 0 536 536 714 714 770 770 ]ND
1656/OtherSubrs"#;
1657        assert!(private.starts_with(EXPECTED_PREFIX.as_bytes()))
1658    }
1659
1660    #[test]
1661    fn parse_ints() {
1662        check_tokens(
1663            "% a comment\n20 -30 2#1011 10#-5 %another!\r 16#fC",
1664            &[
1665                Token::Int(20),
1666                Token::Int(-30),
1667                Token::Int(11),
1668                Token::Int(-5),
1669                Token::Int(252),
1670            ],
1671        );
1672    }
1673
1674    #[test]
1675    fn parse_num_to_int() {
1676        let mut parser =
1677            Parser::new(b"102 102.1 102.4 102.5 102.9 -102.1 -102.5 -102.9 8#146 16#66");
1678        for _ in 0..10 {
1679            assert_eq!(parser.read_num_as_int().unwrap().abs(), 102);
1680        }
1681        assert!(parser.next().is_none());
1682    }
1683
1684    #[test]
1685    fn parse_strings() {
1686        check_tokens(
1687            "(string (nested) 1) % and a hex string:\n <DEAD BEEF>",
1688            &[
1689                Token::LitString(b"string (nested) 1"),
1690                Token::HexString(b"DEAD BEEF"),
1691            ],
1692        );
1693    }
1694
1695    #[test]
1696    fn parse_unterminated_strings() {
1697        check_tokens("(string (nested) 1", &[]);
1698        check_tokens("<DEAD BEEF", &[]);
1699    }
1700
1701    #[test]
1702    fn parse_procs() {
1703        check_tokens(
1704            "{a {nested 20 % comment\n} proc } % and a\n {simple proc}",
1705            &[
1706                Token::Proc(b"a {nested 20 % comment\n} proc "),
1707                Token::Proc(b"simple proc"),
1708            ],
1709        );
1710    }
1711
1712    #[test]
1713    fn parse_procs_with_string_containing_unbalanced_braces() {
1714        check_tokens(
1715            "{a proc with (string {with braces}} {) }",
1716            &[Token::Proc(b"a proc with (string {with braces}} {) ")],
1717        );
1718    }
1719
1720    #[test]
1721    fn parse_proc_with_single_int() {
1722        check_tokens(
1723            "dup 3 {3} executeonly put",
1724            &[
1725                Token::Raw(b"dup"),
1726                Token::Int(3),
1727                Token::Proc(b"3"),
1728                Token::Raw(b"executeonly"),
1729                Token::Raw(b"put"),
1730            ],
1731        );
1732    }
1733
1734    #[test]
1735    fn parse_unterminated_procs() {
1736        check_tokens("{a {nested 20} proc", &[]);
1737    }
1738
1739    #[test]
1740    fn parse_aggregate_tokens_without_whitespace() {
1741        check_tokens(
1742            "{(string)}3(string1)(string2)",
1743            &[
1744                Token::Proc(b"(string)"),
1745                Token::Int(3),
1746                Token::LitString(b"string1"),
1747                Token::LitString(b"string2"),
1748            ],
1749        );
1750    }
1751
1752    #[test]
1753    fn parse_names() {
1754        check_tokens(
1755            "/FontMatrix\r %comment\n /CharStrings",
1756            &[Token::Name(b"FontMatrix"), Token::Name(b"CharStrings")],
1757        );
1758    }
1759
1760    #[test]
1761    fn parse_binary_blobs() {
1762        check_tokens(
1763            "/.notdef 4 RD abcd \n5 11\n \t-| a83jnshf7 3 ",
1764            &[
1765                // simulates a charstring: name followed by data
1766                Token::Name(b".notdef"),
1767                Token::Binary(b"abcd"),
1768                // simulates a subr: index followed by data
1769                Token::Int(5),
1770                Token::Binary(b"a83jnshf7 3"),
1771            ],
1772        )
1773    }
1774
1775    #[test]
1776    fn parse_base_dict_prefix() {
1777        let dicts = RawDicts::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
1778        let ts = parse_to_tokens(dicts.base);
1779        assert_eq!(
1780            &ts[..19],
1781            &[
1782                Token::Int(10),
1783                Token::Raw(b"dict"),
1784                Token::Raw(b"begin"),
1785                Token::Name(b"FontType"),
1786                Token::Int(1),
1787                Token::Raw(b"def"),
1788                Token::Name(b"FontMatrix"),
1789                Token::Raw(b"["),
1790                Token::Raw(b"0.001"),
1791                Token::Int(0),
1792                Token::Int(0),
1793                Token::Raw(b"0.001"),
1794                Token::Int(0),
1795                Token::Int(0),
1796                Token::Raw(b"]"),
1797                Token::Raw(b"readonly"),
1798                Token::Raw(b"def"),
1799                Token::Name(b"FontName"),
1800                Token::Name(b"NotoSerif-Regular"),
1801            ]
1802        );
1803    }
1804
1805    #[track_caller]
1806    fn check_tokens(source: &str, expected: &[Token]) {
1807        let ts = parse_to_tokens(source.as_bytes());
1808        assert_eq!(ts, expected);
1809    }
1810
1811    fn parse_to_tokens(data: &'_ [u8]) -> Vec<Token<'_>> {
1812        let mut tokens = vec![];
1813        let mut parser = Parser::new(data);
1814        while let Some(token) = parser.next() {
1815            tokens.push(token);
1816        }
1817        tokens
1818    }
1819
1820    #[test]
1821    fn parse_fixed() {
1822        // Direct conversions (power_ten = 0)
1823        assert_eq!(decode_fixed(b"42.5", 0).unwrap(), Fixed::from_f64(42.5));
1824        assert_eq!(
1825            decode_fixed(b"0.0015", 0).unwrap(),
1826            Fixed::from_f64(0.001495361328125)
1827        );
1828        assert_eq!(
1829            decode_fixed(b"425.000e-1", 0).unwrap(),
1830            Fixed::from_f64(42.5)
1831        );
1832        assert_eq!(
1833            decode_fixed(b"1.5e-3", 0).unwrap(),
1834            Fixed::from_f64(0.001495361328125)
1835        );
1836        // Scaled by 1000 (power_ten = 3)
1837        assert_eq!(decode_fixed(b"1.5", 3).unwrap(), Fixed::from_f64(1500.0));
1838        assert_eq!(decode_fixed(b"0.001", 3).unwrap(), Fixed::from_f64(1.0));
1839        assert_eq!(
1840            decode_fixed(b"15000e-4", 3).unwrap(),
1841            Fixed::from_f64(1500.0)
1842        );
1843        assert_eq!(decode_fixed(b"1.000e-3", 3).unwrap(), Fixed::from_f64(1.0));
1844    }
1845
1846    #[test]
1847    fn parse_font_matrix() {
1848        // Standard matrix for 1000 upem
1849        assert_eq!(
1850            Parser::new(b"[0.001 0 0 0.001 0 0]")
1851                .read_font_matrix()
1852                .unwrap()
1853                .matrix,
1854            FontMatrix::IDENTITY,
1855        );
1856        // Matrix with a stretch along the x axis and a small
1857        // offset
1858        assert_eq!(
1859            Parser::new(b"[0.002 0 0 0.001 1 2e1]")
1860                .read_font_matrix()
1861                .unwrap()
1862                .matrix,
1863            FontMatrix::from_elements([
1864                Fixed::from_i32(2),
1865                Fixed::ZERO,
1866                Fixed::ZERO,
1867                Fixed::ONE,
1868                Fixed::from_bits(1000),
1869                Fixed::from_bits(20000)
1870            ])
1871        );
1872        // Matrix with modified upem
1873        assert_eq!(
1874            Parser::new(b"[0.001 0 0 0.0005 0.0 0.0]")
1875                .read_font_matrix()
1876                .unwrap(),
1877            ScaledFontMatrix {
1878                matrix: FontMatrix::from_elements([
1879                    Fixed::from_i32(2),
1880                    Fixed::ZERO,
1881                    Fixed::ZERO,
1882                    Fixed::ONE,
1883                    Fixed::from_i32(0),
1884                    Fixed::from_i32(0)
1885                ]),
1886                scale: 2000,
1887            }
1888        );
1889    }
1890
1891    #[test]
1892    fn parse_subrs() {
1893        let dicts = RawDicts::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
1894        let mut parser = Parser::new(&dicts.private);
1895        let mut subrs = None;
1896        while let Some(token) = parser.next() {
1897            if let Token::Name(b"Subrs") = token {
1898                subrs = parser.read_subrs(4);
1899                break;
1900            }
1901        }
1902        let mut subrs = subrs.unwrap();
1903        // The decrypted subroutines extracted from FreeType
1904        let expected_subrs: [&[u8]; 5] = [
1905            &[142, 139, 12, 16, 12, 17, 12, 17, 12, 33, 11],
1906            &[139, 140, 12, 16, 11],
1907            &[139, 141, 12, 16, 11],
1908            &[11],
1909            &[140, 142, 12, 16, 12, 17, 10, 11],
1910        ];
1911        assert_eq!(subrs.index.len(), expected_subrs.len());
1912        assert!(subrs.is_dense);
1913        // These subrs are densely allocated but check binary search mode
1914        // as well
1915        for is_dense in [true, false] {
1916            subrs.is_dense = is_dense;
1917            for (idx, &expected) in expected_subrs.iter().enumerate() {
1918                let subr = subrs.get(idx as u32).unwrap();
1919                assert_eq!(subr, expected);
1920            }
1921        }
1922    }
1923
1924    #[test]
1925    fn parse_empty_array_subrs() {
1926        let subrs = Parser::new(b"[ ]").read_subrs(4).unwrap();
1927        assert!(subrs.data.is_empty());
1928        assert!(subrs.index.is_empty());
1929    }
1930
1931    #[test]
1932    fn parse_empty_subrs() {
1933        let subrs = Parser::new(b" 0 array\nND\n").read_subrs(4).unwrap();
1934        assert!(subrs.data.is_empty());
1935        assert!(subrs.index.is_empty());
1936    }
1937
1938    #[test]
1939    fn parse_malformed_subrs() {
1940        assert!(Parser::new(b" 20 \nND\n").read_subrs(4).is_none());
1941    }
1942
1943    #[test]
1944    fn parse_subrs_duplicate_def() {
1945        // Two definitions of subrs.. we want to keep the first
1946        // one which has two entries at 5 and 42
1947        let private = b"/Subrs 2 array dup 5 2 RD nd NP dup 42 2 RD ab NP ND\n/Subrs 1 array dup 0 2 RD xy NP ND /CharStrings 0";
1948        let font = Type1Font::from_dicts(b"", private).unwrap();
1949        assert_eq!(font.subrs.index.len(), 2);
1950        assert_eq!(font.subrs.index[0].0, 5);
1951        assert_eq!(font.subrs.index[1].0, 42);
1952    }
1953
1954    #[test]
1955    fn parse_charstrings() {
1956        let dicts = RawDicts::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
1957        let mut parser = Parser::new(&dicts.private);
1958        let mut charstrings = None;
1959        while let Some(token) = parser.next() {
1960            if let Token::Name(b"CharStrings") = token {
1961                charstrings = parser.read_charstrings(4);
1962                break;
1963            }
1964        }
1965        let charstrings = charstrings.unwrap();
1966        assert_eq!(charstrings.num_glyphs(), 9);
1967        assert!(charstrings.orig_notdef_index.is_none());
1968        let expected_names = [
1969            ".notdef",
1970            "H",
1971            "f",
1972            "i",
1973            "x",
1974            "f_f.liga",
1975            "f_f_i.liga",
1976            "f_i.liga",
1977            "H.c2sc",
1978        ];
1979        let names = (0..charstrings.num_glyphs())
1980            .map(|idx| charstrings.name(idx).unwrap())
1981            .collect::<Vec<_>>();
1982        assert_eq!(names, expected_names);
1983        // Prefix (up to 8 bytes), extracted from FreeType
1984        let expected_charstrings_prefix: [&[u8]; 9] = [
1985            &[139, 248, 236, 13, 14],
1986            &[177, 249, 173, 13, 139, 4, 247, 183],
1987            &[166, 248, 5, 13, 139, 4, 247, 201],
1988            &[162, 247, 212, 13, 247, 30, 249, 16],
1989            &[144, 248, 214, 13, 139, 4, 247, 130],
1990            &[166, 249, 88, 13, 139, 4, 247, 181],
1991            &[166, 250, 126, 13, 139, 4, 247, 181],
1992            &[166, 249, 43, 13, 139, 4, 247, 181],
1993            &[180, 249, 60, 13, 139, 4, 247, 141],
1994        ];
1995        for (idx, &expected) in expected_charstrings_prefix.iter().enumerate() {
1996            let charstring = charstrings.get(idx as u32).unwrap();
1997            assert_eq!(&charstring[..expected.len()], expected);
1998        }
1999    }
2000
2001    #[test]
2002    fn parse_charstrings_duplicate_def() {
2003        // Two definitions of charstrings.. we want to keep the first
2004        // one which has three glyphs: .notdef, H and I
2005        let private = b"/CharStrings 2 /.notdef 2 RD nd ND /H 2 RD ab ND /I 2 RD cd ND def\n/CharStrings 1 /B 2 RD xy ND def";
2006        let font = Type1Font::from_dicts(b"", private).unwrap();
2007        assert_eq!(font.num_glyphs(), 3);
2008        assert_eq!(font.charstrings.name(0).unwrap(), ".notdef");
2009        assert_eq!(font.charstrings.name(1).unwrap(), "H");
2010        assert_eq!(font.charstrings.name(2).unwrap(), "I");
2011    }
2012
2013    #[test]
2014    fn parse_charstrings_missing_notdef() {
2015        let mut parser = Parser::new(b"1 /H 2 RD ab ND /B 2 RD xy ND");
2016        let charstrings = parser.read_charstrings(-1).unwrap();
2017        assert_eq!(charstrings.num_glyphs(), 3);
2018        assert_eq!(charstrings.orig_notdef_index, Some(2));
2019        let expected_glyphs: &[(&str, &[u8])] =
2020            &[(".notdef", NOTDEF_GLYPH), ("B", b"xy"), ("H", b"ab")];
2021        check_charstrings(&charstrings, expected_glyphs);
2022        let mut font = Type1Font::empty();
2023        font.charstrings = charstrings;
2024        assert_eq!(font.remapped_gid(GlyphId::new(0)), GlyphId::new(2));
2025        assert_eq!(font.remapped_gid(GlyphId::new(1)), GlyphId::new(1));
2026        assert_eq!(font.remapped_gid(GlyphId::new(2)), GlyphId::new(0));
2027    }
2028
2029    #[test]
2030    fn parse_charstrings_notdef_moved() {
2031        let mut parser = Parser::new(b"1 /H 2 RD ab ND /.notdef 2 RD nd ND /B 2 RD xy ND");
2032        let charstrings = parser.read_charstrings(-1).unwrap();
2033        assert_eq!(charstrings.num_glyphs(), 3);
2034        assert_eq!(charstrings.orig_notdef_index, Some(1));
2035        let expected_glyphs: &[(&str, &[u8])] = &[(".notdef", b"nd"), ("H", b"ab"), ("B", b"xy")];
2036        check_charstrings(&charstrings, expected_glyphs);
2037        let mut font = Type1Font::empty();
2038        font.charstrings = charstrings;
2039        assert_eq!(font.remapped_gid(GlyphId::new(0)), GlyphId::new(1));
2040        assert_eq!(font.remapped_gid(GlyphId::new(1)), GlyphId::new(0));
2041        assert_eq!(font.remapped_gid(GlyphId::new(2)), GlyphId::new(2));
2042    }
2043
2044    #[track_caller]
2045    fn check_charstrings(charstrings: &Charstrings, expected_glyphs: &[(&str, &[u8])]) {
2046        for (idx, expected) in expected_glyphs.iter().enumerate() {
2047            let idx = idx as u32;
2048            let name = charstrings.name(idx).unwrap();
2049            let data = charstrings.get(idx).unwrap();
2050            assert_eq!((name, data), *expected);
2051        }
2052    }
2053
2054    #[test]
2055    fn parse_weight_vector() {
2056        let mut parser = Parser::new(b"[0 0.125, 1.25 -0.87]");
2057        let weights = parser
2058            .read_weight_vector()
2059            .unwrap()
2060            .drain(..)
2061            .map(|w| w.to_f32())
2062            .collect::<Vec<_>>();
2063        assert_eq!(weights, &[0.0, 0.125, 1.25, -0.8699951]);
2064    }
2065
2066    #[test]
2067    fn parse_type1_font_pfb() {
2068        check_type1_font(
2069            &Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFB).unwrap(),
2070        );
2071    }
2072
2073    #[test]
2074    fn parse_type1_font_pfa() {
2075        check_type1_font(
2076            &Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap(),
2077        );
2078    }
2079
2080    #[track_caller]
2081    fn check_type1_font(font: &Type1Font) {
2082        assert_eq!(font.name(), Some("NotoSerif-Regular"));
2083        assert_eq!(font.full_name(), Some("Noto Serif Regular"));
2084        assert_eq!(font.family_name(), Some("Noto Serif"));
2085        assert_eq!(font.weight(), Some("Book"));
2086        assert_eq!(font.italic_angle(), 0);
2087        assert!(!font.is_fixed_pitch());
2088        assert_eq!(font.underline_position(), -125);
2089        assert_eq!(font.underline_thickness(), 50);
2090        assert_eq!(
2091            font.bbox(),
2092            BoundingBox {
2093                x_min: Fixed::from_i32(5),
2094                y_min: Fixed::ZERO,
2095                x_max: Fixed::from_i32(989),
2096                y_max: Fixed::from_i32(775)
2097            }
2098        );
2099        assert_eq!(font.num_glyphs(), 9);
2100        assert_eq!(font.subrs.index.len(), 5);
2101        assert_eq!(
2102            font.matrix,
2103            ScaledFontMatrix {
2104                matrix: FontMatrix::IDENTITY,
2105                scale: 1000
2106            }
2107        );
2108        assert!(font
2109            .glyph_names()
2110            .map(|(_, name)| name)
2111            .take(4)
2112            .eq([".notdef", "H", "f", "i"].into_iter()))
2113    }
2114
2115    #[test]
2116    fn parse_encoding() {
2117        assert!(matches!(
2118            Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA)
2119                .unwrap()
2120                .encoding,
2121            Some(RawEncoding::Predefined(PredefinedEncoding::Standard)),
2122        ));
2123    }
2124
2125    #[test]
2126    fn parse_known_encodings() {
2127        for (blob, encoding) in [
2128            (
2129                "StandardEncoding",
2130                RawEncoding::Predefined(PredefinedEncoding::Standard),
2131            ),
2132            (
2133                "ExpertEncoding",
2134                RawEncoding::Predefined(PredefinedEncoding::Expert),
2135            ),
2136            (
2137                "ISOLatin1Encoding",
2138                RawEncoding::Predefined(PredefinedEncoding::IsoLatin1),
2139            ),
2140        ] {
2141            assert_eq!(
2142                Parser::new(blob.as_bytes())
2143                    .read_encoding(&Charstrings::default())
2144                    .unwrap(),
2145                encoding
2146            );
2147        }
2148    }
2149
2150    #[test]
2151    fn parse_custom_dense_encoding() {
2152        let mut map = Vec::new();
2153        map.resize(256, ".notdef".to_string());
2154        let mut parser = Parser::new(b"[/.notdef /A /b /.notdef /comma /at]");
2155        parser.read_dense_encoding(|idx, name| {
2156            map[idx as usize] = name.to_string();
2157        });
2158        for (ch, entry) in map.iter().enumerate() {
2159            let expected = match ch {
2160                1 => "A",
2161                2 => "b",
2162                4 => "comma",
2163                5 => "at",
2164                _ => ".notdef",
2165            };
2166            assert_eq!(entry, expected);
2167        }
2168    }
2169
2170    #[test]
2171    fn parse_custom_sparse_encoding() {
2172        let mut map = Vec::new();
2173        map.resize(256, ".notdef".to_string());
2174        let mut parser = Parser::new(CUSTOM_SPARSE_ENCODING.as_bytes());
2175        parser.read_sparse_encoding(|idx, name| {
2176            map[idx as usize] = name.to_string();
2177        });
2178        for (ch, entry) in map.iter().enumerate() {
2179            let expected = match ch {
2180                66 => "B",
2181                97 => "a",
2182                64 => "at",
2183                44 => "comma",
2184                56 => "eight",
2185                _ => ".notdef",
2186            };
2187            assert_eq!(entry, expected);
2188        }
2189    }
2190
2191    const CUSTOM_SPARSE_ENCODING: &str = r#"
2192        array
2193        0 1 255 {1 index exch /.notdef put} for
2194        dup 66 /B put
2195        dup 97 /a put
2196        dup 64 /at put
2197        dup 44 /comma put
2198        dup 56 /eight put
2199        readonly def    
2200    "#;
2201
2202    #[test]
2203    fn eval_charstrings() {
2204        let font = Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
2205        let expected_eval_prefix = [
2206            "M38,0 L329,0 L329,42 L316,42 C293,42 274,46 258,54 C242,63 234,83 234,114",
2207            "M27,0 L336,0 L336,42 L298,42 C275,42 256,46 240,54 C224,63 216,83 216,114",
2208            "M161,636 C176,636 190,641 201,650 C212,659 218,675 218,698 C218,721 212,738 201,746",
2209            "M5,0 L243,0 L243,42 L240,42 C218,42 202,44 192,50 C183,54 178,62 178,73",
2210            "M27,0 L316,0 L316,42 L298,42 C275,42 256,46 240,54 C224,63 216,83 216,114",
2211            "M27,0 L316,0 L316,42 L298,42 C275,42 256,46 240,54 C224,63 216,83 216,114",
2212            "M27,0 L316,0 L316,42 L298,42 C275,42 256,46 240,54 C224,63 216,83 216,114",
2213            "M41,0 L290,0 L290,42 L269,42 C254,42 240,45 229,52 C218,58 212,73 212,98",
2214        ];
2215        // -1 to ignore the .notdef glyph
2216        assert_eq!(font.num_glyphs() as usize - 1, expected_eval_prefix.len());
2217        let mut commands = CaptureCommandSink::default();
2218        for (gid, expected_prefix) in (1..font.num_glyphs()).zip(&expected_eval_prefix) {
2219            commands.0.clear();
2220            font.evaluate_charstring(gid.into(), &mut commands).unwrap();
2221            assert!(commands.to_svg().starts_with(expected_prefix));
2222        }
2223    }
2224
2225    #[test]
2226    fn eval_charstring_widths() {
2227        let font = Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
2228        let expected_widths = [
2229            600.0, 793.0, 369.0, 320.0, 578.0, 708.0, 1002.0, 663.0, 680.0,
2230        ];
2231        let mut commands = CaptureCommandSink::default();
2232        let widths = (0..font.num_glyphs())
2233            .map(|gid| {
2234                commands.0.clear();
2235                font.evaluate_charstring(gid.into(), &mut commands)
2236                    .unwrap()
2237                    .unwrap()
2238                    .to_f32()
2239            })
2240            .collect::<Vec<_>>();
2241        assert_eq!(widths, expected_widths);
2242    }
2243
2244    #[test]
2245    fn csctx_seac_components() {
2246        let font = Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
2247        // Standard encoding for 'x' and 'i'
2248        let x_code = 120;
2249        let i_code = 105;
2250        let [x_data, i_data] = font.seac_components(x_code, i_code).unwrap();
2251        let name_to_gid = |name| {
2252            font.glyph_names()
2253                .find_map(|(gid, gname)| (name == gname).then_some(gid.to_u32()))
2254                .unwrap()
2255        };
2256        assert_eq!(x_data, font.charstrings.get(name_to_gid("x")).unwrap());
2257        assert_eq!(i_data, font.charstrings.get(name_to_gid("i")).unwrap());
2258    }
2259
2260    #[test]
2261    fn csctx_subrs() {
2262        let font = Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
2263        assert!(!font.subrs.index.is_empty());
2264        for subr_idx in 0..font.subrs.index.len() {
2265            assert_eq!(
2266                font.subrs.get(subr_idx as u32).unwrap(),
2267                font.subr(subr_idx as _).unwrap()
2268            )
2269        }
2270    }
2271
2272    #[test]
2273    fn encoding_mapping() {
2274        let font = Type1Font::new(font_test_data::type1::NOTO_SERIF_REGULAR_SUBSET_PFA).unwrap();
2275        let encoding = font.encoding().unwrap();
2276        let expected = [
2277            // code, gid, name
2278            (0, 0, ".notdef"),
2279            (72, 1, "H"),
2280            (102, 2, "f"),
2281            (105, 3, "i"),
2282            (120, 4, "x"),
2283        ];
2284        for (code, gid, name) in expected {
2285            assert_eq!(encoding.glyph_name(code).unwrap(), name);
2286            assert_eq!(encoding.map(code).unwrap().to_u32(), gid);
2287        }
2288    }
2289}