1use crate::error::{Error, Result};
11use crate::object::{Name, PdfString};
12
13#[derive(Debug, Clone, PartialEq)]
15pub enum Token<'a> {
16 Int(i64),
18 Real(f64),
20 String(PdfString),
22 Name(Name),
24 ArrayStart,
26 ArrayEnd,
28 DictStart,
30 DictEnd,
32 BraceOpen,
34 BraceClose,
36 Keyword(&'a [u8]),
38 Eof,
40}
41
42pub fn is_whitespace(b: u8) -> bool {
44 matches!(b, b'\0' | b'\t' | b'\n' | b'\x0c' | b'\r' | b' ')
45}
46
47pub fn is_delimiter(b: u8) -> bool {
49 matches!(
50 b,
51 b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
52 )
53}
54
55pub fn is_regular(b: u8) -> bool {
57 !is_whitespace(b) && !is_delimiter(b)
58}
59
60fn hex_value(b: u8) -> Option<u8> {
61 match b {
62 b'0'..=b'9' => Some(b - b'0'),
63 b'a'..=b'f' => Some(b - b'a' + 10),
64 b'A'..=b'F' => Some(b - b'A' + 10),
65 _ => None,
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct Lexer<'a> {
72 buf: &'a [u8],
73 pos: usize,
74}
75
76impl<'a> Lexer<'a> {
77 pub fn new(buf: &'a [u8]) -> Self {
79 Self { buf, pos: 0 }
80 }
81
82 pub fn buffer(&self) -> &'a [u8] {
84 self.buf
85 }
86
87 pub fn pos(&self) -> usize {
89 self.pos
90 }
91
92 pub fn set_pos(&mut self, pos: usize) {
94 self.pos = pos.min(self.buf.len());
95 }
96
97 pub fn at_end(&self) -> bool {
99 self.pos >= self.buf.len()
100 }
101
102 pub fn rest(&self) -> &'a [u8] {
104 self.buf.get(self.pos..).unwrap_or_default()
105 }
106
107 pub fn peek(&self) -> Option<u8> {
109 self.buf.get(self.pos).copied()
110 }
111
112 fn bump(&mut self) -> Option<u8> {
113 let b = self.peek()?;
114 self.pos += 1;
115 Some(b)
116 }
117
118 pub fn skip_whitespace(&mut self) {
120 while let Some(b) = self.peek() {
121 if is_whitespace(b) {
122 self.pos += 1;
123 } else if b == b'%' {
124 while let Some(c) = self.peek() {
125 if c == b'\r' || c == b'\n' {
126 break;
127 }
128 self.pos += 1;
129 }
130 } else {
131 break;
132 }
133 }
134 }
135
136 pub fn next_token(&mut self) -> Result<Token<'a>> {
138 self.skip_whitespace();
139 let Some(b) = self.peek() else {
140 return Ok(Token::Eof);
141 };
142 match b {
143 b'[' => {
144 self.pos += 1;
145 Ok(Token::ArrayStart)
146 }
147 b']' => {
148 self.pos += 1;
149 Ok(Token::ArrayEnd)
150 }
151 b'{' => {
152 self.pos += 1;
153 Ok(Token::BraceOpen)
154 }
155 b'}' => {
156 self.pos += 1;
157 Ok(Token::BraceClose)
158 }
159 b'<' => {
160 if self.buf.get(self.pos + 1) == Some(&b'<') {
161 self.pos += 2;
162 Ok(Token::DictStart)
163 } else {
164 self.pos += 1;
165 self.hex_string()
166 }
167 }
168 b'>' => {
169 self.pos += 1;
170 if self.peek() == Some(b'>') {
171 self.pos += 1;
172 Ok(Token::DictEnd)
173 } else {
174 Ok(Token::Keyword(b">"))
177 }
178 }
179 b'(' => {
180 self.pos += 1;
181 self.literal_string()
182 }
183 b')' => {
184 self.pos += 1;
185 Ok(Token::Keyword(b")"))
186 }
187 b'/' => {
188 self.pos += 1;
189 Ok(self.name())
190 }
191 b'0'..=b'9' | b'+' | b'-' | b'.' => Ok(self.number()),
192 _ => {
193 let start = self.pos;
194 while self.peek().is_some_and(is_regular) {
195 self.pos += 1;
196 }
197 Ok(Token::Keyword(
198 self.buf.get(start..self.pos).unwrap_or_default(),
199 ))
200 }
201 }
202 }
203
204 fn number(&mut self) -> Token<'a> {
205 let mut negative = false;
206 while let Some(b @ (b'+' | b'-')) = self.peek() {
209 negative |= b == b'-';
210 self.pos += 1;
211 }
212 let int_start = self.pos;
213 while self.peek().is_some_and(|b| b.is_ascii_digit()) {
214 self.pos += 1;
215 }
216 let int_digits = self.buf.get(int_start..self.pos).unwrap_or_default();
217 let mut frac_digits: &[u8] = &[];
218 let is_real = self.peek() == Some(b'.');
219 if is_real {
220 self.pos += 1;
221 let frac_start = self.pos;
222 while self.peek().is_some_and(|b| b.is_ascii_digit()) {
223 self.pos += 1;
224 }
225 frac_digits = self.buf.get(frac_start..self.pos).unwrap_or_default();
226 while self
229 .peek()
230 .is_some_and(|b| b == b'.' || b == b'-' || b.is_ascii_digit())
231 {
232 self.pos += 1;
233 }
234 }
235 if !is_real {
236 let mut value: i64 = 0;
237 let mut overflow = false;
238 for &d in int_digits {
239 match value
240 .checked_mul(10)
241 .and_then(|v| v.checked_add(i64::from(d - b'0')))
242 {
243 Some(v) => value = v,
244 None => {
245 overflow = true;
246 break;
247 }
248 }
249 }
250 if !overflow {
251 return Token::Int(if negative { -value } else { value });
252 }
253 }
254 let mut text = String::with_capacity(int_digits.len() + frac_digits.len() + 2);
257 text.push('0');
258 text.extend(int_digits.iter().map(|&d| char::from(d)));
259 text.push('.');
260 text.extend(frac_digits.iter().map(|&d| char::from(d)));
261 let value: f64 = text.parse().unwrap_or(0.0);
262 Token::Real(if negative { -value } else { value })
263 }
264
265 fn name(&mut self) -> Token<'a> {
266 let mut out = Vec::new();
267 while let Some(b) = self.peek() {
268 if !is_regular(b) {
269 break;
270 }
271 self.pos += 1;
272 if b == b'#' {
273 let hi = self.peek().and_then(hex_value);
274 let lo = self.buf.get(self.pos + 1).copied().and_then(hex_value);
275 if let (Some(hi), Some(lo)) = (hi, lo) {
276 out.push(hi << 4 | lo);
277 self.pos += 2;
278 continue;
279 }
280 }
281 out.push(b);
282 }
283 Token::Name(Name(out))
284 }
285
286 fn hex_string(&mut self) -> Result<Token<'a>> {
287 let mut out = Vec::new();
288 let mut high: Option<u8> = None;
289 loop {
290 let Some(b) = self.bump() else {
291 return Err(Error::UnexpectedEof);
292 };
293 if b == b'>' {
294 break;
295 }
296 let Some(v) = hex_value(b) else { continue };
298 match high.take() {
299 Some(h) => out.push(h << 4 | v),
300 None => high = Some(v),
301 }
302 }
303 if let Some(h) = high {
304 out.push(h << 4);
305 }
306 Ok(Token::String(PdfString::hex(out)))
307 }
308
309 fn literal_string(&mut self) -> Result<Token<'a>> {
310 let mut out = Vec::new();
311 let mut depth = 1u32;
312 loop {
313 let Some(b) = self.bump() else {
314 return Err(Error::UnexpectedEof);
315 };
316 match b {
317 b'(' => {
318 depth += 1;
319 out.push(b);
320 }
321 b')' => {
322 depth -= 1;
323 if depth == 0 {
324 break;
325 }
326 out.push(b);
327 }
328 b'\r' => {
329 if self.peek() == Some(b'\n') {
331 self.pos += 1;
332 }
333 out.push(b'\n');
334 }
335 b'\\' => {
336 let Some(e) = self.bump() else {
337 return Err(Error::UnexpectedEof);
338 };
339 match e {
340 b'n' => out.push(b'\n'),
341 b'r' => out.push(b'\r'),
342 b't' => out.push(b'\t'),
343 b'b' => out.push(0x08),
344 b'f' => out.push(0x0c),
345 b'0'..=b'7' => {
346 let mut v = u32::from(e - b'0');
347 for _ in 0..2 {
348 match self.peek() {
349 Some(d @ b'0'..=b'7') => {
350 v = v * 8 + u32::from(d - b'0');
351 self.pos += 1;
352 }
353 _ => break,
354 }
355 }
356 out.push((v & 0xff) as u8);
358 }
359 b'\r' => {
360 if self.peek() == Some(b'\n') {
361 self.pos += 1;
362 }
363 }
364 b'\n' => {}
365 other => out.push(other),
366 }
367 }
368 other => out.push(other),
369 }
370 }
371 Ok(Token::String(PdfString::literal(out)))
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378
379 fn tokens(input: &[u8]) -> Vec<Token<'_>> {
380 let mut lexer = Lexer::new(input);
381 let mut out = Vec::new();
382 loop {
383 match lexer.next_token().unwrap() {
384 Token::Eof => return out,
385 t => out.push(t),
386 }
387 }
388 }
389
390 fn one(input: &[u8]) -> Token<'_> {
391 let mut t = tokens(input);
392 assert_eq!(t.len(), 1, "{t:?}");
393 t.remove(0)
394 }
395
396 #[test]
397 fn numbers() {
398 assert_eq!(one(b"123"), Token::Int(123));
399 assert_eq!(one(b"-17"), Token::Int(-17));
400 assert_eq!(one(b"+5"), Token::Int(5));
401 assert_eq!(one(b"0"), Token::Int(0));
402 assert_eq!(one(b"34.5"), Token::Real(34.5));
403 assert_eq!(one(b"-.002"), Token::Real(-0.002));
404 assert_eq!(one(b"4."), Token::Real(4.0));
405 assert_eq!(one(b"--5"), Token::Int(-5));
406 assert_eq!(one(b"-"), Token::Int(0));
407 assert_eq!(one(b"."), Token::Real(0.0));
408 assert_eq!(one(b"1.2.3"), Token::Real(1.2));
409 assert_eq!(one(b"0.00-1"), Token::Real(0.0));
410 assert_eq!(
411 one(b"99999999999999999999"),
412 Token::Real(99999999999999999999.0)
413 );
414 }
415
416 #[test]
417 fn number_then_keyword() {
418 assert_eq!(
419 tokens(b"1 0obj"),
420 vec![Token::Int(1), Token::Int(0), Token::Keyword(b"obj")]
421 );
422 }
423
424 #[test]
425 fn literal_strings() {
426 let s = |b: &[u8]| Token::String(PdfString::literal(b.to_vec()));
427 assert_eq!(one(b"(hello)"), s(b"hello"));
428 assert_eq!(one(b"(a (nested) b)"), s(b"a (nested) b"));
429 assert_eq!(
430 one(b"(\\n\\r\\t\\b\\f\\(\\)\\\\)"),
431 s(b"\n\r\t\x08\x0c()\\")
432 );
433 assert_eq!(one(b"(\\101\\60\\0053)"), s(b"A0\x053"));
434 assert_eq!(one(b"(\\777)"), s(b"\xff"));
435 assert_eq!(one(b"(a\\\r\nb\\\nc)"), s(b"abc"));
436 assert_eq!(one(b"(a\r\nb\rc)"), s(b"a\nb\nc"));
437 assert_eq!(one(b"(\\q)"), s(b"q"));
438 assert_eq!(one(b"()"), s(b""));
439 }
440
441 #[test]
442 fn hex_strings() {
443 let h = |b: &[u8]| Token::String(PdfString::hex(b.to_vec()));
444 assert_eq!(one(b"<48656C6c6f>"), h(b"Hello"));
445 assert_eq!(one(b"<901FA>"), h(b"\x90\x1f\xa0"));
446 assert_eq!(one(b"<4 8\n6 5>"), h(b"He"));
447 assert_eq!(one(b"<4z8>"), h(b"H"));
448 assert_eq!(one(b"<>"), h(b""));
449 }
450
451 #[test]
452 fn names() {
453 let n = |b: &[u8]| Token::Name(Name(b.to_vec()));
454 assert_eq!(one(b"/Name1"), n(b"Name1"));
455 assert_eq!(
456 one(b"/A;Name_With-Various***Characters?"),
457 n(b"A;Name_With-Various***Characters?")
458 );
459 assert_eq!(one(b"/lime#20Green"), n(b"lime Green"));
460 assert_eq!(one(b"/paired#28#29parentheses"), n(b"paired()parentheses"));
461 assert_eq!(one(b"/bad#zz"), n(b"bad#zz"));
462 assert_eq!(one(b"/end#"), n(b"end#"));
463 assert_eq!(one(b"/"), n(b""));
464 assert_eq!(tokens(b"/A/B"), vec![n(b"A"), n(b"B")]);
465 }
466
467 #[test]
468 fn delimiters_and_comments() {
469 assert_eq!(
470 tokens(b"<< /K [1 2] >> % comment\n{ } true"),
471 vec![
472 Token::DictStart,
473 Token::Name(Name(b"K".to_vec())),
474 Token::ArrayStart,
475 Token::Int(1),
476 Token::Int(2),
477 Token::ArrayEnd,
478 Token::DictEnd,
479 Token::BraceOpen,
480 Token::BraceClose,
481 Token::Keyword(b"true"),
482 ]
483 );
484 }
485
486 #[test]
487 fn stray_closers() {
488 assert_eq!(
489 tokens(b"> )"),
490 vec![Token::Keyword(b">"), Token::Keyword(b")")]
491 );
492 }
493
494 #[test]
495 fn unterminated_is_eof() {
496 for input in [&b"(abc"[..], b"<414", b"(a\\"] {
497 assert_eq!(Lexer::new(input).next_token(), Err(Error::UnexpectedEof));
498 }
499 }
500
501 #[test]
502 fn position_helpers() {
503 let mut l = Lexer::new(b" abc");
504 l.skip_whitespace();
505 assert_eq!(l.pos(), 2);
506 assert_eq!(l.rest(), b"abc");
507 l.set_pos(100);
508 assert!(l.at_end());
509 assert_eq!(l.peek(), None);
510 assert_eq!(l.buffer(), b" abc");
511 }
512}