1use std::convert::{TryFrom, TryInto};
4use std::fmt::{self, Write};
5
6use rustc_hash::FxHashSet;
7
8pub use argstack::ArgumentsStack;
9
10use crate::binary::read::{ReadCtxt, ReadScope};
11use crate::binary::write::{WriteBinary, WriteBuffer, WriteContext};
12use crate::binary::{I16Be, U8};
13use crate::cff;
14use crate::cff::cff2::BlendOperand;
15use crate::error::{ParseError, WriteError};
16use crate::tables::variable_fonts::{ItemVariationStore, OwnedTuple};
17use crate::tables::Fixed;
18
19use super::{cff2, CFFError, CFFFont, CFFVariant, MaybeOwnedIndex, Operator};
20
21mod argstack;
22
23pub(crate) const STACK_LIMIT: u8 = 10;
27
28pub(crate) const TWO_BYTE_OPERATOR_MARK: u8 = 12;
29
30pub(crate) trait IsEven {
31 fn is_even(&self) -> bool;
32 fn is_odd(&self) -> bool;
33}
34
35pub(crate) trait TryNumFrom<T>: Sized {
37 fn try_num_from(_: T) -> Option<Self>;
39}
40
41pub(crate) type GlyphId = u16;
42
43pub(crate) struct UsedSubrs {
44 pub(crate) global_subr_used: FxHashSet<usize>,
45 pub(crate) local_subr_used: FxHashSet<usize>,
46}
47
48pub struct CharStringVisitorContext<'a, 'data> {
50 glyph_id: GlyphId, char_strings_index: &'a MaybeOwnedIndex<'data>,
52 local_subr_index: Option<&'a MaybeOwnedIndex<'data>>,
53 global_subr_index: &'a MaybeOwnedIndex<'data>,
54 variable: Option<VariableCharStringVisitorContext<'a, 'data>>,
56 width_parsed: bool,
57 stems_len: u32,
58 has_endchar: bool,
59 has_seac: bool,
60 seen_blend: bool,
61 vsindex: Option<u16>,
62 scalars: Option<Vec<Option<f32>>>,
63}
64
65#[derive(Copy, Clone)]
68pub struct VariableCharStringVisitorContext<'a, 'data> {
69 pub vstore: &'a ItemVariationStore<'data>,
70 pub instance: &'a OwnedTuple,
71}
72
73#[derive(Debug, Copy, Clone, Eq, PartialEq)]
75pub enum SubroutineIndex {
76 Local(usize),
77 Global(usize),
78}
79
80#[derive(Debug, Copy, Clone, Eq, PartialEq)]
82pub enum SeacChar {
83 Base,
84 Accent,
85}
86
87pub struct DebugVisitor;
145
146pub trait CharStringVisitor<T: fmt::Debug, E: std::error::Error> {
152 fn visit(&mut self, _op: VisitOp, _stack: &ArgumentsStack<'_, T>) -> Result<(), E> {
155 Ok(())
156 }
157
158 fn enter_subr(&mut self, _index: SubroutineIndex) -> Result<(), E> {
162 Ok(())
163 }
164
165 fn exit_subr(&mut self) -> Result<(), E> {
167 Ok(())
168 }
169
170 fn enter_seac(&mut self, _seac: SeacChar, _dx: T, _dy: T) -> Result<(), E> {
176 Ok(())
177 }
178
179 fn exit_seac(&mut self, _seac: SeacChar) -> Result<(), E> {
183 Ok(())
184 }
185
186 fn hint_data(&mut self, _op: VisitOp, _hints: &[u8]) -> Result<(), E> {
190 Ok(())
191 }
192}
193
194pub(crate) fn char_string_used_subrs<'a, 'data>(
195 font: CFFFont<'a, 'data>,
196 char_strings_index: &'a MaybeOwnedIndex<'data>,
197 global_subr_index: &'a MaybeOwnedIndex<'data>,
198 glyph_id: GlyphId,
199) -> Result<UsedSubrs, CFFError> {
200 let (local_subrs, max_len) = match font {
201 CFFFont::CFF(font) => match &font.data {
202 CFFVariant::CID(_) => (None, cff::MAX_OPERANDS), CFFVariant::Type1(type1) => (type1.local_subr_index.as_ref(), cff::MAX_OPERANDS),
204 },
205 CFFFont::CFF2(cff2) => (cff2.local_subr_index.as_ref(), cff2::MAX_OPERANDS),
206 };
207
208 let mut ctx = CharStringVisitorContext::new(
209 glyph_id,
210 char_strings_index,
211 local_subrs,
212 global_subr_index,
213 None,
216 );
217
218 let mut used_subrs = UsedSubrs {
219 global_subr_used: FxHashSet::default(),
220 local_subr_used: FxHashSet::default(),
221 };
222
223 let mut stack = ArgumentsStack {
224 data: &mut [0.0; cff2::MAX_OPERANDS],
227 len: 0,
228 max_len,
229 };
230 ctx.visit(font, &mut stack, &mut used_subrs)?;
231
232 if matches!(font, CFFFont::CFF(_)) && !ctx.has_endchar {
233 return Err(CFFError::MissingEndChar);
234 }
235
236 Ok(used_subrs)
237}
238
239pub(crate) fn convert_cff2_to_cff<'a, 'data>(
240 font: CFFFont<'a, 'data>,
241 char_strings_index: &'a MaybeOwnedIndex<'data>,
242 global_subr_index: &'a MaybeOwnedIndex<'data>,
243 glyph_id: GlyphId,
244 width: u16,
245 default_width: u16,
246 nominal_width: u16,
247) -> Result<Vec<u8>, CharStringConversionError> {
248 let (local_subrs, max_len) = match font {
249 CFFFont::CFF(font) => match &font.data {
250 CFFVariant::CID(_) => (None, cff::MAX_OPERANDS), CFFVariant::Type1(type1) => (type1.local_subr_index.as_ref(), cff::MAX_OPERANDS),
252 },
253 CFFFont::CFF2(cff2) => (cff2.local_subr_index.as_ref(), cff2::MAX_OPERANDS),
254 };
255
256 let mut ctx = CharStringVisitorContext::new(
257 glyph_id,
258 char_strings_index,
259 local_subrs,
260 global_subr_index,
261 None,
262 );
263
264 let char_string_width =
267 i16::try_from(i32::from(width) - i32::from(nominal_width)).map_err(ParseError::from)?;
268
269 let mut converter = CharStringConverter {
270 buffer: WriteBuffer::new(),
271 width: char_string_width,
272 width_written: width == default_width,
275 };
276
277 let mut stack = ArgumentsStack {
278 data: &mut [cff2::StackValue::Int(0); cff2::MAX_OPERANDS],
281 len: 0,
282 max_len,
283 };
284 ctx.visit(font, &mut stack, &mut converter)?;
285
286 converter.maybe_write_width()?;
296 U8::write(&mut converter.buffer, operator::ENDCHAR)?;
297
298 Ok(converter.buffer.into_inner())
299}
300
301struct CharStringConverter {
302 buffer: WriteBuffer,
303 width_written: bool,
304 width: i16,
305}
306
307impl CharStringConverter {
308 fn maybe_write_width(&mut self) -> Result<(), WriteError> {
309 if !self.width_written {
310 cff2::write_stack_value(cff2::StackValue::Int(self.width), &mut self.buffer)?;
311 self.width_written = true;
312 }
313 Ok(())
314 }
315}
316
317#[derive(Debug)]
318pub(crate) enum CharStringConversionError {
319 Write(WriteError),
320 Cff(CFFError),
321}
322
323impl From<CFFError> for CharStringConversionError {
324 fn from(err: CFFError) -> Self {
325 CharStringConversionError::Cff(err)
326 }
327}
328
329impl From<ParseError> for CharStringConversionError {
330 fn from(err: ParseError) -> Self {
331 CharStringConversionError::Cff(CFFError::ParseError(err))
332 }
333}
334
335impl From<WriteError> for CharStringConversionError {
336 fn from(err: WriteError) -> Self {
337 CharStringConversionError::Write(err)
338 }
339}
340
341impl fmt::Display for CharStringConversionError {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 match self {
344 CharStringConversionError::Write(err) => {
345 write!(f, "unable to convert charstring: {err}")
346 }
347 CharStringConversionError::Cff(err) => write!(f, "unable to convert charstring: {err}"),
348 }
349 }
350}
351
352impl std::error::Error for CharStringConversionError {}
353
354impl CharStringVisitor<cff2::StackValue, CharStringConversionError> for CharStringConverter {
355 fn visit(
356 &mut self,
357 op: VisitOp,
358 stack: &ArgumentsStack<'_, cff2::StackValue>,
359 ) -> Result<(), CharStringConversionError> {
360 match op {
372 VisitOp::HorizontalStem
373 | VisitOp::HorizontalStemHintMask
374 | VisitOp::VerticalStem
375 | VisitOp::VerticalStemHintMask
376 | VisitOp::VerticalMoveTo
377 | VisitOp::HintMask
378 | VisitOp::CounterMask
379 | VisitOp::MoveTo
380 | VisitOp::HorizontalMoveTo => {
381 self.maybe_write_width()?;
382 cff2::write_stack(&mut self.buffer, stack)?;
383 Ok(U8::write(&mut self.buffer, op)?)
384 }
385
386 VisitOp::Return | VisitOp::Endchar => {
387 Err(CFFError::InvalidOperator.into())
389 }
390
391 VisitOp::LineTo
392 | VisitOp::HorizontalLineTo
393 | VisitOp::VerticalLineTo
394 | VisitOp::CurveTo
395 | VisitOp::CurveLine
396 | VisitOp::LineCurve
397 | VisitOp::VvCurveTo
398 | VisitOp::HhCurveTo
399 | VisitOp::VhCurveTo
400 | VisitOp::HvCurveTo => {
401 if !self.width_written {
402 return Err(CFFError::MissingMoveTo.into());
403 }
404 cff2::write_stack(&mut self.buffer, stack)?;
405 Ok(U8::write(&mut self.buffer, op)?)
406 }
407
408 VisitOp::Hflex | VisitOp::Flex | VisitOp::Hflex1 | VisitOp::Flex1 => {
409 cff2::write_stack(&mut self.buffer, stack)?;
410 U8::write(&mut self.buffer, TWO_BYTE_OPERATOR_MARK)?;
411 Ok(U8::write(&mut self.buffer, op)?)
412 }
413 VisitOp::VsIndex | VisitOp::Blend => {
414 Err(CFFError::InvalidOperator.into())
417 }
418 }
419 }
420
421 fn enter_subr(&mut self, index: SubroutineIndex) -> Result<(), CharStringConversionError> {
422 match index {
424 SubroutineIndex::Local(index) => {
425 cff2::write_stack_value(
426 cff2::StackValue::Int(index.try_into().map_err(ParseError::from)?),
427 &mut self.buffer,
428 )?;
429 Ok(U8::write(
430 &mut self.buffer,
431 operator::CALL_LOCAL_SUBROUTINE,
432 )?)
433 }
434 SubroutineIndex::Global(index) => {
435 cff2::write_stack_value(
436 cff2::StackValue::Int(index.try_into().map_err(ParseError::from)?),
437 &mut self.buffer,
438 )?;
439 Ok(U8::write(
440 &mut self.buffer,
441 operator::CALL_GLOBAL_SUBROUTINE,
442 )?)
443 }
444 }
445 }
446
447 fn exit_subr(&mut self) -> Result<(), CharStringConversionError> {
448 Ok(U8::write(&mut self.buffer, operator::RETURN)?)
449 }
450
451 fn hint_data(&mut self, _op: VisitOp, hints: &[u8]) -> Result<(), CharStringConversionError> {
452 Ok(self.buffer.write_bytes(hints)?)
453 }
454}
455
456impl CharStringVisitor<f32, CFFError> for UsedSubrs {
457 fn enter_subr(&mut self, index: SubroutineIndex) -> Result<(), CFFError> {
458 match index {
459 SubroutineIndex::Local(index) => self.local_subr_used.insert(index),
460 SubroutineIndex::Global(index) => self.global_subr_used.insert(index),
461 };
462
463 Ok(())
464 }
465}
466
467impl CharStringVisitor<f32, CFFError> for DebugVisitor {
468 fn visit(&mut self, op: VisitOp, stack: &ArgumentsStack<'_, f32>) -> Result<(), CFFError> {
469 let mut operands = String::new();
470 stack.all().iter().enumerate().for_each(|(i, operand)| {
471 if i > 0 {
472 operands.push(' ')
473 }
474 write!(operands, "{}", operand).unwrap()
475 });
476 println!("{op} {operands}");
477 Ok(())
478 }
479
480 fn enter_subr(&mut self, index: SubroutineIndex) -> Result<(), CFFError> {
481 match index {
482 SubroutineIndex::Local(index) => println!("callsubr {index}"),
483 SubroutineIndex::Global(index) => println!("callgsubr {index}"),
484 }
485 Ok(())
486 }
487}
488
489#[derive(Copy, Clone)]
490pub enum VisitOp {
491 HorizontalStem,
492 VerticalStem,
493 VerticalMoveTo,
494 LineTo,
495 HorizontalLineTo,
496 VerticalLineTo,
497 CurveTo,
498 Return,
499 Endchar,
500 VsIndex,
501 Blend,
502 HorizontalStemHintMask,
503 HintMask,
504 CounterMask,
505 MoveTo,
506 HorizontalMoveTo,
507 VerticalStemHintMask,
508 CurveLine,
509 LineCurve,
510 VvCurveTo,
511 HhCurveTo,
512 VhCurveTo,
513 HvCurveTo,
514 Hflex,
515 Flex,
516 Hflex1,
517 Flex1,
518}
519
520impl TryFrom<u8> for VisitOp {
521 type Error = ();
522
523 fn try_from(value: u8) -> Result<Self, Self::Error> {
524 match value {
525 operator::HORIZONTAL_STEM => Ok(VisitOp::HorizontalStem),
526 operator::VERTICAL_STEM => Ok(VisitOp::VerticalStem),
527 operator::VERTICAL_MOVE_TO => Ok(VisitOp::VerticalMoveTo),
528 operator::LINE_TO => Ok(VisitOp::LineTo),
529 operator::HORIZONTAL_LINE_TO => Ok(VisitOp::HorizontalLineTo),
530 operator::VERTICAL_LINE_TO => Ok(VisitOp::VerticalLineTo),
531 operator::CURVE_TO => Ok(VisitOp::CurveTo),
532 operator::RETURN => Ok(VisitOp::Return),
534 operator::ENDCHAR => Ok(VisitOp::Endchar),
535 operator::VS_INDEX => Ok(VisitOp::VsIndex),
536 operator::BLEND => Ok(VisitOp::Blend),
537 operator::HORIZONTAL_STEM_HINT_MASK => Ok(VisitOp::HorizontalStemHintMask),
538 operator::HINT_MASK => Ok(VisitOp::HintMask),
539 operator::COUNTER_MASK => Ok(VisitOp::CounterMask),
540 operator::MOVE_TO => Ok(VisitOp::MoveTo),
541 operator::HORIZONTAL_MOVE_TO => Ok(VisitOp::HorizontalMoveTo),
542 operator::VERTICAL_STEM_HINT_MASK => Ok(VisitOp::VerticalStemHintMask),
543 operator::CURVE_LINE => Ok(VisitOp::CurveLine),
544 operator::LINE_CURVE => Ok(VisitOp::LineCurve),
545 operator::VV_CURVE_TO => Ok(VisitOp::VvCurveTo),
546 operator::HH_CURVE_TO => Ok(VisitOp::HhCurveTo),
547 operator::VH_CURVE_TO => Ok(VisitOp::VhCurveTo),
550 operator::HV_CURVE_TO => Ok(VisitOp::HvCurveTo),
551 operator::HFLEX => Ok(VisitOp::Hflex),
553 operator::FLEX => Ok(VisitOp::Flex),
554 operator::HFLEX1 => Ok(VisitOp::Hflex1),
555 operator::FLEX1 => Ok(VisitOp::Flex1),
556 _ => Err(()),
558 }
559 }
560}
561
562impl From<VisitOp> for u8 {
563 fn from(op: VisitOp) -> Self {
564 match op {
565 VisitOp::HorizontalStem => operator::HORIZONTAL_STEM,
566 VisitOp::VerticalStem => operator::VERTICAL_STEM,
567 VisitOp::VerticalMoveTo => operator::VERTICAL_MOVE_TO,
568 VisitOp::LineTo => operator::LINE_TO,
569 VisitOp::HorizontalLineTo => operator::HORIZONTAL_LINE_TO,
570 VisitOp::VerticalLineTo => operator::VERTICAL_LINE_TO,
571 VisitOp::CurveTo => operator::CURVE_TO,
572 VisitOp::Return => operator::RETURN,
573 VisitOp::Endchar => operator::ENDCHAR,
574 VisitOp::VsIndex => operator::VS_INDEX,
575 VisitOp::Blend => operator::BLEND,
576 VisitOp::HorizontalStemHintMask => operator::HORIZONTAL_STEM_HINT_MASK,
577 VisitOp::HintMask => operator::HINT_MASK,
578 VisitOp::CounterMask => operator::COUNTER_MASK,
579 VisitOp::MoveTo => operator::MOVE_TO,
580 VisitOp::HorizontalMoveTo => operator::HORIZONTAL_MOVE_TO,
581 VisitOp::VerticalStemHintMask => operator::VERTICAL_STEM_HINT_MASK,
582 VisitOp::CurveLine => operator::CURVE_LINE,
583 VisitOp::LineCurve => operator::LINE_CURVE,
584 VisitOp::VvCurveTo => operator::VV_CURVE_TO,
585 VisitOp::HhCurveTo => operator::HH_CURVE_TO,
586 VisitOp::VhCurveTo => operator::VH_CURVE_TO,
587 VisitOp::HvCurveTo => operator::HV_CURVE_TO,
588 VisitOp::Hflex => operator::HFLEX,
590 VisitOp::Flex => operator::FLEX,
591 VisitOp::Hflex1 => operator::HFLEX1,
592 VisitOp::Flex1 => operator::FLEX1,
593 }
594 }
595}
596
597impl fmt::Display for VisitOp {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 match self {
600 VisitOp::HorizontalStem => f.write_str("hstem"),
601 VisitOp::VerticalStem => f.write_str("vstem"),
602 VisitOp::VerticalMoveTo => f.write_str("vmoveto"),
603 VisitOp::LineTo => f.write_str("rlineto"),
604 VisitOp::HorizontalLineTo => f.write_str("hlineto"),
605 VisitOp::VerticalLineTo => f.write_str("vlineto"),
606 VisitOp::CurveTo => f.write_str("rrcurveto"),
607 VisitOp::Return => f.write_str("return"),
608 VisitOp::Endchar => f.write_str("endchar"),
609 VisitOp::VsIndex => f.write_str("vsindex"),
610 VisitOp::Blend => f.write_str("blend"),
611 VisitOp::HorizontalStemHintMask => f.write_str("hstemhm"),
612 VisitOp::HintMask => f.write_str("hintmask"),
613 VisitOp::CounterMask => f.write_str("cntrmask"),
614 VisitOp::MoveTo => f.write_str("rmoveto"),
615 VisitOp::HorizontalMoveTo => f.write_str("hmoveto"),
616 VisitOp::VerticalStemHintMask => f.write_str("vstemhm"),
617 VisitOp::CurveLine => f.write_str("rcurveline"),
618 VisitOp::LineCurve => f.write_str("rlinecurve"),
619 VisitOp::VvCurveTo => f.write_str("vvcurveto"),
620 VisitOp::HhCurveTo => f.write_str("hhcurveto"),
621 VisitOp::VhCurveTo => f.write_str("vhcurveto"),
622 VisitOp::HvCurveTo => f.write_str("hvcurveto"),
623 VisitOp::Hflex => f.write_str("hflex"),
624 VisitOp::Flex => f.write_str("flex"),
625 VisitOp::Hflex1 => f.write_str("hflex1"),
626 VisitOp::Flex1 => f.write_str("flex1"),
627 }
628 }
629}
630
631impl<'a, 'data> CharStringVisitorContext<'a, 'data> {
632 pub fn new(
636 glyph_id: GlyphId,
637 char_strings_index: &'a MaybeOwnedIndex<'data>,
638 local_subr_index: Option<&'a MaybeOwnedIndex<'data>>,
639 global_subr_index: &'a MaybeOwnedIndex<'data>,
640 variable: Option<VariableCharStringVisitorContext<'a, 'data>>,
641 ) -> CharStringVisitorContext<'a, 'data> {
642 CharStringVisitorContext {
643 glyph_id,
644 char_strings_index,
645 local_subr_index,
646 global_subr_index,
647 variable,
648 width_parsed: false,
649 stems_len: 0,
650 has_endchar: false,
651 has_seac: false,
652 seen_blend: false,
653 vsindex: None,
654 scalars: None,
655 }
656 }
657
658 pub fn visit<S, V, E>(
660 &mut self,
661 font: CFFFont<'a, 'data>,
662 stack: &mut ArgumentsStack<'_, S>,
663 visitor: &mut V,
664 ) -> Result<(), E>
665 where
666 V: CharStringVisitor<S, E>,
667 S: BlendOperand,
668 E: std::error::Error + From<CFFError> + From<ParseError>,
669 {
670 let char_string = self
671 .char_strings_index
672 .read_object(usize::from(self.glyph_id))
673 .ok_or(CFFError::ParseError(ParseError::BadIndex))?;
674 self.visit_impl(font, char_string, 0, stack, visitor)
675 }
676
677 fn visit_impl<S, V, E>(
678 &mut self,
679 font: CFFFont<'a, 'data>,
680 char_string: &[u8],
681 depth: u8,
682 stack: &mut ArgumentsStack<'_, S>,
683 visitor: &mut V,
684 ) -> Result<(), E>
685 where
686 V: CharStringVisitor<S, E>,
687 S: BlendOperand,
688 E: std::error::Error + From<CFFError> + From<ParseError>,
689 {
690 let mut s = ReadScope::new(char_string).ctxt();
691 while s.bytes_available() {
692 let op = s.read::<U8>()?;
693 match op {
694 0 | 2 | 9 | 13 | 17 => {
695 return Err(CFFError::InvalidOperator.into());
697 }
698 operator::HORIZONTAL_STEM
699 | operator::VERTICAL_STEM
700 | operator::HORIZONTAL_STEM_HINT_MASK
701 | operator::VERTICAL_STEM_HINT_MASK => {
702 let len = if stack.len().is_odd() && !self.width_parsed {
704 self.width_parsed = true;
705 stack.len() - 1
706 } else {
707 stack.len()
708 };
709
710 self.stems_len += len as u32 >> 1;
711
712 visitor.visit(op.try_into().unwrap(), stack)?;
714 stack.clear();
715 }
716 operator::VERTICAL_MOVE_TO => {
717 let offset = self.handle_width(stack.len() == 2 && !self.width_parsed);
718 stack.offset(offset, |stack| visitor.visit(op.try_into().unwrap(), stack))?;
719 stack.clear();
720 }
721 operator::LINE_TO
722 | operator::HORIZONTAL_LINE_TO
723 | operator::VERTICAL_LINE_TO
724 | operator::CURVE_TO => {
725 visitor.visit(op.try_into().unwrap(), stack)?;
726 stack.clear();
727 }
728 operator::CALL_LOCAL_SUBROUTINE => {
729 if stack.is_empty() {
730 return Err(CFFError::InvalidArgumentsStackLength.into());
731 }
732
733 if depth == STACK_LIMIT {
734 return Err(CFFError::NestingLimitReached.into());
735 }
736
737 if self.local_subr_index.is_none() {
741 if let CFFFont::CFF(super::Font {
743 data: CFFVariant::CID(ref cid),
744 ..
745 }) = font
746 {
747 self.local_subr_index = cid
749 .fd_select
750 .font_dict_index(self.glyph_id)
751 .and_then(|font_dict_index| {
752 match cid.local_subr_indices.get(usize::from(font_dict_index)) {
753 Some(Some(index)) => Some(index),
754 _ => None,
755 }
756 });
757 }
758 }
759
760 if let Some(local_subrs) = self.local_subr_index {
761 let subroutine_bias = calc_subroutine_bias(local_subrs.len());
762 let index = conv_subroutine_index(stack.pop(), subroutine_bias)?;
763 let char_string = local_subrs
764 .read_object(index)
765 .ok_or(CFFError::InvalidSubroutineIndex)?;
766 visitor.enter_subr(SubroutineIndex::Local(index))?;
767 self.visit_impl(font, char_string, depth + 1, stack, visitor)?;
768 visitor.exit_subr()?;
769 } else {
770 return Err(CFFError::NoLocalSubroutines.into());
771 }
772
773 if self.has_endchar && !self.has_seac {
774 if s.bytes_available() {
775 return Err(CFFError::DataAfterEndChar.into());
776 }
777
778 break;
779 }
780 }
781 operator::RETURN => {
782 match font {
783 CFFFont::CFF(_) => {
784 visitor.visit(op.try_into().unwrap(), stack)?;
785 break;
786 }
787 CFFFont::CFF2(_) => {
788 return Err(CFFError::InvalidOperator.into());
790 }
791 }
792 }
793 TWO_BYTE_OPERATOR_MARK => {
794 let op2 = s.read::<U8>()?;
796 match op2 {
797 operator::HFLEX | operator::FLEX | operator::HFLEX1 | operator::FLEX1 => {
798 visitor.visit(op2.try_into().unwrap(), stack)?;
799 stack.clear()
800 }
801 _ => return Err(CFFError::UnsupportedOperator.into()),
802 }
803 }
804 operator::ENDCHAR => {
805 match font {
806 CFFFont::CFF(cff) => {
807 if stack.len() == 4 || (!self.width_parsed && stack.len() == 5) {
808 let accent_char = stack
810 .pop()
811 .try_as_u8()
812 .and_then(|code| cff.seac_code_to_glyph_id(code))
813 .ok_or(CFFError::InvalidSeacCode)?;
814 let base_char = stack
815 .pop()
816 .try_as_u8()
817 .and_then(|code| cff.seac_code_to_glyph_id(code))
818 .ok_or(CFFError::InvalidSeacCode)?;
819 let dy = stack.pop();
820 let dx = stack.pop();
821
822 if !self.width_parsed {
823 stack.pop();
824 self.width_parsed = true;
825 }
826
827 self.has_seac = true;
828
829 let base_char_string = self
830 .char_strings_index
831 .read_object(usize::from(base_char))
832 .ok_or(CFFError::InvalidSeacCode)?;
833 visitor.enter_seac(SeacChar::Base, dx, dy)?;
834 self.visit_impl(font, base_char_string, depth + 1, stack, visitor)?;
835 visitor.exit_seac(SeacChar::Base)?;
836
837 let accent_char_string = self
838 .char_strings_index
839 .read_object(usize::from(accent_char))
840 .ok_or(CFFError::InvalidSeacCode)?;
841 visitor.enter_seac(SeacChar::Accent, dx, dy)?;
842 self.visit_impl(
843 font,
844 accent_char_string,
845 depth + 1,
846 stack,
847 visitor,
848 )?;
849 visitor.exit_seac(SeacChar::Accent)?;
850 } else if stack.len() == 1 && !self.width_parsed {
851 stack.pop();
852 self.width_parsed = true;
853 }
854
855 if s.bytes_available() {
856 return Err(CFFError::DataAfterEndChar.into());
857 }
858
859 self.has_endchar = true;
860 visitor.visit(op.try_into().unwrap(), stack)?;
861 break;
862 }
863 CFFFont::CFF2(_) => {
864 return Err(CFFError::InvalidOperator.into());
866 }
867 }
868 }
869 operator::VS_INDEX => {
870 match font {
871 CFFFont::CFF(_) => {
872 return Err(CFFError::InvalidOperator.into());
874 }
875 CFFFont::CFF2(_) => {
876 if self.vsindex.is_some() {
879 return Err(CFFError::DuplicateVsIndex.into());
880 } else if self.seen_blend {
881 return Err(CFFError::VsIndexAfterBlend.into());
882 } else {
883 if stack.len() != 1 {
884 return Err(CFFError::InvalidArgumentsStackLength.into());
885 }
886 visitor.visit(op.try_into().unwrap(), stack)?;
887 let item_variation_data_index = stack
888 .pop()
889 .try_as_u16()
890 .ok_or(CFFError::InvalidArgumentsStackLength)?;
891 self.vsindex = Some(item_variation_data_index);
892 }
893 }
894 }
895 }
896 operator::BLEND => {
897 match font {
898 CFFFont::CFF(_) => {
899 return Err(CFFError::InvalidOperator.into());
901 }
902 CFFFont::CFF2(font) => {
903 let Some(var) = self.variable else {
904 return Err(CFFError::MissingVariationStore.into());
905 };
906
907 if stack.len() > 0 {
908 visitor.visit(op.try_into().unwrap(), stack)?;
909
910 let scalars = match &self.scalars {
912 Some(scalars) => scalars,
913 None => {
914 let vs_index =
915 self.vsindex.map(Ok).unwrap_or_else(|| {
916 font.private_dict
917 .get_i32(Operator::VSIndex)
918 .ok_or(ParseError::BadValue)?
919 .and_then(|val| {
920 u16::try_from(val).map_err(ParseError::from)
921 })
922 })?;
923
924 self.scalars = Some(cff2::scalars(
925 vs_index,
926 var.vstore,
927 var.instance,
928 )?);
929 &self.scalars.as_ref().unwrap()
930 }
931 };
932
933 cff2::blend(&scalars, stack)?;
934 } else {
935 return Err(CFFError::InvalidArgumentsStackLength.into());
936 }
937 }
938 }
939 }
940 operator::HINT_MASK | operator::COUNTER_MASK => {
941 let mut len = stack.len();
942 let visit_op = op.try_into().unwrap();
943 visitor.visit(visit_op, stack)?;
944 stack.clear();
945
946 if len.is_odd() && !self.width_parsed {
948 len -= 1;
949 self.width_parsed = true;
950 }
951
952 self.stems_len += len as u32 >> 1;
953
954 let hints = s
956 .read_slice(
957 usize::try_from((self.stems_len + 7) >> 3)
958 .map_err(|_| ParseError::BadValue)?,
959 )
960 .map_err(|_| ParseError::BadOffset)?;
961 visitor.hint_data(visit_op, hints)?;
962 }
963 operator::MOVE_TO => {
964 let offset = self.handle_width(stack.len() == 3 && !self.width_parsed);
965 stack.offset(offset, |stack| visitor.visit(op.try_into().unwrap(), stack))?;
966 stack.clear();
967 }
968 operator::HORIZONTAL_MOVE_TO => {
969 let offset = self.handle_width(stack.len() == 2 && !self.width_parsed);
970 stack.offset(offset, |stack| visitor.visit(op.try_into().unwrap(), stack))?;
971 stack.clear();
972 }
973 operator::CURVE_LINE
974 | operator::LINE_CURVE
975 | operator::VV_CURVE_TO
976 | operator::HH_CURVE_TO
977 | operator::VH_CURVE_TO
978 | operator::HV_CURVE_TO => {
979 visitor.visit(op.try_into().unwrap(), stack)?;
980 stack.clear();
981 }
982 operator::SHORT_INT => {
983 let n = s.read::<I16Be>()?;
984 stack.push(S::from(n))?;
985 }
986 operator::CALL_GLOBAL_SUBROUTINE => {
987 if stack.is_empty() {
988 return Err(CFFError::InvalidArgumentsStackLength.into());
989 }
990
991 if depth == STACK_LIMIT {
992 return Err(CFFError::NestingLimitReached.into());
993 }
994
995 let subroutine_bias = calc_subroutine_bias(self.global_subr_index.len());
996 let index = conv_subroutine_index(stack.pop(), subroutine_bias)?;
997 let char_string = self
998 .global_subr_index
999 .read_object(index)
1000 .ok_or(CFFError::InvalidSubroutineIndex)?;
1001 visitor.enter_subr(SubroutineIndex::Global(index))?;
1002 self.visit_impl(font, char_string, depth + 1, stack, visitor)?;
1003 visitor.exit_subr()?;
1004
1005 if self.has_endchar && !self.has_seac {
1006 if s.bytes_available() {
1007 return Err(CFFError::DataAfterEndChar.into());
1008 }
1009
1010 break;
1011 }
1012 }
1013 32..=246 => {
1014 stack.push(parse_int1(op)?)?;
1015 }
1016 247..=250 => {
1017 stack.push(parse_int2(op, &mut s)?)?;
1018 }
1019 251..=254 => {
1020 stack.push(parse_int3(op, &mut s)?)?;
1021 }
1022 operator::FIXED_16_16 => {
1023 stack.push(parse_fixed(&mut s)?)?;
1024 }
1025 }
1026 }
1027
1028 Ok(())
1029 }
1030
1031 fn handle_width(&mut self, cond: bool) -> usize {
1032 if cond {
1033 self.width_parsed = true;
1034 1
1035 } else {
1036 0
1037 }
1038 }
1039}
1040
1041fn parse_int1<S: BlendOperand>(op: u8) -> Result<S, CFFError> {
1043 let n = i16::from(op) - 139;
1044 Ok(S::from(n))
1045}
1046
1047fn parse_int2<S: BlendOperand>(op: u8, s: &mut ReadCtxt<'_>) -> Result<S, CFFError> {
1048 let b1 = s.read::<U8>()?;
1049 let n = (i16::from(op) - 247) * 256 + i16::from(b1) + 108;
1050 debug_assert!((108..=1131).contains(&n));
1051 Ok(S::from(n))
1052}
1053
1054fn parse_int3<S: BlendOperand>(op: u8, s: &mut ReadCtxt<'_>) -> Result<S, CFFError> {
1055 let b1 = s.read::<U8>()?;
1056 let n = -(i16::from(op) - 251) * 256 - i16::from(b1) - 108;
1057 debug_assert!((-1131..=-108).contains(&n));
1058 Ok(S::from(n))
1059}
1060
1061fn parse_fixed<S: BlendOperand>(s: &mut ReadCtxt<'_>) -> Result<S, CFFError> {
1062 let n = s.read::<Fixed>()?;
1063 Ok(S::from(n))
1064}
1065
1066pub(crate) fn conv_subroutine_index<S: BlendOperand>(
1068 index: S,
1069 bias: u16,
1070) -> Result<usize, CFFError> {
1071 let index = index.try_as_i32().ok_or(CFFError::InvalidSubroutineIndex)?;
1072 conv_subroutine_index_impl(index, bias).ok_or(CFFError::InvalidSubroutineIndex)
1073}
1074
1075pub(crate) fn conv_subroutine_index_impl(index: i32, bias: u16) -> Option<usize> {
1076 let bias = i32::from(bias);
1077
1078 let index = index.checked_add(bias)?;
1079 usize::try_from(index).ok()
1080}
1081
1082pub(crate) fn calc_subroutine_bias(len: usize) -> u16 {
1084 if len < 1240 {
1085 107
1086 } else if len < 33900 {
1087 1131
1088 } else {
1089 32768
1090 }
1091}
1092
1093impl IsEven for usize {
1094 fn is_even(&self) -> bool {
1095 (*self) & 1 == 0
1096 }
1097
1098 fn is_odd(&self) -> bool {
1099 !self.is_even()
1100 }
1101}
1102
1103impl TryNumFrom<f32> for u8 {
1104 fn try_num_from(v: f32) -> Option<Self> {
1105 i32::try_num_from(v).and_then(|v| u8::try_from(v).ok())
1106 }
1107}
1108
1109impl TryNumFrom<f32> for i16 {
1110 fn try_num_from(v: f32) -> Option<Self> {
1111 i32::try_num_from(v).and_then(|v| i16::try_from(v).ok())
1112 }
1113}
1114
1115impl TryNumFrom<f32> for u16 {
1116 fn try_num_from(v: f32) -> Option<Self> {
1117 i32::try_num_from(v).and_then(|v| u16::try_from(v).ok())
1118 }
1119}
1120
1121impl TryNumFrom<f32> for i32 {
1122 fn try_num_from(v: f32) -> Option<Self> {
1123 const MIN: f32 = core::i32::MIN as f32;
1131 const MAX_P1: f32 = core::i32::MAX as f32;
1134 if v >= MIN && v < MAX_P1 {
1135 Some(v as i32)
1136 } else {
1137 None
1138 }
1139 }
1140}
1141
1142impl From<ParseError> for CFFError {
1143 fn from(error: ParseError) -> CFFError {
1144 CFFError::ParseError(error)
1145 }
1146}
1147
1148impl fmt::Display for CFFError {
1149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1150 match self {
1151 CFFError::ParseError(parse_error) => {
1152 write!(f, "parse error: ")?;
1153 parse_error.fmt(f)
1154 }
1155 CFFError::InvalidOperator => write!(f, "an invalid operator occurred"),
1156 CFFError::UnsupportedOperator => write!(f, "an unsupported operator occurred"),
1157 CFFError::MissingEndChar => write!(f, "the 'endchar' operator is missing"),
1158 CFFError::DataAfterEndChar => write!(f, "unused data left after 'endchar' operator"),
1159 CFFError::NestingLimitReached => write!(f, "subroutines nesting limit reached"),
1160 CFFError::ArgumentsStackLimitReached => write!(f, "arguments stack limit reached"),
1161 CFFError::InvalidArgumentsStackLength => {
1162 write!(f, "an invalid amount of items are in an arguments stack")
1163 }
1164 CFFError::BboxOverflow => write!(f, "outline's bounding box is too large"),
1165 CFFError::MissingMoveTo => write!(f, "missing moveto operator"),
1166 CFFError::DuplicateVsIndex => write!(f, "duplicate vsindex operator"),
1167 CFFError::InvalidSubroutineIndex => write!(f, "an invalid subroutine index"),
1168 CFFError::NoLocalSubroutines => write!(f, "no local subroutines"),
1169 CFFError::InvalidSeacCode => write!(f, "invalid seac code"),
1170 CFFError::InvalidOperand => write!(f, "operand was out of range or invalid"),
1171 CFFError::InvalidFontIndex => write!(f, "invalid font index"),
1172 CFFError::VsIndexAfterBlend => write!(f, "vsindex operator encountered after blend"),
1173 CFFError::MissingVariationStore => write!(f, "missing variation store"),
1174 }
1175 }
1176}
1177
1178impl std::error::Error for CFFError {}
1179
1180pub(crate) mod operator {
1182 pub const HORIZONTAL_STEM: u8 = 1;
1183 pub const VERTICAL_STEM: u8 = 3;
1184 pub const VERTICAL_MOVE_TO: u8 = 4;
1185 pub const LINE_TO: u8 = 5;
1186 pub const HORIZONTAL_LINE_TO: u8 = 6;
1187 pub const VERTICAL_LINE_TO: u8 = 7;
1188 pub const CURVE_TO: u8 = 8;
1189 pub const CALL_LOCAL_SUBROUTINE: u8 = 10;
1190 pub const RETURN: u8 = 11;
1191 pub const ENDCHAR: u8 = 14;
1192 pub const VS_INDEX: u8 = 15; pub const BLEND: u8 = 16; pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18;
1195 pub const HINT_MASK: u8 = 19;
1196 pub const COUNTER_MASK: u8 = 20;
1197 pub const MOVE_TO: u8 = 21;
1198 pub const HORIZONTAL_MOVE_TO: u8 = 22;
1199 pub const VERTICAL_STEM_HINT_MASK: u8 = 23;
1200 pub const CURVE_LINE: u8 = 24;
1201 pub const LINE_CURVE: u8 = 25;
1202 pub const VV_CURVE_TO: u8 = 26;
1203 pub const HH_CURVE_TO: u8 = 27;
1204 pub const SHORT_INT: u8 = 28;
1205 pub const CALL_GLOBAL_SUBROUTINE: u8 = 29;
1206 pub const VH_CURVE_TO: u8 = 30;
1207 pub const HV_CURVE_TO: u8 = 31;
1208 pub const HFLEX: u8 = 34;
1209 pub const FLEX: u8 = 35;
1210 pub const HFLEX1: u8 = 36;
1211 pub const FLEX1: u8 = 37;
1212 pub const FIXED_16_16: u8 = 255;
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use crate::cff::cff2::{self, CFF2};
1218 use crate::error::ReadWriteError;
1219 use crate::tables::variable_fonts::avar::AvarTable;
1220 use crate::tables::variable_fonts::fvar::FvarTable;
1221 use crate::tables::{OpenTypeData, OpenTypeFont};
1222 use crate::tag;
1223 use crate::tests::read_fixture;
1224
1225 use super::*;
1226
1227 struct TraverseCharString;
1228
1229 impl CharStringVisitor<f32, CFFError> for TraverseCharString {}
1230
1231 #[test]
1232 fn traverse_cff2_charstring() -> Result<(), ReadWriteError> {
1233 let buffer = read_fixture("tests/fonts/opentype/cff2/SourceSansVariable-Roman.abc.otf");
1234 let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
1235
1236 let offset_table = match otf.data {
1237 OpenTypeData::Single(ttf) => ttf,
1238 OpenTypeData::Collection(_) => unreachable!(),
1239 };
1240
1241 let cff_table_data = offset_table.read_table(&otf.scope, tag::CFF2)?.unwrap();
1242 let cff = cff_table_data
1243 .read::<CFF2<'_>>()
1244 .expect("error parsing CFF2 table");
1245 let fvar_data = offset_table.read_table(&otf.scope, tag::FVAR)?.unwrap();
1246 let fvar = fvar_data.read::<FvarTable<'_>>()?;
1247 let avar_data = offset_table.read_table(&otf.scope, tag::AVAR)?;
1248 let avar = avar_data
1249 .as_ref()
1250 .map(|avar_data| avar_data.read::<AvarTable<'_>>())
1251 .transpose()?;
1252
1253 let glyph_id = 1;
1255 let font_dict_index = cff
1256 .fd_select
1257 .map(|fd_select| fd_select.font_dict_index(glyph_id).unwrap())
1258 .unwrap_or(0);
1259 let font = &cff.fonts[usize::from(font_dict_index)];
1260
1261 let user_tuple = [Fixed::from(650.0)];
1262 let instance = fvar
1263 .normalize(user_tuple.iter().copied(), avar.as_ref())
1264 .unwrap();
1265
1266 let variable = VariableCharStringVisitorContext {
1267 vstore: cff.vstore.as_ref().unwrap(),
1268 instance: &instance,
1269 };
1270 let mut ctx = CharStringVisitorContext::new(
1271 1,
1272 &cff.char_strings_index,
1273 font.local_subr_index.as_ref(),
1274 &cff.global_subr_index,
1275 Some(variable),
1276 );
1277 let mut stack = ArgumentsStack {
1278 data: &mut [0.0; cff2::MAX_OPERANDS],
1279 len: 0,
1280 max_len: cff2::MAX_OPERANDS,
1281 };
1282 let res = ctx.visit(CFFFont::CFF2(font), &mut stack, &mut TraverseCharString);
1283 assert!(res.is_ok());
1284 Ok(())
1285 }
1286}