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