ttf_parser/tables/cff/
cff2.rs

1// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
2// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
3
4use core::convert::TryFrom;
5use core::ops::Range;
6
7use crate::{GlyphId, OutlineBuilder, Rect, BBox, NormalizedCoordinate};
8use crate::parser::{Stream, NumFrom, TryNumFrom};
9use crate::var_store::*;
10use super::{Builder, CFFError, calc_subroutine_bias, conv_subroutine_index};
11use super::argstack::ArgumentsStack;
12use super::charstring::CharStringParser;
13use super::dict::DictionaryParser;
14use super::index::{Index, parse_index};
15
16// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#7-top-dict-data
17// 'Operators in DICT may be preceded by up to a maximum of 513 operands.'
18const MAX_OPERANDS_LEN: usize = 513;
19
20// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr#appendix-b-cff2-charstring-implementation-limits
21const STACK_LIMIT: u8 = 10;
22const MAX_ARGUMENTS_STACK_LEN: usize = 513;
23
24const TWO_BYTE_OPERATOR_MARK: u8 = 12;
25
26// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr#4-charstring-operators
27mod operator {
28    pub const HORIZONTAL_STEM: u8           = 1;
29    pub const VERTICAL_STEM: u8             = 3;
30    pub const VERTICAL_MOVE_TO: u8          = 4;
31    pub const LINE_TO: u8                   = 5;
32    pub const HORIZONTAL_LINE_TO: u8        = 6;
33    pub const VERTICAL_LINE_TO: u8          = 7;
34    pub const CURVE_TO: u8                  = 8;
35    pub const CALL_LOCAL_SUBROUTINE: u8     = 10;
36    pub const VS_INDEX: u8                  = 15;
37    pub const BLEND: u8                     = 16;
38    pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18;
39    pub const HINT_MASK: u8                 = 19;
40    pub const COUNTER_MASK: u8              = 20;
41    pub const MOVE_TO: u8                   = 21;
42    pub const HORIZONTAL_MOVE_TO: u8        = 22;
43    pub const VERTICAL_STEM_HINT_MASK: u8   = 23;
44    pub const CURVE_LINE: u8                = 24;
45    pub const LINE_CURVE: u8                = 25;
46    pub const VV_CURVE_TO: u8               = 26;
47    pub const HH_CURVE_TO: u8               = 27;
48    pub const SHORT_INT: u8                 = 28;
49    pub const CALL_GLOBAL_SUBROUTINE: u8    = 29;
50    pub const VH_CURVE_TO: u8               = 30;
51    pub const HV_CURVE_TO: u8               = 31;
52    pub const HFLEX: u8                     = 34;
53    pub const FLEX: u8                      = 35;
54    pub const HFLEX1: u8                    = 36;
55    pub const FLEX1: u8                     = 37;
56    pub const FIXED_16_16: u8               = 255;
57}
58
59// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-9-top-dict-operator-entries
60mod top_dict_operator {
61    pub const CHAR_STRINGS_OFFSET: u16      = 17;
62    pub const VARIATION_STORE_OFFSET: u16   = 24;
63    pub const FONT_DICT_INDEX_OFFSET: u16   = 1236;
64}
65
66// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-10-font-dict-operator-entries
67mod font_dict_operator {
68    pub const PRIVATE_DICT_SIZE_AND_OFFSET: u16 = 18;
69}
70
71// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-16-private-dict-operators
72mod private_dict_operator {
73    pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19;
74}
75
76
77#[derive(Clone, Copy, Default)]
78pub struct Metadata<'a> {
79    global_subrs: Index<'a>,
80    local_subrs: Index<'a>,
81    char_strings: Index<'a>,
82    item_variation_store: ItemVariationStore<'a>,
83}
84
85pub(crate) fn parse_metadata(data: &[u8]) -> Option<Metadata> {
86    let mut s = Stream::new(data);
87
88    // Parse Header.
89    let major: u8 = s.read()?;
90    s.skip::<u8>(); // minor
91    let header_size: u8 = s.read()?;
92    let top_dict_length: u16 = s.read()?;
93
94    if major != 2 {
95        return None;
96    }
97
98    // Jump to Top DICT. It's not necessarily right after the header.
99    if header_size > 5 {
100        s.advance(usize::from(header_size) - 5);
101    }
102
103    let top_dict_data = s.read_bytes(usize::from(top_dict_length))?;
104    let top_dict = parse_top_dict(top_dict_data)?;
105
106    let mut metadata = Metadata::default();
107
108    // Parse Global Subroutines INDEX.
109    metadata.global_subrs = parse_index::<u32>(&mut s)?;
110
111    metadata.char_strings = {
112        let mut s = Stream::new_at(data, top_dict.char_strings_offset)?;
113        parse_index::<u32>(&mut s)?
114    };
115
116    if let Some(offset) = top_dict.variation_store_offset {
117        let mut s = Stream::new_at(data, offset)?;
118        s.skip::<u16>(); // length
119        metadata.item_variation_store = ItemVariationStore::parse(s)?;
120    }
121
122    // TODO: simplify
123    if let Some(offset) = top_dict.font_dict_index_offset {
124        let mut s = Stream::new_at(data, offset)?;
125        'outer: for font_dict_data in parse_index::<u32>(&mut s)? {
126            if let Some(private_dict_range) = parse_font_dict(font_dict_data) {
127                // 'Private DICT size and offset, from start of the CFF2 table.'
128                let private_dict_data = data.get(private_dict_range.clone())?;
129                if let Some(subroutines_offset) = parse_private_dict(private_dict_data) {
130                    // 'The local subroutines offset is relative to the beginning
131                    // of the Private DICT data.'
132                    if let Some(start) = private_dict_range.start.checked_add(subroutines_offset) {
133                        let data = data.get(start..data.len())?;
134                        let mut s = Stream::new(data);
135                        metadata.local_subrs = parse_index::<u32>(&mut s)?;
136                        break 'outer;
137                    }
138                }
139            }
140        }
141    }
142
143    Some(metadata)
144}
145
146
147pub(crate) fn outline(
148    metadata: &Metadata,
149    coordinates: &[NormalizedCoordinate],
150    glyph_id: GlyphId,
151    builder: &mut dyn OutlineBuilder,
152) -> Option<Rect> {
153    let data = metadata.char_strings.get(u32::from(glyph_id.0))?;
154    parse_char_string(data, metadata, coordinates, builder).ok()
155}
156
157#[derive(Clone, Copy, Default)]
158struct TopDictData {
159    char_strings_offset: usize,
160    font_dict_index_offset: Option<usize>,
161    variation_store_offset: Option<usize>,
162}
163
164fn parse_top_dict(data: &[u8]) -> Option<TopDictData> {
165    let mut dict_data = TopDictData::default();
166
167    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
168    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
169    while let Some(operator) = dict_parser.parse_next() {
170        if operator.get() == top_dict_operator::CHAR_STRINGS_OFFSET {
171            dict_data.char_strings_offset = dict_parser.parse_offset()?;
172        } else if operator.get() == top_dict_operator::FONT_DICT_INDEX_OFFSET {
173            dict_data.font_dict_index_offset = dict_parser.parse_offset();
174        } else if operator.get() == top_dict_operator::VARIATION_STORE_OFFSET {
175            dict_data.variation_store_offset = dict_parser.parse_offset();
176        }
177    }
178
179    // Must be set, otherwise there are nothing to parse.
180    if dict_data.char_strings_offset == 0 {
181        return None;
182    }
183
184    Some(dict_data)
185}
186
187fn parse_font_dict(data: &[u8]) -> Option<Range<usize>> {
188    let mut private_dict_range = None;
189
190    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
191    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
192    while let Some(operator) = dict_parser.parse_next() {
193        if operator.get() == font_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET {
194            dict_parser.parse_operands()?;
195            let operands = dict_parser.operands();
196
197            if operands.len() == 2 {
198                let len = usize::try_from(operands[0]).ok()?;
199                let start = usize::try_from(operands[1]).ok()?;
200                let end = start.checked_add(len)?;
201                private_dict_range = Some(start..end);
202            }
203
204            break;
205        }
206    }
207
208    private_dict_range
209}
210
211fn parse_private_dict(data: &[u8]) -> Option<usize> {
212    let mut subroutines_offset = None;
213    let mut operands_buffer = [0; MAX_OPERANDS_LEN];
214    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
215    while let Some(operator) = dict_parser.parse_next() {
216        if operator.get() == private_dict_operator::LOCAL_SUBROUTINES_OFFSET {
217            dict_parser.parse_operands()?;
218            let operands = dict_parser.operands();
219
220            if operands.len() == 1 {
221                subroutines_offset = usize::try_from(operands[0]).ok();
222            }
223
224            break;
225        }
226    }
227
228    subroutines_offset
229}
230
231/// CFF2 allows up to 65535 scalars, but an average font will have 3-5.
232/// So 64 is more than enough.
233const SCALARS_MAX: u8 = 64;
234
235#[derive(Clone, Copy)]
236pub struct Scalars {
237    d: [f32; SCALARS_MAX as usize], // 256B
238    len: u8,
239}
240
241impl Default for Scalars {
242    fn default() -> Self {
243        Scalars {
244            d: [0.0; SCALARS_MAX as usize],
245            len: 0,
246        }
247    }
248}
249
250impl Scalars {
251    pub fn len(&self) -> u8 {
252        self.len
253    }
254
255    pub fn clear(&mut self) {
256        self.len = 0;
257    }
258
259    pub fn at(&self, i: u8) -> f32 {
260        if i < self.len {
261            self.d[usize::from(i)]
262        } else {
263            0.0
264        }
265    }
266
267    pub fn push(&mut self, n: f32) -> Option<()> {
268        if self.len < SCALARS_MAX {
269            self.d[usize::from(self.len)] = n;
270            self.len += 1;
271            Some(())
272        } else {
273            None
274        }
275    }
276}
277
278struct CharStringParserContext<'a> {
279    metadata: &'a Metadata<'a>,
280    coordinates: &'a [NormalizedCoordinate],
281    scalars: Scalars,
282    had_vsindex: bool,
283    had_blend: bool,
284    stems_len: u32,
285}
286
287impl CharStringParserContext<'_> {
288    fn update_scalars(&mut self, index: u16) -> Result<(), CFFError> {
289        self.scalars.clear();
290
291        let indices = self.metadata.item_variation_store.region_indices(index)
292            .ok_or(CFFError::InvalidItemVariationDataIndex)?;
293        for index in indices {
294            let scalar = self.metadata.item_variation_store.regions
295                .evaluate_region(index, self.coordinates);
296            self.scalars.push(scalar)
297                .ok_or(CFFError::BlendRegionsLimitReached)?;
298        }
299
300        Ok(())
301    }
302}
303
304fn parse_char_string(
305    data: &[u8],
306    metadata: &Metadata,
307    coordinates: &[NormalizedCoordinate],
308    builder: &mut dyn OutlineBuilder,
309) -> Result<Rect, CFFError> {
310    let mut ctx = CharStringParserContext {
311        metadata,
312        coordinates,
313        scalars: Scalars::default(),
314        had_vsindex: false,
315        had_blend: false,
316        stems_len: 0,
317    };
318
319    // Load scalars at default index.
320    ctx.update_scalars(0)?;
321
322    let mut inner_builder = Builder {
323        builder,
324        bbox: BBox::new(),
325    };
326
327    let stack = ArgumentsStack {
328        data: &mut [0.0; MAX_ARGUMENTS_STACK_LEN], // 2052B
329        len: 0,
330        max_len: MAX_ARGUMENTS_STACK_LEN,
331    };
332    let mut parser = CharStringParser {
333        stack,
334        builder: &mut inner_builder,
335        x: 0.0,
336        y: 0.0,
337        has_move_to: false,
338        is_first_move_to: true,
339    };
340    _parse_char_string(&mut ctx, data, 0, &mut parser)?;
341    // let _ = _parse_char_string(&mut ctx, data, 0.0, 0.0, &mut stack, 0, &mut inner_builder)?;
342
343    let bbox = parser.builder.bbox;
344
345    // Check that bbox was changed.
346    if bbox.is_default() {
347        return Err(CFFError::ZeroBBox);
348    }
349
350    bbox.to_rect().ok_or(CFFError::BboxOverflow)
351}
352
353fn _parse_char_string(
354    ctx: &mut CharStringParserContext,
355    char_string: &[u8],
356    depth: u8,
357    p: &mut CharStringParser,
358) -> Result<(), CFFError> {
359    let mut s = Stream::new(char_string);
360    while !s.at_end() {
361        let op: u8 = s.read().ok_or(CFFError::ReadOutOfBounds)?;
362        match op {
363            0 | 2 | 9 | 11 | 13 | 14 | 17 => {
364                // Reserved.
365                return Err(CFFError::InvalidOperator);
366            }
367            operator::HORIZONTAL_STEM |
368            operator::VERTICAL_STEM |
369            operator::HORIZONTAL_STEM_HINT_MASK |
370            operator::VERTICAL_STEM_HINT_MASK => {
371                // y dy {dya dyb}* hstem
372                // x dx {dxa dxb}* vstem
373                // y dy {dya dyb}* hstemhm
374                // x dx {dxa dxb}* vstemhm
375
376                ctx.stems_len += p.stack.len() as u32 >> 1;
377
378                // We are ignoring the hint operators.
379                p.stack.clear();
380            }
381            operator::VERTICAL_MOVE_TO => {
382                p.parse_vertical_move_to(0)?;
383            }
384            operator::LINE_TO => {
385                p.parse_line_to()?;
386            }
387            operator::HORIZONTAL_LINE_TO => {
388                p.parse_horizontal_line_to()?;
389            }
390            operator::VERTICAL_LINE_TO => {
391                p.parse_vertical_line_to()?;
392            }
393            operator::CURVE_TO => {
394                p.parse_curve_to()?;
395            }
396            operator::CALL_LOCAL_SUBROUTINE => {
397                if p.stack.is_empty() {
398                    return Err(CFFError::InvalidArgumentsStackLength);
399                }
400
401                if depth == STACK_LIMIT {
402                    return Err(CFFError::NestingLimitReached);
403                }
404
405                let subroutine_bias = calc_subroutine_bias(ctx.metadata.local_subrs.len());
406                let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
407                let char_string = ctx.metadata.local_subrs.get(index)
408                    .ok_or(CFFError::InvalidSubroutineIndex)?;
409                _parse_char_string(ctx, char_string, depth + 1, p)?;
410            }
411            TWO_BYTE_OPERATOR_MARK => {
412                // flex
413                let op2: u8 = s.read().ok_or(CFFError::ReadOutOfBounds)?;
414                match op2 {
415                    operator::HFLEX => p.parse_hflex()?,
416                    operator::FLEX => p.parse_flex()?,
417                    operator::HFLEX1 => p.parse_hflex1()?,
418                    operator::FLEX1 => p.parse_flex1()?,
419                    _ => return Err(CFFError::UnsupportedOperator),
420                }
421            }
422            operator::VS_INDEX => {
423                // |- ivs vsindex (15) |-
424
425                // `vsindex` must precede the first `blend` operator, and may occur only once.
426                if ctx.had_blend || ctx.had_vsindex {
427                    // TODO: maybe add a custom error
428                    return Err(CFFError::InvalidOperator);
429                }
430
431                if p.stack.len() != 1 {
432                    return Err(CFFError::InvalidArgumentsStackLength);
433                }
434
435                let index = u16::try_num_from(p.stack.pop())
436                    .ok_or(CFFError::InvalidItemVariationDataIndex)?;
437                ctx.update_scalars(index)?;
438
439                ctx.had_vsindex = true;
440
441                p.stack.clear();
442            }
443            operator::BLEND => {
444                // num(0)..num(n-1), delta(0,0)..delta(k-1,0),
445                // delta(0,1)..delta(k-1,1) .. delta(0,n-1)..delta(k-1,n-1)
446                // n blend (16) val(0)..val(n-1)
447
448                ctx.had_blend = true;
449
450                let n = u16::try_num_from(p.stack.pop())
451                    .ok_or(CFFError::InvalidNumberOfBlendOperands)?;
452                let k = ctx.scalars.len();
453
454                let len = usize::from(n) * (usize::from(k) + 1);
455                if p.stack.len() < len {
456                    return Err(CFFError::InvalidArgumentsStackLength);
457                }
458
459                let start = p.stack.len() - len;
460                for i in (0..n).rev() {
461                    for j in 0..k {
462                        let delta = p.stack.pop();
463                        p.stack.data[start + usize::from(i)] += delta * ctx.scalars.at(k - j - 1);
464                    }
465                }
466            }
467            operator::HINT_MASK | operator::COUNTER_MASK => {
468                ctx.stems_len += p.stack.len() as u32 >> 1;
469                s.advance(usize::num_from((ctx.stems_len + 7) >> 3));
470
471                // We are ignoring the hint operators.
472                p.stack.clear();
473            }
474            operator::MOVE_TO => {
475                p.parse_move_to(0)?;
476            }
477            operator::HORIZONTAL_MOVE_TO => {
478                p.parse_horizontal_move_to(0)?;
479            }
480            operator::CURVE_LINE => {
481                p.parse_curve_line()?;
482            }
483            operator::LINE_CURVE => {
484                p.parse_line_curve()?;
485            }
486            operator::VV_CURVE_TO => {
487                p.parse_vv_curve_to()?;
488            }
489            operator::HH_CURVE_TO => {
490                p.parse_hh_curve_to()?;
491            }
492            operator::SHORT_INT => {
493                let n = s.read::<i16>().ok_or(CFFError::ReadOutOfBounds)?;
494                p.stack.push(f32::from(n))?;
495            }
496            operator::CALL_GLOBAL_SUBROUTINE => {
497                if p.stack.is_empty() {
498                    return Err(CFFError::InvalidArgumentsStackLength);
499                }
500
501                if depth == STACK_LIMIT {
502                    return Err(CFFError::NestingLimitReached);
503                }
504
505                let subroutine_bias = calc_subroutine_bias(ctx.metadata.global_subrs.len());
506                let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
507                let char_string = ctx.metadata.global_subrs.get(index)
508                    .ok_or(CFFError::InvalidSubroutineIndex)?;
509                _parse_char_string(ctx, char_string, depth + 1, p)?;
510            }
511            operator::VH_CURVE_TO => {
512                p.parse_vh_curve_to()?;
513            }
514            operator::HV_CURVE_TO => {
515                p.parse_hv_curve_to()?;
516            }
517            32..=246 => {
518                p.parse_int1(op)?;
519            }
520            247..=250 => {
521                p.parse_int2(op, &mut s)?;
522            }
523            251..=254 => {
524                p.parse_int3(op, &mut s)?;
525            }
526            operator::FIXED_16_16 => {
527                p.parse_fixed(&mut s)?;
528            }
529        }
530    }
531
532    Ok(())
533}