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