1use std::collections::BTreeMap;
5
6use crate::diag::{DiagnosticKind, Diagnostics};
7use crate::error::{Error, Result};
8use crate::lexer::{Lexer, Token};
9use crate::limits::{Limits, Strictness};
10use crate::object::{Dict, Object};
11use crate::parser::Parser;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum XrefEntry {
16 Free,
18 InUse {
20 offset: u64,
22 generation: u16,
24 },
25 Compressed {
27 stream: u32,
29 index: u32,
31 },
32}
33
34pub type XrefMap = BTreeMap<u32, XrefEntry>;
36
37pub fn parse_table(
40 buf: &[u8],
41 base: u64,
42 limits: &Limits,
43 strictness: Strictness,
44 diagnostics: &Diagnostics,
45) -> Result<(XrefMap, Dict)> {
46 let mut lexer = Lexer::new(buf);
47 if lexer.next_token()? != Token::Keyword(b"xref") {
48 return Err(Error::syntax(base, "expected `xref`"));
49 }
50 let mut entries = XrefMap::new();
51 let mut first_subsection = true;
52 loop {
53 let at = base + lexer.pos() as u64;
54 match lexer.next_token()? {
55 Token::Keyword(b"trailer") => break,
56 Token::Int(first) => {
57 let Token::Int(count) = lexer.next_token()? else {
58 return Err(Error::syntax(at, "expected xref subsection count"));
59 };
60 let (Ok(first), Ok(count)) = (u32::try_from(first), u32::try_from(count)) else {
61 return Err(Error::syntax(at, "invalid xref subsection"));
62 };
63 if u64::from(first) + u64::from(count) > u64::from(limits.max_objects) + 1 {
64 return Err(Error::syntax(at, "xref subsection too large"));
65 }
66 let mut parsed = Vec::new();
67 for _ in 0..count {
68 let save = lexer.pos();
69 match (
70 lexer.next_token()?,
71 lexer.next_token()?,
72 lexer.next_token()?,
73 ) {
74 (Token::Int(offset), Token::Int(generation), Token::Keyword(kind))
75 if kind == b"n" || kind == b"f" =>
76 {
77 parsed.push((offset, generation, kind == b"n"));
78 }
79 _ => {
80 lexer.set_pos(save);
82 if strictness == Strictness::Strict {
83 return Err(Error::syntax(at, "short xref subsection"));
84 }
85 diagnostics.push(DiagnosticKind::BadXrefSection, Some(at), None);
86 break;
87 }
88 }
89 }
90 let mut first = first;
93 if first_subsection
94 && first == 1
95 && parsed
96 .first()
97 .is_some_and(|&(o, g, used)| o == 0 && g == 65535 && !used)
98 {
99 first = 0;
100 }
101 first_subsection = false;
102 for (i, (offset, generation, used)) in parsed.into_iter().enumerate() {
103 let Some(num) = first.checked_add(i as u32) else {
104 break;
105 };
106 let entry = if used && offset > 0 {
107 XrefEntry::InUse {
108 offset: offset as u64,
109 generation: generation.clamp(0, 65535) as u16,
110 }
111 } else {
112 XrefEntry::Free
113 };
114 entries.insert(num, entry);
115 }
116 }
117 Token::Eof => return Err(Error::UnexpectedEof),
118 _ => return Err(Error::syntax(at, "expected xref subsection or `trailer`")),
119 }
120 }
121 let pos = lexer.pos();
122 let rest = buf.get(pos..).unwrap_or_default();
123 let trailer = Parser::new(rest, base + pos as u64, limits, strictness)
124 .with_diagnostics(diagnostics)
125 .parse_dict()?;
126 Ok((entries, trailer))
127}
128
129pub fn parse_stream(dict: &Dict, data: &[u8], limits: &Limits) -> Result<XrefMap> {
131 let widths: Vec<usize> = dict
132 .get_array(b"W")
133 .ok_or(Error::syntax(0, "xref stream without /W"))?
134 .iter()
135 .map(|w| {
136 w.as_int()
137 .and_then(|w| usize::try_from(w).ok())
138 .unwrap_or(usize::MAX)
139 })
140 .collect();
141 let [w0, w1, w2] = widths[..] else {
142 return Err(Error::syntax(0, "xref stream /W must have three entries"));
143 };
144 if w0 > 8 || w1 > 8 || w2 > 8 {
145 return Err(Error::syntax(0, "xref stream field wider than 8 bytes"));
146 }
147 let width = w0 + w1 + w2;
148 if width == 0 {
149 return Err(Error::syntax(0, "xref stream entries have zero width"));
150 }
151 let size = dict.get_int(b"Size").unwrap_or(0);
152 let index: Vec<(i64, i64)> = match dict.get_array(b"Index") {
153 Some(a) => a
154 .chunks(2)
155 .filter_map(|p| match p {
156 [a, b] => Some((a.as_int()?, b.as_int()?)),
157 _ => None,
158 })
159 .collect(),
160 None => vec![(0, size)],
161 };
162
163 let mut entries = XrefMap::new();
164 let mut rows = data.chunks_exact(width);
165 for (first, count) in index {
166 let (Ok(first), Ok(count)) = (u32::try_from(first), u32::try_from(count)) else {
167 return Err(Error::syntax(0, "invalid xref stream /Index"));
168 };
169 for i in 0..count {
170 let Some(num) = first.checked_add(i) else {
171 break;
172 };
173 if num > limits.max_objects {
174 return Err(Error::syntax(0, "xref stream object number too large"));
175 }
176 let Some(row) = rows.next() else {
178 return Ok(entries);
179 };
180 let field = |from: usize, len: usize| -> u64 {
181 row.get(from..from + len)
182 .unwrap_or_default()
183 .iter()
184 .fold(0u64, |acc, &b| acc << 8 | u64::from(b))
185 };
186 let kind = if w0 == 0 { 1 } else { field(0, w0) };
187 let a = field(w0, w1);
188 let b = field(w0 + w1, w2);
189 let entry = match kind {
190 0 => XrefEntry::Free,
191 1 if a > 0 => XrefEntry::InUse {
192 offset: a,
193 generation: b.min(65535) as u16,
194 },
195 1 => XrefEntry::Free,
196 2 => XrefEntry::Compressed {
197 stream: u32::try_from(a).unwrap_or(u32::MAX),
198 index: u32::try_from(b).unwrap_or(u32::MAX),
199 },
200 _ => XrefEntry::Free,
202 };
203 entries.insert(num, entry);
204 }
205 }
206 Ok(entries)
207}
208
209pub fn is_xref_stream(object: &Object) -> bool {
211 object.as_stream().is_some_and(|s| {
212 s.dict.is_type(b"XRef") || (s.dict.contains_key(b"W") && s.dict.contains_key(b"Size"))
213 })
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::object::Name;
220
221 fn table(input: &[u8]) -> Result<(XrefMap, Dict)> {
222 parse_table(
223 input,
224 0,
225 &Limits::default(),
226 Strictness::Lenient,
227 &Diagnostics::default(),
228 )
229 }
230
231 #[test]
232 fn classic_table() {
233 let (entries, trailer) = table(
234 b"xref\n0 3\n0000000000 65535 f \n0000000015 00000 n \n0000000079 00001 n \n\
235 5 1\n0000000200 00000 n\r\ntrailer\n<< /Size 6 /Root 1 0 R >>",
236 )
237 .unwrap();
238 assert_eq!(entries.get(&0), Some(&XrefEntry::Free));
239 assert_eq!(
240 entries.get(&1),
241 Some(&XrefEntry::InUse {
242 offset: 15,
243 generation: 0
244 })
245 );
246 assert_eq!(
247 entries.get(&2),
248 Some(&XrefEntry::InUse {
249 offset: 79,
250 generation: 1
251 })
252 );
253 assert!(matches!(
254 entries.get(&5),
255 Some(XrefEntry::InUse { offset: 200, .. })
256 ));
257 assert_eq!(trailer.get_int(b"Size"), Some(6));
258 }
259
260 #[test]
261 fn off_by_one_first_subsection() {
262 let (entries, _) =
263 table(b"xref\n1 2\n0000000000 65535 f \n0000000015 00000 n \ntrailer\n<<>>").unwrap();
264 assert!(matches!(
265 entries.get(&1),
266 Some(XrefEntry::InUse { offset: 15, .. })
267 ));
268 }
269
270 #[test]
271 fn short_subsection() {
272 let diags = Diagnostics::default();
273 let (entries, _) = parse_table(
274 b"xref\n0 5\n0000000000 65535 f \n0000000015 00000 n \ntrailer\n<<>>",
275 0,
276 &Limits::default(),
277 Strictness::Lenient,
278 &diags,
279 )
280 .unwrap();
281 assert_eq!(entries.len(), 2);
282 assert_eq!(diags.len(), 1);
283 assert!(
284 parse_table(
285 b"xref\n0 5\n0000000000 65535 f \ntrailer\n<<>>",
286 0,
287 &Limits::default(),
288 Strictness::Strict,
289 &diags,
290 )
291 .is_err()
292 );
293 }
294
295 #[test]
296 fn huge_claimed_count_is_rejected() {
297 assert!(table(b"xref\n0 2000000000\n0000000000 65535 f \ntrailer\n<<>>").is_err());
298 }
299
300 #[test]
301 fn zero_offset_in_use_is_free() {
302 let (entries, _) =
303 table(b"xref\n0 2\n0000000000 65535 f \n0000000000 00000 n \ntrailer\n<<>>").unwrap();
304 assert_eq!(entries.get(&1), Some(&XrefEntry::Free));
305 }
306
307 #[test]
308 fn malformed_tables() {
309 assert!(table(b"xref\nfoo").is_err());
310 assert!(table(b"xreff").is_err());
311 assert_eq!(table(b"xref\n0 1\n"), Err(Error::UnexpectedEof));
312 assert!(table(b"xref\n0 x").is_err());
313 assert!(table(b"xref\n-1 1\n").is_err());
314 }
315
316 fn stream_dict(w: [i64; 3], index: Option<Vec<i64>>, size: i64) -> Dict {
317 let mut d = Dict::new();
318 d.insert("Type", Name::from("XRef"));
319 d.insert("W", w.iter().map(|&x| Object::Int(x)).collect::<Vec<_>>());
320 d.insert("Size", size);
321 if let Some(index) = index {
322 d.insert(
323 "Index",
324 index.into_iter().map(Object::Int).collect::<Vec<_>>(),
325 );
326 }
327 d
328 }
329
330 #[test]
331 fn xref_stream_entries() {
332 let dict = stream_dict([1, 2, 1], None, 3);
333 let data = [0, 0, 0, 255, 1, 0, 15, 0, 2, 0, 7, 3];
334 let entries = parse_stream(&dict, &data, &Limits::default()).unwrap();
335 assert_eq!(entries.get(&0), Some(&XrefEntry::Free));
336 assert_eq!(
337 entries.get(&1),
338 Some(&XrefEntry::InUse {
339 offset: 15,
340 generation: 0
341 })
342 );
343 assert_eq!(
344 entries.get(&2),
345 Some(&XrefEntry::Compressed {
346 stream: 7,
347 index: 3
348 })
349 );
350 }
351
352 #[test]
353 fn xref_stream_index_and_defaults() {
354 let dict = stream_dict([0, 2, 0], Some(vec![10, 2]), 12);
356 let entries = parse_stream(&dict, &[0, 5, 0, 9], &Limits::default()).unwrap();
357 assert!(matches!(
358 entries.get(&10),
359 Some(XrefEntry::InUse { offset: 5, .. })
360 ));
361 assert!(matches!(
362 entries.get(&11),
363 Some(XrefEntry::InUse { offset: 9, .. })
364 ));
365 let entries = parse_stream(&dict, &[0, 5], &Limits::default()).unwrap();
367 assert_eq!(entries.len(), 1);
368 }
369
370 #[test]
371 fn xref_stream_errors() {
372 let limits = Limits::default();
373 assert!(parse_stream(&Dict::new(), &[], &limits).is_err());
374 assert!(parse_stream(&stream_dict([9, 1, 1], None, 1), &[], &limits).is_err());
375 assert!(parse_stream(&stream_dict([0, 0, 0], None, 1), &[], &limits).is_err());
376 let mut d = stream_dict([1, 1, 1], None, 1);
377 d.insert("W", vec![Object::Int(1)]);
378 assert!(parse_stream(&d, &[], &limits).is_err());
379 let huge = stream_dict([1, 1, 1], Some(vec![u32::MAX as i64, 1]), 1);
380 assert!(parse_stream(&huge, &[1, 1, 1], &limits).is_err());
381 }
382}