1use super::{ParseError, ParseResult};
57use crate::objects::Object;
58use std::collections::HashMap;
59
60#[derive(Debug, Clone, PartialEq)]
74pub enum MarkedContentValue {
75 String(Vec<u8>),
79 Integer(i64),
81 Real(f64),
83 Name(String),
85 Array(Vec<MarkedContentValue>),
87 Dict(HashMap<String, MarkedContentValue>),
89}
90
91#[derive(Debug, Clone, PartialEq)]
101pub enum MarkedContentProps {
102 Inline(HashMap<String, MarkedContentValue>),
103 ResourceRef(String),
104}
105
106#[derive(Debug, Clone, PartialEq)]
140pub enum ContentOperation {
141 BeginText,
145
146 EndText,
149
150 SetCharSpacing(f32),
154
155 SetWordSpacing(f32),
158
159 SetHorizontalScaling(f32),
162
163 SetLeading(f32),
166
167 SetFont(String, f32),
170
171 SetTextRenderMode(i32),
174
175 SetTextRise(f32),
178
179 MoveText(f32, f32),
183
184 MoveTextSetLeading(f32, f32),
187
188 SetTextMatrix(f32, f32, f32, f32, f32, f32),
191
192 NextLine,
195
196 ShowText(Vec<u8>),
200
201 ShowTextArray(Vec<TextElement>),
204
205 NextLineShowText(Vec<u8>),
208
209 SetSpacingNextLineShowText(f32, f32, Vec<u8>),
212
213 SaveGraphicsState,
217
218 RestoreGraphicsState,
221
222 SetTransformMatrix(f32, f32, f32, f32, f32, f32),
225
226 SetLineWidth(f32),
228
229 SetLineCap(i32),
232
233 SetLineJoin(i32),
236
237 SetMiterLimit(f32),
240
241 SetDashPattern(Vec<f32>, f32),
244
245 SetIntent(String),
248
249 SetFlatness(f32),
252
253 SetGraphicsStateParams(String),
256
257 MoveTo(f32, f32),
260
261 LineTo(f32, f32),
263
264 CurveTo(f32, f32, f32, f32, f32, f32),
267
268 CurveToV(f32, f32, f32, f32),
270
271 CurveToY(f32, f32, f32, f32),
273
274 ClosePath,
277
278 Rectangle(f32, f32, f32, f32),
281
282 Stroke,
285
286 CloseStroke,
289
290 Fill,
292
293 FillEvenOdd,
295
296 FillStroke,
299
300 FillStrokeEvenOdd,
302
303 CloseFillStroke,
306
307 CloseFillStrokeEvenOdd,
309
310 EndPath,
313
314 Clip, ClipEvenOdd, SetStrokingColorSpace(String),
322
323 SetNonStrokingColorSpace(String),
326
327 SetStrokingColor(Vec<f32>),
330
331 SetNonStrokingColor(Vec<f32>),
334
335 SetStrokingGray(f32),
338
339 SetNonStrokingGray(f32),
341
342 SetStrokingRGB(f32, f32, f32),
345
346 SetNonStrokingRGB(f32, f32, f32),
348
349 SetStrokingCMYK(f32, f32, f32, f32),
351
352 SetNonStrokingCMYK(f32, f32, f32, f32),
354
355 ShadingFill(String), BeginInlineImage,
361 InlineImage {
363 params: HashMap<String, Object>,
365 data: Vec<u8>,
367 },
368
369 PaintXObject(String),
373
374 BeginMarkedContent(String), BeginMarkedContentWithProps(String, MarkedContentProps), EndMarkedContent, DefineMarkedContentPoint(String), DefineMarkedContentPointWithProps(String, MarkedContentProps), BeginCompatibility, EndCompatibility, }
385
386#[derive(Debug, Clone, PartialEq)]
405pub enum TextElement {
406 Text(Vec<u8>),
408 Spacing(f32),
411}
412
413#[derive(Debug, Clone, PartialEq)]
415pub(super) enum Token {
416 Number(f32),
417 Integer(i32),
418 String(Vec<u8>),
419 HexString(Vec<u8>),
420 Name(String),
421 Operator(String),
422 ArrayStart,
423 ArrayEnd,
424 DictStart,
425 DictEnd,
426 InlineImageData(Vec<u8>),
430}
431
432pub struct ContentTokenizer<'a> {
434 input: &'a [u8],
435 position: usize,
436 in_inline_image: bool,
439}
440
441impl<'a> ContentTokenizer<'a> {
442 pub fn new(input: &'a [u8]) -> Self {
444 Self {
445 input,
446 position: 0,
447 in_inline_image: false,
448 }
449 }
450
451 pub(super) fn next_token(&mut self) -> ParseResult<Option<Token>> {
453 if self.in_inline_image {
455 self.in_inline_image = false;
456 return self.read_inline_image_data();
457 }
458
459 self.skip_whitespace();
460
461 if self.position >= self.input.len() {
462 return Ok(None);
463 }
464
465 let ch = self.input[self.position];
466
467 match ch {
468 b'+' | b'-' | b'.' | b'0'..=b'9' => self.read_number(),
470
471 b'(' => self.read_literal_string(),
473 b'<' => {
474 if self.peek_next() == Some(b'<') {
475 self.position += 2;
476 Ok(Some(Token::DictStart))
477 } else {
478 self.read_hex_string()
479 }
480 }
481 b'>' => {
482 if self.peek_next() == Some(b'>') {
483 self.position += 2;
484 Ok(Some(Token::DictEnd))
485 } else {
486 Err(ParseError::SyntaxError {
487 position: self.position,
488 message: "Unexpected '>'".to_string(),
489 })
490 }
491 }
492
493 b'[' => {
495 self.position += 1;
496 Ok(Some(Token::ArrayStart))
497 }
498 b']' => {
499 self.position += 1;
500 Ok(Some(Token::ArrayEnd))
501 }
502
503 b'/' => self.read_name(),
505
506 b';' | b')' | b'{' | b'}' => {
511 self.position += 1;
512 self.next_token() }
514
515 _ => {
517 let token = self.read_operator()?;
518 if let Some(Token::Operator(ref op)) = token {
520 if op == "ID" {
521 self.in_inline_image = true;
522 }
523 }
524 Ok(token)
525 }
526 }
527 }
528
529 fn skip_whitespace(&mut self) {
530 while self.position < self.input.len() {
531 match self.input[self.position] {
532 b' ' | b'\t' | b'\r' | b'\n' | b'\x0C' => self.position += 1,
533 b'%' => self.skip_comment(),
534 _ => break,
535 }
536 }
537 }
538
539 fn skip_comment(&mut self) {
540 while self.position < self.input.len() && self.input[self.position] != b'\n' {
541 self.position += 1;
542 }
543 }
544
545 fn peek_next(&self) -> Option<u8> {
546 if self.position + 1 < self.input.len() {
547 Some(self.input[self.position + 1])
548 } else {
549 None
550 }
551 }
552
553 fn read_number(&mut self) -> ParseResult<Option<Token>> {
554 let start = self.position;
555 let mut has_dot = false;
556
557 if self.position < self.input.len()
559 && (self.input[self.position] == b'+' || self.input[self.position] == b'-')
560 {
561 self.position += 1;
562 }
563
564 while self.position < self.input.len() {
566 match self.input[self.position] {
567 b'0'..=b'9' => self.position += 1,
568 b'.' if !has_dot => {
569 has_dot = true;
570 self.position += 1;
571 }
572 _ => break,
573 }
574 }
575
576 let num_str = std::str::from_utf8(&self.input[start..self.position]).map_err(|_| {
577 ParseError::SyntaxError {
578 position: start,
579 message: "Invalid number format".to_string(),
580 }
581 })?;
582
583 if has_dot {
584 let value = num_str
585 .parse::<f32>()
586 .map_err(|_| ParseError::SyntaxError {
587 position: start,
588 message: "Invalid float number".to_string(),
589 })?;
590 Ok(Some(Token::Number(value)))
591 } else {
592 let value = num_str
593 .parse::<i32>()
594 .map_err(|_| ParseError::SyntaxError {
595 position: start,
596 message: "Invalid integer number".to_string(),
597 })?;
598 Ok(Some(Token::Integer(value)))
599 }
600 }
601
602 fn read_literal_string(&mut self) -> ParseResult<Option<Token>> {
603 self.position += 1; let mut result = Vec::new();
605 let mut paren_depth = 1;
606 let mut escape = false;
607
608 while self.position < self.input.len() && paren_depth > 0 {
609 let ch = self.input[self.position];
610 self.position += 1;
611
612 if escape {
613 match ch {
614 b'n' => result.push(b'\n'),
615 b'r' => result.push(b'\r'),
616 b't' => result.push(b'\t'),
617 b'b' => result.push(b'\x08'),
618 b'f' => result.push(b'\x0C'),
619 b'(' => result.push(b'('),
620 b')' => result.push(b')'),
621 b'\\' => result.push(b'\\'),
622 b'0'..=b'7' => {
623 self.position -= 1;
625 let octal_value = self.read_octal_escape()?;
626 result.push(octal_value);
627 }
628 _ => result.push(ch), }
630 escape = false;
631 } else {
632 match ch {
633 b'\\' => escape = true,
634 b'(' => {
635 paren_depth += 1;
636 result.push(ch);
637 }
638 b')' => {
639 paren_depth -= 1;
640 if paren_depth > 0 {
641 result.push(ch);
642 }
643 }
644 _ => result.push(ch),
645 }
646 }
647 }
648
649 Ok(Some(Token::String(result)))
650 }
651
652 fn read_octal_escape(&mut self) -> ParseResult<u8> {
653 let mut value = 0u16;
656 let mut count = 0;
657
658 while count < 3 && self.position < self.input.len() {
659 match self.input[self.position] {
660 b'0'..=b'7' => {
661 value = value * 8 + u16::from(self.input[self.position] - b'0');
662 self.position += 1;
663 count += 1;
664 }
665 _ => break,
666 }
667 }
668
669 Ok(value as u8)
670 }
671
672 fn read_hex_string(&mut self) -> ParseResult<Option<Token>> {
673 self.position += 1; let mut result = Vec::new();
675 let mut nibble = None;
676
677 while self.position < self.input.len() {
678 let ch = self.input[self.position];
679
680 match ch {
681 b'>' => {
682 self.position += 1;
683 if let Some(n) = nibble {
685 result.push(n << 4);
686 }
687 return Ok(Some(Token::HexString(result)));
688 }
689 b'0'..=b'9' | b'A'..=b'F' | b'a'..=b'f' => {
690 let digit = if ch <= b'9' {
691 ch - b'0'
692 } else if ch <= b'F' {
693 ch - b'A' + 10
694 } else {
695 ch - b'a' + 10
696 };
697
698 if let Some(n) = nibble {
699 result.push((n << 4) | digit);
700 nibble = None;
701 } else {
702 nibble = Some(digit);
703 }
704 self.position += 1;
705 }
706 b' ' | b'\t' | b'\r' | b'\n' | b'\x0C' => {
707 self.position += 1;
709 }
710 _ => {
711 return Err(ParseError::SyntaxError {
712 position: self.position,
713 message: format!("Invalid character in hex string: {:?}", ch as char),
714 });
715 }
716 }
717 }
718
719 Err(ParseError::SyntaxError {
720 position: self.position,
721 message: "Unterminated hex string".to_string(),
722 })
723 }
724
725 fn read_name(&mut self) -> ParseResult<Option<Token>> {
726 self.position += 1; let start = self.position;
728
729 while self.position < self.input.len() {
730 let ch = self.input[self.position];
731 match ch {
732 b' ' | b'\t' | b'\r' | b'\n' | b'\x0C' | b'(' | b')' | b'<' | b'>' | b'['
733 | b']' | b'{' | b'}' | b'/' | b'%' => break,
734 b'#' => {
735 self.position += 1;
737 if self.position + 1 < self.input.len() {
738 self.position += 2;
739 }
740 }
741 _ => self.position += 1,
742 }
743 }
744
745 let name_bytes = &self.input[start..self.position];
746 let name = self.decode_name(name_bytes)?;
747 Ok(Some(Token::Name(name)))
748 }
749
750 fn decode_name(&self, bytes: &[u8]) -> ParseResult<String> {
751 let mut result = Vec::new();
752 let mut i = 0;
753
754 while i < bytes.len() {
755 if bytes[i] == b'#' && i + 2 < bytes.len() {
756 let hex_str = std::str::from_utf8(&bytes[i + 1..i + 3]).map_err(|_| {
758 ParseError::SyntaxError {
759 position: self.position,
760 message: "Invalid hex escape in name".to_string(),
761 }
762 })?;
763 let value =
764 u8::from_str_radix(hex_str, 16).map_err(|_| ParseError::SyntaxError {
765 position: self.position,
766 message: "Invalid hex escape in name".to_string(),
767 })?;
768 result.push(value);
769 i += 3;
770 } else {
771 result.push(bytes[i]);
772 i += 1;
773 }
774 }
775
776 String::from_utf8(result).map_err(|_| ParseError::SyntaxError {
777 position: self.position,
778 message: "Invalid UTF-8 in name".to_string(),
779 })
780 }
781
782 fn read_operator(&mut self) -> ParseResult<Option<Token>> {
783 let start = self.position;
784
785 while self.position < self.input.len() {
786 let ch = self.input[self.position];
787 match ch {
788 b' ' | b'\t' | b'\r' | b'\n' | b'\x0C' | b'(' | b')' | b'<' | b'>' | b'['
789 | b']' | b'{' | b'}' | b'/' | b'%' | b';' => break,
790 _ => self.position += 1,
791 }
792 }
793
794 let op_bytes = &self.input[start..self.position];
795 let op = std::str::from_utf8(op_bytes).map_err(|_| ParseError::SyntaxError {
796 position: start,
797 message: "Invalid operator".to_string(),
798 })?;
799
800 Ok(Some(Token::Operator(op.to_string())))
801 }
802
803 fn read_inline_image_data(&mut self) -> ParseResult<Option<Token>> {
809 if self.position < self.input.len() {
811 let ch = self.input[self.position];
812 if ch == b' ' || ch == b'\n' || ch == b'\r' || ch == b'\t' {
813 self.position += 1;
814 if ch == b'\r'
816 && self.position < self.input.len()
817 && self.input[self.position] == b'\n'
818 {
819 self.position += 1;
820 }
821 }
822 }
823
824 let start = self.position;
825
826 while self.position + 1 < self.input.len() {
828 let preceded_by_whitespace = self.position == start
829 || matches!(
830 self.input[self.position - 1],
831 b' ' | b'\t' | b'\r' | b'\n' | b'\x0C'
832 );
833
834 if preceded_by_whitespace
835 && self.input[self.position] == b'E'
836 && self.input[self.position + 1] == b'I'
837 {
838 let after_ei = self.position + 2;
839 let followed_by_boundary = after_ei >= self.input.len()
840 || matches!(
841 self.input[after_ei],
842 b' ' | b'\t' | b'\r' | b'\n' | b'\x0C' | b'/' | b'<' | b'(' | b'[' | b'%'
843 );
844
845 if followed_by_boundary {
846 let mut end = self.position;
848 if end > start
849 && matches!(self.input[end - 1], b' ' | b'\t' | b'\r' | b'\n' | b'\x0C')
850 {
851 end -= 1;
852 }
853 let data = self.input[start..end].to_vec();
854 self.position = after_ei; return Ok(Some(Token::InlineImageData(data)));
856 }
857 }
858 self.position += 1;
859 }
860
861 let data = self.input[start..].to_vec();
863 self.position = self.input.len();
864 Ok(Some(Token::InlineImageData(data)))
865 }
866}
867
868pub struct ContentParser {
887 tokens: Vec<Token>,
888 position: usize,
889}
890
891impl ContentParser {
892 pub fn new(_content: &[u8]) -> Self {
894 Self {
895 tokens: Vec::new(),
896 position: 0,
897 }
898 }
899
900 pub fn parse(content: &[u8]) -> ParseResult<Vec<ContentOperation>> {
934 Self::parse_content(content)
935 }
936
937 pub fn parse_content(content: &[u8]) -> ParseResult<Vec<ContentOperation>> {
942 let mut tokenizer = ContentTokenizer::new(content);
943 let mut tokens = Vec::new();
944
945 while let Some(token) = tokenizer.next_token()? {
947 tokens.push(token);
948 }
949
950 let mut parser = Self {
951 tokens,
952 position: 0,
953 };
954
955 parser.parse_operators()
956 }
957
958 fn parse_operators(&mut self) -> ParseResult<Vec<ContentOperation>> {
959 let mut operators = Vec::new();
960 let mut operand_stack: Vec<Token> = Vec::new();
961
962 while self.position < self.tokens.len() {
963 let token = self.tokens[self.position].clone();
964 self.position += 1;
965
966 match &token {
967 Token::Operator(op) => {
968 let operator = self.parse_operator(op, &mut operand_stack)?;
969 operators.push(operator);
970 }
971 _ => {
972 operand_stack.push(token);
974 }
975 }
976 }
977
978 Ok(operators)
979 }
980
981 fn parse_operator(
982 &mut self,
983 op: &str,
984 operands: &mut Vec<Token>,
985 ) -> ParseResult<ContentOperation> {
986 let operator = match op {
987 "BT" => ContentOperation::BeginText,
989 "ET" => ContentOperation::EndText,
990
991 "Tc" => {
993 let spacing = self.pop_number(operands)?;
994 ContentOperation::SetCharSpacing(spacing)
995 }
996 "Tw" => {
997 let spacing = self.pop_number(operands)?;
998 ContentOperation::SetWordSpacing(spacing)
999 }
1000 "Tz" => {
1001 let scale = self.pop_number(operands)?;
1002 ContentOperation::SetHorizontalScaling(scale)
1003 }
1004 "TL" => {
1005 let leading = self.pop_number(operands)?;
1006 ContentOperation::SetLeading(leading)
1007 }
1008 "Tf" => {
1009 let size = self.pop_number(operands)?;
1010 let font = self.pop_name(operands)?;
1011 ContentOperation::SetFont(font, size)
1012 }
1013 "Tr" => {
1014 let mode = self.pop_integer(operands)?;
1015 ContentOperation::SetTextRenderMode(mode)
1016 }
1017 "Ts" => {
1018 let rise = self.pop_number(operands)?;
1019 ContentOperation::SetTextRise(rise)
1020 }
1021
1022 "Td" => {
1024 let ty = self.pop_number(operands)?;
1025 let tx = self.pop_number(operands)?;
1026 ContentOperation::MoveText(tx, ty)
1027 }
1028 "TD" => {
1029 let ty = self.pop_number(operands)?;
1030 let tx = self.pop_number(operands)?;
1031 ContentOperation::MoveTextSetLeading(tx, ty)
1032 }
1033 "Tm" => {
1034 let f = self.pop_number(operands)?;
1035 let e = self.pop_number(operands)?;
1036 let d = self.pop_number(operands)?;
1037 let c = self.pop_number(operands)?;
1038 let b = self.pop_number(operands)?;
1039 let a = self.pop_number(operands)?;
1040 ContentOperation::SetTextMatrix(a, b, c, d, e, f)
1041 }
1042 "T*" => ContentOperation::NextLine,
1043
1044 "Tj" => {
1046 let text = self.pop_string(operands)?;
1047 ContentOperation::ShowText(text)
1048 }
1049 "TJ" => {
1050 let array = self.pop_array(operands)?;
1051 let elements = self.parse_text_array(array)?;
1052 ContentOperation::ShowTextArray(elements)
1053 }
1054 "'" => {
1055 let text = self.pop_string(operands)?;
1056 ContentOperation::NextLineShowText(text)
1057 }
1058 "\"" => {
1059 let text = self.pop_string(operands)?;
1065 let ac = self.pop_number(operands)?;
1066 let aw = self.pop_number(operands)?;
1067 ContentOperation::SetSpacingNextLineShowText(aw, ac, text)
1068 }
1069
1070 "q" => ContentOperation::SaveGraphicsState,
1072 "Q" => ContentOperation::RestoreGraphicsState,
1073 "cm" => {
1074 let f = self.pop_number(operands)?;
1075 let e = self.pop_number(operands)?;
1076 let d = self.pop_number(operands)?;
1077 let c = self.pop_number(operands)?;
1078 let b = self.pop_number(operands)?;
1079 let a = self.pop_number(operands)?;
1080 ContentOperation::SetTransformMatrix(a, b, c, d, e, f)
1081 }
1082 "w" => {
1083 let width = self.pop_number(operands)?;
1084 ContentOperation::SetLineWidth(width)
1085 }
1086 "J" => {
1087 let cap = self.pop_integer(operands)?;
1088 ContentOperation::SetLineCap(cap)
1089 }
1090 "j" => {
1091 let join = self.pop_integer(operands)?;
1092 ContentOperation::SetLineJoin(join)
1093 }
1094 "M" => {
1095 let limit = self.pop_number(operands)?;
1096 ContentOperation::SetMiterLimit(limit)
1097 }
1098 "d" => {
1099 let phase = self.pop_number(operands)?;
1100 let array = self.pop_array(operands)?;
1101 let pattern = self.parse_dash_array(array)?;
1102 ContentOperation::SetDashPattern(pattern, phase)
1103 }
1104 "ri" => {
1105 let intent = self.pop_name(operands)?;
1106 ContentOperation::SetIntent(intent)
1107 }
1108 "i" => {
1109 let flatness = self.pop_number(operands)?;
1110 ContentOperation::SetFlatness(flatness)
1111 }
1112 "gs" => {
1113 let name = self.pop_name(operands)?;
1114 ContentOperation::SetGraphicsStateParams(name)
1115 }
1116
1117 "m" => {
1119 let y = self.pop_number(operands)?;
1120 let x = self.pop_number(operands)?;
1121 ContentOperation::MoveTo(x, y)
1122 }
1123 "l" => {
1124 let y = self.pop_number(operands)?;
1125 let x = self.pop_number(operands)?;
1126 ContentOperation::LineTo(x, y)
1127 }
1128 "c" => {
1129 let y3 = self.pop_number(operands)?;
1130 let x3 = self.pop_number(operands)?;
1131 let y2 = self.pop_number(operands)?;
1132 let x2 = self.pop_number(operands)?;
1133 let y1 = self.pop_number(operands)?;
1134 let x1 = self.pop_number(operands)?;
1135 ContentOperation::CurveTo(x1, y1, x2, y2, x3, y3)
1136 }
1137 "v" => {
1138 let y3 = self.pop_number(operands)?;
1139 let x3 = self.pop_number(operands)?;
1140 let y2 = self.pop_number(operands)?;
1141 let x2 = self.pop_number(operands)?;
1142 ContentOperation::CurveToV(x2, y2, x3, y3)
1143 }
1144 "y" => {
1145 let y3 = self.pop_number(operands)?;
1146 let x3 = self.pop_number(operands)?;
1147 let y1 = self.pop_number(operands)?;
1148 let x1 = self.pop_number(operands)?;
1149 ContentOperation::CurveToY(x1, y1, x3, y3)
1150 }
1151 "h" => ContentOperation::ClosePath,
1152 "re" => {
1153 let height = self.pop_number(operands)?;
1154 let width = self.pop_number(operands)?;
1155 let y = self.pop_number(operands)?;
1156 let x = self.pop_number(operands)?;
1157 ContentOperation::Rectangle(x, y, width, height)
1158 }
1159
1160 "S" => ContentOperation::Stroke,
1162 "s" => ContentOperation::CloseStroke,
1163 "f" | "F" => ContentOperation::Fill,
1164 "f*" => ContentOperation::FillEvenOdd,
1165 "B" => ContentOperation::FillStroke,
1166 "B*" => ContentOperation::FillStrokeEvenOdd,
1167 "b" => ContentOperation::CloseFillStroke,
1168 "b*" => ContentOperation::CloseFillStrokeEvenOdd,
1169 "n" => ContentOperation::EndPath,
1170
1171 "W" => ContentOperation::Clip,
1173 "W*" => ContentOperation::ClipEvenOdd,
1174
1175 "CS" => {
1177 let name = self.pop_name(operands)?;
1178 ContentOperation::SetStrokingColorSpace(name)
1179 }
1180 "cs" => {
1181 let name = self.pop_name(operands)?;
1182 ContentOperation::SetNonStrokingColorSpace(name)
1183 }
1184 "SC" | "SCN" => {
1185 let components = self.pop_color_components(operands)?;
1186 ContentOperation::SetStrokingColor(components)
1187 }
1188 "sc" | "scn" => {
1189 let components = self.pop_color_components(operands)?;
1190 ContentOperation::SetNonStrokingColor(components)
1191 }
1192 "G" => {
1193 let gray = self.pop_number(operands)?;
1194 ContentOperation::SetStrokingGray(gray)
1195 }
1196 "g" => {
1197 let gray = self.pop_number(operands)?;
1198 ContentOperation::SetNonStrokingGray(gray)
1199 }
1200 "RG" => {
1201 let b = self.pop_number(operands)?;
1202 let g = self.pop_number(operands)?;
1203 let r = self.pop_number(operands)?;
1204 ContentOperation::SetStrokingRGB(r, g, b)
1205 }
1206 "rg" => {
1207 let b = self.pop_number(operands)?;
1208 let g = self.pop_number(operands)?;
1209 let r = self.pop_number(operands)?;
1210 ContentOperation::SetNonStrokingRGB(r, g, b)
1211 }
1212 "K" => {
1213 let k = self.pop_number(operands)?;
1214 let y = self.pop_number(operands)?;
1215 let m = self.pop_number(operands)?;
1216 let c = self.pop_number(operands)?;
1217 ContentOperation::SetStrokingCMYK(c, m, y, k)
1218 }
1219 "k" => {
1220 let k = self.pop_number(operands)?;
1221 let y = self.pop_number(operands)?;
1222 let m = self.pop_number(operands)?;
1223 let c = self.pop_number(operands)?;
1224 ContentOperation::SetNonStrokingCMYK(c, m, y, k)
1225 }
1226
1227 "sh" => {
1229 let name = self.pop_name(operands)?;
1230 ContentOperation::ShadingFill(name)
1231 }
1232
1233 "Do" => {
1235 let name = self.pop_name(operands)?;
1236 ContentOperation::PaintXObject(name)
1237 }
1238
1239 "BMC" => {
1241 let tag = self.pop_name(operands)?;
1242 ContentOperation::BeginMarkedContent(tag)
1243 }
1244 "BDC" => {
1245 let props = self.pop_dict_or_name(operands)?;
1246 let tag = self.pop_name(operands)?;
1247 ContentOperation::BeginMarkedContentWithProps(tag, props)
1248 }
1249 "EMC" => ContentOperation::EndMarkedContent,
1250 "MP" => {
1251 let tag = self.pop_name(operands)?;
1252 ContentOperation::DefineMarkedContentPoint(tag)
1253 }
1254 "DP" => {
1255 let props = self.pop_dict_or_name(operands)?;
1256 let tag = self.pop_name(operands)?;
1257 ContentOperation::DefineMarkedContentPointWithProps(tag, props)
1258 }
1259
1260 "BX" => ContentOperation::BeginCompatibility,
1262 "EX" => ContentOperation::EndCompatibility,
1263
1264 "BI" => {
1266 operands.clear(); self.parse_inline_image()?
1268 }
1269
1270 _ => {
1271 return Err(ParseError::SyntaxError {
1272 position: self.position,
1273 message: format!("Unknown operator: {op}"),
1274 });
1275 }
1276 };
1277
1278 operands.clear(); Ok(operator)
1280 }
1281
1282 fn pop_number(&self, operands: &mut Vec<Token>) -> ParseResult<f32> {
1284 match operands.pop() {
1285 Some(Token::Number(n)) => Ok(n),
1286 Some(Token::Integer(i)) => Ok(i as f32),
1287 _ => Err(ParseError::SyntaxError {
1288 position: self.position,
1289 message: "Expected number operand".to_string(),
1290 }),
1291 }
1292 }
1293
1294 fn pop_integer(&self, operands: &mut Vec<Token>) -> ParseResult<i32> {
1295 match operands.pop() {
1296 Some(Token::Integer(i)) => Ok(i),
1297 _ => Err(ParseError::SyntaxError {
1298 position: self.position,
1299 message: "Expected integer operand".to_string(),
1300 }),
1301 }
1302 }
1303
1304 fn pop_name(&self, operands: &mut Vec<Token>) -> ParseResult<String> {
1305 match operands.pop() {
1306 Some(Token::Name(n)) => Ok(n),
1307 _ => Err(ParseError::SyntaxError {
1308 position: self.position,
1309 message: "Expected name operand".to_string(),
1310 }),
1311 }
1312 }
1313
1314 fn pop_string(&self, operands: &mut Vec<Token>) -> ParseResult<Vec<u8>> {
1315 match operands.pop() {
1316 Some(Token::String(s)) => Ok(s),
1317 Some(Token::HexString(s)) => Ok(s),
1318 _ => Err(ParseError::SyntaxError {
1319 position: self.position,
1320 message: "Expected string operand".to_string(),
1321 }),
1322 }
1323 }
1324
1325 fn pop_array(&self, operands: &mut Vec<Token>) -> ParseResult<Vec<Token>> {
1326 let has_array_end = matches!(operands.last(), Some(Token::ArrayEnd));
1328 if has_array_end {
1329 operands.pop(); }
1331
1332 let mut array = Vec::new();
1333 let mut found_start = false;
1334
1335 while let Some(token) = operands.pop() {
1337 match token {
1338 Token::ArrayStart => {
1339 found_start = true;
1340 break;
1341 }
1342 Token::ArrayEnd => {
1343 continue;
1345 }
1346 _ => array.push(token),
1347 }
1348 }
1349
1350 if !found_start {
1351 return Err(ParseError::SyntaxError {
1352 position: self.position,
1353 message: "Expected array".to_string(),
1354 });
1355 }
1356
1357 array.reverse(); Ok(array)
1359 }
1360
1361 fn pop_dict_or_name(&self, operands: &mut Vec<Token>) -> ParseResult<MarkedContentProps> {
1362 let token = operands.pop().ok_or_else(|| ParseError::SyntaxError {
1363 position: self.position,
1364 message: "Expected dict or name operand for BDC/DP".to_string(),
1365 })?;
1366
1367 match token {
1368 Token::Name(name) => Ok(MarkedContentProps::ResourceRef(name)),
1369 Token::DictEnd => {
1370 let mut map: HashMap<String, MarkedContentValue> = HashMap::new();
1374 loop {
1375 let next = operands.pop().ok_or_else(|| ParseError::SyntaxError {
1376 position: self.position,
1377 message: "Unterminated inline dict in BDC/DP".to_string(),
1378 })?;
1379 if matches!(next, Token::DictStart) {
1380 break;
1381 }
1382 let value = Self::token_to_mc_value(next, operands)?;
1383 let key = match operands.pop() {
1384 Some(Token::Name(k)) => k,
1385 Some(other) => {
1386 return Err(ParseError::SyntaxError {
1387 position: self.position,
1388 message: format!(
1389 "Expected Name as inline dict key, got {:?}",
1390 other
1391 ),
1392 });
1393 }
1394 None => {
1395 return Err(ParseError::SyntaxError {
1396 position: self.position,
1397 message: "Unterminated inline dict (missing key)".to_string(),
1398 });
1399 }
1400 };
1401 map.insert(key, value);
1402 }
1403 Ok(MarkedContentProps::Inline(map))
1404 }
1405 other => Err(ParseError::SyntaxError {
1406 position: self.position,
1407 message: format!("Expected name or inline dict for BDC/DP, got {:?}", other),
1408 }),
1409 }
1410 }
1411
1412 fn token_to_mc_value(
1416 token: Token,
1417 operands: &mut Vec<Token>,
1418 ) -> ParseResult<MarkedContentValue> {
1419 match token {
1420 Token::String(b) | Token::HexString(b) => Ok(MarkedContentValue::String(b)),
1421 Token::Integer(i) => Ok(MarkedContentValue::Integer(i as i64)),
1422 Token::Number(f) => Ok(MarkedContentValue::Real(f as f64)),
1423 Token::Name(n) => Ok(MarkedContentValue::Name(n)),
1424 Token::ArrayEnd => {
1425 let mut items: Vec<MarkedContentValue> = Vec::new();
1426 loop {
1427 let next = operands.pop().ok_or_else(|| ParseError::SyntaxError {
1428 position: 0,
1429 message: "Unterminated array in marked-content props".to_string(),
1430 })?;
1431 if matches!(next, Token::ArrayStart) {
1432 break;
1433 }
1434 items.push(Self::token_to_mc_value(next, operands)?);
1435 }
1436 items.reverse();
1437 Ok(MarkedContentValue::Array(items))
1438 }
1439 Token::DictEnd => {
1440 let mut nested: HashMap<String, MarkedContentValue> = HashMap::new();
1441 loop {
1442 let next = operands.pop().ok_or_else(|| ParseError::SyntaxError {
1443 position: 0,
1444 message: "Unterminated nested dict in marked-content props".to_string(),
1445 })?;
1446 if matches!(next, Token::DictStart) {
1447 break;
1448 }
1449 let value = Self::token_to_mc_value(next, operands)?;
1450 let key = match operands.pop() {
1451 Some(Token::Name(k)) => k,
1452 _ => {
1453 return Err(ParseError::SyntaxError {
1454 position: 0,
1455 message: "Expected name key in nested dict".to_string(),
1456 });
1457 }
1458 };
1459 nested.insert(key, value);
1460 }
1461 Ok(MarkedContentValue::Dict(nested))
1462 }
1463 other => Err(ParseError::SyntaxError {
1464 position: 0,
1465 message: format!("Unexpected token type in marked-content value: {:?}", other),
1466 }),
1467 }
1468 }
1469
1470 fn pop_color_components(&self, operands: &mut Vec<Token>) -> ParseResult<Vec<f32>> {
1471 let mut components = Vec::new();
1472
1473 while let Some(token) = operands.last() {
1475 match token {
1476 Token::Number(n) => {
1477 components.push(*n);
1478 operands.pop();
1479 }
1480 Token::Integer(i) => {
1481 components.push(*i as f32);
1482 operands.pop();
1483 }
1484 _ => break,
1485 }
1486 }
1487
1488 components.reverse();
1489 Ok(components)
1490 }
1491
1492 fn parse_text_array(&self, tokens: Vec<Token>) -> ParseResult<Vec<TextElement>> {
1493 let mut elements = Vec::new();
1494
1495 for token in tokens {
1496 match token {
1497 Token::String(s) | Token::HexString(s) => {
1498 elements.push(TextElement::Text(s));
1499 }
1500 Token::Number(n) => {
1501 elements.push(TextElement::Spacing(n));
1502 }
1503 Token::Integer(i) => {
1504 elements.push(TextElement::Spacing(i as f32));
1505 }
1506 _ => {
1507 return Err(ParseError::SyntaxError {
1508 position: self.position,
1509 message: "Invalid element in text array".to_string(),
1510 });
1511 }
1512 }
1513 }
1514
1515 Ok(elements)
1516 }
1517
1518 fn parse_dash_array(&self, tokens: Vec<Token>) -> ParseResult<Vec<f32>> {
1519 let mut pattern = Vec::new();
1520
1521 for token in tokens {
1522 match token {
1523 Token::Number(n) => pattern.push(n),
1524 Token::Integer(i) => pattern.push(i as f32),
1525 _ => {
1526 return Err(ParseError::SyntaxError {
1527 position: self.position,
1528 message: "Invalid element in dash array".to_string(),
1529 });
1530 }
1531 }
1532 }
1533
1534 Ok(pattern)
1535 }
1536
1537 fn parse_inline_image(&mut self) -> ParseResult<ContentOperation> {
1538 let mut params = HashMap::new();
1540
1541 while self.position < self.tokens.len() {
1542 if let Token::Operator(op) = &self.tokens[self.position] {
1544 if op == "ID" {
1545 self.position += 1;
1546 break;
1547 }
1548 }
1549
1550 if let Token::Name(key) = &self.tokens[self.position] {
1555 self.position += 1;
1556 if self.position >= self.tokens.len() {
1557 break;
1558 }
1559
1560 let value = match &self.tokens[self.position] {
1562 Token::Integer(n) => Object::Integer(*n as i64),
1563 Token::Number(n) => Object::Real(*n as f64),
1564 Token::Name(s) => Object::Name(expand_inline_name(s)),
1565 Token::String(s) => Object::String(String::from_utf8_lossy(s).to_string()),
1566 Token::HexString(s) => Object::String(String::from_utf8_lossy(s).to_string()),
1567 _ => Object::Null,
1568 };
1569
1570 let full_key = expand_inline_key(key);
1572 params.insert(full_key, value);
1573 self.position += 1;
1574 } else {
1575 self.position += 1;
1576 }
1577 }
1578
1579 let data = if self.position < self.tokens.len() {
1582 if let Token::InlineImageData(bytes) = &self.tokens[self.position] {
1583 let d = bytes.clone();
1584 self.position += 1;
1585 d
1586 } else {
1587 self.collect_inline_image_data_from_tokens()?
1589 }
1590 } else {
1591 Vec::new()
1592 };
1593
1594 Ok(ContentOperation::InlineImage { params, data })
1595 }
1596
1597 fn collect_inline_image_data_from_tokens(&mut self) -> ParseResult<Vec<u8>> {
1600 let mut data = Vec::new();
1601 while self.position < self.tokens.len() {
1602 if let Token::Operator(op) = &self.tokens[self.position] {
1603 if op == "EI" {
1604 self.position += 1;
1605 break;
1606 }
1607 }
1608 match &self.tokens[self.position] {
1609 Token::String(bytes) | Token::HexString(bytes) => {
1610 data.extend_from_slice(bytes);
1611 }
1612 Token::Integer(n) => data.extend_from_slice(n.to_string().as_bytes()),
1613 Token::Number(n) => data.extend_from_slice(n.to_string().as_bytes()),
1614 Token::Name(s) | Token::Operator(s) => data.extend_from_slice(s.as_bytes()),
1615 _ => {}
1616 }
1617 self.position += 1;
1618 }
1619 Ok(data)
1620 }
1621}
1622
1623fn expand_inline_key(key: &str) -> String {
1625 match key {
1626 "W" => "Width".to_string(),
1627 "H" => "Height".to_string(),
1628 "CS" | "ColorSpace" => "ColorSpace".to_string(),
1629 "BPC" | "BitsPerComponent" => "BitsPerComponent".to_string(),
1630 "F" => "Filter".to_string(),
1631 "DP" | "DecodeParms" => "DecodeParms".to_string(),
1632 "IM" => "ImageMask".to_string(),
1633 "I" => "Interpolate".to_string(),
1634 "Intent" => "Intent".to_string(),
1635 "D" => "Decode".to_string(),
1636 _ => key.to_string(),
1637 }
1638}
1639
1640fn expand_inline_name(name: &str) -> String {
1642 match name {
1643 "G" => "DeviceGray".to_string(),
1644 "RGB" => "DeviceRGB".to_string(),
1645 "CMYK" => "DeviceCMYK".to_string(),
1646 "I" => "Indexed".to_string(),
1647 "AHx" => "ASCIIHexDecode".to_string(),
1648 "A85" => "ASCII85Decode".to_string(),
1649 "LZW" => "LZWDecode".to_string(),
1650 "Fl" => "FlateDecode".to_string(),
1651 "RL" => "RunLengthDecode".to_string(),
1652 "DCT" => "DCTDecode".to_string(),
1653 "CCF" => "CCITTFaxDecode".to_string(),
1654 _ => name.to_string(),
1655 }
1656}
1657
1658#[cfg(test)]
1659mod tests {
1660 use super::*;
1661
1662 #[test]
1663 fn test_tokenize_numbers() {
1664 let input = b"123 -45 3.14159 -0.5 .5";
1665 let mut tokenizer = ContentTokenizer::new(input);
1666
1667 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Integer(123)));
1668 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Integer(-45)));
1669 assert_eq!(
1670 tokenizer.next_token().unwrap(),
1671 Some(Token::Number(3.14159))
1672 );
1673 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(-0.5)));
1674 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(0.5)));
1675 assert_eq!(tokenizer.next_token().unwrap(), None);
1676 }
1677
1678 #[test]
1679 fn test_tokenize_strings() {
1680 let input = b"(Hello World) (Hello\\nWorld) (Nested (paren))";
1681 let mut tokenizer = ContentTokenizer::new(input);
1682
1683 assert_eq!(
1684 tokenizer.next_token().unwrap(),
1685 Some(Token::String(b"Hello World".to_vec()))
1686 );
1687 assert_eq!(
1688 tokenizer.next_token().unwrap(),
1689 Some(Token::String(b"Hello\nWorld".to_vec()))
1690 );
1691 assert_eq!(
1692 tokenizer.next_token().unwrap(),
1693 Some(Token::String(b"Nested (paren)".to_vec()))
1694 );
1695 }
1696
1697 #[test]
1698 fn test_tokenize_hex_strings() {
1699 let input = b"<48656C6C6F> <48 65 6C 6C 6F>";
1700 let mut tokenizer = ContentTokenizer::new(input);
1701
1702 assert_eq!(
1703 tokenizer.next_token().unwrap(),
1704 Some(Token::HexString(b"Hello".to_vec()))
1705 );
1706 assert_eq!(
1707 tokenizer.next_token().unwrap(),
1708 Some(Token::HexString(b"Hello".to_vec()))
1709 );
1710 }
1711
1712 #[test]
1713 fn test_tokenize_names() {
1714 let input = b"/Name /Name#20with#20spaces /A#42C";
1715 let mut tokenizer = ContentTokenizer::new(input);
1716
1717 assert_eq!(
1718 tokenizer.next_token().unwrap(),
1719 Some(Token::Name("Name".to_string()))
1720 );
1721 assert_eq!(
1722 tokenizer.next_token().unwrap(),
1723 Some(Token::Name("Name with spaces".to_string()))
1724 );
1725 assert_eq!(
1726 tokenizer.next_token().unwrap(),
1727 Some(Token::Name("ABC".to_string()))
1728 );
1729 }
1730
1731 #[test]
1732 fn test_tokenize_operators() {
1733 let input = b"BT Tj ET q Q";
1734 let mut tokenizer = ContentTokenizer::new(input);
1735
1736 assert_eq!(
1737 tokenizer.next_token().unwrap(),
1738 Some(Token::Operator("BT".to_string()))
1739 );
1740 assert_eq!(
1741 tokenizer.next_token().unwrap(),
1742 Some(Token::Operator("Tj".to_string()))
1743 );
1744 assert_eq!(
1745 tokenizer.next_token().unwrap(),
1746 Some(Token::Operator("ET".to_string()))
1747 );
1748 assert_eq!(
1749 tokenizer.next_token().unwrap(),
1750 Some(Token::Operator("q".to_string()))
1751 );
1752 assert_eq!(
1753 tokenizer.next_token().unwrap(),
1754 Some(Token::Operator("Q".to_string()))
1755 );
1756 }
1757
1758 #[test]
1759 fn test_parse_text_operators() {
1760 let content = b"BT /F1 12 Tf 100 200 Td (Hello World) Tj ET";
1761 let operators = ContentParser::parse(content).unwrap();
1762
1763 assert_eq!(operators.len(), 5);
1764 assert_eq!(operators[0], ContentOperation::BeginText);
1765 assert_eq!(
1766 operators[1],
1767 ContentOperation::SetFont("F1".to_string(), 12.0)
1768 );
1769 assert_eq!(operators[2], ContentOperation::MoveText(100.0, 200.0));
1770 assert_eq!(
1771 operators[3],
1772 ContentOperation::ShowText(b"Hello World".to_vec())
1773 );
1774 assert_eq!(operators[4], ContentOperation::EndText);
1775 }
1776
1777 #[test]
1778 fn test_parse_graphics_operators() {
1779 let content = b"q 1 0 0 1 50 50 cm 2 w 0 0 100 100 re S Q";
1780 let operators = ContentParser::parse(content).unwrap();
1781
1782 assert_eq!(operators.len(), 6);
1783 assert_eq!(operators[0], ContentOperation::SaveGraphicsState);
1784 assert_eq!(
1785 operators[1],
1786 ContentOperation::SetTransformMatrix(1.0, 0.0, 0.0, 1.0, 50.0, 50.0)
1787 );
1788 assert_eq!(operators[2], ContentOperation::SetLineWidth(2.0));
1789 assert_eq!(
1790 operators[3],
1791 ContentOperation::Rectangle(0.0, 0.0, 100.0, 100.0)
1792 );
1793 assert_eq!(operators[4], ContentOperation::Stroke);
1794 assert_eq!(operators[5], ContentOperation::RestoreGraphicsState);
1795 }
1796
1797 #[test]
1798 fn test_parse_color_operators() {
1799 let content = b"0.5 g 1 0 0 rg 0 0 0 1 k";
1800 let operators = ContentParser::parse(content).unwrap();
1801
1802 assert_eq!(operators.len(), 3);
1803 assert_eq!(operators[0], ContentOperation::SetNonStrokingGray(0.5));
1804 assert_eq!(
1805 operators[1],
1806 ContentOperation::SetNonStrokingRGB(1.0, 0.0, 0.0)
1807 );
1808 assert_eq!(
1809 operators[2],
1810 ContentOperation::SetNonStrokingCMYK(0.0, 0.0, 0.0, 1.0)
1811 );
1812 }
1813
1814 mod comprehensive_tests {
1816 use super::*;
1817
1818 #[test]
1819 fn test_all_text_operators() {
1820 let content = b"BT 5 Tc 10 Tw 120 Tz 15 TL /F1 12 Tf 1 Tr 5 Ts 100 200 Td 50 150 TD T* (Hello) Tj ET";
1822 let operators = ContentParser::parse(content).unwrap();
1823
1824 assert_eq!(operators[0], ContentOperation::BeginText);
1825 assert_eq!(operators[1], ContentOperation::SetCharSpacing(5.0));
1826 assert_eq!(operators[2], ContentOperation::SetWordSpacing(10.0));
1827 assert_eq!(operators[3], ContentOperation::SetHorizontalScaling(120.0));
1828 assert_eq!(operators[4], ContentOperation::SetLeading(15.0));
1829 assert_eq!(
1830 operators[5],
1831 ContentOperation::SetFont("F1".to_string(), 12.0)
1832 );
1833 assert_eq!(operators[6], ContentOperation::SetTextRenderMode(1));
1834 assert_eq!(operators[7], ContentOperation::SetTextRise(5.0));
1835 assert_eq!(operators[8], ContentOperation::MoveText(100.0, 200.0));
1836 assert_eq!(
1837 operators[9],
1838 ContentOperation::MoveTextSetLeading(50.0, 150.0)
1839 );
1840 assert_eq!(operators[10], ContentOperation::NextLine);
1841 assert_eq!(operators[11], ContentOperation::ShowText(b"Hello".to_vec()));
1842 assert_eq!(operators[12], ContentOperation::EndText);
1843 }
1844
1845 #[test]
1846 fn test_all_graphics_state_operators() {
1847 let content = b"q Q 1 0 0 1 50 50 cm 2 w 1 J 2 j 10 M /GS1 gs 0.5 i /Perceptual ri";
1849 let operators = ContentParser::parse(content).unwrap();
1850
1851 assert_eq!(operators[0], ContentOperation::SaveGraphicsState);
1852 assert_eq!(operators[1], ContentOperation::RestoreGraphicsState);
1853 assert_eq!(
1854 operators[2],
1855 ContentOperation::SetTransformMatrix(1.0, 0.0, 0.0, 1.0, 50.0, 50.0)
1856 );
1857 assert_eq!(operators[3], ContentOperation::SetLineWidth(2.0));
1858 assert_eq!(operators[4], ContentOperation::SetLineCap(1));
1859 assert_eq!(operators[5], ContentOperation::SetLineJoin(2));
1860 assert_eq!(operators[6], ContentOperation::SetMiterLimit(10.0));
1861 assert_eq!(
1862 operators[7],
1863 ContentOperation::SetGraphicsStateParams("GS1".to_string())
1864 );
1865 assert_eq!(operators[8], ContentOperation::SetFlatness(0.5));
1866 assert_eq!(
1867 operators[9],
1868 ContentOperation::SetIntent("Perceptual".to_string())
1869 );
1870 }
1871
1872 #[test]
1873 fn test_all_path_construction_operators() {
1874 let content = b"100 200 m 150 200 l 200 200 250 250 300 200 c 250 180 300 200 v 200 180 300 200 y h 50 50 100 100 re";
1875 let operators = ContentParser::parse(content).unwrap();
1876
1877 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
1878 assert_eq!(operators[1], ContentOperation::LineTo(150.0, 200.0));
1879 assert_eq!(
1880 operators[2],
1881 ContentOperation::CurveTo(200.0, 200.0, 250.0, 250.0, 300.0, 200.0)
1882 );
1883 assert_eq!(
1884 operators[3],
1885 ContentOperation::CurveToV(250.0, 180.0, 300.0, 200.0)
1886 );
1887 assert_eq!(
1888 operators[4],
1889 ContentOperation::CurveToY(200.0, 180.0, 300.0, 200.0)
1890 );
1891 assert_eq!(operators[5], ContentOperation::ClosePath);
1892 assert_eq!(
1893 operators[6],
1894 ContentOperation::Rectangle(50.0, 50.0, 100.0, 100.0)
1895 );
1896 }
1897
1898 #[test]
1899 fn test_all_path_painting_operators() {
1900 let content = b"S s f F f* B B* b b* n W W*";
1901 let operators = ContentParser::parse(content).unwrap();
1902
1903 assert_eq!(operators[0], ContentOperation::Stroke);
1904 assert_eq!(operators[1], ContentOperation::CloseStroke);
1905 assert_eq!(operators[2], ContentOperation::Fill);
1906 assert_eq!(operators[3], ContentOperation::Fill); assert_eq!(operators[4], ContentOperation::FillEvenOdd);
1908 assert_eq!(operators[5], ContentOperation::FillStroke);
1909 assert_eq!(operators[6], ContentOperation::FillStrokeEvenOdd);
1910 assert_eq!(operators[7], ContentOperation::CloseFillStroke);
1911 assert_eq!(operators[8], ContentOperation::CloseFillStrokeEvenOdd);
1912 assert_eq!(operators[9], ContentOperation::EndPath);
1913 assert_eq!(operators[10], ContentOperation::Clip);
1914 assert_eq!(operators[11], ContentOperation::ClipEvenOdd);
1915 }
1916
1917 #[test]
1918 fn test_all_color_operators() {
1919 let content = b"/DeviceRGB CS /DeviceGray cs 0.7 G 0.4 g 1 0 0 RG 0 1 0 rg 0 0 0 1 K 0.2 0.3 0.4 0.5 k /Shade1 sh";
1921 let operators = ContentParser::parse(content).unwrap();
1922
1923 assert_eq!(
1924 operators[0],
1925 ContentOperation::SetStrokingColorSpace("DeviceRGB".to_string())
1926 );
1927 assert_eq!(
1928 operators[1],
1929 ContentOperation::SetNonStrokingColorSpace("DeviceGray".to_string())
1930 );
1931 assert_eq!(operators[2], ContentOperation::SetStrokingGray(0.7));
1932 assert_eq!(operators[3], ContentOperation::SetNonStrokingGray(0.4));
1933 assert_eq!(
1934 operators[4],
1935 ContentOperation::SetStrokingRGB(1.0, 0.0, 0.0)
1936 );
1937 assert_eq!(
1938 operators[5],
1939 ContentOperation::SetNonStrokingRGB(0.0, 1.0, 0.0)
1940 );
1941 assert_eq!(
1942 operators[6],
1943 ContentOperation::SetStrokingCMYK(0.0, 0.0, 0.0, 1.0)
1944 );
1945 assert_eq!(
1946 operators[7],
1947 ContentOperation::SetNonStrokingCMYK(0.2, 0.3, 0.4, 0.5)
1948 );
1949 assert_eq!(
1950 operators[8],
1951 ContentOperation::ShadingFill("Shade1".to_string())
1952 );
1953 }
1954
1955 #[test]
1956 fn test_xobject_and_marked_content_operators() {
1957 let content = b"/Image1 Do /MC1 BMC EMC /MP1 MP BX EX";
1959 let operators = ContentParser::parse(content).unwrap();
1960
1961 assert_eq!(
1962 operators[0],
1963 ContentOperation::PaintXObject("Image1".to_string())
1964 );
1965 assert_eq!(
1966 operators[1],
1967 ContentOperation::BeginMarkedContent("MC1".to_string())
1968 );
1969 assert_eq!(operators[2], ContentOperation::EndMarkedContent);
1970 assert_eq!(
1971 operators[3],
1972 ContentOperation::DefineMarkedContentPoint("MP1".to_string())
1973 );
1974 assert_eq!(operators[4], ContentOperation::BeginCompatibility);
1975 assert_eq!(operators[5], ContentOperation::EndCompatibility);
1976 }
1977
1978 #[test]
1979 fn test_complex_content_stream() {
1980 let content = b"q 0.5 0 0 0.5 100 100 cm BT /F1 12 Tf 0 0 Td (Complex) Tj ET Q";
1981 let operators = ContentParser::parse(content).unwrap();
1982
1983 assert_eq!(operators.len(), 8);
1984 assert_eq!(operators[0], ContentOperation::SaveGraphicsState);
1985 assert_eq!(
1986 operators[1],
1987 ContentOperation::SetTransformMatrix(0.5, 0.0, 0.0, 0.5, 100.0, 100.0)
1988 );
1989 assert_eq!(operators[2], ContentOperation::BeginText);
1990 assert_eq!(
1991 operators[3],
1992 ContentOperation::SetFont("F1".to_string(), 12.0)
1993 );
1994 assert_eq!(operators[4], ContentOperation::MoveText(0.0, 0.0));
1995 assert_eq!(
1996 operators[5],
1997 ContentOperation::ShowText(b"Complex".to_vec())
1998 );
1999 assert_eq!(operators[6], ContentOperation::EndText);
2000 assert_eq!(operators[7], ContentOperation::RestoreGraphicsState);
2001 }
2002
2003 #[test]
2004 fn test_tokenizer_whitespace_handling() {
2005 let input = b" \t\n\r BT \t\n /F1 12.5 \t Tf \n\r ET ";
2006 let mut tokenizer = ContentTokenizer::new(input);
2007
2008 assert_eq!(
2009 tokenizer.next_token().unwrap(),
2010 Some(Token::Operator("BT".to_string()))
2011 );
2012 assert_eq!(
2013 tokenizer.next_token().unwrap(),
2014 Some(Token::Name("F1".to_string()))
2015 );
2016 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(12.5)));
2017 assert_eq!(
2018 tokenizer.next_token().unwrap(),
2019 Some(Token::Operator("Tf".to_string()))
2020 );
2021 assert_eq!(
2022 tokenizer.next_token().unwrap(),
2023 Some(Token::Operator("ET".to_string()))
2024 );
2025 assert_eq!(tokenizer.next_token().unwrap(), None);
2026 }
2027
2028 #[test]
2029 fn test_tokenizer_edge_cases() {
2030 let input = b"0 .5 -.5 +.5 123. .123 1.23 -1.23";
2032 let mut tokenizer = ContentTokenizer::new(input);
2033
2034 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Integer(0)));
2035 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(0.5)));
2036 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(-0.5)));
2037 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(0.5)));
2038 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(123.0)));
2039 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(0.123)));
2040 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(1.23)));
2041 assert_eq!(tokenizer.next_token().unwrap(), Some(Token::Number(-1.23)));
2042 }
2043
2044 #[test]
2045 fn test_string_parsing_edge_cases() {
2046 let input = b"(Simple) (With\\\\backslash) (With\\)paren) (With\\newline) (With\\ttab) (With\\rcarriage) (With\\bbackspace) (With\\fformfeed) (With\\(leftparen) (With\\)rightparen) (With\\377octal) (With\\dddoctal)";
2047 let mut tokenizer = ContentTokenizer::new(input);
2048
2049 assert_eq!(
2050 tokenizer.next_token().unwrap(),
2051 Some(Token::String(b"Simple".to_vec()))
2052 );
2053 assert_eq!(
2054 tokenizer.next_token().unwrap(),
2055 Some(Token::String(b"With\\backslash".to_vec()))
2056 );
2057 assert_eq!(
2058 tokenizer.next_token().unwrap(),
2059 Some(Token::String(b"With)paren".to_vec()))
2060 );
2061 assert_eq!(
2062 tokenizer.next_token().unwrap(),
2063 Some(Token::String(b"With\newline".to_vec()))
2064 );
2065 assert_eq!(
2066 tokenizer.next_token().unwrap(),
2067 Some(Token::String(b"With\ttab".to_vec()))
2068 );
2069 assert_eq!(
2070 tokenizer.next_token().unwrap(),
2071 Some(Token::String(b"With\rcarriage".to_vec()))
2072 );
2073 assert_eq!(
2074 tokenizer.next_token().unwrap(),
2075 Some(Token::String(b"With\x08backspace".to_vec()))
2076 );
2077 assert_eq!(
2078 tokenizer.next_token().unwrap(),
2079 Some(Token::String(b"With\x0Cformfeed".to_vec()))
2080 );
2081 assert_eq!(
2082 tokenizer.next_token().unwrap(),
2083 Some(Token::String(b"With(leftparen".to_vec()))
2084 );
2085 assert_eq!(
2086 tokenizer.next_token().unwrap(),
2087 Some(Token::String(b"With)rightparen".to_vec()))
2088 );
2089 }
2090
2091 #[test]
2092 fn test_hex_string_parsing() {
2093 let input = b"<48656C6C6F> <48 65 6C 6C 6F> <48656C6C6F57> <48656C6C6F5>";
2094 let mut tokenizer = ContentTokenizer::new(input);
2095
2096 assert_eq!(
2097 tokenizer.next_token().unwrap(),
2098 Some(Token::HexString(b"Hello".to_vec()))
2099 );
2100 assert_eq!(
2101 tokenizer.next_token().unwrap(),
2102 Some(Token::HexString(b"Hello".to_vec()))
2103 );
2104 assert_eq!(
2105 tokenizer.next_token().unwrap(),
2106 Some(Token::HexString(b"HelloW".to_vec()))
2107 );
2108 assert_eq!(
2109 tokenizer.next_token().unwrap(),
2110 Some(Token::HexString(b"Hello\x50".to_vec()))
2111 );
2112 }
2113
2114 #[test]
2115 fn test_name_parsing_edge_cases() {
2116 let input = b"/Name /Name#20with#20spaces /Name#23with#23hash /Name#2Fwith#2Fslash /#45mptyName";
2117 let mut tokenizer = ContentTokenizer::new(input);
2118
2119 assert_eq!(
2120 tokenizer.next_token().unwrap(),
2121 Some(Token::Name("Name".to_string()))
2122 );
2123 assert_eq!(
2124 tokenizer.next_token().unwrap(),
2125 Some(Token::Name("Name with spaces".to_string()))
2126 );
2127 assert_eq!(
2128 tokenizer.next_token().unwrap(),
2129 Some(Token::Name("Name#with#hash".to_string()))
2130 );
2131 assert_eq!(
2132 tokenizer.next_token().unwrap(),
2133 Some(Token::Name("Name/with/slash".to_string()))
2134 );
2135 assert_eq!(
2136 tokenizer.next_token().unwrap(),
2137 Some(Token::Name("EmptyName".to_string()))
2138 );
2139 }
2140
2141 #[test]
2142 fn test_operator_parsing_edge_cases() {
2143 let content = b"q q q Q Q Q BT BT ET ET";
2144 let operators = ContentParser::parse(content).unwrap();
2145
2146 assert_eq!(operators.len(), 10);
2147 assert_eq!(operators[0], ContentOperation::SaveGraphicsState);
2148 assert_eq!(operators[1], ContentOperation::SaveGraphicsState);
2149 assert_eq!(operators[2], ContentOperation::SaveGraphicsState);
2150 assert_eq!(operators[3], ContentOperation::RestoreGraphicsState);
2151 assert_eq!(operators[4], ContentOperation::RestoreGraphicsState);
2152 assert_eq!(operators[5], ContentOperation::RestoreGraphicsState);
2153 assert_eq!(operators[6], ContentOperation::BeginText);
2154 assert_eq!(operators[7], ContentOperation::BeginText);
2155 assert_eq!(operators[8], ContentOperation::EndText);
2156 assert_eq!(operators[9], ContentOperation::EndText);
2157 }
2158
2159 #[test]
2160 fn test_error_handling_insufficient_operands() {
2161 let content = b"100 Td"; let result = ContentParser::parse(content);
2163 assert!(result.is_err());
2164 }
2165
2166 #[test]
2167 fn test_error_handling_invalid_operator() {
2168 let content = b"100 200 INVALID";
2169 let result = ContentParser::parse(content);
2170 assert!(result.is_err());
2171 }
2172
2173 #[test]
2174 fn test_error_handling_malformed_string() {
2175 let input = b"(Unclosed string";
2177 let mut tokenizer = ContentTokenizer::new(input);
2178 let result = tokenizer.next_token();
2179 assert!(result.is_ok() || result.is_err());
2182 }
2183
2184 #[test]
2185 fn test_error_handling_malformed_hex_string() {
2186 let input = b"<48656C6C6G>";
2187 let mut tokenizer = ContentTokenizer::new(input);
2188 let result = tokenizer.next_token();
2189 assert!(result.is_err());
2190 }
2191
2192 #[test]
2193 fn test_error_handling_malformed_name() {
2194 let input = b"/Name#GG";
2195 let mut tokenizer = ContentTokenizer::new(input);
2196 let result = tokenizer.next_token();
2197 assert!(result.is_err());
2198 }
2199
2200 #[test]
2201 fn test_empty_content_stream() {
2202 let content = b"";
2203 let operators = ContentParser::parse(content).unwrap();
2204 assert_eq!(operators.len(), 0);
2205 }
2206
2207 #[test]
2208 fn test_whitespace_only_content_stream() {
2209 let content = b" \t\n\r ";
2210 let operators = ContentParser::parse(content).unwrap();
2211 assert_eq!(operators.len(), 0);
2212 }
2213
2214 #[test]
2215 fn test_mixed_integer_and_real_operands() {
2216 let content = b"100 200 m 150 200 l";
2218 let operators = ContentParser::parse(content).unwrap();
2219
2220 assert_eq!(operators.len(), 2);
2221 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
2222 assert_eq!(operators[1], ContentOperation::LineTo(150.0, 200.0));
2223 }
2224
2225 #[test]
2226 fn test_negative_operands() {
2227 let content = b"-100 -200 Td -50.5 -75.2 TD";
2228 let operators = ContentParser::parse(content).unwrap();
2229
2230 assert_eq!(operators.len(), 2);
2231 assert_eq!(operators[0], ContentOperation::MoveText(-100.0, -200.0));
2232 assert_eq!(
2233 operators[1],
2234 ContentOperation::MoveTextSetLeading(-50.5, -75.2)
2235 );
2236 }
2237
2238 #[test]
2239 fn test_large_numbers() {
2240 let content = b"999999.999999 -999999.999999 m";
2241 let operators = ContentParser::parse(content).unwrap();
2242
2243 assert_eq!(operators.len(), 1);
2244 assert_eq!(
2245 operators[0],
2246 ContentOperation::MoveTo(999999.999999, -999999.999999)
2247 );
2248 }
2249
2250 #[test]
2251 fn test_scientific_notation() {
2252 let content = b"123.45 -456.78 m";
2254 let operators = ContentParser::parse(content).unwrap();
2255
2256 assert_eq!(operators.len(), 1);
2257 assert_eq!(operators[0], ContentOperation::MoveTo(123.45, -456.78));
2258 }
2259
2260 #[test]
2261 fn test_show_text_array_complex() {
2262 let content = b"(Hello) TJ";
2264 let result = ContentParser::parse(content);
2265 assert!(result.is_err());
2267 }
2268
2269 #[test]
2270 fn test_dash_pattern_empty() {
2271 let content = b"0 d";
2273 let result = ContentParser::parse(content);
2274 assert!(result.is_err());
2276 }
2277
2278 #[test]
2279 fn test_dash_pattern_complex() {
2280 let content = b"2.5 d";
2282 let result = ContentParser::parse(content);
2283 assert!(result.is_err());
2285 }
2286
2287 #[test]
2288 fn test_pop_array_removes_array_end() {
2289 let parser = ContentParser::new(b"");
2291
2292 let mut operands = vec![
2294 Token::ArrayStart,
2295 Token::Integer(1),
2296 Token::Integer(2),
2297 Token::Integer(3),
2298 Token::ArrayEnd,
2299 ];
2300 let result = parser.pop_array(&mut operands).unwrap();
2301 assert_eq!(result.len(), 3);
2302 assert!(operands.is_empty());
2303
2304 let mut operands = vec![Token::ArrayStart, Token::Number(1.5), Token::Number(2.5)];
2306 let result = parser.pop_array(&mut operands).unwrap();
2307 assert_eq!(result.len(), 2);
2308 assert!(operands.is_empty());
2309 }
2310
2311 #[test]
2312 fn test_dash_array_parsing_valid() {
2313 let parser = ContentParser::new(b"");
2315
2316 let valid_tokens = vec![Token::Number(3.0), Token::Integer(2)];
2318 let result = parser.parse_dash_array(valid_tokens).unwrap();
2319 assert_eq!(result, vec![3.0, 2.0]);
2320
2321 let empty_tokens = vec![];
2323 let result = parser.parse_dash_array(empty_tokens).unwrap();
2324 let expected: Vec<f32> = vec![];
2325 assert_eq!(result, expected);
2326 }
2327
2328 #[test]
2329 fn test_text_array_parsing_valid() {
2330 let parser = ContentParser::new(b"");
2332
2333 let valid_tokens = vec![
2335 Token::String(b"Hello".to_vec()),
2336 Token::Number(-100.0),
2337 Token::String(b"World".to_vec()),
2338 ];
2339 let result = parser.parse_text_array(valid_tokens).unwrap();
2340 assert_eq!(result.len(), 3);
2341 }
2342
2343 #[test]
2344 fn test_inline_image_handling() {
2345 let content = b"BI /W 100 /H 100 /BPC 8 /CS /RGB ID some_image_data EI";
2346 let operators = ContentParser::parse(content).unwrap();
2347
2348 assert_eq!(operators.len(), 1);
2349 match &operators[0] {
2350 ContentOperation::InlineImage { params, data: _ } => {
2351 assert_eq!(params.get("Width"), Some(&Object::Integer(100)));
2353 assert_eq!(params.get("Height"), Some(&Object::Integer(100)));
2354 assert_eq!(params.get("BitsPerComponent"), Some(&Object::Integer(8)));
2355 assert_eq!(
2356 params.get("ColorSpace"),
2357 Some(&Object::Name("DeviceRGB".to_string()))
2358 );
2359 }
2361 _ => panic!("Expected InlineImage operation"),
2362 }
2363 }
2364
2365 #[test]
2366 fn test_inline_image_with_filter() {
2367 let content = b"BI /W 50 /H 50 /CS /G /BPC 1 /F /AHx ID 00FF00FF EI";
2368 let operators = ContentParser::parse(content).unwrap();
2369
2370 assert_eq!(operators.len(), 1);
2371 match &operators[0] {
2372 ContentOperation::InlineImage { params, data: _ } => {
2373 assert_eq!(params.get("Width"), Some(&Object::Integer(50)));
2374 assert_eq!(params.get("Height"), Some(&Object::Integer(50)));
2375 assert_eq!(
2376 params.get("ColorSpace"),
2377 Some(&Object::Name("DeviceGray".to_string()))
2378 );
2379 assert_eq!(params.get("BitsPerComponent"), Some(&Object::Integer(1)));
2380 assert_eq!(
2381 params.get("Filter"),
2382 Some(&Object::Name("ASCIIHexDecode".to_string()))
2383 );
2384 }
2385 _ => panic!("Expected InlineImage operation"),
2386 }
2387 }
2388
2389 #[test]
2390 fn test_content_parser_performance() {
2391 let mut content = Vec::new();
2392 for i in 0..1000 {
2393 content.extend_from_slice(format!("{} {} m ", i, i + 1).as_bytes());
2394 }
2395
2396 let start = std::time::Instant::now();
2397 let operators = ContentParser::parse(&content).unwrap();
2398 let duration = start.elapsed();
2399
2400 assert_eq!(operators.len(), 1000);
2401 assert!(duration.as_millis() < 100); }
2403
2404 #[test]
2405 fn test_tokenizer_performance() {
2406 let mut input = Vec::new();
2407 for i in 0..1000 {
2408 input.extend_from_slice(format!("{} {} ", i, i + 1).as_bytes());
2409 }
2410
2411 let start = std::time::Instant::now();
2412 let mut tokenizer = ContentTokenizer::new(&input);
2413 let mut count = 0;
2414 while tokenizer.next_token().unwrap().is_some() {
2415 count += 1;
2416 }
2417 let duration = start.elapsed();
2418
2419 assert_eq!(count, 2000); assert!(duration.as_millis() < 50); }
2422
2423 #[test]
2424 fn test_memory_usage_large_content() {
2425 let mut content = Vec::new();
2426 for i in 0..10000 {
2427 content.extend_from_slice(
2428 format!("{} {} {} {} {} {} c ", i, i + 1, i + 2, i + 3, i + 4, i + 5)
2429 .as_bytes(),
2430 );
2431 }
2432
2433 let operators = ContentParser::parse(&content).unwrap();
2434 assert_eq!(operators.len(), 10000);
2435
2436 for op in operators {
2438 matches!(op, ContentOperation::CurveTo(_, _, _, _, _, _));
2439 }
2440 }
2441
2442 #[test]
2443 fn test_concurrent_parsing() {
2444 use std::sync::Arc;
2445 use std::thread;
2446
2447 let content = Arc::new(b"BT /F1 12 Tf 100 200 Td (Hello) Tj ET".to_vec());
2448 let handles: Vec<_> = (0..10)
2449 .map(|_| {
2450 let content_clone = content.clone();
2451 thread::spawn(move || ContentParser::parse(&content_clone).unwrap())
2452 })
2453 .collect();
2454
2455 for handle in handles {
2456 let operators = handle.join().unwrap();
2457 assert_eq!(operators.len(), 5);
2458 assert_eq!(operators[0], ContentOperation::BeginText);
2459 assert_eq!(operators[4], ContentOperation::EndText);
2460 }
2461 }
2462
2463 #[test]
2466 fn test_tokenizer_hex_string_edge_cases() {
2467 let mut tokenizer = ContentTokenizer::new(b"<>");
2468 let token = tokenizer.next_token().unwrap().unwrap();
2469 match token {
2470 Token::HexString(data) => assert!(data.is_empty()),
2471 _ => panic!("Expected empty hex string"),
2472 }
2473
2474 let mut tokenizer = ContentTokenizer::new(b"<123>");
2476 let token = tokenizer.next_token().unwrap().unwrap();
2477 match token {
2478 Token::HexString(data) => assert_eq!(data, vec![0x12, 0x30]),
2479 _ => panic!("Expected hex string with odd digits"),
2480 }
2481
2482 let mut tokenizer = ContentTokenizer::new(b"<12 34\t56\n78>");
2484 let token = tokenizer.next_token().unwrap().unwrap();
2485 match token {
2486 Token::HexString(data) => assert_eq!(data, vec![0x12, 0x34, 0x56, 0x78]),
2487 _ => panic!("Expected hex string with whitespace"),
2488 }
2489 }
2490
2491 #[test]
2492 fn test_tokenizer_literal_string_escape_sequences() {
2493 let mut tokenizer = ContentTokenizer::new(b"(\\n\\r\\t\\b\\f\\(\\)\\\\)");
2495 let token = tokenizer.next_token().unwrap().unwrap();
2496 match token {
2497 Token::String(data) => {
2498 assert_eq!(
2499 data,
2500 vec![b'\n', b'\r', b'\t', 0x08, 0x0C, b'(', b')', b'\\']
2501 );
2502 }
2503 _ => panic!("Expected string with escapes"),
2504 }
2505
2506 let mut tokenizer = ContentTokenizer::new(b"(\\101\\040\\377)");
2508 let token = tokenizer.next_token().unwrap().unwrap();
2509 match token {
2510 Token::String(data) => assert_eq!(data, vec![b'A', b' ', 255]),
2511 _ => panic!("Expected string with octal escapes"),
2512 }
2513 }
2514
2515 #[test]
2516 fn test_tokenizer_nested_parentheses() {
2517 let mut tokenizer = ContentTokenizer::new(b"(outer (inner) text)");
2518 let token = tokenizer.next_token().unwrap().unwrap();
2519 match token {
2520 Token::String(data) => {
2521 assert_eq!(data, b"outer (inner) text");
2522 }
2523 _ => panic!("Expected string with nested parentheses"),
2524 }
2525
2526 let mut tokenizer = ContentTokenizer::new(b"(level1 (level2 (level3) back2) back1)");
2528 let token = tokenizer.next_token().unwrap().unwrap();
2529 match token {
2530 Token::String(data) => {
2531 assert_eq!(data, b"level1 (level2 (level3) back2) back1");
2532 }
2533 _ => panic!("Expected string with deep nesting"),
2534 }
2535 }
2536
2537 #[test]
2538 fn test_tokenizer_name_hex_escapes() {
2539 let mut tokenizer = ContentTokenizer::new(b"/Name#20With#20Spaces");
2540 let token = tokenizer.next_token().unwrap().unwrap();
2541 match token {
2542 Token::Name(name) => assert_eq!(name, "Name With Spaces"),
2543 _ => panic!("Expected name with hex escapes"),
2544 }
2545
2546 let mut tokenizer = ContentTokenizer::new(b"/Special#2F#28#29#3C#3E");
2548 let token = tokenizer.next_token().unwrap().unwrap();
2549 match token {
2550 Token::Name(name) => assert_eq!(name, "Special/()<>"),
2551 _ => panic!("Expected name with special character escapes"),
2552 }
2553 }
2554
2555 #[test]
2556 fn test_tokenizer_number_edge_cases() {
2557 let mut tokenizer = ContentTokenizer::new(b"2147483647");
2559 let token = tokenizer.next_token().unwrap().unwrap();
2560 match token {
2561 Token::Integer(n) => assert_eq!(n, 2147483647),
2562 _ => panic!("Expected large integer"),
2563 }
2564
2565 let mut tokenizer = ContentTokenizer::new(b"0.00001");
2567 let token = tokenizer.next_token().unwrap().unwrap();
2568 match token {
2569 Token::Number(n) => assert!((n - 0.00001).abs() < f32::EPSILON),
2570 _ => panic!("Expected small float"),
2571 }
2572
2573 let mut tokenizer = ContentTokenizer::new(b".5");
2575 let token = tokenizer.next_token().unwrap().unwrap();
2576 match token {
2577 Token::Number(n) => assert!((n - 0.5).abs() < f32::EPSILON),
2578 _ => panic!("Expected float starting with dot"),
2579 }
2580 }
2581
2582 #[test]
2583 fn test_parser_complex_path_operations() {
2584 let content = b"100 200 m 150 200 l 150 250 l 100 250 l h f";
2585 let operators = ContentParser::parse(content).unwrap();
2586
2587 assert_eq!(operators.len(), 6);
2588 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
2589 assert_eq!(operators[1], ContentOperation::LineTo(150.0, 200.0));
2590 assert_eq!(operators[2], ContentOperation::LineTo(150.0, 250.0));
2591 assert_eq!(operators[3], ContentOperation::LineTo(100.0, 250.0));
2592 assert_eq!(operators[4], ContentOperation::ClosePath);
2593 assert_eq!(operators[5], ContentOperation::Fill);
2594 }
2595
2596 #[test]
2597 fn test_parser_bezier_curves() {
2598 let content = b"100 100 150 50 200 150 c";
2599 let operators = ContentParser::parse(content).unwrap();
2600
2601 assert_eq!(operators.len(), 1);
2602 match &operators[0] {
2603 ContentOperation::CurveTo(x1, y1, x2, y2, x3, y3) => {
2604 assert!(x1.is_finite() && y1.is_finite());
2608 assert!(x2.is_finite() && y2.is_finite());
2609 assert!(x3.is_finite() && y3.is_finite());
2610 assert!(*x1 >= 50.0 && *x1 <= 200.0);
2612 assert!(*y1 >= 50.0 && *y1 <= 200.0);
2613 }
2614 _ => panic!("Expected CurveTo operation"),
2615 }
2616 }
2617
2618 #[test]
2619 fn test_parser_color_operations() {
2620 let content = b"0.5 g 1 0 0 rg 0 1 0 1 k /DeviceRGB cs 0.2 0.4 0.6 sc";
2621 let operators = ContentParser::parse(content).unwrap();
2622
2623 assert_eq!(operators.len(), 5);
2624 match &operators[0] {
2625 ContentOperation::SetNonStrokingGray(gray) => assert_eq!(*gray, 0.5),
2626 _ => panic!("Expected SetNonStrokingGray"),
2627 }
2628 match &operators[1] {
2629 ContentOperation::SetNonStrokingRGB(r, g, b) => {
2630 assert_eq!((*r, *g, *b), (1.0, 0.0, 0.0));
2631 }
2632 _ => panic!("Expected SetNonStrokingRGB"),
2633 }
2634 }
2635
2636 #[test]
2637 fn test_parser_text_positioning_advanced() {
2638 let content = b"BT 1 0 0 1 100 200 Tm 0 TL 10 TL (Line 1) ' (Line 2) ' ET";
2639 let operators = ContentParser::parse(content).unwrap();
2640
2641 assert_eq!(operators.len(), 7);
2642 assert_eq!(operators[0], ContentOperation::BeginText);
2643 match &operators[1] {
2644 ContentOperation::SetTextMatrix(a, b, c, d, e, f) => {
2645 assert_eq!((*a, *b, *c, *d, *e, *f), (1.0, 0.0, 0.0, 1.0, 100.0, 200.0));
2646 }
2647 _ => panic!("Expected SetTextMatrix"),
2648 }
2649 assert_eq!(operators[6], ContentOperation::EndText);
2650 }
2651
2652 #[test]
2653 fn test_parser_graphics_state_operations() {
2654 let content = b"q 2 0 0 2 100 100 cm 5 w 1 J 2 j 10 M Q";
2655 let operators = ContentParser::parse(content).unwrap();
2656
2657 assert_eq!(operators.len(), 7);
2658 assert_eq!(operators[0], ContentOperation::SaveGraphicsState);
2659 match &operators[1] {
2660 ContentOperation::SetTransformMatrix(a, b, c, d, e, f) => {
2661 assert_eq!((*a, *b, *c, *d, *e, *f), (2.0, 0.0, 0.0, 2.0, 100.0, 100.0));
2662 }
2663 _ => panic!("Expected SetTransformMatrix"),
2664 }
2665 assert_eq!(operators[6], ContentOperation::RestoreGraphicsState);
2666 }
2667
2668 #[test]
2669 fn test_parser_xobject_operations() {
2670 let content = b"/Image1 Do /Form2 Do /Pattern3 Do";
2671 let operators = ContentParser::parse(content).unwrap();
2672
2673 assert_eq!(operators.len(), 3);
2674 for (i, expected_name) in ["Image1", "Form2", "Pattern3"].iter().enumerate() {
2675 match &operators[i] {
2676 ContentOperation::PaintXObject(name) => assert_eq!(name, expected_name),
2677 _ => panic!("Expected PaintXObject"),
2678 }
2679 }
2680 }
2681
2682 #[test]
2683 fn test_parser_marked_content_operations() {
2684 let content = b"/P BMC (Tagged content) Tj EMC";
2685 let operators = ContentParser::parse(content).unwrap();
2686
2687 assert_eq!(operators.len(), 3);
2688 match &operators[0] {
2689 ContentOperation::BeginMarkedContent(tag) => assert_eq!(tag, "P"),
2690 _ => panic!("Expected BeginMarkedContent"),
2691 }
2692 assert_eq!(operators[2], ContentOperation::EndMarkedContent);
2693 }
2694
2695 #[test]
2696 fn test_parser_error_handling_invalid_operators() {
2697 let content = b"m";
2699 let result = ContentParser::parse(content);
2700 assert!(result.is_err());
2701
2702 let content = b"<ABC DEF BT";
2704 let result = ContentParser::parse(content);
2705 assert!(result.is_err());
2706
2707 let content = b"100 200 300"; let result = ContentParser::parse(content);
2710 assert!(result.is_ok()); }
2712
2713 #[test]
2714 fn test_parser_whitespace_tolerance() {
2715 let content = b" \n\t 100 \r\n 200 \t m \n";
2716 let operators = ContentParser::parse(content).unwrap();
2717
2718 assert_eq!(operators.len(), 1);
2719 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
2720 }
2721
2722 #[test]
2723 fn test_tokenizer_comment_handling() {
2724 let content = b"100 % This is a comment\n200 m % Another comment";
2725 let operators = ContentParser::parse(content).unwrap();
2726
2727 assert_eq!(operators.len(), 1);
2728 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
2729 }
2730
2731 #[test]
2732 fn test_parser_stream_with_binary_data() {
2733 let content = b"100 200 m % Comment with \xFF binary\n150 250 l";
2735
2736 let operators = ContentParser::parse(content).unwrap();
2737 assert_eq!(operators.len(), 2);
2738 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
2739 assert_eq!(operators[1], ContentOperation::LineTo(150.0, 250.0));
2740 }
2741
2742 #[test]
2743 fn test_tokenizer_array_parsing() {
2744 let content = b"100 200 m 150 250 l";
2746 let operators = ContentParser::parse(content).unwrap();
2747
2748 assert_eq!(operators.len(), 2);
2749 assert_eq!(operators[0], ContentOperation::MoveTo(100.0, 200.0));
2750 assert_eq!(operators[1], ContentOperation::LineTo(150.0, 250.0));
2751 }
2752
2753 #[test]
2754 fn test_parser_rectangle_operations() {
2755 let content = b"10 20 100 50 re 0 0 200 300 re";
2756 let operators = ContentParser::parse(content).unwrap();
2757
2758 assert_eq!(operators.len(), 2);
2759 match &operators[0] {
2760 ContentOperation::Rectangle(x, y, width, height) => {
2761 assert_eq!((*x, *y, *width, *height), (10.0, 20.0, 100.0, 50.0));
2762 }
2763 _ => panic!("Expected Rectangle operation"),
2764 }
2765 match &operators[1] {
2766 ContentOperation::Rectangle(x, y, width, height) => {
2767 assert_eq!((*x, *y, *width, *height), (0.0, 0.0, 200.0, 300.0));
2768 }
2769 _ => panic!("Expected Rectangle operation"),
2770 }
2771 }
2772
2773 #[test]
2774 fn test_parser_clipping_operations() {
2775 let content = b"100 100 50 50 re W n 200 200 75 75 re W* n";
2776 let operators = ContentParser::parse(content).unwrap();
2777
2778 assert_eq!(operators.len(), 6);
2779 assert_eq!(operators[1], ContentOperation::Clip);
2780 assert_eq!(operators[2], ContentOperation::EndPath);
2781 assert_eq!(operators[4], ContentOperation::ClipEvenOdd);
2782 assert_eq!(operators[5], ContentOperation::EndPath);
2783 }
2784
2785 #[test]
2786 fn test_parser_painting_operations() {
2787 let content = b"S s f f* B B* b b*";
2788 let operators = ContentParser::parse(content).unwrap();
2789
2790 assert_eq!(operators.len(), 8);
2791 assert_eq!(operators[0], ContentOperation::Stroke);
2792 assert_eq!(operators[1], ContentOperation::CloseStroke);
2793 assert_eq!(operators[2], ContentOperation::Fill);
2794 assert_eq!(operators[3], ContentOperation::FillEvenOdd);
2795 assert_eq!(operators[4], ContentOperation::FillStroke);
2796 assert_eq!(operators[5], ContentOperation::FillStrokeEvenOdd);
2797 assert_eq!(operators[6], ContentOperation::CloseFillStroke);
2798 assert_eq!(operators[7], ContentOperation::CloseFillStrokeEvenOdd);
2799 }
2800
2801 #[test]
2802 fn test_parser_line_style_operations() {
2803 let content = b"5 w 1 J 2 j 10 M [ 3 2 ] 0 d";
2804 let operators = ContentParser::parse(content).unwrap();
2805
2806 assert_eq!(operators.len(), 5);
2807 assert_eq!(operators[0], ContentOperation::SetLineWidth(5.0));
2808 assert_eq!(operators[1], ContentOperation::SetLineCap(1));
2809 assert_eq!(operators[2], ContentOperation::SetLineJoin(2));
2810 assert_eq!(operators[3], ContentOperation::SetMiterLimit(10.0));
2811 }
2813
2814 #[test]
2815 fn test_parser_text_state_operations() {
2816 let content = b"12 Tc 3 Tw 100 Tz 1 Tr 2 Ts";
2817 let operators = ContentParser::parse(content).unwrap();
2818
2819 assert_eq!(operators.len(), 5);
2820 assert_eq!(operators[0], ContentOperation::SetCharSpacing(12.0));
2821 assert_eq!(operators[1], ContentOperation::SetWordSpacing(3.0));
2822 assert_eq!(operators[2], ContentOperation::SetHorizontalScaling(100.0));
2823 assert_eq!(operators[3], ContentOperation::SetTextRenderMode(1));
2824 assert_eq!(operators[4], ContentOperation::SetTextRise(2.0));
2825 }
2826
2827 #[test]
2828 fn test_parser_unicode_text() {
2829 let content = b"BT (Hello \xC2\xA9 World \xE2\x9C\x93) Tj ET";
2830 let operators = ContentParser::parse(content).unwrap();
2831
2832 assert_eq!(operators.len(), 3);
2833 assert_eq!(operators[0], ContentOperation::BeginText);
2834 match &operators[1] {
2835 ContentOperation::ShowText(text) => {
2836 assert!(text.len() > 5); }
2838 _ => panic!("Expected ShowText operation"),
2839 }
2840 assert_eq!(operators[2], ContentOperation::EndText);
2841 }
2842
2843 #[test]
2844 fn test_parser_stress_test_large_coordinates() {
2845 let content = b"999999.999 -999999.999 999999.999 -999999.999 999999.999 -999999.999 c";
2846 let operators = ContentParser::parse(content).unwrap();
2847
2848 assert_eq!(operators.len(), 1);
2849 match &operators[0] {
2850 ContentOperation::CurveTo(_x1, _y1, _x2, _y2, _x3, _y3) => {
2851 assert!((*_x1 - 999999.999).abs() < 0.1);
2852 assert!((*_y1 - (-999999.999)).abs() < 0.1);
2853 assert!((*_x3 - 999999.999).abs() < 0.1);
2854 }
2855 _ => panic!("Expected CurveTo operation"),
2856 }
2857 }
2858
2859 #[test]
2860 fn test_parser_empty_content_stream() {
2861 let content = b"";
2862 let operators = ContentParser::parse(content).unwrap();
2863 assert!(operators.is_empty());
2864
2865 let content = b" \n\t\r ";
2866 let operators = ContentParser::parse(content).unwrap();
2867 assert!(operators.is_empty());
2868 }
2869
2870 #[test]
2871 fn test_tokenizer_error_recovery() {
2872 let content = b"100 200 m % Comment with\xFFbinary\n150 250 l";
2874 let result = ContentParser::parse(content);
2875 assert!(result.is_ok() || result.is_err());
2877 }
2878
2879 #[test]
2880 fn test_parser_optimization_repeated_operations() {
2881 let mut content = Vec::new();
2883 for i in 0..1000 {
2884 content.extend_from_slice(format!("{} {} m ", i, i * 2).as_bytes());
2885 }
2886
2887 let start = std::time::Instant::now();
2888 let operators = ContentParser::parse(&content).unwrap();
2889 let duration = start.elapsed();
2890
2891 assert_eq!(operators.len(), 1000);
2892 assert!(duration.as_millis() < 200); }
2894
2895 #[test]
2896 fn test_parser_memory_efficiency_large_strings() {
2897 let large_text = "A".repeat(10000);
2899 let content = format!("BT ({}) Tj ET", large_text);
2900 let operators = ContentParser::parse(content.as_bytes()).unwrap();
2901
2902 assert_eq!(operators.len(), 3);
2903 match &operators[1] {
2904 ContentOperation::ShowText(text) => {
2905 assert_eq!(text.len(), 10000);
2906 }
2907 _ => panic!("Expected ShowText operation"),
2908 }
2909 }
2910 }
2911
2912 #[test]
2913 fn test_content_stream_too_large() {
2914 let mut large_content = Vec::new();
2916
2917 for i in 0..10000 {
2919 large_content.extend_from_slice(format!("{} {} m ", i, i).as_bytes());
2920 }
2921 large_content.extend_from_slice(b"S");
2922
2923 let result = ContentParser::parse_content(&large_content);
2925 assert!(result.is_ok());
2926
2927 let operations = result.unwrap();
2928 assert!(operations.len() > 10000);
2930 }
2931
2932 #[test]
2933 fn test_invalid_operator_handling() {
2934 let content = b"100 200 INVALID_OP 300 400 m";
2936 let result = ContentParser::parse_content(content);
2937
2938 if let Ok(operations) = result {
2940 assert!(operations
2942 .iter()
2943 .any(|op| matches!(op, ContentOperation::MoveTo(_, _))));
2944 }
2945 }
2946
2947 #[test]
2948 fn test_nested_arrays_malformed() {
2949 let content = b"[[(Hello] [World)]] TJ";
2951 let result = ContentParser::parse_content(content);
2952
2953 assert!(result.is_ok() || result.is_err());
2955 }
2956
2957 #[test]
2958 fn test_escape_sequences_in_strings() {
2959 let test_cases = vec![
2961 (b"(\\n\\r\\t)".as_slice(), b"\n\r\t".as_slice()),
2962 (b"(\\\\)".as_slice(), b"\\".as_slice()),
2963 (b"(\\(\\))".as_slice(), b"()".as_slice()),
2964 (b"(\\123)".as_slice(), b"S".as_slice()), (b"(\\0)".as_slice(), b"\0".as_slice()),
2966 ];
2967
2968 for (input, expected) in test_cases {
2969 let mut content = Vec::new();
2970 content.extend_from_slice(input);
2971 content.extend_from_slice(b" Tj");
2972
2973 let result = ContentParser::parse_content(&content);
2974 assert!(result.is_ok());
2975
2976 let operations = result.unwrap();
2977 if let ContentOperation::ShowText(text) = &operations[0] {
2978 assert_eq!(text, expected, "Failed for input: {:?}", input);
2979 } else {
2980 panic!("Expected ShowText operation");
2981 }
2982 }
2983 }
2984
2985 #[test]
2986 fn test_content_with_inline_images() {
2987 let content = b"BI /W 10 /H 10 /CS /RGB ID \x00\x01\x02\x03 EI";
2989 let result = ContentParser::parse_content(content);
2990
2991 assert!(result.is_ok() || result.is_err());
2993 }
2994
2995 #[test]
2996 fn test_operator_with_missing_operands() {
2997 let test_cases = vec![
2999 b"Tj" as &[u8], b"m", b"rg", b"Tf", ];
3004
3005 for content in test_cases {
3006 let result = ContentParser::parse_content(content);
3007 assert!(result.is_ok() || result.is_err());
3009 }
3010 }
3011
3012 #[test]
3015 fn test_tokenizer_handles_curly_braces() {
3016 let input = b"q { } Q";
3019 let mut tokenizer = ContentTokenizer::new(input);
3020
3021 let mut tokens = Vec::new();
3022 while let Some(token) = tokenizer.next_token().unwrap() {
3023 tokens.push(token);
3024 }
3025
3026 assert!(tokens.contains(&Token::Operator("q".to_string())));
3028 assert!(tokens.contains(&Token::Operator("Q".to_string())));
3029 }
3030
3031 #[test]
3032 fn test_tokenizer_handles_closing_paren() {
3033 let input = b"q ) Q";
3035 let mut tokenizer = ContentTokenizer::new(input);
3036
3037 let mut tokens = Vec::new();
3038 while let Some(token) = tokenizer.next_token().unwrap() {
3039 tokens.push(token);
3040 }
3041
3042 assert!(tokens.contains(&Token::Operator("q".to_string())));
3043 assert!(tokens.contains(&Token::Operator("Q".to_string())));
3044 }
3045
3046 #[test]
3047 fn test_inline_image_binary_with_curly_braces() {
3048 let content = b"BI /W 2 /H 2 /BPC 8 /CS /G ID \x7B\x7D\x00\xFF EI Q";
3051 let result = ContentParser::parse_content(content);
3052 assert!(
3053 result.is_ok(),
3054 "Parsing inline image with curly braces failed: {:?}",
3055 result.err()
3056 );
3057
3058 let ops = result.unwrap();
3059 let has_inline = ops
3061 .iter()
3062 .any(|op| matches!(op, ContentOperation::InlineImage { .. }));
3063 let has_q = ops
3064 .iter()
3065 .any(|op| matches!(op, ContentOperation::RestoreGraphicsState));
3066 assert!(has_inline, "Expected InlineImage operation");
3067 assert!(has_q, "Expected RestoreGraphicsState after EI");
3068 }
3069
3070 #[test]
3071 fn test_inline_image_binary_with_all_byte_values() {
3072 let mut content = Vec::new();
3074 content.extend_from_slice(b"BI /W 16 /H 16 /BPC 8 /CS /G ID ");
3075 for b in 0u8..=255 {
3077 content.push(b);
3078 }
3079 content.extend_from_slice(b" EI Q");
3080
3081 let result = ContentParser::parse_content(&content);
3082 assert!(
3083 result.is_ok(),
3084 "Parsing inline image with all byte values failed: {:?}",
3085 result.err()
3086 );
3087 }
3088
3089 #[test]
3090 fn test_inline_image_ei_detection() {
3091 let content = b"BI /W 2 /H 1 /BPC 8 /CS /G ID \x45\x49\x00\n EI Q";
3094 let result = ContentParser::parse_content(content);
3096 assert!(result.is_ok(), "EI detection failed: {:?}", result.err());
3097
3098 let ops = result.unwrap();
3099 let has_inline = ops
3100 .iter()
3101 .any(|op| matches!(op, ContentOperation::InlineImage { .. }));
3102 assert!(has_inline, "Expected InlineImage operation");
3103 }
3104
3105 #[test]
3106 fn test_tokenizer_no_infinite_loop_on_consecutive_delimiters() {
3107 let input = b"q {{{}}})))) Q";
3109 let mut tokenizer = ContentTokenizer::new(input);
3110
3111 let mut tokens = Vec::new();
3112 while let Some(token) = tokenizer.next_token().unwrap() {
3113 tokens.push(token);
3114 if tokens.len() > 100 {
3115 panic!("Tokenizer produced too many tokens — possible infinite loop");
3116 }
3117 }
3118
3119 assert!(tokens.contains(&Token::Operator("q".to_string())));
3120 assert!(tokens.contains(&Token::Operator("Q".to_string())));
3121 }
3122
3123 #[test]
3124 fn test_content_parser_inline_image_produces_correct_operation() {
3125 let content = b"BI /W 4 /H 4 /BPC 8 /CS /G ID \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F EI";
3127 let result = ContentParser::parse_content(content);
3128 assert!(result.is_ok(), "Parse failed: {:?}", result.err());
3129
3130 let ops = result.unwrap();
3131 assert_eq!(
3132 ops.len(),
3133 1,
3134 "Expected exactly 1 operation, got {}",
3135 ops.len()
3136 );
3137
3138 if let ContentOperation::InlineImage { params, data } = &ops[0] {
3139 assert_eq!(params.get("Width"), Some(&Object::Integer(4)));
3140 assert_eq!(params.get("Height"), Some(&Object::Integer(4)));
3141 assert_eq!(params.get("BitsPerComponent"), Some(&Object::Integer(8)));
3142 assert!(!data.is_empty(), "Image data should not be empty");
3143 } else {
3144 panic!("Expected InlineImage operation, got {:?}", ops[0]);
3145 }
3146 }
3147
3148 #[test]
3149 fn test_octal_escape_overflow_777() {
3150 let mut tokenizer = ContentTokenizer::new(b"(\\777)");
3154 let token = tokenizer.next_token().unwrap().unwrap();
3155 match token {
3156 Token::String(data) => assert_eq!(data, vec![0xFF]),
3157 _ => panic!("Expected string token"),
3158 }
3159 }
3160
3161 #[test]
3162 fn test_octal_escape_overflow_400() {
3163 let mut tokenizer = ContentTokenizer::new(b"(\\400)");
3166 let token = tokenizer.next_token().unwrap().unwrap();
3167 match token {
3168 Token::String(data) => assert_eq!(data, vec![0x00]),
3169 _ => panic!("Expected string token"),
3170 }
3171 }
3172
3173 #[test]
3174 fn test_octal_escape_overflow_577() {
3175 let mut tokenizer = ContentTokenizer::new(b"(\\577)");
3178 let token = tokenizer.next_token().unwrap().unwrap();
3179 match token {
3180 Token::String(data) => assert_eq!(data, vec![0x7F]),
3181 _ => panic!("Expected string token"),
3182 }
3183 }
3184
3185 #[test]
3186 fn test_octal_escape_max_valid_377() {
3187 let mut tokenizer = ContentTokenizer::new(b"(\\377)");
3189 let token = tokenizer.next_token().unwrap().unwrap();
3190 match token {
3191 Token::String(data) => assert_eq!(data, vec![0xFF]),
3192 _ => panic!("Expected string token"),
3193 }
3194 }
3195
3196 #[test]
3197 fn test_octal_escape_overflow_mixed_with_valid() {
3198 let mut tokenizer = ContentTokenizer::new(b"(A\\777B\\101C)");
3200 let token = tokenizer.next_token().unwrap().unwrap();
3201 match token {
3202 Token::String(data) => {
3203 assert_eq!(data, vec![b'A', 0xFF, b'B', b'A', b'C']);
3204 }
3205 _ => panic!("Expected string token"),
3206 }
3207 }
3208}