Skip to main content

pdf_syntax/object/
array.rs

1//! Arrays.
2
3use crate::object::macros::object;
4use crate::object::r#ref::MaybeRef;
5use crate::object::{FromBytes, Object, ObjectLike};
6use crate::reader::Reader;
7use crate::reader::{Readable, ReaderContext, ReaderExt, Skippable};
8use alloc::vec::Vec;
9use core::fmt::{Debug, Formatter};
10use core::marker::PhantomData;
11use log::warn;
12use smallvec::SmallVec;
13
14/// An array of PDF objects.
15#[derive(Clone)]
16pub struct Array<'a> {
17    data: &'a [u8],
18    ctx: ReaderContext<'a>,
19}
20
21// Note that this is not structural equality, i.e. two arrays with the same
22// items are still considered different if they have different whitespaces.
23impl PartialEq for Array<'_> {
24    fn eq(&self, other: &Self) -> bool {
25        self.data == other.data
26    }
27}
28
29impl<'a> Array<'a> {
30    /// Returns an iterator over the objects of the array.
31    pub fn raw_iter(&self) -> ArrayIter<'a> {
32        ArrayIter::new(self.data, &self.ctx)
33    }
34
35    /// Returns an iterator over the resolved objects of the array.
36    #[allow(
37        private_bounds,
38        reason = "users shouldn't be able to implement `ObjectLike` for custom objects."
39    )]
40    pub fn iter<T>(&self) -> ResolvedArrayIter<'a, T>
41    where
42        T: ObjectLike<'a>,
43    {
44        ResolvedArrayIter::new(self.data, &self.ctx)
45    }
46
47    /// Return a flex iterator over the items in the array.
48    pub fn flex_iter(&self) -> FlexArrayIter<'a> {
49        FlexArrayIter::new(self.data, &self.ctx)
50    }
51
52    /// Return the raw data of the array.
53    pub fn data(&self) -> &'a [u8] {
54        self.data
55    }
56}
57
58impl Debug for Array<'_> {
59    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
60        let mut debug_list = f.debug_list();
61
62        self.raw_iter().for_each(|i| {
63            debug_list.entry(&i);
64        });
65
66        Ok(())
67    }
68}
69
70object!(Array<'a>, Array);
71
72impl Skippable for Array<'_> {
73    fn skip(r: &mut Reader<'_>, is_content_stream: bool) -> Option<()> {
74        r.forward_tag(b"[")?;
75
76        loop {
77            r.skip_white_spaces_and_comments();
78
79            if let Some(()) = r.forward_tag(b"]") {
80                return Some(());
81            } else if is_content_stream {
82                r.skip_not_in_content_stream::<Object<'_>>()?;
83            } else {
84                r.skip_not_in_content_stream::<MaybeRef<Object<'_>>>()?;
85            }
86        }
87    }
88}
89
90impl Default for Array<'_> {
91    fn default() -> Self {
92        Self::from_bytes(b"[]").expect("[] is a valid empty array literal")
93    }
94}
95
96impl<'a> Readable<'a> for Array<'a> {
97    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
98        let bytes = r.skip::<Array<'_>>(ctx.in_content_stream())?;
99
100        Some(Self {
101            data: &bytes[1..bytes.len() - 1],
102            ctx: ctx.clone(),
103        })
104    }
105}
106
107/// An iterator over the items of an array.
108pub struct ArrayIter<'a> {
109    reader: Reader<'a>,
110    ctx: ReaderContext<'a>,
111}
112
113impl<'a> ArrayIter<'a> {
114    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
115        Self {
116            reader: Reader::new(data),
117            ctx: ctx.clone(),
118        }
119    }
120}
121
122impl<'a> Iterator for ArrayIter<'a> {
123    type Item = MaybeRef<Object<'a>>;
124
125    fn next(&mut self) -> Option<Self::Item> {
126        self.reader.skip_white_spaces_and_comments();
127
128        if !self.reader.at_end() {
129            let item = self
130                .reader
131                .read_with_context::<MaybeRef<Object<'_>>>(&self.ctx)?;
132            return Some(item);
133        }
134
135        None
136    }
137}
138
139/// An iterator over the array that resolves objects of a specific type.
140pub struct ResolvedArrayIter<'a, T> {
141    flex_iter: FlexArrayIter<'a>,
142    phantom_data: PhantomData<T>,
143}
144
145impl<'a, T> ResolvedArrayIter<'a, T> {
146    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
147        Self {
148            flex_iter: FlexArrayIter::new(data, ctx),
149            phantom_data: PhantomData,
150        }
151    }
152}
153
154impl<'a, T> Iterator for ResolvedArrayIter<'a, T>
155where
156    T: ObjectLike<'a>,
157{
158    type Item = T;
159
160    fn next(&mut self) -> Option<Self::Item> {
161        self.flex_iter.next::<T>()
162    }
163}
164
165/// An iterator over the array that allows reading a different object each time.
166pub struct FlexArrayIter<'a> {
167    reader: Reader<'a>,
168    ctx: ReaderContext<'a>,
169}
170
171impl<'a> FlexArrayIter<'a> {
172    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
173        Self {
174            reader: Reader::new(data),
175            ctx: ctx.clone(),
176        }
177    }
178
179    #[allow(
180        private_bounds,
181        reason = "users shouldn't be able to implement `ObjectLike` for custom objects."
182    )]
183    #[allow(clippy::should_implement_trait)]
184    /// Try reading the next item as a specific object from the array.
185    pub fn next<T: ObjectLike<'a>>(&mut self) -> Option<T> {
186        self.reader.skip_white_spaces_and_comments();
187
188        if !self.reader.at_end() {
189            return match self.reader.read_with_context::<MaybeRef<T>>(&self.ctx)? {
190                MaybeRef::Ref(r) => self.ctx.xref().get_with::<T>(r.into(), &self.ctx),
191                MaybeRef::NotRef(i) => Some(i),
192            };
193        }
194
195        None
196    }
197}
198
199impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> TryFrom<Array<'a>> for [T; C] {
200    type Error = ();
201
202    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
203        let mut iter = value.iter::<T>();
204
205        let mut val = [T::default(); C];
206
207        for i in 0..C {
208            val[i] = iter.next().ok_or(())?;
209        }
210
211        if iter.next().is_some() {
212            warn!("found excess elements in array");
213
214            return Err(());
215        }
216
217        Ok(val)
218    }
219}
220
221impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> TryFrom<Object<'a>> for [T; C]
222where
223    [T; C]: TryFrom<Array<'a>, Error = ()>,
224{
225    type Error = ();
226
227    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
228        match value {
229            Object::Array(a) => a.try_into(),
230            _ => Err(()),
231        }
232    }
233}
234
235impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> Readable<'a> for [T; C] {
236    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
237        let array = Array::read(r, ctx)?;
238        array.try_into().ok()
239    }
240}
241
242impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> ObjectLike<'a> for [T; C] {}
243
244impl<'a, T: ObjectLike<'a>> TryFrom<Array<'a>> for Vec<T> {
245    type Error = ();
246
247    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
248        Ok(value.iter::<T>().collect())
249    }
250}
251
252impl<'a, T: ObjectLike<'a>> TryFrom<Object<'a>> for Vec<T> {
253    type Error = ();
254
255    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
256        match value {
257            Object::Array(a) => a.try_into(),
258            _ => Err(()),
259        }
260    }
261}
262
263impl<'a, T: ObjectLike<'a>> Readable<'a> for Vec<T> {
264    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
265        let array = Array::read(r, ctx)?;
266        array.try_into().ok()
267    }
268}
269
270impl<'a, T: ObjectLike<'a>> ObjectLike<'a> for Vec<T> {}
271
272impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> TryFrom<Array<'a>>
273    for SmallVec<T>
274{
275    type Error = ();
276
277    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
278        Ok(value.iter::<U>().collect())
279    }
280}
281
282impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> TryFrom<Object<'a>>
283    for SmallVec<T>
284{
285    type Error = ();
286
287    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
288        match value {
289            Object::Array(a) => a.try_into(),
290            _ => Err(()),
291        }
292    }
293}
294
295impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> Readable<'a>
296    for SmallVec<T>
297{
298    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
299        let array = Array::read(r, ctx)?;
300        array.try_into().ok()
301    }
302}
303
304impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> ObjectLike<'a>
305    for SmallVec<T>
306where
307    U: Clone,
308    U: Debug,
309{
310}
311
312#[cfg(test)]
313mod tests {
314    use crate::object::Array;
315    use crate::object::Object;
316    use crate::object::r#ref::{MaybeRef, ObjRef};
317    use crate::reader::Reader;
318    use crate::reader::{ReaderContext, ReaderExt};
319    use crate::xref::XRef;
320
321    fn array_impl(data: &[u8]) -> Option<Vec<Object<'_>>> {
322        Reader::new(data)
323            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
324            .map(|a| a.iter::<Object<'_>>().collect::<Vec<_>>())
325    }
326
327    fn array_ref_impl(data: &[u8]) -> Option<Vec<MaybeRef<Object<'_>>>> {
328        Reader::new(data)
329            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
330            .map(|a| a.raw_iter().collect::<Vec<_>>())
331    }
332
333    #[test]
334    fn empty_array_1() {
335        let res = array_impl(b"[]").unwrap();
336        assert!(res.is_empty());
337    }
338
339    #[test]
340    fn empty_array_2() {
341        let res = array_impl(b"[   \n]").unwrap();
342        assert!(res.is_empty());
343    }
344
345    #[test]
346    fn array_1() {
347        let res = array_impl(b"[34]").unwrap();
348        assert!(matches!(res[0], Object::Number(_)));
349    }
350
351    #[test]
352    fn array_2() {
353        let res = array_impl(b"[true  ]").unwrap();
354        assert!(matches!(res[0], Object::Boolean(_)));
355    }
356
357    #[test]
358    fn array_3() {
359        let res = array_impl(b"[true \n false 34.564]").unwrap();
360        assert!(matches!(res[0], Object::Boolean(_)));
361        assert!(matches!(res[1], Object::Boolean(_)));
362        assert!(matches!(res[2], Object::Number(_)));
363    }
364
365    #[test]
366    fn array_4() {
367        let res = array_impl(b"[(A string.) << /Hi 34.35 >>]").unwrap();
368        assert!(matches!(res[0], Object::String(_)));
369        assert!(matches!(res[1], Object::Dict(_)));
370    }
371
372    #[test]
373    fn array_5() {
374        let res = array_impl(b"[[32]  345.6]").unwrap();
375        assert!(matches!(res[0], Object::Array(_)));
376        assert!(matches!(res[1], Object::Number(_)));
377    }
378
379    #[test]
380    fn array_with_ref() {
381        let res = array_ref_impl(b"[345 34 5 R 34.0]").unwrap();
382        assert!(matches!(res[0], MaybeRef::NotRef(Object::Number(_))));
383        assert!(matches!(
384            res[1],
385            MaybeRef::Ref(ObjRef {
386                obj_number: 34,
387                gen_number: 5
388            })
389        ));
390        assert!(matches!(res[2], MaybeRef::NotRef(Object::Number(_))));
391    }
392
393    #[test]
394    fn array_with_comment() {
395        let res = array_impl(b"[true % A comment \n false]").unwrap();
396        assert!(matches!(res[0], Object::Boolean(_)));
397        assert!(matches!(res[1], Object::Boolean(_)));
398    }
399
400    #[test]
401    fn array_with_trailing() {
402        let res = array_impl(b"[(Hi) /Test]trialing data").unwrap();
403        assert!(matches!(res[0], Object::String(_)));
404        assert!(matches!(res[1], Object::Name(_)));
405    }
406}