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 to_text(&self) -> String {
1215 decode_text_string(&self.0)
1216 }
1217
1218 pub fn as_bytes(&self) -> &[u8] {
1220 &self.0
1221 }
1222}
1223
1224pub(crate) fn decode_text_string(bytes: &[u8]) -> String {
1229 if bytes.len() >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF {
1230 let code_units: Vec<u16> = bytes[2..]
1231 .chunks_exact(2)
1232 .map(|pair| u16::from_be_bytes([pair[0], pair[1]]))
1233 .collect();
1234 String::from_utf16_lossy(&code_units)
1235 } else {
1236 bytes
1237 .iter()
1238 .map(|&byte| crate::text::encoding::winansi_decode_char(byte))
1239 .collect()
1240 }
1241}
1242
1243impl PdfName {
1244 pub fn new(name: String) -> Self {
1246 PdfName(name)
1247 }
1248
1249 pub fn as_str(&self) -> &str {
1251 &self.0
1252 }
1253}
1254
1255#[cfg(test)]
1256mod tests {
1257 use super::*;
1258 use crate::parser::lexer::Lexer;
1259 use crate::parser::ParseOptions;
1260 use std::collections::HashMap;
1261 use std::io::Cursor;
1262
1263 #[test]
1264 fn test_parse_simple_objects() {
1265 let input = b"null true false 123 -456 3.14 /Name (Hello)";
1266 let mut lexer = Lexer::new(Cursor::new(input));
1267
1268 assert_eq!(PdfObject::parse(&mut lexer).unwrap(), PdfObject::Null);
1269 assert_eq!(
1270 PdfObject::parse(&mut lexer).unwrap(),
1271 PdfObject::Boolean(true)
1272 );
1273 assert_eq!(
1274 PdfObject::parse(&mut lexer).unwrap(),
1275 PdfObject::Boolean(false)
1276 );
1277 assert_eq!(
1278 PdfObject::parse(&mut lexer).unwrap(),
1279 PdfObject::Integer(123)
1280 );
1281 assert_eq!(
1282 PdfObject::parse(&mut lexer).unwrap(),
1283 PdfObject::Integer(-456)
1284 );
1285 assert_eq!(PdfObject::parse(&mut lexer).unwrap(), PdfObject::Real(3.14));
1286 assert_eq!(
1287 PdfObject::parse(&mut lexer).unwrap(),
1288 PdfObject::Name(PdfName("Name".to_string()))
1289 );
1290 assert_eq!(
1291 PdfObject::parse(&mut lexer).unwrap(),
1292 PdfObject::String(PdfString(b"Hello".to_vec()))
1293 );
1294 }
1295
1296 #[test]
1297 fn test_parse_array() {
1298 let input = b"[100 200 300 /Name (test)]";
1300 let mut lexer = Lexer::new(Cursor::new(input));
1301
1302 let obj = PdfObject::parse(&mut lexer).unwrap();
1303 let array = obj.as_array().unwrap();
1304
1305 assert_eq!(array.len(), 5);
1306 assert_eq!(array.get(0).unwrap().as_integer(), Some(100));
1307 assert_eq!(array.get(1).unwrap().as_integer(), Some(200));
1308 assert_eq!(array.get(2).unwrap().as_integer(), Some(300));
1309 assert_eq!(array.get(3).unwrap().as_name().unwrap().as_str(), "Name");
1310 assert_eq!(
1311 array.get(4).unwrap().as_string().unwrap().as_bytes(),
1312 b"test"
1313 );
1314 }
1315
1316 #[test]
1317 fn test_parse_array_with_references() {
1318 let input = b"[1 0 R 2 0 R]";
1320 let mut lexer = Lexer::new(Cursor::new(input));
1321
1322 let obj = PdfObject::parse(&mut lexer).unwrap();
1323 let array = obj.as_array().unwrap();
1324
1325 assert_eq!(array.len(), 2);
1326 assert!(array.get(0).unwrap().as_reference().is_some());
1327 assert!(array.get(1).unwrap().as_reference().is_some());
1328 }
1329
1330 #[test]
1331 fn test_parse_dictionary() {
1332 let input = b"<< /Type /Page /Parent 1 0 R /MediaBox [0 0 612 792] >>";
1333 let mut lexer = Lexer::new(Cursor::new(input));
1334
1335 let obj = PdfObject::parse(&mut lexer).unwrap();
1336 let dict = obj.as_dict().unwrap();
1337
1338 assert_eq!(dict.get_type(), Some("Page"));
1339 assert!(dict.get("Parent").unwrap().as_reference().is_some());
1340 assert!(dict.get("MediaBox").unwrap().as_array().is_some());
1341 }
1342
1343 mod comprehensive_tests {
1345 use super::*;
1346
1347 #[test]
1348 fn test_pdf_object_null() {
1349 let obj = PdfObject::Null;
1350 assert!(obj.is_null());
1351 assert_eq!(obj.as_bool(), None);
1352 assert_eq!(obj.as_integer(), None);
1353 assert_eq!(obj.as_real(), None);
1354 assert_eq!(obj.as_string(), None);
1355 assert_eq!(obj.as_name(), None);
1356 assert_eq!(obj.as_array(), None);
1357 assert_eq!(obj.as_dict(), None);
1358 assert_eq!(obj.as_stream(), None);
1359 assert_eq!(obj.as_reference(), None);
1360 }
1361
1362 #[test]
1363 fn test_pdf_object_boolean() {
1364 let obj_true = PdfObject::Boolean(true);
1365 let obj_false = PdfObject::Boolean(false);
1366
1367 assert!(!obj_true.is_null());
1368 assert_eq!(obj_true.as_bool(), Some(true));
1369 assert_eq!(obj_false.as_bool(), Some(false));
1370
1371 assert_eq!(obj_true.as_integer(), None);
1372 assert_eq!(obj_true.as_real(), None);
1373 assert_eq!(obj_true.as_string(), None);
1374 assert_eq!(obj_true.as_name(), None);
1375 assert_eq!(obj_true.as_array(), None);
1376 assert_eq!(obj_true.as_dict(), None);
1377 assert_eq!(obj_true.as_stream(), None);
1378 assert_eq!(obj_true.as_reference(), None);
1379 }
1380
1381 #[test]
1382 fn test_pdf_object_integer() {
1383 let obj = PdfObject::Integer(42);
1384
1385 assert!(!obj.is_null());
1386 assert_eq!(obj.as_bool(), None);
1387 assert_eq!(obj.as_integer(), Some(42));
1388 assert_eq!(obj.as_real(), Some(42.0)); assert_eq!(obj.as_string(), None);
1390 assert_eq!(obj.as_name(), None);
1391 assert_eq!(obj.as_array(), None);
1392 assert_eq!(obj.as_dict(), None);
1393 assert_eq!(obj.as_stream(), None);
1394 assert_eq!(obj.as_reference(), None);
1395
1396 let obj_neg = PdfObject::Integer(-123);
1398 assert_eq!(obj_neg.as_integer(), Some(-123));
1399 assert_eq!(obj_neg.as_real(), Some(-123.0));
1400
1401 let obj_large = PdfObject::Integer(9999999999);
1403 assert_eq!(obj_large.as_integer(), Some(9999999999));
1404 assert_eq!(obj_large.as_real(), Some(9999999999.0));
1405 }
1406
1407 #[test]
1408 fn test_pdf_object_real() {
1409 let obj = PdfObject::Real(3.14159);
1410
1411 assert!(!obj.is_null());
1412 assert_eq!(obj.as_bool(), None);
1413 assert_eq!(obj.as_integer(), None);
1414 assert_eq!(obj.as_real(), Some(3.14159));
1415 assert_eq!(obj.as_string(), None);
1416 assert_eq!(obj.as_name(), None);
1417 assert_eq!(obj.as_array(), None);
1418 assert_eq!(obj.as_dict(), None);
1419 assert_eq!(obj.as_stream(), None);
1420 assert_eq!(obj.as_reference(), None);
1421
1422 let obj_neg = PdfObject::Real(-2.71828);
1424 assert_eq!(obj_neg.as_real(), Some(-2.71828));
1425
1426 let obj_zero = PdfObject::Real(0.0);
1428 assert_eq!(obj_zero.as_real(), Some(0.0));
1429
1430 let obj_small = PdfObject::Real(0.000001);
1432 assert_eq!(obj_small.as_real(), Some(0.000001));
1433
1434 let obj_large = PdfObject::Real(1e10);
1436 assert_eq!(obj_large.as_real(), Some(1e10));
1437 }
1438
1439 #[test]
1440 fn test_pdf_object_string() {
1441 let string_data = b"Hello World".to_vec();
1442 let pdf_string = PdfString(string_data.clone());
1443 let obj = PdfObject::String(pdf_string);
1444
1445 assert!(!obj.is_null());
1446 assert_eq!(obj.as_bool(), None);
1447 assert_eq!(obj.as_integer(), None);
1448 assert_eq!(obj.as_real(), None);
1449 assert!(obj.as_string().is_some());
1450 assert_eq!(obj.as_string().unwrap().as_bytes(), string_data);
1451 assert_eq!(obj.as_name(), None);
1452 assert_eq!(obj.as_array(), None);
1453 assert_eq!(obj.as_dict(), None);
1454 assert_eq!(obj.as_stream(), None);
1455 assert_eq!(obj.as_reference(), None);
1456 }
1457
1458 #[test]
1459 fn test_pdf_object_name() {
1460 let name_str = "Type".to_string();
1461 let pdf_name = PdfName(name_str.clone());
1462 let obj = PdfObject::Name(pdf_name);
1463
1464 assert!(!obj.is_null());
1465 assert_eq!(obj.as_bool(), None);
1466 assert_eq!(obj.as_integer(), None);
1467 assert_eq!(obj.as_real(), None);
1468 assert_eq!(obj.as_string(), None);
1469 assert!(obj.as_name().is_some());
1470 assert_eq!(obj.as_name().unwrap().as_str(), name_str);
1471 assert_eq!(obj.as_array(), None);
1472 assert_eq!(obj.as_dict(), None);
1473 assert_eq!(obj.as_stream(), None);
1474 assert_eq!(obj.as_reference(), None);
1475 }
1476
1477 #[test]
1478 fn test_pdf_object_array() {
1479 let mut array = PdfArray::new();
1480 array.push(PdfObject::Integer(1));
1481 array.push(PdfObject::Integer(2));
1482 array.push(PdfObject::Integer(3));
1483 let obj = PdfObject::Array(array);
1484
1485 assert!(!obj.is_null());
1486 assert_eq!(obj.as_bool(), None);
1487 assert_eq!(obj.as_integer(), None);
1488 assert_eq!(obj.as_real(), None);
1489 assert_eq!(obj.as_string(), None);
1490 assert_eq!(obj.as_name(), None);
1491 assert!(obj.as_array().is_some());
1492 assert_eq!(obj.as_array().unwrap().len(), 3);
1493 assert_eq!(obj.as_dict(), None);
1494 assert_eq!(obj.as_stream(), None);
1495 assert_eq!(obj.as_reference(), None);
1496 }
1497
1498 #[test]
1499 fn test_pdf_object_dictionary() {
1500 let mut dict = PdfDictionary::new();
1501 dict.insert(
1502 "Type".to_string(),
1503 PdfObject::Name(PdfName("Page".to_string())),
1504 );
1505 dict.insert("Count".to_string(), PdfObject::Integer(5));
1506 let obj = PdfObject::Dictionary(dict);
1507
1508 assert!(!obj.is_null());
1509 assert_eq!(obj.as_bool(), None);
1510 assert_eq!(obj.as_integer(), None);
1511 assert_eq!(obj.as_real(), None);
1512 assert_eq!(obj.as_string(), None);
1513 assert_eq!(obj.as_name(), None);
1514 assert_eq!(obj.as_array(), None);
1515 assert!(obj.as_dict().is_some());
1516 assert_eq!(obj.as_dict().unwrap().0.len(), 2);
1517 assert_eq!(obj.as_stream(), None);
1518 assert_eq!(obj.as_reference(), None);
1519 }
1520
1521 #[test]
1522 fn test_pdf_object_stream() {
1523 let mut dict = PdfDictionary::new();
1524 dict.insert("Length".to_string(), PdfObject::Integer(13));
1525 let data = b"Hello, World!".to_vec();
1526 let stream = PdfStream { dict, data };
1527 let obj = PdfObject::Stream(stream);
1528
1529 assert!(!obj.is_null());
1530 assert_eq!(obj.as_bool(), None);
1531 assert_eq!(obj.as_integer(), None);
1532 assert_eq!(obj.as_real(), None);
1533 assert_eq!(obj.as_string(), None);
1534 assert_eq!(obj.as_name(), None);
1535 assert_eq!(obj.as_array(), None);
1536 assert!(obj.as_dict().is_some()); assert!(obj.as_stream().is_some());
1538 assert_eq!(obj.as_stream().unwrap().raw_data(), b"Hello, World!");
1539 assert_eq!(obj.as_reference(), None);
1540 }
1541
1542 #[test]
1543 fn test_pdf_object_reference() {
1544 let obj = PdfObject::Reference(42, 0);
1545
1546 assert!(!obj.is_null());
1547 assert_eq!(obj.as_bool(), None);
1548 assert_eq!(obj.as_integer(), None);
1549 assert_eq!(obj.as_real(), None);
1550 assert_eq!(obj.as_string(), None);
1551 assert_eq!(obj.as_name(), None);
1552 assert_eq!(obj.as_array(), None);
1553 assert_eq!(obj.as_dict(), None);
1554 assert_eq!(obj.as_stream(), None);
1555 assert_eq!(obj.as_reference(), Some((42, 0)));
1556
1557 let obj_gen = PdfObject::Reference(123, 5);
1559 assert_eq!(obj_gen.as_reference(), Some((123, 5)));
1560 }
1561
1562 #[test]
1563 fn test_pdf_string_methods() {
1564 let string_data = b"Hello, World!".to_vec();
1565 let pdf_string = PdfString(string_data.clone());
1566
1567 assert_eq!(pdf_string.as_bytes(), string_data);
1568 assert_eq!(pdf_string.as_str().unwrap(), "Hello, World!");
1569 assert_eq!(pdf_string.0.len(), 13);
1570 assert!(!pdf_string.0.is_empty());
1571
1572 let empty_string = PdfString(vec![]);
1574 assert!(empty_string.0.is_empty());
1575 assert_eq!(empty_string.0.len(), 0);
1576
1577 let binary_data = vec![0xFF, 0xFE, 0x00, 0x48, 0x00, 0x69]; let binary_string = PdfString(binary_data.clone());
1580 assert_eq!(binary_string.as_bytes(), binary_data);
1581 assert!(binary_string.as_str().is_err()); }
1583
1584 #[test]
1585 fn test_pdf_name_methods() {
1586 let name_str = "Type".to_string();
1587 let pdf_name = PdfName(name_str.clone());
1588
1589 assert_eq!(pdf_name.as_str(), name_str);
1590 assert_eq!(pdf_name.0.len(), 4);
1591 assert!(!pdf_name.0.is_empty());
1592
1593 let empty_name = PdfName("".to_string());
1595 assert!(empty_name.0.is_empty());
1596 assert_eq!(empty_name.0.len(), 0);
1597
1598 let special_name = PdfName("Font#20Name".to_string());
1600 assert_eq!(special_name.as_str(), "Font#20Name");
1601 assert_eq!(special_name.0.len(), 11);
1602 }
1603
1604 #[test]
1605 fn test_pdf_array_methods() {
1606 let mut array = PdfArray::new();
1607 assert_eq!(array.len(), 0);
1608 assert!(array.is_empty());
1609
1610 array.push(PdfObject::Integer(1));
1612 array.push(PdfObject::Integer(2));
1613 array.push(PdfObject::Integer(3));
1614
1615 assert_eq!(array.len(), 3);
1616 assert!(!array.is_empty());
1617
1618 assert_eq!(array.get(0).unwrap().as_integer(), Some(1));
1620 assert_eq!(array.get(1).unwrap().as_integer(), Some(2));
1621 assert_eq!(array.get(2).unwrap().as_integer(), Some(3));
1622 assert!(array.get(3).is_none());
1623
1624 let values: Vec<i64> = array.0.iter().filter_map(|obj| obj.as_integer()).collect();
1626 assert_eq!(values, vec![1, 2, 3]);
1627
1628 let mut mixed_array = PdfArray::new();
1630 mixed_array.push(PdfObject::Integer(42));
1631 mixed_array.push(PdfObject::Real(3.14));
1632 mixed_array.push(PdfObject::String(PdfString(b"text".to_vec())));
1633 mixed_array.push(PdfObject::Name(PdfName("Name".to_string())));
1634 mixed_array.push(PdfObject::Boolean(true));
1635 mixed_array.push(PdfObject::Null);
1636
1637 assert_eq!(mixed_array.len(), 6);
1638 assert_eq!(mixed_array.get(0).unwrap().as_integer(), Some(42));
1639 assert_eq!(mixed_array.get(1).unwrap().as_real(), Some(3.14));
1640 assert_eq!(
1641 mixed_array.get(2).unwrap().as_string().unwrap().as_bytes(),
1642 b"text"
1643 );
1644 assert_eq!(
1645 mixed_array.get(3).unwrap().as_name().unwrap().as_str(),
1646 "Name"
1647 );
1648 assert_eq!(mixed_array.get(4).unwrap().as_bool(), Some(true));
1649 assert!(mixed_array.get(5).unwrap().is_null());
1650 }
1651
1652 #[test]
1653 fn test_pdf_dictionary_methods() {
1654 let mut dict = PdfDictionary::new();
1655 assert_eq!(dict.0.len(), 0);
1656 assert!(dict.0.is_empty());
1657
1658 dict.insert(
1660 "Type".to_string(),
1661 PdfObject::Name(PdfName("Page".to_string())),
1662 );
1663 dict.insert("Count".to_string(), PdfObject::Integer(5));
1664 dict.insert("Resources".to_string(), PdfObject::Reference(10, 0));
1665
1666 assert_eq!(dict.0.len(), 3);
1667 assert!(!dict.0.is_empty());
1668
1669 assert_eq!(
1671 dict.get("Type").unwrap().as_name().unwrap().as_str(),
1672 "Page"
1673 );
1674 assert_eq!(dict.get("Count").unwrap().as_integer(), Some(5));
1675 assert_eq!(dict.get("Resources").unwrap().as_reference(), Some((10, 0)));
1676 assert!(dict.get("NonExistent").is_none());
1677
1678 assert!(dict.contains_key("Type"));
1680 assert!(dict.contains_key("Count"));
1681 assert!(dict.contains_key("Resources"));
1682 assert!(!dict.contains_key("NonExistent"));
1683
1684 assert_eq!(dict.get_type(), Some("Page"));
1686
1687 let mut keys: Vec<String> = dict.0.keys().map(|k| k.0.clone()).collect();
1689 keys.sort();
1690 assert_eq!(keys, vec!["Count", "Resources", "Type"]);
1691
1692 let values: Vec<&PdfObject> = dict.0.values().collect();
1694 assert_eq!(values.len(), 3);
1695 }
1696
1697 #[test]
1698 fn test_pdf_stream_methods() {
1699 let mut dict = PdfDictionary::new();
1700 dict.insert("Length".to_string(), PdfObject::Integer(13));
1701 dict.insert(
1702 "Filter".to_string(),
1703 PdfObject::Name(PdfName("FlateDecode".to_string())),
1704 );
1705
1706 let data = b"Hello, World!".to_vec();
1707 let stream = PdfStream {
1708 dict,
1709 data: data.clone(),
1710 };
1711
1712 assert_eq!(stream.raw_data(), data);
1714
1715 assert_eq!(stream.dict.get("Length").unwrap().as_integer(), Some(13));
1717 assert_eq!(
1718 stream
1719 .dict
1720 .get("Filter")
1721 .unwrap()
1722 .as_name()
1723 .unwrap()
1724 .as_str(),
1725 "FlateDecode"
1726 );
1727
1728 let options = ParseOptions::default();
1731 let decode_result = stream.decode(&options);
1732 assert!(decode_result.is_ok() || decode_result.is_err());
1733 }
1734
1735 #[test]
1736 fn test_parse_complex_nested_structures() {
1737 let input = b"[[1 2] [3 4] [5 6]]";
1739 let mut lexer = Lexer::new(Cursor::new(input));
1740 let obj = PdfObject::parse(&mut lexer).unwrap();
1741
1742 let outer_array = obj.as_array().unwrap();
1743 assert_eq!(outer_array.len(), 3);
1744
1745 for i in 0..3 {
1746 let inner_array = outer_array.get(i).unwrap().as_array().unwrap();
1747 assert_eq!(inner_array.len(), 2);
1748 assert_eq!(
1749 inner_array.get(0).unwrap().as_integer(),
1750 Some((i as i64) * 2 + 1)
1751 );
1752 assert_eq!(
1753 inner_array.get(1).unwrap().as_integer(),
1754 Some((i as i64) * 2 + 2)
1755 );
1756 }
1757 }
1758
1759 #[test]
1760 fn test_parse_complex_dictionary() {
1761 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 >>";
1762 let mut lexer = Lexer::new(Cursor::new(input));
1763 let obj = PdfObject::parse(&mut lexer).unwrap();
1764
1765 let dict = obj.as_dict().unwrap();
1766 assert_eq!(dict.get_type(), Some("Page"));
1767 assert_eq!(dict.get("Parent").unwrap().as_reference(), Some((1, 0)));
1768 assert_eq!(dict.get("Contents").unwrap().as_reference(), Some((3, 0)));
1769
1770 let media_box = dict.get("MediaBox").unwrap().as_array().unwrap();
1772 assert_eq!(media_box.len(), 4);
1773 assert_eq!(media_box.get(0).unwrap().as_integer(), Some(0));
1774 assert_eq!(media_box.get(1).unwrap().as_integer(), Some(0));
1775 assert_eq!(media_box.get(2).unwrap().as_integer(), Some(612));
1776 assert_eq!(media_box.get(3).unwrap().as_integer(), Some(792));
1777
1778 let resources = dict.get("Resources").unwrap().as_dict().unwrap();
1780 assert!(resources.contains_key("Font"));
1781 assert!(resources.contains_key("ProcSet"));
1782
1783 let font_dict = resources.get("Font").unwrap().as_dict().unwrap();
1785 assert_eq!(font_dict.get("F1").unwrap().as_reference(), Some((2, 0)));
1786
1787 let proc_set = resources.get("ProcSet").unwrap().as_array().unwrap();
1789 assert_eq!(proc_set.len(), 2);
1790 assert_eq!(proc_set.get(0).unwrap().as_name().unwrap().as_str(), "PDF");
1791 assert_eq!(proc_set.get(1).unwrap().as_name().unwrap().as_str(), "Text");
1792 }
1793
1794 #[test]
1795 fn test_parse_hex_strings() {
1796 let input = b"<48656C6C6F>"; let mut lexer = Lexer::new(Cursor::new(input));
1798 let obj = PdfObject::parse(&mut lexer).unwrap();
1799
1800 let string = obj.as_string().unwrap();
1801 assert_eq!(string.as_str().unwrap(), "Hello");
1802 }
1803
1804 #[test]
1805 fn test_parse_literal_strings() {
1806 let input = b"(Hello World)";
1807 let mut lexer = Lexer::new(Cursor::new(input));
1808 let obj = PdfObject::parse(&mut lexer).unwrap();
1809
1810 let string = obj.as_string().unwrap();
1811 assert_eq!(string.as_str().unwrap(), "Hello World");
1812 }
1813
1814 #[test]
1815 fn test_parse_string_with_escapes() {
1816 let input = b"(Hello\\nWorld\\t!)";
1817 let mut lexer = Lexer::new(Cursor::new(input));
1818 let obj = PdfObject::parse(&mut lexer).unwrap();
1819
1820 let string = obj.as_string().unwrap();
1821 assert!(!string.as_bytes().is_empty());
1823 }
1824
1825 #[test]
1826 fn test_parse_names_with_special_chars() {
1827 let input = b"/Name#20with#20spaces";
1828 let mut lexer = Lexer::new(Cursor::new(input));
1829 let obj = PdfObject::parse(&mut lexer).unwrap();
1830
1831 let name = obj.as_name().unwrap();
1832 assert!(!name.as_str().is_empty());
1834 }
1835
1836 #[test]
1837 fn test_parse_references() {
1838 let input = b"1 0 R";
1839 let mut lexer = Lexer::new(Cursor::new(input));
1840 let obj = PdfObject::parse(&mut lexer).unwrap();
1841
1842 assert_eq!(obj.as_reference(), Some((1, 0)));
1843
1844 let input2 = b"42 5 R";
1846 let mut lexer2 = Lexer::new(Cursor::new(input2));
1847 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1848
1849 assert_eq!(obj2.as_reference(), Some((42, 5)));
1850 }
1851
1852 #[test]
1853 fn test_parse_edge_cases() {
1854 let input = b"9223372036854775807"; let mut lexer = Lexer::new(Cursor::new(input));
1857 let obj = PdfObject::parse(&mut lexer).unwrap();
1858 assert_eq!(obj.as_integer(), Some(9223372036854775807));
1859
1860 let input2 = b"-9223372036854775808"; let mut lexer2 = Lexer::new(Cursor::new(input2));
1863 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1864 assert_eq!(obj2.as_integer(), Some(-9223372036854775808));
1865
1866 let input3 = b"1.23e-10";
1868 let mut lexer3 = Lexer::new(Cursor::new(input3));
1869 let obj3 = PdfObject::parse(&mut lexer3).unwrap();
1870 assert!(obj3.as_real().is_some());
1872 }
1873
1874 #[test]
1875 fn test_parse_empty_structures() {
1876 let input = b"[]";
1878 let mut lexer = Lexer::new(Cursor::new(input));
1879 let obj = PdfObject::parse(&mut lexer).unwrap();
1880
1881 let array = obj.as_array().unwrap();
1882 assert_eq!(array.len(), 0);
1883 assert!(array.is_empty());
1884
1885 let input2 = b"<< >>";
1887 let mut lexer2 = Lexer::new(Cursor::new(input2));
1888 let obj2 = PdfObject::parse(&mut lexer2).unwrap();
1889
1890 let dict = obj2.as_dict().unwrap();
1891 assert_eq!(dict.0.len(), 0);
1892 assert!(dict.0.is_empty());
1893 }
1894
1895 #[test]
1896 fn test_error_handling() {
1897 let input = b"[1 2 3"; let mut lexer = Lexer::new(Cursor::new(input));
1900 let result = PdfObject::parse(&mut lexer);
1901 assert!(result.is_err());
1902
1903 let input2 = b"<< /Type /Page"; let mut lexer2 = Lexer::new(Cursor::new(input2));
1906 let result2 = PdfObject::parse(&mut lexer2);
1907 assert!(result2.is_err());
1908
1909 let input3 = b"1 0 X"; let mut lexer3 = Lexer::new(Cursor::new(input3));
1912 let result3 = PdfObject::parse(&mut lexer3);
1913 assert!(result3.is_ok() || result3.is_err());
1916 }
1917
1918 #[test]
1919 fn test_clone_and_equality() {
1920 let obj1 = PdfObject::Integer(42);
1921 let obj2 = obj1.clone();
1922 assert_eq!(obj1, obj2);
1923
1924 let obj3 = PdfObject::Integer(43);
1925 assert_ne!(obj1, obj3);
1926
1927 let mut array = PdfArray::new();
1929 array.push(PdfObject::Integer(1));
1930 array.push(PdfObject::String(PdfString(b"test".to_vec())));
1931 let obj4 = PdfObject::Array(array);
1932 let obj5 = obj4.clone();
1933 assert_eq!(obj4, obj5);
1934 }
1935
1936 #[test]
1937 fn test_debug_formatting() {
1938 let obj = PdfObject::Integer(42);
1939 let debug_str = format!("{obj:?}");
1940 assert!(debug_str.contains("Integer"));
1941 assert!(debug_str.contains("42"));
1942
1943 let name = PdfName("Type".to_string());
1944 let debug_str2 = format!("{name:?}");
1945 assert!(debug_str2.contains("PdfName"));
1946 assert!(debug_str2.contains("Type"));
1947 }
1948
1949 #[test]
1950 fn test_performance_large_array() {
1951 let mut array = PdfArray::new();
1952 for i in 0..1000 {
1953 array.push(PdfObject::Integer(i));
1954 }
1955
1956 assert_eq!(array.len(), 1000);
1957 assert_eq!(array.get(0).unwrap().as_integer(), Some(0));
1958 assert_eq!(array.get(999).unwrap().as_integer(), Some(999));
1959
1960 let sum: i64 = array.0.iter().filter_map(|obj| obj.as_integer()).sum();
1962 assert_eq!(sum, 499500); }
1964
1965 #[test]
1966 fn test_performance_large_dictionary() {
1967 let mut dict = PdfDictionary::new();
1968 for i in 0..1000 {
1969 dict.insert(format!("Key{i}"), PdfObject::Integer(i));
1970 }
1971
1972 assert_eq!(dict.0.len(), 1000);
1973 assert_eq!(dict.get("Key0").unwrap().as_integer(), Some(0));
1974 assert_eq!(dict.get("Key999").unwrap().as_integer(), Some(999));
1975
1976 for i in 0..1000 {
1978 assert!(dict.contains_key(&format!("Key{i}")));
1979 }
1980 }
1981 }
1982
1983 #[test]
1984 fn test_lenient_stream_parsing_too_short() {
1985 let dict = PdfDictionary(
1988 vec![(PdfName("Length".to_string()), PdfObject::Integer(10))]
1989 .into_iter()
1990 .collect::<HashMap<_, _>>(),
1991 );
1992
1993 let stream_content = b"This is a much longer text content than just 10 bytes";
1996 let test_data = vec![
1997 b"\n".to_vec(), stream_content.to_vec(),
1999 b"\nendstream".to_vec(),
2000 ]
2001 .concat();
2002
2003 let mut cursor = Cursor::new(test_data);
2005 let mut lexer = Lexer::new(&mut cursor);
2006 let mut options = ParseOptions::default();
2007 options.lenient_streams = true;
2008 options.max_recovery_bytes = 100;
2009 options.collect_warnings = false;
2010
2011 let result = PdfObject::parse_stream_data_with_options(&mut lexer, &dict, &options);
2015 if let Err(e) = &result {
2016 tracing::debug!("Error in test_lenient_stream_parsing_too_short: {e:?}");
2017 tracing::debug!("Warning: Stream length mismatch expected, checking if lenient parsing is working correctly");
2018 }
2019 assert!(result.is_ok());
2020
2021 let stream_data = result.unwrap();
2022 let content = String::from_utf8_lossy(&stream_data);
2023
2024 assert!(content.contains("This is a"));
2027 }
2028
2029 #[test]
2030 fn test_lenient_stream_parsing_too_long() {
2031 let dict = PdfDictionary(
2033 vec![(PdfName("Length".to_string()), PdfObject::Integer(100))]
2034 .into_iter()
2035 .collect::<HashMap<_, _>>(),
2036 );
2037
2038 let stream_content = b"Short";
2040 let test_data = vec![
2041 b"\n".to_vec(), stream_content.to_vec(),
2043 b"\nendstream".to_vec(),
2044 ]
2045 .concat();
2046
2047 let mut cursor = Cursor::new(test_data);
2049 let mut lexer = Lexer::new(&mut cursor);
2050 let mut options = ParseOptions::default();
2051 options.lenient_streams = true;
2052 options.max_recovery_bytes = 100;
2053 options.collect_warnings = false;
2054
2055 let result = PdfObject::parse_stream_data_with_options(&mut lexer, &dict, &options);
2058
2059 assert!(result.is_err());
2063 }
2064
2065 #[test]
2066 fn test_lenient_stream_no_endstream_found() {
2067 let input = b"<< /Length 10 >>
2069stream
2070This text does not contain the magic word and continues for a very long time with no proper termination...";
2071
2072 let mut cursor = Cursor::new(input.to_vec());
2073 let mut lexer = Lexer::new(&mut cursor);
2074 let mut options = ParseOptions::default();
2075 options.lenient_streams = true;
2076 options.max_recovery_bytes = 50; options.collect_warnings = false;
2078
2079 let dict_token = lexer.next_token().unwrap();
2080 let obj = PdfObject::parse_from_token_with_options(&mut lexer, dict_token, &options);
2081
2082 assert!(obj.is_err());
2084 }
2085
2086 #[test]
2089 fn test_pdf_name_special_characters() {
2090 let name = PdfName::new("Name#20With#20Spaces".to_string());
2091 assert_eq!(name.as_str(), "Name#20With#20Spaces");
2092
2093 let unicode_name = PdfName::new("café".to_string());
2095 assert_eq!(unicode_name.as_str(), "café");
2096
2097 let special_name = PdfName::new("Font#2FSubtype".to_string());
2099 assert_eq!(special_name.as_str(), "Font#2FSubtype");
2100 }
2101
2102 #[test]
2103 fn test_pdf_name_edge_cases() {
2104 let empty_name = PdfName::new("".to_string());
2106 assert_eq!(empty_name.as_str(), "");
2107
2108 let long_name = PdfName::new("A".repeat(1000));
2110 assert_eq!(long_name.as_str().len(), 1000);
2111
2112 let complex_name = PdfName::new("ABCdef123-._~!*'()".to_string());
2114 assert_eq!(complex_name.as_str(), "ABCdef123-._~!*'()");
2115 }
2116
2117 #[test]
2118 fn test_pdf_string_encoding_validation() {
2119 let utf8_string = PdfString::new("Hello, 世界! 🌍".as_bytes().to_vec());
2121 assert!(utf8_string.as_str().is_ok());
2122
2123 let invalid_utf8 = PdfString::new(vec![0xFF, 0xFE, 0xFD]);
2125 assert!(invalid_utf8.as_str().is_err());
2126
2127 let empty_string = PdfString::new(vec![]);
2129 assert_eq!(empty_string.as_str().unwrap(), "");
2130 }
2131
2132 #[test]
2133 fn test_pdf_string_binary_data() {
2134 let binary_data = vec![0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC];
2136 let binary_string = PdfString::new(binary_data.clone());
2137 assert_eq!(binary_string.as_bytes(), &binary_data);
2138
2139 let null_string = PdfString::new(vec![
2141 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x57, 0x6F, 0x72, 0x6C, 0x64,
2142 ]);
2143 assert_eq!(binary_string.as_bytes().len(), 8);
2144 assert!(null_string.as_bytes().contains(&0x00));
2145 }
2146
2147 #[test]
2148 fn test_pdf_array_nested_structures() {
2149 let mut array = PdfArray::new();
2150
2151 let mut nested_array = PdfArray::new();
2153 nested_array.push(PdfObject::Integer(1));
2154 nested_array.push(PdfObject::Integer(2));
2155 array.push(PdfObject::Array(nested_array));
2156
2157 let mut nested_dict = PdfDictionary(HashMap::new());
2159 nested_dict.0.insert(
2160 PdfName::new("Key".to_string()),
2161 PdfObject::String(PdfString::new(b"Value".to_vec())),
2162 );
2163 array.push(PdfObject::Dictionary(nested_dict));
2164
2165 assert_eq!(array.len(), 2);
2166 assert!(matches!(array.get(0), Some(PdfObject::Array(_))));
2167 assert!(matches!(array.get(1), Some(PdfObject::Dictionary(_))));
2168 }
2169
2170 #[test]
2171 fn test_pdf_array_type_mixing() {
2172 let mut array = PdfArray::new();
2173
2174 array.push(PdfObject::Null);
2176 array.push(PdfObject::Boolean(true));
2177 array.push(PdfObject::Integer(42));
2178 array.push(PdfObject::Real(3.14159));
2179 array.push(PdfObject::String(PdfString::new(b"text".to_vec())));
2180 array.push(PdfObject::Name(PdfName::new("Name".to_string())));
2181
2182 assert_eq!(array.len(), 6);
2183 assert!(matches!(array.get(0), Some(PdfObject::Null)));
2184 assert!(matches!(array.get(1), Some(PdfObject::Boolean(true))));
2185 assert!(matches!(array.get(2), Some(PdfObject::Integer(42))));
2186 assert!(matches!(array.get(3), Some(PdfObject::Real(_))));
2187 assert!(matches!(array.get(4), Some(PdfObject::String(_))));
2188 assert!(matches!(array.get(5), Some(PdfObject::Name(_))));
2189 }
2190
2191 #[test]
2192 fn test_pdf_dictionary_key_operations() {
2193 let mut dict = PdfDictionary(HashMap::new());
2194
2195 dict.0.insert(
2197 PdfName::new("Type".to_string()),
2198 PdfObject::Name(PdfName::new("Test".to_string())),
2199 );
2200 dict.0
2201 .insert(PdfName::new("Count".to_string()), PdfObject::Integer(100));
2202 dict.0
2203 .insert(PdfName::new("Flag".to_string()), PdfObject::Boolean(true));
2204
2205 assert_eq!(dict.0.len(), 3);
2206 assert!(dict.0.contains_key(&PdfName::new("Type".to_string())));
2207 assert!(dict.0.contains_key(&PdfName::new("Count".to_string())));
2208 assert!(dict.0.contains_key(&PdfName::new("Flag".to_string())));
2209 assert!(!dict.0.contains_key(&PdfName::new("Missing".to_string())));
2210
2211 assert!(dict.0.get(&PdfName::new("Type".to_string())).is_some());
2213 }
2214
2215 #[test]
2216 fn test_pdf_dictionary_complex_values() {
2217 let mut dict = PdfDictionary(HashMap::new());
2218
2219 let mut rect_array = PdfArray::new();
2221 rect_array.push(PdfObject::Real(0.0));
2222 rect_array.push(PdfObject::Real(0.0));
2223 rect_array.push(PdfObject::Real(612.0));
2224 rect_array.push(PdfObject::Real(792.0));
2225
2226 dict.0.insert(
2227 PdfName::new("MediaBox".to_string()),
2228 PdfObject::Array(rect_array),
2229 );
2230
2231 let mut resources = PdfDictionary(HashMap::new());
2233 let mut font_dict = PdfDictionary(HashMap::new());
2234 font_dict
2235 .0
2236 .insert(PdfName::new("F1".to_string()), PdfObject::Reference(10, 0));
2237 resources.0.insert(
2238 PdfName::new("Font".to_string()),
2239 PdfObject::Dictionary(font_dict),
2240 );
2241
2242 dict.0.insert(
2243 PdfName::new("Resources".to_string()),
2244 PdfObject::Dictionary(resources),
2245 );
2246
2247 assert_eq!(dict.0.len(), 2);
2248 assert!(dict.0.get(&PdfName::new("MediaBox".to_string())).is_some());
2249 assert!(dict.0.get(&PdfName::new("Resources".to_string())).is_some());
2250 }
2251
2252 #[test]
2253 fn test_object_reference_validation() {
2254 let ref1 = PdfObject::Reference(1, 0);
2255 let ref2 = PdfObject::Reference(1, 0);
2256 let ref3 = PdfObject::Reference(1, 1);
2257 let ref4 = PdfObject::Reference(2, 0);
2258
2259 assert_eq!(ref1, ref2);
2260 assert_ne!(ref1, ref3);
2261 assert_ne!(ref1, ref4);
2262
2263 let max_ref = PdfObject::Reference(u32::MAX, u16::MAX);
2265 assert!(matches!(max_ref, PdfObject::Reference(u32::MAX, u16::MAX)));
2266 }
2267
2268 #[test]
2269 fn test_pdf_object_type_checking() {
2270 let objects = vec![
2271 PdfObject::Null,
2272 PdfObject::Boolean(true),
2273 PdfObject::Integer(42),
2274 PdfObject::Real(3.14),
2275 PdfObject::String(PdfString::new(b"text".to_vec())),
2276 PdfObject::Name(PdfName::new("Name".to_string())),
2277 PdfObject::Array(PdfArray::new()),
2278 PdfObject::Dictionary(PdfDictionary(HashMap::new())),
2279 PdfObject::Reference(1, 0),
2280 ];
2281
2282 assert!(matches!(objects[0], PdfObject::Null));
2284 assert!(matches!(objects[1], PdfObject::Boolean(_)));
2285 assert!(matches!(objects[2], PdfObject::Integer(_)));
2286 assert!(matches!(objects[3], PdfObject::Real(_)));
2287 assert!(matches!(objects[4], PdfObject::String(_)));
2288 assert!(matches!(objects[5], PdfObject::Name(_)));
2289 assert!(matches!(objects[6], PdfObject::Array(_)));
2290 assert!(matches!(objects[7], PdfObject::Dictionary(_)));
2291 assert!(matches!(objects[8], PdfObject::Reference(_, _)));
2292 }
2293
2294 #[test]
2295 fn test_pdf_array_large_capacity() {
2296 let mut array = PdfArray::new();
2297
2298 for i in 0..1000 {
2300 array.push(PdfObject::Integer(i));
2301 }
2302
2303 assert_eq!(array.len(), 1000);
2304 if let Some(PdfObject::Integer(val)) = array.get(999) {
2306 assert_eq!(*val, 999);
2307 } else {
2308 panic!("Expected Integer at index 999");
2309 }
2310 assert!(array.get(1000).is_none());
2311
2312 let mut count = 0;
2314 for i in 0..array.len() {
2315 if let Some(obj) = array.get(i) {
2316 if matches!(obj, PdfObject::Integer(_)) {
2317 count += 1;
2318 }
2319 }
2320 }
2321 assert_eq!(count, 1000);
2322 }
2323
2324 #[test]
2325 fn test_pdf_dictionary_memory_efficiency() {
2326 let mut dict = PdfDictionary(HashMap::new());
2327
2328 for i in 0..100 {
2330 let key = PdfName::new(format!("Key{}", i));
2331 dict.0.insert(key, PdfObject::Integer(i));
2332 }
2333
2334 assert_eq!(dict.0.len(), 100);
2335 assert!(dict.0.contains_key(&PdfName::new("Key99".to_string())));
2336 assert!(!dict.0.contains_key(&PdfName::new("Key100".to_string())));
2337
2338 dict.0.remove(&PdfName::new("Key50".to_string()));
2340 assert_eq!(dict.0.len(), 99);
2341 assert!(!dict.0.contains_key(&PdfName::new("Key50".to_string())));
2342 }
2343
2344 #[test]
2345 fn test_parsing_simple_error_cases() {
2346 use std::io::Cursor;
2347
2348 let empty_input = b"";
2350 let mut cursor = Cursor::new(empty_input.to_vec());
2351 let mut lexer = Lexer::new(&mut cursor);
2352 let result = PdfObject::parse(&mut lexer);
2353
2354 assert!(result.is_err());
2356 }
2357
2358 #[test]
2359 fn test_unicode_string_handling() {
2360 let unicode_tests = vec![
2362 ("ASCII", "Hello World"),
2363 ("Latin-1", "Café résumé"),
2364 ("Emoji", "Hello 🌍 World 🚀"),
2365 ("CJK", "你好世界"),
2366 ("Mixed", "Hello 世界! Bonjour 🌍"),
2367 ];
2368
2369 for (name, text) in unicode_tests {
2370 let pdf_string = PdfString::new(text.as_bytes().to_vec());
2371 match pdf_string.as_str() {
2372 Ok(decoded) => assert_eq!(decoded, text, "Failed for {}", name),
2373 Err(_) => {
2374 assert!(!text.is_empty(), "Should handle {}", name);
2376 }
2377 }
2378 }
2379 }
2380
2381 #[test]
2382 fn test_deep_nesting_limits() {
2383 let mut root_array = PdfArray::new();
2385
2386 for i in 0..10 {
2388 let mut nested = PdfArray::new();
2389 nested.push(PdfObject::Integer(i as i64));
2390 root_array.push(PdfObject::Array(nested));
2391 }
2392
2393 assert_eq!(root_array.len(), 10);
2394
2395 for i in 0..10 {
2397 if let Some(PdfObject::Array(nested)) = root_array.get(i) {
2398 assert_eq!(nested.len(), 1);
2399 }
2400 }
2401 }
2402
2403 #[test]
2404 fn test_special_numeric_values() {
2405 let numbers = vec![
2407 (0i64, 0.0f64),
2408 (i32::MAX as i64, f32::MAX as f64),
2409 (i32::MIN as i64, f32::MIN as f64),
2410 (-1i64, -1.0f64),
2411 (2147483647i64, 2147483647.0f64),
2412 ];
2413
2414 for (int_val, float_val) in numbers {
2415 let int_obj = PdfObject::Integer(int_val);
2416 let float_obj = PdfObject::Real(float_val);
2417
2418 assert!(matches!(int_obj, PdfObject::Integer(_)));
2419 assert!(matches!(float_obj, PdfObject::Real(_)));
2420 }
2421
2422 let special_floats = vec![
2424 (0.0f64, "zero"),
2425 (f64::INFINITY, "infinity"),
2426 (f64::NEG_INFINITY, "negative infinity"),
2427 ];
2428
2429 for (val, _name) in special_floats {
2430 let obj = PdfObject::Real(val);
2431 assert!(matches!(obj, PdfObject::Real(_)));
2432 }
2433 }
2434
2435 #[test]
2436 fn test_array_bounds_checking() {
2437 let mut array = PdfArray::new();
2438 array.push(PdfObject::Integer(1));
2439 array.push(PdfObject::Integer(2));
2440 array.push(PdfObject::Integer(3));
2441
2442 assert!(array.get(0).is_some());
2444 assert!(array.get(1).is_some());
2445 assert!(array.get(2).is_some());
2446
2447 assert!(array.get(3).is_none());
2449 assert!(array.get(100).is_none());
2450
2451 let empty_array = PdfArray::new();
2453 assert!(empty_array.get(0).is_none());
2454 assert_eq!(empty_array.len(), 0);
2455 }
2456
2457 #[test]
2458 fn test_dictionary_case_sensitivity() {
2459 let mut dict = PdfDictionary(HashMap::new());
2460
2461 dict.0.insert(
2463 PdfName::new("Type".to_string()),
2464 PdfObject::Name(PdfName::new("Page".to_string())),
2465 );
2466 dict.0.insert(
2467 PdfName::new("type".to_string()),
2468 PdfObject::Name(PdfName::new("Font".to_string())),
2469 );
2470 dict.0.insert(
2471 PdfName::new("TYPE".to_string()),
2472 PdfObject::Name(PdfName::new("Image".to_string())),
2473 );
2474
2475 assert_eq!(dict.0.len(), 3);
2476 assert!(dict.0.contains_key(&PdfName::new("Type".to_string())));
2477 assert!(dict.0.contains_key(&PdfName::new("type".to_string())));
2478 assert!(dict.0.contains_key(&PdfName::new("TYPE".to_string())));
2479
2480 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("Type".to_string())) {
2482 assert_eq!(name.as_str(), "Page");
2483 }
2484 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("type".to_string())) {
2485 assert_eq!(name.as_str(), "Font");
2486 }
2487 if let Some(PdfObject::Name(name)) = dict.0.get(&PdfName::new("TYPE".to_string())) {
2488 assert_eq!(name.as_str(), "Image");
2489 }
2490 }
2491
2492 #[test]
2493 fn test_object_cloning_and_equality() {
2494 let original_array = {
2495 let mut arr = PdfArray::new();
2496 arr.push(PdfObject::Integer(42));
2497 arr.push(PdfObject::String(PdfString::new(b"test".to_vec())));
2498 arr
2499 };
2500
2501 let cloned_array = original_array.clone();
2502 assert_eq!(original_array.len(), cloned_array.len());
2503
2504 for i in 0..original_array.len() {
2506 let orig = original_array.get(i).unwrap();
2507 let cloned = cloned_array.get(i).unwrap();
2508 match (orig, cloned) {
2509 (PdfObject::Integer(a), PdfObject::Integer(b)) => assert_eq!(a, b),
2510 (PdfObject::String(a), PdfObject::String(b)) => {
2511 assert_eq!(a.as_bytes(), b.as_bytes())
2512 }
2513 _ => panic!("Type mismatch in cloned array"),
2514 }
2515 }
2516 }
2517
2518 #[test]
2519 fn test_concurrent_object_access() {
2520 use std::sync::Arc;
2521 use std::thread;
2522
2523 let dict = Arc::new({
2524 let mut d = PdfDictionary(HashMap::new());
2525 d.0.insert(
2526 PdfName::new("SharedKey".to_string()),
2527 PdfObject::Integer(42),
2528 );
2529 d
2530 });
2531
2532 let dict_clone = Arc::clone(&dict);
2533 let handle = thread::spawn(move || {
2534 if let Some(PdfObject::Integer(val)) =
2536 dict_clone.0.get(&PdfName::new("SharedKey".to_string()))
2537 {
2538 assert_eq!(*val, 42);
2539 }
2540 });
2541
2542 if let Some(PdfObject::Integer(val)) = dict.0.get(&PdfName::new("SharedKey".to_string())) {
2544 assert_eq!(*val, 42);
2545 }
2546
2547 handle.join().unwrap();
2548 }
2549
2550 #[test]
2551 fn test_stream_data_edge_cases() {
2552 let mut dict = PdfDictionary(HashMap::new());
2554 dict.0
2555 .insert(PdfName::new("Length".to_string()), PdfObject::Integer(0));
2556
2557 let stream = PdfStream {
2558 dict: dict.clone(),
2559 data: vec![],
2560 };
2561
2562 assert_eq!(stream.data.len(), 0);
2564 assert!(stream.raw_data().is_empty());
2565
2566 let stream_with_data = PdfStream {
2568 dict,
2569 data: b"Hello World".to_vec(),
2570 };
2571
2572 assert_eq!(stream_with_data.raw_data(), b"Hello World");
2573 }
2574
2575 #[test]
2576 fn test_name_object_hash_consistency() {
2577 use std::collections::HashSet;
2578
2579 let mut name_set = HashSet::new();
2580
2581 name_set.insert(PdfName::new("Type".to_string()));
2583 name_set.insert(PdfName::new("Pages".to_string()));
2584 name_set.insert(PdfName::new("Type".to_string())); assert_eq!(name_set.len(), 2); assert!(name_set.contains(&PdfName::new("Type".to_string())));
2588 assert!(name_set.contains(&PdfName::new("Pages".to_string())));
2589 assert!(!name_set.contains(&PdfName::new("Font".to_string())));
2590 }
2591}
2592
2593