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 = esc - b'0';
332 self.pos += 1;
333 if self.pos < self.data.len()
334 && self.data[self.pos] >= b'0'
335 && self.data[self.pos] <= b'7'
336 {
337 val = val * 8 + (self.data[self.pos] - b'0');
338 self.pos += 1;
339 if self.pos < self.data.len()
340 && self.data[self.pos] >= b'0'
341 && self.data[self.pos] <= b'7'
342 {
343 val = val * 8 + (self.data[self.pos] - b'0');
344 self.pos += 1;
345 }
346 }
347 result.push(val);
348 }
349 _ => {
350 result.push(esc);
352 self.pos += 1;
353 }
354 }
355 }
356 _ => {
357 result.push(b);
358 self.pos += 1;
359 }
360 }
361 }
362
363 Err(PdfError::Unterminated("string"))
364 }
365
366 fn read_hex_string(&mut self) -> Result<Token, PdfError> {
368 self.pos += 1; let mut result = Vec::new();
370 let mut high_nibble: Option<u8> = None;
371
372 while self.pos < self.data.len() {
373 let b = self.data[self.pos];
374 if b == b'>' {
375 self.pos += 1;
376 if let Some(h) = high_nibble {
378 result.push(h << 4);
379 }
380 return Ok(Token::HexString(result));
381 }
382 if is_whitespace(b) {
383 self.pos += 1;
384 continue;
385 }
386 if let Some(nibble) = hex_digit(b) {
387 match high_nibble {
388 None => high_nibble = Some(nibble),
389 Some(h) => {
390 result.push(h << 4 | nibble);
391 high_nibble = None;
392 }
393 }
394 self.pos += 1;
395 } else {
396 self.pos += 1;
397 return Err(PdfError::UnexpectedToken {
398 expected: "hex digit".into(),
399 got: format!("byte 0x{b:02x}"),
400 });
401 }
402 }
403
404 Err(PdfError::Unterminated("hex string"))
405 }
406
407 fn read_keyword(&mut self) -> Result<Token, PdfError> {
409 let start = self.pos;
410 while self.pos < self.data.len() && self.data[self.pos].is_ascii_alphabetic() {
411 self.pos += 1;
412 }
413 let word = &self.data[start..self.pos];
414 match word {
415 b"true" => Ok(Token::Bool(true)),
416 b"false" => Ok(Token::Bool(false)),
417 _ => Ok(Token::Keyword(word.to_vec())),
418 }
419 }
420}
421
422pub fn parse_object(lexer: &mut Lexer) -> Result<PdfObj, PdfError> {
426 let tok = lexer.next_token()?;
427 parse_object_from_token(lexer, tok)
428}
429
430pub fn parse_object_from_token(lexer: &mut Lexer, tok: Token) -> Result<PdfObj, PdfError> {
432 match tok {
433 Token::Bool(b) => Ok(PdfObj::Bool(b)),
434 Token::Real(f) => Ok(PdfObj::Real(f)),
435 Token::Int(n) => {
436 let saved = lexer.pos();
438 match lexer.next_token() {
439 Ok(Token::Int(g)) => match lexer.next_token() {
440 Ok(Token::Keyword(ref kw)) if kw == b"R" => Ok(PdfObj::Ref(n as u32, g as u16)),
441 _ => {
442 lexer.set_pos(saved);
443 Ok(PdfObj::Int(n))
444 }
445 },
446 _ => {
447 lexer.set_pos(saved);
448 Ok(PdfObj::Int(n))
449 }
450 }
451 }
452 Token::Name(n) => Ok(PdfObj::Name(n)),
453 Token::LitString(s) => Ok(PdfObj::Str(s)),
454 Token::HexString(s) => Ok(PdfObj::Str(s)),
455 Token::Keyword(ref kw) if kw == b"null" => Ok(PdfObj::Null),
456 Token::ArrayBegin => {
457 let mut elems = Vec::new();
458 loop {
459 let t = lexer.next_token()?;
460 if t == Token::ArrayEnd || t == Token::Eof {
461 break;
462 }
463 match parse_object_from_token(lexer, t) {
464 Ok(obj) => elems.push(obj),
465 Err(_) => {} }
467 }
468 Ok(PdfObj::Array(elems))
469 }
470 Token::DictBegin => {
471 let dict = parse_dict_body(lexer)?;
472 Ok(PdfObj::Dict(dict))
473 }
474 _ => Err(PdfError::UnexpectedToken {
475 expected: "object".into(),
476 got: format!("{tok:?}"),
477 }),
478 }
479}
480
481pub fn parse_dict_body(lexer: &mut Lexer) -> Result<PdfDict, PdfError> {
483 let mut dict = PdfDict::new();
484 loop {
485 let t = match lexer.next_token() {
490 Ok(t) => t,
491 Err(_) => continue,
492 };
493 match t {
494 Token::DictEnd | Token::Eof => break,
495 Token::Name(key) => {
496 match parse_object(lexer) {
502 Ok(val) => {
503 dict.insert(key, val);
504 }
505 Err(_) => {
506 dict.insert(key, PdfObj::Null);
507 }
508 }
509 }
510 _ => {
511 continue;
513 }
514 }
515 }
516 Ok(dict)
517}
518
519fn is_whitespace(b: u8) -> bool {
521 matches!(b, b' ' | b'\t' | b'\r' | b'\n' | 0x0C | 0x00)
522}
523
524fn is_delimiter(b: u8) -> bool {
526 matches!(
527 b,
528 b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
529 )
530}
531
532fn hex_digit(b: u8) -> Option<u8> {
534 match b {
535 b'0'..=b'9' => Some(b - b'0'),
536 b'a'..=b'f' => Some(b - b'a' + 10),
537 b'A'..=b'F' => Some(b - b'A' + 10),
538 _ => None,
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545
546 fn tokenize(input: &[u8]) -> Vec<Token> {
547 let mut lexer = Lexer::new(input);
548 let mut tokens = Vec::new();
549 loop {
550 let tok = lexer.next_token().unwrap();
551 if tok == Token::Eof {
552 break;
553 }
554 tokens.push(tok);
555 }
556 tokens
557 }
558
559 #[test]
560 fn integers() {
561 assert_eq!(tokenize(b"42"), vec![Token::Int(42)]);
562 assert_eq!(tokenize(b"-7"), vec![Token::Int(-7)]);
563 assert_eq!(tokenize(b"+5"), vec![Token::Int(5)]);
564 assert_eq!(tokenize(b"0"), vec![Token::Int(0)]);
565 }
566
567 #[test]
568 fn reals() {
569 assert_eq!(tokenize(b"2.5"), vec![Token::Real(2.5)]);
570 assert_eq!(tokenize(b".5"), vec![Token::Real(0.5)]);
571 assert_eq!(tokenize(b"-2.0"), vec![Token::Real(-2.0)]);
572 }
573
574 #[test]
575 fn names() {
576 assert_eq!(tokenize(b"/Type"), vec![Token::Name(b"Type".to_vec())]);
577 assert_eq!(tokenize(b"/"), vec![Token::Name(b"".to_vec())]); assert_eq!(tokenize(b"/A#20B"), vec![Token::Name(b"A B".to_vec())]); }
580
581 #[test]
582 fn strings() {
583 assert_eq!(
584 tokenize(b"(hello)"),
585 vec![Token::LitString(b"hello".to_vec())]
586 );
587 assert_eq!(
588 tokenize(b"(nested (parens))"),
589 vec![Token::LitString(b"nested (parens)".to_vec())]
590 );
591 assert_eq!(
592 tokenize(b"(line\\nfeed)"),
593 vec![Token::LitString(b"line\nfeed".to_vec())]
594 );
595 assert_eq!(
596 tokenize(b"(octal\\101)"),
597 vec![Token::LitString(b"octalA".to_vec())]
598 );
599 }
600
601 #[test]
602 fn hex_strings() {
603 assert_eq!(
604 tokenize(b"<48656C6C6F>"),
605 vec![Token::HexString(b"Hello".to_vec())]
606 );
607 assert_eq!(tokenize(b"<ABC>"), vec![Token::HexString(vec![0xAB, 0xC0])]);
609 assert_eq!(
611 tokenize(b"<48 65 6C>"),
612 vec![Token::HexString(b"Hel".to_vec())]
613 );
614 }
615
616 #[test]
617 fn booleans_and_null() {
618 assert_eq!(tokenize(b"true"), vec![Token::Bool(true)]);
619 assert_eq!(tokenize(b"false"), vec![Token::Bool(false)]);
620 let obj = parse_object(&mut Lexer::new(b"null")).unwrap();
621 assert_eq!(obj, PdfObj::Null);
622 }
623
624 #[test]
625 fn delimiters() {
626 let toks = tokenize(b"<< >> [ ]");
627 assert_eq!(
628 toks,
629 vec![
630 Token::DictBegin,
631 Token::DictEnd,
632 Token::ArrayBegin,
633 Token::ArrayEnd,
634 ]
635 );
636 }
637
638 #[test]
639 fn comments_skipped() {
640 assert_eq!(tokenize(b"% comment\n42"), vec![Token::Int(42)]);
641 }
642
643 #[test]
644 fn keywords() {
645 assert_eq!(
646 tokenize(b"obj endobj stream"),
647 vec![
648 Token::Keyword(b"obj".to_vec()),
649 Token::Keyword(b"endobj".to_vec()),
650 Token::Keyword(b"stream".to_vec()),
651 ]
652 );
653 }
654
655 #[test]
656 fn parse_array() {
657 let obj = parse_object(&mut Lexer::new(b"[1 2 /Name]")).unwrap();
658 assert_eq!(
659 obj,
660 PdfObj::Array(vec![
661 PdfObj::Int(1),
662 PdfObj::Int(2),
663 PdfObj::Name(b"Name".to_vec()),
664 ])
665 );
666 }
667
668 #[test]
669 fn parse_dict() {
670 let obj = parse_object(&mut Lexer::new(b"<< /Type /Page /Count 5 >>")).unwrap();
671 let dict = obj.as_dict().unwrap();
672 assert_eq!(dict.get_name(b"Type"), Some(b"Page".as_slice()));
673 assert_eq!(dict.get_int(b"Count"), Some(5));
674 }
675
676 #[test]
677 fn parse_indirect_ref() {
678 let obj = parse_object(&mut Lexer::new(b"10 0 R")).unwrap();
679 assert_eq!(obj, PdfObj::Ref(10, 0));
680 }
681
682 #[test]
683 fn parse_nested_dict() {
684 let obj = parse_object(&mut Lexer::new(
685 b"<< /Resources << /Font << /F1 5 0 R >> >> >>",
686 ))
687 .unwrap();
688 let dict = obj.as_dict().unwrap();
689 let res = dict.get_dict(b"Resources").unwrap();
690 let font = res.get_dict(b"Font").unwrap();
691 assert_eq!(font.get(b"F1"), Some(&PdfObj::Ref(5, 0)));
692 }
693
694 #[test]
695 fn int_not_ref_at_eof() {
696 let obj = parse_object(&mut Lexer::new(b"42")).unwrap();
698 assert_eq!(obj, PdfObj::Int(42));
699 }
700}