ttf_parser/tables/cff/
cff1.rs

1// Useful links:
2// http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
3// http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
4// https://github.com/opentypejs/opentype.js/blob/master/src/tables/cff.js
5
6use core::convert::TryFrom;
7use core::ops::Range;
8
9use crate::{GlyphId, OutlineBuilder, Rect, BBox};
10use crate::parser::{Stream, LazyArray16, NumFrom, TryNumFrom};
11use super::{Builder, IsEven, CFFError, StringId, calc_subroutine_bias, conv_subroutine_index};
12use super::argstack::ArgumentsStack;
13use super::charset::{STANDARD_ENCODING, Charset, parse_charset};
14use super::charstring::CharStringParser;
15use super::dict::DictionaryParser;
16use super::index::{Index, parse_index, skip_index};
17use super::std_names::STANDARD_NAMES;
18
19// Limits according to the Adobe Technical Note #5176, chapter 4 DICT Data.
20const MAX_OPERANDS_LEN: usize = 48;
21
22// Limits according to the Adobe Technical Note #5177 Appendix B.
23const STACK_LIMIT: u8 = 10;
24const MAX_ARGUMENTS_STACK_LEN: usize = 48;
25
26const TWO_BYTE_OPERATOR_MARK: u8 = 12;
27
28/// Enumerates some operators defined in the Adobe Technical Note #5177.
29mod operator {
30    pub const HORIZONTAL_STEM: u8           = 1;
31    pub const VERTICAL_STEM: u8             = 3;
32    pub const VERTICAL_MOVE_TO: u8          = 4;
33    pub const LINE_TO: u8                   = 5;
34    pub const HORIZONTAL_LINE_TO: u8        = 6;
35    pub const VERTICAL_LINE_TO: u8          = 7;
36    pub const CURVE_TO: u8                  = 8;
37    pub const CALL_LOCAL_SUBROUTINE: u8     = 10;
38    pub const RETURN: u8                    = 11;
39    pub const ENDCHAR: u8                   = 14;
40    pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18;
41    pub const HINT_MASK: u8                 = 19;
42    pub const COUNTER_MASK: u8              = 20;
43    pub const MOVE_TO: u8                   = 21;
44    pub const HORIZONTAL_MOVE_TO: u8        = 22;
45    pub const VERTICAL_STEM_HINT_MASK: u8   = 23;
46    pub const CURVE_LINE: u8                = 24;
47    pub const LINE_CURVE: u8                = 25;
48    pub const VV_CURVE_TO: u8               = 26;
49    pub const HH_CURVE_TO: u8               = 27;
50    pub const SHORT_INT: u8                 = 28;
51    pub const CALL_GLOBAL_SUBROUTINE: u8    = 29;
52    pub const VH_CURVE_TO: u8               = 30;
53    pub const HV_CURVE_TO: u8               = 31;
54    pub const HFLEX: u8                     = 34;
55    pub const FLEX: u8                      = 35;
56    pub const HFLEX1: u8                    = 36;
57    pub const FLEX1: u8                     = 37;
58    pub const FIXED_16_16: u8               = 255;
59}
60
61/// Enumerates some operators defined in the Adobe Technical Note #5176,
62/// Table 9 Top DICT Operator Entries
63mod top_dict_operator {
64    pub const CHARSET_OFFSET: u16               = 15;
65    pub const CHAR_STRINGS_OFFSET: u16          = 17;
66    pub const PRIVATE_DICT_SIZE_AND_OFFSET: u16 = 18;
67    pub const ROS: u16                          = 1230;
68    pub const FD_ARRAY: u16                     = 1236;
69    pub const FD_SELECT: u16                    = 1237;
70}
71
72/// Enumerates some operators defined in the Adobe Technical Note #5176,
73/// Table 23 Private DICT Operators
74mod private_dict_operator {
75    pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19;
76}
77
78/// Enumerates Charset IDs defined in the Adobe Technical Note #5176, Table 22
79mod charset_id {
80    pub const ISO_ADOBE: usize = 0;
81    pub const EXPERT: usize = 1;
82    pub const EXPERT_SUBSET: usize = 2;
83}
84
85
86#[derive(Clone, Copy, Debug)]
87pub struct Metadata<'a> {
88    // The whole CFF table.
89    // Used to resolve a local subroutine in a CID font.
90    table_data: &'a [u8],
91
92    strings: Index<'a>,
93    global_subrs: Index<'a>,
94    charset: Charset<'a>,
95    char_strings: Index<'a>,
96    kind: FontKind<'a>,
97}
98
99#[derive(Clone, Copy, Debug)]
100pub enum FontKind<'a> {
101    SID(SIDMetadata<'a>),
102    CID(CIDMetadata<'a>),
103}
104
105#[derive(Clone, Copy, Default, Debug)]
106pub struct SIDMetadata<'a> {
107    local_subrs: Index<'a>,
108}
109
110#[derive(Clone, Copy, Default, Debug)]
111pub struct CIDMetadata<'a> {
112    fd_array: Index<'a>,
113    fd_select: FDSelect<'a>,
114}
115
116pub(crate) fn parse_metadata(data: &[u8]) -> Option<Metadata> {
117    let mut s = Stream::new(data);
118
119    // Parse Header.
120    let major: u8 = s.read()?;
121    s.skip::<u8>(); // minor
122    let header_size: u8 = s.read()?;
123    s.skip::<u8>(); // Absolute offset
124
125    if major != 1 {
126        return None;
127    }
128
129    // Jump to Name INDEX. It's not necessarily right after the header.
130    if header_size > 4 {
131        s.advance(usize::from(header_size) - 4);
132    }
133
134    // Skip Name INDEX.
135    skip_index::<u16>(&mut s)?;
136
137    let top_dict = parse_top_dict(&mut s)?;
138
139    // Must be set, otherwise there are nothing to parse.
140    if top_dict.char_strings_offset == 0 {
141        return None;
142    }
143
144    // String INDEX.
145    let strings = parse_index::<u16>(&mut s)?;
146
147    // Parse Global Subroutines INDEX.
148    let global_subrs = parse_index::<u16>(&mut s)?;
149
150    let char_strings = {
151        let mut s = Stream::new_at(data, top_dict.char_strings_offset)?;
152        parse_index::<u16>(&mut s)?
153    };
154
155    if char_strings.len() == 0 {
156        return None;
157    }
158
159    // 'The number of glyphs is the value of the count field in the CharStrings INDEX.'
160    let number_of_glyphs = u16::try_from(char_strings.len()).ok()?;
161
162    let charset = match top_dict.charset_offset {
163        Some(charset_id::ISO_ADOBE) => Charset::ISOAdobe,
164        Some(charset_id::EXPERT) => Charset::Expert,
165        Some(charset_id::EXPERT_SUBSET) => Charset::ExpertSubset,
166        Some(offset) => parse_charset(number_of_glyphs, &mut Stream::new_at(data, offset)?)?,
167        None => Charset::ISOAdobe, // default
168    };
169
170    let kind = if top_dict.has_ros {
171        parse_cid_metadata(data, top_dict, number_of_glyphs)?
172    } else {
173        parse_sid_metadata(data, top_dict)?
174    };
175
176    Some(Metadata {
177        table_data: data,
178        strings,
179        global_subrs,
180        charset,
181        char_strings,
182        kind,
183    })
184}
185
186fn parse_sid_metadata(data: &[u8], top_dict: TopDict) -> Option<FontKind> {
187    let subroutines_offset = if let Some(range) = top_dict.private_dict_range.clone() {
188        parse_private_dict(data.get(range)?)
189    } else {
190        None
191    };
192
193    // Parse Global Subroutines INDEX.
194    let mut metadata = SIDMetadata::default();
195
196    match (top_dict.private_dict_range, subroutines_offset) {
197        (Some(private_dict_range), Some(subroutines_offset)) => {
198            // 'The local subroutines offset is relative to the beginning
199            // of the Private DICT data.'
200            if let Some(start) = private_dict_range.start.checked_add(subroutines_offset) {
201                let data = data.get(start..data.len())?;
202                let mut s = Stream::new(data);
203                metadata.local_subrs = parse_index::<u16>(&mut s)?;
204            }
205        }
206        _ => {}
207    }
208
209    Some(FontKind::SID(metadata))
210}
211
212fn parse_cid_metadata(data: &[u8], top_dict: TopDict, number_of_glyphs: u16) -> Option<FontKind> {
213    let (charset_offset, fd_array_offset, fd_select_offset) =
214        match (top_dict.charset_offset, top_dict.fd_array_offset, top_dict.fd_select_offset) {
215            (Some(a), Some(b), Some(c)) => (a, b, c),
216            _ => return None, // charset, FDArray and FDSelect must be set.
217        };
218
219    if charset_offset <= charset_id::EXPERT_SUBSET {
220        // 'There are no predefined charsets for CID fonts.'
221        // Adobe Technical Note #5176, chapter 18 CID-keyed Fonts
222        return None;
223    }
224
225    let mut metadata = CIDMetadata::default();
226
227    metadata.fd_array = {
228        let mut s = Stream::new_at(data, fd_array_offset)?;
229        parse_index::<u16>(&mut s)?
230    };
231
232    metadata.fd_select = {
233        let mut s = Stream::new_at(data, fd_select_offset)?;
234        parse_fd_select(number_of_glyphs, &mut s)?
235    };
236
237    Some(FontKind::CID(metadata))
238}
239
240#[derive(Default)]
241struct TopDict {
242    charset_offset: Option<usize>,
243    char_strings_offset: usize,
244    private_dict_range: Option<Range<usize>>,
245    has_ros: bool,
246    fd_array_offset: Option<usize>,
247    fd_select_offset: Option<usize>,
248}
249
250fn parse_top_dict(s: &mut Stream) -> Option<TopDict> {
251    let mut top_dict = TopDict::default();
252
253    let index = parse_index::<u16>(s)?;
254
255    // The Top DICT INDEX should have only one dictionary.
256    let data = index.get(0)?;
257
258    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
259    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
260    while let Some(operator) = dict_parser.parse_next() {
261        match operator.get() {
262            top_dict_operator::CHARSET_OFFSET => {
263                top_dict.charset_offset = dict_parser.parse_offset();
264            }
265            top_dict_operator::CHAR_STRINGS_OFFSET => {
266                top_dict.char_strings_offset = dict_parser.parse_offset()?;
267            }
268            top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET => {
269                top_dict.private_dict_range = dict_parser.parse_range();
270            }
271            top_dict_operator::ROS => {
272                top_dict.has_ros = true;
273            }
274            top_dict_operator::FD_ARRAY => {
275                top_dict.fd_array_offset = dict_parser.parse_offset();
276            }
277            top_dict_operator::FD_SELECT => {
278                top_dict.fd_select_offset = dict_parser.parse_offset();
279            }
280            _ => {}
281        }
282    }
283
284    Some(top_dict)
285}
286
287fn parse_private_dict(data: &[u8]) -> Option<usize> {
288    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
289    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
290    while let Some(operator) = dict_parser.parse_next() {
291        if operator.get() == private_dict_operator::LOCAL_SUBROUTINES_OFFSET {
292            return dict_parser.parse_offset();
293        }
294    }
295
296    None
297}
298
299fn parse_font_dict(data: &[u8]) -> Option<Range<usize>> {
300    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
301    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
302    while let Some(operator) = dict_parser.parse_next() {
303        if operator.get() == top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET {
304            return dict_parser.parse_range();
305        }
306    }
307
308    None
309}
310
311/// In CID fonts, to get local subroutines we have to:
312///   1. Find Font DICT index via FDSelect by GID.
313///   2. Get Font DICT data from FDArray using this index.
314///   3. Get a Private DICT offset from a Font DICT.
315///   4. Get a local subroutine offset from Private DICT.
316///   5. Parse a local subroutine at offset.
317fn parse_cid_local_subrs<'a>(
318    data: &'a [u8],
319    glyph_id: GlyphId,
320    cid: &CIDMetadata,
321) -> Option<Index<'a>> {
322    let font_dict_index = cid.fd_select.font_dict_index(glyph_id)?;
323    let font_dict_data = cid.fd_array.get(u32::from(font_dict_index))?;
324    let private_dict_range = parse_font_dict(font_dict_data)?;
325    let private_dict_data = data.get(private_dict_range.clone())?;
326    let subroutines_offset = parse_private_dict(private_dict_data)?;
327
328    // 'The local subroutines offset is relative to the beginning
329    // of the Private DICT data.'
330    let start = private_dict_range.start.checked_add(subroutines_offset)?;
331    let subrs_data = data.get(start..)?;
332    let mut s = Stream::new(subrs_data);
333    parse_index::<u16>(&mut s)
334}
335
336pub fn glyph_name<'a>(metadata: &'a Metadata, glyph_id: GlyphId) -> Option<&'a str> {
337    match metadata.kind {
338        FontKind::SID(_) => {
339            let sid = metadata.charset.gid_to_sid(glyph_id)?;
340            let sid = usize::from(sid.0);
341            match STANDARD_NAMES.get(sid) {
342                Some(name) => Some(name),
343                None => {
344                    let idx = u32::try_from(sid - STANDARD_NAMES.len()).ok()?;
345                    let name = metadata.strings.get(idx)?;
346                    core::str::from_utf8(name).ok()
347                }
348            }
349        }
350        FontKind::CID(_) => None,
351    }
352}
353
354pub fn outline(
355    metadata: &Metadata,
356    glyph_id: GlyphId,
357    builder: &mut dyn OutlineBuilder,
358) -> Option<Rect> {
359    let data = metadata.char_strings.get(u32::from(glyph_id.0))?;
360    parse_char_string(data, metadata, glyph_id, builder).ok()
361}
362
363struct CharStringParserContext<'a> {
364    metadata: &'a Metadata<'a>,
365    width_parsed: bool,
366    stems_len: u32,
367    has_endchar: bool,
368    has_seac: bool,
369    glyph_id: GlyphId, // Required to parse local subroutine in CID fonts.
370    local_subrs: Option<Index<'a>>,
371}
372
373fn parse_char_string(
374    data: &[u8],
375    metadata: &Metadata,
376    glyph_id: GlyphId,
377    builder: &mut dyn OutlineBuilder,
378) -> Result<Rect, CFFError> {
379    let local_subrs = match metadata.kind {
380        FontKind::SID(ref sid) => Some(sid.local_subrs),
381        FontKind::CID(_) => None, // Will be resolved on request.
382    };
383
384    let mut ctx = CharStringParserContext {
385        metadata,
386        width_parsed: false,
387        stems_len: 0,
388        has_endchar: false,
389        has_seac: false,
390        glyph_id,
391        local_subrs,
392    };
393
394    let mut inner_builder = Builder {
395        builder,
396        bbox: BBox::new(),
397    };
398
399    let stack = ArgumentsStack {
400        data: &mut [0.0; MAX_ARGUMENTS_STACK_LEN], // 192B
401        len: 0,
402        max_len: MAX_ARGUMENTS_STACK_LEN,
403    };
404    let mut parser = CharStringParser {
405        stack,
406        builder: &mut inner_builder,
407        x: 0.0,
408        y: 0.0,
409        has_move_to: false,
410        is_first_move_to: true,
411    };
412    _parse_char_string(&mut ctx, data, 0, &mut parser)?;
413
414    if !ctx.has_endchar {
415        return Err(CFFError::MissingEndChar);
416    }
417
418    let bbox = parser.builder.bbox;
419
420    // Check that bbox was changed.
421    if bbox.is_default() {
422        return Err(CFFError::ZeroBBox);
423    }
424
425    bbox.to_rect().ok_or(CFFError::BboxOverflow)
426}
427
428
429fn _parse_char_string(
430    ctx: &mut CharStringParserContext,
431    char_string: &[u8],
432    depth: u8,
433    p: &mut CharStringParser,
434) -> Result<(), CFFError> {
435    let mut s = Stream::new(char_string);
436    while !s.at_end() {
437        let op: u8 = s.read().ok_or(CFFError::ReadOutOfBounds)?;
438        match op {
439            0 | 2 | 9 | 13 | 15 | 16 | 17 => {
440                // Reserved.
441                return Err(CFFError::InvalidOperator);
442            }
443            operator::HORIZONTAL_STEM |
444            operator::VERTICAL_STEM |
445            operator::HORIZONTAL_STEM_HINT_MASK |
446            operator::VERTICAL_STEM_HINT_MASK => {
447                // y dy {dya dyb}* hstem
448                // x dx {dxa dxb}* vstem
449                // y dy {dya dyb}* hstemhm
450                // x dx {dxa dxb}* vstemhm
451
452                // If the stack length is uneven, than the first value is a `width`.
453                let len = if p.stack.len().is_odd() && !ctx.width_parsed {
454                    ctx.width_parsed = true;
455                    p.stack.len() - 1
456                } else {
457                    p.stack.len()
458                };
459
460                ctx.stems_len += len as u32 >> 1;
461
462                // We are ignoring the hint operators.
463                p.stack.clear();
464            }
465            operator::VERTICAL_MOVE_TO => {
466                let mut i = 0;
467                if p.stack.len() == 2 && !ctx.width_parsed {
468                    i += 1;
469                    ctx.width_parsed = true;
470                }
471
472                p.parse_vertical_move_to(i)?;
473            }
474            operator::LINE_TO => {
475                p.parse_line_to()?;
476            }
477            operator::HORIZONTAL_LINE_TO => {
478                p.parse_horizontal_line_to()?;
479            }
480            operator::VERTICAL_LINE_TO => {
481                p.parse_vertical_line_to()?;
482            }
483            operator::CURVE_TO => {
484                p.parse_curve_to()?;
485            }
486            operator::CALL_LOCAL_SUBROUTINE => {
487                if p.stack.is_empty() {
488                    return Err(CFFError::InvalidArgumentsStackLength);
489                }
490
491                if depth == STACK_LIMIT {
492                    return Err(CFFError::NestingLimitReached);
493                }
494
495                // Parse and remember the local subroutine for the current glyph.
496                // Since it's a pretty complex task, we're doing it only when
497                // a local subroutine is actually requested by the glyphs charstring.
498                if ctx.local_subrs.is_none() {
499                    if let FontKind::CID(ref cid) = ctx.metadata.kind {
500                        ctx.local_subrs = parse_cid_local_subrs(
501                            ctx.metadata.table_data, ctx.glyph_id, cid
502                        );
503                    }
504                }
505
506                if let Some(local_subrs) = ctx.local_subrs {
507                    let subroutine_bias = calc_subroutine_bias(local_subrs.len());
508                    let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
509                    let char_string = local_subrs.get(index).ok_or(CFFError::InvalidSubroutineIndex)?;
510                    _parse_char_string(ctx, char_string, depth + 1, p)?;
511                } else {
512                    return Err(CFFError::NoLocalSubroutines);
513                }
514
515                if ctx.has_endchar && !ctx.has_seac {
516                    if !s.at_end() {
517                        return Err(CFFError::DataAfterEndChar);
518                    }
519
520                    break;
521                }
522            }
523            operator::RETURN => {
524                break;
525            }
526            TWO_BYTE_OPERATOR_MARK => {
527                // flex
528                let op2: u8 = s.read().ok_or(CFFError::ReadOutOfBounds)?;
529                match op2 {
530                    operator::HFLEX => p.parse_hflex()?,
531                    operator::FLEX => p.parse_flex()?,
532                    operator::HFLEX1 => p.parse_hflex1()?,
533                    operator::FLEX1 => p.parse_flex1()?,
534                    _ => return Err(CFFError::UnsupportedOperator),
535                }
536            }
537            operator::ENDCHAR => {
538                if p.stack.len() == 4 || (!ctx.width_parsed && p.stack.len() == 5) {
539                    // Process 'seac'.
540                    let accent_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop())
541                        .ok_or(CFFError::InvalidSeacCode)?;
542                    let base_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop())
543                        .ok_or(CFFError::InvalidSeacCode)?;
544                    let dy = p.stack.pop();
545                    let dx = p.stack.pop();
546
547                    if !ctx.width_parsed {
548                        p.stack.pop();
549                        ctx.width_parsed = true;
550                    }
551
552                    ctx.has_seac = true;
553
554                    let base_char_string = ctx.metadata.char_strings.get(u32::from(base_char.0))
555                        .ok_or(CFFError::InvalidSeacCode)?;
556                    _parse_char_string(ctx, base_char_string, depth + 1, p)?;
557                    p.x = dx;
558                    p.y = dy;
559
560                    let accent_char_string = ctx.metadata.char_strings.get(u32::from(accent_char.0))
561                        .ok_or(CFFError::InvalidSeacCode)?;
562                    _parse_char_string(ctx, accent_char_string, depth + 1, p)?;
563                } else if p.stack.len() == 1 && !ctx.width_parsed {
564                    p.stack.pop();
565                    ctx.width_parsed = true;
566                }
567
568                if !p.is_first_move_to {
569                    p.is_first_move_to = true;
570                    p.builder.close();
571                }
572
573                if !s.at_end() {
574                    return Err(CFFError::DataAfterEndChar);
575                }
576
577                ctx.has_endchar = true;
578
579                break;
580            }
581            operator::HINT_MASK | operator::COUNTER_MASK => {
582                let mut len = p.stack.len();
583
584                // We are ignoring the hint operators.
585                p.stack.clear();
586
587                // If the stack length is uneven, than the first value is a `width`.
588                if len.is_odd() && !ctx.width_parsed {
589                    len -= 1;
590                    ctx.width_parsed = true;
591                }
592
593                ctx.stems_len += len as u32 >> 1;
594
595                s.advance(usize::num_from((ctx.stems_len + 7) >> 3));
596            }
597            operator::MOVE_TO => {
598                let mut i = 0;
599                if p.stack.len() == 3 && !ctx.width_parsed {
600                    i += 1;
601                    ctx.width_parsed = true;
602                }
603
604                p.parse_move_to(i)?;
605            }
606            operator::HORIZONTAL_MOVE_TO => {
607                let mut i = 0;
608                if p.stack.len() == 2 && !ctx.width_parsed {
609                    i += 1;
610                    ctx.width_parsed = true;
611                }
612
613                p.parse_horizontal_move_to(i)?;
614            }
615            operator::CURVE_LINE => {
616                p.parse_curve_line()?;
617            }
618            operator::LINE_CURVE => {
619                p.parse_line_curve()?;
620            }
621            operator::VV_CURVE_TO => {
622                p.parse_vv_curve_to()?;
623            }
624            operator::HH_CURVE_TO => {
625                p.parse_hh_curve_to()?;
626            }
627            operator::SHORT_INT => {
628                let n = s.read::<i16>().ok_or(CFFError::ReadOutOfBounds)?;
629                p.stack.push(f32::from(n))?;
630            }
631            operator::CALL_GLOBAL_SUBROUTINE => {
632                if p.stack.is_empty() {
633                    return Err(CFFError::InvalidArgumentsStackLength);
634                }
635
636                if depth == STACK_LIMIT {
637                    return Err(CFFError::NestingLimitReached);
638                }
639
640                let subroutine_bias = calc_subroutine_bias(ctx.metadata.global_subrs.len());
641                let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
642                let char_string = ctx.metadata.global_subrs.get(index)
643                    .ok_or(CFFError::InvalidSubroutineIndex)?;
644                _parse_char_string(ctx, char_string, depth + 1, p)?;
645
646                if ctx.has_endchar && !ctx.has_seac {
647                    if !s.at_end() {
648                        return Err(CFFError::DataAfterEndChar);
649                    }
650
651                    break;
652                }
653            }
654            operator::VH_CURVE_TO => {
655                p.parse_vh_curve_to()?;
656            }
657            operator::HV_CURVE_TO => {
658                p.parse_hv_curve_to()?;
659            }
660            32..=246 => {
661                p.parse_int1(op)?;
662            }
663            247..=250 => {
664                p.parse_int2(op, &mut s)?;
665            }
666            251..=254 => {
667                p.parse_int3(op, &mut s)?;
668            }
669            operator::FIXED_16_16 => {
670                p.parse_fixed(&mut s)?;
671            }
672        }
673    }
674
675    // TODO: 'A charstring subroutine must end with either an endchar or a return operator.'
676
677    Ok(())
678}
679
680fn seac_code_to_glyph_id(charset: &Charset, n: f32) -> Option<GlyphId> {
681    let code = u8::try_num_from(n)?;
682
683    let sid = STANDARD_ENCODING[code as usize];
684    let sid = StringId(u16::from(sid));
685
686    match charset {
687        Charset::ISOAdobe => {
688            // ISO Adobe charset only defines string ids up to 228 (zcaron)
689            if code <= 228 { Some(GlyphId(sid.0)) } else { None }
690        }
691        Charset::Expert | Charset::ExpertSubset => None,
692        _ => charset.sid_to_gid(sid),
693    }
694}
695
696
697#[derive(Clone, Copy, Debug)]
698enum FDSelect<'a> {
699    Format0(LazyArray16<'a, u8>),
700    Format3(&'a [u8]), // It's easier to parse it in-place.
701}
702
703impl Default for FDSelect<'_> {
704    fn default() -> Self {
705        FDSelect::Format0(LazyArray16::default())
706    }
707}
708
709impl FDSelect<'_> {
710    fn font_dict_index(&self, glyph_id: GlyphId) -> Option<u8> {
711        match self {
712            FDSelect::Format0(ref array) => array.get(glyph_id.0),
713            FDSelect::Format3(ref data) => {
714                let mut s = Stream::new(data);
715                let number_of_ranges: u16 = s.read()?;
716                if number_of_ranges == 0 {
717                    return None;
718                }
719
720                // 'A sentinel GID follows the last range element and serves
721                // to delimit the last range in the array.'
722                // So we can simply increase the number of ranges by one.
723                let number_of_ranges = number_of_ranges.checked_add(1)?;
724
725                // Range is: GlyphId + u8
726                let mut prev_first_glyph: GlyphId = s.read()?;
727                let mut prev_index: u8 = s.read()?;
728                for _ in 1..number_of_ranges {
729                    let curr_first_glyph: GlyphId = s.read()?;
730                    if (prev_first_glyph..curr_first_glyph).contains(&glyph_id) {
731                        return Some(prev_index);
732                    } else {
733                        prev_index = s.read::<u8>()?;
734                    }
735
736                    prev_first_glyph = curr_first_glyph;
737                }
738
739                None
740            }
741        }
742    }
743}
744
745fn parse_fd_select<'a>(number_of_glyphs: u16, s: &mut Stream<'a>) -> Option<FDSelect<'a>> {
746    let format: u8 = s.read()?;
747    match format {
748        0 => Some(FDSelect::Format0(s.read_array16::<u8>(number_of_glyphs)?)),
749        3 => Some(FDSelect::Format3(s.tail()?)),
750        _ => None,
751    }
752}
753
754
755#[cfg(test)]
756mod tests {
757    use super::*;
758    use std::vec::Vec;
759    use std::string::String;
760    use std::fmt::Write;
761    use crate::writer;
762    use writer::TtfType::*;
763
764    struct Builder(String);
765    impl OutlineBuilder for Builder {
766        fn move_to(&mut self, x: f32, y: f32) {
767            write!(&mut self.0, "M {} {} ", x, y).unwrap();
768        }
769
770        fn line_to(&mut self, x: f32, y: f32) {
771            write!(&mut self.0, "L {} {} ", x, y).unwrap();
772        }
773
774        fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
775            write!(&mut self.0, "Q {} {} {} {} ", x1, y1, x, y).unwrap();
776        }
777
778        fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
779            write!(&mut self.0, "C {} {} {} {} {} {} ", x1, y1, x2, y2, x, y).unwrap();
780        }
781
782        fn close(&mut self) {
783            write!(&mut self.0, "Z ").unwrap();
784        }
785    }
786
787    fn gen_cff(
788        global_subrs: &[&[writer::TtfType]],
789        local_subrs: &[&[writer::TtfType]],
790        chars: &[writer::TtfType],
791    ) -> Vec<u8> {
792        fn gen_global_subrs(subrs: &[&[writer::TtfType]]) -> Vec<u8> {
793            let mut w = writer::Writer::new();
794            for v1 in subrs {
795                for v2 in v1.iter() {
796                    w.write(*v2);
797                }
798            }
799            w.data
800        }
801
802        fn gen_local_subrs(subrs: &[&[writer::TtfType]]) -> Vec<u8> {
803            let mut w = writer::Writer::new();
804            for v1 in subrs {
805                for v2 in v1.iter() {
806                    w.write(*v2);
807                }
808            }
809            w.data
810        }
811
812        const EMPTY_INDEX_SIZE: usize = 2;
813        const INDEX_HEADER_SIZE: usize = 5;
814
815        // TODO: support multiple subrs
816        assert!(global_subrs.len() <= 1);
817        assert!(local_subrs.len() <= 1);
818
819        let global_subrs_data = gen_global_subrs(global_subrs);
820        let local_subrs_data = gen_local_subrs(local_subrs);
821        let chars_data = writer::convert(chars);
822
823        assert!(global_subrs_data.len() < 255);
824        assert!(local_subrs_data.len() < 255);
825        assert!(chars_data.len() < 255);
826
827        let mut w = writer::Writer::new();
828        // Header
829        w.write(UInt8(1)); // major version
830        w.write(UInt8(0)); // minor version
831        w.write(UInt8(4)); // header size
832        w.write(UInt8(0)); // absolute offset
833
834        // Name INDEX
835        w.write(UInt16(0)); // count
836
837        // Top DICT
838        // INDEX
839        w.write(UInt16(1)); // count
840        w.write(UInt8(1)); // offset size
841        w.write(UInt8(1)); // index[0]
842
843        let top_dict_idx2 = if local_subrs.is_empty() { 3 } else { 6 };
844        w.write(UInt8(top_dict_idx2)); // index[1]
845        // Item 0
846        let mut charstr_offset = w.offset() + 2;
847        charstr_offset += EMPTY_INDEX_SIZE; // String INDEX
848
849        // Global Subroutines INDEX
850        if !global_subrs_data.is_empty() {
851            charstr_offset += INDEX_HEADER_SIZE + global_subrs_data.len();
852        } else {
853            charstr_offset += EMPTY_INDEX_SIZE;
854        }
855
856        if !local_subrs_data.is_empty() {
857            charstr_offset += 3;
858        }
859
860        w.write(CFFInt(charstr_offset as i32));
861        w.write(UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8));
862
863        if !local_subrs_data.is_empty() {
864            // Item 1
865            w.write(CFFInt(2)); // length
866            w.write(CFFInt((charstr_offset + INDEX_HEADER_SIZE + chars_data.len()) as i32)); // offset
867            w.write(UInt8(top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET as u8));
868        }
869
870        // String INDEX
871        w.write(UInt16(0)); // count
872
873        // Global Subroutines INDEX
874        if global_subrs_data.is_empty() {
875            w.write(UInt16(0)); // count
876        } else {
877            w.write(UInt16(1)); // count
878            w.write(UInt8(1)); // offset size
879            w.write(UInt8(1)); // index[0]
880            w.write(UInt8(global_subrs_data.len() as u8 + 1)); // index[1]
881            w.data.extend_from_slice(&global_subrs_data);
882        }
883
884        // CharString INDEX
885        w.write(UInt16(1)); // count
886        w.write(UInt8(1)); // offset size
887        w.write(UInt8(1)); // index[0]
888        w.write(UInt8(chars_data.len() as u8 + 1)); // index[1]
889        w.data.extend_from_slice(&chars_data);
890
891        if !local_subrs_data.is_empty() {
892            // The local subroutines offset is relative to the beginning of the Private DICT data.
893
894            // Private DICT
895            w.write(CFFInt(2));
896            w.write(UInt8(private_dict_operator::LOCAL_SUBROUTINES_OFFSET as u8));
897
898            // Local Subroutines INDEX
899            w.write(UInt16(1)); // count
900            w.write(UInt8(1)); // offset size
901            w.write(UInt8(1)); // index[0]
902            w.write(UInt8(local_subrs_data.len() as u8 + 1)); // index[1]
903            w.data.extend_from_slice(&local_subrs_data);
904        }
905
906        w.data
907    }
908
909    #[test]
910    fn unsupported_version() {
911        let data = writer::convert(&[
912            UInt8(10), // major version, only 1 is supported
913            UInt8(0), // minor version
914            UInt8(4), // header size
915            UInt8(0), // absolute offset
916        ]);
917
918        assert!(parse_metadata(&data).is_none());
919    }
920
921    #[test]
922    fn non_default_header_size() {
923        let data = writer::convert(&[
924            // Header
925            UInt8(1), // major version
926            UInt8(0), // minor version
927            UInt8(8), // header size
928            UInt8(0), // absolute offset
929
930            // no-op, should be skipped
931            UInt8(0),
932            UInt8(0),
933            UInt8(0),
934            UInt8(0),
935
936            // Name INDEX
937            UInt16(0), // count
938
939            // Top DICT
940            // INDEX
941            UInt16(1), // count
942            UInt8(1), // offset size
943            UInt8(1), // index[0]
944            UInt8(3), // index[1]
945            // Data
946            CFFInt(21),
947            UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
948
949            // String INDEX
950            UInt16(0), // count
951
952            // Global Subroutines INDEX
953            UInt16(0), // count
954
955            // CharString INDEX
956            UInt16(1), // count
957            UInt8(1), // offset size
958            UInt8(1), // index[0]
959            UInt8(4), // index[1]
960            // Data
961            CFFInt(10),
962            UInt8(operator::HORIZONTAL_MOVE_TO),
963            UInt8(operator::ENDCHAR),
964        ]);
965
966        let metadata = parse_metadata(&data).unwrap();
967        let mut builder = Builder(String::new());
968        let char_str = metadata.char_strings.get(0).unwrap();
969        let rect = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder).unwrap();
970
971        assert_eq!(builder.0, "M 10 0 Z ");
972        assert_eq!(rect, Rect { x_min: 10, y_min: 0, x_max: 10, y_max: 0 });
973    }
974
975    fn rect(x_min: i16, y_min: i16, x_max: i16, y_max: i16) -> Rect {
976        Rect { x_min, y_min, x_max, y_max }
977    }
978
979    macro_rules! test_cs_with_subrs {
980        ($name:ident, $glob:expr, $loc:expr, $values:expr, $path:expr, $rect_res:expr) => {
981            #[test]
982            fn $name() {
983                let data = gen_cff($glob, $loc, $values);
984                let metadata = parse_metadata(&data).unwrap();
985                let mut builder = Builder(String::new());
986                let char_str = metadata.char_strings.get(0).unwrap();
987                let rect = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder).unwrap();
988
989                assert_eq!(builder.0, $path);
990                assert_eq!(rect, $rect_res);
991            }
992        };
993    }
994
995    macro_rules! test_cs {
996        ($name:ident, $values:expr, $path:expr, $rect_res:expr) => {
997            test_cs_with_subrs!($name, &[], &[], $values, $path, $rect_res);
998        };
999    }
1000
1001    macro_rules! test_cs_err {
1002        ($name:ident, $values:expr, $err:expr) => {
1003            #[test]
1004            fn $name() {
1005                let data = gen_cff(&[], &[], $values);
1006                let metadata = parse_metadata(&data).unwrap();
1007                let mut builder = Builder(String::new());
1008                let char_str = metadata.char_strings.get(0).unwrap();
1009                let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1010
1011                assert_eq!(res.unwrap_err(), $err);
1012            }
1013        };
1014    }
1015
1016    test_cs!(move_to, &[
1017        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1018        UInt8(operator::ENDCHAR),
1019    ], "M 10 20 Z ",
1020        rect(10, 20, 10, 20)
1021    );
1022
1023    test_cs!(move_to_with_width, &[
1024        CFFInt(5), CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1025        UInt8(operator::ENDCHAR),
1026    ], "M 10 20 Z ",
1027        rect(10, 20, 10, 20)
1028    );
1029
1030    test_cs!(hmove_to, &[
1031        CFFInt(10), UInt8(operator::HORIZONTAL_MOVE_TO),
1032        UInt8(operator::ENDCHAR),
1033    ], "M 10 0 Z ",
1034        rect(10, 0, 10, 0)
1035    );
1036
1037    test_cs!(hmove_to_with_width, &[
1038        CFFInt(10), CFFInt(20), UInt8(operator::HORIZONTAL_MOVE_TO),
1039        UInt8(operator::ENDCHAR),
1040    ], "M 20 0 Z ",
1041        rect(20, 0, 20, 0)
1042    );
1043
1044    test_cs!(vmove_to, &[
1045        CFFInt(10), UInt8(operator::VERTICAL_MOVE_TO),
1046        UInt8(operator::ENDCHAR),
1047    ], "M 0 10 Z ",
1048        rect(0, 10, 0, 10)
1049    );
1050
1051    test_cs!(vmove_to_with_width, &[
1052        CFFInt(10), CFFInt(20), UInt8(operator::VERTICAL_MOVE_TO),
1053        UInt8(operator::ENDCHAR),
1054    ], "M 0 20 Z ",
1055        rect(0, 20, 0, 20)
1056    );
1057
1058    test_cs!(line_to, &[
1059        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1060        CFFInt(30), CFFInt(40), UInt8(operator::LINE_TO),
1061        UInt8(operator::ENDCHAR),
1062    ], "M 10 20 L 40 60 Z ",
1063        rect(10, 20, 40, 60)
1064    );
1065
1066    test_cs!(line_to_with_multiple_pairs, &[
1067        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1068        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), UInt8(operator::LINE_TO),
1069        UInt8(operator::ENDCHAR),
1070    ], "M 10 20 L 40 60 L 90 120 Z ",
1071        rect(10, 20, 90, 120)
1072    );
1073
1074    test_cs!(hline_to, &[
1075        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1076        CFFInt(30), UInt8(operator::HORIZONTAL_LINE_TO),
1077        UInt8(operator::ENDCHAR),
1078    ], "M 10 20 L 40 20 Z ",
1079        rect(10, 20, 40, 20)
1080    );
1081
1082    test_cs!(hline_to_with_two_coords, &[
1083        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1084        CFFInt(30), CFFInt(40), UInt8(operator::HORIZONTAL_LINE_TO),
1085        UInt8(operator::ENDCHAR),
1086    ], "M 10 20 L 40 20 L 40 60 Z ",
1087        rect(10, 20, 40, 60)
1088    );
1089
1090    test_cs!(hline_to_with_three_coords, &[
1091        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1092        CFFInt(30), CFFInt(40), CFFInt(50), UInt8(operator::HORIZONTAL_LINE_TO),
1093        UInt8(operator::ENDCHAR),
1094    ], "M 10 20 L 40 20 L 40 60 L 90 60 Z ",
1095        rect(10, 20, 90, 60)
1096    );
1097
1098    test_cs!(vline_to, &[
1099        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1100        CFFInt(30), UInt8(operator::VERTICAL_LINE_TO),
1101        UInt8(operator::ENDCHAR),
1102    ], "M 10 20 L 10 50 Z ",
1103        rect(10, 20, 10, 50)
1104    );
1105
1106    test_cs!(vline_to_with_two_coords, &[
1107        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1108        CFFInt(30), CFFInt(40), UInt8(operator::VERTICAL_LINE_TO),
1109        UInt8(operator::ENDCHAR),
1110    ], "M 10 20 L 10 50 L 50 50 Z ",
1111        rect(10, 20, 50, 50)
1112    );
1113
1114    test_cs!(vline_to_with_three_coords, &[
1115        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1116        CFFInt(30), CFFInt(40), CFFInt(50), UInt8(operator::VERTICAL_LINE_TO),
1117        UInt8(operator::ENDCHAR),
1118    ], "M 10 20 L 10 50 L 50 50 L 50 100 Z ",
1119        rect(10, 20, 50, 100)
1120    );
1121
1122    test_cs!(curve_to, &[
1123        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1124        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), CFFInt(70), CFFInt(80),
1125        UInt8(operator::CURVE_TO),
1126        UInt8(operator::ENDCHAR),
1127    ], "M 10 20 C 40 60 90 120 160 200 Z ",
1128        rect(10, 20, 160, 200)
1129    );
1130
1131    test_cs!(curve_to_with_two_sets_of_coords, &[
1132        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1133        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), CFFInt(70), CFFInt(80),
1134        CFFInt(90), CFFInt(100), CFFInt(110), CFFInt(120), CFFInt(130), CFFInt(140),
1135        UInt8(operator::CURVE_TO),
1136        UInt8(operator::ENDCHAR),
1137    ], "M 10 20 C 40 60 90 120 160 200 C 250 300 360 420 490 560 Z ",
1138        rect(10, 20, 490, 560)
1139    );
1140
1141    test_cs!(hh_curve_to, &[
1142        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1143        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), UInt8(operator::HH_CURVE_TO),
1144        UInt8(operator::ENDCHAR),
1145    ], "M 10 20 C 40 20 80 70 140 70 Z ",
1146        rect(10, 20, 140, 70)
1147    );
1148
1149    test_cs!(hh_curve_to_with_y, &[
1150        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1151        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), CFFInt(70), UInt8(operator::HH_CURVE_TO),
1152        UInt8(operator::ENDCHAR),
1153    ], "M 10 20 C 50 50 100 110 170 110 Z ",
1154        rect(10, 20, 170, 110)
1155    );
1156
1157    test_cs!(vv_curve_to, &[
1158        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1159        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), UInt8(operator::VV_CURVE_TO),
1160        UInt8(operator::ENDCHAR),
1161    ], "M 10 20 C 10 50 50 100 50 160 Z ",
1162        rect(10, 20, 50, 160)
1163    );
1164
1165    test_cs!(vv_curve_to_with_x, &[
1166        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1167        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), CFFInt(70), UInt8(operator::VV_CURVE_TO),
1168        UInt8(operator::ENDCHAR),
1169    ], "M 10 20 C 40 60 90 120 90 190 Z ",
1170        rect(10, 20, 90, 190)
1171    );
1172
1173    #[test]
1174    fn only_endchar() {
1175        let data = gen_cff(&[], &[], &[UInt8(operator::ENDCHAR)]);
1176        let metadata = parse_metadata(&data).unwrap();
1177        let mut builder = Builder(String::new());
1178        let char_str = metadata.char_strings.get(0).unwrap();
1179        assert!(parse_char_string(char_str, &metadata, GlyphId(0), &mut builder).is_err());
1180    }
1181
1182    test_cs_with_subrs!(local_subr,
1183        &[],
1184        &[&[
1185            CFFInt(30),
1186            CFFInt(40),
1187            UInt8(operator::LINE_TO),
1188            UInt8(operator::RETURN),
1189        ]],
1190        &[
1191            CFFInt(10),
1192            UInt8(operator::HORIZONTAL_MOVE_TO),
1193            CFFInt(0 - 107), // subr index - subr bias
1194            UInt8(operator::CALL_LOCAL_SUBROUTINE),
1195            UInt8(operator::ENDCHAR),
1196        ],
1197        "M 10 0 L 40 40 Z ",
1198        rect(10, 0, 40, 40)
1199    );
1200
1201    test_cs_with_subrs!(endchar_in_subr,
1202        &[],
1203        &[&[
1204            CFFInt(30),
1205            CFFInt(40),
1206            UInt8(operator::LINE_TO),
1207            UInt8(operator::ENDCHAR),
1208        ]],
1209        &[
1210            CFFInt(10),
1211            UInt8(operator::HORIZONTAL_MOVE_TO),
1212            CFFInt(0 - 107), // subr index - subr bias
1213            UInt8(operator::CALL_LOCAL_SUBROUTINE),
1214        ],
1215        "M 10 0 L 40 40 Z ",
1216        rect(10, 0, 40, 40)
1217    );
1218
1219    test_cs_with_subrs!(global_subr,
1220        &[&[
1221            CFFInt(30),
1222            CFFInt(40),
1223            UInt8(operator::LINE_TO),
1224            UInt8(operator::RETURN),
1225        ]],
1226        &[],
1227        &[
1228            CFFInt(10),
1229            UInt8(operator::HORIZONTAL_MOVE_TO),
1230            CFFInt(0 - 107), // subr index - subr bias
1231            UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1232            UInt8(operator::ENDCHAR),
1233        ],
1234        "M 10 0 L 40 40 Z ",
1235        rect(10, 0, 40, 40)
1236    );
1237
1238    test_cs_err!(reserved_operator, &[
1239        CFFInt(10), UInt8(2),
1240        UInt8(operator::ENDCHAR),
1241    ], CFFError::InvalidOperator);
1242
1243    test_cs_err!(line_to_without_move_to, &[
1244        CFFInt(10), CFFInt(20), UInt8(operator::LINE_TO),
1245        UInt8(operator::ENDCHAR),
1246    ], CFFError::MissingMoveTo);
1247
1248    // Width must be set only once.
1249    test_cs_err!(two_vmove_to_with_width, &[
1250        CFFInt(10), CFFInt(20), UInt8(operator::VERTICAL_MOVE_TO),
1251        CFFInt(10), CFFInt(20), UInt8(operator::VERTICAL_MOVE_TO),
1252        UInt8(operator::ENDCHAR),
1253    ], CFFError::InvalidArgumentsStackLength);
1254
1255    test_cs_err!(move_to_with_too_many_coords, &[
1256        CFFInt(10), CFFInt(10), CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1257        UInt8(operator::ENDCHAR),
1258    ], CFFError::InvalidArgumentsStackLength);
1259
1260    test_cs_err!(move_to_with_not_enought_coords, &[
1261        CFFInt(10), UInt8(operator::MOVE_TO),
1262        UInt8(operator::ENDCHAR),
1263    ], CFFError::InvalidArgumentsStackLength);
1264
1265    test_cs_err!(hmove_to_with_too_many_coords, &[
1266        CFFInt(10), CFFInt(10), CFFInt(10), UInt8(operator::HORIZONTAL_MOVE_TO),
1267        UInt8(operator::ENDCHAR),
1268    ], CFFError::InvalidArgumentsStackLength);
1269
1270    test_cs_err!(hmove_to_with_not_enought_coords, &[
1271        UInt8(operator::HORIZONTAL_MOVE_TO),
1272        UInt8(operator::ENDCHAR),
1273    ], CFFError::InvalidArgumentsStackLength);
1274
1275    test_cs_err!(vmove_to_with_too_many_coords, &[
1276        CFFInt(10), CFFInt(10), CFFInt(10), UInt8(operator::VERTICAL_MOVE_TO),
1277        UInt8(operator::ENDCHAR),
1278    ], CFFError::InvalidArgumentsStackLength);
1279
1280    test_cs_err!(vmove_to_with_not_enought_coords, &[
1281        UInt8(operator::VERTICAL_MOVE_TO),
1282        UInt8(operator::ENDCHAR),
1283    ], CFFError::InvalidArgumentsStackLength);
1284
1285    test_cs_err!(line_to_with_single_coord, &[
1286        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1287        CFFInt(30), UInt8(operator::LINE_TO),
1288        UInt8(operator::ENDCHAR),
1289    ], CFFError::InvalidArgumentsStackLength);
1290
1291    test_cs_err!(line_to_with_odd_number_of_coord, &[
1292        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1293        CFFInt(30), CFFInt(40), CFFInt(50), UInt8(operator::LINE_TO),
1294        UInt8(operator::ENDCHAR),
1295    ], CFFError::InvalidArgumentsStackLength);
1296
1297    test_cs_err!(hline_to_without_coords, &[
1298        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1299        UInt8(operator::HORIZONTAL_LINE_TO),
1300        UInt8(operator::ENDCHAR),
1301    ], CFFError::InvalidArgumentsStackLength);
1302
1303    test_cs_err!(vline_to_without_coords, &[
1304        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1305        UInt8(operator::VERTICAL_LINE_TO),
1306        UInt8(operator::ENDCHAR),
1307    ], CFFError::InvalidArgumentsStackLength);
1308
1309    test_cs_err!(curve_to_with_invalid_num_of_coords_1, &[
1310        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1311        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), UInt8(operator::CURVE_TO),
1312        UInt8(operator::ENDCHAR),
1313    ], CFFError::InvalidArgumentsStackLength);
1314
1315    test_cs_err!(curve_to_with_invalid_num_of_coords_2, &[
1316        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1317        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(60), CFFInt(70), CFFInt(80), CFFInt(90),
1318        UInt8(operator::CURVE_TO),
1319        UInt8(operator::ENDCHAR),
1320    ], CFFError::InvalidArgumentsStackLength);
1321
1322    test_cs_err!(hh_curve_to_with_not_enought_coords, &[
1323        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1324        CFFInt(30), CFFInt(40), CFFInt(50), UInt8(operator::HH_CURVE_TO),
1325        UInt8(operator::ENDCHAR),
1326    ], CFFError::InvalidArgumentsStackLength);
1327
1328    test_cs_err!(hh_curve_to_with_too_many_coords, &[
1329        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1330        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(30), CFFInt(40), CFFInt(50),
1331        UInt8(operator::HH_CURVE_TO),
1332        UInt8(operator::ENDCHAR),
1333    ], CFFError::InvalidArgumentsStackLength);
1334
1335    test_cs_err!(vv_curve_to_with_not_enought_coords, &[
1336        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1337        CFFInt(30), CFFInt(40), CFFInt(50), UInt8(operator::VV_CURVE_TO),
1338        UInt8(operator::ENDCHAR),
1339    ], CFFError::InvalidArgumentsStackLength);
1340
1341    test_cs_err!(vv_curve_to_with_too_many_coords, &[
1342        CFFInt(10), CFFInt(20), UInt8(operator::MOVE_TO),
1343        CFFInt(30), CFFInt(40), CFFInt(50), CFFInt(30), CFFInt(40), CFFInt(50),
1344        UInt8(operator::VV_CURVE_TO),
1345        UInt8(operator::ENDCHAR),
1346    ], CFFError::InvalidArgumentsStackLength);
1347
1348    test_cs_err!(multiple_endchar, &[
1349        UInt8(operator::ENDCHAR),
1350        UInt8(operator::ENDCHAR),
1351    ], CFFError::DataAfterEndChar);
1352
1353    test_cs_err!(operands_overflow, &[
1354        CFFInt(0), CFFInt(1), CFFInt(2), CFFInt(3), CFFInt(4), CFFInt(5), CFFInt(6), CFFInt(7), CFFInt(8), CFFInt(9),
1355        CFFInt(0), CFFInt(1), CFFInt(2), CFFInt(3), CFFInt(4), CFFInt(5), CFFInt(6), CFFInt(7), CFFInt(8), CFFInt(9),
1356        CFFInt(0), CFFInt(1), CFFInt(2), CFFInt(3), CFFInt(4), CFFInt(5), CFFInt(6), CFFInt(7), CFFInt(8), CFFInt(9),
1357        CFFInt(0), CFFInt(1), CFFInt(2), CFFInt(3), CFFInt(4), CFFInt(5), CFFInt(6), CFFInt(7), CFFInt(8), CFFInt(9),
1358        CFFInt(0), CFFInt(1), CFFInt(2), CFFInt(3), CFFInt(4), CFFInt(5), CFFInt(6), CFFInt(7), CFFInt(8), CFFInt(9),
1359    ], CFFError::ArgumentsStackLimitReached);
1360
1361    test_cs_err!(operands_overflow_with_4_byte_ints, &[
1362        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1363        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1364        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1365        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1366        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1367        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1368        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1369        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1370        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1371        CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000), CFFInt(30000),
1372    ], CFFError::ArgumentsStackLimitReached);
1373
1374    test_cs_err!(bbox_overflow, &[
1375        CFFInt(32767), UInt8(operator::HORIZONTAL_MOVE_TO),
1376        CFFInt(32767), UInt8(operator::HORIZONTAL_LINE_TO),
1377        UInt8(operator::ENDCHAR),
1378    ], CFFError::BboxOverflow);
1379
1380    #[test]
1381    fn endchar_in_subr_with_extra_data_1() {
1382        let data = gen_cff(
1383            &[],
1384            &[&[
1385                CFFInt(30),
1386                CFFInt(40),
1387                UInt8(operator::LINE_TO),
1388                UInt8(operator::ENDCHAR),
1389            ]],
1390            &[
1391                CFFInt(10),
1392                UInt8(operator::HORIZONTAL_MOVE_TO),
1393                CFFInt(0 - 107), // subr index - subr bias
1394                UInt8(operator::CALL_LOCAL_SUBROUTINE),
1395                CFFInt(30),
1396                CFFInt(40),
1397                UInt8(operator::LINE_TO),
1398            ]
1399        );
1400
1401        let metadata = parse_metadata(&data).unwrap();
1402        let mut builder = Builder(String::new());
1403        let char_str = metadata.char_strings.get(0).unwrap();
1404        let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1405        assert_eq!(res.unwrap_err(), CFFError::DataAfterEndChar);
1406    }
1407
1408    #[test]
1409    fn endchar_in_subr_with_extra_data_2() {
1410        let data = gen_cff(
1411            &[],
1412            &[&[
1413                CFFInt(30),
1414                CFFInt(40),
1415                UInt8(operator::LINE_TO),
1416                UInt8(operator::ENDCHAR),
1417                CFFInt(30),
1418                CFFInt(40),
1419                UInt8(operator::LINE_TO),
1420            ]],
1421            &[
1422                CFFInt(10),
1423                UInt8(operator::HORIZONTAL_MOVE_TO),
1424                CFFInt(0 - 107), // subr index - subr bias
1425                UInt8(operator::CALL_LOCAL_SUBROUTINE),
1426            ]
1427        );
1428
1429        let metadata = parse_metadata(&data).unwrap();
1430        let mut builder = Builder(String::new());
1431        let char_str = metadata.char_strings.get(0).unwrap();
1432        let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1433        assert_eq!(res.unwrap_err(), CFFError::DataAfterEndChar);
1434    }
1435
1436    #[test]
1437    fn subr_without_return() {
1438        let data = gen_cff(
1439            &[],
1440            &[&[
1441                CFFInt(30),
1442                CFFInt(40),
1443                UInt8(operator::LINE_TO),
1444                UInt8(operator::ENDCHAR),
1445                CFFInt(30),
1446                CFFInt(40),
1447                UInt8(operator::LINE_TO),
1448            ]],
1449            &[
1450                CFFInt(10),
1451                UInt8(operator::HORIZONTAL_MOVE_TO),
1452                CFFInt(0 - 107), // subr index - subr bias
1453                UInt8(operator::CALL_LOCAL_SUBROUTINE),
1454            ]
1455        );
1456
1457        let metadata = parse_metadata(&data).unwrap();
1458        let mut builder = Builder(String::new());
1459        let char_str = metadata.char_strings.get(0).unwrap();
1460        let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1461        assert_eq!(res.unwrap_err(), CFFError::DataAfterEndChar);
1462    }
1463
1464    #[test]
1465    fn recursive_local_subr() {
1466        let data = gen_cff(
1467            &[],
1468            &[&[
1469                CFFInt(0 - 107), // subr index - subr bias
1470                UInt8(operator::CALL_LOCAL_SUBROUTINE),
1471            ]],
1472            &[
1473                CFFInt(10),
1474                UInt8(operator::HORIZONTAL_MOVE_TO),
1475                CFFInt(0 - 107), // subr index - subr bias
1476                UInt8(operator::CALL_LOCAL_SUBROUTINE),
1477            ]
1478        );
1479
1480        let metadata = parse_metadata(&data).unwrap();
1481        let mut builder = Builder(String::new());
1482        let char_str = metadata.char_strings.get(0).unwrap();
1483        let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1484        assert_eq!(res.unwrap_err(), CFFError::NestingLimitReached);
1485    }
1486
1487    #[test]
1488    fn recursive_global_subr() {
1489        let data = gen_cff(
1490            &[&[
1491                CFFInt(0 - 107), // subr index - subr bias
1492                UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1493            ]],
1494            &[],
1495            &[
1496                CFFInt(10),
1497                UInt8(operator::HORIZONTAL_MOVE_TO),
1498                CFFInt(0 - 107), // subr index - subr bias
1499                UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1500            ]
1501        );
1502
1503        let metadata = parse_metadata(&data).unwrap();
1504        let mut builder = Builder(String::new());
1505        let char_str = metadata.char_strings.get(0).unwrap();
1506        let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1507        assert_eq!(res.unwrap_err(), CFFError::NestingLimitReached);
1508    }
1509
1510    #[test]
1511    fn recursive_mixed_subr() {
1512        let data = gen_cff(
1513            &[&[
1514                CFFInt(0 - 107), // subr index - subr bias
1515                UInt8(operator::CALL_LOCAL_SUBROUTINE),
1516            ]],
1517            &[&[
1518                CFFInt(0 - 107), // subr index - subr bias
1519                UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1520            ]],
1521            &[
1522                CFFInt(10),
1523                UInt8(operator::HORIZONTAL_MOVE_TO),
1524                CFFInt(0 - 107), // subr index - subr bias
1525                UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1526            ]
1527        );
1528
1529        let metadata = parse_metadata(&data).unwrap();
1530        let mut builder = Builder(String::new());
1531        let char_str = metadata.char_strings.get(0).unwrap();
1532        let res = parse_char_string(char_str, &metadata, GlyphId(0), &mut builder);
1533        assert_eq!(res.unwrap_err(), CFFError::NestingLimitReached);
1534    }
1535
1536    #[test]
1537    fn zero_char_string_offset() {
1538        let data = writer::convert(&[
1539            // Header
1540            UInt8(1), // major version
1541            UInt8(0), // minor version
1542            UInt8(4), // header size
1543            UInt8(0), // absolute offset
1544
1545            // Name INDEX
1546            UInt16(0), // count
1547
1548            // Top DICT
1549            // INDEX
1550            UInt16(1), // count
1551            UInt8(1), // offset size
1552            UInt8(1), // index[0]
1553            UInt8(3), // index[1]
1554            // Data
1555            CFFInt(0), // zero offset!
1556            UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
1557        ]);
1558
1559        assert!(parse_metadata(&data).is_none());
1560    }
1561
1562    #[test]
1563    fn invalid_char_string_offset() {
1564        let data = writer::convert(&[
1565            // Header
1566            UInt8(1), // major version
1567            UInt8(0), // minor version
1568            UInt8(4), // header size
1569            UInt8(0), // absolute offset
1570
1571            // Name INDEX
1572            UInt16(0), // count
1573
1574            // Top DICT
1575            // INDEX
1576            UInt16(1), // count
1577            UInt8(1), // offset size
1578            UInt8(1), // index[0]
1579            UInt8(3), // index[1]
1580            // Data
1581            CFFInt(2), // invalid offset!
1582            UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
1583        ]);
1584
1585        assert!(parse_metadata(&data).is_none());
1586    }
1587
1588    // TODO: return from main
1589    // TODO: return without endchar
1590    // TODO: data after return
1591    // TODO: recursive subr
1592    // TODO: HORIZONTAL_STEM
1593    // TODO: VERTICAL_STEM
1594    // TODO: HORIZONTAL_STEM_HINT_MASK
1595    // TODO: HINT_MASK
1596    // TODO: COUNTER_MASK
1597    // TODO: VERTICAL_STEM_HINT_MASK
1598    // TODO: CURVE_LINE
1599    // TODO: LINE_CURVE
1600    // TODO: VH_CURVE_TO
1601    // TODO: HFLEX
1602    // TODO: FLEX
1603    // TODO: HFLEX1
1604    // TODO: FLEX1
1605
1606    #[test]
1607    fn private_dict_size_overflow() {
1608        let data = &[
1609            0x00, 0x01, // count: 1
1610            0x01, // offset size: 1
1611            0x01, // index [0]: 1
1612            0x0C, // index [1]: 14
1613            0x1D, 0x7F, 0xFF, 0xFF, 0xFF, // length: i32::MAX
1614            0x1D, 0x7F, 0xFF, 0xFF, 0xFF, // offset: i32::MAX
1615            0x12 // operator: 18 (private)
1616        ];
1617
1618        let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
1619        assert_eq!(top_dict.private_dict_range, Some(2147483647..4294967294));
1620    }
1621
1622    #[test]
1623    fn private_dict_negative_char_strings_offset() {
1624        let data = &[
1625            0x00, 0x01, // count: 1
1626            0x01, // offset size: 1
1627            0x01, // index [0]: 1
1628            0x03, // index [1]: 3
1629            // Item 0
1630            0x8A, // offset: -1
1631            0x11, // operator: 17 (char_string)
1632        ];
1633
1634        assert!(parse_top_dict(&mut Stream::new(data)).is_none());
1635    }
1636
1637    #[test]
1638    fn private_dict_no_char_strings_offset_operand() {
1639        let data = &[
1640            0x00, 0x01, // count: 1
1641            0x01, // offset size: 1
1642            0x01, // index [0]: 1
1643            0x02, // index [1]: 2
1644            // Item 0
1645            // <-- No number here.
1646            0x11, // operator: 17 (char_string)
1647        ];
1648
1649        assert!(parse_top_dict(&mut Stream::new(data)).is_none());
1650    }
1651
1652    #[test]
1653    fn negative_private_dict_offset_and_size() {
1654        let data = &[
1655            0x00, 0x01, // count: 1
1656            0x01, // offset size: 1
1657            0x01, // index [0]: 1
1658            0x04, // index [1]: 4
1659            // Item 0
1660            0x8A, // length: -1
1661            0x8A, // offset: -1
1662            0x12, // operator: 18 (private)
1663        ];
1664
1665        let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
1666        assert!(top_dict.private_dict_range.is_none());
1667    }
1668}