1use 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
19const MAX_OPERANDS_LEN: usize = 48;
21
22const STACK_LIMIT: u8 = 10;
24const MAX_ARGUMENTS_STACK_LEN: usize = 48;
25
26const TWO_BYTE_OPERATOR_MARK: u8 = 12;
27
28mod 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
61mod 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
72mod private_dict_operator {
75 pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19;
76}
77
78mod 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 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 let major: u8 = s.read()?;
121 s.skip::<u8>(); let header_size: u8 = s.read()?;
123 s.skip::<u8>(); if major != 1 {
126 return None;
127 }
128
129 if header_size > 4 {
131 s.advance(usize::from(header_size) - 4);
132 }
133
134 skip_index::<u16>(&mut s)?;
136
137 let top_dict = parse_top_dict(&mut s)?;
138
139 if top_dict.char_strings_offset == 0 {
141 return None;
142 }
143
144 let strings = parse_index::<u16>(&mut s)?;
146
147 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 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, };
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 let mut metadata = SIDMetadata::default();
195
196 match (top_dict.private_dict_range, subroutines_offset) {
197 (Some(private_dict_range), Some(subroutines_offset)) => {
198 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, };
218
219 if charset_offset <= charset_id::EXPERT_SUBSET {
220 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 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
311fn 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 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, 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, };
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], 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 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 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 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 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 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 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 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 p.stack.clear();
586
587 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 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 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]), }
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 let number_of_ranges = number_of_ranges.checked_add(1)?;
724
725 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 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 w.write(UInt8(1)); w.write(UInt8(0)); w.write(UInt8(4)); w.write(UInt8(0)); w.write(UInt16(0)); w.write(UInt16(1)); w.write(UInt8(1)); w.write(UInt8(1)); let top_dict_idx2 = if local_subrs.is_empty() { 3 } else { 6 };
844 w.write(UInt8(top_dict_idx2)); let mut charstr_offset = w.offset() + 2;
847 charstr_offset += EMPTY_INDEX_SIZE; 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 w.write(CFFInt(2)); w.write(CFFInt((charstr_offset + INDEX_HEADER_SIZE + chars_data.len()) as i32)); w.write(UInt8(top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET as u8));
868 }
869
870 w.write(UInt16(0)); if global_subrs_data.is_empty() {
875 w.write(UInt16(0)); } else {
877 w.write(UInt16(1)); w.write(UInt8(1)); w.write(UInt8(1)); w.write(UInt8(global_subrs_data.len() as u8 + 1)); w.data.extend_from_slice(&global_subrs_data);
882 }
883
884 w.write(UInt16(1)); w.write(UInt8(1)); w.write(UInt8(1)); w.write(UInt8(chars_data.len() as u8 + 1)); w.data.extend_from_slice(&chars_data);
890
891 if !local_subrs_data.is_empty() {
892 w.write(CFFInt(2));
896 w.write(UInt8(private_dict_operator::LOCAL_SUBROUTINES_OFFSET as u8));
897
898 w.write(UInt16(1)); w.write(UInt8(1)); w.write(UInt8(1)); w.write(UInt8(local_subrs_data.len() as u8 + 1)); 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), UInt8(0), UInt8(4), UInt8(0), ]);
917
918 assert!(parse_metadata(&data).is_none());
919 }
920
921 #[test]
922 fn non_default_header_size() {
923 let data = writer::convert(&[
924 UInt8(1), UInt8(0), UInt8(8), UInt8(0), UInt8(0),
932 UInt8(0),
933 UInt8(0),
934 UInt8(0),
935
936 UInt16(0), UInt16(1), UInt8(1), UInt8(1), UInt8(3), CFFInt(21),
947 UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
948
949 UInt16(0), UInt16(0), UInt16(1), UInt8(1), UInt8(1), UInt8(4), 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), 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), 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), 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 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), 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), 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), 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), UInt8(operator::CALL_LOCAL_SUBROUTINE),
1471 ]],
1472 &[
1473 CFFInt(10),
1474 UInt8(operator::HORIZONTAL_MOVE_TO),
1475 CFFInt(0 - 107), 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), UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1493 ]],
1494 &[],
1495 &[
1496 CFFInt(10),
1497 UInt8(operator::HORIZONTAL_MOVE_TO),
1498 CFFInt(0 - 107), 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), UInt8(operator::CALL_LOCAL_SUBROUTINE),
1516 ]],
1517 &[&[
1518 CFFInt(0 - 107), UInt8(operator::CALL_GLOBAL_SUBROUTINE),
1520 ]],
1521 &[
1522 CFFInt(10),
1523 UInt8(operator::HORIZONTAL_MOVE_TO),
1524 CFFInt(0 - 107), 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 UInt8(1), UInt8(0), UInt8(4), UInt8(0), UInt16(0), UInt16(1), UInt8(1), UInt8(1), UInt8(3), CFFInt(0), 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 UInt8(1), UInt8(0), UInt8(4), UInt8(0), UInt16(0), UInt16(1), UInt8(1), UInt8(1), UInt8(3), CFFInt(2), UInt8(top_dict_operator::CHAR_STRINGS_OFFSET as u8),
1583 ]);
1584
1585 assert!(parse_metadata(&data).is_none());
1586 }
1587
1588 #[test]
1607 fn private_dict_size_overflow() {
1608 let data = &[
1609 0x00, 0x01, 0x01, 0x01, 0x0C, 0x1D, 0x7F, 0xFF, 0xFF, 0xFF, 0x1D, 0x7F, 0xFF, 0xFF, 0xFF, 0x12 ];
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, 0x01, 0x01, 0x03, 0x8A, 0x11, ];
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, 0x01, 0x01, 0x02, 0x11, ];
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, 0x01, 0x01, 0x04, 0x8A, 0x8A, 0x12, ];
1664
1665 let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
1666 assert!(top_dict.private_dict_range.is_none());
1667 }
1668}