1use crate::blend::Blend;
25use crate::encoding;
26use pdfrum_common::kurbo::{BezPath, Point};
27
28const MAX_DEPTH: u32 = 30;
31const MAX_STACK: usize = 192;
35const MAX_SEGMENTS: usize = 65_536;
38
39#[derive(Clone, Copy)]
46pub(crate) struct Env<'a> {
47 pub subrs: &'a [Vec<u8>],
49 pub charstrings: &'a [Vec<u8>],
51 pub name_lookup: &'a dyn Fn(&str) -> Option<usize>,
53 pub weights: &'a [f32],
55 pub blend: Option<&'a Blend>,
59}
60
61#[derive(Debug, Clone, PartialEq)]
63pub struct Glyph {
64 pub path: BezPath,
66 pub advance: f32,
68 pub left_side_bearing: f32,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub(crate) enum Abort {
76 Truncated,
78 StackUnderflow,
80 MissingSubr,
82 TooDeep,
84 BadBlend,
87 BadSeac,
89 TooBig,
91}
92
93pub(crate) fn interpret(code: &[u8], env: Env<'_>) -> (Glyph, Option<Abort>) {
99 let mut m = Machine::new(env);
100 let abort = m.run(code, 0).err();
101 m.finish();
102 (
103 Glyph {
104 path: m.path,
105 advance: m.advance,
106 left_side_bearing: m.lsb,
107 },
108 abort,
109 )
110}
111
112struct Machine<'a> {
113 env: Env<'a>,
114 stack: Vec<f64>,
115 ps_stack: Vec<f64>,
117 path: BezPath,
118 open: bool,
119 current: Point,
120 start: Point,
122 advance: f32,
123 lsb: f32,
124 segments: usize,
125 flex: Option<Vec<Point>>,
128 done: bool,
130}
131
132impl<'a> Machine<'a> {
133 fn new(env: Env<'a>) -> Self {
134 Self {
135 env,
136 stack: Vec::new(),
137 ps_stack: Vec::new(),
138 path: BezPath::new(),
139 open: false,
140 current: Point::ZERO,
141 start: Point::ZERO,
142 advance: 0.0,
143 lsb: 0.0,
144 segments: 0,
145 flex: None,
146 done: false,
147 }
148 }
149
150 fn finish(&mut self) {
151 if self.open {
152 self.path.close_path();
153 self.open = false;
154 }
155 }
156
157 fn push(&mut self, v: f64) -> Result<(), Abort> {
158 if self.stack.len() >= MAX_STACK {
159 return Err(Abort::StackUnderflow);
160 }
161 self.stack.push(v);
162 Ok(())
163 }
164
165 fn take(&mut self, n: usize) -> Result<Vec<f64>, Abort> {
173 if self.stack.len() < n {
174 self.stack.clear();
175 return Err(Abort::StackUnderflow);
176 }
177 let args = self.stack.get(..n).unwrap_or_default().to_vec();
178 self.stack.clear();
179 Ok(args)
180 }
181
182 fn grow(&mut self) -> Result<(), Abort> {
183 self.segments = self.segments.saturating_add(1);
184 if self.segments > MAX_SEGMENTS {
185 return Err(Abort::TooBig);
186 }
187 Ok(())
188 }
189
190 fn move_to(&mut self, p: Point) -> Result<(), Abort> {
191 self.current = p;
192 if let Some(points) = self.flex.as_mut() {
194 points.push(p);
195 return Ok(());
196 }
197 self.grow()?;
198 if self.open {
199 self.path.close_path();
200 }
201 self.path.move_to(p);
202 self.start = p;
203 self.open = true;
204 Ok(())
205 }
206
207 fn line_to(&mut self, p: Point) -> Result<(), Abort> {
208 self.grow()?;
209 if !self.open {
210 self.path.move_to(self.current);
211 self.start = self.current;
212 self.open = true;
213 }
214 self.current = p;
215 self.path.line_to(p);
216 Ok(())
217 }
218
219 fn curve_to(&mut self, c1: Point, c2: Point, p: Point) -> Result<(), Abort> {
220 self.grow()?;
221 if !self.open {
222 self.path.move_to(self.current);
223 self.start = self.current;
224 self.open = true;
225 }
226 self.current = p;
227 self.path.curve_to(c1, c2, p);
228 Ok(())
229 }
230
231 fn close(&mut self) {
232 if self.open {
237 self.path.close_path();
238 self.open = false;
239 }
240 }
241
242 fn run(&mut self, code: &[u8], depth: u32) -> Result<(), Abort> {
243 if depth > MAX_DEPTH {
244 return Err(Abort::TooDeep);
245 }
246 let mut i = 0usize;
247 while let Some(&b) = code.get(i) {
248 i = i.saturating_add(1);
249 match b {
250 32..=246 => self.push(f64::from(i16::from(b) - 139))?,
252 247..=250 => {
253 let w = *code.get(i).ok_or(Abort::Truncated)?;
254 i = i.saturating_add(1);
255 self.push(f64::from((i16::from(b) - 247) * 256 + i16::from(w) + 108))?;
256 }
257 251..=254 => {
258 let w = *code.get(i).ok_or(Abort::Truncated)?;
259 i = i.saturating_add(1);
260 self.push(f64::from(-(i16::from(b) - 251) * 256 - i16::from(w) - 108))?;
261 }
262 255 => {
263 let bytes = code.get(i..i.saturating_add(4)).ok_or(Abort::Truncated)?;
264 i = i.saturating_add(4);
265 let mut v: i32 = 0;
266 for &x in bytes {
267 v = (v << 8) | i32::from(x);
268 }
269 self.push(f64::from(v))?;
270 }
271 12 => {
272 let esc = *code.get(i).ok_or(Abort::Truncated)?;
273 i = i.saturating_add(1);
274 if self.escaped(esc, depth)? {
275 return Ok(());
276 }
277 }
278 _ => {
279 if self.operator(b, code, &mut i, depth)? {
280 return Ok(());
281 }
282 }
283 }
284 if self.done {
285 return Ok(());
286 }
287 }
288 if depth == 0 {
292 return Err(Abort::Truncated);
293 }
294 Ok(())
295 }
296
297 fn operator(&mut self, op: u8, code: &[u8], i: &mut usize, depth: u32) -> Result<bool, Abort> {
299 match op {
300 1 | 3 => {
303 self.stack.clear();
304 }
305 4 => {
306 let a = self.take(1)?;
308 let dy = a.first().copied().unwrap_or(0.0);
309 self.move_to(Point::new(self.current.x, self.current.y + dy))?;
310 }
311 5 => {
312 let a = self.take(2)?;
313 let (dx, dy) = (arg(&a, 0), arg(&a, 1));
314 self.line_to(Point::new(self.current.x + dx, self.current.y + dy))?;
315 }
316 6 => {
317 let a = self.take(1)?;
318 self.line_to(Point::new(self.current.x + arg(&a, 0), self.current.y))?;
319 }
320 7 => {
321 let a = self.take(1)?;
322 self.line_to(Point::new(self.current.x, self.current.y + arg(&a, 0)))?;
323 }
324 8 => {
325 let a = self.take(6)?;
326 self.relative_curve(
327 arg(&a, 0),
328 arg(&a, 1),
329 arg(&a, 2),
330 arg(&a, 3),
331 arg(&a, 4),
332 arg(&a, 5),
333 )?;
334 }
335 9 => {
336 self.stack.clear();
337 self.close();
338 }
339 10 => {
340 let idx = self.stack.pop().ok_or(Abort::StackUnderflow)?;
344 let sub = usize::try_from(idx as i64)
345 .ok()
346 .and_then(|n| self.env.subrs.get(n))
347 .ok_or(Abort::MissingSubr)?;
348 let sub = sub.clone();
351 self.run(&sub, depth.saturating_add(1))?;
352 }
353 11 => return Ok(true), 13 => {
355 let a = self.take(2)?;
357 self.lsb = arg(&a, 0) as f32;
358 self.advance = arg(&a, 1) as f32;
359 self.current = Point::new(arg(&a, 0), 0.0);
362 self.start = self.current;
363 }
364 14 => {
365 self.done = true;
366 return Ok(true); }
368 21 => {
369 let a = self.take(2)?;
370 self.move_to(Point::new(
371 self.current.x + arg(&a, 0),
372 self.current.y + arg(&a, 1),
373 ))?;
374 }
375 22 => {
376 let a = self.take(1)?;
377 self.move_to(Point::new(self.current.x + arg(&a, 0), self.current.y))?;
378 }
379 30 => {
380 let a = self.take(4)?;
382 self.relative_curve(0.0, arg(&a, 0), arg(&a, 1), arg(&a, 2), arg(&a, 3), 0.0)?;
383 }
384 31 => {
385 let a = self.take(4)?;
387 self.relative_curve(arg(&a, 0), 0.0, arg(&a, 1), arg(&a, 2), 0.0, arg(&a, 3))?;
388 }
389 _ => {
393 let _ = (code, i);
394 self.stack.clear();
395 }
396 }
397 Ok(false)
398 }
399
400 fn escaped(&mut self, esc: u8, depth: u32) -> Result<bool, Abort> {
402 match esc {
403 6 => {
404 let a = self.take(5)?;
406 self.seac(
407 arg(&a, 0),
408 arg(&a, 1),
409 arg(&a, 2),
410 arg(&a, 3),
411 arg(&a, 4),
412 depth,
413 )?;
414 self.done = true;
415 return Ok(true);
416 }
417 7 => {
418 let a = self.take(4)?;
420 self.lsb = arg(&a, 0) as f32;
421 self.advance = arg(&a, 2) as f32;
422 self.current = Point::new(arg(&a, 0), arg(&a, 1));
423 self.start = self.current;
424 }
425 12 => {
426 let b = self.stack.pop().ok_or(Abort::StackUnderflow)?;
428 let a = self.stack.pop().ok_or(Abort::StackUnderflow)?;
429 self.push(if b == 0.0 { 0.0 } else { a / b })?;
430 }
431 16 => self.call_other_subr()?,
432 17 => {
433 let v = self.ps_stack.pop().unwrap_or(0.0);
437 self.push(v)?;
438 }
439 33 => {
440 let a = self.take(2)?;
443 self.current = Point::new(arg(&a, 0), arg(&a, 1));
444 }
445 _ => self.stack.clear(),
451 }
452 Ok(false)
453 }
454
455 fn call_other_subr(&mut self) -> Result<(), Abort> {
457 let index = self.stack.pop().ok_or(Abort::StackUnderflow)? as i64;
458 let count = self.stack.pop().ok_or(Abort::StackUnderflow)?;
459 let count = usize::try_from(count as i64).unwrap_or(0);
460 if self.stack.len() < count {
461 self.stack.clear();
462 return Err(Abort::StackUnderflow);
463 }
464 let base = self.stack.len().saturating_sub(count);
465 let args: Vec<f64> = self.stack.get(base..).unwrap_or_default().to_vec();
466 self.stack.truncate(base);
467
468 match index {
469 0 => self.end_flex(&args),
470 1 => {
471 self.flex = Some(Vec::new());
474 Ok(())
475 }
476 2 => Ok(()), 3 => {
478 self.ps_stack.push(3.0);
481 Ok(())
482 }
483 14..=18 => self.blend(index, &args),
484 _ => {
485 self.ps_stack.extend(args.iter().rev().copied());
489 Ok(())
490 }
491 }
492 }
493
494 fn end_flex(&mut self, args: &[f64]) -> Result<(), Abort> {
503 let points = self.flex.take().unwrap_or_default();
504 let p = |n: usize| points.get(n).copied();
508 let end = if let (Some(c1), Some(c2), Some(mid), Some(c3), Some(c4), Some(end)) =
509 (p(1), p(2), p(3), p(4), p(5), p(6))
510 {
511 self.curve_to(c1, c2, mid)?;
512 self.curve_to(c3, c4, end)?;
513 self.current = end;
514 end
515 } else {
516 if let Some(last) = points.last().copied() {
520 self.line_to(last)?;
521 }
522 self.current
523 };
524 self.ps_stack.push(end.y);
527 self.ps_stack.push(end.x);
528 let _ = args;
529 Ok(())
530 }
531
532 fn blend(&mut self, index: i64, args: &[f64]) -> Result<(), Abort> {
540 let masters = self.env.weights.len();
541 if masters < 2 || self.env.blend.is_none() {
542 self.ps_stack.extend(args.iter().rev().copied());
546 return Ok(());
547 }
548 let points = match index {
551 14 => 1usize,
552 15 => 2,
553 16 => 3,
554 17 => 4,
555 18 => 6,
556 _ => return Err(Abort::BadBlend),
557 };
558 if args.len() != points.saturating_mul(masters) {
559 return Err(Abort::BadBlend);
560 }
561 let mut blended = Vec::with_capacity(points);
562 for k in 0..points {
563 let mut v = args.get(k).copied().unwrap_or(0.0);
564 for (j, &w) in self.env.weights.iter().enumerate().skip(1) {
565 let delta = args
566 .get(
567 points
568 .saturating_add(k.saturating_mul(masters.saturating_sub(1)))
569 .saturating_add(j.saturating_sub(1)),
570 )
571 .copied()
572 .unwrap_or(0.0);
573 v += delta * f64::from(w);
574 }
575 blended.push(v);
576 }
577 self.ps_stack.extend(blended.iter().rev().copied());
580 Ok(())
581 }
582
583 fn seac(
585 &mut self,
586 asb: f64,
587 adx: f64,
588 ady: f64,
589 bchar: f64,
590 achar: f64,
591 depth: u32,
592 ) -> Result<(), Abort> {
593 let code = |v: f64| u8::try_from(v as i64).ok();
594 let lookup = |v: f64| -> Option<Vec<u8>> {
595 let name = encoding::standard_encoding_name(code(v)?)?;
596 let gid = (self.env.name_lookup)(name)?;
597 self.env.charstrings.get(gid).cloned()
598 };
599 let (Some(base), Some(accent)) = (lookup(bchar), lookup(achar)) else {
600 return Err(Abort::BadSeac);
601 };
602
603 let outer_lsb = self.lsb;
606 self.reset_for_component();
607 self.run(&base, depth.saturating_add(1))?;
608 self.finish();
609 let base_advance = self.advance;
610
611 let accent_start = self.path.elements().len();
615 self.reset_for_component();
616 self.run(&accent, depth.saturating_add(1))?;
617 self.finish();
618 let shift = kurbo_translate(f64::from(outer_lsb) - asb + adx, ady);
619 translate_from(&mut self.path, accent_start, shift);
620
621 self.advance = base_advance;
622 self.lsb = outer_lsb;
623 Ok(())
624 }
625
626 fn reset_for_component(&mut self) {
628 self.stack.clear();
629 self.ps_stack.clear();
630 self.flex = None;
631 self.open = false;
632 self.current = Point::ZERO;
633 self.start = Point::ZERO;
634 self.done = false;
635 }
636
637 fn relative_curve(
638 &mut self,
639 dx1: f64,
640 dy1: f64,
641 dx2: f64,
642 dy2: f64,
643 dx3: f64,
644 dy3: f64,
645 ) -> Result<(), Abort> {
646 let c1 = Point::new(self.current.x + dx1, self.current.y + dy1);
647 let c2 = Point::new(c1.x + dx2, c1.y + dy2);
648 let end = Point::new(c2.x + dx3, c2.y + dy3);
649 self.curve_to(c1, c2, end)
650 }
651}
652
653fn arg(args: &[f64], i: usize) -> f64 {
654 args.get(i).copied().unwrap_or(0.0)
655}
656
657fn kurbo_translate(dx: f64, dy: f64) -> (f64, f64) {
658 (dx, dy)
659}
660
661fn translate_from(path: &mut BezPath, from: usize, (dx, dy): (f64, f64)) {
664 use pdfrum_common::kurbo::PathEl;
665 let shift = |p: Point| Point::new(p.x + dx, p.y + dy);
666 let tail: Vec<PathEl> = path
667 .elements()
668 .get(from..)
669 .unwrap_or_default()
670 .iter()
671 .map(|el| match *el {
672 PathEl::MoveTo(p) => PathEl::MoveTo(shift(p)),
673 PathEl::LineTo(p) => PathEl::LineTo(shift(p)),
674 PathEl::QuadTo(a, b) => PathEl::QuadTo(shift(a), shift(b)),
675 PathEl::CurveTo(a, b, c) => PathEl::CurveTo(shift(a), shift(b), shift(c)),
676 PathEl::ClosePath => PathEl::ClosePath,
677 })
678 .collect();
679 path.truncate(from);
680 for el in tail {
681 path.push(el);
682 }
683}
684
685#[cfg(test)]
686#[allow(
687 clippy::indexing_slicing,
688 clippy::float_cmp,
689 clippy::cast_possible_truncation,
690 clippy::cast_sign_loss,
691 clippy::similar_names
692)]
693mod tests {
694 use super::{Abort, Env, Glyph, interpret};
695 use crate::eexec;
696 use pdfrum_common::kurbo::Shape;
697
698 fn cs(items: &[Item]) -> Vec<u8> {
703 let mut out = Vec::new();
704 for it in items {
705 match *it {
706 Item::N(v) => encode_number(&mut out, v),
707 Item::Op(o) => out.push(o),
708 Item::Esc(o) => out.extend_from_slice(&[12, o]),
709 }
710 }
711 out
712 }
713
714 #[derive(Clone, Copy)]
715 enum Item {
716 N(i32),
717 Op(u8),
718 Esc(u8),
719 }
720 use Item::{Esc, N, Op};
721
722 fn encode_number(out: &mut Vec<u8>, v: i32) {
723 match v {
724 -107..=107 => out.push((v + 139) as u8),
725 108..=1131 => {
726 let v = v - 108;
727 out.push(((v >> 8) + 247) as u8);
728 out.push((v & 0xFF) as u8);
729 }
730 -1131..=-108 => {
731 let v = -v - 108;
732 out.push(((v >> 8) + 251) as u8);
733 out.push((v & 0xFF) as u8);
734 }
735 _ => {
736 out.push(255);
737 out.extend_from_slice(&v.to_be_bytes());
738 }
739 }
740 }
741
742 fn no_names(_: &str) -> Option<usize> {
743 None
744 }
745
746 fn run(code: &[u8]) -> (Glyph, Option<Abort>) {
747 run_with(code, &[], &[])
748 }
749
750 fn run_with(code: &[u8], subrs: &[Vec<u8>], charstrings: &[Vec<u8>]) -> (Glyph, Option<Abort>) {
751 interpret(
752 code,
753 Env {
754 subrs,
755 charstrings,
756 name_lookup: &no_names,
757 weights: &[],
758 blend: None,
759 },
760 )
761 }
762
763 #[test]
764 fn hsbw_sets_the_origin_and_advance() {
765 let (g, abort) = run(&cs(&[N(50), N(600), Op(13), Op(14)]));
766 assert_eq!(abort, None);
767 assert_eq!(g.advance, 600.0);
768 assert_eq!(g.left_side_bearing, 50.0);
769 let (g, _) = run(&cs(&[
772 N(50),
773 N(600),
774 Op(13),
775 N(0),
776 N(0),
777 Op(21),
778 N(10),
779 Op(6),
780 Op(9),
781 Op(14),
782 ]));
783 assert_eq!(g.path.bounding_box().x0, 50.0);
784 assert_eq!(g.path.bounding_box().x1, 60.0);
785 }
786
787 #[test]
788 fn a_square_from_the_line_operators() {
789 let (g, abort) = run(&cs(&[
791 N(0),
792 N(1000),
793 Op(13),
794 N(100),
795 N(100),
796 Op(21), N(200),
798 Op(6), N(200),
800 Op(7), N(-200),
802 N(0),
803 Op(5), Op(9), Op(14),
806 ]));
807 assert_eq!(abort, None);
808 let bb = g.path.bounding_box();
809 assert_eq!((bb.x0, bb.y0, bb.x1, bb.y1), (100.0, 100.0, 300.0, 300.0));
810 assert_eq!(g.path.elements().len(), 5); }
812
813 #[test]
814 fn rrcurveto_vhcurveto_hvcurveto_place_control_points() {
815 use pdfrum_common::kurbo::{PathEl, Point};
816 let (g, _) = run(&cs(&[
817 N(0),
818 N(1000),
819 Op(13),
820 N(0),
821 N(0),
822 Op(21),
823 N(10),
824 N(20),
825 N(30),
826 N(40),
827 N(50),
828 N(60),
829 Op(8), N(10),
831 N(20),
832 N(30),
833 N(40),
834 Op(30), N(10),
836 N(20),
837 N(30),
838 N(40),
839 Op(31), Op(14),
841 ]));
842 let els = g.path.elements();
843 assert_eq!(
845 els.get(1),
846 Some(&PathEl::CurveTo(
847 Point::new(10.0, 20.0),
848 Point::new(40.0, 60.0),
849 Point::new(90.0, 120.0)
850 ))
851 );
852 assert_eq!(
855 els.get(2),
856 Some(&PathEl::CurveTo(
857 Point::new(90.0, 130.0),
858 Point::new(110.0, 160.0),
859 Point::new(150.0, 160.0)
860 ))
861 );
862 assert_eq!(
865 els.get(3),
866 Some(&PathEl::CurveTo(
867 Point::new(160.0, 160.0),
868 Point::new(180.0, 190.0),
869 Point::new(180.0, 230.0)
870 ))
871 );
872 }
873
874 #[test]
875 fn callsubr_and_return() {
876 let subrs = vec![
877 Vec::new(),
878 Vec::new(),
879 Vec::new(),
880 Vec::new(),
881 cs(&[N(100), Op(6), Op(11)]), ];
883 let (g, abort) = run_with(
884 &cs(&[
885 N(0),
886 N(1000),
887 Op(13),
888 N(0),
889 N(0),
890 Op(21),
891 N(4),
892 Op(10),
893 Op(14),
894 ]),
895 &subrs,
896 &[],
897 );
898 assert_eq!(abort, None);
899 assert_eq!(g.path.bounding_box().x1, 100.0);
900 }
901
902 #[test]
903 fn a_missing_subr_aborts_but_keeps_the_path() {
904 let (g, abort) = run(&cs(&[
905 N(0),
906 N(1000),
907 Op(13),
908 N(0),
909 N(0),
910 Op(21),
911 N(50),
912 Op(6),
913 N(99),
914 Op(10),
915 Op(14),
916 ]));
917 assert_eq!(abort, Some(Abort::MissingSubr));
918 assert_eq!(g.path.bounding_box().x1, 50.0);
919 }
920
921 #[test]
922 fn div_leaves_a_quotient_on_the_stack() {
923 let (g, _) = run(&cs(&[
924 N(0),
925 N(1000),
926 Op(13),
927 N(0),
928 N(0),
929 Op(21),
930 N(300),
931 N(3),
932 Esc(12),
933 Op(6),
934 Op(14),
935 ]));
936 assert_eq!(g.path.bounding_box().x1, 100.0);
937 let (g, _) = run(&cs(&[
940 N(0),
941 N(1000),
942 Op(13),
943 N(0),
944 N(0),
945 Op(21),
946 N(300),
947 N(0),
948 Esc(12),
949 Op(6),
950 Op(14),
951 ]));
952 assert!(g.path.bounding_box().x1.is_finite());
953 }
954
955 #[test]
956 fn setcurrentpoint_is_absolute() {
957 let (g, _) = run(&cs(&[
958 N(0),
959 N(1000),
960 Op(13),
961 N(0),
962 N(0),
963 Op(21),
964 N(500),
965 N(500),
966 Esc(33), N(10),
968 Op(6),
969 Op(14),
970 ]));
971 let bb = g.path.bounding_box();
972 assert_eq!((bb.x1, bb.y1), (510.0, 500.0));
973 }
974
975 #[test]
976 fn flex_becomes_two_curves() {
977 use pdfrum_common::kurbo::PathEl;
978 let mut items = vec![N(0), N(1000), Op(13), N(0), N(0), Op(21)];
980 items.extend([N(0), N(1), Esc(16)]); for (dx, dy) in [
982 (10, 10),
983 (10, 10),
984 (10, 10),
985 (10, 10),
986 (10, 10),
987 (10, 10),
988 (10, 10),
989 ] {
990 items.extend([N(0), N(2), Esc(16), N(dx), N(dy), Op(21)]);
991 }
992 items.extend([N(50), N(70), N(70), N(3), N(0), Esc(16)]);
994 items.extend([Esc(17), Esc(17), Esc(33)]);
995 items.push(Op(14));
996 let (g, abort) = run(&cs(&items));
997 assert_eq!(abort, None);
998 let curves = g
1000 .path
1001 .elements()
1002 .iter()
1003 .filter(|e| matches!(e, PathEl::CurveTo(..)))
1004 .count();
1005 assert_eq!(curves, 2);
1006 assert_eq!(
1007 g.path
1008 .elements()
1009 .iter()
1010 .filter(|e| matches!(e, PathEl::MoveTo(_)))
1011 .count(),
1012 1
1013 );
1014 }
1015
1016 #[test]
1017 fn hint_replacement_returns_three_and_calls_the_subr() {
1018 let subrs = vec![Vec::new(), Vec::new(), Vec::new(), cs(&[Op(11)])];
1022 let (g, abort) = run_with(
1023 &cs(&[
1024 N(0),
1025 N(1000),
1026 Op(13),
1027 N(0),
1028 N(0),
1029 Op(21),
1030 N(3),
1031 N(1),
1032 N(3),
1033 Esc(16), Esc(17),
1035 Op(10), N(70),
1037 Op(6),
1038 Op(14),
1039 ]),
1040 &subrs,
1041 &[],
1042 );
1043 assert_eq!(abort, None);
1044 assert_eq!(g.path.bounding_box().x1, 70.0);
1045 }
1046
1047 #[test]
1048 fn seac_composes_two_glyphs_with_the_side_bearing_correction() {
1049 let base = cs(&[
1052 N(0),
1053 N(500),
1054 Op(13),
1055 N(0),
1056 N(0),
1057 Op(21),
1058 N(100),
1059 Op(6),
1060 N(100),
1061 Op(7),
1062 Op(9),
1063 Op(14),
1064 ]);
1065 let accent = cs(&[
1066 N(20),
1067 N(300),
1068 Op(13),
1069 N(0),
1070 N(0),
1071 Op(21),
1072 N(50),
1073 Op(6),
1074 N(50),
1075 Op(7),
1076 Op(9),
1077 Op(14),
1078 ]);
1079 let charstrings = vec![Vec::new(), base, accent];
1080 let lookup = |n: &str| match n {
1082 "A" => Some(1usize),
1083 "B" => Some(2usize),
1084 _ => None,
1085 };
1086 let (g, abort) = interpret(
1087 &cs(&[
1089 N(0),
1090 N(500),
1091 Op(13),
1092 N(20),
1093 N(300),
1094 N(100),
1095 N(65),
1096 N(66),
1097 Esc(6),
1098 ]),
1099 Env {
1100 subrs: &[],
1101 charstrings: &charstrings,
1102 name_lookup: &lookup,
1103 weights: &[],
1104 blend: None,
1105 },
1106 );
1107 assert_eq!(abort, None);
1108 assert_eq!(g.advance, 500.0);
1113 let bb = g.path.bounding_box();
1114 assert_eq!((bb.x0, bb.y0), (0.0, 0.0));
1115 assert_eq!((bb.x1, bb.y1), (350.0, 150.0));
1116 }
1117
1118 #[test]
1119 fn seac_naming_a_missing_component_aborts_cleanly() {
1120 let (_, abort) = run(&cs(&[
1121 N(0),
1122 N(500),
1123 Op(13),
1124 N(0),
1125 N(0),
1126 N(0),
1127 N(65),
1128 N(66),
1129 Esc(6),
1130 ]));
1131 assert_eq!(abort, Some(Abort::BadSeac));
1132 }
1133
1134 #[test]
1135 fn multiple_master_blend_interpolates_the_operands() {
1136 use crate::blend::{AxisKind, Blend, DesignMap};
1137 use pdfrum_common::Diagnostics;
1138 let mut d = Diagnostics::default();
1139 let blend = Blend::new(
1140 vec![AxisKind::Weight],
1141 vec![vec![0.0], vec![1.0]],
1142 vec![DesignMap {
1143 knots: vec![(0.0, 0.0), (1.0, 1.0)],
1144 }],
1145 vec![0.5, 0.5],
1146 &mut d,
1147 )
1148 .expect("consistent");
1149
1150 let code = cs(&[
1153 N(0),
1154 N(1000),
1155 Op(13),
1156 N(0),
1157 N(0),
1158 Op(21),
1159 N(100),
1160 N(200),
1161 N(2),
1162 N(14),
1163 Esc(16), Esc(17), Op(6), Op(14),
1167 ]);
1168 let (g, abort) = interpret(
1169 &code,
1170 Env {
1171 subrs: &[],
1172 charstrings: &[],
1173 name_lookup: &no_names,
1174 weights: &[0.25, 0.75],
1175 blend: Some(&blend),
1176 },
1177 );
1178 assert_eq!(abort, None);
1179 assert!((g.path.bounding_box().x1 - 250.0).abs() < 1e-9);
1180
1181 let (g2, _) = interpret(
1183 &code,
1184 Env {
1185 subrs: &[],
1186 charstrings: &[],
1187 name_lookup: &no_names,
1188 weights: &[1.0, 0.0],
1189 blend: Some(&blend),
1190 },
1191 );
1192 assert!((g2.path.bounding_box().x1 - 100.0).abs() < 1e-9);
1193 }
1194
1195 #[test]
1196 fn a_blend_with_the_wrong_arity_aborts() {
1197 use crate::blend::{AxisKind, Blend, DesignMap};
1198 use pdfrum_common::Diagnostics;
1199 let mut d = Diagnostics::default();
1200 let blend = Blend::new(
1201 vec![AxisKind::Weight],
1202 vec![vec![0.0], vec![1.0]],
1203 vec![DesignMap {
1204 knots: vec![(0.0, 0.0), (1.0, 1.0)],
1205 }],
1206 vec![0.5, 0.5],
1207 &mut d,
1208 )
1209 .expect("consistent");
1210 let (_, abort) = interpret(
1212 &cs(&[
1213 N(0),
1214 N(1000),
1215 Op(13),
1216 N(1),
1217 N(2),
1218 N(3),
1219 N(3),
1220 N(14),
1221 Esc(16),
1222 Op(14),
1223 ]),
1224 Env {
1225 subrs: &[],
1226 charstrings: &[],
1227 name_lookup: &no_names,
1228 weights: &[0.5, 0.5],
1229 blend: Some(&blend),
1230 },
1231 );
1232 assert_eq!(abort, Some(Abort::BadBlend));
1233 }
1234
1235 #[test]
1236 fn closepath_leaves_the_current_point_alone() {
1237 let (g, _) = run(&cs(&[
1240 N(0),
1241 N(1000),
1242 Op(13),
1243 N(100),
1244 N(0),
1245 Op(21),
1246 N(100),
1247 Op(6), Op(9), N(50),
1250 N(50),
1251 Op(21), N(10),
1253 Op(6),
1254 Op(14),
1255 ]));
1256 let bb = g.path.bounding_box();
1257 assert_eq!(bb.x1, 260.0);
1258 }
1259
1260 #[test]
1261 fn recursion_is_capped() {
1262 let subrs = vec![cs(&[N(0), Op(10), Op(11)])];
1264 let (_, abort) = run_with(
1265 &cs(&[N(0), N(1000), Op(13), N(0), Op(10), Op(14)]),
1266 &subrs,
1267 &[],
1268 );
1269 assert_eq!(abort, Some(Abort::TooDeep));
1270 }
1271
1272 #[test]
1273 fn unknown_operators_clear_the_stack_and_carry_on() {
1274 let (g, abort) = run(&cs(&[
1275 N(0),
1276 N(1000),
1277 Op(13),
1278 N(0),
1279 N(0),
1280 Op(21),
1281 N(1),
1282 N(2),
1283 Op(23),
1284 N(80),
1285 Op(6),
1286 Op(14),
1287 ]));
1288 assert_eq!(abort, None);
1289 assert_eq!(g.path.bounding_box().x1, 80.0);
1290 }
1291
1292 #[test]
1293 fn the_four_byte_number_encoding_round_trips() {
1294 let (g, _) = run(&cs(&[
1295 N(0),
1296 N(1000),
1297 Op(13),
1298 N(0),
1299 N(0),
1300 Op(21),
1301 N(70000),
1302 Op(6),
1303 Op(14),
1304 ]));
1305 assert_eq!(g.path.bounding_box().x1, 70000.0);
1306 let (g, _) = run(&cs(&[
1307 N(0),
1308 N(1000),
1309 Op(13),
1310 N(0),
1311 N(0),
1312 Op(21),
1313 N(-70000),
1314 Op(6),
1315 Op(14),
1316 ]));
1317 assert_eq!(g.path.bounding_box().x0, -70000.0);
1318 }
1319
1320 #[test]
1321 fn a_truncated_charstring_reports_it() {
1322 let (_, abort) = run(&[255, 0, 1]);
1324 assert_eq!(abort, Some(Abort::Truncated));
1325 let (g, abort) = run(&[]);
1327 assert_eq!(abort, Some(Abort::Truncated));
1328 assert!(g.path.is_empty());
1329 }
1330
1331 #[test]
1332 fn encrypted_charstrings_decode_through_the_cipher() {
1333 let plain = {
1336 let mut v = vec![0u8; 4];
1337 v.extend(cs(&[
1338 N(0),
1339 N(1000),
1340 Op(13),
1341 N(0),
1342 N(0),
1343 Op(21),
1344 N(42),
1345 Op(6),
1346 Op(14),
1347 ]));
1348 v
1349 };
1350 let cipher = eexec::encrypt(&plain, eexec::CHARSTRING_SEED);
1351 let decoded = eexec::decrypt(&cipher, eexec::CHARSTRING_SEED, 4);
1352 let (g, abort) = run(&decoded);
1353 assert_eq!(abort, None);
1354 assert_eq!(g.path.bounding_box().x1, 42.0);
1355 }
1356}