Skip to main content

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