1use crate::document::Document;
2use crate::error::{PdfError, Result};
3use crate::objects::{Dictionary, Object, ObjectId};
4use crate::text::fonts::embedding::CjkFontType;
5use crate::text::fonts::truetype::CmapSubtable;
6use crate::writer::{ObjectStreamConfig, ObjectStreamWriter, XRefStreamWriter};
7use chrono::{DateTime, Utc};
8use std::collections::HashMap;
9use std::io::{BufWriter, Write};
10use std::path::Path;
11
12#[derive(Debug, Clone)]
14pub struct WriterConfig {
15 pub use_xref_streams: bool,
17 pub use_object_streams: bool,
19 pub pdf_version: String,
21 pub compress_streams: bool,
23 pub incremental_update: bool,
25}
26
27impl Default for WriterConfig {
28 fn default() -> Self {
29 Self {
30 use_xref_streams: false,
31 use_object_streams: false,
32 pdf_version: "1.7".to_string(),
33 compress_streams: true,
34 incremental_update: false,
35 }
36 }
37}
38
39impl WriterConfig {
40 pub fn modern() -> Self {
42 Self {
43 use_xref_streams: true,
44 use_object_streams: true,
45 pdf_version: "1.5".to_string(),
46 compress_streams: true,
47 incremental_update: false,
48 }
49 }
50
51 pub fn legacy() -> Self {
53 Self {
54 use_xref_streams: false,
55 use_object_streams: false,
56 pdf_version: "1.4".to_string(),
57 compress_streams: true,
58 incremental_update: false,
59 }
60 }
61
62 pub fn incremental() -> Self {
64 Self {
65 use_xref_streams: false,
66 use_object_streams: false,
67 pdf_version: "1.4".to_string(),
68 compress_streams: true,
69 incremental_update: true,
70 }
71 }
72}
73
74fn escape_pdf_string_bytes(input: &[u8]) -> Vec<u8> {
99 let mut out = Vec::with_capacity(input.len());
100 for &byte in input {
101 match byte {
102 b'\\' => out.extend_from_slice(b"\\\\"),
103 b'(' => out.extend_from_slice(b"\\("),
104 b')' => out.extend_from_slice(b"\\)"),
105 other => out.push(other),
106 }
107 }
108 out
109}
110
111pub struct PdfWriter<W: Write> {
112 writer: W,
113 xref_positions: HashMap<ObjectId, u64>,
114 current_position: u64,
115 next_object_id: u32,
116 catalog_id: Option<ObjectId>,
118 pages_id: Option<ObjectId>,
119 info_id: Option<ObjectId>,
120 #[allow(dead_code)]
122 field_widget_map: HashMap<String, Vec<ObjectId>>, #[allow(dead_code)]
124 field_id_map: HashMap<String, ObjectId>, form_field_ids: Vec<ObjectId>, page_ids: Vec<ObjectId>, config: WriterConfig,
129 document_used_chars_by_font: std::collections::HashMap<String, std::collections::HashSet<char>>,
135 buffered_objects: HashMap<ObjectId, Vec<u8>>,
137 compressed_object_map: HashMap<ObjectId, (ObjectId, u32)>, prev_xref_offset: Option<u64>,
140 base_pdf_size: Option<u64>,
141 encrypt_obj_id: Option<ObjectId>,
143 file_id: Option<Vec<u8>>,
144 encryption_state: Option<WriterEncryptionState>,
145 pending_encrypt_dict: Option<Dictionary>,
146 form_field_placeholder_map: HashMap<crate::objects::ObjectReference, ObjectId>,
159 form_manager_field_refs: Vec<crate::objects::ObjectReference>,
160}
161
162struct WriterEncryptionState {
164 encryptor: crate::encryption::ObjectEncryptor,
165}
166
167impl<W: Write> PdfWriter<W> {
168 pub fn new_with_writer(writer: W) -> Self {
169 Self::with_config(writer, WriterConfig::default())
170 }
171
172 pub fn with_config(writer: W, config: WriterConfig) -> Self {
173 Self {
174 writer,
175 xref_positions: HashMap::new(),
176 current_position: 0,
177 next_object_id: 1, catalog_id: None,
179 pages_id: None,
180 info_id: None,
181 field_widget_map: HashMap::new(),
182 field_id_map: HashMap::new(),
183 form_field_ids: Vec::new(),
184 page_ids: Vec::new(),
185 config,
186 document_used_chars_by_font: std::collections::HashMap::new(),
187 buffered_objects: HashMap::new(),
188 compressed_object_map: HashMap::new(),
189 prev_xref_offset: None,
190 base_pdf_size: None,
191 encrypt_obj_id: None,
192 file_id: None,
193 encryption_state: None,
194 pending_encrypt_dict: None,
195 form_field_placeholder_map: HashMap::new(),
196 form_manager_field_refs: Vec::new(),
197 }
198 }
199
200 pub fn write_document(&mut self, document: &mut Document) -> Result<()> {
201 if !document.used_characters_by_font.is_empty() {
203 self.document_used_chars_by_font = document.used_characters_by_font.clone();
204 }
205
206 self.write_header()?;
207
208 self.catalog_id = Some(self.allocate_object_id());
210 self.pages_id = Some(self.allocate_object_id());
211 self.info_id = Some(self.allocate_object_id());
212
213 if let Some(ref encryption) = document.encryption {
216 self.init_encryption(encryption)?;
217 }
218
219 let font_refs = self.write_fonts(document)?;
221
222 self.preallocate_form_manager_fields(document)?;
229
230 self.write_pages(document, &font_refs)?;
232
233 self.write_form_fields(document)?;
235
236 self.write_catalog(document)?;
238
239 self.write_info(document)?;
241
242 self.write_encryption_dict()?;
244
245 if self.config.use_object_streams {
247 self.flush_object_streams()?;
248 }
249
250 let xref_position = self.current_position;
252 if self.config.use_xref_streams {
253 self.write_xref_stream()?;
254 } else {
255 self.write_xref()?;
256 }
257
258 if !self.config.use_xref_streams {
260 self.write_trailer(xref_position)?;
261 }
262
263 if let Ok(()) = self.writer.flush() {
264 }
266 Ok(())
267 }
268
269 pub fn write_incremental_update(
303 &mut self,
304 base_pdf_path: impl AsRef<std::path::Path>,
305 document: &mut Document,
306 ) -> Result<()> {
307 use std::io::{BufReader, Read, Seek, SeekFrom};
308
309 let base_pdf_file = std::fs::File::open(base_pdf_path.as_ref())?;
311 let mut pdf_reader = crate::parser::PdfReader::new(BufReader::new(base_pdf_file))?;
312
313 let base_catalog = pdf_reader.catalog()?;
315
316 let (base_pages_id, base_pages_gen) = base_catalog
318 .get("Pages")
319 .and_then(|obj| {
320 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
321 Some((*id, *gen))
322 } else {
323 None
324 }
325 })
326 .ok_or_else(|| {
327 crate::error::PdfError::InvalidStructure(
328 "Base PDF catalog missing /Pages reference".to_string(),
329 )
330 })?;
331
332 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
334 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
335 base_pages_obj
336 {
337 dict.get("Kids")
338 .and_then(|obj| {
339 if let crate::parser::objects::PdfObject::Array(arr) = obj {
340 Some(
343 arr.0
344 .iter()
345 .filter_map(|item| {
346 if let crate::parser::objects::PdfObject::Reference(id, gen) =
347 item
348 {
349 Some(crate::objects::Object::Reference(
350 crate::objects::ObjectId::new(*id, *gen),
351 ))
352 } else {
353 None
354 }
355 })
356 .collect::<Vec<_>>(),
357 )
358 } else {
359 None
360 }
361 })
362 .unwrap_or_default()
363 } else {
364 Vec::new()
365 };
366
367 let base_page_count = base_pages_kids.len();
369
370 let base_pdf = std::fs::File::open(base_pdf_path.as_ref())?;
372 let mut base_reader = BufReader::new(base_pdf);
373
374 base_reader.seek(SeekFrom::End(-100))?;
376 let mut end_buffer = vec![0u8; 100];
377 let bytes_read = base_reader.read(&mut end_buffer)?;
378 end_buffer.truncate(bytes_read);
379
380 let end_str = String::from_utf8_lossy(&end_buffer);
381 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
382 let after_startxref = &end_str[startxref_pos + 9..];
383
384 let number_str: String = after_startxref
385 .chars()
386 .skip_while(|c| c.is_whitespace())
387 .take_while(|c| c.is_ascii_digit())
388 .collect();
389
390 number_str.parse::<u64>().map_err(|_| {
391 crate::error::PdfError::InvalidStructure(
392 "Could not parse startxref offset".to_string(),
393 )
394 })?
395 } else {
396 return Err(crate::error::PdfError::InvalidStructure(
397 "startxref not found in base PDF".to_string(),
398 ));
399 };
400
401 base_reader.seek(SeekFrom::Start(0))?;
403 let base_size = std::io::copy(&mut base_reader, &mut self.writer)? as u64;
404
405 self.prev_xref_offset = Some(prev_xref);
407 self.base_pdf_size = Some(base_size);
408 self.current_position = base_size;
409
410 if !document.used_characters_by_font.is_empty() {
412 self.document_used_chars_by_font = document.used_characters_by_font.clone();
413 }
414
415 self.catalog_id = Some(self.allocate_object_id());
417 self.pages_id = Some(self.allocate_object_id());
418 self.info_id = Some(self.allocate_object_id());
419
420 let font_refs = self.write_fonts(document)?;
422
423 self.write_pages(document, &font_refs)?;
425
426 self.write_form_fields(document)?;
428
429 let catalog_id = self.get_catalog_id()?;
431 let new_pages_id = self.get_pages_id()?;
432
433 let mut catalog = crate::objects::Dictionary::new();
434 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
435 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
436
437 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
442
443 let mut all_pages_kids = base_pages_kids;
445
446 for page_id in &self.page_ids {
448 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
449 }
450
451 let mut pages_dict = crate::objects::Dictionary::new();
452 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
453 pages_dict.set("Kids", crate::objects::Object::Array(all_pages_kids));
454 pages_dict.set(
455 "Count",
456 crate::objects::Object::Integer((base_page_count + self.page_ids.len()) as i64),
457 );
458
459 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
460
461 self.write_info(document)?;
463
464 let xref_position = self.current_position;
466 self.write_xref()?;
467
468 self.write_trailer(xref_position)?;
470
471 self.writer.flush()?;
472 Ok(())
473 }
474
475 pub fn write_incremental_with_page_replacement(
541 &mut self,
542 base_pdf_path: impl AsRef<std::path::Path>,
543 document: &mut Document,
544 ) -> Result<()> {
545 use std::io::Cursor;
546
547 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
549 let base_size = base_pdf_bytes.len() as u64;
550
551 let mut pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
553
554 let base_catalog = pdf_reader.catalog()?;
555
556 let (base_pages_id, base_pages_gen) = base_catalog
557 .get("Pages")
558 .and_then(|obj| {
559 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
560 Some((*id, *gen))
561 } else {
562 None
563 }
564 })
565 .ok_or_else(|| {
566 crate::error::PdfError::InvalidStructure(
567 "Base PDF catalog missing /Pages reference".to_string(),
568 )
569 })?;
570
571 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
572 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
573 base_pages_obj
574 {
575 dict.get("Kids")
576 .and_then(|obj| {
577 if let crate::parser::objects::PdfObject::Array(arr) = obj {
578 Some(
579 arr.0
580 .iter()
581 .filter_map(|item| {
582 if let crate::parser::objects::PdfObject::Reference(id, gen) =
583 item
584 {
585 Some(crate::objects::Object::Reference(
586 crate::objects::ObjectId::new(*id, *gen),
587 ))
588 } else {
589 None
590 }
591 })
592 .collect::<Vec<_>>(),
593 )
594 } else {
595 None
596 }
597 })
598 .unwrap_or_default()
599 } else {
600 Vec::new()
601 };
602
603 let base_page_count = base_pages_kids.len();
604
605 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
607 let end_bytes = &base_pdf_bytes[start_search..];
608 let end_str = String::from_utf8_lossy(end_bytes);
609
610 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
611 let after_startxref = &end_str[startxref_pos + 9..];
612 let number_str: String = after_startxref
613 .chars()
614 .skip_while(|c| c.is_whitespace())
615 .take_while(|c| c.is_ascii_digit())
616 .collect();
617
618 number_str.parse::<u64>().map_err(|_| {
619 crate::error::PdfError::InvalidStructure(
620 "Could not parse startxref offset".to_string(),
621 )
622 })?
623 } else {
624 return Err(crate::error::PdfError::InvalidStructure(
625 "startxref not found in base PDF".to_string(),
626 ));
627 };
628
629 self.writer.write_all(&base_pdf_bytes)?;
631
632 self.prev_xref_offset = Some(prev_xref);
633 self.base_pdf_size = Some(base_size);
634 self.current_position = base_size;
635
636 if !document.used_characters_by_font.is_empty() {
638 self.document_used_chars_by_font = document.used_characters_by_font.clone();
639 }
640
641 self.catalog_id = Some(self.allocate_object_id());
642 self.pages_id = Some(self.allocate_object_id());
643 self.info_id = Some(self.allocate_object_id());
644
645 let font_refs = self.write_fonts(document)?;
646 self.write_pages(document, &font_refs)?;
647 self.write_form_fields(document)?;
648
649 let catalog_id = self.get_catalog_id()?;
651 let new_pages_id = self.get_pages_id()?;
652
653 let mut catalog = crate::objects::Dictionary::new();
654 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
655 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
656 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
657
658 let mut all_pages_kids = Vec::new();
660 let replacement_count = document.pages.len();
661
662 for page_id in &self.page_ids {
664 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
665 }
666
667 if replacement_count < base_page_count {
669 for i in replacement_count..base_page_count {
670 if let Some(page_ref) = base_pages_kids.get(i) {
671 all_pages_kids.push(page_ref.clone());
672 }
673 }
674 }
675
676 let mut pages_dict = crate::objects::Dictionary::new();
677 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
678 pages_dict.set(
679 "Kids",
680 crate::objects::Object::Array(all_pages_kids.clone()),
681 );
682 pages_dict.set(
683 "Count",
684 crate::objects::Object::Integer(all_pages_kids.len() as i64),
685 );
686
687 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
688 self.write_info(document)?;
689
690 let xref_position = self.current_position;
691 self.write_xref()?;
692 self.write_trailer(xref_position)?;
693
694 self.writer.flush()?;
695 Ok(())
696 }
697
698 pub fn write_incremental_with_overlay<P: AsRef<std::path::Path>>(
746 &mut self,
747 base_pdf_path: P,
748 mut overlay_fn: impl FnMut(&mut crate::Page) -> Result<()>,
749 ) -> Result<()> {
750 use std::io::Cursor;
751
752 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
754 let base_size = base_pdf_bytes.len() as u64;
755
756 let pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
758 let parsed_doc = crate::parser::PdfDocument::new(pdf_reader);
759
760 let page_count = parsed_doc.page_count()?;
762
763 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
765 let end_bytes = &base_pdf_bytes[start_search..];
766 let end_str = String::from_utf8_lossy(end_bytes);
767
768 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
769 let after_startxref = &end_str[startxref_pos + 9..];
770 let number_str: String = after_startxref
771 .chars()
772 .skip_while(|c| c.is_whitespace())
773 .take_while(|c| c.is_ascii_digit())
774 .collect();
775
776 number_str.parse::<u64>().map_err(|_| {
777 crate::error::PdfError::InvalidStructure(
778 "Could not parse startxref offset".to_string(),
779 )
780 })?
781 } else {
782 return Err(crate::error::PdfError::InvalidStructure(
783 "startxref not found in base PDF".to_string(),
784 ));
785 };
786
787 self.writer.write_all(&base_pdf_bytes)?;
789
790 self.prev_xref_offset = Some(prev_xref);
791 self.base_pdf_size = Some(base_size);
792 self.current_position = base_size;
793
794 let mut temp_doc = crate::Document::new();
796
797 for page_idx in 0..page_count {
798 let parsed_page = parsed_doc.get_page(page_idx)?;
800 let mut writable_page =
801 crate::Page::from_parsed_with_content(&parsed_page, &parsed_doc)?;
802
803 overlay_fn(&mut writable_page)?;
805
806 temp_doc.add_page(writable_page);
808 }
809
810 if !temp_doc.used_characters_by_font.is_empty() {
813 self.document_used_chars_by_font = temp_doc.used_characters_by_font.clone();
814 }
815
816 self.catalog_id = Some(self.allocate_object_id());
817 self.pages_id = Some(self.allocate_object_id());
818 self.info_id = Some(self.allocate_object_id());
819
820 let font_refs = self.write_fonts(&temp_doc)?;
821 self.write_pages(&temp_doc, &font_refs)?;
822 self.write_form_fields(&mut temp_doc)?;
823
824 let catalog_id = self.get_catalog_id()?;
826 let new_pages_id = self.get_pages_id()?;
827
828 let mut catalog = crate::objects::Dictionary::new();
829 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
830 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
831 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
832
833 let mut all_pages_kids = Vec::new();
835 for page_id in &self.page_ids {
836 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
837 }
838
839 let mut pages_dict = crate::objects::Dictionary::new();
840 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
841 pages_dict.set(
842 "Kids",
843 crate::objects::Object::Array(all_pages_kids.clone()),
844 );
845 pages_dict.set(
846 "Count",
847 crate::objects::Object::Integer(all_pages_kids.len() as i64),
848 );
849
850 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
851 self.write_info(&temp_doc)?;
852
853 let xref_position = self.current_position;
854 self.write_xref()?;
855 self.write_trailer(xref_position)?;
856
857 self.writer.flush()?;
858 Ok(())
859 }
860
861 fn write_header(&mut self) -> Result<()> {
862 let header = format!("%PDF-{}\n", self.config.pdf_version);
863 self.write_bytes(header.as_bytes())?;
864 self.write_bytes(&[b'%', 0xE2, 0xE3, 0xCF, 0xD3, b'\n'])?;
866 Ok(())
867 }
868
869 fn convert_pdf_objects_dict_to_writer(
872 &self,
873 pdf_dict: &crate::pdf_objects::Dictionary,
874 ) -> crate::objects::Dictionary {
875 let mut writer_dict = crate::objects::Dictionary::new();
876
877 for (key, value) in pdf_dict.iter() {
878 let writer_obj = self.convert_pdf_object_to_writer(value);
879 writer_dict.set(key.as_str(), writer_obj);
880 }
881
882 writer_dict
883 }
884
885 fn convert_pdf_object_to_writer(
886 &self,
887 obj: &crate::pdf_objects::Object,
888 ) -> crate::objects::Object {
889 use crate::objects::Object as WriterObj;
890 use crate::pdf_objects::Object as PdfObj;
891
892 match obj {
893 PdfObj::Null => WriterObj::Null,
894 PdfObj::Boolean(b) => WriterObj::Boolean(*b),
895 PdfObj::Integer(i) => WriterObj::Integer(*i),
896 PdfObj::Real(f) => WriterObj::Real(*f),
897 PdfObj::String(s) => {
901 WriterObj::String(crate::parser::objects::decode_text_string(s.as_bytes()))
902 }
903 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
904 PdfObj::Array(arr) => {
905 let items: Vec<WriterObj> = arr
906 .iter()
907 .map(|item| self.convert_pdf_object_to_writer(item))
908 .collect();
909 WriterObj::Array(items)
910 }
911 PdfObj::Dictionary(dict) => {
912 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
913 }
914 PdfObj::Stream(stream) => {
915 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
916 WriterObj::Stream(dict, stream.data.clone())
917 }
918 PdfObj::Reference(id) => {
919 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
920 }
921 }
922 }
923
924 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
925 let catalog_id = self.get_catalog_id()?;
926 let pages_id = self.get_pages_id()?;
927
928 let mut catalog = Dictionary::new();
929 catalog.set("Type", Object::Name("Catalog".to_string()));
930 catalog.set("Pages", Object::Reference(pages_id));
931
932 if let Some(form_manager) = &document.form_manager {
952 if document.acro_form.is_none() {
953 document.acro_form = Some(crate::forms::AcroForm::new());
954 }
955
956 let mut sorted: Vec<(Dictionary, crate::objects::ObjectReference)> = Vec::new();
962 for (name, form_field, placeholder) in form_manager.iter_fields_sorted() {
963 let real_id = *self.form_field_placeholder_map.get(&placeholder).ok_or_else(
964 || {
965 PdfError::Internal(format!(
966 "AcroForm writer internal invariant broken: field '{name}' (placeholder {placeholder}) has no pre-allocated real object id — preallocate_form_manager_fields must run before write_catalog"
967 ))
968 },
969 )?;
970 sorted.push((form_field.field_dict.clone(), real_id));
971 }
972 for (field_dict, real_id) in sorted {
973 self.write_object(real_id, Object::Dictionary(field_dict))?;
974 }
975
976 if let Some(acro) = document.acro_form.as_mut() {
977 for r in &self.form_manager_field_refs {
978 if !acro.fields.contains(r) {
979 acro.fields.push(*r);
980 }
981 }
982 }
983 }
984
985 if let Some(acro_form) = &document.acro_form {
987 let acro_form_id = self.allocate_object_id();
989
990 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
992
993 catalog.set("AcroForm", Object::Reference(acro_form_id));
995 }
996
997 if let Some(outline_tree) = &document.outline {
999 if !outline_tree.items.is_empty() {
1000 let outline_root_id = self.write_outline_tree(outline_tree)?;
1001 catalog.set("Outlines", Object::Reference(outline_root_id));
1002 }
1003 }
1004
1005 if let Some(struct_tree) = &document.struct_tree {
1007 if !struct_tree.is_empty() {
1008 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
1009 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
1010 catalog.set("MarkInfo", {
1012 let mut mark_info = Dictionary::new();
1013 mark_info.set("Marked", Object::Boolean(true));
1014 Object::Dictionary(mark_info)
1015 });
1016 }
1017 }
1018
1019 let xmp_metadata = document.create_xmp_metadata();
1022 let xmp_packet = xmp_metadata.to_xmp_packet();
1023 let metadata_id = self.allocate_object_id();
1024
1025 let mut metadata_dict = Dictionary::new();
1027 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
1028 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
1029 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
1030
1031 self.write_object(
1033 metadata_id,
1034 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
1035 )?;
1036
1037 catalog.set("Metadata", Object::Reference(metadata_id));
1039
1040 if let Some(action) = &document.open_action {
1042 catalog.set("OpenAction", Object::Dictionary(action.to_dict()));
1043 }
1044
1045 if let Some(prefs) = &document.viewer_preferences {
1047 catalog.set("ViewerPreferences", Object::Dictionary(prefs.to_dict()));
1048 }
1049
1050 if let Some(named_dests) = &document.named_destinations {
1055 let dests_tree_id = self.allocate_object_id();
1056 self.write_object(dests_tree_id, Object::Dictionary(named_dests.to_dict()))?;
1057
1058 let mut names_dict = Dictionary::new();
1059 names_dict.set("Dests", Object::Reference(dests_tree_id));
1060 let names_dict_id = self.allocate_object_id();
1061 self.write_object(names_dict_id, Object::Dictionary(names_dict))?;
1062
1063 catalog.set("Names", Object::Reference(names_dict_id));
1064 }
1065
1066 if let Some(page_labels) = &document.page_labels {
1070 let labels_id = self.allocate_object_id();
1071 self.write_object(labels_id, Object::Dictionary(page_labels.to_dict()))?;
1072 catalog.set("PageLabels", Object::Reference(labels_id));
1073 }
1074
1075 self.write_object(catalog_id, Object::Dictionary(catalog))?;
1076 Ok(())
1077 }
1078
1079 fn preserved_font_disambiguation_map(
1087 page: &crate::page::Page,
1088 font_refs: &HashMap<String, ObjectId>,
1089 ) -> HashMap<String, String> {
1090 let preserved_fonts = match page
1091 .get_preserved_resources()
1092 .and_then(|res| res.get("Font"))
1093 {
1094 Some(crate::pdf_objects::Object::Dictionary(fonts)) => fonts,
1095 _ => return HashMap::new(),
1096 };
1097
1098 let mut reserved: std::collections::HashSet<String> =
1099 crate::writer::INJECTED_BASE_FONT_KEYS
1100 .iter()
1101 .map(|s| s.to_string())
1102 .collect();
1103 reserved.extend(font_refs.keys().cloned());
1104
1105 let preserved_keys: Vec<String> = preserved_fonts
1106 .keys()
1107 .map(|k| k.as_str().to_string())
1108 .collect();
1109 crate::writer::collision_font_mapping(preserved_keys.iter().map(|s| s.as_str()), &reserved)
1110 }
1111
1112 fn write_page_content(
1113 &mut self,
1114 content_id: ObjectId,
1115 page: &crate::page::Page,
1116 preserved_font_map: &HashMap<String, String>,
1117 ) -> Result<()> {
1118 let mut page_copy = page.clone();
1119 page_copy.preserved_font_rewrite_map = preserved_font_map.clone();
1122 let content = page_copy.generate_content()?;
1123
1124 #[cfg(feature = "compression")]
1126 {
1127 use crate::objects::Stream;
1128 let mut stream = Stream::new(content);
1129 if self.config.compress_streams {
1131 stream.compress_flate()?;
1132 }
1133
1134 self.write_object(
1135 content_id,
1136 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
1137 )?;
1138 }
1139
1140 #[cfg(not(feature = "compression"))]
1141 {
1142 let mut stream_dict = Dictionary::new();
1143 stream_dict.set("Length", Object::Integer(content.len() as i64));
1144
1145 self.write_object(content_id, Object::Stream(stream_dict, content))?;
1146 }
1147
1148 Ok(())
1149 }
1150
1151 fn write_outline_tree(
1152 &mut self,
1153 outline_tree: &crate::structure::OutlineTree,
1154 ) -> Result<ObjectId> {
1155 let outline_root_id = self.allocate_object_id();
1157
1158 let mut outline_root = Dictionary::new();
1159 outline_root.set("Type", Object::Name("Outlines".to_string()));
1160
1161 if !outline_tree.items.is_empty() {
1162 let mut item_ids = Vec::new();
1164
1165 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
1167 let mut count = items.len();
1168 for item in items {
1169 count += count_items(&item.children);
1170 }
1171 count
1172 }
1173
1174 let total_items = count_items(&outline_tree.items);
1175
1176 for _ in 0..total_items {
1178 item_ids.push(self.allocate_object_id());
1179 }
1180
1181 let mut id_index = 0;
1182
1183 let first_id = item_ids[0];
1185 let last_id = item_ids[outline_tree.items.len() - 1];
1186
1187 outline_root.set("First", Object::Reference(first_id));
1188 outline_root.set("Last", Object::Reference(last_id));
1189
1190 let visible_count = outline_tree.visible_count();
1192 outline_root.set("Count", Object::Integer(visible_count));
1193
1194 let mut written_items = Vec::new();
1196
1197 for (i, item) in outline_tree.items.iter().enumerate() {
1198 let item_id = item_ids[id_index];
1199 id_index += 1;
1200
1201 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1202 let next_id = if i < outline_tree.items.len() - 1 {
1203 Some(item_ids[i + 1])
1204 } else {
1205 None
1206 };
1207
1208 let children_ids = self.write_outline_item(
1210 item,
1211 item_id,
1212 outline_root_id,
1213 prev_id,
1214 next_id,
1215 &mut item_ids,
1216 &mut id_index,
1217 )?;
1218
1219 written_items.extend(children_ids);
1220 }
1221 }
1222
1223 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1224 Ok(outline_root_id)
1225 }
1226
1227 #[allow(clippy::too_many_arguments)]
1228 fn write_outline_item(
1229 &mut self,
1230 item: &crate::structure::OutlineItem,
1231 item_id: ObjectId,
1232 parent_id: ObjectId,
1233 prev_id: Option<ObjectId>,
1234 next_id: Option<ObjectId>,
1235 all_ids: &mut Vec<ObjectId>,
1236 id_index: &mut usize,
1237 ) -> Result<Vec<ObjectId>> {
1238 let mut written_ids = vec![item_id];
1239
1240 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1242 let first_idx = *id_index;
1243 let first_id = all_ids[first_idx];
1244 let last_idx = first_idx + item.children.len() - 1;
1245 let last_id = all_ids[last_idx];
1246
1247 for (i, child) in item.children.iter().enumerate() {
1249 let child_id = all_ids[*id_index];
1250 *id_index += 1;
1251
1252 let child_prev = if i > 0 {
1253 Some(all_ids[first_idx + i - 1])
1254 } else {
1255 None
1256 };
1257 let child_next = if i < item.children.len() - 1 {
1258 Some(all_ids[first_idx + i + 1])
1259 } else {
1260 None
1261 };
1262
1263 let child_ids = self.write_outline_item(
1264 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1266 )?;
1267
1268 written_ids.extend(child_ids);
1269 }
1270
1271 (Some(first_id), Some(last_id))
1272 } else {
1273 (None, None)
1274 };
1275
1276 let item_dict = crate::structure::outline_item_to_dict(
1278 item,
1279 parent_id,
1280 first_child_id,
1281 last_child_id,
1282 prev_id,
1283 next_id,
1284 );
1285
1286 self.write_object(item_id, Object::Dictionary(item_dict))?;
1287
1288 Ok(written_ids)
1289 }
1290
1291 fn write_struct_tree(
1293 &mut self,
1294 struct_tree: &crate::structure::StructTree,
1295 ) -> Result<ObjectId> {
1296 let struct_tree_root_id = self.allocate_object_id();
1298 let mut element_ids = Vec::new();
1299 for _ in 0..struct_tree.len() {
1300 element_ids.push(self.allocate_object_id());
1301 }
1302
1303 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1305 std::collections::HashMap::new();
1306
1307 if let Some(root_index) = struct_tree.root_index() {
1309 parent_map.insert(root_index, struct_tree_root_id);
1310
1311 fn map_children_parents(
1313 tree: &crate::structure::StructTree,
1314 parent_index: usize,
1315 parent_id: ObjectId,
1316 element_ids: &[ObjectId],
1317 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1318 ) {
1319 if let Some(parent_elem) = tree.get(parent_index) {
1320 for &child_index in &parent_elem.children {
1321 parent_map.insert(child_index, parent_id);
1322 map_children_parents(
1323 tree,
1324 child_index,
1325 element_ids[child_index],
1326 element_ids,
1327 parent_map,
1328 );
1329 }
1330 }
1331 }
1332
1333 map_children_parents(
1334 struct_tree,
1335 root_index,
1336 element_ids[root_index],
1337 &element_ids,
1338 &mut parent_map,
1339 );
1340 }
1341
1342 for (index, element) in struct_tree.iter().enumerate() {
1344 let element_id = element_ids[index];
1345 let mut element_dict = Dictionary::new();
1346
1347 element_dict.set("Type", Object::Name("StructElem".to_string()));
1348 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1349
1350 if let Some(&parent_id) = parent_map.get(&index) {
1352 element_dict.set("P", Object::Reference(parent_id));
1353 }
1354
1355 if let Some(ref id) = element.id {
1357 element_dict.set("ID", Object::String(id.clone()));
1358 }
1359
1360 if let Some(ref lang) = element.attributes.lang {
1362 element_dict.set("Lang", Object::String(lang.clone()));
1363 }
1364 if let Some(ref alt) = element.attributes.alt {
1365 element_dict.set("Alt", Object::String(alt.clone()));
1366 }
1367 if let Some(ref actual_text) = element.attributes.actual_text {
1368 element_dict.set("ActualText", Object::String(actual_text.clone()));
1369 }
1370 if let Some(ref title) = element.attributes.title {
1371 element_dict.set("T", Object::String(title.clone()));
1372 }
1373 if let Some(bbox) = element.attributes.bbox {
1374 element_dict.set(
1375 "BBox",
1376 Object::Array(vec![
1377 Object::Real(bbox[0]),
1378 Object::Real(bbox[1]),
1379 Object::Real(bbox[2]),
1380 Object::Real(bbox[3]),
1381 ]),
1382 );
1383 }
1384
1385 let mut kids = Vec::new();
1387
1388 for &child_index in &element.children {
1390 kids.push(Object::Reference(element_ids[child_index]));
1391 }
1392
1393 for mcid_ref in &element.mcids {
1395 let mut mcr = Dictionary::new();
1396 mcr.set("Type", Object::Name("MCR".to_string()));
1397 mcr.set("Pg", Object::Integer(mcid_ref.page_index as i64));
1398 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1399 kids.push(Object::Dictionary(mcr));
1400 }
1401
1402 if !kids.is_empty() {
1403 element_dict.set("K", Object::Array(kids));
1404 }
1405
1406 self.write_object(element_id, Object::Dictionary(element_dict))?;
1407 }
1408
1409 let mut struct_tree_root = Dictionary::new();
1411 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1412
1413 if let Some(root_index) = struct_tree.root_index() {
1415 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1416 }
1417
1418 if !struct_tree.role_map.mappings().is_empty() {
1420 let mut role_map = Dictionary::new();
1421 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1422 role_map.set(
1423 custom_type.as_str(),
1424 Object::Name(standard_type.as_pdf_name().to_string()),
1425 );
1426 }
1427 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1428 }
1429
1430 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1431 Ok(struct_tree_root_id)
1432 }
1433
1434 fn preallocate_form_manager_fields(&mut self, document: &Document) -> Result<()> {
1447 let Some(form_manager) = &document.form_manager else {
1448 return Ok(());
1449 };
1450
1451 for (_name, _form_field, placeholder) in form_manager.iter_fields_sorted() {
1452 let real_id = self.allocate_object_id();
1453 self.form_field_placeholder_map.insert(placeholder, real_id);
1454 self.form_manager_field_refs.push(real_id);
1455 }
1456 Ok(())
1457 }
1458
1459 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1460 if !self.form_field_ids.is_empty() {
1462 if let Some(acro_form) = &mut document.acro_form {
1463 acro_form.fields.clear();
1465 for field_id in &self.form_field_ids {
1466 acro_form.add_field(*field_id);
1467 }
1468
1469 acro_form.need_appearances = true;
1471 if acro_form.da.is_none() {
1472 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1473 }
1474 }
1475 }
1476 Ok(())
1477 }
1478
1479 fn write_info(&mut self, document: &Document) -> Result<()> {
1480 let info_id = self.get_info_id()?;
1481 let mut info_dict = Dictionary::new();
1482
1483 if let Some(ref title) = document.metadata.title {
1484 info_dict.set("Title", Object::String(title.clone()));
1485 }
1486 if let Some(ref author) = document.metadata.author {
1487 info_dict.set("Author", Object::String(author.clone()));
1488 }
1489 if let Some(ref subject) = document.metadata.subject {
1490 info_dict.set("Subject", Object::String(subject.clone()));
1491 }
1492 if let Some(ref keywords) = document.metadata.keywords {
1493 info_dict.set("Keywords", Object::String(keywords.clone()));
1494 }
1495 if let Some(ref creator) = document.metadata.creator {
1496 info_dict.set("Creator", Object::String(creator.clone()));
1497 }
1498 if let Some(ref producer) = document.metadata.producer {
1499 info_dict.set("Producer", Object::String(producer.clone()));
1500 }
1501
1502 if let Some(creation_date) = document.metadata.creation_date {
1504 let date_string = format_pdf_date(creation_date);
1505 info_dict.set("CreationDate", Object::String(date_string));
1506 }
1507
1508 if let Some(mod_date) = document.metadata.modification_date {
1510 let date_string = format_pdf_date(mod_date);
1511 info_dict.set("ModDate", Object::String(date_string));
1512 }
1513
1514 let edition = super::Edition::OpenSource;
1517
1518 let signature = super::PdfSignature::new(document, edition);
1519 signature.write_to_info_dict(&mut info_dict);
1520
1521 self.write_object(info_id, Object::Dictionary(info_dict))?;
1522 Ok(())
1523 }
1524
1525 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1526 let mut font_refs = HashMap::new();
1527
1528 for font_name in document.custom_font_names() {
1538 let has_usage = self
1539 .document_used_chars_by_font
1540 .get(&font_name)
1541 .map(|chars| !chars.is_empty())
1542 .unwrap_or(false);
1543 if !has_usage {
1544 continue;
1545 }
1546 if let Some(font) = document.get_custom_font(&font_name) {
1547 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1550 font_refs.insert(font_name.clone(), font_id);
1551 }
1552 }
1553
1554 let mut cid_fonts: Vec<(&String, &(Vec<u8>, crate::fonts::CidMapping))> =
1560 document.cid_keyed_fonts().iter().collect();
1561 cid_fonts.sort_by(|a, b| a.0.cmp(b.0));
1562 for (font_name, (data, mapping)) in cid_fonts {
1563 let font_id = self.write_cid_keyed_font(font_name, data, mapping)?;
1564 font_refs.insert(font_name.clone(), font_id);
1565 }
1566
1567 Ok(font_refs)
1568 }
1569
1570 fn write_font_with_unicode_support(
1572 &mut self,
1573 font_name: &str,
1574 font: &crate::fonts::Font,
1575 ) -> Result<ObjectId> {
1576 self.write_type0_font_from_font(font_name, font)
1579 }
1580
1581 fn write_type0_font_from_font(
1583 &mut self,
1584 font_name: &str,
1585 font: &crate::fonts::Font,
1586 ) -> Result<ObjectId> {
1587 let used_chars = self
1594 .document_used_chars_by_font
1595 .get(font_name)
1596 .cloned()
1597 .unwrap_or_else(|| {
1598 let mut chars = std::collections::HashSet::new();
1599 for ch in
1600 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1601 {
1602 chars.insert(ch);
1603 }
1604 chars
1605 });
1606
1607 let used_text: String = used_chars.iter().copied().collect();
1615 let mut missing = font.missing_glyphs(&used_text);
1616 if !missing.is_empty() {
1617 missing.sort_unstable();
1618 let list = missing
1619 .iter()
1620 .map(|c| format!("U+{:04X} {:?}", *c as u32, c))
1621 .collect::<Vec<_>>()
1622 .join(", ");
1623 tracing::warn!(
1624 "Custom font '{}' has no glyph for {} character(s): {}. \
1625 They will render as .notdef (empty boxes); the embedded font \
1626 does not contain these glyphs.",
1627 font_name,
1628 missing.len(),
1629 list
1630 );
1631 }
1632
1633 let font_id = self.allocate_object_id();
1635 let descendant_font_id = self.allocate_object_id();
1636 let descriptor_id = self.allocate_object_id();
1637 let font_file_id = self.allocate_object_id();
1638 let to_unicode_id = self.allocate_object_id();
1639
1640 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1645 if font.data.len() > 100_000 && !used_chars.is_empty() {
1646 match crate::text::fonts::truetype_subsetter::subset_font(
1647 font.data.clone(),
1648 &used_chars,
1649 ) {
1650 Ok(subset_result) => (
1651 subset_result.font_data,
1652 Some(subset_result.glyph_mapping),
1653 font.clone(),
1654 ),
1655 Err(_) => {
1656 if font.data.len() < 25_000_000 {
1657 (font.data.clone(), None, font.clone())
1658 } else {
1659 (Vec::new(), None, font.clone())
1660 }
1661 }
1662 }
1663 } else {
1664 (font.data.clone(), None, font.clone())
1665 };
1666
1667 if !font_data_to_embed.is_empty() {
1668 let mut font_file_dict = Dictionary::new();
1673 match font.format {
1674 crate::fonts::FontFormat::OpenType => {
1675 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1676 }
1677 crate::fonts::FontFormat::TrueType => {
1678 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1679 }
1680 }
1681
1682 #[cfg(feature = "compression")]
1687 {
1688 let font_stream_obj = if self.config.compress_streams {
1689 let mut stream =
1690 crate::objects::Stream::with_dictionary(font_file_dict, font_data_to_embed);
1691 stream.compress_flate()?;
1692 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1693 } else {
1694 Object::Stream(font_file_dict, font_data_to_embed)
1695 };
1696 self.write_object(font_file_id, font_stream_obj)?;
1697 }
1698 #[cfg(not(feature = "compression"))]
1699 {
1700 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1701 self.write_object(font_file_id, font_stream_obj)?;
1702 }
1703 } else {
1704 let font_file_dict = Dictionary::new();
1706 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1707 self.write_object(font_file_id, font_stream_obj)?;
1708 }
1709
1710 let mut descriptor = Dictionary::new();
1712 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1713 descriptor.set("FontName", Object::Name(font_name.to_string()));
1714 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1716 "FontBBox",
1717 Object::Array(vec![
1718 Object::Integer(font.descriptor.font_bbox[0] as i64),
1719 Object::Integer(font.descriptor.font_bbox[1] as i64),
1720 Object::Integer(font.descriptor.font_bbox[2] as i64),
1721 Object::Integer(font.descriptor.font_bbox[3] as i64),
1722 ]),
1723 );
1724 descriptor.set(
1725 "ItalicAngle",
1726 Object::Real(font.descriptor.italic_angle as f64),
1727 );
1728 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1729 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1730 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1731 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1732 let font_file_key = match font.format {
1734 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1737 descriptor.set(font_file_key, Object::Reference(font_file_id));
1738 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1739
1740 let mut cid_font = Dictionary::new();
1742 cid_font.set("Type", Object::Name("Font".to_string()));
1743 let cid_font_subtype = match font.format {
1745 crate::fonts::FontFormat::OpenType => "CIDFontType0",
1746 crate::fonts::FontFormat::TrueType => "CIDFontType2",
1747 };
1748 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1749 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1750
1751 let mut cid_system_info = Dictionary::new();
1753 let (registry, ordering, supplement) =
1754 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1755 cjk_type.cid_system_info()
1756 } else {
1757 ("Adobe", "Identity", 0)
1758 };
1759
1760 cid_system_info.set("Registry", Object::String(registry.to_string()));
1761 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1762 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1763 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1764
1765 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1766
1767 let default_width = self.calculate_default_width(font);
1769 cid_font.set("DW", Object::Integer(default_width));
1770
1771 let w_array = self.generate_width_array(
1775 &original_font_for_widths,
1776 default_width,
1777 subset_glyph_mapping.as_ref(),
1778 );
1779 cid_font.set("W", Object::Array(w_array));
1780
1781 if cid_font_subtype == "CIDFontType2" {
1785 let cid_to_gid_map =
1787 self.generate_cid_to_gid_map(font_name, font, subset_glyph_mapping.as_ref())?;
1788 if !cid_to_gid_map.is_empty() {
1789 let cid_to_gid_map_id = self.allocate_object_id();
1797 let map_dict = Dictionary::new();
1798 #[cfg(feature = "compression")]
1799 let map_stream = if self.config.compress_streams {
1800 let mut stream =
1801 crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1802 stream.compress_flate()?;
1803 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1804 } else {
1805 let mut d = map_dict;
1806 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1807 Object::Stream(d, cid_to_gid_map)
1808 };
1809 #[cfg(not(feature = "compression"))]
1810 let map_stream = {
1811 let mut d = map_dict;
1812 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1813 Object::Stream(d, cid_to_gid_map)
1814 };
1815 self.write_object(cid_to_gid_map_id, map_stream)?;
1816 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1817 } else {
1818 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1819 }
1820 }
1821 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1824
1825 let cmap_data = self.generate_tounicode_cmap_from_font(font_name, font);
1831 let cmap_dict = Dictionary::new();
1832 #[cfg(feature = "compression")]
1833 let cmap_stream = if self.config.compress_streams {
1834 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1835 stream.compress_flate()?;
1836 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1837 } else {
1838 Object::Stream(cmap_dict, cmap_data)
1839 };
1840 #[cfg(not(feature = "compression"))]
1841 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1842 self.write_object(to_unicode_id, cmap_stream)?;
1843
1844 let mut type0_font = Dictionary::new();
1846 type0_font.set("Type", Object::Name("Font".to_string()));
1847 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1848 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1849 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1850 type0_font.set(
1851 "DescendantFonts",
1852 Object::Array(vec![Object::Reference(descendant_font_id)]),
1853 );
1854 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1855
1856 self.write_object(font_id, Object::Dictionary(type0_font))?;
1857
1858 Ok(font_id)
1859 }
1860
1861 fn write_cid_keyed_font(
1871 &mut self,
1872 font_name: &str,
1873 data: &[u8],
1874 mapping: &crate::fonts::CidMapping,
1875 ) -> Result<ObjectId> {
1876 use crate::text::fonts::truetype::TrueTypeFont;
1877
1878 let font = crate::fonts::Font::from_bytes(font_name, data.to_vec())?;
1882 let tt_font = TrueTypeFont::parse(data.to_vec())?;
1883
1884 let used_gids: std::collections::HashSet<u16> =
1891 mapping.cid_to_gid.values().copied().collect();
1892 let (embed_bytes, gid_remap): (Vec<u8>, Option<std::collections::HashMap<u16, u16>>) =
1893 match crate::text::fonts::truetype_subsetter::subset_font_by_gids(
1894 data.to_vec(),
1895 &used_gids,
1896 ) {
1897 Ok(subset) => (subset.font_data, Some(subset.old_to_new)),
1898 Err(e) => {
1899 tracing::debug!("CID-keyed subsetting failed ({e:?}); embedding full font");
1900 (data.to_vec(), None)
1901 }
1902 };
1903
1904 let font_id = self.allocate_object_id();
1905 let descendant_font_id = self.allocate_object_id();
1906 let descriptor_id = self.allocate_object_id();
1907 let font_file_id = self.allocate_object_id();
1908 let to_unicode_id = self.allocate_object_id();
1909
1910 let mut font_file_dict = Dictionary::new();
1913 font_file_dict.set("Length1", Object::Integer(embed_bytes.len() as i64));
1914 #[cfg(feature = "compression")]
1915 {
1916 let font_stream_obj = if self.config.compress_streams {
1917 let mut stream =
1918 crate::objects::Stream::with_dictionary(font_file_dict, embed_bytes);
1919 stream.compress_flate()?;
1920 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1921 } else {
1922 let mut d = font_file_dict;
1923 d.set("Length", Object::Integer(embed_bytes.len() as i64));
1924 Object::Stream(d, embed_bytes)
1925 };
1926 self.write_object(font_file_id, font_stream_obj)?;
1927 }
1928 #[cfg(not(feature = "compression"))]
1929 {
1930 let mut d = font_file_dict;
1931 d.set("Length", Object::Integer(embed_bytes.len() as i64));
1932 self.write_object(font_file_id, Object::Stream(d, embed_bytes))?;
1933 }
1934
1935 let mut descriptor = Dictionary::new();
1937 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1938 descriptor.set("FontName", Object::Name(font_name.to_string()));
1939 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1941 "FontBBox",
1942 Object::Array(vec![
1943 Object::Integer(font.descriptor.font_bbox[0] as i64),
1944 Object::Integer(font.descriptor.font_bbox[1] as i64),
1945 Object::Integer(font.descriptor.font_bbox[2] as i64),
1946 Object::Integer(font.descriptor.font_bbox[3] as i64),
1947 ]),
1948 );
1949 descriptor.set(
1950 "ItalicAngle",
1951 Object::Real(font.descriptor.italic_angle as f64),
1952 );
1953 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1954 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1955 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1956 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1957 descriptor.set("FontFile2", Object::Reference(font_file_id));
1958 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1959
1960 let mut cid_font = Dictionary::new();
1963 cid_font.set("Type", Object::Name("Font".to_string()));
1964 cid_font.set("Subtype", Object::Name("CIDFontType2".to_string()));
1965 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1966 let mut cid_system_info = Dictionary::new();
1967 cid_system_info.set("Registry", Object::String("Adobe".to_string()));
1968 cid_system_info.set("Ordering", Object::String("Identity".to_string()));
1969 cid_system_info.set("Supplement", Object::Integer(0));
1970 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1971 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1972 cid_font.set("DW", Object::Integer(self.calculate_default_width(&font)));
1973
1974 let w_tuples = mapping.generate_width_array(&tt_font)?;
1978 let mut w_array: Vec<Object> = Vec::new();
1979 for (cid, _cid_last, width) in w_tuples {
1980 w_array.push(Object::Integer(cid as i64));
1981 w_array.push(Object::Array(vec![Object::Integer(width as i64)]));
1982 }
1983 cid_font.set("W", Object::Array(w_array));
1984
1985 let cid_to_gid_map = match &gid_remap {
1990 Some(remap) => {
1991 let mut remapped = mapping.clone();
1992 remapped.cid_to_gid = mapping
1993 .cid_to_gid
1994 .iter()
1995 .map(|(&cid, &old_gid)| (cid, remap.get(&old_gid).copied().unwrap_or(0)))
1996 .collect();
1997 remapped.generate_cid_to_gid_map()
1998 }
1999 None => mapping.generate_cid_to_gid_map(),
2000 };
2001 if cid_to_gid_map.is_empty() {
2002 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
2003 } else {
2004 let cid_to_gid_map_id = self.allocate_object_id();
2005 let map_dict = Dictionary::new();
2006 #[cfg(feature = "compression")]
2007 let map_stream = if self.config.compress_streams {
2008 let mut stream = crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
2009 stream.compress_flate()?;
2010 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2011 } else {
2012 let mut d = map_dict;
2013 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
2014 Object::Stream(d, cid_to_gid_map)
2015 };
2016 #[cfg(not(feature = "compression"))]
2017 let map_stream = {
2018 let mut d = map_dict;
2019 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
2020 Object::Stream(d, cid_to_gid_map)
2021 };
2022 self.write_object(cid_to_gid_map_id, map_stream)?;
2023 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
2024 }
2025 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
2026
2027 let cmap_data = mapping.generate_tounicode_cmap();
2029 let cmap_dict = Dictionary::new();
2030 #[cfg(feature = "compression")]
2031 let cmap_stream = if self.config.compress_streams {
2032 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
2033 stream.compress_flate()?;
2034 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2035 } else {
2036 let mut d = cmap_dict;
2037 d.set("Length", Object::Integer(cmap_data.len() as i64));
2038 Object::Stream(d, cmap_data)
2039 };
2040 #[cfg(not(feature = "compression"))]
2041 let cmap_stream = {
2042 let mut d = cmap_dict;
2043 d.set("Length", Object::Integer(cmap_data.len() as i64));
2044 Object::Stream(d, cmap_data)
2045 };
2046 self.write_object(to_unicode_id, cmap_stream)?;
2047
2048 let mut type0_font = Dictionary::new();
2050 type0_font.set("Type", Object::Name("Font".to_string()));
2051 type0_font.set("Subtype", Object::Name("Type0".to_string()));
2052 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
2053 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
2054 type0_font.set(
2055 "DescendantFonts",
2056 Object::Array(vec![Object::Reference(descendant_font_id)]),
2057 );
2058 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
2059 self.write_object(font_id, Object::Dictionary(type0_font))?;
2060
2061 Ok(font_id)
2062 }
2063
2064 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
2066 use crate::text::fonts::truetype::TrueTypeFont;
2067
2068 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2070 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2071 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2072 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
2073 let common_chars =
2077 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
2078 let mut total_width = 0;
2079 let mut count = 0;
2080
2081 for ch in common_chars.chars() {
2082 let unicode = ch as u32;
2083 if let Some(&pdf_width) = widths.get(&unicode) {
2084 total_width += pdf_width as i64;
2085 count += 1;
2086 }
2087 }
2088
2089 if count > 0 {
2090 return total_width / count;
2091 }
2092 }
2093 }
2094 }
2095 }
2096
2097 500
2099 }
2100
2101 fn generate_width_array(
2103 &self,
2104 font: &crate::fonts::Font,
2105 _default_width: i64,
2106 subset_mapping: Option<&HashMap<u32, u16>>,
2107 ) -> Vec<Object> {
2108 use crate::text::fonts::truetype::TrueTypeFont;
2109
2110 let mut w_array = Vec::new();
2111
2112 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2114 let char_to_glyph = {
2118 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2120 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2121 if let Some(subset_map) = subset_mapping {
2123 let mut filtered = HashMap::new();
2124 for unicode in subset_map.keys() {
2125 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
2127 filtered.insert(*unicode, orig_glyph);
2128 }
2129 }
2130 filtered
2131 } else {
2132 cmap.mappings.clone()
2133 }
2134 } else {
2135 HashMap::new()
2136 }
2137 } else {
2138 HashMap::new()
2139 }
2140 };
2141
2142 if !char_to_glyph.is_empty() {
2143 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
2145 let mut sorted_chars: Vec<_> = widths.iter().collect();
2150 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
2151
2152 let mut i = 0;
2153 while i < sorted_chars.len() {
2154 let start_unicode = *sorted_chars[i].0;
2155 let pdf_width = *sorted_chars[i].1 as i64;
2157
2158 let mut end_unicode = start_unicode;
2160 let mut j = i + 1;
2161 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
2162 let next_pdf_width = *sorted_chars[j].1 as i64;
2163 if next_pdf_width == pdf_width {
2164 end_unicode = *sorted_chars[j].0;
2165 j += 1;
2166 } else {
2167 break;
2168 }
2169 }
2170
2171 if start_unicode == end_unicode {
2173 w_array.push(Object::Integer(start_unicode as i64));
2175 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
2176 } else {
2177 w_array.push(Object::Integer(start_unicode as i64));
2179 w_array.push(Object::Integer(end_unicode as i64));
2180 w_array.push(Object::Integer(pdf_width));
2181 }
2182
2183 i = j;
2184 }
2185
2186 return w_array;
2187 }
2188 }
2189 }
2190
2191 let ranges = vec![
2193 (0x20, 0x20, 250), (0x21, 0x2F, 333), (0x30, 0x39, 500), (0x3A, 0x40, 333), (0x41, 0x5A, 667), (0x5B, 0x60, 333), (0x61, 0x7A, 500), (0x7B, 0x7E, 333), (0xA0, 0xA0, 250), (0xA1, 0xBF, 333), (0xC0, 0xD6, 667), (0xD7, 0xD7, 564), (0xD8, 0xDE, 667), (0xDF, 0xF6, 500), (0xF7, 0xF7, 564), (0xF8, 0xFF, 500), (0x100, 0x17F, 500), (0x2000, 0x200F, 250), (0x2010, 0x2027, 333), (0x2028, 0x202F, 250), (0x2030, 0x206F, 500), (0x2070, 0x209F, 400), (0x20A0, 0x20CF, 600), (0x2100, 0x214F, 700), (0x2190, 0x21FF, 600), (0x2200, 0x22FF, 600), (0x2300, 0x23FF, 600), (0x2500, 0x257F, 500), (0x2580, 0x259F, 500), (0x25A0, 0x25FF, 600), (0x2600, 0x26FF, 600), (0x2700, 0x27BF, 600), ];
2230
2231 for (start, end, width) in ranges {
2233 if start == end {
2234 w_array.push(Object::Integer(start));
2236 w_array.push(Object::Array(vec![Object::Integer(width)]));
2237 } else {
2238 w_array.push(Object::Integer(start));
2240 w_array.push(Object::Integer(end));
2241 w_array.push(Object::Integer(width));
2242 }
2243 }
2244
2245 w_array
2246 }
2247
2248 fn generate_cid_to_gid_map(
2250 &mut self,
2251 font_name: &str,
2252 font: &crate::fonts::Font,
2253 subset_mapping: Option<&HashMap<u32, u16>>,
2254 ) -> Result<Vec<u8>> {
2255 use crate::text::fonts::truetype::TrueTypeFont;
2256
2257 let cmap_mappings = if let Some(subset_map) = subset_mapping {
2260 subset_map.clone()
2262 } else {
2263 let tt_font = TrueTypeFont::parse(font.data.clone())?;
2265 let cmap_tables = tt_font.parse_cmap()?;
2266
2267 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
2269 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
2270 })?;
2271
2272 cmap.mappings.clone()
2273 };
2274
2275 let used_chars = self
2282 .document_used_chars_by_font
2283 .get(font_name)
2284 .cloned()
2285 .unwrap_or_default();
2286
2287 let max_unicode = if !used_chars.is_empty() {
2289 used_chars
2291 .iter()
2292 .map(|ch| *ch as u32)
2293 .max()
2294 .unwrap_or(0x00FF) .min(0xFFFF) as usize
2296 } else {
2297 cmap_mappings
2299 .keys()
2300 .max()
2301 .copied()
2302 .unwrap_or(0xFFFF)
2303 .min(0xFFFF) as usize
2304 };
2305
2306 let mut map = vec![0u8; (max_unicode + 1) * 2];
2308
2309 let mut sample_mappings = Vec::new();
2311 for (&unicode, &glyph_id) in &cmap_mappings {
2312 if unicode <= max_unicode as u32 {
2313 let idx = (unicode as usize) * 2;
2314 map[idx] = (glyph_id >> 8) as u8;
2316 map[idx + 1] = (glyph_id & 0xFF) as u8;
2317
2318 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
2320 {
2321 sample_mappings.push((unicode, glyph_id));
2322 }
2323 }
2324 }
2325
2326 Ok(map)
2327 }
2328
2329 fn generate_tounicode_cmap_from_font(
2331 &self,
2332 font_name: &str,
2333 font: &crate::fonts::Font,
2334 ) -> Vec<u8> {
2335 use crate::text::fonts::truetype::TrueTypeFont;
2336
2337 let mut cmap = String::new();
2338
2339 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
2341 cmap.push_str("12 dict begin\n");
2342 cmap.push_str("begincmap\n");
2343 cmap.push_str("/CIDSystemInfo\n");
2344 cmap.push_str("<< /Registry (Adobe)\n");
2345 cmap.push_str(" /Ordering (UCS)\n");
2346 cmap.push_str(" /Supplement 0\n");
2347 cmap.push_str(">> def\n");
2348 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
2349 cmap.push_str("/CMapType 2 def\n");
2350 cmap.push_str("1 begincodespacerange\n");
2351 cmap.push_str("<0000> <FFFF>\n");
2352 cmap.push_str("endcodespacerange\n");
2353
2354 let used_codepoints: Option<std::collections::HashSet<u32>> = self
2360 .document_used_chars_by_font
2361 .get(font_name)
2362 .map(|chars| {
2363 chars
2364 .iter()
2365 .map(|c| *c as u32)
2366 .filter(|cp| *cp <= 0xFFFF)
2367 .collect()
2368 });
2369
2370 let mut mappings: Vec<(u32, u32)> = Vec::new();
2371
2372 if let Some(used) = &used_codepoints {
2373 for cp in used {
2375 mappings.push((*cp, *cp));
2376 }
2377 } else if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2378 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2380 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
2381 for (&unicode, &glyph_id) in &cmap_table.mappings {
2382 if glyph_id > 0 && unicode <= 0xFFFF {
2383 mappings.push((unicode, unicode));
2384 }
2385 }
2386 }
2387 }
2388 }
2389
2390 mappings.sort_by_key(|&(cid, _)| cid);
2392
2393 let mut i = 0;
2395 while i < mappings.len() {
2396 let start_cid = mappings[i].0;
2398 let start_unicode = mappings[i].1;
2399 let mut end_idx = i;
2400
2401 while end_idx + 1 < mappings.len()
2403 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
2404 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
2405 && end_idx - i < 99
2406 {
2408 end_idx += 1;
2409 }
2410
2411 if end_idx > i {
2412 cmap.push_str("1 beginbfrange\n");
2414 cmap.push_str(&format!(
2415 "<{:04X}> <{:04X}> <{:04X}>\n",
2416 start_cid, mappings[end_idx].0, start_unicode
2417 ));
2418 cmap.push_str("endbfrange\n");
2419 i = end_idx + 1;
2420 } else {
2421 let mut chars = Vec::new();
2423 let chunk_end = (i + 100).min(mappings.len());
2424
2425 for item in &mappings[i..chunk_end] {
2426 chars.push(*item);
2427 }
2428
2429 if !chars.is_empty() {
2430 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
2431 for (cid, unicode) in chars {
2432 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
2433 }
2434 cmap.push_str("endbfchar\n");
2435 }
2436
2437 i = chunk_end;
2438 }
2439 }
2440
2441 cmap.push_str("endcmap\n");
2443 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
2444 cmap.push_str("end\n");
2445 cmap.push_str("end\n");
2446
2447 cmap.into_bytes()
2448 }
2449
2450 #[allow(dead_code)]
2452 fn write_truetype_font(
2453 &mut self,
2454 font_name: &str,
2455 font: &crate::text::font_manager::CustomFont,
2456 ) -> Result<ObjectId> {
2457 let font_id = self.allocate_object_id();
2459 let descriptor_id = self.allocate_object_id();
2460 let font_file_id = self.allocate_object_id();
2461
2462 if let Some(ref data) = font.font_data {
2464 let mut font_file_dict = Dictionary::new();
2465 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2466 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2467 self.write_object(font_file_id, font_stream_obj)?;
2468 }
2469
2470 let mut descriptor = Dictionary::new();
2472 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2473 descriptor.set("FontName", Object::Name(font_name.to_string()));
2474 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2476 "FontBBox",
2477 Object::Array(vec![
2478 Object::Integer(-1000),
2479 Object::Integer(-1000),
2480 Object::Integer(2000),
2481 Object::Integer(2000),
2482 ]),
2483 );
2484 descriptor.set("ItalicAngle", Object::Integer(0));
2485 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2486 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2487 descriptor.set(
2488 "CapHeight",
2489 Object::Integer(font.descriptor.cap_height as i64),
2490 );
2491 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2492 descriptor.set("FontFile2", Object::Reference(font_file_id));
2493 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2494
2495 let mut font_dict = Dictionary::new();
2497 font_dict.set("Type", Object::Name("Font".to_string()));
2498 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2499 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2500 font_dict.set("FirstChar", Object::Integer(0));
2501 font_dict.set("LastChar", Object::Integer(255));
2502
2503 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2505 font_dict.set("Widths", Object::Array(widths));
2506 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2507
2508 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2510
2511 self.write_object(font_id, Object::Dictionary(font_dict))?;
2512
2513 Ok(font_id)
2514 }
2515
2516 fn write_pages(
2517 &mut self,
2518 document: &Document,
2519 font_refs: &HashMap<String, ObjectId>,
2520 ) -> Result<()> {
2521 let pages_id = self.get_pages_id()?;
2522 let mut pages_dict = Dictionary::new();
2523 pages_dict.set("Type", Object::Name("Pages".to_string()));
2524 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2525
2526 let mut kids = Vec::new();
2527
2528 let mut page_ids = Vec::new();
2530 let mut content_ids = Vec::new();
2531 for _ in 0..document.pages.len() {
2532 page_ids.push(self.allocate_object_id());
2533 content_ids.push(self.allocate_object_id());
2534 }
2535
2536 for page_id in &page_ids {
2537 kids.push(Object::Reference(*page_id));
2538 }
2539
2540 pages_dict.set("Kids", Object::Array(kids));
2541
2542 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2543
2544 self.page_ids = page_ids.clone();
2546
2547 for (i, page) in document.pages.iter().enumerate() {
2549 let page_id = page_ids[i];
2550 let content_id = content_ids[i];
2551
2552 let preserved_font_map = Self::preserved_font_disambiguation_map(page, font_refs);
2557 self.write_page_with_fonts(
2558 page_id,
2559 pages_id,
2560 content_id,
2561 page,
2562 document,
2563 font_refs,
2564 &preserved_font_map,
2565 )?;
2566 self.write_page_content(content_id, page, &preserved_font_map)?;
2567 }
2568
2569 Ok(())
2570 }
2571
2572 #[allow(dead_code)]
2574 fn write_pages_with_fonts(
2575 &mut self,
2576 document: &Document,
2577 font_refs: &HashMap<String, ObjectId>,
2578 ) -> Result<()> {
2579 self.write_pages(document, font_refs)
2580 }
2581
2582 fn write_page_with_fonts(
2583 &mut self,
2584 page_id: ObjectId,
2585 parent_id: ObjectId,
2586 content_id: ObjectId,
2587 page: &crate::page::Page,
2588 _document: &Document,
2589 font_refs: &HashMap<String, ObjectId>,
2590 preserved_font_map: &HashMap<String, String>,
2591 ) -> Result<()> {
2592 let mut page_dict = page.to_dict();
2594
2595 page_dict.set("Type", Object::Name("Page".to_string()));
2596 page_dict.set("Parent", Object::Reference(parent_id));
2597 page_dict.set("Contents", Object::Reference(content_id));
2598
2599 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2601 res.clone()
2602 } else {
2603 Dictionary::new()
2604 };
2605
2606 let mut font_dict = Dictionary::new();
2608
2609 let mut helvetica_dict = Dictionary::new();
2614 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2615 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2616 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2617 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2618 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2619
2620 let mut helvetica_bold_dict = Dictionary::new();
2621 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2622 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2623 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2624 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2625 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2626
2627 let mut helvetica_oblique_dict = Dictionary::new();
2628 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2629 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2630 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2631 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2632 font_dict.set(
2633 "Helvetica-Oblique",
2634 Object::Dictionary(helvetica_oblique_dict),
2635 );
2636
2637 let mut helvetica_bold_oblique_dict = Dictionary::new();
2638 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2639 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2640 helvetica_bold_oblique_dict.set(
2641 "BaseFont",
2642 Object::Name("Helvetica-BoldOblique".to_string()),
2643 );
2644 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2645 font_dict.set(
2646 "Helvetica-BoldOblique",
2647 Object::Dictionary(helvetica_bold_oblique_dict),
2648 );
2649
2650 let mut times_dict = Dictionary::new();
2652 times_dict.set("Type", Object::Name("Font".to_string()));
2653 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2654 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2655 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2656 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2657
2658 let mut times_bold_dict = Dictionary::new();
2659 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2660 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2661 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2662 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2663 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2664
2665 let mut times_italic_dict = Dictionary::new();
2666 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2667 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2668 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2669 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2670 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2671
2672 let mut times_bold_italic_dict = Dictionary::new();
2673 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2674 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2675 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2676 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2677 font_dict.set(
2678 "Times-BoldItalic",
2679 Object::Dictionary(times_bold_italic_dict),
2680 );
2681
2682 let mut courier_dict = Dictionary::new();
2684 courier_dict.set("Type", Object::Name("Font".to_string()));
2685 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2686 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2687 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2688 font_dict.set("Courier", Object::Dictionary(courier_dict));
2689
2690 let mut courier_bold_dict = Dictionary::new();
2691 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2692 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2693 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2694 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2695 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2696
2697 let mut courier_oblique_dict = Dictionary::new();
2698 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2699 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2700 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2701 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2702 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2703
2704 let mut courier_bold_oblique_dict = Dictionary::new();
2705 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2706 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2707 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2708 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2709 font_dict.set(
2710 "Courier-BoldOblique",
2711 Object::Dictionary(courier_bold_oblique_dict),
2712 );
2713
2714 for (font_name, font_id) in font_refs {
2716 font_dict.set(font_name, Object::Reference(*font_id));
2717 }
2718
2719 resources.set("Font", Object::Dictionary(font_dict));
2720
2721 let has_images = !page.images().is_empty();
2723 let has_forms = !page.form_xobjects().is_empty();
2724
2725 let mut form_xobject_ids: HashMap<String, ObjectId> = HashMap::new();
2732
2733 if has_images || has_forms {
2734 let mut xobject_dict = Dictionary::new();
2735
2736 let mut image_entries: Vec<(&String, &crate::graphics::Image)> =
2740 page.images().iter().collect();
2741 image_entries.sort_by_key(|(name, _)| name.as_str());
2742 for (name, image) in image_entries {
2743 let image_id = self.allocate_object_id();
2745
2746 if image.has_transparency() {
2748 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2750
2751 if let Some(smask_stream) = smask_obj {
2753 let smask_id = self.allocate_object_id();
2754 self.write_object(smask_id, smask_stream)?;
2755
2756 if let Object::Stream(ref mut dict, _) = main_obj {
2758 dict.set("SMask", Object::Reference(smask_id));
2759 }
2760 }
2761
2762 self.write_object(image_id, main_obj)?;
2764 } else {
2765 self.write_object(image_id, image.to_pdf_object())?;
2767 }
2768
2769 xobject_dict.set(name, Object::Reference(image_id));
2771 }
2772
2773 let mut form_entries: Vec<(&String, &crate::graphics::FormXObject)> =
2775 page.form_xobjects().iter().collect();
2776 form_entries.sort_by_key(|(name, _)| name.as_str());
2777 for (name, form) in form_entries {
2778 let form_id = self.allocate_object_id();
2779 let stream = form.to_stream()?;
2780 let stream_obj =
2781 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2782 self.write_object(form_id, stream_obj)?;
2783 xobject_dict.set(name, Object::Reference(form_id));
2784 form_xobject_ids.insert(name.clone(), form_id);
2787 }
2788
2789 resources.set("XObject", Object::Dictionary(xobject_dict));
2790 }
2791
2792 if let Some(extgstate_states) = page.get_extgstate_resources() {
2794 let mut extgstate_dict = Dictionary::new();
2795 let mut extgstate_entries: Vec<(&String, &crate::graphics::ExtGState)> =
2797 extgstate_states.iter().collect();
2798 extgstate_entries.sort_by_key(|(name, _)| name.as_str());
2799 for (name, state) in extgstate_entries {
2800 let mut state_dict = Dictionary::new();
2801 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2802
2803 if let Some(alpha_stroke) = state.alpha_stroke {
2805 state_dict.set("CA", Object::Real(alpha_stroke));
2806 }
2807 if let Some(alpha_fill) = state.alpha_fill {
2808 state_dict.set("ca", Object::Real(alpha_fill));
2809 }
2810
2811 if let Some(line_width) = state.line_width {
2813 state_dict.set("LW", Object::Real(line_width));
2814 }
2815 if let Some(line_cap) = state.line_cap {
2816 state_dict.set("LC", Object::Integer(line_cap as i64));
2817 }
2818 if let Some(line_join) = state.line_join {
2819 state_dict.set("LJ", Object::Integer(line_join as i64));
2820 }
2821 if let Some(dash_pattern) = &state.dash_pattern {
2822 let dash_objects: Vec<Object> = dash_pattern
2823 .array
2824 .iter()
2825 .map(|&d| Object::Real(d))
2826 .collect();
2827 state_dict.set(
2828 "D",
2829 Object::Array(vec![
2830 Object::Array(dash_objects),
2831 Object::Real(dash_pattern.phase),
2832 ]),
2833 );
2834 }
2835
2836 if let Some(ref bm) = state.blend_mode {
2840 state_dict.set("BM", Object::Name(bm.pdf_name().to_string()));
2841 }
2842
2843 if let Some(ref soft_mask) = state.soft_mask {
2861 let mut mask_dict = soft_mask.to_pdf_dictionary()?;
2862 if let Some(Object::Name(ref g_name)) = mask_dict.get("G").cloned() {
2863 let form_id = form_xobject_ids.get(g_name).ok_or_else(|| {
2864 crate::error::PdfError::InvalidStructure(format!(
2865 "SoftMask references transparency group {:?} but no matching \
2866 FormXObject is registered on the page; call \
2867 Page::add_form_xobject({:?}, ...) before saving",
2868 g_name, g_name
2869 ))
2870 })?;
2871 mask_dict.set("G", Object::Reference(*form_id));
2872 }
2873 state_dict.set("SMask", Object::Dictionary(mask_dict));
2874 }
2875
2876 extgstate_dict.set(name, Object::Dictionary(state_dict));
2877 }
2878 if !extgstate_dict.is_empty() {
2879 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2880 }
2881 }
2882
2883 if !page.color_spaces().is_empty() {
2898 let mut cs_dict = Dictionary::new();
2899 let mut entries: Vec<(&String, &crate::graphics::PageColorSpace)> =
2902 page.color_spaces().iter().collect();
2903 entries.sort_by_key(|(name, _)| name.as_str());
2904 for (name, cs) in entries {
2905 if let Some((icc_dict, icc_data)) = cs.icc_stream_parts() {
2910 let icc_id = self.allocate_object_id();
2911 self.write_object(icc_id, Object::Stream(icc_dict, icc_data))?;
2912 cs_dict.set(
2913 name,
2914 Object::Array(vec![
2915 Object::Name("ICCBased".to_string()),
2916 Object::Reference(icc_id),
2917 ]),
2918 );
2919 } else {
2920 cs_dict.set(name, cs.to_object());
2921 }
2922 }
2923 resources.set("ColorSpace", Object::Dictionary(cs_dict));
2924 }
2925
2926 if !page.patterns().is_empty() {
2927 let mut pat_dict = Dictionary::new();
2928 let mut entries: Vec<(&String, &crate::graphics::TilingPattern)> =
2929 page.patterns().iter().collect();
2930 entries.sort_by_key(|(name, _)| name.as_str());
2931 for (name, pattern) in entries {
2932 let pattern_id = self.allocate_object_id();
2933 let pattern_dict = pattern.to_pdf_dictionary()?;
2934 self.write_object(
2935 pattern_id,
2936 Object::Stream(pattern_dict, pattern.content_stream.clone()),
2937 )?;
2938 pat_dict.set(name, Object::Reference(pattern_id));
2939 }
2940 resources.set("Pattern", Object::Dictionary(pat_dict));
2941 }
2942
2943 if !page.shadings().is_empty() || !page.advanced_shadings().is_empty() {
2944 let mut sh_dict = Dictionary::new();
2945
2946 let mut entries: Vec<(&String, &crate::graphics::ShadingDefinition)> =
2948 page.shadings().iter().collect();
2949 entries.sort_by_key(|(name, _)| name.as_str());
2950 for (name, shading) in entries {
2951 let obj = Object::Dictionary(shading.to_pdf_dictionary()?);
2952 let shading_id = self.write_shading_object(obj)?;
2953 sh_dict.set(name, Object::Reference(shading_id));
2954 }
2955
2956 let mut adv: Vec<(&String, &crate::graphics::AdvancedShading)> =
2959 page.advanced_shadings().iter().collect();
2960 adv.sort_by_key(|(name, _)| name.as_str());
2961 for (name, shading) in adv {
2962 let obj = shading.to_pdf_object()?;
2963 let shading_id = self.write_shading_object(obj)?;
2964 sh_dict.set(name, Object::Reference(shading_id));
2965 }
2966
2967 resources.set("Shading", Object::Dictionary(sh_dict));
2968 }
2969
2970 if let Some(preserved_res) = page.get_preserved_resources() {
2973 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
2975
2976 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2981 let renamed_fonts = crate::writer::apply_font_rename_map(fonts, preserved_font_map);
2982 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
2983 }
2984
2985 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2989 let mut fonts_with_refs = crate::objects::Dictionary::new();
2990
2991 for (font_name, font_obj) in fonts.iter() {
2992 if let Object::Dictionary(font_dict) = font_obj {
2993 let updated_font = self.write_embedded_font_streams(font_dict)?;
2995 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
2996 } else {
2997 fonts_with_refs.set(font_name, font_obj.clone());
2999 }
3000 }
3001
3002 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
3004 }
3005
3006 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
3010 let mut xobjects_with_refs = crate::objects::Dictionary::new();
3011 tracing::debug!(
3012 "Externalizing {} preserved XObject entries as indirect objects",
3013 xobjects.len()
3014 );
3015
3016 for (xobj_name, xobj_obj) in xobjects.iter() {
3017 match xobj_obj {
3018 Object::Stream(dict, data) => {
3019 let dict = self.externalize_nested_streams_in_dict(dict)?;
3027 let obj_id = self.allocate_object_id();
3028 self.write_object(obj_id, Object::Stream(dict, data.clone()))?;
3029 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
3030 }
3031 Object::Dictionary(dict) => {
3032 let externalized = self.externalize_streams_in_dict(dict)?;
3039 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
3040 }
3041 _ => {
3042 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
3043 }
3044 }
3045 }
3046
3047 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
3048 }
3049
3050 for (key, value) in preserved_writer_dict.iter() {
3052 if let Some(Object::Dictionary(existing)) = resources.get(key) {
3054 if let Object::Dictionary(preserved_dict) = value {
3055 let mut merged = existing.clone();
3056 for (res_name, res_obj) in preserved_dict.iter() {
3058 if !merged.contains_key(res_name) {
3059 merged.set(res_name, res_obj.clone());
3060 }
3061 }
3062 resources.set(key, Object::Dictionary(merged));
3063 }
3064 } else {
3065 resources.set(key, value.clone());
3067 }
3068 }
3069 }
3070
3071 page_dict.set("Resources", Object::Dictionary(resources));
3072
3073 let mut annot_refs: Vec<Object> = Vec::new();
3075
3076 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
3078 for annot in annots {
3079 if let Object::Dictionary(ref annot_dict) = annot {
3080 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
3081 if subtype == "Widget" {
3082 let widget_id = self.allocate_object_id();
3083 self.write_object(widget_id, annot.clone())?;
3084 annot_refs.push(Object::Reference(widget_id));
3085
3086 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
3088 if let Some(Object::String(field_name)) = annot_dict.get("T") {
3089 self.field_widget_map
3090 .entry(field_name.clone())
3091 .or_default()
3092 .push(widget_id);
3093 self.field_id_map.insert(field_name.clone(), widget_id);
3094 self.form_field_ids.push(widget_id);
3095 }
3096 }
3097 continue;
3098 }
3099 }
3100 }
3101 annot_refs.push(annot.clone());
3102 }
3103 }
3104
3105 for annotation in page.annotations() {
3109 let annot_id = self.allocate_object_id();
3110 let mut annot_dict = annotation.to_dict();
3111
3112 if let Some(placeholder) = annotation.field_parent {
3130 if let Some(real_id) = self.form_field_placeholder_map.get(&placeholder) {
3131 annot_dict.set("Parent", Object::Reference(*real_id));
3132 }
3133 }
3134
3135 if let Some(Object::Dictionary(ap_dict)) = annot_dict.get("AP") {
3154 let mut updated_ap = crate::objects::Dictionary::new();
3155 for (state_key, state_val) in ap_dict.iter() {
3156 match state_val {
3157 Object::Stream(sd, data) => {
3158 let patched_sd = Self::rewrite_ap_stream_font_resources(sd, font_refs);
3164 let stream_id = self.allocate_object_id();
3165 self.write_object(stream_id, Object::Stream(patched_sd, data.clone()))?;
3166 updated_ap.set(state_key, Object::Reference(stream_id));
3167 }
3168 Object::Dictionary(down_dict) => {
3169 let externalized = self
3171 .externalize_streams_in_dict_with_font_refs(down_dict, font_refs)?;
3172 updated_ap.set(state_key, Object::Dictionary(externalized));
3173 }
3174 _ => {
3175 updated_ap.set(state_key, state_val.clone());
3176 }
3177 }
3178 }
3179 annot_dict.set("AP", Object::Dictionary(updated_ap));
3180 }
3181
3182 self.write_object(annot_id, Object::Dictionary(annot_dict))?;
3183 annot_refs.push(Object::Reference(annot_id));
3184
3185 if annotation.annotation_type == crate::annotations::AnnotationType::Widget {
3187 if let Some(Object::String(field_name)) = annotation.properties.get("T") {
3188 self.field_widget_map
3189 .entry(field_name.clone())
3190 .or_default()
3191 .push(annot_id);
3192 self.field_id_map.insert(field_name.clone(), annot_id);
3193 self.form_field_ids.push(annot_id);
3194 }
3195 }
3196 }
3197
3198 if !annot_refs.is_empty() {
3200 page_dict.set("Annots", Object::Array(annot_refs));
3201 } else {
3202 page_dict.remove("Annots");
3203 }
3204
3205 self.write_object(page_id, Object::Dictionary(page_dict))?;
3206 Ok(())
3207 }
3208}
3209
3210impl PdfWriter<BufWriter<std::fs::File>> {
3211 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
3212 let file = std::fs::File::create(path)?;
3213 let writer = BufWriter::new(file);
3214
3215 Ok(Self {
3216 writer,
3217 xref_positions: HashMap::new(),
3218 current_position: 0,
3219 next_object_id: 1,
3220 catalog_id: None,
3221 pages_id: None,
3222 info_id: None,
3223 field_widget_map: HashMap::new(),
3224 field_id_map: HashMap::new(),
3225 form_field_ids: Vec::new(),
3226 page_ids: Vec::new(),
3227 config: WriterConfig::default(),
3228 document_used_chars_by_font: std::collections::HashMap::new(),
3229 buffered_objects: HashMap::new(),
3230 compressed_object_map: HashMap::new(),
3231 prev_xref_offset: None,
3232 base_pdf_size: None,
3233 encrypt_obj_id: None,
3234 file_id: None,
3235 encryption_state: None,
3236 pending_encrypt_dict: None,
3237 form_field_placeholder_map: HashMap::new(),
3238 form_manager_field_refs: Vec::new(),
3239 })
3240 }
3241}
3242
3243impl<W: Write> PdfWriter<W> {
3244 fn externalize_streams_in_dict(
3263 &mut self,
3264 dict: &crate::objects::Dictionary,
3265 ) -> Result<crate::objects::Dictionary> {
3266 self.externalize_streams_in_dict_with_font_refs(dict, &HashMap::new())
3267 }
3268
3269 fn externalize_nested_streams_in_dict(
3281 &mut self,
3282 dict: &crate::objects::Dictionary,
3283 ) -> Result<crate::objects::Dictionary> {
3284 let mut result = crate::objects::Dictionary::new();
3285 for (key, value) in dict.iter() {
3286 result.set(key, self.externalize_nested_streams_value(value)?);
3287 }
3288 Ok(result)
3289 }
3290
3291 fn externalize_nested_streams_value(&mut self, value: &Object) -> Result<Object> {
3292 match value {
3293 Object::Stream(d, data) => {
3294 let d = self.externalize_nested_streams_in_dict(d)?;
3295 let obj_id = self.allocate_object_id();
3296 self.write_object(obj_id, Object::Stream(d, data.clone()))?;
3297 Ok(Object::Reference(obj_id))
3298 }
3299 Object::Dictionary(d) => Ok(Object::Dictionary(
3300 self.externalize_nested_streams_in_dict(d)?,
3301 )),
3302 Object::Array(items) => {
3303 let mut out = Vec::with_capacity(items.len());
3304 for item in items {
3305 out.push(self.externalize_nested_streams_value(item)?);
3306 }
3307 Ok(Object::Array(out))
3308 }
3309 other => Ok(other.clone()),
3310 }
3311 }
3312
3313 fn externalize_streams_in_dict_with_font_refs(
3317 &mut self,
3318 dict: &crate::objects::Dictionary,
3319 font_refs: &HashMap<String, ObjectId>,
3320 ) -> Result<crate::objects::Dictionary> {
3321 let mut result = crate::objects::Dictionary::new();
3322 for (key, value) in dict.iter() {
3323 match value {
3324 Object::Stream(d, data) => {
3325 let patched_d = Self::rewrite_ap_stream_font_resources(d, font_refs);
3326 let obj_id = self.allocate_object_id();
3327 self.write_object(obj_id, Object::Stream(patched_d, data.clone()))?;
3328 result.set(key, Object::Reference(obj_id));
3329 }
3330 _ => {
3331 result.set(key, value.clone());
3332 }
3333 }
3334 }
3335 Ok(result)
3336 }
3337
3338 fn rewrite_ap_stream_font_resources(
3354 stream_dict: &crate::objects::Dictionary,
3355 font_refs: &HashMap<String, ObjectId>,
3356 ) -> crate::objects::Dictionary {
3357 if font_refs.is_empty() {
3364 return stream_dict.clone();
3365 }
3366
3367 let mut out = stream_dict.clone();
3368
3369 let Some(Object::Dictionary(resources)) = stream_dict.get("Resources") else {
3375 return out;
3376 };
3377 let Some(Object::Dictionary(fonts)) = resources.get("Font") else {
3378 return out;
3379 };
3380
3381 let mut patched_fonts = crate::objects::Dictionary::new();
3382 let mut changed = false;
3383 for (font_name, entry) in fonts.iter() {
3384 let should_rewrite = match entry {
3388 Object::Dictionary(d) => {
3389 matches!(d.get("Subtype"), Some(Object::Name(s)) if s == "Type0")
3390 }
3391 _ => false,
3392 };
3393 if should_rewrite {
3394 if let Some(font_id) = font_refs.get(font_name.as_str()) {
3395 patched_fonts.set(font_name, Object::Reference(*font_id));
3396 changed = true;
3397 continue;
3398 }
3399 }
3400 patched_fonts.set(font_name, entry.clone());
3401 }
3402
3403 if changed {
3404 let mut patched_resources = resources.clone();
3405 patched_resources.set("Font", Object::Dictionary(patched_fonts));
3406 out.set("Resources", Object::Dictionary(patched_resources));
3407 }
3408 out
3409 }
3410
3411 fn write_embedded_font_streams(
3412 &mut self,
3413 font_dict: &crate::objects::Dictionary,
3414 ) -> Result<crate::objects::Dictionary> {
3415 let mut updated_font = font_dict.clone();
3416
3417 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
3419 if subtype == "Type0" {
3420 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
3422 let mut updated_descendants = Vec::new();
3423
3424 for descendant in descendants {
3425 match descendant {
3426 Object::Dictionary(cidfont) => {
3427 let updated_cidfont =
3429 self.write_cidfont_embedded_streams(cidfont)?;
3430 let cidfont_id = self.allocate_object_id();
3432 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
3433 updated_descendants.push(Object::Reference(cidfont_id));
3435 }
3436 Object::Reference(_) => {
3437 updated_descendants.push(descendant.clone());
3439 }
3440 _ => {
3441 updated_descendants.push(descendant.clone());
3442 }
3443 }
3444 }
3445
3446 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
3447 }
3448
3449 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
3451 let tounicode_id = self.allocate_object_id();
3452 self.write_object(
3453 tounicode_id,
3454 Object::Stream(stream_dict.clone(), stream_data.clone()),
3455 )?;
3456 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
3457 }
3458
3459 return Ok(updated_font);
3460 }
3461 }
3462
3463 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
3466 let mut updated_descriptor = descriptor.clone();
3467 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3468
3469 for key in &font_file_keys {
3471 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3472 let stream_id = self.allocate_object_id();
3474 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
3475 self.write_object(stream_id, stream_obj)?;
3476
3477 updated_descriptor.set(*key, Object::Reference(stream_id));
3479 }
3480 }
3482
3483 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
3485 }
3486
3487 Ok(updated_font)
3488 }
3489
3490 fn write_cidfont_embedded_streams(
3492 &mut self,
3493 cidfont: &crate::objects::Dictionary,
3494 ) -> Result<crate::objects::Dictionary> {
3495 let mut updated_cidfont = cidfont.clone();
3496
3497 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
3499 let mut updated_descriptor = descriptor.clone();
3500 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3501
3502 for key in &font_file_keys {
3504 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3505 let stream_id = self.allocate_object_id();
3506 self.write_object(
3507 stream_id,
3508 Object::Stream(stream_dict.clone(), stream_data.clone()),
3509 )?;
3510 updated_descriptor.set(*key, Object::Reference(stream_id));
3511 }
3512 }
3513
3514 let descriptor_id = self.allocate_object_id();
3516 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
3517
3518 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
3520 }
3521
3522 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
3524 let map_id = self.allocate_object_id();
3525 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
3526 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
3527 }
3528
3529 Ok(updated_cidfont)
3530 }
3531
3532 fn allocate_object_id(&mut self) -> ObjectId {
3533 let id = ObjectId::new(self.next_object_id, 0);
3534 self.next_object_id += 1;
3535 id
3536 }
3537
3538 fn write_shading_object(&mut self, obj: Object) -> Result<ObjectId> {
3547 match obj {
3548 Object::Dictionary(mut dict) => {
3549 if matches!(
3550 dict.get("Function"),
3551 Some(Object::Dictionary(_)) | Some(Object::Stream(_, _))
3552 ) {
3553 if let Some(func_obj) = dict.remove("Function") {
3554 let func_id = self.allocate_object_id();
3555 self.write_object(func_id, func_obj)?;
3556 dict.set("Function", Object::Reference(func_id));
3557 }
3558 }
3559 let shading_id = self.allocate_object_id();
3560 self.write_object(shading_id, Object::Dictionary(dict))?;
3561 Ok(shading_id)
3562 }
3563 stream @ Object::Stream(_, _) => {
3564 let shading_id = self.allocate_object_id();
3565 self.write_object(shading_id, stream)?;
3566 Ok(shading_id)
3567 }
3568 other => {
3569 let shading_id = self.allocate_object_id();
3570 self.write_object(shading_id, other)?;
3571 Ok(shading_id)
3572 }
3573 }
3574 }
3575
3576 fn get_catalog_id(&self) -> Result<ObjectId> {
3578 self.catalog_id.ok_or_else(|| {
3579 PdfError::InvalidOperation(
3580 "catalog_id not initialized - write_document() must be called first".to_string(),
3581 )
3582 })
3583 }
3584
3585 fn get_pages_id(&self) -> Result<ObjectId> {
3587 self.pages_id.ok_or_else(|| {
3588 PdfError::InvalidOperation(
3589 "pages_id not initialized - write_document() must be called first".to_string(),
3590 )
3591 })
3592 }
3593
3594 fn get_info_id(&self) -> Result<ObjectId> {
3596 self.info_id.ok_or_else(|| {
3597 PdfError::InvalidOperation(
3598 "info_id not initialized - write_document() must be called first".to_string(),
3599 )
3600 })
3601 }
3602
3603 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
3604 use crate::writer::ObjectStreamWriter;
3605
3606 let object = if let Some(ref enc_state) = self.encryption_state {
3608 let mut obj = object;
3609 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
3610 obj
3611 } else {
3612 object
3613 };
3614
3615 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
3617 let mut buffer = Vec::new();
3618 self.write_object_value_to_buffer(&object, &mut buffer)?;
3619 self.buffered_objects.insert(id, buffer);
3620 return Ok(());
3621 }
3622
3623 self.xref_positions.insert(id, self.current_position);
3625
3626 let header = format!("{} {} obj\n", id.number(), id.generation());
3628 self.write_bytes(header.as_bytes())?;
3629
3630 self.write_object_value(&object)?;
3631
3632 self.write_bytes(b"\nendobj\n")?;
3633 Ok(())
3634 }
3635
3636 fn write_object_value(&mut self, object: &Object) -> Result<()> {
3637 match object {
3638 Object::Null => self.write_bytes(b"null")?,
3639 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
3640 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
3641 Object::Real(f) => self.write_bytes(
3642 format!("{f:.6}")
3643 .trim_end_matches('0')
3644 .trim_end_matches('.')
3645 .as_bytes(),
3646 )?,
3647 Object::String(s) => {
3648 self.write_bytes(b"(")?;
3657 self.write_bytes(&escape_pdf_string_bytes(s.as_bytes()))?;
3658 self.write_bytes(b")")?;
3659 }
3660 Object::ByteString(bytes) => {
3661 self.write_bytes(b"<")?;
3663 for byte in bytes {
3664 self.write_bytes(format!("{byte:02X}").as_bytes())?;
3665 }
3666 self.write_bytes(b">")?;
3667 }
3668 Object::Name(n) => {
3669 self.write_bytes(b"/")?;
3670 self.write_bytes(n.as_bytes())?;
3671 }
3672 Object::Array(arr) => {
3673 self.write_bytes(b"[")?;
3674 for (i, obj) in arr.iter().enumerate() {
3675 if i > 0 {
3676 self.write_bytes(b" ")?;
3677 }
3678 self.write_object_value(obj)?;
3679 }
3680 self.write_bytes(b"]")?;
3681 }
3682 Object::Dictionary(dict) => {
3683 self.write_bytes(b"<<")?;
3691 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3692 entries.sort_by_key(|(k, _)| k.as_str());
3693 for (key, value) in entries {
3694 self.write_bytes(b"\n/")?;
3695 self.write_bytes(key.as_bytes())?;
3696 self.write_bytes(b" ")?;
3697 self.write_object_value(value)?;
3698 }
3699 self.write_bytes(b"\n>>")?;
3700 }
3701 Object::Stream(dict, data) => {
3702 let mut corrected_dict = dict.clone();
3705 corrected_dict.set("Length", Object::Integer(data.len() as i64));
3706
3707 self.write_object_value(&Object::Dictionary(corrected_dict))?;
3708 self.write_bytes(b"\nstream\n")?;
3709 self.write_bytes(data)?;
3710 self.write_bytes(b"\nendstream")?;
3711 }
3712 Object::Reference(id) => {
3713 let ref_str = format!("{} {} R", id.number(), id.generation());
3714 self.write_bytes(ref_str.as_bytes())?;
3715 }
3716 }
3717 Ok(())
3718 }
3719
3720 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
3722 match object {
3723 Object::Null => buffer.extend_from_slice(b"null"),
3724 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
3725 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
3726 Object::Real(f) => buffer.extend_from_slice(
3727 format!("{f:.6}")
3728 .trim_end_matches('0')
3729 .trim_end_matches('.')
3730 .as_bytes(),
3731 ),
3732 Object::String(s) => {
3733 buffer.push(b'(');
3736 buffer.extend_from_slice(&escape_pdf_string_bytes(s.as_bytes()));
3737 buffer.push(b')');
3738 }
3739 Object::ByteString(bytes) => {
3740 buffer.push(b'<');
3741 for byte in bytes {
3742 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
3743 }
3744 buffer.push(b'>');
3745 }
3746 Object::Name(n) => {
3747 buffer.push(b'/');
3748 buffer.extend_from_slice(n.as_bytes());
3749 }
3750 Object::Array(arr) => {
3751 buffer.push(b'[');
3752 for (i, obj) in arr.iter().enumerate() {
3753 if i > 0 {
3754 buffer.push(b' ');
3755 }
3756 self.write_object_value_to_buffer(obj, buffer)?;
3757 }
3758 buffer.push(b']');
3759 }
3760 Object::Dictionary(dict) => {
3761 buffer.extend_from_slice(b"<<");
3765 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3766 entries.sort_by_key(|(k, _)| k.as_str());
3767 for (key, value) in entries {
3768 buffer.extend_from_slice(b"\n/");
3769 buffer.extend_from_slice(key.as_bytes());
3770 buffer.push(b' ');
3771 self.write_object_value_to_buffer(value, buffer)?;
3772 }
3773 buffer.extend_from_slice(b"\n>>");
3774 }
3775 Object::Stream(_, _) => {
3776 return Err(crate::error::PdfError::ObjectStreamError(
3778 "Cannot compress stream objects in object streams".to_string(),
3779 ));
3780 }
3781 Object::Reference(id) => {
3782 let ref_str = format!("{} {} R", id.number(), id.generation());
3783 buffer.extend_from_slice(ref_str.as_bytes());
3784 }
3785 }
3786 Ok(())
3787 }
3788
3789 fn flush_object_streams(&mut self) -> Result<()> {
3791 if self.buffered_objects.is_empty() {
3792 return Ok(());
3793 }
3794
3795 let config = ObjectStreamConfig {
3797 max_objects_per_stream: 100,
3798 compression_level: 6,
3799 enabled: true,
3800 };
3801 let mut os_writer = ObjectStreamWriter::new(config);
3802
3803 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
3805 buffered.sort_by_key(|(id, _)| id.number());
3806
3807 for (id, data) in buffered {
3809 os_writer.add_object(*id, data.clone())?;
3810 }
3811
3812 let streams = os_writer.finalize()?;
3814
3815 for mut stream in streams {
3817 let stream_id = stream.stream_id;
3818
3819 let compressed_data = stream.generate_stream_data(6)?;
3821
3822 let dict = stream.generate_dictionary(&compressed_data);
3824
3825 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
3827 self.compressed_object_map
3828 .insert(*obj_id, (stream_id, index as u32));
3829 }
3830
3831 self.xref_positions.insert(stream_id, self.current_position);
3833
3834 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
3835 self.write_bytes(header.as_bytes())?;
3836
3837 self.write_object_value(&Object::Dictionary(dict))?;
3838
3839 self.write_bytes(b"\nstream\n")?;
3840 self.write_bytes(&compressed_data)?;
3841 self.write_bytes(b"\nendstream\nendobj\n")?;
3842 }
3843
3844 Ok(())
3845 }
3846
3847 fn write_xref(&mut self) -> Result<()> {
3848 self.write_bytes(b"xref\n")?;
3849
3850 let mut entries: Vec<_> = self
3852 .xref_positions
3853 .iter()
3854 .map(|(id, pos)| (*id, *pos))
3855 .collect();
3856 entries.sort_by_key(|(id, _)| id.number());
3857
3858 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
3860
3861 self.write_bytes(b"0 ")?;
3864 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
3865 self.write_bytes(b"\n")?;
3866
3867 self.write_bytes(b"0000000000 65535 f \n")?;
3869
3870 for obj_num in 1..=max_obj_num {
3873 let _obj_id = ObjectId::new(obj_num, 0);
3874 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
3875 let entry = format!("{:010} {:05} n \n", position, 0);
3876 self.write_bytes(entry.as_bytes())?;
3877 } else {
3878 self.write_bytes(b"0000000000 00000 f \n")?;
3880 }
3881 }
3882
3883 Ok(())
3884 }
3885
3886 fn write_xref_stream(&mut self) -> Result<()> {
3887 let catalog_id = self.get_catalog_id()?;
3888 let info_id = self.get_info_id()?;
3889
3890 let xref_stream_id = self.allocate_object_id();
3892 let xref_position = self.current_position;
3893
3894 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
3896 xref_writer.set_trailer_info(catalog_id, info_id);
3897
3898 xref_writer.add_free_entry(0, 65535);
3900
3901 let mut entries: Vec<_> = self
3903 .xref_positions
3904 .iter()
3905 .map(|(id, pos)| (*id, *pos))
3906 .collect();
3907 entries.sort_by_key(|(id, _)| id.number());
3908
3909 let max_obj_num = entries
3911 .iter()
3912 .map(|(id, _)| id.number())
3913 .max()
3914 .unwrap_or(0)
3915 .max(xref_stream_id.number());
3916
3917 for obj_num in 1..=max_obj_num {
3919 let obj_id = ObjectId::new(obj_num, 0);
3920
3921 if obj_num == xref_stream_id.number() {
3922 xref_writer.add_in_use_entry(xref_position, 0);
3924 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
3925 xref_writer.add_compressed_entry(stream_id.number(), *index);
3927 } else if let Some((id, position)) =
3928 entries.iter().find(|(id, _)| id.number() == obj_num)
3929 {
3930 xref_writer.add_in_use_entry(*position, id.generation());
3932 } else {
3933 xref_writer.add_free_entry(0, 0);
3935 }
3936 }
3937
3938 self.xref_positions.insert(xref_stream_id, xref_position);
3940
3941 self.write_bytes(
3943 format!(
3944 "{} {} obj\n",
3945 xref_stream_id.number(),
3946 xref_stream_id.generation()
3947 )
3948 .as_bytes(),
3949 )?;
3950
3951 let uncompressed_data = xref_writer.encode_entries();
3953 let final_data = if self.config.compress_streams {
3954 crate::compression::compress(&uncompressed_data)?
3955 } else {
3956 uncompressed_data
3957 };
3958
3959 let mut dict = xref_writer.create_dictionary(None);
3961 dict.set("Length", Object::Integer(final_data.len() as i64));
3962
3963 if self.config.compress_streams {
3965 dict.set("Filter", Object::Name("FlateDecode".to_string()));
3966 }
3967 self.write_bytes(b"<<")?;
3968 for (key, value) in dict.iter() {
3969 self.write_bytes(b"\n/")?;
3970 self.write_bytes(key.as_bytes())?;
3971 self.write_bytes(b" ")?;
3972 self.write_object_value(value)?;
3973 }
3974 self.write_bytes(b"\n>>\n")?;
3975
3976 self.write_bytes(b"stream\n")?;
3978 self.write_bytes(&final_data)?;
3979 self.write_bytes(b"\nendstream\n")?;
3980 self.write_bytes(b"endobj\n")?;
3981
3982 self.write_bytes(b"\nstartxref\n")?;
3984 self.write_bytes(xref_position.to_string().as_bytes())?;
3985 self.write_bytes(b"\n%%EOF\n")?;
3986
3987 Ok(())
3988 }
3989
3990 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
3997 use crate::encryption::{
3998 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
3999 };
4000 use std::sync::Arc;
4001
4002 let mut fid = vec![0u8; 16];
4004 use rand::Rng;
4005 rand::rng().fill_bytes(&mut fid);
4006
4007 let enc_dict = encryption
4008 .create_encryption_dict(Some(&fid))
4009 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
4010
4011 let enc_key = encryption
4013 .get_encryption_key(&enc_dict, Some(&fid))
4014 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
4015
4016 let handler = encryption.handler();
4018 let (method, key_len) = match encryption.strength {
4019 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
4020 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
4021 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
4022 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
4023 };
4024
4025 let std_filter = FunctionalCryptFilter {
4026 name: "StdCF".to_string(),
4027 method,
4028 length: key_len,
4029 auth_event: crate::encryption::AuthEvent::DocOpen,
4030 recipients: None,
4031 };
4032
4033 let mut filter_manager =
4034 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
4035 filter_manager.add_filter(std_filter);
4036
4037 let encryptor =
4038 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
4039
4040 let encrypt_id = self.allocate_object_id();
4042 self.encrypt_obj_id = Some(encrypt_id);
4043 self.file_id = Some(fid);
4044 self.encryption_state = Some(WriterEncryptionState { encryptor });
4045
4046 self.pending_encrypt_dict = Some(enc_dict.to_dict());
4048
4049 Ok(())
4050 }
4051
4052 fn write_encryption_dict(&mut self) -> Result<()> {
4054 if let (Some(encrypt_id), Some(dict)) =
4055 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
4056 {
4057 let enc_state = self.encryption_state.take();
4059 self.write_object(encrypt_id, Object::Dictionary(dict))?;
4060 self.encryption_state = enc_state;
4061 }
4062 Ok(())
4063 }
4064
4065 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
4066 let catalog_id = self.get_catalog_id()?;
4067 let info_id = self.get_info_id()?;
4068 let max_obj_num = self
4070 .xref_positions
4071 .keys()
4072 .map(|id| id.number())
4073 .max()
4074 .unwrap_or(0);
4075
4076 let mut trailer = Dictionary::new();
4077 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
4078 trailer.set("Root", Object::Reference(catalog_id));
4079 trailer.set("Info", Object::Reference(info_id));
4080
4081 if let Some(prev_xref) = self.prev_xref_offset {
4083 trailer.set("Prev", Object::Integer(prev_xref as i64));
4084 }
4085
4086 if let Some(encrypt_id) = self.encrypt_obj_id {
4088 trailer.set("Encrypt", Object::Reference(encrypt_id));
4089 }
4090 if let Some(ref fid) = self.file_id {
4091 trailer.set(
4092 "ID",
4093 Object::Array(vec![
4094 Object::ByteString(fid.clone()),
4095 Object::ByteString(fid.clone()),
4096 ]),
4097 );
4098 }
4099
4100 self.write_bytes(b"trailer\n")?;
4101 self.write_object_value(&Object::Dictionary(trailer))?;
4102 self.write_bytes(b"\nstartxref\n")?;
4103 self.write_bytes(xref_position.to_string().as_bytes())?;
4104 self.write_bytes(b"\n%%EOF\n")?;
4105
4106 Ok(())
4107 }
4108
4109 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
4110 self.writer.write_all(data)?;
4111 self.current_position += data.len() as u64;
4112 Ok(())
4113 }
4114
4115 #[allow(dead_code)]
4116 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
4117 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
4119 if rect_array.len() >= 4 {
4120 if let (
4121 Some(Object::Real(x1)),
4122 Some(Object::Real(y1)),
4123 Some(Object::Real(x2)),
4124 Some(Object::Real(y2)),
4125 ) = (
4126 rect_array.first(),
4127 rect_array.get(1),
4128 rect_array.get(2),
4129 rect_array.get(3),
4130 ) {
4131 (*x1, *y1, *x2, *y2)
4132 } else {
4133 (0.0, 0.0, 100.0, 20.0) }
4135 } else {
4136 (0.0, 0.0, 100.0, 20.0) }
4138 } else {
4139 (0.0, 0.0, 100.0, 20.0) };
4141
4142 let width = rect.2 - rect.0;
4143 let height = rect.3 - rect.1;
4144
4145 let mut content = String::new();
4147
4148 content.push_str("q\n");
4150
4151 crate::graphics::color::write_stroke_color(&mut content, crate::graphics::Color::black());
4153 content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
4157 content.push_str("S\n"); crate::graphics::color::write_fill_color(&mut content, crate::graphics::Color::white());
4161 content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
4162 content.push_str("f\n"); content.push_str("Q\n");
4166
4167 let mut stream_dict = Dictionary::new();
4169 stream_dict.set("Type", Object::Name("XObject".to_string()));
4170 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4171 stream_dict.set(
4172 "BBox",
4173 Object::Array(vec![
4174 Object::Real(0.0),
4175 Object::Real(0.0),
4176 Object::Real(width),
4177 Object::Real(height),
4178 ]),
4179 );
4180 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4181 stream_dict.set("Length", Object::Integer(content.len() as i64));
4182
4183 let stream_id = self.allocate_object_id();
4185 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4186
4187 Ok(stream_id)
4188 }
4189
4190 #[allow(dead_code)]
4191 fn create_field_appearance_stream(
4192 &mut self,
4193 field_dict: &Dictionary,
4194 widget: &crate::forms::Widget,
4195 ) -> Result<ObjectId> {
4196 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
4197 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
4198
4199 let mut content = String::new();
4201
4202 content.push_str("q\n");
4204
4205 if let Some(bg_color) = &widget.appearance.background_color {
4208 crate::graphics::color::write_fill_color(&mut content, *bg_color);
4209 content.push_str(&format!("0 0 {width} {height} re\n"));
4210 content.push_str("f\n");
4211 }
4212
4213 if let Some(border_color) = &widget.appearance.border_color {
4215 crate::graphics::color::write_stroke_color(&mut content, *border_color);
4216 content.push_str(&format!("{} w\n", widget.appearance.border_width));
4217 content.push_str(&format!("0 0 {width} {height} re\n"));
4218 content.push_str("S\n");
4219 }
4220
4221 if let Some(Object::Name(ft)) = field_dict.get("FT") {
4223 if ft == "Btn" {
4224 if let Some(Object::Name(v)) = field_dict.get("V") {
4225 if v == "Yes" {
4226 crate::graphics::color::write_stroke_color(
4228 &mut content,
4229 crate::graphics::Color::black(),
4230 );
4231 content.push_str("2 w\n");
4232 let margin = width * 0.2;
4233 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
4234 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
4235 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
4236 content.push_str("S\n");
4237 }
4238 }
4239 }
4240 }
4241
4242 content.push_str("Q\n");
4244
4245 let mut stream_dict = Dictionary::new();
4247 stream_dict.set("Type", Object::Name("XObject".to_string()));
4248 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4249 stream_dict.set(
4250 "BBox",
4251 Object::Array(vec![
4252 Object::Real(0.0),
4253 Object::Real(0.0),
4254 Object::Real(width),
4255 Object::Real(height),
4256 ]),
4257 );
4258 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4259 stream_dict.set("Length", Object::Integer(content.len() as i64));
4260
4261 let stream_id = self.allocate_object_id();
4263 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4264
4265 Ok(stream_id)
4266 }
4267}
4268
4269fn format_pdf_date(date: DateTime<Utc>) -> String {
4271 let formatted = date.format("D:%Y%m%d%H%M%S");
4274
4275 format!("{formatted}+00'00")
4277}
4278
4279#[cfg(test)]
4280mod tests;
4281
4282#[cfg(test)]
4283mod rigorous_tests;