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            return None;
150        }
151
152        // The array was validated by skipping its items, which accepts some items that
153        // can't be read.
154        self.reader
155            .read_with_context::<MaybeRef<Object<'_>>>(&self.ctx)
156    }
157}
158
159/// An iterator over the array that resolves objects of a specific type.
160pub struct ResolvedArrayIter<'a, T> {
161    flex_iter: FlexArrayIter<'a>,
162    phantom_data: PhantomData<T>,
163}
164
165impl<'a, T> ResolvedArrayIter<'a, T> {
166    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
167        Self {
168            flex_iter: FlexArrayIter::new(data, ctx),
169            phantom_data: PhantomData,
170        }
171    }
172}
173
174impl<'a, T> Iterator for ResolvedArrayIter<'a, T>
175where
176    T: ObjectLike<'a>,
177{
178    type Item = T;
179
180    fn next(&mut self) -> Option<Self::Item> {
181        self.flex_iter.next::<T>()
182    }
183}
184
185/// An iterator over the array that allows reading a different object each time.
186pub struct FlexArrayIter<'a> {
187    reader: Reader<'a>,
188    ctx: ReaderContext<'a>,
189}
190
191impl<'a> FlexArrayIter<'a> {
192    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
193        Self {
194            reader: Reader::new(data),
195            ctx: ctx.clone(),
196        }
197    }
198
199    #[allow(
200        private_bounds,
201        reason = "users shouldn't be able to implement `ObjectLike` for custom objects."
202    )]
203    #[allow(clippy::should_implement_trait)]
204    /// Try reading the next item as a specific object from the array.
205    pub fn next<T: ObjectLike<'a>>(&mut self) -> Option<T> {
206        self.reader.skip_white_spaces_and_comments();
207
208        if !self.reader.at_end() {
209            return match self.reader.read_with_context::<MaybeRef<T>>(&self.ctx)? {
210                MaybeRef::Ref(r) => self.ctx.xref().get_with::<T>(r.into(), &self.ctx),
211                MaybeRef::NotRef(i) => Some(i),
212            };
213        }
214
215        None
216    }
217}
218
219impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> TryFrom<Array<'a>> for [T; C] {
220    type Error = ();
221
222    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
223        let mut iter = value.iter::<T>();
224
225        let mut val = [T::default(); C];
226
227        for i in 0..C {
228            val[i] = iter.next().ok_or(())?;
229        }
230
231        if iter.next().is_some() {
232            return Err(());
233        }
234
235        Ok(val)
236    }
237}
238
239impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> TryFrom<Object<'a>> for [T; C]
240where
241    [T; C]: TryFrom<Array<'a>, Error = ()>,
242{
243    type Error = ();
244
245    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
246        match value {
247            Object::Array(a) => a.try_into(),
248            _ => Err(()),
249        }
250    }
251}
252
253impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> Readable<'a> for [T; C] {
254    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
255        let array = Array::read(r, ctx)?;
256        array.try_into().ok()
257    }
258}
259
260impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> ObjectLike<'a> for [T; C] {}
261
262impl<'a, T: ObjectLike<'a>> TryFrom<Array<'a>> for Vec<T> {
263    type Error = ();
264
265    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
266        Ok(value.iter::<T>().collect())
267    }
268}
269
270impl<'a, T: ObjectLike<'a>> TryFrom<Object<'a>> for Vec<T> {
271    type Error = ();
272
273    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
274        match value {
275            Object::Array(a) => a.try_into(),
276            _ => Err(()),
277        }
278    }
279}
280
281impl<'a, T: ObjectLike<'a>> Readable<'a> for Vec<T> {
282    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
283        let array = Array::read(r, ctx)?;
284        array.try_into().ok()
285    }
286}
287
288impl<'a, T: ObjectLike<'a>> ObjectLike<'a> for Vec<T> {}
289
290impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> TryFrom<Array<'a>>
291    for SmallVec<T>
292{
293    type Error = ();
294
295    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
296        Ok(value.iter::<U>().collect())
297    }
298}
299
300impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> TryFrom<Object<'a>>
301    for SmallVec<T>
302{
303    type Error = ();
304
305    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
306        match value {
307            Object::Array(a) => a.try_into(),
308            _ => Err(()),
309        }
310    }
311}
312
313impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> Readable<'a>
314    for SmallVec<T>
315{
316    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
317        let array = Array::read(r, ctx)?;
318        array.try_into().ok()
319    }
320}
321
322impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> ObjectLike<'a>
323    for SmallVec<T>
324where
325    U: Clone,
326    U: Debug,
327{
328}
329
330#[cfg(test)]
331mod tests {
332    use crate::object::Object;
333    use crate::object::r#ref::{MaybeRef, ObjRef};
334    use crate::object::{Array, FromBytes};
335    use crate::reader::Reader;
336    use crate::reader::{ReaderContext, ReaderExt};
337    use crate::xref::XRef;
338
339    fn array_impl(data: &[u8]) -> Option<Vec<Object<'_>>> {
340        Reader::new(data)
341            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
342            .map(|a| a.iter::<Object<'_>>().collect::<Vec<_>>())
343    }
344
345    fn array_ref_impl(data: &[u8]) -> Option<Vec<MaybeRef<Object<'_>>>> {
346        Reader::new(data)
347            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
348            .map(|a| a.raw_iter().collect::<Vec<_>>())
349    }
350
351    #[test]
352    fn empty_array_1() {
353        let res = array_impl(b"[]").unwrap();
354        assert!(res.is_empty());
355    }
356
357    #[test]
358    fn empty_array_2() {
359        let res = array_impl(b"[   \n]").unwrap();
360        assert!(res.is_empty());
361    }
362
363    #[test]
364    fn array_1() {
365        let res = array_impl(b"[34]").unwrap();
366        assert!(matches!(res[0], Object::Number(_)));
367    }
368
369    #[test]
370    fn array_2() {
371        let res = array_impl(b"[true  ]").unwrap();
372        assert!(matches!(res[0], Object::Boolean(_)));
373    }
374
375    #[test]
376    fn array_3() {
377        let res = array_impl(b"[true \n false 34.564]").unwrap();
378        assert!(matches!(res[0], Object::Boolean(_)));
379        assert!(matches!(res[1], Object::Boolean(_)));
380        assert!(matches!(res[2], Object::Number(_)));
381    }
382
383    #[test]
384    fn array_4() {
385        let res = array_impl(b"[(A string.) << /Hi 34.35 >>]").unwrap();
386        assert!(matches!(res[0], Object::String(_)));
387        assert!(matches!(res[1], Object::Dict(_)));
388    }
389
390    #[test]
391    fn array_5() {
392        let res = array_impl(b"[[32]  345.6]").unwrap();
393        assert!(matches!(res[0], Object::Array(_)));
394        assert!(matches!(res[1], Object::Number(_)));
395    }
396
397    #[test]
398    fn array_with_ref() {
399        let res = array_ref_impl(b"[345 34 5 R 34.0]").unwrap();
400        assert!(matches!(res[0], MaybeRef::NotRef(Object::Number(_))));
401        assert!(matches!(
402            res[1],
403            MaybeRef::Ref(ObjRef {
404                obj_number: 34,
405                gen_number: 5
406            })
407        ));
408        assert!(matches!(res[2], MaybeRef::NotRef(Object::Number(_))));
409    }
410
411    #[test]
412    fn malformed_ref_is_not_valid_array() {
413        assert!(array_ref_impl(b"[8 0R]").is_none());
414    }
415
416    #[test]
417    fn array_with_single_number_is_valid() {
418        assert!(array_ref_impl(b"[345345345]").is_some());
419    }
420
421    #[test]
422    fn array_with_comment() {
423        let res = array_impl(b"[true % A comment \n false]").unwrap();
424        assert!(matches!(res[0], Object::Boolean(_)));
425        assert!(matches!(res[1], Object::Boolean(_)));
426    }
427
428    #[test]
429    fn array_with_trailing() {
430        let res = array_impl(b"[(Hi) /Test]trialing data").unwrap();
431        assert!(matches!(res[0], Object::String(_)));
432        assert!(matches!(res[1], Object::Name(_)));
433    }
434
435    #[test]
436    fn array_repr() {
437        let array = Array::from_bytes(b"[34]").unwrap();
438
439        assert_eq!(format!("{array:?}"), "[Number(Number(Integer(34)))]");
440    }
441
442    #[test]
443    fn display() {
444        let array = Array::from_bytes(b"[  34 % comment\n /Test  [1   2] ]").unwrap();
445
446        assert_eq!(format!("{array}"), "[34 /Test [1 2]]");
447    }
448
449    #[test]
450    fn array_with_bare_number_sign_name() {
451        let res = array_impl(b"[34 /quotedbl /# 37 /percent]").unwrap();
452        assert_eq!(res.len(), 5);
453        assert!(matches!(&res[2], Object::Name(name) if name.as_ref() == b"#"));
454    }
455
456    #[test]
457    fn array_with_unreadable_item() {
458        let _ = array_ref_impl(b"[true <<>>stream false]");
459    }
460}