Skip to main content

pith_pdf/
object.rs

1//! The object model: `Obj` trees plus the recursive-descent object parser.
2//!
3//! Objects are owned (`'static`): direct objects and object-stream members
4//! live in one arena, so a [`Document`](crate::Document) can resolve any
5//! indirect reference without lifetime juggling. Stream bytes stay raw
6//! (undecoded) here; filters are applied at the point of use so the caller
7//! decides which [`Limits`](pith_inflate::Limits) apply.
8
9use alloc::vec::Vec;
10
11use pith_digest::{Error, Result};
12
13use crate::lex::{Lexer, Tok, f64_as_i64, is_ws};
14
15/// An indirect reference: object number and generation.
16#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
17pub struct Ref {
18    /// Object number.
19    pub num: u32,
20    /// Generation number.
21    pub generation: u16,
22}
23
24/// A parsed PDF object. `Dict` keeps file order: dictionary keys repeat
25/// legally and `/Index`-style array payloads rely on position, not sorting.
26#[derive(Clone, Debug, PartialEq)]
27pub enum Obj {
28    /// `null`
29    Null,
30    /// `true` / `false`
31    Bool(bool),
32    /// A number. Integers and reals share one node; [`Obj::as_i64`]
33    /// refuses non-integral values.
34    Num(f64),
35    /// A `/Name` without the leading slash, `#xx` escapes decoded.
36    Name(Vec<u8>),
37    /// A decoded string (literal or hex; escapes resolved).
38    Str(Vec<u8>),
39    /// `[` ... `]`
40    Arr(Vec<Obj>),
41    /// `<<` ... `>>`; keys are decoded names.
42    Dict(Vec<(Vec<u8>, Obj)>),
43    /// A dictionary followed by `stream` data. `data` is the raw stream
44    /// content (filters NOT applied).
45    Stream {
46        /// The stream's dictionary.
47        dict: Vec<(Vec<u8>, Obj)>,
48        /// Raw stream bytes.
49        data: Vec<u8>,
50    },
51    /// `N G R`
52    Ref(Ref),
53}
54
55impl Obj {
56    /// Look up `key` in a `Dict` or `Stream` dictionary.
57    pub fn get(&self, key: &[u8]) -> Option<&Obj> {
58        match self {
59            Obj::Dict(kv) | Obj::Stream { dict: kv, .. } => {
60                kv.iter().find(|(k, _)| k == key).map(|(_, v)| v)
61            }
62            _ => None,
63        }
64    }
65
66    /// The dictionary part of a `Dict` or `Stream`.
67    pub fn dict(&self) -> Option<&[(Vec<u8>, Obj)]> {
68        match self {
69            Obj::Dict(kv) | Obj::Stream { dict: kv, .. } => Some(kv),
70            _ => None,
71        }
72    }
73
74    /// The reference if this is an indirect reference.
75    pub fn as_ref(&self) -> Option<Ref> {
76        match self {
77            Obj::Ref(r) => Some(*r),
78            _ => None,
79        }
80    }
81
82    /// `f64` for `Num`.
83    pub fn as_f64(&self) -> Option<f64> {
84        match self {
85            Obj::Num(v) => Some(*v),
86            _ => None,
87        }
88    }
89
90    /// `i64` for integer-valued `Num`.
91    pub fn as_i64(&self) -> Option<i64> {
92        self.as_f64().and_then(f64_as_i64)
93    }
94
95    /// `u32` for non-negative integer-valued `Num`.
96    pub fn as_u32(&self) -> Option<u32> {
97        self.as_i64().and_then(|v| u32::try_from(v).ok())
98    }
99
100    /// `usize` for non-negative integer-valued `Num`.
101    pub fn as_usize(&self) -> Option<usize> {
102        self.as_i64().and_then(|v| usize::try_from(v).ok())
103    }
104
105    /// `bool` for `Bool`.
106    pub fn as_bool(&self) -> Option<bool> {
107        match self {
108            Obj::Bool(b) => Some(*b),
109            _ => None,
110        }
111    }
112
113    /// `&[u8]` for `Name` and `Str`.
114    pub fn as_bytes(&self) -> Option<&[u8]> {
115        match self {
116            Obj::Name(b) | Obj::Str(b) => Some(b),
117            _ => None,
118        }
119    }
120
121    /// Array elements for `Arr`.
122    pub fn as_array(&self) -> Option<&[Obj]> {
123        match self {
124            Obj::Arr(a) => Some(a),
125            _ => None,
126        }
127    }
128
129    /// Raw stream bytes for `Stream`.
130    pub fn stream_data(&self) -> Option<&[u8]> {
131        match self {
132            Obj::Stream { data, .. } => Some(data),
133            _ => None,
134        }
135    }
136}
137
138/// A parsed indirect object plus a deferred stream-length fixup.
139///
140/// Streams whose `/Length` is an indirect reference cannot have their data
141/// sliced until the referenced object is known, so [`parse_obj`] records
142/// `(length_ref, data_start)` in `pending`; the
143/// [`Document`](crate::Document) re-slices after resolving it.
144pub(crate) struct Parsed {
145    /// The parsed object (a `Stream`'s `data` may run to `endstream`
146    /// rather than `/Length` until `pending` is resolved).
147    pub(crate) obj: Obj,
148    /// `(length_ref, data_start_offset)` for streams with indirect `/Length`.
149    pub(crate) pending: Option<(Ref, usize)>,
150}
151
152const MAX_DEPTH: usize = 64;
153const MAX_ELEMS: usize = 1 << 22; // 4M array/dict members
154
155/// Parse the indirect object whose `<num> <gen> obj` header starts at
156/// `offset` in `data`.
157pub(crate) fn parse_obj(data: &[u8], offset: usize) -> Result<Parsed> {
158    let mut lx = Lexer { data, pos: offset };
159    let num = lx.expect_int("object number")?;
160    let generation = lx.expect_int("generation number")?;
161    if num < 0 || num > i64::from(u32::MAX) || generation < 0 || generation > i64::from(u16::MAX) {
162        return Err(Error::BadValue("object number/generation"));
163    }
164    lx.expect_kw(b"obj", "'obj' keyword")?;
165    let (obj, pending) = parse_value(&mut lx, 0, true)?;
166    Ok(Parsed { obj, pending })
167}
168
169/// Parse one standalone value (object-stream members, trailer dictionaries).
170/// Streams may NOT appear at value level; `pending` is always `None` except
171/// when `top` allows a trailing stream.
172pub(crate) fn parse_standalone(data: &[u8], offset: usize) -> Result<Parsed> {
173    let mut lx = Lexer { data, pos: offset };
174    let (obj, pending) = parse_value(&mut lx, 0, true)?;
175    Ok(Parsed { obj, pending })
176}
177
178/// Parse one value. `top` permits a `stream` tail after a dict (only legal
179/// at object level).
180fn parse_value(lx: &mut Lexer, depth: usize, top: bool) -> Result<(Obj, Option<(Ref, usize)>)> {
181    let t = lx.next()?.ok_or(Error::Truncated {
182        what: "object",
183        needed: 1,
184        found: 0,
185    })?;
186    value_from_head(lx, t, depth, top)
187}
188
189fn value_from_head(
190    lx: &mut Lexer,
191    t: Tok,
192    depth: usize,
193    top: bool,
194) -> Result<(Obj, Option<(Ref, usize)>)> {
195    // the nesting cap lives here (not in parse_value) because container
196    // members recurse through value_from_head directly
197    if depth >= MAX_DEPTH {
198        return Err(Error::BadValue("object nesting depth"));
199    }
200    match t {
201        Tok::Num(v) => {
202            // indirect reference lookahead: `N G R`
203            let save = lx.pos;
204            if crate::lex::is_int(v) && v >= 0.0 {
205                let second = lx.next()?;
206                if let Some(Tok::Num(g)) = second {
207                    if crate::lex::is_int(g) && g >= 0.0 {
208                        let third = lx.next()?;
209                        if let Some(Tok::Kw(k)) = third {
210                            if &*k == b"R" {
211                                let n = f64_as_i64(v).unwrap_or(i64::MAX);
212                                let gg = f64_as_i64(g).unwrap_or(i64::MAX);
213                                if n > i64::from(u32::MAX) || gg > i64::from(u16::MAX) {
214                                    return Err(Error::BadValue("indirect reference bounds"));
215                                }
216                                return Ok((
217                                    Obj::Ref(Ref {
218                                        num: n as u32,
219                                        generation: gg as u16,
220                                    }),
221                                    None,
222                                ));
223                            }
224                            lx.pos = save;
225                            return Ok((Obj::Num(v), None));
226                        }
227                        lx.pos = save;
228                        return Ok((Obj::Num(v), None));
229                    }
230                    lx.pos = save;
231                    return Ok((Obj::Num(v), None));
232                }
233                lx.pos = save;
234            }
235            Ok((Obj::Num(v), None))
236        }
237        Tok::Name(b) => Ok((Obj::Name(b.into_owned()), None)),
238        Tok::Str(b) => Ok((Obj::Str(b.into_owned()), None)),
239        Tok::ArrOpen => {
240            let mut v = Vec::new();
241            loop {
242                match lx.next()? {
243                    Some(Tok::ArrClose) => break,
244                    Some(other) => {
245                        let (o, pend) = value_from_head(lx, other, depth + 1, false)?;
246                        debug_assert!(pend.is_none());
247                        v.push(o);
248                        if v.len() > MAX_ELEMS {
249                            return Err(Error::TooLarge {
250                                what: "array elements",
251                                limit: MAX_ELEMS,
252                            });
253                        }
254                    }
255                    None => {
256                        return Err(Error::Truncated {
257                            what: "array",
258                            needed: 1,
259                            found: 0,
260                        });
261                    }
262                }
263            }
264            Ok((Obj::Arr(v), None))
265        }
266        Tok::DictOpen => {
267            let mut kv: Vec<(Vec<u8>, Obj)> = Vec::new();
268            loop {
269                match lx.next()? {
270                    Some(Tok::DictClose) => break,
271                    Some(Tok::Name(k)) => {
272                        let nt = lx.next()?.ok_or(Error::Truncated {
273                            what: "dictionary value",
274                            needed: 1,
275                            found: 0,
276                        })?;
277                        let (o, pend) = value_from_head(lx, nt, depth + 1, false)?;
278                        debug_assert!(pend.is_none());
279                        kv.push((k.into_owned(), o));
280                        if kv.len() > MAX_ELEMS {
281                            return Err(Error::TooLarge {
282                                what: "dict entries",
283                                limit: MAX_ELEMS,
284                            });
285                        }
286                    }
287                    Some(_) => return Err(Error::BadValue("dictionary key is not a name")),
288                    None => {
289                        return Err(Error::Truncated {
290                            what: "dictionary",
291                            needed: 1,
292                            found: 0,
293                        });
294                    }
295                }
296            }
297            if top {
298                let save = lx.pos;
299                if lx.eat_kw(b"stream")? {
300                    return parse_stream(lx, kv);
301                }
302                lx.pos = save;
303            }
304            Ok((Obj::Dict(kv), None))
305        }
306        Tok::Kw(k) => match &*k {
307            b"true" => Ok((Obj::Bool(true), None)),
308            b"false" => Ok((Obj::Bool(false), None)),
309            b"null" => Ok((Obj::Null, None)),
310            _ => Err(Error::BadValue("unexpected keyword in object")),
311        },
312        Tok::ArrClose | Tok::DictClose => Err(Error::BadValue("stray closer")),
313    }
314}
315
316/// After `<<...>> stream <EOL>`: slice `/Length` bytes (or run to
317/// `endstream` when the length is an indirect reference).
318fn parse_stream(lx: &mut Lexer, dict: Vec<(Vec<u8>, Obj)>) -> Result<(Obj, Option<(Ref, usize)>)> {
319    match lx.rest().first() {
320        Some(&0x0D) => {
321            lx.pos += 1;
322            if lx.data.get(lx.pos) == Some(&0x0A) {
323                lx.pos += 1;
324            }
325        }
326        Some(&0x0A) => lx.pos += 1,
327        _ => return Err(Error::BadValue("'stream' keyword not followed by EOL")),
328    }
329    let start = lx.pos;
330    let len_obj = dict_get(&dict, b"Length").cloned();
331    match len_obj {
332        Some(Obj::Num(n)) => {
333            let n = f64_as_i64(n).ok_or(Error::BadValue("/Length value"))?;
334            if n < 0 {
335                return Err(Error::BadValue("negative /Length"));
336            }
337            let n = n as usize;
338            let end = start
339                .checked_add(n)
340                .ok_or(Error::BadValue("/Length overflow"))?;
341            if end > lx.data.len() {
342                return Err(Error::Truncated {
343                    what: "stream data",
344                    needed: n,
345                    found: lx.data.len().saturating_sub(start),
346                });
347            }
348            let data = lx.data[start..end].to_vec();
349            lx.pos = end;
350            if !consume_endstream(lx) && end != lx.data.len() {
351                // a wrong length is only tolerated when data runs out at EOF
352                return Err(Error::BadValue("stream without endstream"));
353            }
354            Ok((Obj::Stream { dict, data }, None))
355        }
356        Some(Obj::Ref(r)) => {
357            let marker = lx.data[start..]
358                .windows(9)
359                .position(|w| w == b"endstream")
360                .ok_or(Error::Truncated {
361                    what: "stream endstream marker",
362                    needed: 1,
363                    found: 0,
364                })?;
365            let end = start + marker;
366            let data = lx.data[start..end].to_vec();
367            lx.pos = end;
368            consume_endstream(lx);
369            Ok((Obj::Stream { dict, data }, Some((r, start))))
370        }
371        Some(_) => Err(Error::BadValue("/Length type")),
372        None => Err(Error::BadValue("stream without /Length")),
373    }
374}
375
376fn consume_endstream(lx: &mut Lexer) -> bool {
377    let save = lx.pos;
378    lx.skip_ws();
379    match lx.next() {
380        Ok(Some(Tok::Kw(k))) if &*k == b"endstream" => true,
381        _ => {
382            lx.pos = save;
383            false
384        }
385    }
386}
387
388pub(crate) fn dict_get<'o>(dict: &'o [(Vec<u8>, Obj)], key: &[u8]) -> Option<&'o Obj> {
389    dict.iter()
390        .find(|(k, _)| k.as_slice() == key)
391        .map(|(_, v)| v)
392}
393
394fn dict_i64(dict: &[(Vec<u8>, Obj)], key: &[u8]) -> Option<i64> {
395    dict_get(dict, key).and_then(Obj::as_i64)
396}
397
398/// Apply a stream object's `/Filter` chain to its raw bytes.
399///
400/// FlateDecode (zlib, with raw-deflate fallback for non-conforming files),
401/// ASCII85Decode and ASCIIHexDecode are implemented. Anything else - LZW,
402/// DCT, JBIG2, JPX, CCITT, RunLength, Crypt - is [`Error::Unsupported`]
403/// naming the filter. `DecodeParms` predictors (PNG optimums 10-15 and
404/// TIFF 2) are applied after FlateDecode.
405pub fn decode_stream(obj: &Obj, limits: &pith_inflate::Limits) -> Result<Vec<u8>> {
406    let (dict, data) = match obj {
407        Obj::Stream { dict, data } => (dict, data.as_slice()),
408        _ => return Err(Error::BadValue("not a stream")),
409    };
410    let filters: Vec<Vec<u8>> = match dict_get(dict, b"Filter") {
411        None | Some(Obj::Null) => Vec::new(),
412        Some(Obj::Name(n)) => alloc::vec![n.clone()],
413        Some(Obj::Arr(a)) => a
414            .iter()
415            .map(|o| match o {
416                Obj::Name(n) => Ok(n.clone()),
417                _ => Err(Error::BadValue("/Filter element type")),
418            })
419            .collect::<Result<Vec<_>>>()?,
420        Some(_) => return Err(Error::BadValue("/Filter type")),
421    };
422    let parms: Vec<&Obj> = match dict_get(dict, b"DecodeParms") {
423        None | Some(Obj::Null) => Vec::new(),
424        Some(p @ Obj::Dict(_)) => alloc::vec![p],
425        Some(Obj::Arr(a)) => a.iter().collect(),
426        Some(_) => return Err(Error::BadValue("/DecodeParms type")),
427    };
428    let mut cur: Vec<u8> = data.to_vec();
429    for (i, f) in filters.iter().enumerate() {
430        let dp = parms.get(i).copied().and_then(|o| o.dict());
431        cur = match f.as_slice() {
432            b"FlateDecode" | b"Fl" | b"Flate" => {
433                let d = pith_inflate::inflate_zlib(&cur, limits)
434                    .or_else(|_| pith_inflate::inflate_raw(&cur, limits))?;
435                apply_predictor(d, dp)?
436            }
437            b"ASCIIHexDecode" | b"AHx" => ascii_hex(&cur)?,
438            b"ASCII85Decode" | b"A85" => ascii85(&cur)?,
439            b"Crypt" => return Err(Error::Unsupported("Crypt stream filter")),
440            b"LZWDecode" | b"LZW" => return Err(Error::Unsupported("LZWDecode stream filter")),
441            b"DCTDecode" | b"DCT" => return Err(Error::Unsupported("DCTDecode stream filter")),
442            b"JBIG2Decode" => return Err(Error::Unsupported("JBIG2Decode stream filter")),
443            b"JPXDecode" => return Err(Error::Unsupported("JPXDecode stream filter")),
444            b"CCITTFaxDecode" | b"CCF" => {
445                return Err(Error::Unsupported("CCITTFaxDecode stream filter"));
446            }
447            b"RunLengthDecode" | b"RL" => {
448                return Err(Error::Unsupported("RunLengthDecode stream filter"));
449            }
450            _ => return Err(Error::Unsupported("unknown stream filter")),
451        };
452    }
453    Ok(cur)
454}
455
456/// PNG/TIFF predictor pass on decoded bytes (DecodeParms `/Predictor`).
457fn apply_predictor(data: Vec<u8>, parms: Option<&[(Vec<u8>, Obj)]>) -> Result<Vec<u8>> {
458    let p = parms.and_then(|d| dict_i64(d, b"Predictor")).unwrap_or(1);
459    if p == 1 {
460        return Ok(data);
461    }
462    let colors = parms.and_then(|d| dict_i64(d, b"Colors")).unwrap_or(1);
463    let bpc = parms
464        .and_then(|d| dict_i64(d, b"BitsPerComponent"))
465        .unwrap_or(8);
466    let cols = parms.and_then(|d| dict_i64(d, b"Columns")).unwrap_or(1);
467    if colors <= 0 || bpc <= 0 || cols <= 0 {
468        return Err(Error::BadValue("DecodeParms dimensions"));
469    }
470    if p == 2 {
471        if bpc != 8 {
472            return Err(Error::Unsupported("TIFF predictor bpc != 8"));
473        }
474        let bpp = colors as usize;
475        let rowlen = bpp
476            .checked_mul(cols as usize)
477            .ok_or(Error::BadValue("TIFF predictor row size"))?;
478        if rowlen == 0 {
479            return Err(Error::BadValue("TIFF predictor row size"));
480        }
481        let mut out = data;
482        for row in out.chunks_exact_mut(rowlen) {
483            for i in bpp..row.len() {
484                let prev = row[i - bpp];
485                row[i] = row[i].wrapping_add(prev);
486            }
487        }
488        return Ok(out);
489    }
490    if !(10..=15).contains(&p) {
491        return Err(Error::BadValue("Predictor value"));
492    }
493    let bits = colors
494        .checked_mul(cols)
495        .and_then(|c| c.checked_mul(bpc))
496        .ok_or(Error::BadValue("PNG predictor row size"))?;
497    let rowlen = (bits as usize).div_ceil(8);
498    let bpp = ((colors as usize) * (bpc as usize)).div_ceil(8);
499    if bpp == 0 || rowlen == 0 {
500        return Err(Error::BadValue("PNG predictor row size"));
501    }
502    let mut out = Vec::with_capacity(data.len());
503    let mut prev = alloc::vec![0u8; rowlen];
504    let mut rows = data.chunks_exact(rowlen + 1);
505    for row in rows.by_ref() {
506        let (ft, line) = (row[0], &row[1..]);
507        let mut cur = line.to_vec();
508        match ft {
509            0 => {}
510            1 => {
511                for i in bpp..cur.len() {
512                    cur[i] = cur[i].wrapping_add(cur[i - bpp]);
513                }
514            }
515            2 => {
516                for i in 0..cur.len() {
517                    cur[i] = cur[i].wrapping_add(prev[i]);
518                }
519            }
520            3 => {
521                for i in 0..cur.len() {
522                    let a = if i >= bpp { cur[i - bpp] } else { 0 };
523                    cur[i] = cur[i].wrapping_add(((u16::from(a) + u16::from(prev[i])) / 2) as u8);
524                }
525            }
526            4 => {
527                for i in 0..cur.len() {
528                    let a = if i >= bpp { cur[i - bpp] } else { 0 };
529                    let b = prev[i];
530                    let c = if i >= bpp { prev[i - bpp] } else { 0 };
531                    cur[i] = cur[i].wrapping_add(paeth(a, b, c));
532                }
533            }
534            _ => return Err(Error::BadValue("PNG predictor filter type")),
535        }
536        out.extend_from_slice(&cur);
537        prev = cur;
538    }
539    if !rows.remainder().is_empty() {
540        return Err(Error::Truncated {
541            what: "PNG predictor row",
542            needed: rowlen + 1,
543            found: rows.remainder().len(),
544        });
545    }
546    Ok(out)
547}
548
549fn iabs(v: i32) -> i32 {
550    if v < 0 { -v } else { v }
551}
552
553fn paeth(a: u8, b: u8, c: u8) -> u8 {
554    let (a, b, c) = (i32::from(a), i32::from(b), i32::from(c));
555    let p = a + b - c;
556    let (pa, pb, pc) = (iabs(p - a), iabs(p - b), iabs(p - c));
557    if pa <= pb && pa <= pc {
558        a as u8
559    } else if pb <= pc {
560        b as u8
561    } else {
562        c as u8
563    }
564}
565
566fn ascii_hex(data: &[u8]) -> Result<Vec<u8>> {
567    let mut out = Vec::with_capacity(data.len() / 2 + 1);
568    let mut hi: Option<u8> = None;
569    for &b in data {
570        if b == b'>' {
571            break;
572        }
573        if is_ws(b) {
574            continue;
575        }
576        match crate::lex::hex_val(b) {
577            Some(v) => match hi.take() {
578                Some(h) => out.push((h << 4) | v),
579                None => hi = Some(v),
580            },
581            None => return Err(Error::BadValue("ASCIIHex character")),
582        }
583    }
584    if let Some(h) = hi {
585        out.push(h << 4);
586    }
587    Ok(out)
588}
589
590fn ascii85(data: &[u8]) -> Result<Vec<u8>> {
591    let mut out = Vec::with_capacity(data.len() / 5 * 4 + 4);
592    let mut group: Vec<u8> = Vec::with_capacity(5);
593    let mut i = 0;
594    while i < data.len() {
595        let b = data[i];
596        i += 1;
597        if is_ws(b) {
598            continue;
599        }
600        if b == b'~' {
601            if data.get(i) == Some(&b'>') {
602                break;
603            }
604            return Err(Error::BadValue("ASCII85 terminator"));
605        }
606        if b == b'z' && group.is_empty() {
607            out.extend_from_slice(&[0, 0, 0, 0]);
608            continue;
609        }
610        if !(b'!'..=b'u').contains(&b) {
611            return Err(Error::BadValue("ASCII85 character"));
612        }
613        group.push(b - b'!');
614        if group.len() == 5 {
615            let v = group.iter().fold(0u32, |a, &g| a * 85 + u32::from(g));
616            out.extend_from_slice(&v.to_be_bytes());
617            group.clear();
618        }
619    }
620    if !group.is_empty() {
621        let n = group.len();
622        while group.len() < 5 {
623            group.push(84); // 'u'
624        }
625        let v = group.iter().fold(0u32, |a, &g| a * 85 + u32::from(g));
626        out.extend_from_slice(&v.to_be_bytes()[..n - 1]);
627    }
628    Ok(out)
629}