1use std::fs::File;
2use std::io::{BufWriter, Result, Write};
3use std::path::Path;
4use std::vec;
5
6use super::Object::*;
7use super::{Dictionary, Document, Object, Stream, StringFormat};
8use crate::{IncrementalDocument, xref::*};
9
10impl Document {
11 #[inline]
13 pub fn save<P: AsRef<Path>>(&mut self, path: P) -> Result<File> {
14 let mut file = BufWriter::new(File::create(path)?);
15 self.save_internal(&mut file)?;
16 Ok(file.into_inner()?)
17 }
18
19 #[inline]
21 pub fn save_to<W: Write>(&mut self, target: &mut W) -> Result<()> {
22 self.save_internal(target)
23 }
24
25 pub fn save_with_options<W: Write>(
27 &mut self,
28 target: &mut W,
29 options: crate::SaveOptions,
30 ) -> Result<()> {
31 if options.use_object_streams {
32 self.save_with_object_streams(target, options)
33 } else {
34 self.save_internal(target)
35 }
36 }
37
38 pub fn save_modern<W: Write>(&mut self, target: &mut W) -> Result<()> {
40 let options = crate::SaveOptions {
41 use_object_streams: true,
42 use_xref_streams: true,
43 ..Default::default()
44 };
45 self.save_with_options(target, options)
46 }
47
48 fn save_internal<W: Write>(&mut self, target: &mut W) -> Result<()> {
49 let mut target = CountingWrite {
50 inner: target,
51 bytes_written: 0,
52 };
53
54 let mut xref = Xref::new(self.max_id + 1, self.reference_table.cross_reference_type);
55 writeln!(target, "%PDF-{}", self.version)?;
56
57 Writer::write_binary_mark(&mut target, &self.binary_mark)?;
58
59 for (&(id, generation), object) in &self.objects {
60 if object
61 .type_name()
62 .map(|name| {
63 [
64 b"ObjStm".as_slice(),
65 b"XRef".as_slice(),
66 b"Linearized".as_slice(),
67 ]
68 .contains(&name)
69 })
70 .ok()
71 != Some(true)
72 {
73 Writer::write_indirect_object(&mut target, id, generation, object, &mut xref)?;
74 }
75 }
76
77 let xref_start = target.bytes_written;
78
79 match xref.cross_reference_type {
81 XrefType::CrossReferenceTable => {
82 Writer::write_xref(&mut target, &xref)?;
83 self.write_trailer(&mut target)?;
84 }
85 XrefType::CrossReferenceStream => {
86 self.write_cross_reference_stream(&mut target, &mut xref, xref_start as u32)?;
88 }
89 }
90 write!(target, "\nstartxref\n{xref_start}\n%%EOF")?;
92
93 Ok(())
94 }
95
96 fn save_with_object_streams<W: Write>(
98 &mut self,
99 target: &mut W,
100 options: crate::SaveOptions,
101 ) -> Result<()> {
102 use crate::ObjectStream;
103 use std::collections::HashMap;
104
105 let mut target = CountingWrite {
106 inner: target,
107 bytes_written: 0,
108 };
109
110 if self.version.as_str() < "1.5" {
112 self.version = "1.5".to_string();
113 }
114
115 if options.use_xref_streams {
117 self.reference_table.cross_reference_type = XrefType::CrossReferenceStream;
118 }
119
120 let mut xref = Xref::new(self.max_id + 1, self.reference_table.cross_reference_type);
121 writeln!(target, "%PDF-{}", self.version)?;
122 Writer::write_binary_mark(&mut target, &self.binary_mark)?;
123
124 let mut object_streams: Vec<crate::ObjectStream> = Vec::new();
126 let mut objects_to_write_directly = Vec::new();
127 let mut object_to_stream_map = HashMap::new();
128
129 for (&(id, generation), object) in &self.objects {
131 if let Object::Stream(stream) = object {
133 if let Ok(type_obj) = stream.dict.get(b"Type") {
134 if let Ok(type_name) = type_obj.as_name() {
135 if type_name == b"ObjStm" {
136 continue; }
138 }
139 }
140 }
141
142 if generation == 0 && ObjectStream::can_be_compressed((id, generation), object, self) {
143 let stream_index = object_streams.len().saturating_sub(1);
146
147 if object_streams.is_empty()
148 || object_streams[stream_index].object_count()
149 >= options.object_stream_config.max_objects_per_stream
150 {
151 let new_stream = ObjectStream::builder()
153 .max_objects(options.object_stream_config.max_objects_per_stream)
154 .compression_level(options.object_stream_config.compression_level)
155 .build();
156 object_streams.push(new_stream);
157 }
158
159 let stream_index = object_streams.len() - 1;
160 object_streams[stream_index]
161 .add_object((id, generation), object.clone())
162 .ok();
163 object_to_stream_map.insert((id, generation), stream_index);
164 } else {
165 objects_to_write_directly.push(((id, generation), object));
167 }
168 }
169
170 for ((id, generation), object) in objects_to_write_directly {
172 Writer::write_indirect_object(&mut target, id, generation, object, &mut xref)?;
173 }
174
175 let mut stream_count = 0;
177 for obj_stream in object_streams.into_iter() {
178 let stream_id = self.max_id + 1 + stream_count;
179 let stream_obj = obj_stream
180 .to_stream_object()
181 .map_err(std::io::Error::other)?;
182
183 let mut sorted_objects: Vec<_> = obj_stream.objects.keys().cloned().collect();
186 sorted_objects.sort_by_key(|id| *id);
187 for (index_in_stream, (obj_id, _gen)) in sorted_objects.iter().enumerate() {
188 xref.insert(
189 *obj_id,
190 XrefEntry::Compressed {
191 container: stream_id,
192 index: index_in_stream as u16,
193 },
194 );
195 }
196
197 Writer::write_indirect_object(
199 &mut target,
200 stream_id,
201 0,
202 &Object::Stream(stream_obj),
203 &mut xref,
204 )?;
205 stream_count += 1;
206 }
207
208 self.max_id += stream_count;
210
211 let xref_start = target.bytes_written;
212
213 match xref.cross_reference_type {
215 XrefType::CrossReferenceTable => {
216 Writer::write_xref(&mut target, &xref)?;
217 self.write_trailer(&mut target)?;
218 }
219 XrefType::CrossReferenceStream => {
220 self.write_cross_reference_stream(&mut target, &mut xref, xref_start as u32)?;
221 }
222 }
223
224 write!(target, "\nstartxref\n{xref_start}\n%%EOF")?;
225 Ok(())
226 }
227
228 fn write_cross_reference_stream<W: Write>(
233 &mut self,
234 file: &mut CountingWrite<&mut W>,
235 xref: &mut Xref,
236 xref_start: u32,
237 ) -> Result<()> {
238 self.max_id += 1;
240 let new_obj_id_for_crs = self.max_id;
241 xref.insert(
242 new_obj_id_for_crs,
243 XrefEntry::Normal {
244 offset: xref_start,
245 generation: 0,
246 },
247 );
248 self.trailer.set("Type", Name(b"XRef".to_vec()));
249 self.trailer.set("Size", i64::from(self.max_id + 1));
251 self.trailer
255 .set("W", Array(vec![Integer(1), Integer(4), Integer(2)]));
256 let filter = XRefStreamFilter::None;
259 let (stream, stream_length, indexes) = Writer::create_xref_steam(xref, filter)?;
260 self.trailer.set("Index", indexes);
261
262 if filter == XRefStreamFilter::ASCIIHexDecode {
263 self.trailer.set("Filter", Name(b"ASCIIHexDecode".to_vec()));
264 } else {
265 self.trailer.remove(b"Filter");
266 }
267
268 self.trailer.set("Length", stream_length as i64);
269
270 let trailer = &self.trailer;
271 let cross_reference_stream = Stream(Stream {
272 dict: trailer.clone(),
273 allows_compression: true,
274 content: stream,
275 start_position: None,
276 });
277 Writer::write_indirect_object(file, new_obj_id_for_crs, 0, &cross_reference_stream, xref)?;
280
281 Ok(())
282 }
283
284 fn write_trailer(&mut self, file: &mut dyn Write) -> Result<()> {
285 self.trailer.set("Size", i64::from(self.max_id + 1));
286 file.write_all(b"trailer\n")?;
287 Writer::write_dictionary(file, &self.trailer)?;
288 Ok(())
289 }
290}
291
292impl IncrementalDocument {
293 #[inline]
295 pub fn save<P: AsRef<Path>>(&mut self, path: P) -> Result<File> {
296 let mut file = BufWriter::new(File::create(path)?);
297 self.save_internal(&mut file)?;
298 Ok(file.into_inner()?)
299 }
300
301 #[inline]
303 pub fn save_to<W: Write>(&mut self, target: &mut W) -> Result<()> {
304 self.save_internal(target)
305 }
306
307 fn save_internal<W: Write>(&mut self, target: &mut W) -> Result<()> {
308 let mut target = CountingWrite {
309 inner: target,
310 bytes_written: 0,
311 };
312
313 let prev_document_bytes = self.get_prev_documents_bytes();
315 target.inner.write_all(prev_document_bytes)?;
316 target.bytes_written += prev_document_bytes.len();
317
318 let mut xref = Xref::new(
320 self.new_document.max_id + 1,
321 self.get_prev_documents()
322 .reference_table
323 .cross_reference_type,
324 );
325
326 if let Some(last_byte) = prev_document_bytes.last() {
327 if *last_byte != b'\n' {
328 writeln!(target)?;
330 }
331 }
332 writeln!(target, "%PDF-{}", self.new_document.version)?;
333
334 Writer::write_binary_mark(&mut target, &self.new_document.binary_mark)?;
335
336 for (&(id, generation), object) in &self.new_document.objects {
337 if object
338 .type_name()
339 .map(|name| {
340 [
341 b"ObjStm".as_slice(),
342 b"XRef".as_slice(),
343 b"Linearized".as_slice(),
344 ]
345 .contains(&name)
346 })
347 .ok()
348 != Some(true)
349 {
350 Writer::write_indirect_object(&mut target, id, generation, object, &mut xref)?;
351 }
352 }
353
354 let xref_start = target.bytes_written;
355
356 match xref.cross_reference_type {
358 XrefType::CrossReferenceTable => {
359 Writer::write_xref(&mut target, &xref)?;
360 self.new_document.write_trailer(&mut target)?;
361 }
362 XrefType::CrossReferenceStream => {
363 self.new_document.write_cross_reference_stream(
365 &mut target,
366 &mut xref,
367 xref_start as u32,
368 )?;
369 }
370 }
371 write!(target, "\nstartxref\n{xref_start}\n%%EOF")?;
373
374 Ok(())
375 }
376}
377
378pub struct Writer;
379
380#[derive(Debug, PartialEq, Eq, Clone, Copy)]
381pub enum XRefStreamFilter {
382 ASCIIHexDecode,
383 _FlateDecode, None,
385}
386
387impl Writer {
388 fn need_separator(object: &Object) -> bool {
389 matches!(
390 *object,
391 Null | Boolean(_) | Integer(_) | Real(_) | Reference(_)
392 )
393 }
394
395 fn need_end_separator(object: &Object) -> bool {
396 matches!(
397 *object,
398 Null | Boolean(_) | Integer(_) | Real(_) | Name(_) | Reference(_) | Object::Stream(_)
399 )
400 }
401
402 fn write_xref(file: &mut dyn Write, xref: &Xref) -> Result<()> {
406 file.write_all(b"xref\n")?;
407
408 let mut xref_section = XrefSection::new(0);
409 xref_section.add_unusable_free_entry();
411
412 for obj_id in 1..xref.size {
413 if xref_section.is_empty() {
415 xref_section = XrefSection::new(obj_id);
416 }
417 if let Some(entry) = xref.get(obj_id) {
418 match *entry {
419 XrefEntry::Normal { offset, generation } => {
420 xref_section.add_entry(XrefEntry::Normal { offset, generation });
422 }
423 XrefEntry::Compressed {
424 container: _,
425 index: _,
426 } => {
427 xref_section.add_unusable_free_entry();
428 }
429 XrefEntry::Free => {
430 xref_section.add_entry(XrefEntry::Free);
431 }
432 XrefEntry::UnusableFree => {
433 xref_section.add_unusable_free_entry();
434 }
435 }
436 } else {
437 if !xref_section.is_empty() {
439 xref_section.write_xref_section(file)?;
440 xref_section = XrefSection::new(obj_id);
441 }
442 }
443 }
444 if !xref_section.is_empty() {
446 xref_section.write_xref_section(file)?;
447 }
448 Ok(())
449 }
450
451 fn create_xref_steam(
453 xref: &Xref,
454 filter: XRefStreamFilter,
455 ) -> Result<(Vec<u8>, usize, Object)> {
456 let mut xref_sections = Vec::new();
457 let mut xref_section = XrefSection::new(0);
458
459 for obj_id in 1..xref.size + 1 {
460 if xref_section.is_empty() {
462 xref_section = XrefSection::new(obj_id);
463 }
464 if let Some(entry) = xref.get(obj_id) {
465 xref_section.add_entry(entry.clone());
466 } else {
467 if !xref_section.is_empty() {
469 xref_sections.push(xref_section);
470 xref_section = XrefSection::new(obj_id);
471 }
472 }
473 }
474 if !xref_section.is_empty() {
476 xref_sections.push(xref_section);
477 }
478
479 let mut xref_stream = Vec::new();
480 let mut xref_index = Vec::new();
481
482 for section in xref_sections {
483 xref_index.push(Integer(section.starting_id as i64));
485 xref_index.push(Integer(section.entries.len() as i64));
486 for (obj_id, entry) in (section.starting_id..).zip(section.entries) {
488 match entry {
489 XrefEntry::Free => {
490 xref_stream.push(0);
492 xref_stream.extend(obj_id.to_be_bytes());
493 xref_stream.extend(vec![0, 0]); }
495 XrefEntry::UnusableFree => {
496 xref_stream.push(0);
498 xref_stream.extend(obj_id.to_be_bytes());
499 xref_stream.extend(65535_u16.to_be_bytes());
500 }
501 XrefEntry::Normal { offset, generation } => {
502 xref_stream.push(1);
504 xref_stream.extend(offset.to_be_bytes());
505 xref_stream.extend(generation.to_be_bytes());
506 }
507 XrefEntry::Compressed { container, index } => {
508 xref_stream.push(2);
510 xref_stream.extend(container.to_be_bytes());
511 xref_stream.extend(index.to_be_bytes());
512 }
513 }
514 }
515 }
516
517 let stream_length = xref_stream.len();
519
520 if filter == XRefStreamFilter::ASCIIHexDecode {
521 xref_stream = xref_stream
522 .iter()
523 .flat_map(|c| format!("{c:02X}").as_bytes().to_vec())
524 .collect::<Vec<u8>>();
525 }
526
527 Ok((xref_stream, stream_length, Array(xref_index)))
528 }
529
530 fn write_indirect_object<W: Write>(
531 file: &mut CountingWrite<&mut W>,
532 id: u32,
533 generation: u16,
534 object: &Object,
535 xref: &mut Xref,
536 ) -> Result<()> {
537 let offset = file.bytes_written as u32;
538 xref.insert(id, XrefEntry::Normal { offset, generation });
539 write!(
540 file,
541 "{} {} obj\n{}",
542 id,
543 generation,
544 if Writer::need_separator(object) {
545 " "
546 } else {
547 ""
548 }
549 )?;
550 Writer::write_object(file, object)?;
551 write!(
552 file,
553 "{}\nendobj\n",
554 if Writer::need_end_separator(object) {
555 " "
556 } else {
557 ""
558 }
559 )?;
560 Ok(())
561 }
562
563 pub fn write_object(file: &mut dyn Write, object: &Object) -> Result<()> {
564 match object {
565 Null => file.write_all(b"null"),
566 Boolean(value) => {
567 if *value {
568 file.write_all(b"true")
569 } else {
570 file.write_all(b"false")
571 }
572 }
573 Integer(value) => {
574 let mut buf = itoa::Buffer::new();
575 file.write_all(buf.format(*value).as_bytes())
576 }
577 Real(value) => {
578 let v = *value;
579 if !v.is_finite() {
580 file.write_all(b"0")
584 } else {
585 let s = format!("{v}");
586 if s.contains('.') {
587 file.write_all(s.as_bytes())
588 } else {
589 file.write_all(s.as_bytes())?;
594 file.write_all(b".0")
595 }
596 }
597 }
598 Name(name) => Writer::write_name(file, name),
599 String(text, format) => Writer::write_string(file, text, format),
600 Array(array) => Writer::write_array(file, array),
601 Object::Dictionary(dict) => Writer::write_dictionary(file, dict),
602 Object::Stream(stream) => Writer::write_stream(file, stream),
603 Reference(id) => write!(file, "{} {} R", id.0, id.1),
604 }
605 }
606
607 fn write_name(file: &mut dyn Write, name: &[u8]) -> Result<()> {
608 file.write_all(b"/")?;
609 for &byte in name {
610 if b" \t\n\r\x0C()<>[]{}/%#".contains(&byte) || !(33..=126).contains(&byte) {
613 write!(file, "#{byte:02X}")?;
614 } else {
615 file.write_all(&[byte])?;
616 }
617 }
618 Ok(())
619 }
620
621 fn write_string(file: &mut dyn Write, text: &[u8], format: &StringFormat) -> Result<()> {
622 match *format {
623 StringFormat::Literal => {
629 let mut escape_indice = Vec::new();
630 let mut parentheses = Vec::new();
631 for (index, &byte) in text.iter().enumerate() {
632 match byte {
633 b'(' => parentheses.push(index),
634 b')' => {
635 if !parentheses.is_empty() {
636 parentheses.pop();
637 } else {
638 escape_indice.push(index);
639 }
640 }
641 b'\\' | b'\r' => escape_indice.push(index),
642 _ => continue,
643 }
644 }
645 escape_indice.append(&mut parentheses);
646
647 file.write_all(b"(")?;
648 if !escape_indice.is_empty() {
649 for (index, &byte) in text.iter().enumerate() {
650 if escape_indice.contains(&index) {
651 file.write_all(b"\\")?;
652 file.write_all(&[if byte == b'\r' { b'r' } else { byte }])?;
653 } else {
654 file.write_all(&[byte])?;
655 }
656 }
657 } else {
658 file.write_all(text)?;
659 }
660 file.write_all(b")")?;
661 }
662 StringFormat::Hexadecimal => {
663 file.write_all(b"<")?;
664 for &byte in text {
665 write!(file, "{byte:02X}")?;
666 }
667 file.write_all(b">")?;
668 }
669 }
670 Ok(())
671 }
672
673 fn write_array(file: &mut dyn Write, array: &[Object]) -> Result<()> {
674 file.write_all(b"[")?;
675 let mut first = true;
676 for object in array {
677 if first {
678 first = false;
679 } else if Writer::need_separator(object) {
680 file.write_all(b" ")?;
681 }
682 Writer::write_object(file, object)?;
683 }
684 file.write_all(b"]")?;
685 Ok(())
686 }
687
688 fn write_dictionary(file: &mut dyn Write, dictionary: &Dictionary) -> Result<()> {
689 file.write_all(b"<<")?;
690 for (key, value) in dictionary {
691 Writer::write_name(file, key)?;
692 if Writer::need_separator(value) {
693 file.write_all(b" ")?;
694 }
695 Writer::write_object(file, value)?;
696 }
697 file.write_all(b">>")?;
698 Ok(())
699 }
700
701 fn write_stream(file: &mut dyn Write, stream: &Stream) -> Result<()> {
702 let actual_len = stream.content.len() as i64;
707 let length_ok = matches!(
708 stream.dict.get(b"Length").ok(),
709 Some(&Object::Integer(l)) if l == actual_len
710 );
711 if length_ok {
712 Writer::write_dictionary(file, &stream.dict)?;
713 } else {
714 let mut dict = stream.dict.clone();
715 dict.set("Length", actual_len);
716 Writer::write_dictionary(file, &dict)?;
717 }
718 file.write_all(b"stream\n")?;
719 file.write_all(&stream.content)?;
720 file.write_all(b"\nendstream")?;
721 Ok(())
722 }
723
724 fn write_binary_mark(file: &mut dyn Write, binary_mark: &[u8]) -> Result<()> {
729 if binary_mark.iter().all(|&byte| byte >= 128) {
730 file.write_all(b"%")?;
731 file.write_all(binary_mark)?;
732 file.write_all(b"\n")?;
733 } else {
734 return Err(std::io::Error::new(
735 std::io::ErrorKind::InvalidData,
736 "Invalid binary mark",
737 ));
738 }
739
740 Ok(())
741 }
742}
743
744pub struct CountingWrite<W: Write> {
745 inner: W,
746 bytes_written: usize,
747}
748
749impl<W: Write> Write for CountingWrite<W> {
750 #[inline]
751 fn write(&mut self, buffer: &[u8]) -> Result<usize> {
752 let result = self.inner.write(buffer);
753 if let Ok(bytes) = result {
754 self.bytes_written += bytes;
755 }
756 result
757 }
758
759 #[inline]
760 fn write_all(&mut self, buffer: &[u8]) -> Result<()> {
761 self.bytes_written += buffer.len();
762 self.inner.write_all(buffer)
765 }
766
767 #[inline]
768 fn flush(&mut self) -> Result<()> {
769 self.inner.flush()
770 }
771}
772
773#[test]
774fn save_document() {
775 let mut doc = Document::with_version("1.5");
776 doc.objects.insert((1, 0), Null);
777 doc.objects.insert((2, 0), Boolean(true));
778 doc.objects.insert((3, 0), Integer(3));
779 doc.objects.insert((4, 0), Real(0.5));
780 doc.objects.insert(
781 (5, 0),
782 String("text((\r)".as_bytes().to_vec(), StringFormat::Literal),
783 );
784 doc.objects.insert(
785 (6, 0),
786 String("text((\r)".as_bytes().to_vec(), StringFormat::Hexadecimal),
787 );
788 doc.objects.insert((7, 0), Name(b"name \t".to_vec()));
789 doc.objects.insert((8, 0), Reference((1, 0)));
790 doc.objects
791 .insert((9, 2), Array(vec![Integer(1), Integer(2), Integer(3)]));
792 doc.objects.insert(
793 (11, 0),
794 Stream(Stream::new(Dictionary::new(), vec![0x41, 0x42, 0x43])),
795 );
796 let mut dict = Dictionary::new();
797 dict.set("A", Null);
798 dict.set("B", false);
799 dict.set("C", Name(b"name".to_vec()));
800 doc.objects.insert((12, 0), Object::Dictionary(dict));
801 doc.max_id = 12;
802
803 let temp_dir = tempfile::tempdir().unwrap();
805 let file_path = temp_dir.path().join("test_0_save.pdf");
806 doc.save(&file_path).unwrap();
807 assert!(file_path.exists());
809 assert!(file_path.is_file());
811 assert!(file_path.metadata().unwrap().len() > 400);
813}