1use crate::parser::objects::{PdfArray, PdfDictionary, PdfName, PdfObject};
10use crate::parser::ParseOptions;
11use crate::parser::{ParseError, ParseResult};
12use std::io::{Read, Seek};
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum XRefEntry {
17 Free {
19 next_free_object: u32,
21 generation: u16,
23 },
24 InUse {
26 offset: u64,
28 generation: u16,
30 },
31 Compressed {
33 stream_object_number: u32,
35 index_within_stream: u32,
37 },
38}
39
40pub struct XRefStream {
42 pub dict: PdfDictionary,
44 pub data: Vec<u8>,
46 pub widths: Vec<usize>,
48 pub index: Vec<(u32, u32)>,
50}
51
52impl XRefStream {
53 pub fn parse<R: Read + Seek>(
62 _reader: &mut R,
63 stream_dict: PdfDictionary,
64 stream_data: Vec<u8>,
65 _options: &ParseOptions,
66 ) -> ParseResult<Self> {
67 let widths = stream_dict
69 .get("W")
70 .and_then(|obj| obj.as_array())
71 .ok_or_else(|| ParseError::MissingKey("W array in xref stream".to_string()))?
72 .0
73 .iter()
74 .map(|obj| {
75 obj.as_integer()
76 .ok_or_else(|| ParseError::SyntaxError {
77 position: 0,
78 message: "Invalid width in W array".to_string(),
79 })
80 .map(|n| n as usize)
81 })
82 .collect::<ParseResult<Vec<_>>>()?;
83
84 if widths.len() != 3 {
85 return Err(ParseError::SyntaxError {
86 position: 0,
87 message: format!(
88 "W array must have 3 elements, found {len}",
89 len = widths.len()
90 ),
91 });
92 }
93
94 let index =
96 if let Some(index_array) = stream_dict.get("Index").and_then(|obj| obj.as_array()) {
97 let mut index_pairs = Vec::new();
98 let mut i = 0;
99 while i + 1 < index_array.len() {
100 let first =
101 index_array.0[i]
102 .as_integer()
103 .ok_or_else(|| ParseError::SyntaxError {
104 position: 0,
105 message: "Invalid first object number in Index".to_string(),
106 })? as u32;
107 let count = index_array.0[i + 1].as_integer().ok_or_else(|| {
108 ParseError::SyntaxError {
109 position: 0,
110 message: "Invalid count in Index".to_string(),
111 }
112 })? as u32;
113 index_pairs.push((first, count));
114 i += 2;
115 }
116 index_pairs
117 } else {
118 let size = stream_dict
120 .get("Size")
121 .and_then(|obj| obj.as_integer())
122 .ok_or_else(|| ParseError::MissingKey("Size in xref stream".to_string()))?
123 as u32;
124 vec![(0, size)]
125 };
126
127 let decoded_data = stream_data;
135
136 Ok(XRefStream {
137 dict: stream_dict,
138 data: decoded_data,
139 widths,
140 index,
141 })
142 }
143
144 pub fn to_xref_entries(&self) -> ParseResult<Vec<(u32, XRefEntry)>> {
146 let mut entries = Vec::new();
147 let entry_size = self.widths.iter().sum::<usize>();
148
149 if entry_size == 0 {
150 return Err(ParseError::SyntaxError {
151 position: 0,
152 message: "Invalid entry size (0) in xref stream".to_string(),
153 });
154 }
155
156 let mut data_offset = 0;
157
158 for &(first_obj, count) in &self.index {
159 for i in 0..count {
160 if data_offset + entry_size > self.data.len() {
161 return Err(ParseError::SyntaxError {
162 position: data_offset,
163 message: format!("Xref stream data truncated at obj {}", first_obj + i),
164 });
165 }
166
167 let mut field_offset = data_offset;
169 let mut fields = Vec::new();
170
171 for &width in &self.widths {
172 let field_value = if width == 0 {
173 0 } else {
175 read_field(&self.data[field_offset..field_offset + width])
176 };
177 fields.push(field_value);
178 field_offset += width;
179 }
180
181 let entry_type = fields[0];
183 let obj_num = first_obj + i;
184
185 let entry = match entry_type {
186 0 => {
187 XRefEntry::Free {
189 next_free_object: fields[1] as u32,
190 generation: fields[2] as u16,
191 }
192 }
193 1 => {
194 XRefEntry::InUse {
196 offset: fields[1],
197 generation: fields[2] as u16,
198 }
199 }
200 2 => {
201 XRefEntry::Compressed {
203 stream_object_number: fields[1] as u32,
204 index_within_stream: fields[2] as u32,
205 }
206 }
207 _ => {
208 return Err(ParseError::SyntaxError {
209 position: data_offset,
210 message: format!("Invalid xref entry type: {entry_type}"),
211 });
212 }
213 };
214
215 entries.push((obj_num, entry));
216 data_offset += entry_size;
217 }
218 }
219
220 Ok(entries)
221 }
222
223 pub fn trailer_dict(&self) -> &PdfDictionary {
225 &self.dict
226 }
227
228 pub fn is_hybrid(&self) -> bool {
230 self.dict.get("XRefStm").is_some()
232 }
233
234 pub fn get_xref_stm_offset(&self) -> Option<u64> {
236 self.dict
237 .get("XRefStm")
238 .and_then(|obj| obj.as_integer())
239 .map(|n| n as u64)
240 }
241
242 pub fn get_prev_offset(&self) -> Option<u64> {
244 self.dict
245 .get("Prev")
246 .and_then(|obj| obj.as_integer())
247 .map(|n| n as u64)
248 }
249}
250
251fn read_field(bytes: &[u8]) -> u64 {
253 let mut value = 0u64;
254 for &byte in bytes {
255 value = (value << 8) | (byte as u64);
256 }
257 value
258}
259
260pub struct XRefStreamBuilder {
262 entries: Vec<(u32, XRefEntry)>,
264 trailer_entries: PdfDictionary,
266}
267
268impl Default for XRefStreamBuilder {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274impl XRefStreamBuilder {
275 pub fn new() -> Self {
277 Self {
278 entries: Vec::new(),
279 trailer_entries: PdfDictionary::new(),
280 }
281 }
282
283 pub fn add_entry(&mut self, obj_num: u32, entry: XRefEntry) {
285 self.entries.push((obj_num, entry));
286 }
287
288 pub fn add_trailer_entry(&mut self, key: &str, value: PdfObject) {
290 self.trailer_entries.insert(key.to_string(), value);
291 }
292
293 pub fn build(mut self) -> ParseResult<(PdfDictionary, Vec<u8>)> {
295 self.entries.sort_by_key(|(num, _)| *num);
297
298 let mut max_offset = 0u64;
300 let mut max_obj_num = 0u32;
301 let mut max_gen = 0u16;
302 let mut _has_compressed = false;
303
304 for (obj_num, entry) in &self.entries {
305 max_obj_num = max_obj_num.max(*obj_num);
306 match entry {
307 XRefEntry::InUse { offset, generation } => {
308 max_offset = max_offset.max(*offset);
309 max_gen = max_gen.max(*generation);
310 }
311 XRefEntry::Free { generation, .. } => {
312 max_gen = max_gen.max(*generation);
313 }
314 XRefEntry::Compressed {
315 stream_object_number,
316 index_within_stream,
317 } => {
318 _has_compressed = true;
319 max_obj_num = max_obj_num.max(*stream_object_number);
320 max_offset = max_offset.max(*index_within_stream as u64);
321 }
322 }
323 }
324
325 let w1 = 1; let w2 = bytes_needed(max_offset.max(max_obj_num as u64));
328 let w3 = bytes_needed(max_gen as u64);
329
330 let mut stream_data = Vec::new();
332
333 for (_obj_num, entry) in &self.entries {
334 match entry {
335 XRefEntry::Free {
336 next_free_object,
337 generation,
338 } => {
339 write_field(&mut stream_data, 0, w1); write_field(&mut stream_data, *next_free_object as u64, w2);
341 write_field(&mut stream_data, *generation as u64, w3);
342 }
343 XRefEntry::InUse { offset, generation } => {
344 write_field(&mut stream_data, 1, w1); write_field(&mut stream_data, *offset, w2);
346 write_field(&mut stream_data, *generation as u64, w3);
347 }
348 XRefEntry::Compressed {
349 stream_object_number,
350 index_within_stream,
351 } => {
352 write_field(&mut stream_data, 2, w1); write_field(&mut stream_data, *stream_object_number as u64, w2);
354 write_field(&mut stream_data, *index_within_stream as u64, w3);
355 }
356 }
357 }
358
359 let mut dict = self.trailer_entries;
361 dict.insert(
362 "Type".to_string(),
363 PdfObject::Name(PdfName("XRef".to_string())),
364 );
365 dict.insert(
366 "W".to_string(),
367 PdfObject::Array(PdfArray(vec![
368 PdfObject::Integer(w1 as i64),
369 PdfObject::Integer(w2 as i64),
370 PdfObject::Integer(w3 as i64),
371 ])),
372 );
373
374 let size = self.entries.iter().map(|(n, _)| n + 1).max().unwrap_or(0);
376 dict.insert("Size".to_string(), PdfObject::Integer(size as i64));
377
378 if !self.entries.is_empty() {
380 let first = self.entries[0].0;
381 let count = self.entries.len() as u32;
382 if first != 0 {
383 dict.insert(
384 "Index".to_string(),
385 PdfObject::Array(PdfArray(vec![
386 PdfObject::Integer(first as i64),
387 PdfObject::Integer(count as i64),
388 ])),
389 );
390 }
391 }
392
393 dict.insert(
395 "Length".to_string(),
396 PdfObject::Integer(stream_data.len() as i64),
397 );
398
399 let compressed = compress_data(&stream_data)?;
401 dict.insert(
402 "Filter".to_string(),
403 PdfObject::Name(PdfName("FlateDecode".to_string())),
404 );
405
406 Ok((dict, compressed))
407 }
408}
409
410fn bytes_needed(value: u64) -> usize {
412 if value == 0 {
413 1
414 } else {
415 ((64 - value.leading_zeros()).div_ceil(8)) as usize
416 }
417}
418
419fn write_field(output: &mut Vec<u8>, value: u64, width: usize) {
421 for i in (0..width).rev() {
422 output.push((value >> (i * 8)) as u8);
423 }
424}
425
426fn compress_data(data: &[u8]) -> ParseResult<Vec<u8>> {
428 use flate2::write::ZlibEncoder;
429 use flate2::Compression;
430 use std::io::Write;
431
432 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
433 encoder
434 .write_all(data)
435 .map_err(|e| ParseError::StreamDecodeError(format!("Compression failed: {e}")))?;
436 encoder
437 .finish()
438 .map_err(|e| ParseError::StreamDecodeError(format!("Compression failed: {e}")))
439}
440
441#[cfg(test)]
442mod tests {
443 use super::*;
444 use crate::parser::filters::{apply_filter, Filter};
445
446 #[test]
447 fn test_read_field() {
448 assert_eq!(read_field(&[0x00]), 0);
449 assert_eq!(read_field(&[0xFF]), 255);
450 assert_eq!(read_field(&[0x01, 0x23]), 0x0123);
451 assert_eq!(read_field(&[0x12, 0x34, 0x56]), 0x123456);
452 }
453
454 #[test]
455 fn test_write_field() {
456 let mut data = Vec::new();
457 write_field(&mut data, 0x1234, 2);
458 assert_eq!(data, vec![0x12, 0x34]);
459
460 data.clear();
461 write_field(&mut data, 0xFF, 1);
462 assert_eq!(data, vec![0xFF]);
463
464 data.clear();
465 write_field(&mut data, 0x123456, 3);
466 assert_eq!(data, vec![0x12, 0x34, 0x56]);
467 }
468
469 #[test]
470 fn test_bytes_needed() {
471 assert_eq!(bytes_needed(0), 1);
472 assert_eq!(bytes_needed(0xFF), 1);
473 assert_eq!(bytes_needed(0x100), 2);
474 assert_eq!(bytes_needed(0xFFFF), 2);
475 assert_eq!(bytes_needed(0x10000), 3);
476 assert_eq!(bytes_needed(0xFFFFFF), 3);
477 assert_eq!(bytes_needed(0x1000000), 4);
478 }
479
480 #[test]
481 fn test_xref_stream_builder() {
482 let mut builder = XRefStreamBuilder::new();
483
484 builder.add_entry(
486 0,
487 XRefEntry::Free {
488 next_free_object: 0,
489 generation: 65535,
490 },
491 );
492
493 builder.add_entry(
494 1,
495 XRefEntry::InUse {
496 offset: 15,
497 generation: 0,
498 },
499 );
500
501 builder.add_entry(
502 2,
503 XRefEntry::Compressed {
504 stream_object_number: 5,
505 index_within_stream: 0,
506 },
507 );
508
509 let result = builder.build();
510 assert!(result.is_ok());
511
512 let (dict, _data) = result.unwrap();
513
514 assert_eq!(
516 dict.get("Type")
517 .and_then(|o| o.as_name())
518 .map(|n| n.0.as_str()),
519 Some("XRef")
520 );
521 assert!(dict.get("W").is_some());
522 assert!(dict.get("Size").is_some());
523 assert!(dict.get("Filter").is_some());
524 }
525
526 fn decoded_xref_stream_inputs() -> (PdfDictionary, Vec<u8>) {
532 let mut builder = XRefStreamBuilder::new();
533 builder.add_entry(
534 0,
535 XRefEntry::Free {
536 next_free_object: 0,
537 generation: 65535,
538 },
539 );
540 builder.add_entry(
541 1,
542 XRefEntry::InUse {
543 offset: 15,
544 generation: 0,
545 },
546 );
547 builder.add_entry(
548 2,
549 XRefEntry::Compressed {
550 stream_object_number: 5,
551 index_within_stream: 0,
552 },
553 );
554
555 let (dict, compressed) = builder.build().unwrap();
556 let decoded = apply_filter(&compressed, Filter::FlateDecode).unwrap();
562 (dict, decoded)
563 }
564
565 fn assert_three_entries(stream: &XRefStream) {
566 let entries = stream.to_xref_entries().unwrap();
567 assert_eq!(entries.len(), 3, "must recover all three xref entries");
568 assert!(matches!(entries[0].1, XRefEntry::Free { .. }));
569 assert!(matches!(
570 entries[1].1,
571 XRefEntry::InUse {
572 offset: 15,
573 generation: 0
574 }
575 ));
576 assert!(matches!(
577 entries[2].1,
578 XRefEntry::Compressed {
579 stream_object_number: 5,
580 index_within_stream: 0
581 }
582 ));
583 }
584
585 #[test]
590 fn parse_treats_input_as_already_decoded_flate() {
591 let (dict, decoded) = decoded_xref_stream_inputs();
592 assert!(
593 dict.get("Filter").is_some(),
594 "precondition: dict still carries /Filter, as in production"
595 );
596
597 let mut reader = std::io::Cursor::new(Vec::<u8>::new());
598 let stream =
599 XRefStream::parse(&mut reader, dict, decoded, &ParseOptions::default()).unwrap();
600
601 assert_three_entries(&stream);
602 }
603
604 #[test]
605 fn test_xref_entry_parsing() {
606 let entry_data = vec![
609 1, 0x03, 0xE8, 0, ];
613
614 let xref_stream = XRefStream {
615 dict: PdfDictionary::new(),
616 data: entry_data,
617 widths: vec![1, 2, 1],
618 index: vec![(10, 1)],
619 };
620
621 let entries = xref_stream.to_xref_entries().unwrap();
622 assert_eq!(entries.len(), 1);
623
624 let (obj_num, entry) = &entries[0];
625 assert_eq!(*obj_num, 10);
626
627 match entry {
628 XRefEntry::InUse { offset, generation } => {
629 assert_eq!(*offset, 1000);
630 assert_eq!(*generation, 0);
631 }
632 _ => panic!("Expected InUse entry"),
633 }
634 }
635
636 #[test]
637 fn test_compressed_entry_parsing() {
638 let entry_data = vec![
640 2, 0x00, 0x05, 0x00, 0x03, ];
644
645 let xref_stream = XRefStream {
646 dict: PdfDictionary::new(),
647 data: entry_data,
648 widths: vec![1, 2, 2],
649 index: vec![(20, 1)],
650 };
651
652 let entries = xref_stream.to_xref_entries().unwrap();
653 assert_eq!(entries.len(), 1);
654
655 let (obj_num, entry) = &entries[0];
656 assert_eq!(*obj_num, 20);
657
658 match entry {
659 XRefEntry::Compressed {
660 stream_object_number,
661 index_within_stream,
662 } => {
663 assert_eq!(*stream_object_number, 5);
664 assert_eq!(*index_within_stream, 3);
665 }
666 _ => panic!("Expected Compressed entry"),
667 }
668 }
669
670 #[test]
671 fn test_multiple_index_ranges() {
672 let entry_data = vec![
674 0, 0, 0, 0xFF, 0xFF, 1, 0, 0x0A, 0, 0, 1, 0, 0x14, 0, 0, 1, 0, 0x1E, 0, 0, ];
681
682 let xref_stream = XRefStream {
683 dict: PdfDictionary::new(),
684 data: entry_data,
685 widths: vec![1, 2, 2],
686 index: vec![(0, 2), (10, 2)],
687 };
688
689 let entries = xref_stream.to_xref_entries().unwrap();
690 assert_eq!(entries.len(), 4);
691
692 assert_eq!(entries[0].0, 0);
694 assert_eq!(entries[1].0, 1);
695 assert_eq!(entries[2].0, 10);
696 assert_eq!(entries[3].0, 11);
697 }
698}