ttf_parser/tables/cff/
cff1.rs

1//! A [Compact Font Format Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/cff) implementation.
3
4// Useful links:
5// http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
6// http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
7// https://github.com/opentypejs/opentype.js/blob/master/src/tables/cff.js
8
9use core::convert::TryFrom;
10use core::ops::Range;
11
12use crate::{GlyphId, OutlineBuilder, Rect, BBox};
13use crate::parser::{Stream, LazyArray16, NumFrom, TryNumFrom};
14use super::{Builder, IsEven, CFFError, StringId, calc_subroutine_bias, conv_subroutine_index};
15use super::argstack::ArgumentsStack;
16use super::charset::{STANDARD_ENCODING, Charset, parse_charset};
17use super::charstring::CharStringParser;
18use super::dict::DictionaryParser;
19use super::index::{Index, parse_index, skip_index};
20#[cfg(feature = "glyph-names")] use super::std_names::STANDARD_NAMES;
21
22// Limits according to the Adobe Technical Note #5176, chapter 4 DICT Data.
23const MAX_OPERANDS_LEN: usize = 48;
24
25// Limits according to the Adobe Technical Note #5177 Appendix B.
26const STACK_LIMIT: u8 = 10;
27const MAX_ARGUMENTS_STACK_LEN: usize = 48;
28
29const TWO_BYTE_OPERATOR_MARK: u8 = 12;
30
31/// Enumerates some operators defined in the Adobe Technical Note #5177.
32mod operator {
33    pub const HORIZONTAL_STEM: u8           = 1;
34    pub const VERTICAL_STEM: u8             = 3;
35    pub const VERTICAL_MOVE_TO: u8          = 4;
36    pub const LINE_TO: u8                   = 5;
37    pub const HORIZONTAL_LINE_TO: u8        = 6;
38    pub const VERTICAL_LINE_TO: u8          = 7;
39    pub const CURVE_TO: u8                  = 8;
40    pub const CALL_LOCAL_SUBROUTINE: u8     = 10;
41    pub const RETURN: u8                    = 11;
42    pub const ENDCHAR: u8                   = 14;
43    pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18;
44    pub const HINT_MASK: u8                 = 19;
45    pub const COUNTER_MASK: u8              = 20;
46    pub const MOVE_TO: u8                   = 21;
47    pub const HORIZONTAL_MOVE_TO: u8        = 22;
48    pub const VERTICAL_STEM_HINT_MASK: u8   = 23;
49    pub const CURVE_LINE: u8                = 24;
50    pub const LINE_CURVE: u8                = 25;
51    pub const VV_CURVE_TO: u8               = 26;
52    pub const HH_CURVE_TO: u8               = 27;
53    pub const SHORT_INT: u8                 = 28;
54    pub const CALL_GLOBAL_SUBROUTINE: u8    = 29;
55    pub const VH_CURVE_TO: u8               = 30;
56    pub const HV_CURVE_TO: u8               = 31;
57    pub const HFLEX: u8                     = 34;
58    pub const FLEX: u8                      = 35;
59    pub const HFLEX1: u8                    = 36;
60    pub const FLEX1: u8                     = 37;
61    pub const FIXED_16_16: u8               = 255;
62}
63
64/// Enumerates some operators defined in the Adobe Technical Note #5176,
65/// Table 9 Top DICT Operator Entries
66mod top_dict_operator {
67    pub const CHARSET_OFFSET: u16               = 15;
68    pub const CHAR_STRINGS_OFFSET: u16          = 17;
69    pub const PRIVATE_DICT_SIZE_AND_OFFSET: u16 = 18;
70    pub const ROS: u16                          = 1230;
71    pub const FD_ARRAY: u16                     = 1236;
72    pub const FD_SELECT: u16                    = 1237;
73}
74
75/// Enumerates some operators defined in the Adobe Technical Note #5176,
76/// Table 23 Private DICT Operators
77mod private_dict_operator {
78    pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19;
79}
80
81/// Enumerates Charset IDs defined in the Adobe Technical Note #5176, Table 22
82mod charset_id {
83    pub const ISO_ADOBE: usize = 0;
84    pub const EXPERT: usize = 1;
85    pub const EXPERT_SUBSET: usize = 2;
86}
87
88#[derive(Clone, Copy, Debug)]
89pub(crate) enum FontKind<'a> {
90    SID(SIDMetadata<'a>),
91    CID(CIDMetadata<'a>),
92}
93
94#[derive(Clone, Copy, Default, Debug)]
95pub(crate) struct SIDMetadata<'a> {
96    local_subrs: Index<'a>,
97}
98
99#[derive(Clone, Copy, Default, Debug)]
100pub(crate) struct CIDMetadata<'a> {
101    fd_array: Index<'a>,
102    fd_select: FDSelect<'a>,
103}
104
105#[derive(Default)]
106struct TopDict {
107    charset_offset: Option<usize>,
108    char_strings_offset: usize,
109    private_dict_range: Option<Range<usize>>,
110    has_ros: bool,
111    fd_array_offset: Option<usize>,
112    fd_select_offset: Option<usize>,
113}
114
115fn parse_top_dict(s: &mut Stream) -> Option<TopDict> {
116    let mut top_dict = TopDict::default();
117
118    let index = parse_index::<u16>(s)?;
119
120    // The Top DICT INDEX should have only one dictionary.
121    let data = index.get(0)?;
122
123    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
124    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
125    while let Some(operator) = dict_parser.parse_next() {
126        match operator.get() {
127            top_dict_operator::CHARSET_OFFSET => {
128                top_dict.charset_offset = dict_parser.parse_offset();
129            }
130            top_dict_operator::CHAR_STRINGS_OFFSET => {
131                top_dict.char_strings_offset = dict_parser.parse_offset()?;
132            }
133            top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET => {
134                top_dict.private_dict_range = dict_parser.parse_range();
135            }
136            top_dict_operator::ROS => {
137                top_dict.has_ros = true;
138            }
139            top_dict_operator::FD_ARRAY => {
140                top_dict.fd_array_offset = dict_parser.parse_offset();
141            }
142            top_dict_operator::FD_SELECT => {
143                top_dict.fd_select_offset = dict_parser.parse_offset();
144            }
145            _ => {}
146        }
147    }
148
149    Some(top_dict)
150}
151
152// TODO: move to integration
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn private_dict_size_overflow() {
159        let data = &[
160            0x00, 0x01, // count: 1
161            0x01, // offset size: 1
162            0x01, // index [0]: 1
163            0x0C, // index [1]: 14
164            0x1D, 0x7F, 0xFF, 0xFF, 0xFF, // length: i32::MAX
165            0x1D, 0x7F, 0xFF, 0xFF, 0xFF, // offset: i32::MAX
166            0x12 // operator: 18 (private)
167        ];
168
169        let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
170        assert_eq!(top_dict.private_dict_range, Some(2147483647..4294967294));
171    }
172
173    #[test]
174    fn private_dict_negative_char_strings_offset() {
175        let data = &[
176            0x00, 0x01, // count: 1
177            0x01, // offset size: 1
178            0x01, // index [0]: 1
179            0x03, // index [1]: 3
180            // Item 0
181            0x8A, // offset: -1
182            0x11, // operator: 17 (char_string)
183        ];
184
185        assert!(parse_top_dict(&mut Stream::new(data)).is_none());
186    }
187
188    #[test]
189    fn private_dict_no_char_strings_offset_operand() {
190        let data = &[
191            0x00, 0x01, // count: 1
192            0x01, // offset size: 1
193            0x01, // index [0]: 1
194            0x02, // index [1]: 2
195            // Item 0
196            // <-- No number here.
197            0x11, // operator: 17 (char_string)
198        ];
199
200        assert!(parse_top_dict(&mut Stream::new(data)).is_none());
201    }
202
203    #[test]
204    fn negative_private_dict_offset_and_size() {
205        let data = &[
206            0x00, 0x01, // count: 1
207            0x01, // offset size: 1
208            0x01, // index [0]: 1
209            0x04, // index [1]: 4
210            // Item 0
211            0x8A, // length: -1
212            0x8A, // offset: -1
213            0x12, // operator: 18 (private)
214        ];
215
216        let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
217        assert!(top_dict.private_dict_range.is_none());
218    }
219}
220
221fn parse_private_dict(data: &[u8]) -> Option<usize> {
222    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
223    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
224    while let Some(operator) = dict_parser.parse_next() {
225        if operator.get() == private_dict_operator::LOCAL_SUBROUTINES_OFFSET {
226            return dict_parser.parse_offset();
227        }
228    }
229
230    None
231}
232
233fn parse_font_dict(data: &[u8]) -> Option<Range<usize>> {
234    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
235    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
236    while let Some(operator) = dict_parser.parse_next() {
237        if operator.get() == top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET {
238            return dict_parser.parse_range();
239        }
240    }
241
242    None
243}
244
245/// In CID fonts, to get local subroutines we have to:
246///   1. Find Font DICT index via FDSelect by GID.
247///   2. Get Font DICT data from FDArray using this index.
248///   3. Get a Private DICT offset from a Font DICT.
249///   4. Get a local subroutine offset from Private DICT.
250///   5. Parse a local subroutine at offset.
251fn parse_cid_local_subrs<'a>(
252    data: &'a [u8],
253    glyph_id: GlyphId,
254    cid: &CIDMetadata,
255) -> Option<Index<'a>> {
256    let font_dict_index = cid.fd_select.font_dict_index(glyph_id)?;
257    let font_dict_data = cid.fd_array.get(u32::from(font_dict_index))?;
258    let private_dict_range = parse_font_dict(font_dict_data)?;
259    let private_dict_data = data.get(private_dict_range.clone())?;
260    let subroutines_offset = parse_private_dict(private_dict_data)?;
261
262    // 'The local subroutines offset is relative to the beginning
263    // of the Private DICT data.'
264    let start = private_dict_range.start.checked_add(subroutines_offset)?;
265    let subrs_data = data.get(start..)?;
266    let mut s = Stream::new(subrs_data);
267    parse_index::<u16>(&mut s)
268}
269
270struct CharStringParserContext<'a> {
271    metadata: &'a Table<'a>,
272    width_parsed: bool,
273    stems_len: u32,
274    has_endchar: bool,
275    has_seac: bool,
276    glyph_id: GlyphId, // Required to parse local subroutine in CID fonts.
277    local_subrs: Option<Index<'a>>,
278}
279
280fn parse_char_string(
281    data: &[u8],
282    metadata: &Table,
283    glyph_id: GlyphId,
284    builder: &mut dyn OutlineBuilder,
285) -> Result<Rect, CFFError> {
286    let local_subrs = match metadata.kind {
287        FontKind::SID(ref sid) => Some(sid.local_subrs),
288        FontKind::CID(_) => None, // Will be resolved on request.
289    };
290
291    let mut ctx = CharStringParserContext {
292        metadata,
293        width_parsed: false,
294        stems_len: 0,
295        has_endchar: false,
296        has_seac: false,
297        glyph_id,
298        local_subrs,
299    };
300
301    let mut inner_builder = Builder {
302        builder,
303        bbox: BBox::new(),
304    };
305
306    let stack = ArgumentsStack {
307        data: &mut [0.0; MAX_ARGUMENTS_STACK_LEN], // 192B
308        len: 0,
309        max_len: MAX_ARGUMENTS_STACK_LEN,
310    };
311    let mut parser = CharStringParser {
312        stack,
313        builder: &mut inner_builder,
314        x: 0.0,
315        y: 0.0,
316        has_move_to: false,
317        is_first_move_to: true,
318    };
319    _parse_char_string(&mut ctx, data, 0, &mut parser)?;
320
321    if !ctx.has_endchar {
322        return Err(CFFError::MissingEndChar);
323    }
324
325    let bbox = parser.builder.bbox;
326
327    // Check that bbox was changed.
328    if bbox.is_default() {
329        return Err(CFFError::ZeroBBox);
330    }
331
332    bbox.to_rect().ok_or(CFFError::BboxOverflow)
333}
334
335
336fn _parse_char_string(
337    ctx: &mut CharStringParserContext,
338    char_string: &[u8],
339    depth: u8,
340    p: &mut CharStringParser,
341) -> Result<(), CFFError> {
342    let mut s = Stream::new(char_string);
343    while !s.at_end() {
344        let op = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?;
345        match op {
346            0 | 2 | 9 | 13 | 15 | 16 | 17 => {
347                // Reserved.
348                return Err(CFFError::InvalidOperator);
349            }
350            operator::HORIZONTAL_STEM |
351            operator::VERTICAL_STEM |
352            operator::HORIZONTAL_STEM_HINT_MASK |
353            operator::VERTICAL_STEM_HINT_MASK => {
354                // y dy {dya dyb}* hstem
355                // x dx {dxa dxb}* vstem
356                // y dy {dya dyb}* hstemhm
357                // x dx {dxa dxb}* vstemhm
358
359                // If the stack length is uneven, than the first value is a `width`.
360                let len = if p.stack.len().is_odd() && !ctx.width_parsed {
361                    ctx.width_parsed = true;
362                    p.stack.len() - 1
363                } else {
364                    p.stack.len()
365                };
366
367                ctx.stems_len += len as u32 >> 1;
368
369                // We are ignoring the hint operators.
370                p.stack.clear();
371            }
372            operator::VERTICAL_MOVE_TO => {
373                let mut i = 0;
374                if p.stack.len() == 2 && !ctx.width_parsed {
375                    i += 1;
376                    ctx.width_parsed = true;
377                }
378
379                p.parse_vertical_move_to(i)?;
380            }
381            operator::LINE_TO => {
382                p.parse_line_to()?;
383            }
384            operator::HORIZONTAL_LINE_TO => {
385                p.parse_horizontal_line_to()?;
386            }
387            operator::VERTICAL_LINE_TO => {
388                p.parse_vertical_line_to()?;
389            }
390            operator::CURVE_TO => {
391                p.parse_curve_to()?;
392            }
393            operator::CALL_LOCAL_SUBROUTINE => {
394                if p.stack.is_empty() {
395                    return Err(CFFError::InvalidArgumentsStackLength);
396                }
397
398                if depth == STACK_LIMIT {
399                    return Err(CFFError::NestingLimitReached);
400                }
401
402                // Parse and remember the local subroutine for the current glyph.
403                // Since it's a pretty complex task, we're doing it only when
404                // a local subroutine is actually requested by the glyphs charstring.
405                if ctx.local_subrs.is_none() {
406                    if let FontKind::CID(ref cid) = ctx.metadata.kind {
407                        ctx.local_subrs = parse_cid_local_subrs(
408                            ctx.metadata.table_data, ctx.glyph_id, cid
409                        );
410                    }
411                }
412
413                if let Some(local_subrs) = ctx.local_subrs {
414                    let subroutine_bias = calc_subroutine_bias(local_subrs.len());
415                    let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
416                    let char_string = local_subrs.get(index).ok_or(CFFError::InvalidSubroutineIndex)?;
417                    _parse_char_string(ctx, char_string, depth + 1, p)?;
418                } else {
419                    return Err(CFFError::NoLocalSubroutines);
420                }
421
422                if ctx.has_endchar && !ctx.has_seac {
423                    if !s.at_end() {
424                        return Err(CFFError::DataAfterEndChar);
425                    }
426
427                    break;
428                }
429            }
430            operator::RETURN => {
431                break;
432            }
433            TWO_BYTE_OPERATOR_MARK => {
434                // flex
435                let op2 = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?;
436                match op2 {
437                    operator::HFLEX => p.parse_hflex()?,
438                    operator::FLEX => p.parse_flex()?,
439                    operator::HFLEX1 => p.parse_hflex1()?,
440                    operator::FLEX1 => p.parse_flex1()?,
441                    _ => return Err(CFFError::UnsupportedOperator),
442                }
443            }
444            operator::ENDCHAR => {
445                if p.stack.len() == 4 || (!ctx.width_parsed && p.stack.len() == 5) {
446                    // Process 'seac'.
447                    let accent_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop())
448                        .ok_or(CFFError::InvalidSeacCode)?;
449                    let base_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop())
450                        .ok_or(CFFError::InvalidSeacCode)?;
451                    let dy = p.stack.pop();
452                    let dx = p.stack.pop();
453
454                    if !ctx.width_parsed && !p.stack.is_empty() {
455                        p.stack.pop();
456                        ctx.width_parsed = true;
457                    }
458
459                    ctx.has_seac = true;
460
461                    if depth == STACK_LIMIT {
462                        return Err(CFFError::NestingLimitReached);
463                    }
464
465                    let base_char_string = ctx.metadata.char_strings.get(u32::from(base_char.0))
466                        .ok_or(CFFError::InvalidSeacCode)?;
467                    _parse_char_string(ctx, base_char_string, depth + 1, p)?;
468                    p.x = dx;
469                    p.y = dy;
470
471                    let accent_char_string = ctx.metadata.char_strings.get(u32::from(accent_char.0))
472                        .ok_or(CFFError::InvalidSeacCode)?;
473                    _parse_char_string(ctx, accent_char_string, depth + 1, p)?;
474                } else if p.stack.len() == 1 && !ctx.width_parsed {
475                    p.stack.pop();
476                    ctx.width_parsed = true;
477                }
478
479                if !p.is_first_move_to {
480                    p.is_first_move_to = true;
481                    p.builder.close();
482                }
483
484                if !s.at_end() {
485                    return Err(CFFError::DataAfterEndChar);
486                }
487
488                ctx.has_endchar = true;
489
490                break;
491            }
492            operator::HINT_MASK | operator::COUNTER_MASK => {
493                let mut len = p.stack.len();
494
495                // We are ignoring the hint operators.
496                p.stack.clear();
497
498                // If the stack length is uneven, than the first value is a `width`.
499                if len.is_odd() && !ctx.width_parsed {
500                    len -= 1;
501                    ctx.width_parsed = true;
502                }
503
504                ctx.stems_len += len as u32 >> 1;
505
506                s.advance(usize::num_from((ctx.stems_len + 7) >> 3));
507            }
508            operator::MOVE_TO => {
509                let mut i = 0;
510                if p.stack.len() == 3 && !ctx.width_parsed {
511                    i += 1;
512                    ctx.width_parsed = true;
513                }
514
515                p.parse_move_to(i)?;
516            }
517            operator::HORIZONTAL_MOVE_TO => {
518                let mut i = 0;
519                if p.stack.len() == 2 && !ctx.width_parsed {
520                    i += 1;
521                    ctx.width_parsed = true;
522                }
523
524                p.parse_horizontal_move_to(i)?;
525            }
526            operator::CURVE_LINE => {
527                p.parse_curve_line()?;
528            }
529            operator::LINE_CURVE => {
530                p.parse_line_curve()?;
531            }
532            operator::VV_CURVE_TO => {
533                p.parse_vv_curve_to()?;
534            }
535            operator::HH_CURVE_TO => {
536                p.parse_hh_curve_to()?;
537            }
538            operator::SHORT_INT => {
539                let n = s.read::<i16>().ok_or(CFFError::ReadOutOfBounds)?;
540                p.stack.push(f32::from(n))?;
541            }
542            operator::CALL_GLOBAL_SUBROUTINE => {
543                if p.stack.is_empty() {
544                    return Err(CFFError::InvalidArgumentsStackLength);
545                }
546
547                if depth == STACK_LIMIT {
548                    return Err(CFFError::NestingLimitReached);
549                }
550
551                let subroutine_bias = calc_subroutine_bias(ctx.metadata.global_subrs.len());
552                let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
553                let char_string = ctx.metadata.global_subrs.get(index)
554                    .ok_or(CFFError::InvalidSubroutineIndex)?;
555                _parse_char_string(ctx, char_string, depth + 1, p)?;
556
557                if ctx.has_endchar && !ctx.has_seac {
558                    if !s.at_end() {
559                        return Err(CFFError::DataAfterEndChar);
560                    }
561
562                    break;
563                }
564            }
565            operator::VH_CURVE_TO => {
566                p.parse_vh_curve_to()?;
567            }
568            operator::HV_CURVE_TO => {
569                p.parse_hv_curve_to()?;
570            }
571            32..=246 => {
572                p.parse_int1(op)?;
573            }
574            247..=250 => {
575                p.parse_int2(op, &mut s)?;
576            }
577            251..=254 => {
578                p.parse_int3(op, &mut s)?;
579            }
580            operator::FIXED_16_16 => {
581                p.parse_fixed(&mut s)?;
582            }
583        }
584    }
585
586    // TODO: 'A charstring subroutine must end with either an endchar or a return operator.'
587
588    Ok(())
589}
590
591fn seac_code_to_glyph_id(charset: &Charset, n: f32) -> Option<GlyphId> {
592    let code = u8::try_num_from(n)?;
593
594    let sid = STANDARD_ENCODING[code as usize];
595    let sid = StringId(u16::from(sid));
596
597    match charset {
598        Charset::ISOAdobe => {
599            // ISO Adobe charset only defines string ids up to 228 (zcaron)
600            if code <= 228 { Some(GlyphId(sid.0)) } else { None }
601        }
602        Charset::Expert | Charset::ExpertSubset => None,
603        _ => charset.sid_to_gid(sid),
604    }
605}
606
607
608#[derive(Clone, Copy, Debug)]
609enum FDSelect<'a> {
610    Format0(LazyArray16<'a, u8>),
611    Format3(&'a [u8]), // It's easier to parse it in-place.
612}
613
614impl Default for FDSelect<'_> {
615    fn default() -> Self {
616        FDSelect::Format0(LazyArray16::default())
617    }
618}
619
620impl FDSelect<'_> {
621    fn font_dict_index(&self, glyph_id: GlyphId) -> Option<u8> {
622        match self {
623            FDSelect::Format0(ref array) => array.get(glyph_id.0),
624            FDSelect::Format3(ref data) => {
625                let mut s = Stream::new(data);
626                let number_of_ranges = s.read::<u16>()?;
627                if number_of_ranges == 0 {
628                    return None;
629                }
630
631                // 'A sentinel GID follows the last range element and serves
632                // to delimit the last range in the array.'
633                // So we can simply increase the number of ranges by one.
634                let number_of_ranges = number_of_ranges.checked_add(1)?;
635
636                // Range is: GlyphId + u8
637                let mut prev_first_glyph = s.read::<GlyphId>()?;
638                let mut prev_index = s.read::<u8>()?;
639                for _ in 1..number_of_ranges {
640                    let curr_first_glyph = s.read::<GlyphId>()?;
641                    if (prev_first_glyph..curr_first_glyph).contains(&glyph_id) {
642                        return Some(prev_index);
643                    } else {
644                        prev_index = s.read::<u8>()?;
645                    }
646
647                    prev_first_glyph = curr_first_glyph;
648                }
649
650                None
651            }
652        }
653    }
654}
655
656fn parse_fd_select<'a>(number_of_glyphs: u16, s: &mut Stream<'a>) -> Option<FDSelect<'a>> {
657    let format = s.read::<u8>()?;
658    match format {
659        0 => Some(FDSelect::Format0(s.read_array16::<u8>(number_of_glyphs)?)),
660        3 => Some(FDSelect::Format3(s.tail()?)),
661        _ => None,
662    }
663}
664
665fn parse_sid_metadata(data: &[u8], top_dict: TopDict) -> Option<FontKind> {
666    let subroutines_offset = if let Some(range) = top_dict.private_dict_range.clone() {
667        parse_private_dict(data.get(range)?)
668    } else {
669        None
670    };
671
672    // Parse Global Subroutines INDEX.
673    let mut metadata = SIDMetadata::default();
674
675    match (top_dict.private_dict_range, subroutines_offset) {
676        (Some(private_dict_range), Some(subroutines_offset)) => {
677            // 'The local subroutines offset is relative to the beginning
678            // of the Private DICT data.'
679            if let Some(start) = private_dict_range.start.checked_add(subroutines_offset) {
680                let data = data.get(start..data.len())?;
681                let mut s = Stream::new(data);
682                metadata.local_subrs = parse_index::<u16>(&mut s)?;
683            }
684        }
685        _ => {}
686    }
687
688    Some(FontKind::SID(metadata))
689}
690
691fn parse_cid_metadata(data: &[u8], top_dict: TopDict, number_of_glyphs: u16) -> Option<FontKind> {
692    let (charset_offset, fd_array_offset, fd_select_offset) =
693        match (top_dict.charset_offset, top_dict.fd_array_offset, top_dict.fd_select_offset) {
694            (Some(a), Some(b), Some(c)) => (a, b, c),
695            _ => return None, // charset, FDArray and FDSelect must be set.
696        };
697
698    if charset_offset <= charset_id::EXPERT_SUBSET {
699        // 'There are no predefined charsets for CID fonts.'
700        // Adobe Technical Note #5176, chapter 18 CID-keyed Fonts
701        return None;
702    }
703
704    let mut metadata = CIDMetadata::default();
705
706    metadata.fd_array = {
707        let mut s = Stream::new_at(data, fd_array_offset)?;
708        parse_index::<u16>(&mut s)?
709    };
710
711    metadata.fd_select = {
712        let mut s = Stream::new_at(data, fd_select_offset)?;
713        parse_fd_select(number_of_glyphs, &mut s)?
714    };
715
716    Some(FontKind::CID(metadata))
717}
718
719
720/// A [Compact Font Format Table](
721/// https://docs.microsoft.com/en-us/typography/opentype/spec/cff).
722#[derive(Clone, Copy)]
723pub struct Table<'a> {
724    // The whole CFF table.
725    // Used to resolve a local subroutine in a CID font.
726    table_data: &'a [u8],
727
728    #[allow(dead_code)] strings: Index<'a>,
729    global_subrs: Index<'a>,
730    charset: Charset<'a>,
731    char_strings: Index<'a>,
732    kind: FontKind<'a>,
733}
734
735impl<'a> Table<'a> {
736    /// Parses a table from raw data.
737    pub fn parse(data: &'a [u8]) -> Option<Self> {
738        let mut s = Stream::new(data);
739
740        // Parse Header.
741        let major = s.read::<u8>()?;
742        s.skip::<u8>(); // minor
743        let header_size = s.read::<u8>()?;
744        s.skip::<u8>(); // Absolute offset
745
746        if major != 1 {
747            return None;
748        }
749
750        // Jump to Name INDEX. It's not necessarily right after the header.
751        if header_size > 4 {
752            s.advance(usize::from(header_size) - 4);
753        }
754
755        // Skip Name INDEX.
756        skip_index::<u16>(&mut s)?;
757
758        let top_dict = parse_top_dict(&mut s)?;
759
760        // Must be set, otherwise there are nothing to parse.
761        if top_dict.char_strings_offset == 0 {
762            return None;
763        }
764
765        // String INDEX.
766        let strings = parse_index::<u16>(&mut s)?;
767
768        // Parse Global Subroutines INDEX.
769        let global_subrs = parse_index::<u16>(&mut s)?;
770
771        let char_strings = {
772            let mut s = Stream::new_at(data, top_dict.char_strings_offset)?;
773            parse_index::<u16>(&mut s)?
774        };
775
776        if char_strings.len() == 0 {
777            return None;
778        }
779
780        // 'The number of glyphs is the value of the count field in the CharStrings INDEX.'
781        let number_of_glyphs = u16::try_from(char_strings.len()).ok()?;
782
783        let charset = match top_dict.charset_offset {
784            Some(charset_id::ISO_ADOBE) => Charset::ISOAdobe,
785            Some(charset_id::EXPERT) => Charset::Expert,
786            Some(charset_id::EXPERT_SUBSET) => Charset::ExpertSubset,
787            Some(offset) => parse_charset(number_of_glyphs, &mut Stream::new_at(data, offset)?)?,
788            None => Charset::ISOAdobe, // default
789        };
790
791        let kind = if top_dict.has_ros {
792            parse_cid_metadata(data, top_dict, number_of_glyphs)?
793        } else {
794            parse_sid_metadata(data, top_dict)?
795        };
796
797        Some(Self {
798            table_data: data,
799            strings,
800            global_subrs,
801            charset,
802            char_strings,
803            kind,
804        })
805    }
806
807    /// Outlines a glyph.
808    pub fn outline(
809        &self,
810        glyph_id: GlyphId,
811        builder: &mut dyn OutlineBuilder,
812    ) -> Result<Rect, CFFError> {
813        let data = self.char_strings.get(u32::from(glyph_id.0)).ok_or(CFFError::NoGlyph)?;
814        parse_char_string(data, self, glyph_id, builder)
815    }
816
817    /// Returns a glyph name.
818    #[cfg(feature = "glyph-names")]
819    pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&'a str> {
820        match self.kind {
821            FontKind::SID(_) => {
822                let sid = self.charset.gid_to_sid(glyph_id)?;
823                let sid = usize::from(sid.0);
824                match STANDARD_NAMES.get(sid) {
825                    Some(name) => Some(name),
826                    None => {
827                        let idx = u32::try_from(sid - STANDARD_NAMES.len()).ok()?;
828                        let name = self.strings.get(idx)?;
829                        core::str::from_utf8(name).ok()
830                    }
831                }
832            }
833            FontKind::CID(_) => None,
834        }
835    }
836}
837
838impl core::fmt::Debug for Table<'_> {
839    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
840        write!(f, "Table {{ ... }}")
841    }
842}