1use crate::crypto::{DecryptionError, DecryptionTarget, Decryptor, get};
4use crate::data::Data;
5use crate::metadata::Metadata;
6use crate::object::Name;
7use crate::object::ObjectIdentifier;
8use crate::object::Stream;
9use crate::object::dict::keys::{
10 AUTHOR, CREATION_DATE, CREATOR, ENCRYPT, FIRST, ID, INDEX, INFO, KEYWORDS, MOD_DATE, N,
11 OCPROPERTIES, PAGES, PREV, PRODUCER, ROOT, SIZE, SUBJECT, TITLE, TYPE, VERSION, W, XREF_STM,
12};
13use crate::object::dict::probe_dict;
14use crate::object::indirect::IndirectObject;
15use crate::object::{Array, MaybeRef};
16use crate::object::{DateTime, Dict};
17use crate::object::{Object, ObjectLike};
18use crate::pdf::PdfVersion;
19use crate::reader::Reader;
20use crate::reader::{Readable, ReaderContext, ReaderExt};
21use crate::sync::{Arc, FxHashMap, RwLock, RwLockExt};
22use crate::trivia::is_white_space_character;
23use crate::util::findr_needle;
24use crate::{PdfData, object};
25use alloc::boxed::Box;
26use alloc::collections::BTreeSet;
27use alloc::vec;
28use alloc::vec::Vec;
29use core::cmp::max;
30use core::iter;
31use core::ops::Deref;
32
33pub(crate) const XREF_ENTRY_LEN: usize = 20;
34
35#[derive(Debug, Copy, Clone)]
36pub(crate) enum XRefError {
37 Unknown,
38 Encryption(DecryptionError),
39}
40
41pub(crate) fn root_xref(data: PdfData, password: &[u8]) -> Result<XRef, XRefError> {
43 let mut xref_map = FxHashMap::default();
44 let xref_pos = find_last_xref_pos(data.as_ref()).ok_or(XRefError::Unknown)?;
45 let trailer =
46 populate_xref_impl(data.as_ref(), xref_pos, &mut xref_map).ok_or(XRefError::Unknown)?;
47
48 XRef::new(
49 data.clone(),
50 xref_map,
51 XRefInput::TrailerDictData(trailer),
52 false,
53 password,
54 )
55}
56
57pub(crate) fn fallback(data: PdfData, password: &[u8]) -> Option<XRef> {
59 let (xref_map, xref_input) = fallback_xref_map(&data, password);
60
61 if let Some(xref_input) = xref_input {
62 XRef::new(data.clone(), xref_map, xref_input, true, password).ok()
63 } else {
64 None
65 }
66}
67
68fn fallback_xref_map<'a>(data: &'a PdfData, password: &[u8]) -> (XrefMap, Option<XRefInput<'a>>) {
69 fallback_xref_map_inner(data, ReaderContext::dummy(), true, password)
70}
71
72fn fallback_xref_map_inner<'a>(
73 data: &'a PdfData,
74 mut dummy_ctx: ReaderContext<'a>,
75 recurse: bool,
76 password: &[u8],
77) -> (XrefMap, Option<XRefInput<'a>>) {
78 let mut xref_map = FxHashMap::default();
79 let mut trailer_dicts = vec![];
80 let mut root_ref = None;
81
82 let mut r = Reader::new(data.as_ref());
83
84 let mut last_obj_num = None;
85
86 loop {
87 let cur_pos = r.offset();
88
89 let mut old_r = r.clone();
90
91 if r.peek_byte().is_some_and(|b: u8| b.is_ascii_digit()) {
93 if let Some(obj_id) = r.read::<ObjectIdentifier>(&dummy_ctx) {
94 let mut cloned = r.clone();
95 cloned.skip_white_spaces_and_comments();
97 if cloned.skip::<Object<'_>>(false).is_some() {
98 xref_map.insert(obj_id, EntryType::Normal(cur_pos));
99 last_obj_num = Some(obj_id);
100 dummy_ctx.set_obj_number(obj_id);
101 }
102 } else {
103 r.forward_while(|b| !is_white_space_character(b));
105 }
106 } else {
107 let mut probe_reader = r.clone();
110 if r.peek_bytes(2).is_some_and(|b| b == b"<<")
111 && let Some(probe) =
112 { probe_dict(&mut probe_reader, &dummy_ctx, Some(b"<<"), b">>") }
113 {
114 r = probe_reader;
115 if probe.has_root || probe.has_type {
116 let mut dict_reader = Reader::new(probe.data);
117 if let Some(dict) = dict_reader.read_with_context::<Dict<'_>>(&dummy_ctx) {
118 if probe.has_root && dict.contains_key(ROOT) {
119 trailer_dicts.push(dict.clone());
120 }
121
122 if dict
123 .get::<Name<'_>>(TYPE)
124 .is_some_and(|n| n.as_str() == "Catalog")
125 {
126 root_ref = last_obj_num;
127 }
128
129 if dict.get::<Name<'_>>(TYPE).as_deref() == Some(b"ObjStm")
130 && {
131 let mut after_dict = r.clone();
132 after_dict.skip_white_spaces_and_comments();
133 after_dict.forward_tag(b"stream").is_some()
134 }
135 && let Some(stream) = old_r.read::<Stream<'_>>(&dummy_ctx)
136 && let Some(last_obj_num) = last_obj_num
137 && let Some(obj_stream) = ObjectStream::new(&stream)
138 {
139 for (idx, (obj_num, offset)) in obj_stream.offsets.iter().enumerate() {
140 if offset.is_none() {
141 continue;
142 }
143
144 let id = ObjectIdentifier::new(*obj_num, 0);
145 if xref_map
150 .get(&id)
151 .is_none_or(|e| !matches!(e, &EntryType::Normal(_)))
152 {
153 xref_map.insert(
154 id,
155 EntryType::ObjStream(last_obj_num.obj_number, idx as u32),
156 );
157 }
158 }
159 }
160 }
161 }
162 } else {
163 let old_pos = r.offset;
167 r.forward_while(|b| !is_white_space_character(b));
168 if r.offset == old_pos {
169 r.read_byte();
170 }
171 }
172 }
173
174 if r.at_end() {
175 break;
176 }
177 }
178
179 let mut trailer_dict = None;
181
182 for dict in trailer_dicts {
183 if let Some(root_id) = dict.get_raw::<Dict<'_>>(ROOT) {
184 let check = |dict: &Dict<'_>| -> bool { dict.contains_key(PAGES) };
185
186 match root_id {
187 MaybeRef::Ref(r) => match xref_map.get(&r.into()) {
188 Some(EntryType::Normal(offset)) => {
189 let mut reader = Reader::new(&data.as_ref()[*offset..]);
190
191 if let Some(obj) =
192 reader.read_with_context::<IndirectObject<Dict<'_>>>(&dummy_ctx)
193 && {
194 let obj = obj.get();
195 check(&obj)
196 }
197 {
198 trailer_dict = Some(dict);
199 }
200 }
201 Some(EntryType::ObjStream(obj_num, idx)) => {
202 if let Some(EntryType::Normal(offset)) =
203 xref_map.get(&ObjectIdentifier::new(*obj_num, 0))
204 {
205 let mut reader = Reader::new(&data.as_ref()[*offset..]);
206
207 if let Some(stream) =
208 reader.read_with_context::<IndirectObject<Stream<'_>>>(&dummy_ctx)
209 && {
210 if let Some(object_stream) = ObjectStream::new(&stream.get())
211 && let Some(obj) =
212 object_stream.get::<Dict<'_>>(*idx, &dummy_ctx)
213 {
214 check(&obj)
215 } else {
216 false
217 }
218 }
219 {
220 trailer_dict = Some(dict);
221 }
222 }
223 }
224 _ => {}
225 },
226 MaybeRef::NotRef(d) => {
227 if check(&d) {
228 trailer_dict = Some(dict);
229 }
230 }
231 }
232 }
233 }
234
235 let has_encryption = trailer_dict
236 .as_ref()
237 .is_some_and(|t| t.contains_key(ENCRYPT));
238
239 if has_encryption && recurse {
240 if let Ok(xref) = XRef::new(
245 data.clone(),
246 xref_map.clone(),
247 XRefInput::TrailerDictData(trailer_dict.as_ref().map(|d| d.data()).unwrap()),
248 true,
249 password,
250 ) {
251 let ctx = ReaderContext::new(&xref, false);
252 let (patched_map, _) = fallback_xref_map_inner(data, ctx, false, password);
253 xref_map = patched_map;
254 }
255 }
256
257 if let Some(trailer_dict_data) = trailer_dict.map(|d| d.data()) {
258 (
259 xref_map,
260 Some(XRefInput::TrailerDictData(trailer_dict_data)),
261 )
262 } else if let Some(root_ref) = root_ref {
263 (xref_map, Some(XRefInput::RootRef(root_ref)))
264 } else {
265 (xref_map, None)
266 }
267}
268
269const DUMMY_XREF: XRef = XRef(Inner::Dummy);
270
271#[derive(Debug, Clone)]
273pub struct XRef(Inner);
274
275impl XRef {
276 fn new(
277 data: PdfData,
278 xref_map: XrefMap,
279 input: XRefInput<'_>,
280 repaired: bool,
281 password: &[u8],
282 ) -> Result<Self, XRefError> {
283 let trailer_data = TrailerData::dummy();
287
288 let mut xref = Self(Inner::Some(Arc::new(SomeRepr {
289 data: Arc::new(Data::new(data)),
290 map: Arc::new(RwLock::new(MapRepr { xref_map, repaired })),
291 decryptor: Arc::new(Decryptor::None),
292 has_ocgs: false,
293 metadata: Arc::new(Metadata::default()),
294 trailer_data,
295 password: password.to_vec(),
296 })));
297
298 let decryptor = {
303 match input {
304 XRefInput::TrailerDictData(trailer_dict_data) => {
305 let mut r = Reader::new(trailer_dict_data);
306
307 let trailer_dict = r
308 .read_with_context::<Dict<'_>>(&ReaderContext::new(&xref, false))
309 .ok_or(XRefError::Unknown)?;
310
311 get_decryptor(&trailer_dict, password)?
312 }
313 XRefInput::RootRef(_) => Decryptor::None,
314 }
315 };
316
317 match &mut xref.0 {
318 Inner::Dummy => unreachable!(),
319 Inner::Some(r) => {
320 let mutable = Arc::make_mut(r);
321 mutable.decryptor = Arc::new(decryptor.clone());
322 }
323 }
324
325 let (trailer_data, has_ocgs, metadata) = match input {
326 XRefInput::TrailerDictData(trailer_dict_data) => {
327 let mut r = Reader::new(trailer_dict_data);
328
329 let trailer_dict = r
330 .read_with_context::<Dict<'_>>(&ReaderContext::new(&xref, false))
331 .ok_or(XRefError::Unknown)?;
332
333 let root_ref = trailer_dict.get_ref(ROOT).ok_or(XRefError::Unknown)?;
334 let root = trailer_dict
335 .get::<Dict<'_>>(ROOT)
336 .ok_or(XRefError::Unknown)?;
337 let metadata = trailer_dict
338 .get::<Dict<'_>>(INFO)
339 .map(|d| parse_metadata(&d))
340 .unwrap_or_default();
341 let pages_ref = root.get_ref(PAGES).ok_or(XRefError::Unknown)?;
342 let has_ocgs = root.get::<Dict<'_>>(OCPROPERTIES).is_some();
343 let version = root
344 .get::<Name<'_>>(VERSION)
345 .and_then(|v| PdfVersion::from_bytes(v.deref()));
346
347 let td = TrailerData {
348 pages_ref: pages_ref.into(),
349 root_ref: root_ref.into(),
350 version,
351 };
352
353 (td, has_ocgs, metadata)
354 }
355 XRefInput::RootRef(root_ref) => {
356 let root = xref.get::<Dict<'_>>(root_ref).ok_or(XRefError::Unknown)?;
357 let pages_ref = root.get_ref(PAGES).ok_or(XRefError::Unknown)?;
358
359 let td = TrailerData {
360 pages_ref: pages_ref.into(),
361 root_ref,
362 version: None,
363 };
364
365 (td, false, Metadata::default())
366 }
367 };
368
369 match &mut xref.0 {
370 Inner::Dummy => unreachable!(),
371 Inner::Some(r) => {
372 let mutable = Arc::make_mut(r);
373 mutable.trailer_data = trailer_data;
374 mutable.decryptor = Arc::new(decryptor);
375 mutable.has_ocgs = has_ocgs;
376 mutable.metadata = Arc::new(metadata);
377 }
378 }
379
380 Ok(xref)
381 }
382
383 pub(crate) fn dummy() -> &'static Self {
384 &DUMMY_XREF
385 }
386
387 pub(crate) fn len(&self) -> usize {
388 match &self.0 {
389 Inner::Dummy => 0,
390 Inner::Some(r) => r.map.get().xref_map.len(),
391 }
392 }
393
394 pub(crate) fn trailer_data(&self) -> &TrailerData {
395 match &self.0 {
396 Inner::Dummy => unreachable!(),
397 Inner::Some(r) => &r.trailer_data,
398 }
399 }
400
401 pub(crate) fn metadata(&self) -> &Metadata {
402 match &self.0 {
403 Inner::Dummy => unreachable!(),
404 Inner::Some(r) => &r.metadata,
405 }
406 }
407
408 pub fn root_id(&self) -> ObjectIdentifier {
410 self.trailer_data().root_ref
411 }
412
413 pub fn has_optional_content_groups(&self) -> bool {
415 match &self.0 {
416 Inner::Dummy => false,
417 Inner::Some(r) => r.has_ocgs,
418 }
419 }
420
421 pub(crate) fn objects(&self) -> impl IntoIterator<Item = Object<'_>> + '_ {
422 match &self.0 {
423 Inner::Dummy => unimplemented!(),
424 Inner::Some(r) => {
425 let locked = r.map.get();
426 let mut elements = locked
427 .xref_map
428 .iter()
429 .map(|(id, e)| {
430 let offset = match e {
431 EntryType::Normal(o) => (*o, 0),
432 EntryType::ObjStream(id, index) => {
433 if let Some(EntryType::Normal(offset)) =
434 locked.xref_map.get(&ObjectIdentifier::new(*id, 0))
435 {
436 (*offset, *index)
437 } else {
438 (usize::MAX, 0)
439 }
440 }
441 };
442
443 (*id, offset)
444 })
445 .collect::<Vec<_>>();
446
447 elements.sort_by_key(|e1| e1.1);
450
451 let mut iter = elements.into_iter();
452
453 iter::from_fn(move || {
454 for next in iter.by_ref() {
455 if let Some(obj) = self.get_with(next.0, &ReaderContext::new(self, false)) {
456 return Some(obj);
457 } else {
458 continue;
460 }
461 }
462
463 None
464 })
465 }
466 }
467 }
468
469 pub(crate) fn repair(&self) {
470 let Inner::Some(r) = &self.0 else {
471 unreachable!();
472 };
473
474 let mut locked = r.map.put();
475 if locked.repaired {
476 return;
477 }
478
479 let (xref_map, _) = fallback_xref_map(r.data.get(), &r.password);
480 locked.xref_map = xref_map;
481 locked.repaired = true;
482 }
483
484 #[inline]
485 pub(crate) fn needs_decryption(&self, ctx: &ReaderContext<'_>) -> bool {
486 match &self.0 {
487 Inner::Dummy => false,
488 Inner::Some(r) => {
489 if matches!(r.decryptor.as_ref(), Decryptor::None) {
490 false
491 } else {
492 !ctx.in_content_stream() && !ctx.in_object_stream()
493 }
494 }
495 }
496 }
497
498 #[inline]
499 pub(crate) fn decrypt(
500 &self,
501 id: ObjectIdentifier,
502 data: &[u8],
503 target: DecryptionTarget<'_>,
504 ) -> Option<Vec<u8>> {
505 match &self.0 {
506 Inner::Dummy => Some(data.to_vec()),
507 Inner::Some(r) => r.decryptor.decrypt(id, data, target),
508 }
509 }
510
511 #[allow(private_bounds)]
513 pub fn get<'a, T>(&'a self, id: ObjectIdentifier) -> Option<T>
514 where
515 T: ObjectLike<'a>,
516 {
517 let ctx = ReaderContext::new(self, false);
518 self.get_with(id, &ctx)
519 }
520
521 #[allow(private_bounds)]
523 pub(crate) fn get_with<'a, T>(
524 &'a self,
525 id: ObjectIdentifier,
526 ctx: &ReaderContext<'a>,
527 ) -> Option<T>
528 where
529 T: ObjectLike<'a>,
530 {
531 let Inner::Some(repr) = &self.0 else {
532 return None;
533 };
534
535 let (entry, was_repaired) = {
537 let locked = repr.map.get();
538 (*locked.xref_map.get(&id)?, locked.repaired)
540 };
541
542 let mut r = Reader::new(repr.data.get().as_ref());
543 let caller_ctx = ctx;
544 let mut ctx = ctx.clone();
545 ctx.set_obj_number(id);
546 ctx.set_in_content_stream(false);
547
548 match entry {
549 EntryType::Normal(offset) => {
550 ctx.set_in_object_stream(false);
551 r.jump(offset);
552
553 if let Some(object) = r.read_with_context::<IndirectObject<T>>(&ctx) {
554 if object.id() == &id {
555 return Some(object.get());
556 }
557 } else {
558 if r.skip::<IndirectObject<Object<'_>>>(false).is_some() {
561 return None;
562 }
563 };
564
565 self.repair_and_get(id, caller_ctx, was_repaired)
566 }
567 EntryType::ObjStream(obj_stram_gen_num, index) => {
568 let obj_stream_id = ObjectIdentifier::new(obj_stram_gen_num, 0);
570
571 if obj_stream_id == id || ctx.parent_chain_contains(&id) {
572 return None;
573 }
574
575 if !matches!(
578 repr.map.get().xref_map.get(&obj_stream_id),
579 Some(EntryType::Normal(_))
580 ) {
581 return None;
582 }
583
584 ctx.parent_chain_push(id);
585
586 let object_stream = repr.data.object_stream(obj_stream_id, &ctx)?;
587
588 if object_stream.obj_number(index) == Some(id.obj_number) {
589 return object_stream.get(index, &ctx);
590 }
591
592 self.repair_and_get(id, caller_ctx, was_repaired)
593 }
594 }
595 }
596
597 fn repair_and_get<'a, T>(
598 &'a self,
599 id: ObjectIdentifier,
600 ctx: &ReaderContext<'a>,
601 was_repaired: bool,
602 ) -> Option<T>
603 where
604 T: ObjectLike<'a>,
605 {
606 if was_repaired {
608 None
609 } else {
610 self.repair();
611
612 self.get_with::<T>(id, ctx)
614 }
615 }
616}
617
618#[derive(Debug, Copy, Clone)]
621pub(crate) enum XRefInput<'a> {
622 TrailerDictData(&'a [u8]),
625 RootRef(ObjectIdentifier),
633}
634
635pub(crate) fn find_last_xref_pos(data: &[u8]) -> Option<usize> {
636 let needle = b"startxref";
637 let pos = findr_needle(data, needle)?;
638 let mut finder = Reader::new(data);
639 finder.jump(pos);
640 finder.forward_tag(needle)?;
641 finder.skip_white_spaces_and_comments();
642 finder.read_without_context::<i32>()?.try_into().ok()
643}
644
645#[derive(Debug, PartialEq, Eq, Clone, Copy)]
647enum EntryType {
648 Normal(usize),
650 ObjStream(u32, u32),
654}
655
656type XrefMap = FxHashMap<ObjectIdentifier, EntryType>;
657
658#[derive(Debug)]
660struct MapRepr {
661 xref_map: XrefMap,
662 repaired: bool,
663}
664
665#[derive(Debug, Copy, Clone)]
666pub(crate) struct TrailerData {
667 pub(crate) pages_ref: ObjectIdentifier,
668 pub(crate) root_ref: ObjectIdentifier,
669 pub(crate) version: Option<PdfVersion>,
670}
671
672impl TrailerData {
673 pub(crate) fn dummy() -> Self {
674 Self {
675 pages_ref: ObjectIdentifier::new(0, 0),
676 root_ref: ObjectIdentifier::new(0, 0),
677 version: None,
678 }
679 }
680}
681
682#[derive(Debug, Clone)]
683struct SomeRepr {
684 data: Arc<Data>,
685 map: Arc<RwLock<MapRepr>>,
686 metadata: Arc<Metadata>,
687 decryptor: Arc<Decryptor>,
688 has_ocgs: bool,
689 password: Vec<u8>,
690 trailer_data: TrailerData,
691}
692
693#[derive(Debug, Clone)]
694enum Inner {
695 Dummy,
697 Some(Arc<SomeRepr>),
699}
700
701#[derive(Debug)]
702struct XRefEntry {
703 offset: usize,
704 gen_number: i32,
705 used: bool,
706}
707
708impl XRefEntry {
709 pub(crate) fn read(data: &[u8]) -> Option<Self> {
710 #[inline(always)]
711 fn parse_u32(data: &[u8]) -> Option<u32> {
712 let mut accum = 0_u32;
713
714 for byte in data {
715 accum = accum.checked_mul(10)?;
716
717 match *byte {
718 b'0'..=b'9' => accum = accum.checked_add((*byte - b'0') as u32)?,
719 _ => return None,
720 }
721 }
722
723 Some(accum)
724 }
725
726 let offset = parse_u32(&data[0..10])? as usize;
727 let gen_number = i32::try_from(parse_u32(&data[11..16])?).ok()?;
728
729 let used = data[17] == b'n';
730
731 Some(Self {
732 offset,
733 gen_number,
734 used,
735 })
736 }
737}
738
739fn populate_xref_impl<'a>(data: &'a [u8], pos: usize, xref_map: &mut XrefMap) -> Option<&'a [u8]> {
740 let mut visited = BTreeSet::new();
741 populate_xref_impl_inner(data, pos, xref_map, &mut visited)
742}
743
744const MAX_XREF_CHAIN_DEPTH: usize = 256;
746
747fn populate_xref_impl_inner<'a>(
748 data: &'a [u8],
749 pos: usize,
750 xref_map: &mut XrefMap,
751 visited: &mut BTreeSet<usize>,
752) -> Option<&'a [u8]> {
753 if !visited.insert(pos) {
754 return None;
755 }
756
757 if visited.len() > MAX_XREF_CHAIN_DEPTH {
758 return None;
759 }
760
761 let mut reader = Reader::new(data);
762 reader.jump(pos);
763 reader.skip_white_spaces_and_comments();
765
766 let mut r2 = reader.clone();
767 if reader
768 .clone()
769 .read_without_context::<ObjectIdentifier>()
770 .is_some()
771 {
772 populate_from_xref_stream(data, &mut r2, xref_map, visited)
773 } else {
774 populate_from_xref_table(data, &mut r2, xref_map, visited)
775 }
776}
777
778pub(super) struct SubsectionHeader {
779 pub(super) start: u32,
780 pub(super) num_entries: u32,
781}
782
783impl Readable<'_> for SubsectionHeader {
784 fn read(r: &mut Reader<'_>, _: &ReaderContext<'_>) -> Option<Self> {
785 r.skip_white_spaces();
786 let start = r.read_without_context::<u32>()?;
787 r.skip_white_spaces();
788 let num_entries = r.read_without_context::<u32>()?;
789 r.skip_white_spaces();
790
791 Some(Self { start, num_entries })
792 }
793}
794
795fn populate_from_xref_table<'a>(
797 data: &'a [u8],
798 reader: &mut Reader<'a>,
799 insert_map: &mut XrefMap,
800 visited: &mut BTreeSet<usize>,
801) -> Option<&'a [u8]> {
802 let trailer = {
803 let mut reader = reader.clone();
804 read_xref_table_trailer(&mut reader, &ReaderContext::dummy())?
805 };
806
807 reader.skip_white_spaces();
808 reader.forward_tag(b"xref")?;
809 reader.skip_white_spaces();
810
811 let mut max_obj = 0;
812
813 if let Some(prev) = trailer.get::<i32>(PREV) {
814 populate_xref_impl_inner(data, prev as usize, insert_map, visited)?;
816 }
817
818 if let Some(xref_stm) = trailer.get::<i32>(XREF_STM) {
821 populate_xref_impl_inner(data, xref_stm as usize, insert_map, visited)?;
822 }
823
824 while let Some(header) = reader.read_without_context::<SubsectionHeader>() {
825 reader.skip_white_spaces();
826
827 let start = header.start;
828 let end = start.checked_add(header.num_entries)?;
829
830 for obj_number in start..end {
831 max_obj = max(max_obj, obj_number);
832 let bytes = reader.read_bytes(XREF_ENTRY_LEN)?;
833 let entry = XRefEntry::read(bytes)?;
834
835 if entry.used {
838 insert_map.insert(
839 ObjectIdentifier::new(obj_number, entry.gen_number),
840 EntryType::Normal(entry.offset),
841 );
842 }
843 }
844 }
845
846 Some(trailer.data())
847}
848
849fn populate_from_xref_stream<'a>(
850 data: &'a [u8],
851 reader: &mut Reader<'a>,
852 insert_map: &mut XrefMap,
853 visited: &mut BTreeSet<usize>,
854) -> Option<&'a [u8]> {
855 let stream = reader
856 .read_with_context::<IndirectObject<Stream<'_>>>(&ReaderContext::dummy())?
857 .get();
858
859 if let Some(prev) = stream.dict().get::<i32>(PREV) {
860 let _ = populate_xref_impl_inner(data, prev as usize, insert_map, visited)?;
862 }
863
864 let size = stream.dict().get::<u32>(SIZE)?;
865
866 let [f1_len, f2_len, f3_len] = stream.dict().get::<[u8; 3]>(W)?;
867
868 if f2_len == 0 || f2_len > size_of::<u64>() as u8 {
869 return None;
870 }
871
872 let xref_data = stream.decoded_data().ok()?;
873 let mut xref_reader = Reader::new(xref_data.as_ref());
874
875 if let Some(arr) = stream.dict().get::<Array<'_>>(INDEX) {
876 let iter = arr.iter::<(u32, u32)>();
877
878 for (start, num_elements) in iter {
879 xref_stream_subsection(
880 &mut xref_reader,
881 start,
882 num_elements,
883 f1_len,
884 f2_len,
885 f3_len,
886 insert_map,
887 )?;
888 }
889 } else {
890 xref_stream_subsection(
891 &mut xref_reader,
892 0,
893 size,
894 f1_len,
895 f2_len,
896 f3_len,
897 insert_map,
898 )?;
899 }
900
901 Some(stream.dict().data())
902}
903
904fn xref_stream_num(data: &[u8]) -> Option<u32> {
905 Some(match data.len() {
906 0 => return None,
907 1 => u8::from_be(data[0]) as u32,
908 2 => u16::from_be_bytes(data[0..2].try_into().ok()?) as u32,
909 3 => u32::from_be_bytes([0, data[0], data[1], data[2]]),
910 4 => u32::from_be_bytes(data[0..4].try_into().ok()?),
911 8 => {
912 if let Ok(num) = u32::try_from(u64::from_be_bytes(data[0..8].try_into().ok()?)) {
913 return Some(num);
914 } else {
915 return None;
916 }
917 }
918 _n => {
919 return None;
920 }
921 })
922}
923
924fn xref_stream_subsection<'a>(
925 xref_reader: &mut Reader<'a>,
926 start: u32,
927 num_elements: u32,
928 f1_len: u8,
929 f2_len: u8,
930 f3_len: u8,
931 insert_map: &mut XrefMap,
932) -> Option<()> {
933 for i in 0..num_elements {
934 let f_type = if f1_len == 0 {
935 1
936 } else {
937 xref_reader.read_bytes(1)?[0]
939 };
940
941 let obj_number = start.checked_add(i)?;
942
943 match f_type {
944 0 => {
946 xref_reader.skip_bytes(f2_len as usize + f3_len as usize)?;
947 }
948 1 => {
949 let offset = if f2_len > 0 {
950 let data = xref_reader.read_bytes(f2_len as usize)?;
951 xref_stream_num(data)?
952 } else {
953 0
954 };
955
956 let gen_number = if f3_len > 0 {
957 let data = xref_reader.read_bytes(f3_len as usize)?;
958 xref_stream_num(data)?
959 } else {
960 0
961 };
962
963 insert_map.insert(
964 ObjectIdentifier::new(obj_number, gen_number as i32),
965 EntryType::Normal(offset as usize),
966 );
967 }
968 2 => {
969 let obj_stream_number = {
970 let data = xref_reader.read_bytes(f2_len as usize)?;
971 xref_stream_num(data)?
972 };
973 let gen_number = 0;
974 let index = if f3_len > 0 {
975 let data = xref_reader.read_bytes(f3_len as usize)?;
976 xref_stream_num(data)?
977 } else {
978 0
979 };
980
981 insert_map.insert(
982 ObjectIdentifier::new(obj_number, gen_number),
983 EntryType::ObjStream(obj_stream_number, index),
984 );
985 }
986 _ => {
987 return None;
988 }
989 }
990 }
991
992 Some(())
993}
994
995fn read_xref_table_trailer<'a>(
996 reader: &mut Reader<'a>,
997 ctx: &ReaderContext<'a>,
998) -> Option<Dict<'a>> {
999 reader.skip_white_spaces();
1000 reader.forward_tag(b"xref")?;
1001 reader.skip_white_spaces();
1002
1003 while let Some(header) = reader.read_without_context::<SubsectionHeader>() {
1004 let len = XREF_ENTRY_LEN.checked_mul(header.num_entries as usize)?;
1005 reader.jump(reader.offset().checked_add(len)?);
1006 }
1007
1008 reader.skip_white_spaces();
1009 reader.forward_tag(b"trailer")?;
1010 reader.skip_white_spaces();
1011
1012 reader.read_with_context::<Dict<'_>>(ctx)
1013}
1014
1015fn get_decryptor(trailer_dict: &Dict<'_>, password: &[u8]) -> Result<Decryptor, XRefError> {
1016 if let Some(encryption_dict) = trailer_dict.get::<Dict<'_>>(ENCRYPT) {
1017 let id = if let Some(id) = trailer_dict
1018 .get::<Array<'_>>(ID)
1019 .and_then(|a| a.flex_iter().next::<object::String<'_>>())
1020 {
1021 id.to_vec()
1022 } else {
1023 vec![]
1025 };
1026
1027 get(&encryption_dict, &id, password).map_err(XRefError::Encryption)
1028 } else {
1029 Ok(Decryptor::None)
1030 }
1031}
1032
1033pub(crate) struct ObjectStream {
1034 data: Vec<u8>,
1035 offsets: Vec<(u32, Option<usize>)>,
1036}
1037
1038impl ObjectStream {
1039 pub(crate) fn new(stream: &Stream<'_>) -> Option<Self> {
1040 let data = stream.decoded_data().ok()?.into_owned();
1041 let offsets = Self::read_offsets(stream, &data).unwrap_or_default();
1042
1043 Some(Self { data, offsets })
1044 }
1045
1046 fn read_offsets(stream: &Stream<'_>, data: &[u8]) -> Option<Vec<(u32, Option<usize>)>> {
1047 let num_objects = stream.dict().get::<usize>(N)?;
1048 let first_offset = stream.dict().get::<usize>(FIRST)?;
1049
1050 let mut r = Reader::new(data);
1051
1052 let mut offsets = vec![];
1053 let mut last_offset = None;
1054
1055 for _ in 0..num_objects {
1056 r.skip_white_spaces_and_comments();
1057 let obj_num = r.read_without_context::<u32>()?;
1059 r.skip_white_spaces_and_comments();
1060 let relative_offset = r.read_without_context::<usize>()?;
1061 let offset = first_offset + relative_offset;
1062
1063 if offset >= data.len() || last_offset.is_some_and(|last| offset <= last) {
1067 offsets.push((obj_num, None));
1068 } else {
1069 let mut member = Reader::new(data);
1070 member.jump(offset);
1071 member.skip_white_spaces_and_comments();
1072 last_offset = Some(member.offset());
1073 offsets.push((obj_num, Some(member.offset())));
1074 }
1075 }
1076
1077 Some(offsets)
1078 }
1079
1080 fn obj_number(&self, index: u32) -> Option<u32> {
1081 Some(self.offsets.get(index as usize)?.0)
1082 }
1083
1084 fn get<'a, T>(&'a self, index: u32, ctx: &ReaderContext<'a>) -> Option<T>
1085 where
1086 T: ObjectLike<'a>,
1087 {
1088 let offset = self.offsets.get(index as usize)?.1?;
1089 let mut r = Reader::new(&self.data);
1090 r.jump(offset);
1091
1092 let mut ctx = ctx.clone();
1093 ctx.set_in_object_stream(true);
1094
1095 r.read_with_context::<T>(&ctx)
1096 }
1097}
1098
1099fn parse_metadata(info_dict: &Dict<'_>) -> Metadata {
1100 Metadata {
1101 creation_date: info_dict
1102 .get::<object::String<'_>>(CREATION_DATE)
1103 .and_then(|c| DateTime::from_bytes(&c)),
1104 modification_date: info_dict
1105 .get::<object::String<'_>>(MOD_DATE)
1106 .and_then(|c| DateTime::from_bytes(&c)),
1107 title: info_dict
1108 .get::<object::String<'_>>(TITLE)
1109 .map(|t| Box::from(t.as_bytes())),
1110 author: info_dict
1111 .get::<object::String<'_>>(AUTHOR)
1112 .map(|t| Box::from(t.as_bytes())),
1113 subject: info_dict
1114 .get::<object::String<'_>>(SUBJECT)
1115 .map(|t| Box::from(t.as_bytes())),
1116 keywords: info_dict
1117 .get::<object::String<'_>>(KEYWORDS)
1118 .map(|t| Box::from(t.as_bytes())),
1119 creator: info_dict
1120 .get::<object::String<'_>>(CREATOR)
1121 .map(|t| Box::from(t.as_bytes())),
1122 producer: info_dict
1123 .get::<object::String<'_>>(PRODUCER)
1124 .map(|t| Box::from(t.as_bytes())),
1125 }
1126}
1127
1128#[cfg(test)]
1129mod tests {
1130 use super::*;
1131
1132 #[test]
1133 fn circular_prev_chain() {
1134 let mut pdf = b"%PDF-1.0\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n".to_vec();
1135 let expected_xref_pos = pdf.len();
1136 pdf.extend_from_slice(
1137 format!(
1138 "xref\n\
1139 0 1\n\
1140 0000000000 65535 f \r\n\
1141 trailer\n<< /Size 1 /Root 1 0 R /Prev {expected_xref_pos} >>\n\
1142 startxref\n{expected_xref_pos}\n%%EOF"
1143 )
1144 .as_bytes(),
1145 );
1146
1147 let mut xref_map = FxHashMap::default();
1148 let xref_pos = find_last_xref_pos(pdf.as_ref()).unwrap();
1149 let _result = populate_xref_impl(pdf.as_ref(), xref_pos, &mut xref_map);
1150 }
1151
1152 #[test]
1153 fn find_last_xref_uses_last_startxref() {
1154 let pdf = b"%PDF-1.0\nstartxref\n5\n%%EOF\nstartxref\n42\n%%EOF";
1155 assert_eq!(find_last_xref_pos(pdf), Some(42));
1156 }
1157
1158 #[test]
1159 fn xref_table_trailer_rejects_overflowing_entry_skip() {
1160 let data = b"xref\n0 999999999999999999999\ntrailer\n<<>>";
1161 let mut reader = Reader::new(data);
1162 assert!(read_xref_table_trailer(&mut reader, &ReaderContext::dummy()).is_none());
1163 }
1164
1165 #[test]
1166 fn xref_stream_subsection_rejects_overflowing_object_number() {
1167 let data = [1, 0, 1, 0];
1168 let mut xref_map = FxHashMap::default();
1169 let result =
1170 xref_stream_subsection(&mut Reader::new(&data), u32::MAX, 2, 1, 1, 0, &mut xref_map);
1171 assert!(result.is_none());
1172 assert!(xref_map.contains_key(&ObjectIdentifier::new(u32::MAX, 0)));
1173 }
1174}