1use super::lexer::{Lexer, Token};
36use super::{ParseError, ParseOptions, ParseResult};
37use std::collections::HashMap;
38use std::io::Read;
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
60pub struct PdfName(pub String);
61
62#[derive(Debug, Clone, PartialEq)]
88pub struct PdfString(pub Vec<u8>);
89
90#[derive(Debug, Clone, PartialEq)]
117pub struct PdfArray(pub Vec<PdfObject>);
118
119#[derive(Debug, Clone, PartialEq)]
147pub struct PdfDictionary(pub HashMap<PdfName, PdfObject>);
148
149#[derive(Debug, Clone, PartialEq)]
186pub struct PdfStream {
187 pub dict: PdfDictionary,
189 pub data: Vec<u8>,
191}
192
193pub static EMPTY_PDF_ARRAY: PdfArray = PdfArray(Vec::new());
195
196impl PdfStream {
197 pub fn decode(&self, options: &ParseOptions) -> ParseResult<Vec<u8>> {
232 super::filters::decode_stream(&self.data, &self.dict, options)
233 }
234
235 pub fn raw_data(&self) -> &[u8] {
249 &self.data
250 }
251}
252
253#[derive(Debug, Clone, PartialEq)]
289pub enum PdfObject {
290 Null,
292 Boolean(bool),
294 Integer(i64),
296 Real(f64),
298 String(PdfString),
300 Name(PdfName),
302 Array(PdfArray),
304 Dictionary(PdfDictionary),
306 Stream(PdfStream),
308 Reference(u32, u16),
310}
311
312impl PdfObject {
313 pub fn parse<R: Read + std::io::Seek>(lexer: &mut Lexer<R>) -> ParseResult<Self> {
349 let token = lexer.next_token()?;
350 Self::parse_from_token(lexer, token)
351 }
352
353 pub fn parse_with_options<R: Read + std::io::Seek>(
355 lexer: &mut Lexer<R>,
356 options: &super::ParseOptions,
357 ) -> ParseResult<Self> {
358 let token = lexer.next_token()?;
359 Self::parse_from_token_with_options(lexer, token, options)
360 }
361
362 fn parse_from_token<R: Read + std::io::Seek>(
364 lexer: &mut Lexer<R>,
365 token: Token,
366 ) -> ParseResult<Self> {
367 Self::parse_from_token_with_options(lexer, token, &super::ParseOptions::default())
368 }
369
370 fn parse_from_token_with_options<R: Read + std::io::Seek>(
372 lexer: &mut Lexer<R>,
373 token: Token,
374 options: &super::ParseOptions,
375 ) -> ParseResult<Self> {
376 match token {
377 Token::Null => Ok(PdfObject::Null),
378 Token::Boolean(b) => Ok(PdfObject::Boolean(b)),
379 Token::Integer(i) => {
380 if !(0..=9999999).contains(&i) {
382 return Ok(PdfObject::Integer(i));
383 }
384
385 match lexer.next_token()? {
387 Token::Integer(gen) if (0..=65535).contains(&gen) => {
388 match lexer.next_token()? {
390 Token::Name(s) if s == "R" => {
391 Ok(PdfObject::Reference(i as u32, gen as u16))
392 }
393 token => {
394 lexer.push_token(token);
396 lexer.push_token(Token::Integer(gen));
397 Ok(PdfObject::Integer(i))
398 }
399 }
400 }
401 token => {
402 lexer.push_token(token);
404 Ok(PdfObject::Integer(i))
405 }
406 }
407 }
408 Token::Real(r) => Ok(PdfObject::Real(r)),
409 Token::String(s) => Ok(PdfObject::String(PdfString(s))),
410 Token::Name(n) => Ok(PdfObject::Name(PdfName(n))),
411 Token::ArrayStart => Self::parse_array_with_options(lexer, options),
412 Token::DictStart => Self::parse_dictionary_or_stream_with_options(lexer, options),
413 Token::Comment(_) => {
414 Self::parse_with_options(lexer, options)
416 }
417 Token::StartXRef => {
418 Err(ParseError::SyntaxError {
420 position: 0,
421 message: "StartXRef encountered - this is not a PDF object".to_string(),
422 })
423 }
424 Token::Eof => Err(ParseError::SyntaxError {
425 position: 0,
426 message: "Unexpected end of file".to_string(),
427 }),
428 _ => Err(ParseError::UnexpectedToken {
429 expected: "PDF object".to_string(),
430 found: format!("{token:?}"),
431 }),
432 }
433 }
434
435 fn parse_array_with_options<R: Read + std::io::Seek>(
437 lexer: &mut Lexer<R>,
438 options: &super::ParseOptions,
439 ) -> ParseResult<Self> {
440 let mut elements = Vec::new();
441
442 loop {
443 let token = lexer.next_token()?;
444 match token {
445 Token::ArrayEnd => break,
446 Token::Comment(_) => continue, _ => {
448 let obj = Self::parse_from_token_with_options(lexer, token, options)?;
449 elements.push(obj);
450 }
451 }
452 }
453
454 Ok(PdfObject::Array(PdfArray(elements)))
455 }
456
457 fn parse_dictionary_or_stream_with_options<R: Read + std::io::Seek>(
459 lexer: &mut Lexer<R>,
460 options: &super::ParseOptions,
461 ) -> ParseResult<Self> {
462 let dict = Self::parse_dictionary_inner_with_options(lexer, options)?;
463
464 loop {
466 let token = lexer.next_token()?;
467 match token {
469 Token::Stream => {
470 let stream_data = Self::parse_stream_data_with_options(lexer, &dict, options)?;
472 return Ok(PdfObject::Stream(PdfStream {
473 dict,
474 data: stream_data,
475 }));
476 }
477 Token::Comment(_) => {
478 continue;
480 }
481 Token::StartXRef => {
482 lexer.push_token(token);
486 return Ok(PdfObject::Dictionary(dict));
487 }
488 _ => {
489 lexer.push_token(token);
493 return Ok(PdfObject::Dictionary(dict));
494 }
495 }
496 }
497 }
498
499 pub(crate) fn parse_dictionary_inner_with_options<R: Read + std::io::Seek>(
507 lexer: &mut Lexer<R>,
508 options: &super::ParseOptions,
509 ) -> ParseResult<PdfDictionary> {
510 let mut dict = HashMap::new();
511
512 loop {
513 let token = lexer.next_token()?;
514 match token {
515 Token::DictEnd => break,
516 Token::Comment(_) => continue, Token::Name(key) => {
518 let value = Self::parse_with_options(lexer, options)?;
519 dict.insert(PdfName(key), value);
520 }
521 _ => {
522 return Err(ParseError::UnexpectedToken {
523 expected: "dictionary key (name) or >>".to_string(),
524 found: format!("{token:?}"),
525 });
526 }
527 }
528 }
529
530 Ok(PdfDictionary(dict))
531 }
532
533 fn parse_stream_data_with_options<R: Read + std::io::Seek>(
535 lexer: &mut Lexer<R>,
536 dict: &PdfDictionary,
537 options: &super::ParseOptions,
538 ) -> ParseResult<Vec<u8>> {
539 let length = dict
541 .0
542 .get(&PdfName("Length".to_string()))
543 .or_else(|| {
544 if options.lenient_streams {
546 if options.collect_warnings {
547 tracing::debug!("Warning: Missing Length key in stream dictionary, will search for endstream marker");
548 }
549 Some(&PdfObject::Integer(-1))
551 } else {
552 None
553 }
554 })
555 .ok_or_else(|| ParseError::MissingKey("Length".to_string()))?;
556
557 let length = match length {
558 PdfObject::Integer(len) => {
559 if *len == -1 {
560 usize::MAX } else if *len < 0 {
563 if options.lenient_streams {
571 if options.collect_warnings {
572 tracing::debug!(
573 "Warning: negative stream /Length {len}; searching for endstream marker"
574 );
575 }
576 usize::MAX
577 } else {
578 return Err(ParseError::SyntaxError {
579 position: lexer.position(),
580 message: format!("Invalid negative stream length: {len}"),
581 });
582 }
583 } else {
584 *len as usize
585 }
586 }
587 PdfObject::Reference(obj_num, gen_num) => {
588 if options.lenient_streams {
591 if options.collect_warnings {
592 tracing::debug!("Warning: Stream length is an indirect reference ({obj_num} {gen_num} R). Using unlimited endstream search.");
593 }
594 usize::MAX - 1 } else {
597 return Err(ParseError::SyntaxError {
598 position: lexer.position(),
599 message: format!(
600 "Stream length reference ({obj_num} {gen_num} R) requires lenient mode"
601 ),
602 });
603 }
604 }
605 _ => {
606 return Err(ParseError::SyntaxError {
607 position: lexer.position(),
608 message: "Invalid stream length type".to_string(),
609 });
610 }
611 };
612
613 lexer.read_newline()?;
615
616 let mut stream_data = if length == usize::MAX || length == usize::MAX - 1 {
618 let is_indirect_ref = length == usize::MAX - 1;
620 let is_dct_decode = dict
622 .0
623 .get(&PdfName("Filter".to_string()))
624 .map(|filter| match filter {
625 PdfObject::Name(name) => name.0 == "DCTDecode",
626 PdfObject::Array(arr) => arr
627 .0
628 .iter()
629 .any(|f| matches!(f, PdfObject::Name(name) if name.0 == "DCTDecode")),
630 _ => false,
631 })
632 .unwrap_or(false);
633
634 let mut data = Vec::new();
635 let max_search = if is_indirect_ref {
638 10 * 1024 * 1024 } else {
640 65536 };
642 let mut found_endstream = false;
643
644 if is_indirect_ref && options.collect_warnings {
645 tracing::debug!("Searching for endstream without fixed limit (up to {}MB) for indirect reference", max_search / 1024 / 1024);
646 }
647
648 for i in 0..max_search {
649 match lexer.peek_byte() {
650 Ok(b) => {
651 if b == b'e' {
653 let mut temp_buffer = vec![b'e'];
655 let expected = b"ndstream";
656 let mut is_endstream = true;
657
658 let _ = lexer.read_byte();
660
661 for &expected_byte in expected.iter() {
663 match lexer.read_byte() {
664 Ok(byte) => {
665 temp_buffer.push(byte);
666 if byte != expected_byte {
667 is_endstream = false;
668 break;
669 }
670 }
671 Err(_) => {
672 is_endstream = false;
673 break;
674 }
675 }
676 }
677
678 if is_endstream && temp_buffer.len() == 9 {
679 found_endstream = true;
681 if is_dct_decode {
682 tracing::debug!("🔍 [PARSER] Found 'endstream' after reading {} bytes for DCTDecode", data.len());
683 }
684 break;
685 } else {
686 data.extend(temp_buffer);
689 continue;
690 }
691 } else {
692 data.push(lexer.read_byte()?);
694 }
695
696 if is_dct_decode && i % 10000 == 0 && i > 0 {
698 }
700 }
701 Err(_) => {
702 break;
704 }
705 }
706 }
707
708 if !found_endstream && !options.lenient_streams {
709 return Err(ParseError::SyntaxError {
710 position: lexer.position(),
711 message: "Could not find endstream marker".to_string(),
712 });
713 }
714
715 if is_dct_decode {
716 tracing::debug!(
719 "DCTDecode stream: read {} bytes (full stream based on endstream marker)",
720 data.len()
721 );
722 }
723
724 data
725 } else {
726 lexer.read_bytes(length)?
727 };
728
729 lexer.skip_whitespace()?;
731
732 let peek_result = lexer.peek_token();
734
735 match peek_result {
736 Ok(Token::EndStream) => {
737 lexer.next_token()?;
739 Ok(stream_data)
740 }
741 Ok(other_token) => {
742 if options.lenient_streams {
743 let is_dct_decode = dict
745 .0
746 .get(&PdfName("Filter".to_string()))
747 .map(|filter| match filter {
748 PdfObject::Name(name) => name.0 == "DCTDecode",
749 PdfObject::Array(arr) => arr.0.iter().any(
750 |f| matches!(f, PdfObject::Name(name) if name.0 == "DCTDecode"),
751 ),
752 _ => false,
753 })
754 .unwrap_or(false);
755
756 if is_dct_decode {
757 tracing::debug!("Warning: DCTDecode stream length mismatch at {length} bytes, but not extending JPEG data");
760
761 if let Some(additional_bytes) =
763 lexer.find_keyword_ahead("endstream", options.max_recovery_bytes)?
764 {
765 let _ = lexer.read_bytes(additional_bytes)?;
767 }
768
769 lexer.skip_whitespace()?;
771 lexer.expect_keyword("endstream")?;
772
773 Ok(stream_data)
774 } else {
775 tracing::debug!("Warning: Stream length mismatch. Expected 'endstream' after {length} bytes, got {other_token:?}");
777
778 let search_limit = if length == usize::MAX - 1 {
780 10 * 1024 * 1024 } else {
782 options.max_recovery_bytes
783 };
784
785 if let Some(additional_bytes) =
786 lexer.find_keyword_ahead("endstream", search_limit)?
787 {
788 let extra_data = lexer.read_bytes(additional_bytes)?;
790 stream_data.extend_from_slice(&extra_data);
791
792 let actual_length = stream_data.len();
793 tracing::debug!(
794 "Stream length corrected: declared={length}, actual={actual_length}"
795 );
796
797 lexer.skip_whitespace()?;
799 lexer.expect_keyword("endstream")?;
800
801 Ok(stream_data)
802 } else {
803 Err(ParseError::SyntaxError {
805 position: lexer.position(),
806 message: format!(
807 "Could not find 'endstream' within {} bytes",
808 search_limit
809 ),
810 })
811 }
812 }
813 } else {
814 Err(ParseError::UnexpectedToken {
816 expected: "endstream".to_string(),
817 found: format!("{other_token:?}"),
818 })
819 }
820 }
821 Err(e) => {
822 if options.lenient_streams {
823 tracing::debug!(
825 "Warning: Stream length mismatch. Could not peek next token after {length} bytes"
826 );
827
828 let search_limit = if length == usize::MAX - 1 {
830 10 * 1024 * 1024 } else {
832 options.max_recovery_bytes
833 };
834
835 if let Some(additional_bytes) =
836 lexer.find_keyword_ahead("endstream", search_limit)?
837 {
838 let extra_data = lexer.read_bytes(additional_bytes)?;
840 stream_data.extend_from_slice(&extra_data);
841
842 let actual_length = stream_data.len();
843 tracing::debug!(
844 "Stream length corrected: declared={length}, actual={actual_length}"
845 );
846
847 lexer.skip_whitespace()?;
849 lexer.expect_keyword("endstream")?;
850
851 Ok(stream_data)
852 } else {
853 Err(ParseError::SyntaxError {
855 position: lexer.position(),
856 message: format!(
857 "Could not find 'endstream' within {} bytes",
858 search_limit
859 ),
860 })
861 }
862 } else {
863 Err(e)
865 }
866 }
867 }
868 }
869
870 pub fn is_null(&self) -> bool {
881 matches!(self, PdfObject::Null)
882 }
883
884 pub fn as_bool(&self) -> Option<bool> {
902 match self {
903 PdfObject::Boolean(b) => Some(*b),
904 _ => None,
905 }
906 }
907
908 pub fn as_integer(&self) -> Option<i64> {
910 match self {
911 PdfObject::Integer(i) => Some(*i),
912 _ => None,
913 }
914 }
915
916 pub fn as_real(&self) -> Option<f64> {
937 match self {
938 PdfObject::Real(r) => Some(*r),
939 PdfObject::Integer(i) => Some(*i as f64),
940 _ => None,
941 }
942 }
943
944 pub fn as_string(&self) -> Option<&PdfString> {
946 match self {
947 PdfObject::String(s) => Some(s),
948 _ => None,
949 }
950 }
951
952 pub fn as_name(&self) -> Option<&PdfName> {
954 match self {
955 PdfObject::Name(n) => Some(n),
956 _ => None,
957 }
958 }
959
960 pub fn as_array(&self) -> Option<&PdfArray> {
962 match self {
963 PdfObject::Array(a) => Some(a),
964 _ => None,
965 }
966 }
967
968 pub fn as_dict(&self) -> Option<&PdfDictionary> {
970 match self {
971 PdfObject::Dictionary(d) => Some(d),
972 PdfObject::Stream(s) => Some(&s.dict),
973 _ => None,
974 }
975 }
976
977 pub fn as_stream(&self) -> Option<&PdfStream> {
979 match self {
980 PdfObject::Stream(s) => Some(s),
981 _ => None,
982 }
983 }
984
985 pub fn as_reference(&self) -> Option<(u32, u16)> {
1005 match self {
1006 PdfObject::Reference(obj, gen) => Some((*obj, *gen)),
1007 _ => None,
1008 }
1009 }
1010}
1011
1012impl Default for PdfDictionary {
1013 fn default() -> Self {
1014 Self::new()
1015 }
1016}
1017
1018impl PdfDictionary {
1019 pub fn new() -> Self {
1030 PdfDictionary(HashMap::new())
1031 }
1032
1033 pub fn get(&self, key: &str) -> Option<&PdfObject> {
1056 self.0.get(&PdfName(key.to_string()))
1057 }
1058
1059 pub fn insert(&mut self, key: String, value: PdfObject) {
1061 self.0.insert(PdfName(key), value);
1062 }
1063
1064 pub fn contains_key(&self, key: &str) -> bool {
1066 self.0.contains_key(&PdfName(key.to_string()))
1067 }
1068
1069 pub fn get_type(&self) -> Option<&str> {
1095 self.get("Type")
1096 .and_then(|obj| obj.as_name())
1097 .map(|n| n.0.as_str())
1098 }
1099}
1100
1101impl Default for PdfArray {
1102 fn default() -> Self {
1103 Self::new()
1104 }
1105}
1106
1107impl PdfArray {
1108 pub fn new() -> Self {
1110 PdfArray(Vec::new())
1111 }
1112
1113 pub fn len(&self) -> usize {
1115 self.0.len()
1116 }
1117
1118 pub fn is_empty(&self) -> bool {
1120 self.0.is_empty()
1121 }
1122
1123 pub fn get(&self, index: usize) -> Option<&PdfObject> {
1147 self.0.get(index)
1148 }
1149
1150 pub fn push(&mut self, obj: PdfObject) {
1152 self.0.push(obj);
1153 }
1154}
1155
1156impl PdfString {
1157 pub fn new(data: Vec<u8>) -> Self {
1159 PdfString(data)
1160 }
1161
1162 pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
1183 std::str::from_utf8(&self.0)
1184 }
1185
1186 pub fn as_bytes(&self) -> &[u8] {
1188 &self.0
1189 }
1190}
1191
1192impl PdfName {
1193 pub fn new(name: String) -> Self {
1195 PdfName(name)
1196 }
1197
1198 pub fn as_str(&self) -> &str {
1200 &self.0
1201 }
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206 use super::*;
1207 use crate::parser::lexer::Lexer;
1208 use crate::parser::ParseOptions;
1209 use std::collections::HashMap;
1210 use std::io::Cursor;
1211
1212 #[test]
1213 fn test_parse_simple_objects() {
1214 let input = b"null true false 123 -456 3.14 /Name (Hello)";
1215 let mut lexer = Lexer::new(Cursor::new(input));
1216
1217 assert_eq!(PdfObject::parse(&mut lexer).unwrap(), PdfObject::Null);
1218 assert_eq!(
1219 PdfObject::parse(&mut lexer).unwrap(),
1220 PdfObject::Boolean(true)
1221 );
1222 assert_eq!(
1223 PdfObject::parse(&mut lexer).unwrap(),
1224 PdfObject::Boolean(false)
1225 );
1226 assert_eq!(
1227 PdfObject::parse(&mut lexer).unwrap(),
1228 PdfObject::Integer(123)
1229 );
1230 assert_eq!(
1231 PdfObject::parse(&mut lexer).unwrap(),
1232 PdfObject::Integer(-456)
1233 );
1234 assert_eq!(PdfObject::parse(&mut lexer).unwrap(), PdfObject::Real(3.14));
1235 assert_eq!(
1236 PdfObject::parse(&mut lexer).unwrap(),
1237 PdfObject::Name(PdfName("Name".to_string()))
1238 );
1239 assert_eq!(
1240 PdfObject::parse(&mut lexer).unwrap(),
1241 PdfObject::String(PdfString(b"Hello".to_vec()))
1242 );
1243 }
1244
1245 #[test]
1246 fn test_parse_array() {
1247 let input = b"[100 200 300 /Name (test)]";
1249 let mut lexer = Lexer::new(Cursor::new(input));
1250
1251 let obj = PdfObject::parse(&mut lexer).unwrap();
1252 let array = obj.as_array().unwrap();
1253
1254 assert_eq!(array.len(), 5);
1255 assert_eq!(array.get(0).unwrap().as_integer(), Some(100));
1256 assert_eq!(array.get(1).unwrap().as_integer(), Some(200));
1257 assert_eq!(array.get(2).unwrap().as_integer(), Some(300));
1258 assert_eq!(array.get(3).unwrap().as_name().unwrap().as_str(), "Name");
1259 assert_eq!(
1260 array.get(4).unwrap().as_string().unwrap().as_bytes(),
1261 b"test"
1262 );
1263 }
1264
1265 #[test]
1266 fn test_parse_array_with_references() {
1267 let input = b"[1 0 R 2 0 R]";
1269 let mut lexer = Lexer::new(Cursor::new(input));
1270
1271 let obj = PdfObject::parse(&mut lexer).unwrap();
1272 let array = obj.as_array().unwrap();
1273
1274 assert_eq!(array.len(), 2);
1275 assert!(array.get(0).unwrap().as_reference().is_some());
1276 assert!(array.get(1).unwrap().as_reference().is_some());
1277 }
1278
1279 #[test]
1280 fn test_parse_dictionary() {
1281 let input = b"<< /Type /Page /Parent 1 0 R /MediaBox [0 0 612 792] >>";
1282 let mut lexer = Lexer::new(Cursor::new(input));
1283
1284 let obj = PdfObject::parse(&mut lexer).unwrap();
1285 let dict = obj.as_dict().unwrap();
1286
1287 assert_eq!(dict.get_type(), Some("Page"));
1288 assert!(dict.get("Parent").unwrap().as_reference().is_some());
1289 assert!(dict.get("MediaBox").unwrap().as_array().is_some());
1290 }
1291
1292 mod comprehensive_tests {
1294 use super::*;
1295
1296 #[test]
1297 fn test_pdf_object_null() {
1298 let obj = PdfObject::Null;
1299 assert!(obj.is_null());
1300 assert_eq!(obj.as_bool(), None);
1301 assert_eq!(obj.as_integer(), None);
1302 assert_eq!(obj.as_real(), None);
1303 assert_eq!(obj.as_string(), None);
1304 assert_eq!(obj.as_name(), None);
1305 assert_eq!(obj.as_array(), None);
1306 assert_eq!(obj.as_dict(), None);
1307 assert_eq!(obj.as_stream(), None);
1308 assert_eq!(obj.as_reference(), None);
1309 }
1310
1311 #[test]
1312 fn test_pdf_object_boolean() {
1313 let obj_true = PdfObject::Boolean(true);
1314 let obj_false = PdfObject::Boolean(false);
1315
1316 assert!(!obj_true.is_null());
1317 assert_eq!(obj_true.as_bool(), Some(true));
1318 assert_eq!(obj_false.as_bool(), Some(false));
1319
1320 assert_eq!(obj_true.as_integer(), None);
1321 assert_eq!(obj_true.as_real(), None);
1322 assert_eq!(obj_true.as_string(), None);
1323 assert_eq!(obj_true.as_name(), None);
1324 assert_eq!(obj_true.as_array(), None);
1325 assert_eq!(obj_true.as_dict(), None);
1326 assert_eq!(obj_true.as_stream(), None);
1327 assert_eq!(obj_true.as_reference(), None);
1328 }
1329
1330 #[test]
1331 fn test_pdf_object_integer() {
1332 let obj = PdfObject::Integer(42);
1333
1334 assert!(!obj.is_null());
1335 assert_eq!(obj.as_bool(), None);
1336 assert_eq!(obj.as_integer(), Some(42));
1337 assert_eq!(obj.as_real(), Some(42.0)); assert_eq!(obj.as_string(), None);
1339 assert_eq!(obj.as_name(), None);
1340 assert_eq!(obj.as_array(), None);
1341 assert_eq!(obj.as_dict(), None);
1342 assert_eq!(obj.as_stream(), None);
1343 assert_eq!(obj.as_reference(), None);
1344
1345 let obj_neg = PdfObject::Integer(-123);
1347 assert_eq!(obj_neg.as_integer(), Some(-123));
1348 assert_eq!(obj_neg.as_real(), Some(-123.0));
1349
1350 let obj_large = PdfObject::Integer(9999999999);
1352 assert_eq!(obj_large.as_integer(), Some(9999999999));
1353 assert_eq!(obj_large.as_real(), Some(9999999999.0));
1354 }
1355
1356 #[test]
1357 fn test_pdf_object_real() {
1358 let obj = PdfObject::Real(3.14159);
1359
1360 assert!(!obj.is_null());
1361 assert_eq!(obj.as_bool(), None);
1362 assert_eq!(obj.as_integer(), None);
1363 assert_eq!(obj.as_real(), Some(3.14159));
1364 assert_eq!(obj.as_string(), None);
1365 assert_eq!(obj.as_name(), None);
1366 assert_eq!(obj.as_array(), None);
1367 assert_eq!(obj.as_dict(), None);
1368 assert_eq!(obj.as_stream(), None);
1369 assert_eq!(obj.as_reference(), None);
1370
1371 let obj_neg = PdfObject::Real(-2.71828);
1373 assert_eq!(obj_neg.as_real(), Some(-2.71828));
1374
1375 let obj_zero = PdfObject::Real(0.0);
1377 assert_eq!(obj_zero.as_real(), Some(0.0));
1378
1379 let obj_small = PdfObject::Real(0.000001);
1381 assert_eq!(obj_small.as_real(), Some(0.000001));
1382
1383 let obj_large = PdfObject::Real(1e10);
1385 assert_eq!(obj_large.as_real(), Some(1e10));
1386 }
1387
1388 #[test]
1389 fn test_pdf_object_string() {
1390 let string_data = b"Hello World".to_vec();
1391 let pdf_string = PdfString(string_data.clone());
1392 let obj = PdfObject::String(pdf_string);
1393
1394 assert!(!obj.is_null());
1395 assert_eq!(obj.as_bool(), None);
1396 assert_eq!(obj.as_integer(), None);
1397 assert_eq!(obj.as_real(), None);
1398 assert!(obj.as_string().is_some());
1399 assert_eq!(obj.as_string().unwrap().as_bytes(), string_data);
1400 assert_eq!(obj.as_name(), None);
1401 assert_eq!(obj.as_array(), None);
1402 assert_eq!(obj.as_dict(), None);
1403 assert_eq!(obj.as_stream(), None);
1404 assert_eq!(obj.as_reference(), None);
1405 }
1406
1407 #[test]
1408 fn test_pdf_object_name() {
1409 let name_str = "Type".to_string();
1410 let pdf_name = PdfName(name_str.clone());
1411 let obj = PdfObject::Name(pdf_name);
1412
1413 assert!(!obj.is_null());
1414 assert_eq!(obj.as_bool(), None);
1415 assert_eq!(obj.as_integer(), None);
1416 assert_eq!(obj.as_real(), None);
1417 assert_eq!(obj.as_string(), None);
1418 assert!(obj.as_name().is_some());
1419 assert_eq!(obj.as_name().unwrap().as_str(), name_str);
1420 assert_eq!(obj.as_array(), None);
1421 assert_eq!(obj.as_dict(), None);
1422 assert_eq!(obj.as_stream(), None);
1423 assert_eq!(obj.as_reference(), None);
1424 }
1425
1426 #[test]
1427 fn test_pdf_object_array() {
1428 let mut array = PdfArray::new();
1429 array.push(PdfObject::Integer(1));
1430 array.push(PdfObject::Integer(2));
1431 array.push(PdfObject::Integer(3));
1432 let obj = PdfObject::Array(array);
1433
1434 assert!(!obj.is_null());
1435 assert_eq!(obj.as_bool(), None);
1436 assert_eq!(obj.as_integer(), None);
1437 assert_eq!(obj.as_real(), None);
1438 assert_eq!(obj.as_string(), None);
1439 assert_eq!(obj.as_name(), None);
1440 assert!(obj.as_array().is_some());
1441 assert_eq!(obj.as_array().unwrap().len(), 3);
1442 assert_eq!(obj.as_dict(), None);
1443 assert_eq!(obj.as_stream(), None);
1444 assert_eq!(obj.as_reference(), None);
1445 }
1446
1447 #[test]
1448 fn test_pdf_object_dictionary() {
1449 let mut dict = PdfDictionary::new();
1450 dict.insert(
1451 "Type".to_string(),
1452 PdfObject::Name(PdfName("Page".to_string())),
1453 );
1454 dict.insert("Count".to_string(), PdfObject::Integer(5));
1455 let obj = PdfObject::Dictionary(dict);
1456
1457 assert!(!obj.is_null());
1458 assert_eq!(obj.as_bool(), None);
1459 assert_eq!(obj.as_integer(), None);
1460 assert_eq!(obj.as_real(), None);
1461 assert_eq!(obj.as_string(), None);
1462 assert_eq!(obj.as_name(), None);
1463 assert_eq!(obj.as_array(), None);
1464 assert!(obj.as_dict().is_some());
1465 assert_eq!(obj.as_dict().unwrap().0.len(), 2);
1466 assert_eq!(obj.as_stream(), None);
1467 assert_eq!(obj.as_reference(), None);
1468 }
1469
1470 #[test]
1471 fn test_pdf_object_stream() {
1472 let mut dict = PdfDictionary::new();
1473 dict.insert("Length".to_string(), PdfObject::Integer(13));
1474 let data = b"Hello, World!".to_vec();
1475 let stream = PdfStream { dict, data };
1476 let obj = PdfObject::Stream(stream);
1477
1478 assert!(!obj.is_null());
1479 assert_eq!(obj.as_bool(), None);
1480 assert_eq!(obj.as_integer(), None);
1481 assert_eq!(obj.as_real(), None);
1482 assert_eq!(obj.as_string(), None);
1483 assert_eq!(obj.as_name(), None);
1484 assert_eq!(obj.as_array(), None);
1485 assert!(obj.as_dict().is_some()); assert!(obj.as_stream().is_some());
1487 assert_eq!(obj.as_stream().unwrap().raw_data(), b"Hello, World!");
1488 assert_eq!(obj.as_reference(), None);
1489 }
1490
1491 #[test]
1492 fn test_pdf_object_reference() {
1493 let obj = PdfObject::Reference(42, 0);
1494
1495 assert!(!obj.is_null());
1496 assert_eq!(obj.as_bool(), None);
1497 assert_eq!(obj.as_integer(), None);
1498 assert_eq!(obj.as_real(), None);
1499 assert_eq!(obj.as_string(), None);
1500 assert_eq!(obj.as_name(), None);
1501 assert_eq!(obj.as_array(), None);
1502 assert_eq!(obj.as_dict(), None);
1503 assert_eq!(obj.as_stream(), None);
1504 assert_eq!(obj.as_reference(), Some((42, 0)));
1505
1506 let obj_gen = PdfObject::Reference(123, 5);
1508 assert_eq!(obj_gen.as_reference(), Some((123, 5)));
1509 }
1510
1511 #[test]
1512 fn test_pdf_string_methods() {
1513 let string_data = b"Hello, World!".to_vec();
1514 let pdf_string = PdfString(string_data.clone());
1515
1516 assert_eq!(pdf_string.as_bytes(), string_data);
1517 assert_eq!(pdf_string.as_str().unwrap(), "Hello, World!");
1518 assert_eq!(pdf_string.0.len(), 13);
1519 assert!(!pdf_string.0.is_empty());
1520
1521 let empty_string = PdfString(vec![]);
1523 assert!(empty_string.0.is_empty());
1524 assert_eq!(empty_string.0.len(), 0);
1525
1526 let binary_data = vec![0xFF, 0xFE, 0x00, 0x48, 0x00, 0x69]; let binary_string = PdfString(binary_data.clone());
1529 assert_eq!(binary_string.as_bytes(), binary_data);
1530 assert!(binary_string.as_str().is_err()); }
1532
1533 #[test]
1534 fn test_pdf_name_methods() {
1535 let name_str = "Type".to_string();
1536 let pdf_name = PdfName(name_str.clone());
1537
1538 assert_eq!(pdf_name.as_str(), name_str);
1539 assert_eq!(pdf_name.0.len(), 4);
1540 assert!(!pdf_name.0.is_empty());
1541
1542 let empty_name = PdfName("".to_string());
1544 assert!(empty_name.0.is_empty());
1545 assert_eq!(empty_name.0.len(), 0);
1546
1547 let special_name = PdfName("Font#20Name".to_string());
1549 assert_eq!(special_name.as_str(), "Font#20Name");
1550 assert_eq!(special_name.0.len(), 11);
1551 }
1552
1553 #[test]
1554 fn test_pdf_array_methods() {
1555 let mut array = PdfArray::new();
1556 assert_eq!(array.len(), 0);
1557 assert!(array.is_empty());
1558
1559 array.push(PdfObject::Integer(1));
1561 array.push(PdfObject::Integer(2));
1562 array.push(PdfObject::Integer(3));
1563
1564 assert_eq!(array.len(), 3);
1565 assert!(!array.is_empty());
1566
1567 assert_eq!(array.get(0).unwrap().as_integer(), Some(1));
1569 assert_eq!(array.get(1).unwrap().as_integer(), Some(2));
1570 assert_eq!(array.get(2).unwrap().as_integer(), Some(3));
1571 assert!(array.get(3).is_none());
1572
1573 let values: Vec<i64> = array.0.iter().filter_map(|obj| obj.as_integer()).collect();
1575 assert_eq!(values, vec![1, 2, 3]);
1576
1577 let mut mixed_array = PdfArray::new();
1579 mixed_array.push(PdfObject::Integer(42));
1580 mixed_array.push(PdfObject::Real(3.14));
1581 mixed_array.push(PdfObject::String(PdfString(b"text".to_vec())));
1582 mixed_array.push(PdfObject::Name(PdfName("Name".to_string())));
1583 mixed_array.push(PdfObject::Boolean(true));
1584 mixed_array.push(PdfObject::Null);
1585
1586 assert_eq!(mixed_array.len(), 6);
1587 assert_eq!(mixed_array.get(0).unwrap().as_integer(), Some(42));
1588 assert_eq!(mixed_array.get(1).unwrap().as_real(), Some(3.14));
1589 assert_eq!(
1590 mixed_array.get(2).unwrap().as_string().unwrap().as_bytes(),
1591 b"text"
1592 );
1593 assert_eq!(
1594 mixed_array.get(3).unwrap().as_name().unwrap().as_str(),
1595 "Name"
1596 );
1597 assert_eq!(mixed_array.get(4).unwrap().as_bool(), Some(true));
1598 assert!(mixed_array.get(5).unwrap().is_null());
1599 }
1600
1601 #[test]
1602 fn test_pdf_dictionary_methods() {
1603 let mut dict = PdfDictionary::new();
1604 assert_eq!(dict.0.len(), 0);
1605 assert!(dict.0.is_empty());
1606
1607 dict.insert(
1609 "Type".to_string(),
1610 PdfObject::Name(PdfName("Page".to_string())),
1611 );
1612 dict.insert("Count".to_string(), PdfObject::Integer(5));
1613 dict.insert("Resources".to_string(), PdfObject::Reference(10, 0));
1614
1615 assert_eq!(dict.0.len(), 3);
1616 assert!(!dict.0.is_empty());
1617
1618 assert_eq!(
1620 dict.get("Type").unwrap().as_name().unwrap().as_str(),
1621 "Page"
1622 );
1623 assert_eq!(dict.get("Count").unwrap().as_integer(), Some(5));
1624 assert_eq!(dict.get("Resources").unwrap().as_reference(), Some((10, 0)));
1625 assert!(dict.get("NonExistent").is_none());
1626
1627 assert!(dict.contains_key("Type"));
1629 assert!(dict.contains_key("Count"));
1630 assert!(dict.contains_key("Resources"));
1631 assert!(!dict.contains_key("NonExistent"));
1632
1633 assert_eq!(dict.get_type(), Some("Page"));
1635
1636 let mut keys: Vec<String> = dict.0.keys().map(|k| k.0.clone()).collect();
1638 keys.sort();
1639 assert_eq!(keys, vec!["Count", "Resources", "Type"]);
1640
1641 let values: Vec<&PdfObject> = dict.0.values().collect();
1643 assert_eq!(values.len(), 3);
1644 }
1645
1646 #[test]
1647 fn test_pdf_stream_methods() {
1648 let mut dict = PdfDictionary::new();
1649 dict.insert("Length".to_string(), PdfObject::Integer(13));
1650 dict.insert(
1651 "Filter".to_string(),
1652 PdfObject::Name(PdfName("FlateDecode".to_string())),
1653 );
1654
1655 let data = b"Hello, World!".to_vec();
1656 let stream = PdfStream {
1657 dict,
1658 data: data.clone(),
1659 };
1660
1661 assert_eq!(stream.raw_data(), data);
1663
1664 assert_eq!(stream.dict.get("Length").unwrap().as_integer(), Some(13));
1666 assert_eq!(
1667 stream
1668 .dict
1669 .get("Filter")
1670 .unwrap()
1671 .as_name()
1672 .unwrap()
1673 .as_str(),
1674 "FlateDecode"
1675 );
1676
1677 let options = ParseOptions::default();
1680 let decode_result = stream.decode(&options);
1681 assert!(decode_result.is_ok() || decode_result.is_err());
1682 }
1683
1684 #[test]
1685 fn test_parse_complex_nested_structures() {
1686 let input = b"[[1 2] [3 4] [5 6]]";
1688 let mut lexer = Lexer::new(Cursor::new(input));
1689 let obj = PdfObject::parse(&mut lexer).unwrap();
1690
1691 let outer_array = obj.as_array().unwrap();
1692 assert_eq!(outer_array.len(), 3);
1693
1694 for i in 0..3 {
1695 let inner_array = outer_array.get(i).unwrap().as_array().unwrap();
1696 assert_eq!(inner_array.len(), 2);
1697 assert_eq!(
1698 inner_array.get(0).unwrap().as_integer(),
1699 Some((i as i64) * 2 + 1)
1700 );
1701 assert_eq!(
1702 inner_array.get(1).unwrap().as_integer(),
1703 Some((i as i64) * 2 + 2)
1704 );
1705 }
1706 }
1707
1708 #[test]
1709 fn test_parse_complex_dictionary() {
1710 let input = b"<< /Type /Page /Parent 1 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 2 0 R >> /ProcSet [/PDF /Text] >> /Contents 3 0 R >>";
1711 let mut lexer = Lexer::new(Cursor::new(input));
1712 let obj = PdfObject::parse(&mut lexer).unwrap();
1713
1714 let dict = obj.as_dict().unwrap();
1715 assert_eq!(dict.get_type(), Some("Page"));
1716 assert_eq!(dict.get("Parent").unwrap().as_reference(), Some((1, 0)));
1717 assert_eq!(dict.get("Contents").unwrap().as_reference(), Some((3, 0)));
1718
1719 let media_box = dict.get("MediaBox").unwrap().as_array().unwrap();
1721 assert_eq!(media_box.len(), 4);
1722 assert_eq!(media_box.get(0).unwrap().as_integer(), Some(0));
1723 assert_eq!(media_box.get(1).unwrap().as_integer(), Some(0));
1724 assert_eq!(media_box.get(2).unwrap().as_integer(), Some(612));
1725 assert_eq!(media_box.get(3).unwrap().as_integer(), Some(792));
1726
1727 let resources = dict.get("Resources").unwrap().as_dict().unwrap();
1729 assert!(resources.contains_key("Font"));
1730 assert!(resources.contains_key("ProcSet"));
1731
1732 let font_dict = resources.get("Font").unwrap().as_dict().unwrap();
1734 assert_eq!(font_dict.get("F1").unwrap().as_reference(), Some((2, 0)));
1735
1736 let proc_set = resources.get("ProcSet").unwrap().as_array().unwrap();
1738 assert_eq!(proc_set.len(), 2);
1739 assert_eq!(proc_set.get(0).unwrap().as_name().unwrap().as_str(), "PDF");
1740 assert_eq!(proc_set.get(1).unwrap().as_name().unwrap().as_str(), "Text");
1741 }
1742
1743 #[test]
1744 fn test_parse_hex_strings() {
1745 let input = b"<48656C6C6F>"; let mut lexer = Lexer::new(Cursor::new(input));
1747 let obj = PdfObject::parse(&mut lexer).unwrap();
1748
1749 let string = obj.as_string().unwrap();
1750 assert_eq!(string.as_str().unwrap(), "Hello");
1751 }
1752
1753 #[test]
1754 fn test_parse_literal_strings() {
1755 let input = b"(Hello World)";
1756 let mut lexer = Lexer::new(Cursor::new(input));
1757 let obj = PdfObject::parse(&mut lexer).unwrap();
1758
1759 let string = obj.as_string().unwrap();
1760 assert_eq!(string.as_str().unwrap(), "Hello World");
1761 }
1762
1763 #[test]
1764 fn test_parse_string_with_escapes() {
1765 let input = b"(Hello\\nWorld\\t!)";
1766 let mut lexer = Lexer::new(Cursor::new(input));
1767 let obj = PdfObject::parse(&mut lexer).unwrap();
1768
1769 let string = obj.as_string().unwrap();
1770 assert!(!string.as_bytes().is_empty());
1772 }
1773
1774 #[test]
1775 fn test_parse_names_with_special_chars() {
1776 let input = b"/Name#20with#20spaces";
1777 let mut lexer = Lexer::new(Cursor::new(input));
1778 let obj = PdfObject::parse(&mut lexer).unwrap();
1779
1780 let name = obj.as_name().unwrap();
1781 assert!(!name.as_str().is_empty());
1783 }
1784
1785 #[test]
1786 fn test_parse_references() {
1787 let input = b"1 0 R";
1788 let mut lexer = Lexer::new(Cursor::new(input));
1789 let obj = PdfObject::parse(&mut lexer).unwrap();
1790
1791 assert_eq!(obj.as_reference(), Some((1, 0)));
1792
1793 let input2 = b"42 5 R";
1795 let mut lexer2 = Lexer::new(Cursor::new(input2));
1796 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1797
1798 assert_eq!(obj2.as_reference(), Some((42, 5)));
1799 }
1800
1801 #[test]
1802 fn test_parse_edge_cases() {
1803 let input = b"9223372036854775807"; let mut lexer = Lexer::new(Cursor::new(input));
1806 let obj = PdfObject::parse(&mut lexer).unwrap();
1807 assert_eq!(obj.as_integer(), Some(9223372036854775807));
1808
1809 let input2 = b"-9223372036854775808"; let mut lexer2 = Lexer::new(Cursor::new(input2));
1812 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1813 assert_eq!(obj2.as_integer(), Some(-9223372036854775808));
1814
1815 let input3 = b"1.23e-10";
1817 let mut lexer3 = Lexer::new(Cursor::new(input3));
1818 let obj3 = PdfObject::parse(&mut lexer3).unwrap();
1819 assert!(obj3.as_real().is_some());
1821 }
1822
1823 #[test]
1824 fn test_parse_empty_structures() {
1825 let input = b"[]";
1827 let mut lexer = Lexer::new(Cursor::new(input));
1828 let obj = PdfObject::parse(&mut lexer).unwrap();
1829
1830 let array = obj.as_array().unwrap();
1831 assert_eq!(array.len(), 0);
1832 assert!(array.is_empty());
1833
1834 let input2 = b"<< >>";
1836 let mut lexer2 = Lexer::new(Cursor::new(input2));
1837 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1838
1839 let dict = obj2.as_dict().unwrap();
1840 assert_eq!(dict.0.len(), 0);
1841 assert!(dict.0.is_empty());
1842 }
1843
1844 #[test]
1845 fn test_error_handling() {
1846 let input = b"[1 2 3"; let mut lexer = Lexer::new(Cursor::new(input));
1849 let result = PdfObject::parse(&mut lexer);
1850 assert!(result.is_err());
1851
1852 let input2 = b"<< /Type /Page"; let mut lexer2 = Lexer::new(Cursor::new(input2));
1855 let result2 = PdfObject::parse(&mut lexer2);
1856 assert!(result2.is_err());
1857
1858 let input3 = b"1 0 X"; let mut lexer3 = Lexer::new(Cursor::new(input3));
1861 let result3 = PdfObject::parse(&mut lexer3);
1862 assert!(result3.is_ok() || result3.is_err());
1865 }
1866
1867 #[test]
1868 fn test_clone_and_equality() {
1869 let obj1 = PdfObject::Integer(42);
1870 let obj2 = obj1.clone();
1871 assert_eq!(obj1, obj2);
1872
1873 let obj3 = PdfObject::Integer(43);
1874 assert_ne!(obj1, obj3);
1875
1876 let mut array = PdfArray::new();
1878 array.push(PdfObject::Integer(1));
1879 array.push(PdfObject::String(PdfString(b"test".to_vec())));
1880 let obj4 = PdfObject::Array(array);
1881 let obj5 = obj4.clone();
1882 assert_eq!(obj4, obj5);
1883 }
1884
1885 #[test]
1886 fn test_debug_formatting() {
1887 let obj = PdfObject::Integer(42);
1888 let debug_str = format!("{obj:?}");
1889 assert!(debug_str.contains("Integer"));
1890 assert!(debug_str.contains("42"));
1891
1892 let name = PdfName("Type".to_string());
1893 let debug_str2 = format!("{name:?}");
1894 assert!(debug_str2.contains("PdfName"));
1895 assert!(debug_str2.contains("Type"));
1896 }
1897
1898 #[test]
1899 fn test_performance_large_array() {
1900 let mut array = PdfArray::new();
1901 for i in 0..1000 {
1902 array.push(PdfObject::Integer(i));
1903 }
1904
1905 assert_eq!(array.len(), 1000);
1906 assert_eq!(array.get(0).unwrap().as_integer(), Some(0));
1907 assert_eq!(array.get(999).unwrap().as_integer(), Some(999));
1908
1909 let sum: i64 = array.0.iter().filter_map(|obj| obj.as_integer()).sum();
1911 assert_eq!(sum, 499500); }
1913
1914 #[test]
1915 fn test_performance_large_dictionary() {
1916 let mut dict = PdfDictionary::new();
1917 for i in 0..1000 {
1918 dict.insert(format!("Key{i}"), PdfObject::Integer(i));
1919 }
1920
1921 assert_eq!(dict.0.len(), 1000);
1922 assert_eq!(dict.get("Key0").unwrap().as_integer(), Some(0));
1923 assert_eq!(dict.get("Key999").unwrap().as_integer(), Some(999));
1924
1925 for i in 0..1000 {
1927 assert!(dict.contains_key(&format!("Key{i}")));
1928 }
1929 }
1930 }
1931
1932 #[test]
1933 fn test_lenient_stream_parsing_too_short() {
1934 let dict = PdfDictionary(
1937 vec![(PdfName("Length".to_string()), PdfObject::Integer(10))]
1938 .into_iter()
1939 .collect::<HashMap<_, _>>(),
1940 );
1941
1942 let stream_content = b"This is a much longer text content than just 10 bytes";
1945 let test_data = vec![
1946 b"\n".to_vec(), stream_content.to_vec(),
1948 b"\nendstream".to_vec(),
1949 ]
1950 .concat();
1951
1952 let mut cursor = Cursor::new(test_data);
1954 let mut lexer = Lexer::new(&mut cursor);
1955 let mut options = ParseOptions::default();
1956 options.lenient_streams = true;
1957 options.max_recovery_bytes = 100;
1958 options.collect_warnings = false;
1959
1960 let result = PdfObject::parse_stream_data_with_options(&mut lexer, &dict, &options);
1964 if let Err(e) = &result {
1965 tracing::debug!("Error in test_lenient_stream_parsing_too_short: {e:?}");
1966 tracing::debug!("Warning: Stream length mismatch expected, checking if lenient parsing is working correctly");
1967 }
1968 assert!(result.is_ok());
1969
1970 let stream_data = result.unwrap();
1971 let content = String::from_utf8_lossy(&stream_data);
1972
1973 assert!(content.contains("This is a"));
1976 }
1977
1978 #[test]
1979 fn test_lenient_stream_parsing_too_long() {
1980 let dict = PdfDictionary(
1982 vec![(PdfName("Length".to_string()), PdfObject::Integer(100))]
1983 .into_iter()
1984 .collect::<HashMap<_, _>>(),
1985 );
1986
1987 let stream_content = b"Short";
1989 let test_data = vec![
1990 b"\n".to_vec(), stream_content.to_vec(),
1992 b"\nendstream".to_vec(),
1993 ]
1994 .concat();
1995
1996 let mut cursor = Cursor::new(test_data);
1998 let mut lexer = Lexer::new(&mut cursor);
1999 let mut options = ParseOptions::default();
2000 options.lenient_streams = true;
2001 options.max_recovery_bytes = 100;
2002 options.collect_warnings = false;
2003
2004 let result = PdfObject::parse_stream_data_with_options(&mut lexer, &dict, &options);
2007
2008 assert!(result.is_err());
2012 }
2013
2014 #[test]
2015 fn test_lenient_stream_no_endstream_found() {
2016 let input = b"<< /Length 10 >>
2018stream
2019This text does not contain the magic word and continues for a very long time with no proper termination...";
2020
2021 let mut cursor = Cursor::new(input.to_vec());
2022 let mut lexer = Lexer::new(&mut cursor);
2023 let mut options = ParseOptions::default();
2024 options.lenient_streams = true;
2025 options.max_recovery_bytes = 50; options.collect_warnings = false;
2027
2028 let dict_token = lexer.next_token().unwrap();
2029 let obj = PdfObject::parse_from_token_with_options(&mut lexer, dict_token, &options);
2030
2031 assert!(obj.is_err());
2033 }
2034
2035 #[test]
2038 fn test_pdf_name_special_characters() {
2039 let name = PdfName::new("Name#20With#20Spaces".to_string());
2040 assert_eq!(name.as_str(), "Name#20With#20Spaces");
2041
2042 let unicode_name = PdfName::new("café".to_string());
2044 assert_eq!(unicode_name.as_str(), "café");
2045
2046 let special_name = PdfName::new("Font#2FSubtype".to_string());
2048 assert_eq!(special_name.as_str(), "Font#2FSubtype");
2049 }
2050
2051 #[test]
2052 fn test_pdf_name_edge_cases() {
2053 let empty_name = PdfName::new("".to_string());
2055 assert_eq!(empty_name.as_str(), "");
2056
2057 let long_name = PdfName::new("A".repeat(1000));
2059 assert_eq!(long_name.as_str().len(), 1000);
2060
2061 let complex_name = PdfName::new("ABCdef123-._~!*'()".to_string());
2063 assert_eq!(complex_name.as_str(), "ABCdef123-._~!*'()");
2064 }
2065
2066 #[test]
2067 fn test_pdf_string_encoding_validation() {
2068 let utf8_string = PdfString::new("Hello, 世界! 🌍".as_bytes().to_vec());
2070 assert!(utf8_string.as_str().is_ok());
2071
2072 let invalid_utf8 = PdfString::new(vec![0xFF, 0xFE, 0xFD]);
2074 assert!(invalid_utf8.as_str().is_err());
2075
2076 let empty_string = PdfString::new(vec![]);
2078 assert_eq!(empty_string.as_str().unwrap(), "");
2079 }
2080
2081 #[test]
2082 fn test_pdf_string_binary_data() {
2083 let binary_data = vec![0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC];
2085 let binary_string = PdfString::new(binary_data.clone());
2086 assert_eq!(binary_string.as_bytes(), &binary_data);
2087
2088 let null_string = PdfString::new(vec![
2090 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x57, 0x6F, 0x72, 0x6C, 0x64,
2091 ]);
2092 assert_eq!(binary_string.as_bytes().len(), 8);
2093 assert!(null_string.as_bytes().contains(&0x00));
2094 }
2095
2096 #[test]
2097 fn test_pdf_array_nested_structures() {
2098 let mut array = PdfArray::new();
2099
2100 let mut nested_array = PdfArray::new();
2102 nested_array.push(PdfObject::Integer(1));
2103 nested_array.push(PdfObject::Integer(2));
2104 array.push(PdfObject::Array(nested_array));
2105
2106 let mut nested_dict = PdfDictionary(HashMap::new());
2108 nested_dict.0.insert(
2109 PdfName::new("Key".to_string()),
2110 PdfObject::String(PdfString::new(b"Value".to_vec())),
2111 );
2112 array.push(PdfObject::Dictionary(nested_dict));
2113
2114 assert_eq!(array.len(), 2);
2115 assert!(matches!(array.get(0), Some(PdfObject::Array(_))));
2116 assert!(matches!(array.get(1), Some(PdfObject::Dictionary(_))));
2117 }
2118
2119 #[test]
2120 fn test_pdf_array_type_mixing() {
2121 let mut array = PdfArray::new();
2122
2123 array.push(PdfObject::Null);
2125 array.push(PdfObject::Boolean(true));
2126 array.push(PdfObject::Integer(42));
2127 array.push(PdfObject::Real(3.14159));
2128 array.push(PdfObject::String(PdfString::new(b"text".to_vec())));
2129 array.push(PdfObject::Name(PdfName::new("Name".to_string())));
2130
2131 assert_eq!(array.len(), 6);
2132 assert!(matches!(array.get(0), Some(PdfObject::Null)));
2133 assert!(matches!(array.get(1), Some(PdfObject::Boolean(true))));
2134 assert!(matches!(array.get(2), Some(PdfObject::Integer(42))));
2135 assert!(matches!(array.get(3), Some(PdfObject::Real(_))));
2136 assert!(matches!(array.get(4), Some(PdfObject::String(_))));
2137 assert!(matches!(array.get(5), Some(PdfObject::Name(_))));
2138 }
2139
2140 #[test]
2141 fn test_pdf_dictionary_key_operations() {
2142 let mut dict = PdfDictionary(HashMap::new());
2143
2144 dict.0.insert(
2146 PdfName::new("Type".to_string()),
2147 PdfObject::Name(PdfName::new("Test".to_string())),
2148 );
2149 dict.0
2150 .insert(PdfName::new("Count".to_string()), PdfObject::Integer(100));
2151 dict.0
2152 .insert(PdfName::new("Flag".to_string()), PdfObject::Boolean(true));
2153
2154 assert_eq!(dict.0.len(), 3);
2155 assert!(dict.0.contains_key(&PdfName::new("Type".to_string())));
2156 assert!(dict.0.contains_key(&PdfName::new("Count".to_string())));
2157 assert!(dict.0.contains_key(&PdfName::new("Flag".to_string())));
2158 assert!(!dict.0.contains_key(&PdfName::new("Missing".to_string())));
2159
2160 assert!(dict.0.get(&PdfName::new("Type".to_string())).is_some());
2162 }
2163
2164 #[test]
2165 fn test_pdf_dictionary_complex_values() {
2166 let mut dict = PdfDictionary(HashMap::new());
2167
2168 let mut rect_array = PdfArray::new();
2170 rect_array.push(PdfObject::Real(0.0));
2171 rect_array.push(PdfObject::Real(0.0));
2172 rect_array.push(PdfObject::Real(612.0));
2173 rect_array.push(PdfObject::Real(792.0));
2174
2175 dict.0.insert(
2176 PdfName::new("MediaBox".to_string()),
2177 PdfObject::Array(rect_array),
2178 );
2179
2180 let mut resources = PdfDictionary(HashMap::new());
2182 let mut font_dict = PdfDictionary(HashMap::new());
2183 font_dict
2184 .0
2185 .insert(PdfName::new("F1".to_string()), PdfObject::Reference(10, 0));
2186 resources.0.insert(
2187 PdfName::new("Font".to_string()),
2188 PdfObject::Dictionary(font_dict),
2189 );
2190
2191 dict.0.insert(
2192 PdfName::new("Resources".to_string()),
2193 PdfObject::Dictionary(resources),
2194 );
2195
2196 assert_eq!(dict.0.len(), 2);
2197 assert!(dict.0.get(&PdfName::new("MediaBox".to_string())).is_some());
2198 assert!(dict.0.get(&PdfName::new("Resources".to_string())).is_some());
2199 }
2200
2201 #[test]
2202 fn test_object_reference_validation() {
2203 let ref1 = PdfObject::Reference(1, 0);
2204 let ref2 = PdfObject::Reference(1, 0);
2205 let ref3 = PdfObject::Reference(1, 1);
2206 let ref4 = PdfObject::Reference(2, 0);
2207
2208 assert_eq!(ref1, ref2);
2209 assert_ne!(ref1, ref3);
2210 assert_ne!(ref1, ref4);
2211
2212 let max_ref = PdfObject::Reference(u32::MAX, u16::MAX);
2214 assert!(matches!(max_ref, PdfObject::Reference(u32::MAX, u16::MAX)));
2215 }
2216
2217 #[test]
2218 fn test_pdf_object_type_checking() {
2219 let objects = vec![
2220 PdfObject::Null,
2221 PdfObject::Boolean(true),
2222 PdfObject::Integer(42),
2223 PdfObject::Real(3.14),
2224 PdfObject::String(PdfString::new(b"text".to_vec())),
2225 PdfObject::Name(PdfName::new("Name".to_string())),
2226 PdfObject::Array(PdfArray::new()),
2227 PdfObject::Dictionary(PdfDictionary(HashMap::new())),
2228 PdfObject::Reference(1, 0),
2229 ];
2230
2231 assert!(matches!(objects[0], PdfObject::Null));
2233 assert!(matches!(objects[1], PdfObject::Boolean(_)));
2234 assert!(matches!(objects[2], PdfObject::Integer(_)));
2235 assert!(matches!(objects[3], PdfObject::Real(_)));
2236 assert!(matches!(objects[4], PdfObject::String(_)));
2237 assert!(matches!(objects[5], PdfObject::Name(_)));
2238 assert!(matches!(objects[6], PdfObject::Array(_)));
2239 assert!(matches!(objects[7], PdfObject::Dictionary(_)));
2240 assert!(matches!(objects[8], PdfObject::Reference(_, _)));
2241 }
2242
2243 #[test]
2244 fn test_pdf_array_large_capacity() {
2245 let mut array = PdfArray::new();
2246
2247 for i in 0..1000 {
2249 array.push(PdfObject::Integer(i));
2250 }
2251
2252 assert_eq!(array.len(), 1000);
2253 if let Some(PdfObject::Integer(val)) = array.get(999) {
2255 assert_eq!(*val, 999);
2256 } else {
2257 panic!("Expected Integer at index 999");
2258 }
2259 assert!(array.get(1000).is_none());
2260
2261 let mut count = 0;
2263 for i in 0..array.len() {
2264 if let Some(obj) = array.get(i) {
2265 if matches!(obj, PdfObject::Integer(_)) {
2266 count += 1;
2267 }
2268 }
2269 }
2270 assert_eq!(count, 1000);
2271 }
2272
2273 #[test]
2274 fn test_pdf_dictionary_memory_efficiency() {
2275 let mut dict = PdfDictionary(HashMap::new());
2276
2277 for i in 0..100 {
2279 let key = PdfName::new(format!("Key{}", i));
2280 dict.0.insert(key, PdfObject::Integer(i));
2281 }
2282
2283 assert_eq!(dict.0.len(), 100);
2284 assert!(dict.0.contains_key(&PdfName::new("Key99".to_string())));
2285 assert!(!dict.0.contains_key(&PdfName::new("Key100".to_string())));
2286
2287 dict.0.remove(&PdfName::new("Key50".to_string()));
2289 assert_eq!(dict.0.len(), 99);
2290 assert!(!dict.0.contains_key(&PdfName::new("Key50".to_string())));
2291 }
2292
2293 #[test]
2294 fn test_parsing_simple_error_cases() {
2295 use std::io::Cursor;
2296
2297 let empty_input = b"";
2299 let mut cursor = Cursor::new(empty_input.to_vec());
2300 let mut lexer = Lexer::new(&mut cursor);
2301 let result = PdfObject::parse(&mut lexer);
2302
2303 assert!(result.is_err());
2305 }
2306
2307 #[test]
2308 fn test_unicode_string_handling() {
2309 let unicode_tests = vec![
2311 ("ASCII", "Hello World"),
2312 ("Latin-1", "Café résumé"),
2313 ("Emoji", "Hello 🌍 World 🚀"),
2314 ("CJK", "你好世界"),
2315 ("Mixed", "Hello 世界! Bonjour 🌍"),
2316 ];
2317
2318 for (name, text) in unicode_tests {
2319 let pdf_string = PdfString::new(text.as_bytes().to_vec());
2320 match pdf_string.as_str() {
2321 Ok(decoded) => assert_eq!(decoded, text, "Failed for {}", name),
2322 Err(_) => {
2323 assert!(!text.is_empty(), "Should handle {}", name);
2325 }
2326 }
2327 }
2328 }
2329
2330 #[test]
2331 fn test_deep_nesting_limits() {
2332 let mut root_array = PdfArray::new();
2334
2335 for i in 0..10 {
2337 let mut nested = PdfArray::new();
2338 nested.push(PdfObject::Integer(i as i64));
2339 root_array.push(PdfObject::Array(nested));
2340 }
2341
2342 assert_eq!(root_array.len(), 10);
2343
2344 for i in 0..10 {
2346 if let Some(PdfObject::Array(nested)) = root_array.get(i) {
2347 assert_eq!(nested.len(), 1);
2348 }
2349 }
2350 }
2351
2352 #[test]
2353 fn test_special_numeric_values() {
2354 let numbers = vec![
2356 (0i64, 0.0f64),
2357 (i32::MAX as i64, f32::MAX as f64),
2358 (i32::MIN as i64, f32::MIN as f64),
2359 (-1i64, -1.0f64),
2360 (2147483647i64, 2147483647.0f64),
2361 ];
2362
2363 for (int_val, float_val) in numbers {
2364 let int_obj = PdfObject::Integer(int_val);
2365 let float_obj = PdfObject::Real(float_val);
2366
2367 assert!(matches!(int_obj, PdfObject::Integer(_)));
2368 assert!(matches!(float_obj, PdfObject::Real(_)));
2369 }
2370
2371 let special_floats = vec![
2373 (0.0f64, "zero"),
2374 (f64::INFINITY, "infinity"),
2375 (f64::NEG_INFINITY, "negative infinity"),
2376 ];
2377
2378 for (val, _name) in special_floats {
2379 let obj = PdfObject::Real(val);
2380 assert!(matches!(obj, PdfObject::Real(_)));
2381 }
2382 }
2383
2384 #[test]
2385 fn test_array_bounds_checking() {
2386 let mut array = PdfArray::new();
2387 array.push(PdfObject::Integer(1));
2388 array.push(PdfObject::Integer(2));
2389 array.push(PdfObject::Integer(3));
2390
2391 assert!(array.get(0).is_some());
2393 assert!(array.get(1).is_some());
2394 assert!(array.get(2).is_some());
2395
2396 assert!(array.get(3).is_none());
2398 assert!(array.get(100).is_none());
2399
2400 let empty_array = PdfArray::new();
2402 assert!(empty_array.get(0).is_none());
2403 assert_eq!(empty_array.len(), 0);
2404 }
2405
2406 #[test]
2407 fn test_dictionary_case_sensitivity() {
2408 let mut dict = PdfDictionary(HashMap::new());
2409
2410 dict.0.insert(
2412 PdfName::new("Type".to_string()),
2413 PdfObject::Name(PdfName::new("Page".to_string())),
2414 );
2415 dict.0.insert(
2416 PdfName::new("type".to_string()),
2417 PdfObject::Name(PdfName::new("Font".to_string())),
2418 );
2419 dict.0.insert(
2420 PdfName::new("TYPE".to_string()),
2421 PdfObject::Name(PdfName::new("Image".to_string())),
2422 );
2423
2424 assert_eq!(dict.0.len(), 3);
2425 assert!(dict.0.contains_key(&PdfName::new("Type".to_string())));
2426 assert!(dict.0.contains_key(&PdfName::new("type".to_string())));
2427 assert!(dict.0.contains_key(&PdfName::new("TYPE".to_string())));
2428
2429 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("Type".to_string())) {
2431 assert_eq!(name.as_str(), "Page");
2432 }
2433 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("type".to_string())) {
2434 assert_eq!(name.as_str(), "Font");
2435 }
2436 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("TYPE".to_string())) {
2437 assert_eq!(name.as_str(), "Image");
2438 }
2439 }
2440
2441 #[test]
2442 fn test_object_cloning_and_equality() {
2443 let original_array = {
2444 let mut arr = PdfArray::new();
2445 arr.push(PdfObject::Integer(42));
2446 arr.push(PdfObject::String(PdfString::new(b"test".to_vec())));
2447 arr
2448 };
2449
2450 let cloned_array = original_array.clone();
2451 assert_eq!(original_array.len(), cloned_array.len());
2452
2453 for i in 0..original_array.len() {
2455 let orig = original_array.get(i).unwrap();
2456 let cloned = cloned_array.get(i).unwrap();
2457 match (orig, cloned) {
2458 (PdfObject::Integer(a), PdfObject::Integer(b)) => assert_eq!(a, b),
2459 (PdfObject::String(a), PdfObject::String(b)) => {
2460 assert_eq!(a.as_bytes(), b.as_bytes())
2461 }
2462 _ => panic!("Type mismatch in cloned array"),
2463 }
2464 }
2465 }
2466
2467 #[test]
2468 fn test_concurrent_object_access() {
2469 use std::sync::Arc;
2470 use std::thread;
2471
2472 let dict = Arc::new({
2473 let mut d = PdfDictionary(HashMap::new());
2474 d.0.insert(
2475 PdfName::new("SharedKey".to_string()),
2476 PdfObject::Integer(42),
2477 );
2478 d
2479 });
2480
2481 let dict_clone = Arc::clone(&dict);
2482 let handle = thread::spawn(move || {
2483 if let Some(PdfObject::Integer(val)) =
2485 dict_clone.0.get(&PdfName::new("SharedKey".to_string()))
2486 {
2487 assert_eq!(*val, 42);
2488 }
2489 });
2490
2491 if let Some(PdfObject::Integer(val)) = dict.0.get(&PdfName::new("SharedKey".to_string())) {
2493 assert_eq!(*val, 42);
2494 }
2495
2496 handle.join().unwrap();
2497 }
2498
2499 #[test]
2500 fn test_stream_data_edge_cases() {
2501 let mut dict = PdfDictionary(HashMap::new());
2503 dict.0
2504 .insert(PdfName::new("Length".to_string()), PdfObject::Integer(0));
2505
2506 let stream = PdfStream {
2507 dict: dict.clone(),
2508 data: vec![],
2509 };
2510
2511 assert_eq!(stream.data.len(), 0);
2513 assert!(stream.raw_data().is_empty());
2514
2515 let stream_with_data = PdfStream {
2517 dict,
2518 data: b"Hello World".to_vec(),
2519 };
2520
2521 assert_eq!(stream_with_data.raw_data(), b"Hello World");
2522 }
2523
2524 #[test]
2525 fn test_name_object_hash_consistency() {
2526 use std::collections::HashSet;
2527
2528 let mut name_set = HashSet::new();
2529
2530 name_set.insert(PdfName::new("Type".to_string()));
2532 name_set.insert(PdfName::new("Pages".to_string()));
2533 name_set.insert(PdfName::new("Type".to_string())); assert_eq!(name_set.len(), 2); assert!(name_set.contains(&PdfName::new("Type".to_string())));
2537 assert!(name_set.contains(&PdfName::new("Pages".to_string())));
2538 assert!(!name_set.contains(&PdfName::new("Font".to_string())));
2539 }
2540}
2541
2542