Skip to main content

otf_pdf_syntax/
parser.rs

1//! Object parser on top of the [`Lexer`].
2
3use crate::diag::{DiagnosticKind, Diagnostics};
4use crate::error::{Error, Result};
5use crate::lexer::{Lexer, Token};
6use crate::limits::{LimitKind, Limits, Strictness};
7use crate::object::{Dict, ObjId, Object};
8
9/// Where an indirect object's body ends.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ObjectEnd {
12    /// `endobj` followed (or was missing and the object was accepted).
13    Endobj,
14    /// `stream` followed; stream data starts at this buffer position.
15    Stream {
16        /// Position of the first byte of stream data.
17        data_start: usize,
18    },
19}
20
21/// Parses objects from a buffer.
22pub struct Parser<'a, 'd> {
23    lexer: Lexer<'a>,
24    limits: &'d Limits,
25    strictness: Strictness,
26    diagnostics: Option<&'d Diagnostics>,
27    /// Added to buffer positions when reporting file offsets.
28    base: u64,
29}
30
31impl<'a, 'd> Parser<'a, 'd> {
32    /// A parser over `buf`, whose first byte is at file offset `base`.
33    pub fn new(buf: &'a [u8], base: u64, limits: &'d Limits, strictness: Strictness) -> Self {
34        Self {
35            lexer: Lexer::new(buf),
36            limits,
37            strictness,
38            diagnostics: None,
39            base,
40        }
41    }
42
43    /// Records repairs into `diagnostics`.
44    pub fn with_diagnostics(mut self, diagnostics: &'d Diagnostics) -> Self {
45        self.diagnostics = Some(diagnostics);
46        self
47    }
48
49    /// The underlying lexer.
50    pub fn lexer(&mut self) -> &mut Lexer<'a> {
51        &mut self.lexer
52    }
53
54    fn offset(&self) -> u64 {
55        self.base + self.lexer.pos() as u64
56    }
57
58    /// Accepts a repair in lenient mode (recording it) or fails in strict mode.
59    fn repair(&self, kind: DiagnosticKind, message: &'static str) -> Result<()> {
60        match self.strictness {
61            Strictness::Strict => Err(Error::syntax(self.offset(), message)),
62            Strictness::Lenient => {
63                if let Some(d) = self.diagnostics {
64                    d.push(kind, Some(self.offset()), None);
65                }
66                Ok(())
67            }
68        }
69    }
70
71    /// Parses one direct object.
72    pub fn parse_object(&mut self) -> Result<Object> {
73        let token = self.lexer.next_token()?;
74        self.object_from(token, 0)
75    }
76
77    fn object_from(&mut self, token: Token<'a>, depth: u32) -> Result<Object> {
78        if depth > self.limits.max_depth {
79            return Err(Error::Limit(LimitKind::Depth));
80        }
81        match token {
82            Token::Int(n) => self.int_or_ref(n),
83            Token::Real(r) => Ok(Object::Real(r)),
84            Token::String(s) => Ok(Object::String(s)),
85            Token::Name(n) => Ok(Object::Name(n)),
86            Token::ArrayStart => self.array(depth),
87            Token::DictStart => self.dict(depth).map(Object::Dict),
88            Token::Keyword(b"true") => Ok(Object::Bool(true)),
89            Token::Keyword(b"false") => Ok(Object::Bool(false)),
90            Token::Keyword(b"null") => Ok(Object::Null),
91            Token::Eof => Err(Error::UnexpectedEof),
92            _ => Err(Error::syntax(self.offset(), "expected an object")),
93        }
94    }
95
96    /// `n` alone, or `n g R`.
97    fn int_or_ref(&mut self, n: i64) -> Result<Object> {
98        let save = self.lexer.pos();
99        if let (Ok(num), Ok(Token::Int(g))) = (u32::try_from(n), self.lexer.next_token())
100            && let (Ok(generation), Ok(Token::Keyword(b"R"))) =
101                (u16::try_from(g), self.lexer.next_token())
102        {
103            return Ok(Object::Ref(ObjId::new(num, generation)));
104        }
105        self.lexer.set_pos(save);
106        Ok(Object::Int(n))
107    }
108
109    fn array(&mut self, depth: u32) -> Result<Object> {
110        let mut items = Vec::new();
111        loop {
112            let save = self.lexer.pos();
113            match self.lexer.next_token()? {
114                Token::ArrayEnd => return Ok(Object::Array(items)),
115                Token::Eof => return Err(Error::UnexpectedEof),
116                Token::Keyword(b"endobj" | b"stream" | b"endstream" | b"obj") => {
117                    // An unterminated array: end it here and leave the
118                    // keyword for the caller.
119                    self.lexer.set_pos(save);
120                    self.repair(DiagnosticKind::SyntaxError, "unterminated array")?;
121                    return Ok(Object::Array(items));
122                }
123                // Junk, but not the keywords that are objects.
124                Token::Keyword(k) if !matches!(k, b"true" | b"false" | b"null") => {
125                    self.repair(DiagnosticKind::SyntaxError, "unexpected token in array")?;
126                }
127                Token::DictEnd | Token::BraceOpen | Token::BraceClose => {
128                    self.repair(DiagnosticKind::SyntaxError, "unexpected token in array")?;
129                }
130                t => items.push(self.object_from(t, depth + 1)?),
131            }
132        }
133    }
134
135    fn dict(&mut self, depth: u32) -> Result<Dict> {
136        let mut dict = Dict::new();
137        loop {
138            let save = self.lexer.pos();
139            match self.lexer.next_token()? {
140                Token::DictEnd => return Ok(dict),
141                Token::Eof => return Err(Error::UnexpectedEof),
142                Token::Name(key) => {
143                    let save_value = self.lexer.pos();
144                    match self.lexer.next_token()? {
145                        Token::DictEnd => {
146                            self.repair(
147                                DiagnosticKind::SyntaxError,
148                                "dictionary key without value",
149                            )?;
150                            dict.insert(key, Object::Null);
151                            return Ok(dict);
152                        }
153                        Token::Keyword(b"endobj" | b"stream" | b"endstream" | b"obj") => {
154                            self.lexer.set_pos(save_value);
155                            self.repair(DiagnosticKind::SyntaxError, "unterminated dictionary")?;
156                            dict.insert(key, Object::Null);
157                            return Ok(dict);
158                        }
159                        Token::Eof => return Err(Error::UnexpectedEof),
160                        t => match self.object_from(t, depth + 1) {
161                            Ok(v) => {
162                                dict.insert(key, v);
163                            }
164                            Err(Error::Syntax { .. }) if self.strictness == Strictness::Lenient => {
165                                self.repair(DiagnosticKind::SyntaxError, "bad dictionary value")?;
166                                dict.insert(key, Object::Null);
167                            }
168                            Err(e) => return Err(e),
169                        },
170                    }
171                }
172                Token::Keyword(b"endobj" | b"stream" | b"endstream" | b"obj") => {
173                    self.lexer.set_pos(save);
174                    self.repair(DiagnosticKind::SyntaxError, "unterminated dictionary")?;
175                    return Ok(dict);
176                }
177                _ => {
178                    self.repair(DiagnosticKind::SyntaxError, "dictionary key is not a name")?;
179                }
180            }
181        }
182    }
183
184    /// Parses `num gen obj <object>` and what ends it. When `expected` is
185    /// given, a different id is an error.
186    pub fn parse_indirect(
187        &mut self,
188        expected: Option<ObjId>,
189    ) -> Result<(ObjId, Object, ObjectEnd)> {
190        let id = self.indirect_header()?;
191        if let Some(want) = expected {
192            if want.num != id.num {
193                return Err(Error::syntax(self.offset(), "object number mismatch"));
194            }
195            if want.generation != id.generation {
196                self.repair(DiagnosticKind::ObjectIdMismatch, "generation mismatch")?;
197            }
198        }
199        let object = self.parse_object_or_empty()?;
200        let save = self.lexer.pos();
201        let end = match self.lexer.next_token() {
202            Ok(Token::Keyword(b"endobj")) => ObjectEnd::Endobj,
203            Ok(Token::Keyword(b"stream")) => {
204                if !matches!(object, Object::Dict(_)) {
205                    return Err(Error::syntax(self.offset(), "stream without dictionary"));
206                }
207                ObjectEnd::Stream {
208                    data_start: self.stream_data_start(),
209                }
210            }
211            Err(Error::UnexpectedEof) | Ok(Token::Eof) => {
212                return Err(Error::UnexpectedEof);
213            }
214            _ => {
215                // Missing `endobj`: the next object (or junk) follows.
216                self.lexer.set_pos(save);
217                self.repair(DiagnosticKind::MissingEndobj, "missing endobj")?;
218                ObjectEnd::Endobj
219            }
220        };
221        Ok((id, object, end))
222    }
223
224    /// `num gen obj`.
225    fn indirect_header(&mut self) -> Result<ObjId> {
226        let (Token::Int(num), Token::Int(generation), Token::Keyword(b"obj")) = (
227            self.lexer.next_token()?,
228            self.lexer.next_token()?,
229            self.lexer.next_token()?,
230        ) else {
231            return Err(Error::syntax(self.offset(), "expected `num gen obj`"));
232        };
233        match (u32::try_from(num), u16::try_from(generation)) {
234            (Ok(num), Ok(generation)) => Ok(ObjId::new(num, generation)),
235            _ => Err(Error::syntax(self.offset(), "object id out of range")),
236        }
237    }
238
239    /// An object body; `n g obj endobj` is accepted as `null`.
240    fn parse_object_or_empty(&mut self) -> Result<Object> {
241        let save = self.lexer.pos();
242        let token = self.lexer.next_token()?;
243        if let Token::Keyword(b"endobj") = token {
244            self.lexer.set_pos(save);
245            self.repair(DiagnosticKind::SyntaxError, "empty object")?;
246            return Ok(Object::Null);
247        }
248        self.object_from(token, 0)
249    }
250
251    /// After the `stream` keyword: skips the end-of-line marker. The
252    /// standard requires CRLF or LF; a lone CR and trailing spaces are
253    /// tolerated.
254    fn stream_data_start(&mut self) -> usize {
255        let buf = self.lexer.buffer();
256        let mut pos = self.lexer.pos();
257        while buf.get(pos) == Some(&b' ') {
258            pos += 1;
259        }
260        match (buf.get(pos), buf.get(pos + 1)) {
261            (Some(b'\r'), Some(b'\n')) => pos += 2,
262            (Some(b'\r' | b'\n'), _) => pos += 1,
263            _ => {}
264        }
265        self.lexer.set_pos(pos);
266        pos
267    }
268
269    /// Parses a trailer dictionary, after the `trailer` keyword.
270    pub fn parse_dict(&mut self) -> Result<Dict> {
271        match self.lexer.next_token()? {
272            Token::DictStart => self.dict(0),
273            Token::Eof => Err(Error::UnexpectedEof),
274            _ => Err(Error::syntax(self.offset(), "expected a dictionary")),
275        }
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282    use crate::object::{Name, PdfString};
283
284    fn name(bytes: &[u8]) -> Object {
285        Object::Name(Name(bytes.to_vec()))
286    }
287
288    fn parse(input: &[u8]) -> Result<Object> {
289        let limits = Limits::default();
290        Parser::new(input, 0, &limits, Strictness::Lenient).parse_object()
291    }
292
293    fn strict(input: &[u8]) -> Result<Object> {
294        let limits = Limits::default();
295        Parser::new(input, 0, &limits, Strictness::Strict).parse_object()
296    }
297
298    #[test]
299    fn scalars() {
300        assert_eq!(parse(b"true"), Ok(Object::Bool(true)));
301        assert_eq!(parse(b"false"), Ok(Object::Bool(false)));
302        assert_eq!(parse(b"null"), Ok(Object::Null));
303        assert_eq!(parse(b"42"), Ok(Object::Int(42)));
304        assert_eq!(parse(b"-1.5"), Ok(Object::Real(-1.5)));
305        assert_eq!(parse(b"/N"), Ok(name(b"N")));
306        assert_eq!(
307            parse(b"(s)"),
308            Ok(Object::String(PdfString::literal(b"s".to_vec())))
309        );
310    }
311
312    #[test]
313    fn references() {
314        assert_eq!(parse(b"12 0 R"), Ok(Object::Ref(ObjId::new(12, 0))));
315        assert_eq!(
316            parse(b"[1 2 R 3 4 5 R]"),
317            Ok(Object::Array(vec![
318                Object::Ref(ObjId::new(1, 2)),
319                Object::Int(3),
320                Object::Ref(ObjId::new(4, 5)),
321            ]))
322        );
323        // Not a reference: negative number, generation too large.
324        assert_eq!(
325            parse(b"[-1 0 R]").unwrap().as_array().map(<[_]>::len),
326            Some(2)
327        );
328        assert_eq!(
329            parse(b"[1 70000 R]").unwrap().as_array().map(<[_]>::len),
330            Some(2)
331        );
332    }
333
334    #[test]
335    fn dictionaries() {
336        let obj = parse(b"<< /Type /Page /Kids [1 0 R] /Sub << /A 1 >> /N null >>").unwrap();
337        let d = obj.as_dict().unwrap();
338        assert!(d.is_type(b"Page"));
339        assert_eq!(d.get_dict(b"Sub").unwrap().get_int(b"A"), Some(1));
340        assert!(d.get(b"N").is_none());
341    }
342
343    #[test]
344    fn lenient_dictionary_repairs() {
345        let d = parse(b"<< /A 1 /B >>").unwrap();
346        assert_eq!(d.as_dict().unwrap().get_int(b"A"), Some(1));
347        let d = parse(b"<< /A 1 2 /B 3 >>").unwrap();
348        assert_eq!(d.as_dict().unwrap().get_int(b"B"), Some(3));
349        assert!(strict(b"<< /A 1 /B >>").is_err());
350        assert!(strict(b"<< 1 /B 3 >>").is_err());
351    }
352
353    #[test]
354    fn keyword_objects_in_arrays() {
355        assert_eq!(
356            strict(b"[true false null 1]"),
357            Ok(Object::Array(vec![
358                Object::Bool(true),
359                Object::Bool(false),
360                Object::Null,
361                Object::Int(1),
362            ]))
363        );
364    }
365
366    #[test]
367    fn lenient_array_repairs() {
368        assert_eq!(
369            parse(b"[1 foo 2]"),
370            Ok(Object::Array(vec![Object::Int(1), Object::Int(2)]))
371        );
372        assert!(strict(b"[1 foo 2]").is_err());
373    }
374
375    #[test]
376    fn unterminated_containers() {
377        assert_eq!(parse(b"[1 2"), Err(Error::UnexpectedEof));
378        assert_eq!(parse(b"<< /A 1"), Err(Error::UnexpectedEof));
379        assert_eq!(parse(b""), Err(Error::UnexpectedEof));
380    }
381
382    #[test]
383    fn depth_limit() {
384        let mut deep = vec![b'['; 300];
385        deep.extend(vec![b']'; 300]);
386        assert_eq!(parse(&deep), Err(Error::Limit(LimitKind::Depth)));
387        let mut ok = vec![b'['; 200];
388        ok.extend(vec![b']'; 200]);
389        assert!(parse(&ok).is_ok());
390    }
391
392    fn indirect(input: &[u8], mode: Strictness) -> Result<(ObjId, Object, ObjectEnd)> {
393        let limits = Limits::default();
394        Parser::new(input, 0, &limits, mode).parse_indirect(None)
395    }
396
397    #[test]
398    fn indirect_objects() {
399        let (id, obj, end) = indirect(b"3 0 obj\n<< /A 1 >>\nendobj", Strictness::Strict).unwrap();
400        assert_eq!(id, ObjId::new(3, 0));
401        assert!(obj.as_dict().is_some());
402        assert_eq!(end, ObjectEnd::Endobj);
403    }
404
405    #[test]
406    fn stream_start() {
407        for (input, start) in [
408            (&b"1 0 obj << /Length 3 >> stream\r\nabc"[..], 32),
409            (b"1 0 obj << /Length 3 >> stream\nabc", 31),
410            (b"1 0 obj << /Length 3 >> stream\rabc", 31),
411            (b"1 0 obj << /Length 3 >> stream  \nabc", 33),
412        ] {
413            let (_, _, end) = indirect(input, Strictness::Lenient).unwrap();
414            assert_eq!(end, ObjectEnd::Stream { data_start: start });
415            assert_eq!(&input[start..], b"abc");
416        }
417        assert!(indirect(b"1 0 obj 5 stream\nabc", Strictness::Lenient).is_err());
418    }
419
420    #[test]
421    fn missing_endobj() {
422        let diags = Diagnostics::default();
423        let limits = Limits::default();
424        let (_, obj, end) = Parser::new(b"1 0 obj 5 2 0 obj", 0, &limits, Strictness::Lenient)
425            .with_diagnostics(&diags)
426            .parse_indirect(None)
427            .unwrap();
428        assert_eq!(obj, Object::Int(5));
429        assert_eq!(end, ObjectEnd::Endobj);
430        assert_eq!(diags.snapshot()[0].kind, DiagnosticKind::MissingEndobj);
431        assert!(indirect(b"1 0 obj 5 2 0 obj", Strictness::Strict).is_err());
432    }
433
434    #[test]
435    fn empty_object_is_null() {
436        let (_, obj, _) = indirect(b"1 0 obj endobj", Strictness::Lenient).unwrap();
437        assert_eq!(obj, Object::Null);
438    }
439
440    #[test]
441    fn id_checks() {
442        let limits = Limits::default();
443        let mut p = Parser::new(b"4 0 obj 1 endobj", 0, &limits, Strictness::Lenient);
444        assert!(p.parse_indirect(Some(ObjId::new(5, 0))).is_err());
445        let mut p = Parser::new(b"4 1 obj 1 endobj", 0, &limits, Strictness::Lenient);
446        assert!(p.parse_indirect(Some(ObjId::new(4, 0))).is_ok());
447        let mut p = Parser::new(b"4 1 obj 1 endobj", 0, &limits, Strictness::Strict);
448        assert!(p.parse_indirect(Some(ObjId::new(4, 0))).is_err());
449        assert!(indirect(b"x 0 obj", Strictness::Lenient).is_err());
450        assert!(indirect(b"99999999999 0 obj 1 endobj", Strictness::Lenient).is_err());
451    }
452
453    #[test]
454    fn trailer_dict() {
455        let limits = Limits::default();
456        let mut p = Parser::new(b" << /Size 3 >>", 0, &limits, Strictness::Strict);
457        assert_eq!(p.parse_dict().unwrap().get_int(b"Size"), Some(3));
458        let mut p = Parser::new(b" 5", 0, &limits, Strictness::Strict);
459        assert!(p.parse_dict().is_err());
460    }
461}