1#[allow(missing_docs)]
34pub mod ops;
35
36use crate::content::ops::TypedInstruction;
37use crate::object;
38use crate::object::dict::InlineImageDict;
39use crate::object::name::{Name, skip_name_like};
40use crate::object::{Array, Number, Object, Stream};
41use crate::reader::Reader;
42use crate::reader::{Readable, ReaderContext, ReaderExt, Skippable};
43use crate::trivia::{is_regular_character, is_white_space_character};
44use crate::util::find_needle;
45use alloc::vec::Vec;
46use core::fmt::{Debug, Display, Formatter};
47use core::ops::Deref;
48use smallvec::SmallVec;
49
50const OPERANDS_THRESHOLD: usize = 33;
52const INLINE_COLOR_COMPONENTS: usize = 4;
53
54impl Debug for Operator<'_> {
55 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
56 write!(f, "{}", self.0.as_str())
57 }
58}
59
60impl Display for Operator<'_> {
61 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
62 write!(f, "{}", self.0.as_str())
63 }
64}
65
66#[derive(Clone, PartialEq)]
68pub struct Operator<'a>(Name<'a>);
69
70impl Deref for Operator<'_> {
71 type Target = [u8];
72
73 fn deref(&self) -> &Self::Target {
74 self.0.as_ref()
75 }
76}
77
78impl Skippable for Operator<'_> {
79 fn skip(r: &mut Reader<'_>, _: bool) -> Option<()> {
80 skip_name_like(r, false).map(|_| ())
81 }
82}
83
84impl<'a> Readable<'a> for Operator<'a> {
85 fn read(r: &mut Reader<'a>, _: &ReaderContext<'a>) -> Option<Self> {
86 let start = r.offset();
87 let mut escaped = false;
88 while let Some(b) = r.peek_byte().filter(|b| is_regular_character(*b)) {
89 escaped |= b == b'#';
90 r.forward();
91 }
92
93 let data = r.range(start..r.offset())?;
94 if data.is_empty() {
95 return None;
96 }
97
98 let name = if escaped {
99 Name::new_escaped(data)?
100 } else {
101 Name::new_unescaped(data)
102 };
103
104 Some(Self(name))
105 }
106}
107
108#[derive(Debug, Clone)]
110pub struct UntypedIter<'a> {
111 reader: Reader<'a>,
112 stack: Stack<'a>,
113 operator: Option<Operator<'a>>,
114}
115
116impl<'a> UntypedIter<'a> {
117 pub fn new(data: &'a [u8]) -> Self {
119 Self {
120 reader: Reader::new(data),
121 stack: Stack::new(),
122 operator: None,
123 }
124 }
125
126 pub fn empty() -> Self {
128 Self {
129 reader: Reader::new(&[]),
130 stack: Stack::new(),
131 operator: None,
132 }
133 }
134
135 #[allow(clippy::should_implement_trait)]
137 pub fn next(&mut self) -> Option<Instruction<'_, 'a>> {
138 self.stack.clear();
139
140 let operator = read_instruction(&mut self.reader, &mut self.stack)?;
141 self.operator = Some(operator);
142 Some(Instruction {
143 operands: &self.stack,
144 operator: self.operator.as_ref().unwrap(),
145 })
146 }
147}
148
149fn read_instruction<'a>(r: &mut Reader<'a>, stack: &mut Stack<'a>) -> Option<Operator<'a>> {
150 let mut cur = r.clone();
152
153 loop {
154 cur.skip_white_spaces_and_comments();
155
156 let Some(first) = cur.peek_byte() else {
157 *r = cur;
158 return None;
159 };
160
161 if matches!(first, b'.' | b'+' | b'-' | b'0'..=b'9') {
162 let start = cur.offset();
163 match Number::read_simple(&mut cur) {
164 Some(number) => {
165 if stack.push(Object::Number(number)).is_none() {
166 *r = cur;
167 return None;
168 }
169 continue;
170 }
171 None => cur.jump(start),
172 }
173 }
174
175 match first {
176 b'.' | b'+' | b'-' | b'0'..=b'9' | b'/' | b'[' | b'<' | b'(' => {
178 *r = cur;
179 read_operand(r, stack)?;
180 cur = r.clone();
181 }
182 _ => {
183 *r = cur;
184 let Some(operator) = r.read_without_context::<Operator<'a>>() else {
185 r.jump_to_end();
186 return None;
187 };
188
189 if operator.as_ref() == b"BI" {
191 read_inline_image(r, stack)?;
192 }
193
194 return Some(operator);
195 }
196 }
197 }
198}
199
200fn read_operand<'a>(r: &mut Reader<'a>, stack: &mut Stack<'a>) -> Option<()> {
201 if let Some(object) = r.read_without_context::<Object<'a>>() {
209 stack.push(object)
210 } else if r.read_without_context::<Operator<'a>>().is_some() {
211 stack.clear();
212 Some(())
213 } else {
214 None
215 }
216}
217
218fn read_inline_image<'a>(r: &mut Reader<'a>, stack: &mut Stack<'a>) -> Option<()> {
219 let inline_dict = r.read_without_context::<InlineImageDict<'_>>()?;
221 let dict = inline_dict.get_dict().clone();
222
223 r.read_white_space()?;
225
226 let stream_data = r.tail()?;
227 let start_offset = r.offset();
228
229 'outer: while let Some(pos) = find_needle(r.tail()?, b"EI") {
230 r.read_bytes(pos)?;
231
232 if r.peek_bytes(2) == Some(b"EI") {
233 if r.peek_bytes(3)
235 .is_some_and(|b| !is_white_space_character(b[2]))
236 {
237 r.read_bytes(3)?;
238
239 continue;
240 }
241
242 let end_offset = r.offset() - start_offset;
243 let image_data = &stream_data[..end_offset];
244
245 let stream = Stream::new(image_data, dict.clone());
246
247 let tail = &r.tail()?[2..];
254 let mut find_reader = Reader::new(tail);
255 let mut binary = BinaryTracker::default();
256 let mut is_content = None;
257 let mut next_bi = find_needle(tail, b"BI");
258
259 while !find_reader.at_end() {
260 let offset = find_reader.offset();
261
262 if next_bi.is_some_and(|bi| bi < offset) {
263 next_bi = find_needle(&tail[offset..], b"BI").map(|bi| offset + bi);
264 }
265
266 let next_ei = find_needle(&tail[offset..], b"EI").map(|ei| offset + ei);
267
268 let (next_pos, is_ei) = match (next_ei, next_bi) {
269 (Some(ei), Some(bi)) if ei <= bi => (ei, true),
270 (Some(_), Some(bi)) => (bi, false),
271 (Some(ei), None) => (ei, true),
272 (None, Some(bi)) => (bi, false),
273 (None, None) => break,
274 };
275
276 find_reader.read_bytes(next_pos - offset)?;
277
278 if is_ei {
279 if binary.found_before(tail, next_pos) {
282 r.read_bytes(2)?;
283 continue 'outer;
284 }
285
286 if !*is_content.get_or_insert_with(|| starts_with_content(tail)) {
293 r.read_bytes(2)?;
296 continue 'outer;
297 }
298 } else {
299 let mut cloned = find_reader.clone();
302 cloned.read_bytes(2)?;
303 if cloned
304 .read_without_context::<InlineImageDict<'_>>()
305 .is_some()
306 {
307 break;
308 }
309 }
310
311 find_reader.read_byte()?;
312 }
313
314 stack.push(Object::Stream(stream))?;
315
316 r.read_bytes(2)?;
317 r.skip_white_spaces();
318
319 break;
320 }
321 }
322
323 Some(())
324}
325
326fn starts_with_content(data: &[u8]) -> bool {
327 let mut iter = TypedIter::new(data);
328 let mut counter = 0;
329
330 while let Some(op) = iter.next() {
331 if counter >= 20 {
337 return true;
338 }
339
340 if matches!(
341 op,
342 TypedInstruction::NextLineAndShowText(_)
343 | TypedInstruction::ShowText(_)
344 | TypedInstruction::ShowTexts(_)
345 | TypedInstruction::ShowTextWithParameters(_)
346 ) {
347 return true;
348 }
349
350 counter += 1;
351 }
352
353 false
354}
355
356#[derive(Default)]
359struct BinaryTracker {
360 pos: usize,
361 string_depth: usize,
362 escaped: bool,
363 found: bool,
364}
365
366impl BinaryTracker {
367 fn found_before(&mut self, data: &[u8], end: usize) -> bool {
368 if self.found || end <= self.pos {
369 return self.found;
370 }
371
372 for (i, &b) in data[self.pos..end].iter().enumerate() {
373 if self.string_depth > 0 {
374 if self.escaped {
375 self.escaped = false;
376 } else if b == b'\\' {
377 self.escaped = true;
378 } else if b == b'(' {
379 self.string_depth += 1;
380 } else if b == b')' {
381 self.string_depth -= 1;
382 }
383 } else if !b.is_ascii() {
384 self.found = true;
385 self.pos += i;
386 return true;
387 } else if b == b'(' {
388 self.string_depth = 1;
389 }
390 }
391
392 self.pos = end;
393 false
394 }
395}
396
397#[derive(Clone)]
400pub struct TypedIter<'a> {
401 untyped: UntypedIter<'a>,
402}
403
404impl<'a> TypedIter<'a> {
405 pub fn new(data: &'a [u8]) -> Self {
407 Self {
408 untyped: UntypedIter::new(data),
409 }
410 }
411
412 pub(crate) fn from_untyped(untyped: UntypedIter<'a>) -> Self {
413 Self { untyped }
414 }
415
416 #[allow(clippy::should_implement_trait)]
418 pub fn next(&mut self) -> Option<TypedInstruction<'_, 'a>> {
419 let op = self.untyped.next()?;
420 match TypedInstruction::dispatch(&op) {
422 Some(op) => Some(op),
423 None => {
428 if [
429 &b"m"[..],
430 &b"l"[..],
431 &b"c"[..],
432 &b"v"[..],
433 &b"y"[..],
434 &b"h"[..],
435 &b"re"[..],
436 ]
437 .contains(&op.operator.0.deref())
438 {
439 None
440 } else {
441 Some(TypedInstruction::Fallback(op.operator))
442 }
443 }
444 }
445 }
446}
447
448#[derive(Debug)]
450pub struct Instruction<'b, 'a> {
451 pub operands: &'b Stack<'a>,
453 pub operator: &'b Operator<'a>,
455}
456
457impl<'b, 'a> Instruction<'b, 'a> {
458 pub fn operands(&self) -> OperandIterator<'b, 'a> {
460 OperandIterator::new(self.operands)
461 }
462}
463
464#[derive(Clone, PartialEq)]
466pub struct Stack<'a> {
467 data: Vec<Object<'a>>,
470}
471
472impl<'a> Default for Stack<'a> {
473 fn default() -> Self {
474 Self::new()
475 }
476}
477
478impl<'a> Stack<'a> {
479 pub fn new() -> Self {
481 Self { data: Vec::new() }
482 }
483
484 fn push(&mut self, operand: Object<'a>) -> Option<()> {
485 if self.data.len() >= OPERANDS_THRESHOLD {
486 return None;
487 }
488
489 self.data.push(operand);
490 Some(())
491 }
492
493 fn clear(&mut self) {
494 self.data.clear();
495 }
496
497 fn len(&self) -> usize {
498 self.data.len()
499 }
500
501 fn as_slice(&self) -> &[Object<'a>] {
502 &self.data
503 }
504
505 fn get<'b, T>(&'b self, index: usize) -> Option<T>
506 where
507 T: Operand<'b, 'a>,
508 {
509 self.as_slice().get(index).and_then(T::from_object)
510 }
511
512 fn get_all<'b, T>(&'b self) -> Option<SmallVec<[T; INLINE_COLOR_COMPONENTS]>>
513 where
514 T: Operand<'b, 'a>,
515 {
516 let mut operands = SmallVec::new();
517
518 for op in self.as_slice() {
519 let converted = T::from_object(op)?;
520 operands.push(converted);
521 }
522
523 Some(operands)
524 }
525}
526
527impl Debug for Stack<'_> {
528 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
529 f.debug_list().entries(self.as_slice()).finish()
530 }
531}
532
533trait Operand<'b, 'a>: Sized {
534 fn from_object(object: &'b Object<'a>) -> Option<Self>;
535}
536
537impl<'b, 'a> Operand<'b, 'a> for Number {
538 fn from_object(object: &'b Object<'a>) -> Option<Self> {
539 match object {
540 Object::Number(n) => Some(*n),
541 _ => None,
542 }
543 }
544}
545
546impl<'b, 'a> Operand<'b, 'a> for &'b object::String<'a> {
547 fn from_object(object: &'b Object<'a>) -> Option<Self> {
548 match object {
549 Object::String(s) => Some(s),
550 _ => None,
551 }
552 }
553}
554
555impl<'b, 'a> Operand<'b, 'a> for &'b Name<'a> {
556 fn from_object(object: &'b Object<'a>) -> Option<Self> {
557 match object {
558 Object::Name(n) => Some(n),
559 _ => None,
560 }
561 }
562}
563
564impl<'b, 'a> Operand<'b, 'a> for &'b Array<'a> {
565 fn from_object(object: &'b Object<'a>) -> Option<Self> {
566 match object {
567 Object::Array(a) => Some(a),
568 _ => None,
569 }
570 }
571}
572
573impl<'b, 'a> Operand<'b, 'a> for &'b Stream<'a> {
574 fn from_object(object: &'b Object<'a>) -> Option<Self> {
575 match object {
576 Object::Stream(s) => Some(s),
577 _ => None,
578 }
579 }
580}
581
582impl<'b, 'a> Operand<'b, 'a> for &'b Object<'a> {
583 fn from_object(object: &'b Object<'a>) -> Option<Self> {
584 Some(object)
585 }
586}
587
588pub struct OperandIterator<'b, 'a> {
590 stack: &'b Stack<'a>,
591 cur_index: usize,
592}
593
594impl<'b, 'a> OperandIterator<'b, 'a> {
595 fn new(stack: &'b Stack<'a>) -> Self {
596 Self {
597 stack,
598 cur_index: 0,
599 }
600 }
601}
602
603impl<'b, 'a> Iterator for OperandIterator<'b, 'a> {
604 type Item = &'b Object<'a>;
605
606 fn next(&mut self) -> Option<Self::Item> {
607 if let Some(item) = self.stack.as_slice().get(self.cur_index) {
608 self.cur_index += 1;
609
610 Some(item)
611 } else {
612 None
613 }
614 }
615}
616
617pub(crate) trait OperatorTrait<'b, 'a>: Sized {
618 fn from_stack(stack: &'b Stack<'a>) -> Option<Self>;
619}
620
621mod macros {
622 macro_rules! display_op {
623 ($t:ident $(<$($l:lifetime),+>)?;) => {
624 impl$(<$($l),+>)? core::fmt::Display for $t$(<$($l),+>)? {
625 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
626 f.write_str(stringify!($t))
627 }
628 }
629 };
630 ($t:ident $(<$($l:lifetime),+>)?; $first:tt $(, $field:tt)*) => {
631 impl$(<$($l),+>)? core::fmt::Display for $t$(<$($l),+>)? {
632 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
633 f.write_str(concat!(stringify!($t), "("))?;
634 core::fmt::Display::fmt(&self.$first, f)?;
635 $(
636 f.write_str(" ")?;
637 core::fmt::Display::fmt(&self.$field, f)?;
638 )*
639 f.write_str(")")
640 }
641 }
642 };
643 }
644
645 macro_rules! display_variadic_op {
646 ($t:ident $(<$($l:lifetime),+>)?) => {
647 impl$(<$($l),+>)? core::fmt::Display for $t$(<$($l),+>)? {
648 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
649 f.write_str(stringify!($t))?;
650 if let Some((first, rest)) = self.0.split_first() {
651 f.write_str("(")?;
652 core::fmt::Display::fmt(first, f)?;
653 for value in rest {
654 f.write_str(" ")?;
655 core::fmt::Display::fmt(value, f)?;
656 }
657 f.write_str(")")?;
658 }
659 Ok(())
660 }
661 }
662 };
663 }
664
665 macro_rules! op_impl {
666 ($t:ident $(<$($l:lifetime),+>)?, $e:expr, $n:expr, |$stack:ident : $stack_ty:ty| $body:block) => {
667 impl<'b, 'a> OperatorTrait<'b, 'a> for $t$(<$($l),+>)? {
668 #[inline(always)]
669 fn from_stack($stack: $stack_ty) -> Option<Self> {
670 $body
671 }
672 }
673
674 impl<'b, 'a> From<$t$(<$($l),+>)?> for TypedInstruction<'b, 'a> {
675 fn from(value: $t$(<$($l),+>)?) -> Self {
676 TypedInstruction::$t(value)
677 }
678 }
679
680 impl<'b, 'a> TryFrom<TypedInstruction<'b, 'a>> for $t$(<$($l),+>)? {
681 type Error = ();
682
683 fn try_from(value: TypedInstruction<'b, 'a>) -> core::result::Result<Self, Self::Error> {
684 match value {
685 TypedInstruction::$t(e) => Ok(e),
686 _ => Err(())
687 }
688 }
689 }
690 };
691 }
692
693 macro_rules! op0 {
699 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
700 crate::content::macros::display_op!($t$(<$($l),+>)?;);
701 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, 0, |_stack: &'b Stack<'a>| {
702 Some(Self)
703 });
704 }
705 }
706
707 macro_rules! op1 {
708 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
709 crate::content::macros::display_op!($t$(<$($l),+>)?; 0);
710 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, 1, |stack: &'b Stack<'a>| {
711 let shift = stack.len().saturating_sub(1);
712 Some(Self(stack.get(0 + shift)?))
713 });
714 }
715 }
716
717 macro_rules! op_all {
718 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
719 crate::content::macros::display_variadic_op!($t$(<$($l),+>)?);
720 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, u8::MAX as usize, |stack: &'b Stack<'a>| {
721 Some(Self(stack.get_all()?))
722 });
723 }
724 }
725
726 macro_rules! op2 {
727 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
728 crate::content::macros::display_op!($t$(<$($l),+>)?; 0, 1);
729 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, 2, |stack: &'b Stack<'a>| {
730 let shift = stack.len().saturating_sub(2);
731 Some(Self(stack.get(0 + shift)?, stack.get(1 + shift)?))
732 });
733 }
734 }
735
736 macro_rules! op3 {
737 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
738 crate::content::macros::display_op!($t$(<$($l),+>)?; 0, 1, 2);
739 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, 3, |stack: &'b Stack<'a>| {
740 let shift = stack.len().saturating_sub(3);
741 Some(Self(stack.get(0 + shift)?, stack.get(1 + shift)?,
742 stack.get(2 + shift)?))
743 });
744 }
745 }
746
747 macro_rules! op4 {
748 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
749 crate::content::macros::display_op!($t$(<$($l),+>)?; 0, 1, 2, 3);
750 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, 4, |stack: &'b Stack<'a>| {
751 let shift = stack.len().saturating_sub(4);
752 Some(Self(stack.get(0 + shift)?, stack.get(1 + shift)?,
753 stack.get(2 + shift)?, stack.get(3 + shift)?))
754 });
755 }
756 }
757
758 macro_rules! op6 {
759 ($t:ident $(<$($l:lifetime),+>)?, $e:expr) => {
760 crate::content::macros::display_op!($t$(<$($l),+>)?; 0, 1, 2, 3, 4, 5);
761 crate::content::macros::op_impl!($t$(<$($l),+>)?, $e, 6, |stack: &'b Stack<'a>| {
762 let shift = stack.len().saturating_sub(6);
763 Some(Self(stack.get(0 + shift)?, stack.get(1 + shift)?,
764 stack.get(2 + shift)?, stack.get(3 + shift)?,
765 stack.get(4 + shift)?, stack.get(5 + shift)?))
766 });
767 }
768 }
769
770 pub(crate) use display_op;
771 pub(crate) use display_variadic_op;
772 pub(crate) use op_all;
773 pub(crate) use op_impl;
774 pub(crate) use op0;
775 pub(crate) use op1;
776 pub(crate) use op2;
777 pub(crate) use op3;
778 pub(crate) use op4;
779 pub(crate) use op6;
780}