1use core::convert::TryFrom;
10use core::ops::Range;
11
12use crate::{GlyphId, OutlineBuilder, Rect, BBox};
13use crate::parser::{Stream, LazyArray16, NumFrom, TryNumFrom};
14use super::{Builder, IsEven, CFFError, StringId, calc_subroutine_bias, conv_subroutine_index};
15use super::argstack::ArgumentsStack;
16use super::charset::{STANDARD_ENCODING, Charset, parse_charset};
17use super::charstring::CharStringParser;
18use super::dict::DictionaryParser;
19use super::index::{Index, parse_index, skip_index};
20#[cfg(feature = "glyph-names")] use super::std_names::STANDARD_NAMES;
21
22const MAX_OPERANDS_LEN: usize = 48;
24
25const STACK_LIMIT: u8 = 10;
27const MAX_ARGUMENTS_STACK_LEN: usize = 48;
28
29const TWO_BYTE_OPERATOR_MARK: u8 = 12;
30
31mod operator {
33 pub const HORIZONTAL_STEM: u8 = 1;
34 pub const VERTICAL_STEM: u8 = 3;
35 pub const VERTICAL_MOVE_TO: u8 = 4;
36 pub const LINE_TO: u8 = 5;
37 pub const HORIZONTAL_LINE_TO: u8 = 6;
38 pub const VERTICAL_LINE_TO: u8 = 7;
39 pub const CURVE_TO: u8 = 8;
40 pub const CALL_LOCAL_SUBROUTINE: u8 = 10;
41 pub const RETURN: u8 = 11;
42 pub const ENDCHAR: u8 = 14;
43 pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18;
44 pub const HINT_MASK: u8 = 19;
45 pub const COUNTER_MASK: u8 = 20;
46 pub const MOVE_TO: u8 = 21;
47 pub const HORIZONTAL_MOVE_TO: u8 = 22;
48 pub const VERTICAL_STEM_HINT_MASK: u8 = 23;
49 pub const CURVE_LINE: u8 = 24;
50 pub const LINE_CURVE: u8 = 25;
51 pub const VV_CURVE_TO: u8 = 26;
52 pub const HH_CURVE_TO: u8 = 27;
53 pub const SHORT_INT: u8 = 28;
54 pub const CALL_GLOBAL_SUBROUTINE: u8 = 29;
55 pub const VH_CURVE_TO: u8 = 30;
56 pub const HV_CURVE_TO: u8 = 31;
57 pub const HFLEX: u8 = 34;
58 pub const FLEX: u8 = 35;
59 pub const HFLEX1: u8 = 36;
60 pub const FLEX1: u8 = 37;
61 pub const FIXED_16_16: u8 = 255;
62}
63
64mod top_dict_operator {
67 pub const CHARSET_OFFSET: u16 = 15;
68 pub const CHAR_STRINGS_OFFSET: u16 = 17;
69 pub const PRIVATE_DICT_SIZE_AND_OFFSET: u16 = 18;
70 pub const ROS: u16 = 1230;
71 pub const FD_ARRAY: u16 = 1236;
72 pub const FD_SELECT: u16 = 1237;
73}
74
75mod private_dict_operator {
78 pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19;
79}
80
81mod charset_id {
83 pub const ISO_ADOBE: usize = 0;
84 pub const EXPERT: usize = 1;
85 pub const EXPERT_SUBSET: usize = 2;
86}
87
88#[derive(Clone, Copy, Debug)]
89pub(crate) enum FontKind<'a> {
90 SID(SIDMetadata<'a>),
91 CID(CIDMetadata<'a>),
92}
93
94#[derive(Clone, Copy, Default, Debug)]
95pub(crate) struct SIDMetadata<'a> {
96 local_subrs: Index<'a>,
97}
98
99#[derive(Clone, Copy, Default, Debug)]
100pub(crate) struct CIDMetadata<'a> {
101 fd_array: Index<'a>,
102 fd_select: FDSelect<'a>,
103}
104
105#[derive(Default)]
106struct TopDict {
107 charset_offset: Option<usize>,
108 char_strings_offset: usize,
109 private_dict_range: Option<Range<usize>>,
110 has_ros: bool,
111 fd_array_offset: Option<usize>,
112 fd_select_offset: Option<usize>,
113}
114
115fn parse_top_dict(s: &mut Stream) -> Option<TopDict> {
116 let mut top_dict = TopDict::default();
117
118 let index = parse_index::<u16>(s)?;
119
120 let data = index.get(0)?;
122
123 let mut operands_buffer = [0; MAX_OPERANDS_LEN];
124 let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
125 while let Some(operator) = dict_parser.parse_next() {
126 match operator.get() {
127 top_dict_operator::CHARSET_OFFSET => {
128 top_dict.charset_offset = dict_parser.parse_offset();
129 }
130 top_dict_operator::CHAR_STRINGS_OFFSET => {
131 top_dict.char_strings_offset = dict_parser.parse_offset()?;
132 }
133 top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET => {
134 top_dict.private_dict_range = dict_parser.parse_range();
135 }
136 top_dict_operator::ROS => {
137 top_dict.has_ros = true;
138 }
139 top_dict_operator::FD_ARRAY => {
140 top_dict.fd_array_offset = dict_parser.parse_offset();
141 }
142 top_dict_operator::FD_SELECT => {
143 top_dict.fd_select_offset = dict_parser.parse_offset();
144 }
145 _ => {}
146 }
147 }
148
149 Some(top_dict)
150}
151
152#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn private_dict_size_overflow() {
159 let data = &[
160 0x00, 0x01, 0x01, 0x01, 0x0C, 0x1D, 0x7F, 0xFF, 0xFF, 0xFF, 0x1D, 0x7F, 0xFF, 0xFF, 0xFF, 0x12 ];
168
169 let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
170 assert_eq!(top_dict.private_dict_range, Some(2147483647..4294967294));
171 }
172
173 #[test]
174 fn private_dict_negative_char_strings_offset() {
175 let data = &[
176 0x00, 0x01, 0x01, 0x01, 0x03, 0x8A, 0x11, ];
184
185 assert!(parse_top_dict(&mut Stream::new(data)).is_none());
186 }
187
188 #[test]
189 fn private_dict_no_char_strings_offset_operand() {
190 let data = &[
191 0x00, 0x01, 0x01, 0x01, 0x02, 0x11, ];
199
200 assert!(parse_top_dict(&mut Stream::new(data)).is_none());
201 }
202
203 #[test]
204 fn negative_private_dict_offset_and_size() {
205 let data = &[
206 0x00, 0x01, 0x01, 0x01, 0x04, 0x8A, 0x8A, 0x12, ];
215
216 let top_dict = parse_top_dict(&mut Stream::new(data)).unwrap();
217 assert!(top_dict.private_dict_range.is_none());
218 }
219}
220
221fn parse_private_dict(data: &[u8]) -> Option<usize> {
222 let mut operands_buffer = [0; MAX_OPERANDS_LEN];
223 let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
224 while let Some(operator) = dict_parser.parse_next() {
225 if operator.get() == private_dict_operator::LOCAL_SUBROUTINES_OFFSET {
226 return dict_parser.parse_offset();
227 }
228 }
229
230 None
231}
232
233fn parse_font_dict(data: &[u8]) -> Option<Range<usize>> {
234 let mut operands_buffer = [0; MAX_OPERANDS_LEN];
235 let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
236 while let Some(operator) = dict_parser.parse_next() {
237 if operator.get() == top_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET {
238 return dict_parser.parse_range();
239 }
240 }
241
242 None
243}
244
245fn parse_cid_local_subrs<'a>(
252 data: &'a [u8],
253 glyph_id: GlyphId,
254 cid: &CIDMetadata,
255) -> Option<Index<'a>> {
256 let font_dict_index = cid.fd_select.font_dict_index(glyph_id)?;
257 let font_dict_data = cid.fd_array.get(u32::from(font_dict_index))?;
258 let private_dict_range = parse_font_dict(font_dict_data)?;
259 let private_dict_data = data.get(private_dict_range.clone())?;
260 let subroutines_offset = parse_private_dict(private_dict_data)?;
261
262 let start = private_dict_range.start.checked_add(subroutines_offset)?;
265 let subrs_data = data.get(start..)?;
266 let mut s = Stream::new(subrs_data);
267 parse_index::<u16>(&mut s)
268}
269
270struct CharStringParserContext<'a> {
271 metadata: &'a Table<'a>,
272 width_parsed: bool,
273 stems_len: u32,
274 has_endchar: bool,
275 has_seac: bool,
276 glyph_id: GlyphId, local_subrs: Option<Index<'a>>,
278}
279
280fn parse_char_string(
281 data: &[u8],
282 metadata: &Table,
283 glyph_id: GlyphId,
284 builder: &mut dyn OutlineBuilder,
285) -> Result<Rect, CFFError> {
286 let local_subrs = match metadata.kind {
287 FontKind::SID(ref sid) => Some(sid.local_subrs),
288 FontKind::CID(_) => None, };
290
291 let mut ctx = CharStringParserContext {
292 metadata,
293 width_parsed: false,
294 stems_len: 0,
295 has_endchar: false,
296 has_seac: false,
297 glyph_id,
298 local_subrs,
299 };
300
301 let mut inner_builder = Builder {
302 builder,
303 bbox: BBox::new(),
304 };
305
306 let stack = ArgumentsStack {
307 data: &mut [0.0; MAX_ARGUMENTS_STACK_LEN], len: 0,
309 max_len: MAX_ARGUMENTS_STACK_LEN,
310 };
311 let mut parser = CharStringParser {
312 stack,
313 builder: &mut inner_builder,
314 x: 0.0,
315 y: 0.0,
316 has_move_to: false,
317 is_first_move_to: true,
318 };
319 _parse_char_string(&mut ctx, data, 0, &mut parser)?;
320
321 if !ctx.has_endchar {
322 return Err(CFFError::MissingEndChar);
323 }
324
325 let bbox = parser.builder.bbox;
326
327 if bbox.is_default() {
329 return Err(CFFError::ZeroBBox);
330 }
331
332 bbox.to_rect().ok_or(CFFError::BboxOverflow)
333}
334
335
336fn _parse_char_string(
337 ctx: &mut CharStringParserContext,
338 char_string: &[u8],
339 depth: u8,
340 p: &mut CharStringParser,
341) -> Result<(), CFFError> {
342 let mut s = Stream::new(char_string);
343 while !s.at_end() {
344 let op = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?;
345 match op {
346 0 | 2 | 9 | 13 | 15 | 16 | 17 => {
347 return Err(CFFError::InvalidOperator);
349 }
350 operator::HORIZONTAL_STEM |
351 operator::VERTICAL_STEM |
352 operator::HORIZONTAL_STEM_HINT_MASK |
353 operator::VERTICAL_STEM_HINT_MASK => {
354 let len = if p.stack.len().is_odd() && !ctx.width_parsed {
361 ctx.width_parsed = true;
362 p.stack.len() - 1
363 } else {
364 p.stack.len()
365 };
366
367 ctx.stems_len += len as u32 >> 1;
368
369 p.stack.clear();
371 }
372 operator::VERTICAL_MOVE_TO => {
373 let mut i = 0;
374 if p.stack.len() == 2 && !ctx.width_parsed {
375 i += 1;
376 ctx.width_parsed = true;
377 }
378
379 p.parse_vertical_move_to(i)?;
380 }
381 operator::LINE_TO => {
382 p.parse_line_to()?;
383 }
384 operator::HORIZONTAL_LINE_TO => {
385 p.parse_horizontal_line_to()?;
386 }
387 operator::VERTICAL_LINE_TO => {
388 p.parse_vertical_line_to()?;
389 }
390 operator::CURVE_TO => {
391 p.parse_curve_to()?;
392 }
393 operator::CALL_LOCAL_SUBROUTINE => {
394 if p.stack.is_empty() {
395 return Err(CFFError::InvalidArgumentsStackLength);
396 }
397
398 if depth == STACK_LIMIT {
399 return Err(CFFError::NestingLimitReached);
400 }
401
402 if ctx.local_subrs.is_none() {
406 if let FontKind::CID(ref cid) = ctx.metadata.kind {
407 ctx.local_subrs = parse_cid_local_subrs(
408 ctx.metadata.table_data, ctx.glyph_id, cid
409 );
410 }
411 }
412
413 if let Some(local_subrs) = ctx.local_subrs {
414 let subroutine_bias = calc_subroutine_bias(local_subrs.len());
415 let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
416 let char_string = local_subrs.get(index).ok_or(CFFError::InvalidSubroutineIndex)?;
417 _parse_char_string(ctx, char_string, depth + 1, p)?;
418 } else {
419 return Err(CFFError::NoLocalSubroutines);
420 }
421
422 if ctx.has_endchar && !ctx.has_seac {
423 if !s.at_end() {
424 return Err(CFFError::DataAfterEndChar);
425 }
426
427 break;
428 }
429 }
430 operator::RETURN => {
431 break;
432 }
433 TWO_BYTE_OPERATOR_MARK => {
434 let op2 = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?;
436 match op2 {
437 operator::HFLEX => p.parse_hflex()?,
438 operator::FLEX => p.parse_flex()?,
439 operator::HFLEX1 => p.parse_hflex1()?,
440 operator::FLEX1 => p.parse_flex1()?,
441 _ => return Err(CFFError::UnsupportedOperator),
442 }
443 }
444 operator::ENDCHAR => {
445 if p.stack.len() == 4 || (!ctx.width_parsed && p.stack.len() == 5) {
446 let accent_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop())
448 .ok_or(CFFError::InvalidSeacCode)?;
449 let base_char = seac_code_to_glyph_id(&ctx.metadata.charset, p.stack.pop())
450 .ok_or(CFFError::InvalidSeacCode)?;
451 let dy = p.stack.pop();
452 let dx = p.stack.pop();
453
454 if !ctx.width_parsed && !p.stack.is_empty() {
455 p.stack.pop();
456 ctx.width_parsed = true;
457 }
458
459 ctx.has_seac = true;
460
461 if depth == STACK_LIMIT {
462 return Err(CFFError::NestingLimitReached);
463 }
464
465 let base_char_string = ctx.metadata.char_strings.get(u32::from(base_char.0))
466 .ok_or(CFFError::InvalidSeacCode)?;
467 _parse_char_string(ctx, base_char_string, depth + 1, p)?;
468 p.x = dx;
469 p.y = dy;
470
471 let accent_char_string = ctx.metadata.char_strings.get(u32::from(accent_char.0))
472 .ok_or(CFFError::InvalidSeacCode)?;
473 _parse_char_string(ctx, accent_char_string, depth + 1, p)?;
474 } else if p.stack.len() == 1 && !ctx.width_parsed {
475 p.stack.pop();
476 ctx.width_parsed = true;
477 }
478
479 if !p.is_first_move_to {
480 p.is_first_move_to = true;
481 p.builder.close();
482 }
483
484 if !s.at_end() {
485 return Err(CFFError::DataAfterEndChar);
486 }
487
488 ctx.has_endchar = true;
489
490 break;
491 }
492 operator::HINT_MASK | operator::COUNTER_MASK => {
493 let mut len = p.stack.len();
494
495 p.stack.clear();
497
498 if len.is_odd() && !ctx.width_parsed {
500 len -= 1;
501 ctx.width_parsed = true;
502 }
503
504 ctx.stems_len += len as u32 >> 1;
505
506 s.advance(usize::num_from((ctx.stems_len + 7) >> 3));
507 }
508 operator::MOVE_TO => {
509 let mut i = 0;
510 if p.stack.len() == 3 && !ctx.width_parsed {
511 i += 1;
512 ctx.width_parsed = true;
513 }
514
515 p.parse_move_to(i)?;
516 }
517 operator::HORIZONTAL_MOVE_TO => {
518 let mut i = 0;
519 if p.stack.len() == 2 && !ctx.width_parsed {
520 i += 1;
521 ctx.width_parsed = true;
522 }
523
524 p.parse_horizontal_move_to(i)?;
525 }
526 operator::CURVE_LINE => {
527 p.parse_curve_line()?;
528 }
529 operator::LINE_CURVE => {
530 p.parse_line_curve()?;
531 }
532 operator::VV_CURVE_TO => {
533 p.parse_vv_curve_to()?;
534 }
535 operator::HH_CURVE_TO => {
536 p.parse_hh_curve_to()?;
537 }
538 operator::SHORT_INT => {
539 let n = s.read::<i16>().ok_or(CFFError::ReadOutOfBounds)?;
540 p.stack.push(f32::from(n))?;
541 }
542 operator::CALL_GLOBAL_SUBROUTINE => {
543 if p.stack.is_empty() {
544 return Err(CFFError::InvalidArgumentsStackLength);
545 }
546
547 if depth == STACK_LIMIT {
548 return Err(CFFError::NestingLimitReached);
549 }
550
551 let subroutine_bias = calc_subroutine_bias(ctx.metadata.global_subrs.len());
552 let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
553 let char_string = ctx.metadata.global_subrs.get(index)
554 .ok_or(CFFError::InvalidSubroutineIndex)?;
555 _parse_char_string(ctx, char_string, depth + 1, p)?;
556
557 if ctx.has_endchar && !ctx.has_seac {
558 if !s.at_end() {
559 return Err(CFFError::DataAfterEndChar);
560 }
561
562 break;
563 }
564 }
565 operator::VH_CURVE_TO => {
566 p.parse_vh_curve_to()?;
567 }
568 operator::HV_CURVE_TO => {
569 p.parse_hv_curve_to()?;
570 }
571 32..=246 => {
572 p.parse_int1(op)?;
573 }
574 247..=250 => {
575 p.parse_int2(op, &mut s)?;
576 }
577 251..=254 => {
578 p.parse_int3(op, &mut s)?;
579 }
580 operator::FIXED_16_16 => {
581 p.parse_fixed(&mut s)?;
582 }
583 }
584 }
585
586 Ok(())
589}
590
591fn seac_code_to_glyph_id(charset: &Charset, n: f32) -> Option<GlyphId> {
592 let code = u8::try_num_from(n)?;
593
594 let sid = STANDARD_ENCODING[code as usize];
595 let sid = StringId(u16::from(sid));
596
597 match charset {
598 Charset::ISOAdobe => {
599 if code <= 228 { Some(GlyphId(sid.0)) } else { None }
601 }
602 Charset::Expert | Charset::ExpertSubset => None,
603 _ => charset.sid_to_gid(sid),
604 }
605}
606
607
608#[derive(Clone, Copy, Debug)]
609enum FDSelect<'a> {
610 Format0(LazyArray16<'a, u8>),
611 Format3(&'a [u8]), }
613
614impl Default for FDSelect<'_> {
615 fn default() -> Self {
616 FDSelect::Format0(LazyArray16::default())
617 }
618}
619
620impl FDSelect<'_> {
621 fn font_dict_index(&self, glyph_id: GlyphId) -> Option<u8> {
622 match self {
623 FDSelect::Format0(ref array) => array.get(glyph_id.0),
624 FDSelect::Format3(ref data) => {
625 let mut s = Stream::new(data);
626 let number_of_ranges = s.read::<u16>()?;
627 if number_of_ranges == 0 {
628 return None;
629 }
630
631 let number_of_ranges = number_of_ranges.checked_add(1)?;
635
636 let mut prev_first_glyph = s.read::<GlyphId>()?;
638 let mut prev_index = s.read::<u8>()?;
639 for _ in 1..number_of_ranges {
640 let curr_first_glyph = s.read::<GlyphId>()?;
641 if (prev_first_glyph..curr_first_glyph).contains(&glyph_id) {
642 return Some(prev_index);
643 } else {
644 prev_index = s.read::<u8>()?;
645 }
646
647 prev_first_glyph = curr_first_glyph;
648 }
649
650 None
651 }
652 }
653 }
654}
655
656fn parse_fd_select<'a>(number_of_glyphs: u16, s: &mut Stream<'a>) -> Option<FDSelect<'a>> {
657 let format = s.read::<u8>()?;
658 match format {
659 0 => Some(FDSelect::Format0(s.read_array16::<u8>(number_of_glyphs)?)),
660 3 => Some(FDSelect::Format3(s.tail()?)),
661 _ => None,
662 }
663}
664
665fn parse_sid_metadata(data: &[u8], top_dict: TopDict) -> Option<FontKind> {
666 let subroutines_offset = if let Some(range) = top_dict.private_dict_range.clone() {
667 parse_private_dict(data.get(range)?)
668 } else {
669 None
670 };
671
672 let mut metadata = SIDMetadata::default();
674
675 match (top_dict.private_dict_range, subroutines_offset) {
676 (Some(private_dict_range), Some(subroutines_offset)) => {
677 if let Some(start) = private_dict_range.start.checked_add(subroutines_offset) {
680 let data = data.get(start..data.len())?;
681 let mut s = Stream::new(data);
682 metadata.local_subrs = parse_index::<u16>(&mut s)?;
683 }
684 }
685 _ => {}
686 }
687
688 Some(FontKind::SID(metadata))
689}
690
691fn parse_cid_metadata(data: &[u8], top_dict: TopDict, number_of_glyphs: u16) -> Option<FontKind> {
692 let (charset_offset, fd_array_offset, fd_select_offset) =
693 match (top_dict.charset_offset, top_dict.fd_array_offset, top_dict.fd_select_offset) {
694 (Some(a), Some(b), Some(c)) => (a, b, c),
695 _ => return None, };
697
698 if charset_offset <= charset_id::EXPERT_SUBSET {
699 return None;
702 }
703
704 let mut metadata = CIDMetadata::default();
705
706 metadata.fd_array = {
707 let mut s = Stream::new_at(data, fd_array_offset)?;
708 parse_index::<u16>(&mut s)?
709 };
710
711 metadata.fd_select = {
712 let mut s = Stream::new_at(data, fd_select_offset)?;
713 parse_fd_select(number_of_glyphs, &mut s)?
714 };
715
716 Some(FontKind::CID(metadata))
717}
718
719
720#[derive(Clone, Copy)]
723pub struct Table<'a> {
724 table_data: &'a [u8],
727
728 #[allow(dead_code)] strings: Index<'a>,
729 global_subrs: Index<'a>,
730 charset: Charset<'a>,
731 char_strings: Index<'a>,
732 kind: FontKind<'a>,
733}
734
735impl<'a> Table<'a> {
736 pub fn parse(data: &'a [u8]) -> Option<Self> {
738 let mut s = Stream::new(data);
739
740 let major = s.read::<u8>()?;
742 s.skip::<u8>(); let header_size = s.read::<u8>()?;
744 s.skip::<u8>(); if major != 1 {
747 return None;
748 }
749
750 if header_size > 4 {
752 s.advance(usize::from(header_size) - 4);
753 }
754
755 skip_index::<u16>(&mut s)?;
757
758 let top_dict = parse_top_dict(&mut s)?;
759
760 if top_dict.char_strings_offset == 0 {
762 return None;
763 }
764
765 let strings = parse_index::<u16>(&mut s)?;
767
768 let global_subrs = parse_index::<u16>(&mut s)?;
770
771 let char_strings = {
772 let mut s = Stream::new_at(data, top_dict.char_strings_offset)?;
773 parse_index::<u16>(&mut s)?
774 };
775
776 if char_strings.len() == 0 {
777 return None;
778 }
779
780 let number_of_glyphs = u16::try_from(char_strings.len()).ok()?;
782
783 let charset = match top_dict.charset_offset {
784 Some(charset_id::ISO_ADOBE) => Charset::ISOAdobe,
785 Some(charset_id::EXPERT) => Charset::Expert,
786 Some(charset_id::EXPERT_SUBSET) => Charset::ExpertSubset,
787 Some(offset) => parse_charset(number_of_glyphs, &mut Stream::new_at(data, offset)?)?,
788 None => Charset::ISOAdobe, };
790
791 let kind = if top_dict.has_ros {
792 parse_cid_metadata(data, top_dict, number_of_glyphs)?
793 } else {
794 parse_sid_metadata(data, top_dict)?
795 };
796
797 Some(Self {
798 table_data: data,
799 strings,
800 global_subrs,
801 charset,
802 char_strings,
803 kind,
804 })
805 }
806
807 pub fn outline(
809 &self,
810 glyph_id: GlyphId,
811 builder: &mut dyn OutlineBuilder,
812 ) -> Result<Rect, CFFError> {
813 let data = self.char_strings.get(u32::from(glyph_id.0)).ok_or(CFFError::NoGlyph)?;
814 parse_char_string(data, self, glyph_id, builder)
815 }
816
817 #[cfg(feature = "glyph-names")]
819 pub fn glyph_name(&self, glyph_id: GlyphId) -> Option<&'a str> {
820 match self.kind {
821 FontKind::SID(_) => {
822 let sid = self.charset.gid_to_sid(glyph_id)?;
823 let sid = usize::from(sid.0);
824 match STANDARD_NAMES.get(sid) {
825 Some(name) => Some(name),
826 None => {
827 let idx = u32::try_from(sid - STANDARD_NAMES.len()).ok()?;
828 let name = self.strings.get(idx)?;
829 core::str::from_utf8(name).ok()
830 }
831 }
832 }
833 FontKind::CID(_) => None,
834 }
835 }
836}
837
838impl core::fmt::Debug for Table<'_> {
839 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
840 write!(f, "Table {{ ... }}")
841 }
842}