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        // finish(), not Ok(()). A DebugList that is never finished writes no
67        // closing bracket and, depending on the formatter, no entries either --
68        // so `{array:?}` produced something that was not the array. Ported from
69        // LaurenzV/hayro#1268.
70        debug_list.finish()
71    }
72}
73
74object!(Array<'a>, Array);
75
76impl Skippable for Array<'_> {
77    fn skip(r: &mut Reader<'_>, is_content_stream: bool) -> Option<()> {
78        r.forward_tag(b"[")?;
79
80        loop {
81            r.skip_white_spaces_and_comments();
82
83            if let Some(()) = r.forward_tag(b"]") {
84                return Some(());
85            } else if is_content_stream {
86                r.skip_not_in_content_stream::<Object<'_>>()?;
87            } else {
88                r.skip_not_in_content_stream::<MaybeRef<Object<'_>>>()?;
89            }
90        }
91    }
92}
93
94impl Default for Array<'_> {
95    fn default() -> Self {
96        Self::from_bytes(b"[]").expect("[] is a valid empty array literal")
97    }
98}
99
100impl<'a> Readable<'a> for Array<'a> {
101    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
102        let bytes = r.skip::<Array<'_>>(ctx.in_content_stream())?;
103
104        Some(Self {
105            data: &bytes[1..bytes.len() - 1],
106            ctx: ctx.clone(),
107        })
108    }
109}
110
111/// An iterator over the items of an array.
112pub struct ArrayIter<'a> {
113    reader: Reader<'a>,
114    ctx: ReaderContext<'a>,
115}
116
117impl<'a> ArrayIter<'a> {
118    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
119        Self {
120            reader: Reader::new(data),
121            ctx: ctx.clone(),
122        }
123    }
124}
125
126impl<'a> Iterator for ArrayIter<'a> {
127    type Item = MaybeRef<Object<'a>>;
128
129    fn next(&mut self) -> Option<Self::Item> {
130        self.reader.skip_white_spaces_and_comments();
131
132        if !self.reader.at_end() {
133            let item = self
134                .reader
135                .read_with_context::<MaybeRef<Object<'_>>>(&self.ctx)?;
136            return Some(item);
137        }
138
139        None
140    }
141}
142
143/// An iterator over the array that resolves objects of a specific type.
144pub struct ResolvedArrayIter<'a, T> {
145    flex_iter: FlexArrayIter<'a>,
146    phantom_data: PhantomData<T>,
147}
148
149impl<'a, T> ResolvedArrayIter<'a, T> {
150    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
151        Self {
152            flex_iter: FlexArrayIter::new(data, ctx),
153            phantom_data: PhantomData,
154        }
155    }
156}
157
158impl<'a, T> Iterator for ResolvedArrayIter<'a, T>
159where
160    T: ObjectLike<'a>,
161{
162    type Item = T;
163
164    fn next(&mut self) -> Option<Self::Item> {
165        self.flex_iter.next::<T>()
166    }
167}
168
169/// An iterator over the array that allows reading a different object each time.
170pub struct FlexArrayIter<'a> {
171    reader: Reader<'a>,
172    ctx: ReaderContext<'a>,
173}
174
175impl<'a> FlexArrayIter<'a> {
176    fn new(data: &'a [u8], ctx: &ReaderContext<'a>) -> Self {
177        Self {
178            reader: Reader::new(data),
179            ctx: ctx.clone(),
180        }
181    }
182
183    #[allow(
184        private_bounds,
185        reason = "users shouldn't be able to implement `ObjectLike` for custom objects."
186    )]
187    #[allow(clippy::should_implement_trait)]
188    /// Try reading the next item as a specific object from the array.
189    pub fn next<T: ObjectLike<'a>>(&mut self) -> Option<T> {
190        self.reader.skip_white_spaces_and_comments();
191
192        if !self.reader.at_end() {
193            return match self.reader.read_with_context::<MaybeRef<T>>(&self.ctx)? {
194                MaybeRef::Ref(r) => self.ctx.xref().get_with::<T>(r.into(), &self.ctx),
195                MaybeRef::NotRef(i) => Some(i),
196            };
197        }
198
199        None
200    }
201}
202
203impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> TryFrom<Array<'a>> for [T; C] {
204    type Error = ();
205
206    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
207        let mut iter = value.iter::<T>();
208
209        let mut val = [T::default(); C];
210
211        for i in 0..C {
212            val[i] = iter.next().ok_or(())?;
213        }
214
215        if iter.next().is_some() {
216            warn!("found excess elements in array");
217
218            return Err(());
219        }
220
221        Ok(val)
222    }
223}
224
225impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> TryFrom<Object<'a>> for [T; C]
226where
227    [T; C]: TryFrom<Array<'a>, Error = ()>,
228{
229    type Error = ();
230
231    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
232        match value {
233            Object::Array(a) => a.try_into(),
234            _ => Err(()),
235        }
236    }
237}
238
239impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> Readable<'a> for [T; C] {
240    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
241        let array = Array::read(r, ctx)?;
242        array.try_into().ok()
243    }
244}
245
246impl<'a, T: ObjectLike<'a> + Copy + Default, const C: usize> ObjectLike<'a> for [T; C] {}
247
248impl<'a, T: ObjectLike<'a>> TryFrom<Array<'a>> for Vec<T> {
249    type Error = ();
250
251    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
252        Ok(value.iter::<T>().collect())
253    }
254}
255
256impl<'a, T: ObjectLike<'a>> TryFrom<Object<'a>> for Vec<T> {
257    type Error = ();
258
259    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
260        match value {
261            Object::Array(a) => a.try_into(),
262            _ => Err(()),
263        }
264    }
265}
266
267impl<'a, T: ObjectLike<'a>> Readable<'a> for Vec<T> {
268    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
269        let array = Array::read(r, ctx)?;
270        array.try_into().ok()
271    }
272}
273
274impl<'a, T: ObjectLike<'a>> ObjectLike<'a> for Vec<T> {}
275
276impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> TryFrom<Array<'a>>
277    for SmallVec<T>
278{
279    type Error = ();
280
281    fn try_from(value: Array<'a>) -> Result<Self, Self::Error> {
282        Ok(value.iter::<U>().collect())
283    }
284}
285
286impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> TryFrom<Object<'a>>
287    for SmallVec<T>
288{
289    type Error = ();
290
291    fn try_from(value: Object<'a>) -> Result<Self, Self::Error> {
292        match value {
293            Object::Array(a) => a.try_into(),
294            _ => Err(()),
295        }
296    }
297}
298
299impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> Readable<'a>
300    for SmallVec<T>
301{
302    fn read(r: &mut Reader<'a>, ctx: &ReaderContext<'a>) -> Option<Self> {
303        let array = Array::read(r, ctx)?;
304        array.try_into().ok()
305    }
306}
307
308impl<'a, U: ObjectLike<'a>, T: ObjectLike<'a> + smallvec::Array<Item = U>> ObjectLike<'a>
309    for SmallVec<T>
310where
311    U: Clone,
312    U: Debug,
313{
314}
315
316#[cfg(test)]
317mod tests {
318    use crate::object::Array;
319    use crate::object::Object;
320    use crate::object::r#ref::{MaybeRef, ObjRef};
321    use crate::reader::Reader;
322    use crate::reader::{ReaderContext, ReaderExt};
323    use crate::xref::XRef;
324
325    fn array_impl(data: &[u8]) -> Option<Vec<Object<'_>>> {
326        Reader::new(data)
327            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
328            .map(|a| a.iter::<Object<'_>>().collect::<Vec<_>>())
329    }
330
331    fn array_ref_impl(data: &[u8]) -> Option<Vec<MaybeRef<Object<'_>>>> {
332        Reader::new(data)
333            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
334            .map(|a| a.raw_iter().collect::<Vec<_>>())
335    }
336
337    #[test]
338    fn empty_array_1() {
339        let res = array_impl(b"[]").unwrap();
340        assert!(res.is_empty());
341    }
342
343    #[test]
344    fn empty_array_2() {
345        let res = array_impl(b"[   \n]").unwrap();
346        assert!(res.is_empty());
347    }
348
349    #[test]
350    fn array_1() {
351        let res = array_impl(b"[34]").unwrap();
352        assert!(matches!(res[0], Object::Number(_)));
353    }
354
355    #[test]
356    fn array_2() {
357        let res = array_impl(b"[true  ]").unwrap();
358        assert!(matches!(res[0], Object::Boolean(_)));
359    }
360
361    #[test]
362    fn array_3() {
363        let res = array_impl(b"[true \n false 34.564]").unwrap();
364        assert!(matches!(res[0], Object::Boolean(_)));
365        assert!(matches!(res[1], Object::Boolean(_)));
366        assert!(matches!(res[2], Object::Number(_)));
367    }
368
369    #[test]
370    fn array_4() {
371        let res = array_impl(b"[(A string.) << /Hi 34.35 >>]").unwrap();
372        assert!(matches!(res[0], Object::String(_)));
373        assert!(matches!(res[1], Object::Dict(_)));
374    }
375
376    #[test]
377    fn array_5() {
378        let res = array_impl(b"[[32]  345.6]").unwrap();
379        assert!(matches!(res[0], Object::Array(_)));
380        assert!(matches!(res[1], Object::Number(_)));
381    }
382
383    #[test]
384    fn array_with_ref() {
385        let res = array_ref_impl(b"[345 34 5 R 34.0]").unwrap();
386        assert!(matches!(res[0], MaybeRef::NotRef(Object::Number(_))));
387        assert!(matches!(
388            res[1],
389            MaybeRef::Ref(ObjRef {
390                obj_number: 34,
391                gen_number: 5
392            })
393        ));
394        assert!(matches!(res[2], MaybeRef::NotRef(Object::Number(_))));
395    }
396
397    #[test]
398    fn array_with_comment() {
399        let res = array_impl(b"[true % A comment \n false]").unwrap();
400        assert!(matches!(res[0], Object::Boolean(_)));
401        assert!(matches!(res[1], Object::Boolean(_)));
402    }
403
404    #[test]
405    fn array_with_trailing() {
406        let res = array_impl(b"[(Hi) /Test]trialing data").unwrap();
407        assert!(matches!(res[0], Object::String(_)));
408        assert!(matches!(res[1], Object::Name(_)));
409    }
410
411    /// Ported from LaurenzV/hayro#1191, which applied issue 994's rule to the
412    /// third of three parsing paths.
413    ///
414    /// `Number::skip` and `read_inner` already refused a number pressed against
415    /// a regular character; the `int_num!` macro's own `skip` did not. So an
416    /// array being skipped as integers accepted `[8 0R]` -- a reference with the
417    /// space missing before `R` -- as two integers, and the malformed reference
418    /// became a silently different document rather than a parse failure.
419    #[test]
420    fn a_reference_missing_its_space_is_not_an_array_of_integers() {
421        assert!(array_ref_impl(b"[8 0R]").is_none());
422    }
423
424    /// The guard must not reject a number that legitimately ends the array.
425    #[test]
426    fn a_single_large_number_is_still_an_array() {
427        assert!(array_ref_impl(b"[345345345]").is_some());
428    }
429
430    /// And a properly spaced reference still reads as one.
431    #[test]
432    fn a_well_formed_reference_still_reads() {
433        assert!(array_ref_impl(b"[8 0 R]").is_some());
434    }
435
436    /// Ported from LaurenzV/hayro#1268: the Debug impls ended in `Ok(())`
437    /// rather than `finish()`, so the formatter never closed the list and the
438    /// output was not the array. Cosmetic until you are reading a log to work
439    /// out what a malformed file contained.
440    #[test]
441    fn the_debug_output_is_the_array() {
442        // Not array_impl: that returns a Vec<Object>, so formatting it exercises
443        // the standard library's Debug and not ours. The first version of this
444        // test did exactly that and passed against the unfixed code.
445        let array = Reader::new(b"[34]")
446            .read_with_context::<Array<'_>>(&ReaderContext::new(XRef::dummy(), false))
447            .unwrap();
448        let rendered = format!("{array:?}");
449        assert!(
450            rendered.starts_with('[') && rendered.ends_with(']'),
451            "expected a bracketed list, got {rendered:?}"
452        );
453        assert!(
454            rendered.contains("34"),
455            "the value is missing from {rendered:?}"
456        );
457    }
458}