1use crate::error::PdfError;
8use crate::objects::{PdfDict, PdfObj};
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum Token {
13 Bool(bool),
14 Int(i64),
15 Real(f64),
16 Name(Vec<u8>),
18 LitString(Vec<u8>),
20 HexString(Vec<u8>),
22 ArrayBegin,
24 ArrayEnd,
26 DictBegin,
28 DictEnd,
30 Keyword(Vec<u8>),
32 Eof,
33}
34
35pub struct Lexer<'a> {
37 data: &'a [u8],
38 pos: usize,
39}
40
41impl<'a> Lexer<'a> {
42 pub fn new(data: &'a [u8]) -> Self {
43 Self { data, pos: 0 }
44 }
45
46 pub fn at(data: &'a [u8], pos: usize) -> Self {
48 Self { data, pos }
49 }
50
51 pub fn pos(&self) -> usize {
53 self.pos
54 }
55
56 pub fn set_pos(&mut self, pos: usize) {
58 self.pos = pos;
59 }
60
61 pub fn data(&self) -> &'a [u8] {
63 self.data
64 }
65
66 pub fn next_token(&mut self) -> Result<Token, PdfError> {
68 self.skip_whitespace_and_comments();
69
70 if self.pos >= self.data.len() {
71 return Ok(Token::Eof);
72 }
73
74 let b = self.data[self.pos];
75 match b {
76 b'/' => self.read_name(),
77 b'(' => self.read_literal_string(),
78 b'<' => {
79 if self.pos + 1 < self.data.len() && self.data[self.pos + 1] == b'<' {
80 self.pos += 2;
81 Ok(Token::DictBegin)
82 } else {
83 self.read_hex_string()
84 }
85 }
86 b'>' => {
87 if self.pos + 1 < self.data.len() && self.data[self.pos + 1] == b'>' {
88 self.pos += 2;
89 Ok(Token::DictEnd)
90 } else {
91 self.pos += 1;
92 Err(PdfError::UnexpectedToken {
93 expected: ">>".into(),
94 got: ">".into(),
95 })
96 }
97 }
98 b'[' => {
99 self.pos += 1;
100 Ok(Token::ArrayBegin)
101 }
102 b']' => {
103 self.pos += 1;
104 Ok(Token::ArrayEnd)
105 }
106 b'+' | b'-' | b'.' | b'0'..=b'9' => self.read_number(),
107 b'\'' | b'"' => {
108 self.pos += 1;
110 Ok(Token::Keyword(vec![b]))
111 }
112 _ if b.is_ascii_alphabetic() => self.read_keyword(),
113 _ => {
114 let ch = b as char;
115 self.pos += 1;
116 Err(PdfError::UnexpectedToken {
117 expected: "token".into(),
118 got: format!("byte 0x{b:02x} '{ch}'"),
119 })
120 }
121 }
122 }
123
124 pub fn peek_token(&mut self) -> Result<Token, PdfError> {
126 let saved = self.pos;
127 let tok = self.next_token();
128 self.pos = saved;
129 tok
130 }
131
132 fn skip_whitespace_and_comments(&mut self) {
134 loop {
135 while self.pos < self.data.len() && is_whitespace(self.data[self.pos]) {
137 self.pos += 1;
138 }
139 if self.pos < self.data.len() && self.data[self.pos] == b'%' {
141 while self.pos < self.data.len()
142 && self.data[self.pos] != b'\n'
143 && self.data[self.pos] != b'\r'
144 {
145 self.pos += 1;
146 }
147 } else {
148 break;
149 }
150 }
151 }
152
153 fn read_number(&mut self) -> Result<Token, PdfError> {
155 let start = self.pos;
156 let mut has_dot = false;
157
158 if self.pos < self.data.len()
160 && (self.data[self.pos] == b'+' || self.data[self.pos] == b'-')
161 {
162 self.pos += 1;
163 }
164
165 while self.pos < self.data.len() {
167 let b = self.data[self.pos];
168 if b == b'.' && !has_dot {
169 has_dot = true;
170 self.pos += 1;
171 } else if b.is_ascii_digit() {
172 self.pos += 1;
173 } else {
174 break;
175 }
176 }
177
178 let mut implicit_exp = false;
182 if has_dot
183 && self.pos < self.data.len()
184 && (self.data[self.pos] == b'+' || self.data[self.pos] == b'-')
185 {
186 let sign_pos = self.pos;
188 let mut peek = sign_pos + 1;
189 while peek < self.data.len() && self.data[peek].is_ascii_digit() {
190 peek += 1;
191 }
192 if peek > sign_pos + 1 {
193 self.pos = peek;
195 implicit_exp = true;
196 }
197 }
198
199 let s = &self.data[start..self.pos];
200 if s == b"+" || s == b"-" || s == b"." || s == b"+." || s == b"-." {
201 return Ok(Token::Int(0));
205 }
206
207 if has_dot {
208 let f: f64 = if implicit_exp {
209 let s_str =
211 std::str::from_utf8(s).map_err(|_| PdfError::Other("invalid number".into()))?;
212 let sign_idx = s_str.rfind(['+', '-']).unwrap();
213 let mut with_e = String::from(&s_str[..sign_idx]);
214 with_e.push('e');
215 with_e.push_str(&s_str[sign_idx..]);
216 with_e
217 .parse()
218 .map_err(|_| PdfError::Other(format!("invalid real: {s_str}")))?
219 } else {
220 let s_str =
221 std::str::from_utf8(s).map_err(|_| PdfError::Other("invalid number".into()))?;
222 s_str
223 .parse()
224 .map_err(|_| PdfError::Other(format!("invalid real: {s_str}")))?
225 };
226 Ok(Token::Real(f))
227 } else {
228 let s_str =
229 std::str::from_utf8(s).map_err(|_| PdfError::Other("invalid number".into()))?;
230 let n: i64 = s_str
231 .parse()
232 .map_err(|_| PdfError::Other(format!("invalid integer: {s_str}")))?;
233 Ok(Token::Int(n))
234 }
235 }
236
237 fn read_name(&mut self) -> Result<Token, PdfError> {
239 self.pos += 1; let mut name = Vec::new();
241
242 while self.pos < self.data.len() {
243 let b = self.data[self.pos];
244 if is_whitespace(b) || is_delimiter(b) {
245 break;
246 }
247 if b == b'#' && self.pos + 2 < self.data.len() {
248 let hi = hex_digit(self.data[self.pos + 1]);
250 let lo = hex_digit(self.data[self.pos + 2]);
251 if let (Some(h), Some(l)) = (hi, lo) {
252 name.push(h << 4 | l);
253 self.pos += 3;
254 continue;
255 }
256 }
257 name.push(b);
258 self.pos += 1;
259 }
260
261 Ok(Token::Name(name))
262 }
263
264 fn read_literal_string(&mut self) -> Result<Token, PdfError> {
266 self.pos += 1; let mut result = Vec::new();
268 let mut depth = 1u32;
269
270 while self.pos < self.data.len() {
271 let b = self.data[self.pos];
272 match b {
273 b'(' => {
274 depth += 1;
275 result.push(b);
276 self.pos += 1;
277 }
278 b')' => {
279 depth -= 1;
280 if depth == 0 {
281 self.pos += 1;
282 return Ok(Token::LitString(result));
283 }
284 result.push(b);
285 self.pos += 1;
286 }
287 b'\\' => {
288 self.pos += 1;
289 if self.pos >= self.data.len() {
290 break;
291 }
292 let esc = self.data[self.pos];
293 match esc {
294 b'n' => {
295 result.push(b'\n');
296 self.pos += 1;
297 }
298 b'r' => {
299 result.push(b'\r');
300 self.pos += 1;
301 }
302 b't' => {
303 result.push(b'\t');
304 self.pos += 1;
305 }
306 b'b' => {
307 result.push(0x08);
308 self.pos += 1;
309 }
310 b'f' => {
311 result.push(0x0C);
312 self.pos += 1;
313 }
314 b'(' | b')' | b'\\' => {
315 result.push(esc);
316 self.pos += 1;
317 }
318 b'\r' => {
319 self.pos += 1;
321 if self.pos < self.data.len() && self.data[self.pos] == b'\n' {
322 self.pos += 1;
323 }
324 }
325 b'\n' => {
326 self.pos += 1;
328 }
329 b'0'..=b'7' => {
330 let mut val: u32 = u32::from(esc - b'0');
342 self.pos += 1;
343 if self.pos < self.data.len()
344 && self.data[self.pos] >= b'0'
345 && self.data[self.pos] <= b'7'
346 {
347 val = val * 8 + u32::from(self.data[self.pos] - b'0');
348 self.pos += 1;
349 if self.pos < self.data.len()
350 && self.data[self.pos] >= b'0'
351 && self.data[self.pos] <= b'7'
352 {
353 val = val * 8 + u32::from(self.data[self.pos] - b'0');
354 self.pos += 1;
355 }
356 }
357 result.push(val as u8);
358 }
359 _ => {
360 result.push(esc);
362 self.pos += 1;
363 }
364 }
365 }
366 _ => {
367 result.push(b);
368 self.pos += 1;
369 }
370 }
371 }
372
373 Err(PdfError::Unterminated("string"))
374 }
375
376 fn read_hex_string(&mut self) -> Result<Token, PdfError> {
378 self.pos += 1; let mut result = Vec::new();
380 let mut high_nibble: Option<u8> = None;
381
382 while self.pos < self.data.len() {
383 let b = self.data[self.pos];
384 if b == b'>' {
385 self.pos += 1;
386 if let Some(h) = high_nibble {
388 result.push(h << 4);
389 }
390 return Ok(Token::HexString(result));
391 }
392 if is_whitespace(b) {
393 self.pos += 1;
394 continue;
395 }
396 if let Some(nibble) = hex_digit(b) {
397 match high_nibble {
398 None => high_nibble = Some(nibble),
399 Some(h) => {
400 result.push(h << 4 | nibble);
401 high_nibble = None;
402 }
403 }
404 self.pos += 1;
405 } else {
406 self.pos += 1;
407 return Err(PdfError::UnexpectedToken {
408 expected: "hex digit".into(),
409 got: format!("byte 0x{b:02x}"),
410 });
411 }
412 }
413
414 Err(PdfError::Unterminated("hex string"))
415 }
416
417 fn read_keyword(&mut self) -> Result<Token, PdfError> {
419 let start = self.pos;
420 while self.pos < self.data.len() && self.data[self.pos].is_ascii_alphabetic() {
421 self.pos += 1;
422 }
423 let word = &self.data[start..self.pos];
424 match word {
425 b"true" => Ok(Token::Bool(true)),
426 b"false" => Ok(Token::Bool(false)),
427 _ => Ok(Token::Keyword(word.to_vec())),
428 }
429 }
430}
431
432pub const MAX_OBJECT_DEPTH: u32 = 256;
442
443pub fn parse_object(lexer: &mut Lexer) -> Result<PdfObj, PdfError> {
450 parse_object_at_depth(lexer, 0)
451}
452
453pub fn parse_object_from_token(lexer: &mut Lexer, tok: Token) -> Result<PdfObj, PdfError> {
457 parse_object_from_token_at_depth(lexer, tok, 0)
458}
459
460pub fn parse_object_at_depth(lexer: &mut Lexer, depth: u32) -> Result<PdfObj, PdfError> {
462 let tok = lexer.next_token()?;
463 parse_object_from_token_at_depth(lexer, tok, depth)
464}
465
466pub fn parse_object_from_token_at_depth(
470 lexer: &mut Lexer,
471 tok: Token,
472 depth: u32,
473) -> Result<PdfObj, PdfError> {
474 if depth >= MAX_OBJECT_DEPTH && matches!(tok, Token::ArrayBegin | Token::DictBegin) {
479 return Err(PdfError::NestingTooDeep {
480 context: "array/dictionary",
481 limit: MAX_OBJECT_DEPTH,
482 });
483 }
484 match tok {
485 Token::Bool(b) => Ok(PdfObj::Bool(b)),
486 Token::Real(f) => Ok(PdfObj::Real(f)),
487 Token::Int(n) => {
488 let saved = lexer.pos();
490 match lexer.next_token() {
491 Ok(Token::Int(g)) => match lexer.next_token() {
492 Ok(Token::Keyword(ref kw)) if kw == b"R" => Ok(PdfObj::Ref(n as u32, g as u16)),
493 _ => {
494 lexer.set_pos(saved);
495 Ok(PdfObj::Int(n))
496 }
497 },
498 _ => {
499 lexer.set_pos(saved);
500 Ok(PdfObj::Int(n))
501 }
502 }
503 }
504 Token::Name(n) => Ok(PdfObj::Name(n)),
505 Token::LitString(s) => Ok(PdfObj::Str(s)),
506 Token::HexString(s) => Ok(PdfObj::Str(s)),
507 Token::Keyword(ref kw) if kw == b"null" => Ok(PdfObj::Null),
508 Token::ArrayBegin => {
509 let mut elems = Vec::new();
510 loop {
511 let t = lexer.next_token()?;
512 if t == Token::ArrayEnd || t == Token::Eof {
513 break;
514 }
515 if let Ok(obj) = parse_object_from_token_at_depth(lexer, t, depth + 1) {
518 elems.push(obj);
519 }
520 }
521 Ok(PdfObj::Array(elems))
522 }
523 Token::DictBegin => {
524 let dict = parse_dict_body_at_depth(lexer, depth + 1)?;
525 Ok(PdfObj::Dict(dict))
526 }
527 _ => Err(PdfError::UnexpectedToken {
528 expected: "object".into(),
529 got: format!("{tok:?}"),
530 }),
531 }
532}
533
534pub fn parse_dict_body(lexer: &mut Lexer) -> Result<PdfDict, PdfError> {
538 parse_dict_body_at_depth(lexer, 0)
539}
540
541pub fn parse_dict_body_at_depth(lexer: &mut Lexer, depth: u32) -> Result<PdfDict, PdfError> {
546 let mut dict = PdfDict::new();
547 loop {
548 let t = match lexer.next_token() {
553 Ok(t) => t,
554 Err(_) => continue,
555 };
556 match t {
557 Token::DictEnd | Token::Eof => break,
558 Token::Name(key) => {
559 match parse_object_at_depth(lexer, depth) {
565 Ok(val) => {
566 dict.insert(key, val);
567 }
568 Err(_) => {
569 dict.insert(key, PdfObj::Null);
570 }
571 }
572 }
573 _ => {
574 continue;
576 }
577 }
578 }
579 Ok(dict)
580}
581
582fn is_whitespace(b: u8) -> bool {
584 matches!(b, b' ' | b'\t' | b'\r' | b'\n' | 0x0C | 0x00)
585}
586
587fn is_delimiter(b: u8) -> bool {
589 matches!(
590 b,
591 b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
592 )
593}
594
595fn hex_digit(b: u8) -> Option<u8> {
597 match b {
598 b'0'..=b'9' => Some(b - b'0'),
599 b'a'..=b'f' => Some(b - b'a' + 10),
600 b'A'..=b'F' => Some(b - b'A' + 10),
601 _ => None,
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608
609 fn tokenize(input: &[u8]) -> Vec<Token> {
610 let mut lexer = Lexer::new(input);
611 let mut tokens = Vec::new();
612 loop {
613 let tok = lexer.next_token().unwrap();
614 if tok == Token::Eof {
615 break;
616 }
617 tokens.push(tok);
618 }
619 tokens
620 }
621
622 #[test]
623 fn integers() {
624 assert_eq!(tokenize(b"42"), vec![Token::Int(42)]);
625 assert_eq!(tokenize(b"-7"), vec![Token::Int(-7)]);
626 assert_eq!(tokenize(b"+5"), vec![Token::Int(5)]);
627 assert_eq!(tokenize(b"0"), vec![Token::Int(0)]);
628 }
629
630 #[test]
631 fn reals() {
632 assert_eq!(tokenize(b"2.5"), vec![Token::Real(2.5)]);
633 assert_eq!(tokenize(b".5"), vec![Token::Real(0.5)]);
634 assert_eq!(tokenize(b"-2.0"), vec![Token::Real(-2.0)]);
635 }
636
637 #[test]
638 fn names() {
639 assert_eq!(tokenize(b"/Type"), vec![Token::Name(b"Type".to_vec())]);
640 assert_eq!(tokenize(b"/"), vec![Token::Name(b"".to_vec())]); assert_eq!(tokenize(b"/A#20B"), vec![Token::Name(b"A B".to_vec())]); }
643
644 #[test]
645 fn strings() {
646 assert_eq!(
647 tokenize(b"(hello)"),
648 vec![Token::LitString(b"hello".to_vec())]
649 );
650 assert_eq!(
651 tokenize(b"(nested (parens))"),
652 vec![Token::LitString(b"nested (parens)".to_vec())]
653 );
654 assert_eq!(
655 tokenize(b"(line\\nfeed)"),
656 vec![Token::LitString(b"line\nfeed".to_vec())]
657 );
658 assert_eq!(
659 tokenize(b"(octal\\101)"),
660 vec![Token::LitString(b"octalA".to_vec())]
661 );
662 }
663
664 #[test]
665 fn hex_strings() {
666 assert_eq!(
667 tokenize(b"<48656C6C6F>"),
668 vec![Token::HexString(b"Hello".to_vec())]
669 );
670 assert_eq!(tokenize(b"<ABC>"), vec![Token::HexString(vec![0xAB, 0xC0])]);
672 assert_eq!(
674 tokenize(b"<48 65 6C>"),
675 vec![Token::HexString(b"Hel".to_vec())]
676 );
677 }
678
679 #[test]
680 fn booleans_and_null() {
681 assert_eq!(tokenize(b"true"), vec![Token::Bool(true)]);
682 assert_eq!(tokenize(b"false"), vec![Token::Bool(false)]);
683 let obj = parse_object(&mut Lexer::new(b"null")).unwrap();
684 assert_eq!(obj, PdfObj::Null);
685 }
686
687 #[test]
688 fn delimiters() {
689 let toks = tokenize(b"<< >> [ ]");
690 assert_eq!(
691 toks,
692 vec![
693 Token::DictBegin,
694 Token::DictEnd,
695 Token::ArrayBegin,
696 Token::ArrayEnd,
697 ]
698 );
699 }
700
701 #[test]
702 fn comments_skipped() {
703 assert_eq!(tokenize(b"% comment\n42"), vec![Token::Int(42)]);
704 }
705
706 #[test]
707 fn keywords() {
708 assert_eq!(
709 tokenize(b"obj endobj stream"),
710 vec![
711 Token::Keyword(b"obj".to_vec()),
712 Token::Keyword(b"endobj".to_vec()),
713 Token::Keyword(b"stream".to_vec()),
714 ]
715 );
716 }
717
718 #[test]
719 fn parse_array() {
720 let obj = parse_object(&mut Lexer::new(b"[1 2 /Name]")).unwrap();
721 assert_eq!(
722 obj,
723 PdfObj::Array(vec![
724 PdfObj::Int(1),
725 PdfObj::Int(2),
726 PdfObj::Name(b"Name".to_vec()),
727 ])
728 );
729 }
730
731 #[test]
732 fn parse_dict() {
733 let obj = parse_object(&mut Lexer::new(b"<< /Type /Page /Count 5 >>")).unwrap();
734 let dict = obj.as_dict().unwrap();
735 assert_eq!(dict.get_name(b"Type"), Some(b"Page".as_slice()));
736 assert_eq!(dict.get_int(b"Count"), Some(5));
737 }
738
739 #[test]
740 fn parse_indirect_ref() {
741 let obj = parse_object(&mut Lexer::new(b"10 0 R")).unwrap();
742 assert_eq!(obj, PdfObj::Ref(10, 0));
743 }
744
745 #[test]
746 fn parse_nested_dict() {
747 let obj = parse_object(&mut Lexer::new(
748 b"<< /Resources << /Font << /F1 5 0 R >> >> >>",
749 ))
750 .unwrap();
751 let dict = obj.as_dict().unwrap();
752 let res = dict.get_dict(b"Resources").unwrap();
753 let font = res.get_dict(b"Font").unwrap();
754 assert_eq!(font.get(b"F1"), Some(&PdfObj::Ref(5, 0)));
755 }
756
757 #[test]
758 fn int_not_ref_at_eof() {
759 let obj = parse_object(&mut Lexer::new(b"42")).unwrap();
761 assert_eq!(obj, PdfObj::Int(42));
762 }
763}