use crate::object::ObjectIdentifier; use crate::object::Stream; use crate::reader::ReaderContext; use crate::sync::HashMap; use crate::sync::{Arc, Mutex, MutexExt}; use crate::util::SegmentList; use alloc::borrow::Cow; use alloc::vec::Vec; use core::fmt::{Debug, Formatter}; /// A container for the bytes of a PDF file. #[derive(Clone)] pub struct PdfData { #[cfg(feature = "std")] inner: Arc + Send + Sync>, #[cfg(not(feature = "std"))] inner: Arc>, } impl Debug for PdfData { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "PdfData {{ ... }}") } } impl AsRef<[u8]> for PdfData { fn as_ref(&self) -> &[u8] { (*self.inner).as_ref() } } #[cfg(feature = "std")] impl + Send + Sync + 'static> From> for PdfData { fn from(data: Arc) -> Self { Self { inner: data } } } #[cfg(not(feature = "std"))] impl + 'static> From> for PdfData { fn from(data: Arc) -> Self { Self { inner: data } } } impl From> for PdfData { fn from(data: Vec) -> Self { Self { inner: Arc::new(data), } } } /// A structure for storing the data of the PDF. // To explain further: This crate uses a zero-parse approach, meaning that objects like // dictionaries or arrays always store the underlying data and parse objects lazily as needed, // instead of allocating the data and storing it in an owned way. However, the problem is that // not all data is readily available in the original data of the PDF: Objects can also be // stored in an object streams, in which case we first need to decode the stream before we can // access the data. // // The purpose of `Data` is to allow us to access the original data as well as maybe decoded data // by faking the same lifetime, so that we don't run into lifetime issues when dealing with // PDF objects that actually stem from different data sources. pub(crate) struct Data { data: PdfData, // 32 segments are more than enough as we can't have more objects than this. decoded: SegmentList>, 32>, map: Mutex>, } impl Debug for Data { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "Data {{ ... }}") } } impl Data { /// Create a new `Data` structure. pub(crate) fn new(data: PdfData) -> Self { Self { data, decoded: SegmentList::new(), map: Mutex::new(HashMap::new()), } } /// Get access to the original data of the PDF. pub(crate) fn get(&self) -> &PdfData { &self.data } /// Get access to the data of a decoded object stream. pub(crate) fn get_with(&self, id: ObjectIdentifier, ctx: &ReaderContext<'_>) -> Option<&[u8]> { if let Some(&idx) = self.map.get().get(&id) { self.decoded.get(idx)?.as_deref() } else { // Block scope to keep the lock short-lived. let idx = { let mut locked = self.map.get(); let idx = locked.len(); locked.insert(id, idx); idx }; self.decoded .get_or_init(idx, || { let stream = ctx.xref().get_with::>(id, ctx)?; stream.decoded().ok().map(Cow::into_owned) }) .as_deref() } } }