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 decode_with_limit(
237 &self,
238 options: &ParseOptions,
239 max_bytes: usize,
240 ) -> ParseResult<Vec<u8>> {
241 super::filters::decode_stream_with_limit(&self.data, &self.dict, options, max_bytes)
242 }
243
244 pub fn raw_data(&self) -> &[u8] {
258 &self.data
259 }
260}
261
262#[derive(Debug, Clone, PartialEq)]
298pub enum PdfObject {
299 Null,
301 Boolean(bool),
303 Integer(i64),
305 Real(f64),
307 String(PdfString),
309 Name(PdfName),
311 Array(PdfArray),
313 Dictionary(PdfDictionary),
315 Stream(PdfStream),
317 Reference(u32, u16),
319}
320
321impl PdfObject {
322 pub fn parse<R: Read + std::io::Seek>(lexer: &mut Lexer<R>) -> ParseResult<Self> {
358 let token = lexer.next_token()?;
359 Self::parse_from_token(lexer, token)
360 }
361
362 pub fn parse_with_options<R: Read + std::io::Seek>(
364 lexer: &mut Lexer<R>,
365 options: &super::ParseOptions,
366 ) -> ParseResult<Self> {
367 let token = lexer.next_token()?;
368 Self::parse_from_token_with_options(lexer, token, options)
369 }
370
371 fn parse_from_token<R: Read + std::io::Seek>(
373 lexer: &mut Lexer<R>,
374 token: Token,
375 ) -> ParseResult<Self> {
376 Self::parse_from_token_with_options(lexer, token, &super::ParseOptions::default())
377 }
378
379 fn parse_from_token_with_options<R: Read + std::io::Seek>(
381 lexer: &mut Lexer<R>,
382 token: Token,
383 options: &super::ParseOptions,
384 ) -> ParseResult<Self> {
385 match token {
386 Token::Null => Ok(PdfObject::Null),
387 Token::Boolean(b) => Ok(PdfObject::Boolean(b)),
388 Token::Integer(i) => {
389 if !(0..=9999999).contains(&i) {
391 return Ok(PdfObject::Integer(i));
392 }
393
394 match lexer.next_token()? {
396 Token::Integer(gen) if (0..=65535).contains(&gen) => {
397 match lexer.next_token()? {
399 Token::Name(s) if s == "R" => {
400 Ok(PdfObject::Reference(i as u32, gen as u16))
401 }
402 token => {
403 lexer.push_token(token);
405 lexer.push_token(Token::Integer(gen));
406 Ok(PdfObject::Integer(i))
407 }
408 }
409 }
410 token => {
411 lexer.push_token(token);
413 Ok(PdfObject::Integer(i))
414 }
415 }
416 }
417 Token::Real(r) => Ok(PdfObject::Real(r)),
418 Token::String(s) => Ok(PdfObject::String(PdfString(s))),
419 Token::Name(n) => Ok(PdfObject::Name(PdfName(n))),
420 Token::ArrayStart => Self::parse_array_with_options(lexer, options),
421 Token::DictStart => Self::parse_dictionary_or_stream_with_options(lexer, options),
422 Token::Comment(_) => {
423 Self::parse_with_options(lexer, options)
425 }
426 Token::StartXRef => {
427 Err(ParseError::SyntaxError {
429 position: 0,
430 message: "StartXRef encountered - this is not a PDF object".to_string(),
431 })
432 }
433 Token::Eof => Err(ParseError::SyntaxError {
434 position: 0,
435 message: "Unexpected end of file".to_string(),
436 }),
437 _ => Err(ParseError::UnexpectedToken {
438 expected: "PDF object".to_string(),
439 found: format!("{token:?}"),
440 }),
441 }
442 }
443
444 fn parse_array_with_options<R: Read + std::io::Seek>(
446 lexer: &mut Lexer<R>,
447 options: &super::ParseOptions,
448 ) -> ParseResult<Self> {
449 let mut elements = Vec::new();
450
451 loop {
452 let token = lexer.next_token()?;
453 match token {
454 Token::ArrayEnd => break,
455 Token::Comment(_) => continue, _ => {
457 let obj = Self::parse_from_token_with_options(lexer, token, options)?;
458 elements.push(obj);
459 }
460 }
461 }
462
463 Ok(PdfObject::Array(PdfArray(elements)))
464 }
465
466 fn parse_dictionary_or_stream_with_options<R: Read + std::io::Seek>(
468 lexer: &mut Lexer<R>,
469 options: &super::ParseOptions,
470 ) -> ParseResult<Self> {
471 let dict = Self::parse_dictionary_inner_with_options(lexer, options)?;
472
473 loop {
475 let token = lexer.next_token()?;
476 match token {
478 Token::Stream => {
479 let stream_data = Self::parse_stream_data_with_options(lexer, &dict, options)?;
481 return Ok(PdfObject::Stream(PdfStream {
482 dict,
483 data: stream_data,
484 }));
485 }
486 Token::Comment(_) => {
487 continue;
489 }
490 Token::StartXRef => {
491 lexer.push_token(token);
495 return Ok(PdfObject::Dictionary(dict));
496 }
497 _ => {
498 lexer.push_token(token);
502 return Ok(PdfObject::Dictionary(dict));
503 }
504 }
505 }
506 }
507
508 pub(crate) fn parse_dictionary_inner_with_options<R: Read + std::io::Seek>(
516 lexer: &mut Lexer<R>,
517 options: &super::ParseOptions,
518 ) -> ParseResult<PdfDictionary> {
519 let mut dict = HashMap::new();
520
521 loop {
522 let token = lexer.next_token()?;
523 match token {
524 Token::DictEnd => break,
525 Token::Comment(_) => continue, Token::Name(key) => {
527 let value = Self::parse_with_options(lexer, options)?;
528 dict.insert(PdfName(key), value);
529 }
530 _ => {
531 return Err(ParseError::UnexpectedToken {
532 expected: "dictionary key (name) or >>".to_string(),
533 found: format!("{token:?}"),
534 });
535 }
536 }
537 }
538
539 Ok(PdfDictionary(dict))
540 }
541
542 fn parse_stream_data_with_options<R: Read + std::io::Seek>(
544 lexer: &mut Lexer<R>,
545 dict: &PdfDictionary,
546 options: &super::ParseOptions,
547 ) -> ParseResult<Vec<u8>> {
548 let length = dict
550 .0
551 .get(&PdfName("Length".to_string()))
552 .or_else(|| {
553 if options.lenient_streams {
555 if options.collect_warnings {
556 tracing::debug!("Warning: Missing Length key in stream dictionary, will search for endstream marker");
557 }
558 Some(&PdfObject::Integer(-1))
560 } else {
561 None
562 }
563 })
564 .ok_or_else(|| ParseError::MissingKey("Length".to_string()))?;
565
566 let length = match length {
567 PdfObject::Integer(len) => {
568 if *len == -1 {
569 usize::MAX } else if *len < 0 {
572 if options.lenient_streams {
580 if options.collect_warnings {
581 tracing::debug!(
582 "Warning: negative stream /Length {len}; searching for endstream marker"
583 );
584 }
585 usize::MAX
586 } else {
587 return Err(ParseError::SyntaxError {
588 position: lexer.position(),
589 message: format!("Invalid negative stream length: {len}"),
590 });
591 }
592 } else {
593 *len as usize
594 }
595 }
596 PdfObject::Reference(obj_num, gen_num) => {
597 if options.lenient_streams {
600 if options.collect_warnings {
601 tracing::debug!("Warning: Stream length is an indirect reference ({obj_num} {gen_num} R). Using unlimited endstream search.");
602 }
603 usize::MAX - 1 } else {
606 return Err(ParseError::SyntaxError {
607 position: lexer.position(),
608 message: format!(
609 "Stream length reference ({obj_num} {gen_num} R) requires lenient mode"
610 ),
611 });
612 }
613 }
614 _ => {
615 return Err(ParseError::SyntaxError {
616 position: lexer.position(),
617 message: "Invalid stream length type".to_string(),
618 });
619 }
620 };
621
622 lexer.read_newline()?;
624
625 let mut stream_data = if length == usize::MAX || length == usize::MAX - 1 {
627 let is_indirect_ref = length == usize::MAX - 1;
629 let is_dct_decode = dict
631 .0
632 .get(&PdfName("Filter".to_string()))
633 .map(|filter| match filter {
634 PdfObject::Name(name) => name.0 == "DCTDecode",
635 PdfObject::Array(arr) => arr
636 .0
637 .iter()
638 .any(|f| matches!(f, PdfObject::Name(name) if name.0 == "DCTDecode")),
639 _ => false,
640 })
641 .unwrap_or(false);
642
643 let mut data = Vec::new();
644 let max_search = if is_indirect_ref {
647 10 * 1024 * 1024 } else {
649 65536 };
651 let mut found_endstream = false;
652
653 if is_indirect_ref && options.collect_warnings {
654 tracing::debug!("Searching for endstream without fixed limit (up to {}MB) for indirect reference", max_search / 1024 / 1024);
655 }
656
657 for i in 0..max_search {
658 match lexer.peek_byte() {
659 Ok(b) => {
660 if b == b'e' {
662 let mut temp_buffer = vec![b'e'];
664 let expected = b"ndstream";
665 let mut is_endstream = true;
666
667 let _ = lexer.read_byte();
669
670 for &expected_byte in expected.iter() {
672 match lexer.read_byte() {
673 Ok(byte) => {
674 temp_buffer.push(byte);
675 if byte != expected_byte {
676 is_endstream = false;
677 break;
678 }
679 }
680 Err(_) => {
681 is_endstream = false;
682 break;
683 }
684 }
685 }
686
687 if is_endstream && temp_buffer.len() == 9 {
688 found_endstream = true;
690 if is_dct_decode {
691 tracing::debug!("🔍 [PARSER] Found 'endstream' after reading {} bytes for DCTDecode", data.len());
692 }
693 break;
694 } else {
695 data.extend(temp_buffer);
698 continue;
699 }
700 } else {
701 data.push(lexer.read_byte()?);
703 }
704
705 if is_dct_decode && i % 10000 == 0 && i > 0 {
707 }
709 }
710 Err(_) => {
711 break;
713 }
714 }
715 }
716
717 if !found_endstream && !options.lenient_streams {
718 return Err(ParseError::SyntaxError {
719 position: lexer.position(),
720 message: "Could not find endstream marker".to_string(),
721 });
722 }
723
724 if is_dct_decode {
725 tracing::debug!(
728 "DCTDecode stream: read {} bytes (full stream based on endstream marker)",
729 data.len()
730 );
731 }
732
733 data
734 } else {
735 lexer.read_bytes(length)?
736 };
737
738 lexer.skip_whitespace()?;
740
741 let peek_result = lexer.peek_token();
743
744 match peek_result {
745 Ok(Token::EndStream) => {
746 lexer.next_token()?;
748 Ok(stream_data)
749 }
750 Ok(other_token) => {
751 if options.lenient_streams {
752 let is_dct_decode = dict
754 .0
755 .get(&PdfName("Filter".to_string()))
756 .map(|filter| match filter {
757 PdfObject::Name(name) => name.0 == "DCTDecode",
758 PdfObject::Array(arr) => arr.0.iter().any(
759 |f| matches!(f, PdfObject::Name(name) if name.0 == "DCTDecode"),
760 ),
761 _ => false,
762 })
763 .unwrap_or(false);
764
765 if is_dct_decode {
766 tracing::debug!("Warning: DCTDecode stream length mismatch at {length} bytes, but not extending JPEG data");
769
770 if let Some(additional_bytes) =
772 lexer.find_keyword_ahead("endstream", options.max_recovery_bytes)?
773 {
774 let _ = lexer.read_bytes(additional_bytes)?;
776 }
777
778 lexer.skip_whitespace()?;
780 lexer.expect_keyword("endstream")?;
781
782 Ok(stream_data)
783 } else {
784 tracing::debug!("Warning: Stream length mismatch. Expected 'endstream' after {length} bytes, got {other_token:?}");
786
787 let search_limit = if length == usize::MAX - 1 {
789 10 * 1024 * 1024 } else {
791 options.max_recovery_bytes
792 };
793
794 if let Some(additional_bytes) =
795 lexer.find_keyword_ahead("endstream", search_limit)?
796 {
797 let extra_data = lexer.read_bytes(additional_bytes)?;
799 stream_data.extend_from_slice(&extra_data);
800
801 let actual_length = stream_data.len();
802 tracing::debug!(
803 "Stream length corrected: declared={length}, actual={actual_length}"
804 );
805
806 lexer.skip_whitespace()?;
808 lexer.expect_keyword("endstream")?;
809
810 Ok(stream_data)
811 } else {
812 Err(ParseError::SyntaxError {
814 position: lexer.position(),
815 message: format!(
816 "Could not find 'endstream' within {} bytes",
817 search_limit
818 ),
819 })
820 }
821 }
822 } else {
823 Err(ParseError::UnexpectedToken {
825 expected: "endstream".to_string(),
826 found: format!("{other_token:?}"),
827 })
828 }
829 }
830 Err(e) => {
831 if options.lenient_streams {
832 tracing::debug!(
834 "Warning: Stream length mismatch. Could not peek next token after {length} bytes"
835 );
836
837 let search_limit = if length == usize::MAX - 1 {
839 10 * 1024 * 1024 } else {
841 options.max_recovery_bytes
842 };
843
844 if let Some(additional_bytes) =
845 lexer.find_keyword_ahead("endstream", search_limit)?
846 {
847 let extra_data = lexer.read_bytes(additional_bytes)?;
849 stream_data.extend_from_slice(&extra_data);
850
851 let actual_length = stream_data.len();
852 tracing::debug!(
853 "Stream length corrected: declared={length}, actual={actual_length}"
854 );
855
856 lexer.skip_whitespace()?;
858 lexer.expect_keyword("endstream")?;
859
860 Ok(stream_data)
861 } else {
862 Err(ParseError::SyntaxError {
864 position: lexer.position(),
865 message: format!(
866 "Could not find 'endstream' within {} bytes",
867 search_limit
868 ),
869 })
870 }
871 } else {
872 Err(e)
874 }
875 }
876 }
877 }
878
879 pub fn is_null(&self) -> bool {
890 matches!(self, PdfObject::Null)
891 }
892
893 pub fn as_bool(&self) -> Option<bool> {
911 match self {
912 PdfObject::Boolean(b) => Some(*b),
913 _ => None,
914 }
915 }
916
917 pub fn as_integer(&self) -> Option<i64> {
919 match self {
920 PdfObject::Integer(i) => Some(*i),
921 _ => None,
922 }
923 }
924
925 pub fn as_real(&self) -> Option<f64> {
946 match self {
947 PdfObject::Real(r) => Some(*r),
948 PdfObject::Integer(i) => Some(*i as f64),
949 _ => None,
950 }
951 }
952
953 pub fn as_string(&self) -> Option<&PdfString> {
955 match self {
956 PdfObject::String(s) => Some(s),
957 _ => None,
958 }
959 }
960
961 pub fn as_name(&self) -> Option<&PdfName> {
963 match self {
964 PdfObject::Name(n) => Some(n),
965 _ => None,
966 }
967 }
968
969 pub fn as_array(&self) -> Option<&PdfArray> {
971 match self {
972 PdfObject::Array(a) => Some(a),
973 _ => None,
974 }
975 }
976
977 pub fn as_dict(&self) -> Option<&PdfDictionary> {
979 match self {
980 PdfObject::Dictionary(d) => Some(d),
981 PdfObject::Stream(s) => Some(&s.dict),
982 _ => None,
983 }
984 }
985
986 pub fn as_stream(&self) -> Option<&PdfStream> {
988 match self {
989 PdfObject::Stream(s) => Some(s),
990 _ => None,
991 }
992 }
993
994 pub fn as_reference(&self) -> Option<(u32, u16)> {
1014 match self {
1015 PdfObject::Reference(obj, gen) => Some((*obj, *gen)),
1016 _ => None,
1017 }
1018 }
1019}
1020
1021impl Default for PdfDictionary {
1022 fn default() -> Self {
1023 Self::new()
1024 }
1025}
1026
1027impl PdfDictionary {
1028 pub fn new() -> Self {
1039 PdfDictionary(HashMap::new())
1040 }
1041
1042 pub fn get(&self, key: &str) -> Option<&PdfObject> {
1065 self.0.get(&PdfName(key.to_string()))
1066 }
1067
1068 pub fn insert(&mut self, key: String, value: PdfObject) {
1070 self.0.insert(PdfName(key), value);
1071 }
1072
1073 pub fn contains_key(&self, key: &str) -> bool {
1075 self.0.contains_key(&PdfName(key.to_string()))
1076 }
1077
1078 pub fn get_type(&self) -> Option<&str> {
1104 self.get("Type")
1105 .and_then(|obj| obj.as_name())
1106 .map(|n| n.0.as_str())
1107 }
1108}
1109
1110impl Default for PdfArray {
1111 fn default() -> Self {
1112 Self::new()
1113 }
1114}
1115
1116impl PdfArray {
1117 pub fn new() -> Self {
1119 PdfArray(Vec::new())
1120 }
1121
1122 pub fn len(&self) -> usize {
1124 self.0.len()
1125 }
1126
1127 pub fn is_empty(&self) -> bool {
1129 self.0.is_empty()
1130 }
1131
1132 pub fn get(&self, index: usize) -> Option<&PdfObject> {
1156 self.0.get(index)
1157 }
1158
1159 pub fn push(&mut self, obj: PdfObject) {
1161 self.0.push(obj);
1162 }
1163}
1164
1165impl PdfString {
1166 pub fn new(data: Vec<u8>) -> Self {
1168 PdfString(data)
1169 }
1170
1171 pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
1192 std::str::from_utf8(&self.0)
1193 }
1194
1195 pub fn to_text(&self) -> String {
1224 decode_text_string(&self.0)
1225 }
1226
1227 pub fn as_bytes(&self) -> &[u8] {
1229 &self.0
1230 }
1231}
1232
1233pub(crate) fn decode_text_string(bytes: &[u8]) -> String {
1238 if bytes.len() >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF {
1239 let code_units: Vec<u16> = bytes[2..]
1240 .chunks_exact(2)
1241 .map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
1242 .collect();
1243 String::from_utf16_lossy(&code_units)
1244 } else {
1245 bytes
1246 .iter()
1247 .map(|&byte| crate::text::encoding::winansi_decode_char(byte))
1248 .collect()
1249 }
1250}
1251
1252impl PdfName {
1253 pub fn new(name: String) -> Self {
1255 PdfName(name)
1256 }
1257
1258 pub fn as_str(&self) -> &str {
1260 &self.0
1261 }
1262}
1263
1264#[cfg(test)]
1265mod tests {
1266 use super::*;
1267 use crate::parser::lexer::Lexer;
1268 use crate::parser::ParseOptions;
1269 use std::collections::HashMap;
1270 use std::io::Cursor;
1271
1272 #[test]
1273 fn test_parse_simple_objects() {
1274 let input = b"null true false 123 -456 3.14 /Name (Hello)";
1275 let mut lexer = Lexer::new(Cursor::new(input));
1276
1277 assert_eq!(PdfObject::parse(&mut lexer).unwrap(), PdfObject::Null);
1278 assert_eq!(
1279 PdfObject::parse(&mut lexer).unwrap(),
1280 PdfObject::Boolean(true)
1281 );
1282 assert_eq!(
1283 PdfObject::parse(&mut lexer).unwrap(),
1284 PdfObject::Boolean(false)
1285 );
1286 assert_eq!(
1287 PdfObject::parse(&mut lexer).unwrap(),
1288 PdfObject::Integer(123)
1289 );
1290 assert_eq!(
1291 PdfObject::parse(&mut lexer).unwrap(),
1292 PdfObject::Integer(-456)
1293 );
1294 assert_eq!(PdfObject::parse(&mut lexer).unwrap(), PdfObject::Real(3.14));
1295 assert_eq!(
1296 PdfObject::parse(&mut lexer).unwrap(),
1297 PdfObject::Name(PdfName("Name".to_string()))
1298 );
1299 assert_eq!(
1300 PdfObject::parse(&mut lexer).unwrap(),
1301 PdfObject::String(PdfString(b"Hello".to_vec()))
1302 );
1303 }
1304
1305 #[test]
1306 fn test_parse_array() {
1307 let input = b"[100 200 300 /Name (test)]";
1309 let mut lexer = Lexer::new(Cursor::new(input));
1310
1311 let obj = PdfObject::parse(&mut lexer).unwrap();
1312 let array = obj.as_array().unwrap();
1313
1314 assert_eq!(array.len(), 5);
1315 assert_eq!(array.get(0).unwrap().as_integer(), Some(100));
1316 assert_eq!(array.get(1).unwrap().as_integer(), Some(200));
1317 assert_eq!(array.get(2).unwrap().as_integer(), Some(300));
1318 assert_eq!(array.get(3).unwrap().as_name().unwrap().as_str(), "Name");
1319 assert_eq!(
1320 array.get(4).unwrap().as_string().unwrap().as_bytes(),
1321 b"test"
1322 );
1323 }
1324
1325 #[test]
1326 fn test_parse_array_with_references() {
1327 let input = b"[1 0 R 2 0 R]";
1329 let mut lexer = Lexer::new(Cursor::new(input));
1330
1331 let obj = PdfObject::parse(&mut lexer).unwrap();
1332 let array = obj.as_array().unwrap();
1333
1334 assert_eq!(array.len(), 2);
1335 assert!(array.get(0).unwrap().as_reference().is_some());
1336 assert!(array.get(1).unwrap().as_reference().is_some());
1337 }
1338
1339 #[test]
1340 fn test_parse_dictionary() {
1341 let input = b"<< /Type /Page /Parent 1 0 R /MediaBox [0 0 612 792] >>";
1342 let mut lexer = Lexer::new(Cursor::new(input));
1343
1344 let obj = PdfObject::parse(&mut lexer).unwrap();
1345 let dict = obj.as_dict().unwrap();
1346
1347 assert_eq!(dict.get_type(), Some("Page"));
1348 assert!(dict.get("Parent").unwrap().as_reference().is_some());
1349 assert!(dict.get("MediaBox").unwrap().as_array().is_some());
1350 }
1351
1352 mod comprehensive_tests {
1354 use super::*;
1355
1356 #[test]
1357 fn test_pdf_object_null() {
1358 let obj = PdfObject::Null;
1359 assert!(obj.is_null());
1360 assert_eq!(obj.as_bool(), None);
1361 assert_eq!(obj.as_integer(), None);
1362 assert_eq!(obj.as_real(), None);
1363 assert_eq!(obj.as_string(), None);
1364 assert_eq!(obj.as_name(), None);
1365 assert_eq!(obj.as_array(), None);
1366 assert_eq!(obj.as_dict(), None);
1367 assert_eq!(obj.as_stream(), None);
1368 assert_eq!(obj.as_reference(), None);
1369 }
1370
1371 #[test]
1372 fn test_pdf_object_boolean() {
1373 let obj_true = PdfObject::Boolean(true);
1374 let obj_false = PdfObject::Boolean(false);
1375
1376 assert!(!obj_true.is_null());
1377 assert_eq!(obj_true.as_bool(), Some(true));
1378 assert_eq!(obj_false.as_bool(), Some(false));
1379
1380 assert_eq!(obj_true.as_integer(), None);
1381 assert_eq!(obj_true.as_real(), None);
1382 assert_eq!(obj_true.as_string(), None);
1383 assert_eq!(obj_true.as_name(), None);
1384 assert_eq!(obj_true.as_array(), None);
1385 assert_eq!(obj_true.as_dict(), None);
1386 assert_eq!(obj_true.as_stream(), None);
1387 assert_eq!(obj_true.as_reference(), None);
1388 }
1389
1390 #[test]
1391 fn test_pdf_object_integer() {
1392 let obj = PdfObject::Integer(42);
1393
1394 assert!(!obj.is_null());
1395 assert_eq!(obj.as_bool(), None);
1396 assert_eq!(obj.as_integer(), Some(42));
1397 assert_eq!(obj.as_real(), Some(42.0)); assert_eq!(obj.as_string(), None);
1399 assert_eq!(obj.as_name(), None);
1400 assert_eq!(obj.as_array(), None);
1401 assert_eq!(obj.as_dict(), None);
1402 assert_eq!(obj.as_stream(), None);
1403 assert_eq!(obj.as_reference(), None);
1404
1405 let obj_neg = PdfObject::Integer(-123);
1407 assert_eq!(obj_neg.as_integer(), Some(-123));
1408 assert_eq!(obj_neg.as_real(), Some(-123.0));
1409
1410 let obj_large = PdfObject::Integer(9999999999);
1412 assert_eq!(obj_large.as_integer(), Some(9999999999));
1413 assert_eq!(obj_large.as_real(), Some(9999999999.0));
1414 }
1415
1416 #[test]
1417 fn test_pdf_object_real() {
1418 let obj = PdfObject::Real(3.14159);
1419
1420 assert!(!obj.is_null());
1421 assert_eq!(obj.as_bool(), None);
1422 assert_eq!(obj.as_integer(), None);
1423 assert_eq!(obj.as_real(), Some(3.14159));
1424 assert_eq!(obj.as_string(), None);
1425 assert_eq!(obj.as_name(), None);
1426 assert_eq!(obj.as_array(), None);
1427 assert_eq!(obj.as_dict(), None);
1428 assert_eq!(obj.as_stream(), None);
1429 assert_eq!(obj.as_reference(), None);
1430
1431 let obj_neg = PdfObject::Real(-2.71828);
1433 assert_eq!(obj_neg.as_real(), Some(-2.71828));
1434
1435 let obj_zero = PdfObject::Real(0.0);
1437 assert_eq!(obj_zero.as_real(), Some(0.0));
1438
1439 let obj_small = PdfObject::Real(0.000001);
1441 assert_eq!(obj_small.as_real(), Some(0.000001));
1442
1443 let obj_large = PdfObject::Real(1e10);
1445 assert_eq!(obj_large.as_real(), Some(1e10));
1446 }
1447
1448 #[test]
1449 fn test_pdf_object_string() {
1450 let string_data = b"Hello World".to_vec();
1451 let pdf_string = PdfString(string_data.clone());
1452 let obj = PdfObject::String(pdf_string);
1453
1454 assert!(!obj.is_null());
1455 assert_eq!(obj.as_bool(), None);
1456 assert_eq!(obj.as_integer(), None);
1457 assert_eq!(obj.as_real(), None);
1458 assert!(obj.as_string().is_some());
1459 assert_eq!(obj.as_string().unwrap().as_bytes(), string_data);
1460 assert_eq!(obj.as_name(), None);
1461 assert_eq!(obj.as_array(), None);
1462 assert_eq!(obj.as_dict(), None);
1463 assert_eq!(obj.as_stream(), None);
1464 assert_eq!(obj.as_reference(), None);
1465 }
1466
1467 #[test]
1468 fn test_pdf_object_name() {
1469 let name_str = "Type".to_string();
1470 let pdf_name = PdfName(name_str.clone());
1471 let obj = PdfObject::Name(pdf_name);
1472
1473 assert!(!obj.is_null());
1474 assert_eq!(obj.as_bool(), None);
1475 assert_eq!(obj.as_integer(), None);
1476 assert_eq!(obj.as_real(), None);
1477 assert_eq!(obj.as_string(), None);
1478 assert!(obj.as_name().is_some());
1479 assert_eq!(obj.as_name().unwrap().as_str(), name_str);
1480 assert_eq!(obj.as_array(), None);
1481 assert_eq!(obj.as_dict(), None);
1482 assert_eq!(obj.as_stream(), None);
1483 assert_eq!(obj.as_reference(), None);
1484 }
1485
1486 #[test]
1487 fn test_pdf_object_array() {
1488 let mut array = PdfArray::new();
1489 array.push(PdfObject::Integer(1));
1490 array.push(PdfObject::Integer(2));
1491 array.push(PdfObject::Integer(3));
1492 let obj = PdfObject::Array(array);
1493
1494 assert!(!obj.is_null());
1495 assert_eq!(obj.as_bool(), None);
1496 assert_eq!(obj.as_integer(), None);
1497 assert_eq!(obj.as_real(), None);
1498 assert_eq!(obj.as_string(), None);
1499 assert_eq!(obj.as_name(), None);
1500 assert!(obj.as_array().is_some());
1501 assert_eq!(obj.as_array().unwrap().len(), 3);
1502 assert_eq!(obj.as_dict(), None);
1503 assert_eq!(obj.as_stream(), None);
1504 assert_eq!(obj.as_reference(), None);
1505 }
1506
1507 #[test]
1508 fn test_pdf_object_dictionary() {
1509 let mut dict = PdfDictionary::new();
1510 dict.insert(
1511 "Type".to_string(),
1512 PdfObject::Name(PdfName("Page".to_string())),
1513 );
1514 dict.insert("Count".to_string(), PdfObject::Integer(5));
1515 let obj = PdfObject::Dictionary(dict);
1516
1517 assert!(!obj.is_null());
1518 assert_eq!(obj.as_bool(), None);
1519 assert_eq!(obj.as_integer(), None);
1520 assert_eq!(obj.as_real(), None);
1521 assert_eq!(obj.as_string(), None);
1522 assert_eq!(obj.as_name(), None);
1523 assert_eq!(obj.as_array(), None);
1524 assert!(obj.as_dict().is_some());
1525 assert_eq!(obj.as_dict().unwrap().0.len(), 2);
1526 assert_eq!(obj.as_stream(), None);
1527 assert_eq!(obj.as_reference(), None);
1528 }
1529
1530 #[test]
1531 fn test_pdf_object_stream() {
1532 let mut dict = PdfDictionary::new();
1533 dict.insert("Length".to_string(), PdfObject::Integer(13));
1534 let data = b"Hello, World!".to_vec();
1535 let stream = PdfStream { dict, data };
1536 let obj = PdfObject::Stream(stream);
1537
1538 assert!(!obj.is_null());
1539 assert_eq!(obj.as_bool(), None);
1540 assert_eq!(obj.as_integer(), None);
1541 assert_eq!(obj.as_real(), None);
1542 assert_eq!(obj.as_string(), None);
1543 assert_eq!(obj.as_name(), None);
1544 assert_eq!(obj.as_array(), None);
1545 assert!(obj.as_dict().is_some()); assert!(obj.as_stream().is_some());
1547 assert_eq!(obj.as_stream().unwrap().raw_data(), b"Hello, World!");
1548 assert_eq!(obj.as_reference(), None);
1549 }
1550
1551 #[test]
1552 fn test_pdf_object_reference() {
1553 let obj = PdfObject::Reference(42, 0);
1554
1555 assert!(!obj.is_null());
1556 assert_eq!(obj.as_bool(), None);
1557 assert_eq!(obj.as_integer(), None);
1558 assert_eq!(obj.as_real(), None);
1559 assert_eq!(obj.as_string(), None);
1560 assert_eq!(obj.as_name(), None);
1561 assert_eq!(obj.as_array(), None);
1562 assert_eq!(obj.as_dict(), None);
1563 assert_eq!(obj.as_stream(), None);
1564 assert_eq!(obj.as_reference(), Some((42, 0)));
1565
1566 let obj_gen = PdfObject::Reference(123, 5);
1568 assert_eq!(obj_gen.as_reference(), Some((123, 5)));
1569 }
1570
1571 #[test]
1572 fn test_pdf_string_methods() {
1573 let string_data = b"Hello, World!".to_vec();
1574 let pdf_string = PdfString(string_data.clone());
1575
1576 assert_eq!(pdf_string.as_bytes(), string_data);
1577 assert_eq!(pdf_string.as_str().unwrap(), "Hello, World!");
1578 assert_eq!(pdf_string.0.len(), 13);
1579 assert!(!pdf_string.0.is_empty());
1580
1581 let empty_string = PdfString(vec![]);
1583 assert!(empty_string.0.is_empty());
1584 assert_eq!(empty_string.0.len(), 0);
1585
1586 let binary_data = vec![0xFF, 0xFE, 0x00, 0x48, 0x00, 0x69]; let binary_string = PdfString(binary_data.clone());
1589 assert_eq!(binary_string.as_bytes(), binary_data);
1590 assert!(binary_string.as_str().is_err()); }
1592
1593 #[test]
1594 fn test_pdf_name_methods() {
1595 let name_str = "Type".to_string();
1596 let pdf_name = PdfName(name_str.clone());
1597
1598 assert_eq!(pdf_name.as_str(), name_str);
1599 assert_eq!(pdf_name.0.len(), 4);
1600 assert!(!pdf_name.0.is_empty());
1601
1602 let empty_name = PdfName("".to_string());
1604 assert!(empty_name.0.is_empty());
1605 assert_eq!(empty_name.0.len(), 0);
1606
1607 let special_name = PdfName("Font#20Name".to_string());
1609 assert_eq!(special_name.as_str(), "Font#20Name");
1610 assert_eq!(special_name.0.len(), 11);
1611 }
1612
1613 #[test]
1614 fn test_pdf_array_methods() {
1615 let mut array = PdfArray::new();
1616 assert_eq!(array.len(), 0);
1617 assert!(array.is_empty());
1618
1619 array.push(PdfObject::Integer(1));
1621 array.push(PdfObject::Integer(2));
1622 array.push(PdfObject::Integer(3));
1623
1624 assert_eq!(array.len(), 3);
1625 assert!(!array.is_empty());
1626
1627 assert_eq!(array.get(0).unwrap().as_integer(), Some(1));
1629 assert_eq!(array.get(1).unwrap().as_integer(), Some(2));
1630 assert_eq!(array.get(2).unwrap().as_integer(), Some(3));
1631 assert!(array.get(3).is_none());
1632
1633 let values: Vec<i64> = array.0.iter().filter_map(|obj| obj.as_integer()).collect();
1635 assert_eq!(values, vec![1, 2, 3]);
1636
1637 let mut mixed_array = PdfArray::new();
1639 mixed_array.push(PdfObject::Integer(42));
1640 mixed_array.push(PdfObject::Real(3.14));
1641 mixed_array.push(PdfObject::String(PdfString(b"text".to_vec())));
1642 mixed_array.push(PdfObject::Name(PdfName("Name".to_string())));
1643 mixed_array.push(PdfObject::Boolean(true));
1644 mixed_array.push(PdfObject::Null);
1645
1646 assert_eq!(mixed_array.len(), 6);
1647 assert_eq!(mixed_array.get(0).unwrap().as_integer(), Some(42));
1648 assert_eq!(mixed_array.get(1).unwrap().as_real(), Some(3.14));
1649 assert_eq!(
1650 mixed_array.get(2).unwrap().as_string().unwrap().as_bytes(),
1651 b"text"
1652 );
1653 assert_eq!(
1654 mixed_array.get(3).unwrap().as_name().unwrap().as_str(),
1655 "Name"
1656 );
1657 assert_eq!(mixed_array.get(4).unwrap().as_bool(), Some(true));
1658 assert!(mixed_array.get(5).unwrap().is_null());
1659 }
1660
1661 #[test]
1662 fn test_pdf_dictionary_methods() {
1663 let mut dict = PdfDictionary::new();
1664 assert_eq!(dict.0.len(), 0);
1665 assert!(dict.0.is_empty());
1666
1667 dict.insert(
1669 "Type".to_string(),
1670 PdfObject::Name(PdfName("Page".to_string())),
1671 );
1672 dict.insert("Count".to_string(), PdfObject::Integer(5));
1673 dict.insert("Resources".to_string(), PdfObject::Reference(10, 0));
1674
1675 assert_eq!(dict.0.len(), 3);
1676 assert!(!dict.0.is_empty());
1677
1678 assert_eq!(
1680 dict.get("Type").unwrap().as_name().unwrap().as_str(),
1681 "Page"
1682 );
1683 assert_eq!(dict.get("Count").unwrap().as_integer(), Some(5));
1684 assert_eq!(dict.get("Resources").unwrap().as_reference(), Some((10, 0)));
1685 assert!(dict.get("NonExistent").is_none());
1686
1687 assert!(dict.contains_key("Type"));
1689 assert!(dict.contains_key("Count"));
1690 assert!(dict.contains_key("Resources"));
1691 assert!(!dict.contains_key("NonExistent"));
1692
1693 assert_eq!(dict.get_type(), Some("Page"));
1695
1696 let mut keys: Vec<String> = dict.0.keys().map(|k| k.0.clone()).collect();
1698 keys.sort();
1699 assert_eq!(keys, vec!["Count", "Resources", "Type"]);
1700
1701 let values: Vec<&PdfObject> = dict.0.values().collect();
1703 assert_eq!(values.len(), 3);
1704 }
1705
1706 #[test]
1707 fn test_pdf_stream_methods() {
1708 let mut dict = PdfDictionary::new();
1709 dict.insert("Length".to_string(), PdfObject::Integer(13));
1710 dict.insert(
1711 "Filter".to_string(),
1712 PdfObject::Name(PdfName("FlateDecode".to_string())),
1713 );
1714
1715 let data = b"Hello, World!".to_vec();
1716 let stream = PdfStream {
1717 dict,
1718 data: data.clone(),
1719 };
1720
1721 assert_eq!(stream.raw_data(), data);
1723
1724 assert_eq!(stream.dict.get("Length").unwrap().as_integer(), Some(13));
1726 assert_eq!(
1727 stream
1728 .dict
1729 .get("Filter")
1730 .unwrap()
1731 .as_name()
1732 .unwrap()
1733 .as_str(),
1734 "FlateDecode"
1735 );
1736
1737 let options = ParseOptions::default();
1740 let decode_result = stream.decode(&options);
1741 assert!(decode_result.is_ok() || decode_result.is_err());
1742 }
1743
1744 #[test]
1745 fn test_parse_complex_nested_structures() {
1746 let input = b"[[1 2] [3 4] [5 6]]";
1748 let mut lexer = Lexer::new(Cursor::new(input));
1749 let obj = PdfObject::parse(&mut lexer).unwrap();
1750
1751 let outer_array = obj.as_array().unwrap();
1752 assert_eq!(outer_array.len(), 3);
1753
1754 for i in 0..3 {
1755 let inner_array = outer_array.get(i).unwrap().as_array().unwrap();
1756 assert_eq!(inner_array.len(), 2);
1757 assert_eq!(
1758 inner_array.get(0).unwrap().as_integer(),
1759 Some((i as i64) * 2 + 1)
1760 );
1761 assert_eq!(
1762 inner_array.get(1).unwrap().as_integer(),
1763 Some((i as i64) * 2 + 2)
1764 );
1765 }
1766 }
1767
1768 #[test]
1769 fn test_parse_complex_dictionary() {
1770 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 >>";
1771 let mut lexer = Lexer::new(Cursor::new(input));
1772 let obj = PdfObject::parse(&mut lexer).unwrap();
1773
1774 let dict = obj.as_dict().unwrap();
1775 assert_eq!(dict.get_type(), Some("Page"));
1776 assert_eq!(dict.get("Parent").unwrap().as_reference(), Some((1, 0)));
1777 assert_eq!(dict.get("Contents").unwrap().as_reference(), Some((3, 0)));
1778
1779 let media_box = dict.get("MediaBox").unwrap().as_array().unwrap();
1781 assert_eq!(media_box.len(), 4);
1782 assert_eq!(media_box.get(0).unwrap().as_integer(), Some(0));
1783 assert_eq!(media_box.get(1).unwrap().as_integer(), Some(0));
1784 assert_eq!(media_box.get(2).unwrap().as_integer(), Some(612));
1785 assert_eq!(media_box.get(3).unwrap().as_integer(), Some(792));
1786
1787 let resources = dict.get("Resources").unwrap().as_dict().unwrap();
1789 assert!(resources.contains_key("Font"));
1790 assert!(resources.contains_key("ProcSet"));
1791
1792 let font_dict = resources.get("Font").unwrap().as_dict().unwrap();
1794 assert_eq!(font_dict.get("F1").unwrap().as_reference(), Some((2, 0)));
1795
1796 let proc_set = resources.get("ProcSet").unwrap().as_array().unwrap();
1798 assert_eq!(proc_set.len(), 2);
1799 assert_eq!(proc_set.get(0).unwrap().as_name().unwrap().as_str(), "PDF");
1800 assert_eq!(proc_set.get(1).unwrap().as_name().unwrap().as_str(), "Text");
1801 }
1802
1803 #[test]
1804 fn test_parse_hex_strings() {
1805 let input = b"<48656C6C6F>"; let mut lexer = Lexer::new(Cursor::new(input));
1807 let obj = PdfObject::parse(&mut lexer).unwrap();
1808
1809 let string = obj.as_string().unwrap();
1810 assert_eq!(string.as_str().unwrap(), "Hello");
1811 }
1812
1813 #[test]
1814 fn test_parse_literal_strings() {
1815 let input = b"(Hello World)";
1816 let mut lexer = Lexer::new(Cursor::new(input));
1817 let obj = PdfObject::parse(&mut lexer).unwrap();
1818
1819 let string = obj.as_string().unwrap();
1820 assert_eq!(string.as_str().unwrap(), "Hello World");
1821 }
1822
1823 #[test]
1824 fn test_parse_string_with_escapes() {
1825 let input = b"(Hello\\nWorld\\t!)";
1826 let mut lexer = Lexer::new(Cursor::new(input));
1827 let obj = PdfObject::parse(&mut lexer).unwrap();
1828
1829 let string = obj.as_string().unwrap();
1830 assert!(!string.as_bytes().is_empty());
1832 }
1833
1834 #[test]
1835 fn test_parse_names_with_special_chars() {
1836 let input = b"/Name#20with#20spaces";
1837 let mut lexer = Lexer::new(Cursor::new(input));
1838 let obj = PdfObject::parse(&mut lexer).unwrap();
1839
1840 let name = obj.as_name().unwrap();
1841 assert!(!name.as_str().is_empty());
1843 }
1844
1845 #[test]
1846 fn test_parse_references() {
1847 let input = b"1 0 R";
1848 let mut lexer = Lexer::new(Cursor::new(input));
1849 let obj = PdfObject::parse(&mut lexer).unwrap();
1850
1851 assert_eq!(obj.as_reference(), Some((1, 0)));
1852
1853 let input2 = b"42 5 R";
1855 let mut lexer2 = Lexer::new(Cursor::new(input2));
1856 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1857
1858 assert_eq!(obj2.as_reference(), Some((42, 5)));
1859 }
1860
1861 #[test]
1862 fn test_parse_edge_cases() {
1863 let input = b"9223372036854775807"; let mut lexer = Lexer::new(Cursor::new(input));
1866 let obj = PdfObject::parse(&mut lexer).unwrap();
1867 assert_eq!(obj.as_integer(), Some(9223372036854775807));
1868
1869 let input2 = b"-9223372036854775808"; let mut lexer2 = Lexer::new(Cursor::new(input2));
1872 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1873 assert_eq!(obj2.as_integer(), Some(-9223372036854775808));
1874
1875 let input3 = b"1.23e-10";
1877 let mut lexer3 = Lexer::new(Cursor::new(input3));
1878 let obj3 = PdfObject::parse(&mut lexer3).unwrap();
1879 assert!(obj3.as_real().is_some());
1881 }
1882
1883 #[test]
1884 fn test_parse_empty_structures() {
1885 let input = b"[]";
1887 let mut lexer = Lexer::new(Cursor::new(input));
1888 let obj = PdfObject::parse(&mut lexer).unwrap();
1889
1890 let array = obj.as_array().unwrap();
1891 assert_eq!(array.len(), 0);
1892 assert!(array.is_empty());
1893
1894 let input2 = b"<< >>";
1896 let mut lexer2 = Lexer::new(Cursor::new(input2));
1897 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1898
1899 let dict = obj2.as_dict().unwrap();
1900 assert_eq!(dict.0.len(), 0);
1901 assert!(dict.0.is_empty());
1902 }
1903
1904 #[test]
1905 fn test_error_handling() {
1906 let input = b"[1 2 3"; let mut lexer = Lexer::new(Cursor::new(input));
1909 let result = PdfObject::parse(&mut lexer);
1910 assert!(result.is_err());
1911
1912 let input2 = b"<< /Type /Page"; let mut lexer2 = Lexer::new(Cursor::new(input2));
1915 let result2 = PdfObject::parse(&mut lexer2);
1916 assert!(result2.is_err());
1917
1918 let input3 = b"1 0 X"; let mut lexer3 = Lexer::new(Cursor::new(input3));
1921 let result3 = PdfObject::parse(&mut lexer3);
1922 assert!(result3.is_ok() || result3.is_err());
1925 }
1926
1927 #[test]
1928 fn test_clone_and_equality() {
1929 let obj1 = PdfObject::Integer(42);
1930 let obj2 = obj1.clone();
1931 assert_eq!(obj1, obj2);
1932
1933 let obj3 = PdfObject::Integer(43);
1934 assert_ne!(obj1, obj3);
1935
1936 let mut array = PdfArray::new();
1938 array.push(PdfObject::Integer(1));
1939 array.push(PdfObject::String(PdfString(b"test".to_vec())));
1940 let obj4 = PdfObject::Array(array);
1941 let obj5 = obj4.clone();
1942 assert_eq!(obj4, obj5);
1943 }
1944
1945 #[test]
1946 fn test_debug_formatting() {
1947 let obj = PdfObject::Integer(42);
1948 let debug_str = format!("{obj:?}");
1949 assert!(debug_str.contains("Integer"));
1950 assert!(debug_str.contains("42"));
1951
1952 let name = PdfName("Type".to_string());
1953 let debug_str2 = format!("{name:?}");
1954 assert!(debug_str2.contains("PdfName"));
1955 assert!(debug_str2.contains("Type"));
1956 }
1957
1958 #[test]
1959 fn test_performance_large_array() {
1960 let mut array = PdfArray::new();
1961 for i in 0..1000 {
1962 array.push(PdfObject::Integer(i));
1963 }
1964
1965 assert_eq!(array.len(), 1000);
1966 assert_eq!(array.get(0).unwrap().as_integer(), Some(0));
1967 assert_eq!(array.get(999).unwrap().as_integer(), Some(999));
1968
1969 let sum: i64 = array.0.iter().filter_map(|obj| obj.as_integer()).sum();
1971 assert_eq!(sum, 499500); }
1973
1974 #[test]
1975 fn test_performance_large_dictionary() {
1976 let mut dict = PdfDictionary::new();
1977 for i in 0..1000 {
1978 dict.insert(format!("Key{i}"), PdfObject::Integer(i));
1979 }
1980
1981 assert_eq!(dict.0.len(), 1000);
1982 assert_eq!(dict.get("Key0").unwrap().as_integer(), Some(0));
1983 assert_eq!(dict.get("Key999").unwrap().as_integer(), Some(999));
1984
1985 for i in 0..1000 {
1987 assert!(dict.contains_key(&format!("Key{i}")));
1988 }
1989 }
1990 }
1991
1992 #[test]
1993 fn test_lenient_stream_parsing_too_short() {
1994 let dict = PdfDictionary(
1997 vec![(PdfName("Length".to_string()), PdfObject::Integer(10))]
1998 .into_iter()
1999 .collect::<HashMap<_, _>>(),
2000 );
2001
2002 let stream_content = b"This is a much longer text content than just 10 bytes";
2005 let test_data = vec![
2006 b"\n".to_vec(), stream_content.to_vec(),
2008 b"\nendstream".to_vec(),
2009 ]
2010 .concat();
2011
2012 let mut cursor = Cursor::new(test_data);
2014 let mut lexer = Lexer::new(&mut cursor);
2015 let mut options = ParseOptions::default();
2016 options.lenient_streams = true;
2017 options.max_recovery_bytes = 100;
2018 options.collect_warnings = false;
2019
2020 let result = PdfObject::parse_stream_data_with_options(&mut lexer, &dict, &options);
2024 if let Err(e) = &result {
2025 tracing::debug!("Error in test_lenient_stream_parsing_too_short: {e:?}");
2026 tracing::debug!("Warning: Stream length mismatch expected, checking if lenient parsing is working correctly");
2027 }
2028 assert!(result.is_ok());
2029
2030 let stream_data = result.unwrap();
2031 let content = String::from_utf8_lossy(&stream_data);
2032
2033 assert!(content.contains("This is a"));
2036 }
2037
2038 #[test]
2039 fn test_lenient_stream_parsing_too_long() {
2040 let dict = PdfDictionary(
2042 vec![(PdfName("Length".to_string()), PdfObject::Integer(100))]
2043 .into_iter()
2044 .collect::<HashMap<_, _>>(),
2045 );
2046
2047 let stream_content = b"Short";
2049 let test_data = vec![
2050 b"\n".to_vec(), stream_content.to_vec(),
2052 b"\nendstream".to_vec(),
2053 ]
2054 .concat();
2055
2056 let mut cursor = Cursor::new(test_data);
2058 let mut lexer = Lexer::new(&mut cursor);
2059 let mut options = ParseOptions::default();
2060 options.lenient_streams = true;
2061 options.max_recovery_bytes = 100;
2062 options.collect_warnings = false;
2063
2064 let result = PdfObject::parse_stream_data_with_options(&mut lexer, &dict, &options);
2067
2068 assert!(result.is_err());
2072 }
2073
2074 #[test]
2075 fn test_lenient_stream_no_endstream_found() {
2076 let input = b"<< /Length 10 >>
2078stream
2079This text does not contain the magic word and continues for a very long time with no proper termination...";
2080
2081 let mut cursor = Cursor::new(input.to_vec());
2082 let mut lexer = Lexer::new(&mut cursor);
2083 let mut options = ParseOptions::default();
2084 options.lenient_streams = true;
2085 options.max_recovery_bytes = 50; options.collect_warnings = false;
2087
2088 let dict_token = lexer.next_token().unwrap();
2089 let obj = PdfObject::parse_from_token_with_options(&mut lexer, dict_token, &options);
2090
2091 assert!(obj.is_err());
2093 }
2094
2095 #[test]
2098 fn test_pdf_name_special_characters() {
2099 let name = PdfName::new("Name#20With#20Spaces".to_string());
2100 assert_eq!(name.as_str(), "Name#20With#20Spaces");
2101
2102 let unicode_name = PdfName::new("café".to_string());
2104 assert_eq!(unicode_name.as_str(), "café");
2105
2106 let special_name = PdfName::new("Font#2FSubtype".to_string());
2108 assert_eq!(special_name.as_str(), "Font#2FSubtype");
2109 }
2110
2111 #[test]
2112 fn test_pdf_name_edge_cases() {
2113 let empty_name = PdfName::new("".to_string());
2115 assert_eq!(empty_name.as_str(), "");
2116
2117 let long_name = PdfName::new("A".repeat(1000));
2119 assert_eq!(long_name.as_str().len(), 1000);
2120
2121 let complex_name = PdfName::new("ABCdef123-._~!*'()".to_string());
2123 assert_eq!(complex_name.as_str(), "ABCdef123-._~!*'()");
2124 }
2125
2126 #[test]
2127 fn test_pdf_string_encoding_validation() {
2128 let utf8_string = PdfString::new("Hello, 世界! 🌍".as_bytes().to_vec());
2130 assert!(utf8_string.as_str().is_ok());
2131
2132 let invalid_utf8 = PdfString::new(vec![0xFF, 0xFE, 0xFD]);
2134 assert!(invalid_utf8.as_str().is_err());
2135
2136 let empty_string = PdfString::new(vec![]);
2138 assert_eq!(empty_string.as_str().unwrap(), "");
2139 }
2140
2141 #[test]
2142 fn test_pdf_string_binary_data() {
2143 let binary_data = vec![0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC];
2145 let binary_string = PdfString::new(binary_data.clone());
2146 assert_eq!(binary_string.as_bytes(), &binary_data);
2147
2148 let null_string = PdfString::new(vec![
2150 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x57, 0x6F, 0x72, 0x6C, 0x64,
2151 ]);
2152 assert_eq!(binary_string.as_bytes().len(), 8);
2153 assert!(null_string.as_bytes().contains(&0x00));
2154 }
2155
2156 #[test]
2157 fn test_pdf_array_nested_structures() {
2158 let mut array = PdfArray::new();
2159
2160 let mut nested_array = PdfArray::new();
2162 nested_array.push(PdfObject::Integer(1));
2163 nested_array.push(PdfObject::Integer(2));
2164 array.push(PdfObject::Array(nested_array));
2165
2166 let mut nested_dict = PdfDictionary(HashMap::new());
2168 nested_dict.0.insert(
2169 PdfName::new("Key".to_string()),
2170 PdfObject::String(PdfString::new(b"Value".to_vec())),
2171 );
2172 array.push(PdfObject::Dictionary(nested_dict));
2173
2174 assert_eq!(array.len(), 2);
2175 assert!(matches!(array.get(0), Some(PdfObject::Array(_))));
2176 assert!(matches!(array.get(1), Some(PdfObject::Dictionary(_))));
2177 }
2178
2179 #[test]
2180 fn test_pdf_array_type_mixing() {
2181 let mut array = PdfArray::new();
2182
2183 array.push(PdfObject::Null);
2185 array.push(PdfObject::Boolean(true));
2186 array.push(PdfObject::Integer(42));
2187 array.push(PdfObject::Real(3.14159));
2188 array.push(PdfObject::String(PdfString::new(b"text".to_vec())));
2189 array.push(PdfObject::Name(PdfName::new("Name".to_string())));
2190
2191 assert_eq!(array.len(), 6);
2192 assert!(matches!(array.get(0), Some(PdfObject::Null)));
2193 assert!(matches!(array.get(1), Some(PdfObject::Boolean(true))));
2194 assert!(matches!(array.get(2), Some(PdfObject::Integer(42))));
2195 assert!(matches!(array.get(3), Some(PdfObject::Real(_))));
2196 assert!(matches!(array.get(4), Some(PdfObject::String(_))));
2197 assert!(matches!(array.get(5), Some(PdfObject::Name(_))));
2198 }
2199
2200 #[test]
2201 fn test_pdf_dictionary_key_operations() {
2202 let mut dict = PdfDictionary(HashMap::new());
2203
2204 dict.0.insert(
2206 PdfName::new("Type".to_string()),
2207 PdfObject::Name(PdfName::new("Test".to_string())),
2208 );
2209 dict.0
2210 .insert(PdfName::new("Count".to_string()), PdfObject::Integer(100));
2211 dict.0
2212 .insert(PdfName::new("Flag".to_string()), PdfObject::Boolean(true));
2213
2214 assert_eq!(dict.0.len(), 3);
2215 assert!(dict.0.contains_key(&PdfName::new("Type".to_string())));
2216 assert!(dict.0.contains_key(&PdfName::new("Count".to_string())));
2217 assert!(dict.0.contains_key(&PdfName::new("Flag".to_string())));
2218 assert!(!dict.0.contains_key(&PdfName::new("Missing".to_string())));
2219
2220 assert!(dict.0.get(&PdfName::new("Type".to_string())).is_some());
2222 }
2223
2224 #[test]
2225 fn test_pdf_dictionary_complex_values() {
2226 let mut dict = PdfDictionary(HashMap::new());
2227
2228 let mut rect_array = PdfArray::new();
2230 rect_array.push(PdfObject::Real(0.0));
2231 rect_array.push(PdfObject::Real(0.0));
2232 rect_array.push(PdfObject::Real(612.0));
2233 rect_array.push(PdfObject::Real(792.0));
2234
2235 dict.0.insert(
2236 PdfName::new("MediaBox".to_string()),
2237 PdfObject::Array(rect_array),
2238 );
2239
2240 let mut resources = PdfDictionary(HashMap::new());
2242 let mut font_dict = PdfDictionary(HashMap::new());
2243 font_dict
2244 .0
2245 .insert(PdfName::new("F1".to_string()), PdfObject::Reference(10, 0));
2246 resources.0.insert(
2247 PdfName::new("Font".to_string()),
2248 PdfObject::Dictionary(font_dict),
2249 );
2250
2251 dict.0.insert(
2252 PdfName::new("Resources".to_string()),
2253 PdfObject::Dictionary(resources),
2254 );
2255
2256 assert_eq!(dict.0.len(), 2);
2257 assert!(dict.0.get(&PdfName::new("MediaBox".to_string())).is_some());
2258 assert!(dict.0.get(&PdfName::new("Resources".to_string())).is_some());
2259 }
2260
2261 #[test]
2262 fn test_object_reference_validation() {
2263 let ref1 = PdfObject::Reference(1, 0);
2264 let ref2 = PdfObject::Reference(1, 0);
2265 let ref3 = PdfObject::Reference(1, 1);
2266 let ref4 = PdfObject::Reference(2, 0);
2267
2268 assert_eq!(ref1, ref2);
2269 assert_ne!(ref1, ref3);
2270 assert_ne!(ref1, ref4);
2271
2272 let max_ref = PdfObject::Reference(u32::MAX, u16::MAX);
2274 assert!(matches!(max_ref, PdfObject::Reference(u32::MAX, u16::MAX)));
2275 }
2276
2277 #[test]
2278 fn test_pdf_object_type_checking() {
2279 let objects = vec![
2280 PdfObject::Null,
2281 PdfObject::Boolean(true),
2282 PdfObject::Integer(42),
2283 PdfObject::Real(3.14),
2284 PdfObject::String(PdfString::new(b"text".to_vec())),
2285 PdfObject::Name(PdfName::new("Name".to_string())),
2286 PdfObject::Array(PdfArray::new()),
2287 PdfObject::Dictionary(PdfDictionary(HashMap::new())),
2288 PdfObject::Reference(1, 0),
2289 ];
2290
2291 assert!(matches!(objects[0], PdfObject::Null));
2293 assert!(matches!(objects[1], PdfObject::Boolean(_)));
2294 assert!(matches!(objects[2], PdfObject::Integer(_)));
2295 assert!(matches!(objects[3], PdfObject::Real(_)));
2296 assert!(matches!(objects[4], PdfObject::String(_)));
2297 assert!(matches!(objects[5], PdfObject::Name(_)));
2298 assert!(matches!(objects[6], PdfObject::Array(_)));
2299 assert!(matches!(objects[7], PdfObject::Dictionary(_)));
2300 assert!(matches!(objects[8], PdfObject::Reference(_, _)));
2301 }
2302
2303 #[test]
2304 fn test_pdf_array_large_capacity() {
2305 let mut array = PdfArray::new();
2306
2307 for i in 0..1000 {
2309 array.push(PdfObject::Integer(i));
2310 }
2311
2312 assert_eq!(array.len(), 1000);
2313 if let Some(PdfObject::Integer(val)) = array.get(999) {
2315 assert_eq!(*val, 999);
2316 } else {
2317 panic!("Expected Integer at index 999");
2318 }
2319 assert!(array.get(1000).is_none());
2320
2321 let mut count = 0;
2323 for i in 0..array.len() {
2324 if let Some(obj) = array.get(i) {
2325 if matches!(obj, PdfObject::Integer(_)) {
2326 count += 1;
2327 }
2328 }
2329 }
2330 assert_eq!(count, 1000);
2331 }
2332
2333 #[test]
2334 fn test_pdf_dictionary_memory_efficiency() {
2335 let mut dict = PdfDictionary(HashMap::new());
2336
2337 for i in 0..100 {
2339 let key = PdfName::new(format!("Key{}", i));
2340 dict.0.insert(key, PdfObject::Integer(i));
2341 }
2342
2343 assert_eq!(dict.0.len(), 100);
2344 assert!(dict.0.contains_key(&PdfName::new("Key99".to_string())));
2345 assert!(!dict.0.contains_key(&PdfName::new("Key100".to_string())));
2346
2347 dict.0.remove(&PdfName::new("Key50".to_string()));
2349 assert_eq!(dict.0.len(), 99);
2350 assert!(!dict.0.contains_key(&PdfName::new("Key50".to_string())));
2351 }
2352
2353 #[test]
2354 fn test_parsing_simple_error_cases() {
2355 use std::io::Cursor;
2356
2357 let empty_input = b"";
2359 let mut cursor = Cursor::new(empty_input.to_vec());
2360 let mut lexer = Lexer::new(&mut cursor);
2361 let result = PdfObject::parse(&mut lexer);
2362
2363 assert!(result.is_err());
2365 }
2366
2367 #[test]
2368 fn test_unicode_string_handling() {
2369 let unicode_tests = vec![
2371 ("ASCII", "Hello World"),
2372 ("Latin-1", "Café résumé"),
2373 ("Emoji", "Hello 🌍 World 🚀"),
2374 ("CJK", "你好世界"),
2375 ("Mixed", "Hello 世界! Bonjour 🌍"),
2376 ];
2377
2378 for (name, text) in unicode_tests {
2379 let pdf_string = PdfString::new(text.as_bytes().to_vec());
2380 match pdf_string.as_str() {
2381 Ok(decoded) => assert_eq!(decoded, text, "Failed for {}", name),
2382 Err(_) => {
2383 assert!(!text.is_empty(), "Should handle {}", name);
2385 }
2386 }
2387 }
2388 }
2389
2390 #[test]
2391 fn test_deep_nesting_limits() {
2392 let mut root_array = PdfArray::new();
2394
2395 for i in 0..10 {
2397 let mut nested = PdfArray::new();
2398 nested.push(PdfObject::Integer(i as i64));
2399 root_array.push(PdfObject::Array(nested));
2400 }
2401
2402 assert_eq!(root_array.len(), 10);
2403
2404 for i in 0..10 {
2406 if let Some(PdfObject::Array(nested)) = root_array.get(i) {
2407 assert_eq!(nested.len(), 1);
2408 }
2409 }
2410 }
2411
2412 #[test]
2413 fn test_special_numeric_values() {
2414 let numbers = vec![
2416 (0i64, 0.0f64),
2417 (i32::MAX as i64, f32::MAX as f64),
2418 (i32::MIN as i64, f32::MIN as f64),
2419 (-1i64, -1.0f64),
2420 (2147483647i64, 2147483647.0f64),
2421 ];
2422
2423 for (int_val, float_val) in numbers {
2424 let int_obj = PdfObject::Integer(int_val);
2425 let float_obj = PdfObject::Real(float_val);
2426
2427 assert!(matches!(int_obj, PdfObject::Integer(_)));
2428 assert!(matches!(float_obj, PdfObject::Real(_)));
2429 }
2430
2431 let special_floats = vec![
2433 (0.0f64, "zero"),
2434 (f64::INFINITY, "infinity"),
2435 (f64::NEG_INFINITY, "negative infinity"),
2436 ];
2437
2438 for (val, _name) in special_floats {
2439 let obj = PdfObject::Real(val);
2440 assert!(matches!(obj, PdfObject::Real(_)));
2441 }
2442 }
2443
2444 #[test]
2445 fn test_array_bounds_checking() {
2446 let mut array = PdfArray::new();
2447 array.push(PdfObject::Integer(1));
2448 array.push(PdfObject::Integer(2));
2449 array.push(PdfObject::Integer(3));
2450
2451 assert!(array.get(0).is_some());
2453 assert!(array.get(1).is_some());
2454 assert!(array.get(2).is_some());
2455
2456 assert!(array.get(3).is_none());
2458 assert!(array.get(100).is_none());
2459
2460 let empty_array = PdfArray::new();
2462 assert!(empty_array.get(0).is_none());
2463 assert_eq!(empty_array.len(), 0);
2464 }
2465
2466 #[test]
2467 fn test_dictionary_case_sensitivity() {
2468 let mut dict = PdfDictionary(HashMap::new());
2469
2470 dict.0.insert(
2472 PdfName::new("Type".to_string()),
2473 PdfObject::Name(PdfName::new("Page".to_string())),
2474 );
2475 dict.0.insert(
2476 PdfName::new("type".to_string()),
2477 PdfObject::Name(PdfName::new("Font".to_string())),
2478 );
2479 dict.0.insert(
2480 PdfName::new("TYPE".to_string()),
2481 PdfObject::Name(PdfName::new("Image".to_string())),
2482 );
2483
2484 assert_eq!(dict.0.len(), 3);
2485 assert!(dict.0.contains_key(&PdfName::new("Type".to_string())));
2486 assert!(dict.0.contains_key(&PdfName::new("type".to_string())));
2487 assert!(dict.0.contains_key(&PdfName::new("TYPE".to_string())));
2488
2489 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("Type".to_string())) {
2491 assert_eq!(name.as_str(), "Page");
2492 }
2493 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("type".to_string())) {
2494 assert_eq!(name.as_str(), "Font");
2495 }
2496 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("TYPE".to_string())) {
2497 assert_eq!(name.as_str(), "Image");
2498 }
2499 }
2500
2501 #[test]
2502 fn test_object_cloning_and_equality() {
2503 let original_array = {
2504 let mut arr = PdfArray::new();
2505 arr.push(PdfObject::Integer(42));
2506 arr.push(PdfObject::String(PdfString::new(b"test".to_vec())));
2507 arr
2508 };
2509
2510 let cloned_array = original_array.clone();
2511 assert_eq!(original_array.len(), cloned_array.len());
2512
2513 for i in 0..original_array.len() {
2515 let orig = original_array.get(i).unwrap();
2516 let cloned = cloned_array.get(i).unwrap();
2517 match (orig, cloned) {
2518 (PdfObject::Integer(a), PdfObject::Integer(b)) => assert_eq!(a, b),
2519 (PdfObject::String(a), PdfObject::String(b)) => {
2520 assert_eq!(a.as_bytes(), b.as_bytes())
2521 }
2522 _ => panic!("Type mismatch in cloned array"),
2523 }
2524 }
2525 }
2526
2527 #[test]
2528 fn test_concurrent_object_access() {
2529 use std::sync::Arc;
2530 use std::thread;
2531
2532 let dict = Arc::new({
2533 let mut d = PdfDictionary(HashMap::new());
2534 d.0.insert(
2535 PdfName::new("SharedKey".to_string()),
2536 PdfObject::Integer(42),
2537 );
2538 d
2539 });
2540
2541 let dict_clone = Arc::clone(&dict);
2542 let handle = thread::spawn(move || {
2543 if let Some(PdfObject::Integer(val)) =
2545 dict_clone.0.get(&PdfName::new("SharedKey".to_string()))
2546 {
2547 assert_eq!(*val, 42);
2548 }
2549 });
2550
2551 if let Some(PdfObject::Integer(val)) = dict.0.get(&PdfName::new("SharedKey".to_string())) {
2553 assert_eq!(*val, 42);
2554 }
2555
2556 handle.join().unwrap();
2557 }
2558
2559 #[test]
2560 fn test_stream_data_edge_cases() {
2561 let mut dict = PdfDictionary(HashMap::new());
2563 dict.0
2564 .insert(PdfName::new("Length".to_string()), PdfObject::Integer(0));
2565
2566 let stream = PdfStream {
2567 dict: dict.clone(),
2568 data: vec![],
2569 };
2570
2571 assert_eq!(stream.data.len(), 0);
2573 assert!(stream.raw_data().is_empty());
2574
2575 let stream_with_data = PdfStream {
2577 dict,
2578 data: b"Hello World".to_vec(),
2579 };
2580
2581 assert_eq!(stream_with_data.raw_data(), b"Hello World");
2582 }
2583
2584 #[test]
2585 fn test_name_object_hash_consistency() {
2586 use std::collections::HashSet;
2587
2588 let mut name_set = HashSet::new();
2589
2590 name_set.insert(PdfName::new("Type".to_string()));
2592 name_set.insert(PdfName::new("Pages".to_string()));
2593 name_set.insert(PdfName::new("Type".to_string())); assert_eq!(name_set.len(), 2); assert!(name_set.contains(&PdfName::new("Type".to_string())));
2597 assert!(name_set.contains(&PdfName::new("Pages".to_string())));
2598 assert!(!name_set.contains(&PdfName::new("Font".to_string())));
2599 }
2600}
2601
2602