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) => {
898 WriterObj::String(String::from_utf8_lossy(s.as_bytes()).to_string())
899 }
900 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
901 PdfObj::Array(arr) => {
902 let items: Vec<WriterObj> = arr
903 .iter()
904 .map(|item| self.convert_pdf_object_to_writer(item))
905 .collect();
906 WriterObj::Array(items)
907 }
908 PdfObj::Dictionary(dict) => {
909 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
910 }
911 PdfObj::Stream(stream) => {
912 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
913 WriterObj::Stream(dict, stream.data.clone())
914 }
915 PdfObj::Reference(id) => {
916 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
917 }
918 }
919 }
920
921 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
922 let catalog_id = self.get_catalog_id()?;
923 let pages_id = self.get_pages_id()?;
924
925 let mut catalog = Dictionary::new();
926 catalog.set("Type", Object::Name("Catalog".to_string()));
927 catalog.set("Pages", Object::Reference(pages_id));
928
929 if let Some(form_manager) = &document.form_manager {
949 if document.acro_form.is_none() {
950 document.acro_form = Some(crate::forms::AcroForm::new());
951 }
952
953 let mut sorted: Vec<(Dictionary, crate::objects::ObjectReference)> = Vec::new();
959 for (name, form_field, placeholder) in form_manager.iter_fields_sorted() {
960 let real_id = *self.form_field_placeholder_map.get(&placeholder).ok_or_else(
961 || {
962 PdfError::Internal(format!(
963 "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"
964 ))
965 },
966 )?;
967 sorted.push((form_field.field_dict.clone(), real_id));
968 }
969 for (field_dict, real_id) in sorted {
970 self.write_object(real_id, Object::Dictionary(field_dict))?;
971 }
972
973 if let Some(acro) = document.acro_form.as_mut() {
974 for r in &self.form_manager_field_refs {
975 if !acro.fields.contains(r) {
976 acro.fields.push(*r);
977 }
978 }
979 }
980 }
981
982 if let Some(acro_form) = &document.acro_form {
984 let acro_form_id = self.allocate_object_id();
986
987 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
989
990 catalog.set("AcroForm", Object::Reference(acro_form_id));
992 }
993
994 if let Some(outline_tree) = &document.outline {
996 if !outline_tree.items.is_empty() {
997 let outline_root_id = self.write_outline_tree(outline_tree)?;
998 catalog.set("Outlines", Object::Reference(outline_root_id));
999 }
1000 }
1001
1002 if let Some(struct_tree) = &document.struct_tree {
1004 if !struct_tree.is_empty() {
1005 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
1006 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
1007 catalog.set("MarkInfo", {
1009 let mut mark_info = Dictionary::new();
1010 mark_info.set("Marked", Object::Boolean(true));
1011 Object::Dictionary(mark_info)
1012 });
1013 }
1014 }
1015
1016 let xmp_metadata = document.create_xmp_metadata();
1019 let xmp_packet = xmp_metadata.to_xmp_packet();
1020 let metadata_id = self.allocate_object_id();
1021
1022 let mut metadata_dict = Dictionary::new();
1024 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
1025 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
1026 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
1027
1028 self.write_object(
1030 metadata_id,
1031 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
1032 )?;
1033
1034 catalog.set("Metadata", Object::Reference(metadata_id));
1036
1037 if let Some(action) = &document.open_action {
1039 catalog.set("OpenAction", Object::Dictionary(action.to_dict()));
1040 }
1041
1042 if let Some(prefs) = &document.viewer_preferences {
1044 catalog.set("ViewerPreferences", Object::Dictionary(prefs.to_dict()));
1045 }
1046
1047 if let Some(named_dests) = &document.named_destinations {
1052 let dests_tree_id = self.allocate_object_id();
1053 self.write_object(dests_tree_id, Object::Dictionary(named_dests.to_dict()))?;
1054
1055 let mut names_dict = Dictionary::new();
1056 names_dict.set("Dests", Object::Reference(dests_tree_id));
1057 let names_dict_id = self.allocate_object_id();
1058 self.write_object(names_dict_id, Object::Dictionary(names_dict))?;
1059
1060 catalog.set("Names", Object::Reference(names_dict_id));
1061 }
1062
1063 if let Some(page_labels) = &document.page_labels {
1067 let labels_id = self.allocate_object_id();
1068 self.write_object(labels_id, Object::Dictionary(page_labels.to_dict()))?;
1069 catalog.set("PageLabels", Object::Reference(labels_id));
1070 }
1071
1072 self.write_object(catalog_id, Object::Dictionary(catalog))?;
1073 Ok(())
1074 }
1075
1076 fn write_page_content(&mut self, content_id: ObjectId, page: &crate::page::Page) -> Result<()> {
1077 let mut page_copy = page.clone();
1078 let content = page_copy.generate_content()?;
1079
1080 #[cfg(feature = "compression")]
1082 {
1083 use crate::objects::Stream;
1084 let mut stream = Stream::new(content);
1085 if self.config.compress_streams {
1087 stream.compress_flate()?;
1088 }
1089
1090 self.write_object(
1091 content_id,
1092 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
1093 )?;
1094 }
1095
1096 #[cfg(not(feature = "compression"))]
1097 {
1098 let mut stream_dict = Dictionary::new();
1099 stream_dict.set("Length", Object::Integer(content.len() as i64));
1100
1101 self.write_object(content_id, Object::Stream(stream_dict, content))?;
1102 }
1103
1104 Ok(())
1105 }
1106
1107 fn write_outline_tree(
1108 &mut self,
1109 outline_tree: &crate::structure::OutlineTree,
1110 ) -> Result<ObjectId> {
1111 let outline_root_id = self.allocate_object_id();
1113
1114 let mut outline_root = Dictionary::new();
1115 outline_root.set("Type", Object::Name("Outlines".to_string()));
1116
1117 if !outline_tree.items.is_empty() {
1118 let mut item_ids = Vec::new();
1120
1121 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
1123 let mut count = items.len();
1124 for item in items {
1125 count += count_items(&item.children);
1126 }
1127 count
1128 }
1129
1130 let total_items = count_items(&outline_tree.items);
1131
1132 for _ in 0..total_items {
1134 item_ids.push(self.allocate_object_id());
1135 }
1136
1137 let mut id_index = 0;
1138
1139 let first_id = item_ids[0];
1141 let last_id = item_ids[outline_tree.items.len() - 1];
1142
1143 outline_root.set("First", Object::Reference(first_id));
1144 outline_root.set("Last", Object::Reference(last_id));
1145
1146 let visible_count = outline_tree.visible_count();
1148 outline_root.set("Count", Object::Integer(visible_count));
1149
1150 let mut written_items = Vec::new();
1152
1153 for (i, item) in outline_tree.items.iter().enumerate() {
1154 let item_id = item_ids[id_index];
1155 id_index += 1;
1156
1157 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1158 let next_id = if i < outline_tree.items.len() - 1 {
1159 Some(item_ids[i + 1])
1160 } else {
1161 None
1162 };
1163
1164 let children_ids = self.write_outline_item(
1166 item,
1167 item_id,
1168 outline_root_id,
1169 prev_id,
1170 next_id,
1171 &mut item_ids,
1172 &mut id_index,
1173 )?;
1174
1175 written_items.extend(children_ids);
1176 }
1177 }
1178
1179 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1180 Ok(outline_root_id)
1181 }
1182
1183 #[allow(clippy::too_many_arguments)]
1184 fn write_outline_item(
1185 &mut self,
1186 item: &crate::structure::OutlineItem,
1187 item_id: ObjectId,
1188 parent_id: ObjectId,
1189 prev_id: Option<ObjectId>,
1190 next_id: Option<ObjectId>,
1191 all_ids: &mut Vec<ObjectId>,
1192 id_index: &mut usize,
1193 ) -> Result<Vec<ObjectId>> {
1194 let mut written_ids = vec![item_id];
1195
1196 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1198 let first_idx = *id_index;
1199 let first_id = all_ids[first_idx];
1200 let last_idx = first_idx + item.children.len() - 1;
1201 let last_id = all_ids[last_idx];
1202
1203 for (i, child) in item.children.iter().enumerate() {
1205 let child_id = all_ids[*id_index];
1206 *id_index += 1;
1207
1208 let child_prev = if i > 0 {
1209 Some(all_ids[first_idx + i - 1])
1210 } else {
1211 None
1212 };
1213 let child_next = if i < item.children.len() - 1 {
1214 Some(all_ids[first_idx + i + 1])
1215 } else {
1216 None
1217 };
1218
1219 let child_ids = self.write_outline_item(
1220 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1222 )?;
1223
1224 written_ids.extend(child_ids);
1225 }
1226
1227 (Some(first_id), Some(last_id))
1228 } else {
1229 (None, None)
1230 };
1231
1232 let item_dict = crate::structure::outline_item_to_dict(
1234 item,
1235 parent_id,
1236 first_child_id,
1237 last_child_id,
1238 prev_id,
1239 next_id,
1240 );
1241
1242 self.write_object(item_id, Object::Dictionary(item_dict))?;
1243
1244 Ok(written_ids)
1245 }
1246
1247 fn write_struct_tree(
1249 &mut self,
1250 struct_tree: &crate::structure::StructTree,
1251 ) -> Result<ObjectId> {
1252 let struct_tree_root_id = self.allocate_object_id();
1254 let mut element_ids = Vec::new();
1255 for _ in 0..struct_tree.len() {
1256 element_ids.push(self.allocate_object_id());
1257 }
1258
1259 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1261 std::collections::HashMap::new();
1262
1263 if let Some(root_index) = struct_tree.root_index() {
1265 parent_map.insert(root_index, struct_tree_root_id);
1266
1267 fn map_children_parents(
1269 tree: &crate::structure::StructTree,
1270 parent_index: usize,
1271 parent_id: ObjectId,
1272 element_ids: &[ObjectId],
1273 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1274 ) {
1275 if let Some(parent_elem) = tree.get(parent_index) {
1276 for &child_index in &parent_elem.children {
1277 parent_map.insert(child_index, parent_id);
1278 map_children_parents(
1279 tree,
1280 child_index,
1281 element_ids[child_index],
1282 element_ids,
1283 parent_map,
1284 );
1285 }
1286 }
1287 }
1288
1289 map_children_parents(
1290 struct_tree,
1291 root_index,
1292 element_ids[root_index],
1293 &element_ids,
1294 &mut parent_map,
1295 );
1296 }
1297
1298 for (index, element) in struct_tree.iter().enumerate() {
1300 let element_id = element_ids[index];
1301 let mut element_dict = Dictionary::new();
1302
1303 element_dict.set("Type", Object::Name("StructElem".to_string()));
1304 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1305
1306 if let Some(&parent_id) = parent_map.get(&index) {
1308 element_dict.set("P", Object::Reference(parent_id));
1309 }
1310
1311 if let Some(ref id) = element.id {
1313 element_dict.set("ID", Object::String(id.clone()));
1314 }
1315
1316 if let Some(ref lang) = element.attributes.lang {
1318 element_dict.set("Lang", Object::String(lang.clone()));
1319 }
1320 if let Some(ref alt) = element.attributes.alt {
1321 element_dict.set("Alt", Object::String(alt.clone()));
1322 }
1323 if let Some(ref actual_text) = element.attributes.actual_text {
1324 element_dict.set("ActualText", Object::String(actual_text.clone()));
1325 }
1326 if let Some(ref title) = element.attributes.title {
1327 element_dict.set("T", Object::String(title.clone()));
1328 }
1329 if let Some(bbox) = element.attributes.bbox {
1330 element_dict.set(
1331 "BBox",
1332 Object::Array(vec![
1333 Object::Real(bbox[0]),
1334 Object::Real(bbox[1]),
1335 Object::Real(bbox[2]),
1336 Object::Real(bbox[3]),
1337 ]),
1338 );
1339 }
1340
1341 let mut kids = Vec::new();
1343
1344 for &child_index in &element.children {
1346 kids.push(Object::Reference(element_ids[child_index]));
1347 }
1348
1349 for mcid_ref in &element.mcids {
1351 let mut mcr = Dictionary::new();
1352 mcr.set("Type", Object::Name("MCR".to_string()));
1353 mcr.set("Pg", Object::Integer(mcid_ref.page_index as i64));
1354 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1355 kids.push(Object::Dictionary(mcr));
1356 }
1357
1358 if !kids.is_empty() {
1359 element_dict.set("K", Object::Array(kids));
1360 }
1361
1362 self.write_object(element_id, Object::Dictionary(element_dict))?;
1363 }
1364
1365 let mut struct_tree_root = Dictionary::new();
1367 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1368
1369 if let Some(root_index) = struct_tree.root_index() {
1371 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1372 }
1373
1374 if !struct_tree.role_map.mappings().is_empty() {
1376 let mut role_map = Dictionary::new();
1377 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1378 role_map.set(
1379 custom_type.as_str(),
1380 Object::Name(standard_type.as_pdf_name().to_string()),
1381 );
1382 }
1383 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1384 }
1385
1386 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1387 Ok(struct_tree_root_id)
1388 }
1389
1390 fn preallocate_form_manager_fields(&mut self, document: &Document) -> Result<()> {
1403 let Some(form_manager) = &document.form_manager else {
1404 return Ok(());
1405 };
1406
1407 for (_name, _form_field, placeholder) in form_manager.iter_fields_sorted() {
1408 let real_id = self.allocate_object_id();
1409 self.form_field_placeholder_map.insert(placeholder, real_id);
1410 self.form_manager_field_refs.push(real_id);
1411 }
1412 Ok(())
1413 }
1414
1415 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1416 if !self.form_field_ids.is_empty() {
1418 if let Some(acro_form) = &mut document.acro_form {
1419 acro_form.fields.clear();
1421 for field_id in &self.form_field_ids {
1422 acro_form.add_field(*field_id);
1423 }
1424
1425 acro_form.need_appearances = true;
1427 if acro_form.da.is_none() {
1428 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1429 }
1430 }
1431 }
1432 Ok(())
1433 }
1434
1435 fn write_info(&mut self, document: &Document) -> Result<()> {
1436 let info_id = self.get_info_id()?;
1437 let mut info_dict = Dictionary::new();
1438
1439 if let Some(ref title) = document.metadata.title {
1440 info_dict.set("Title", Object::String(title.clone()));
1441 }
1442 if let Some(ref author) = document.metadata.author {
1443 info_dict.set("Author", Object::String(author.clone()));
1444 }
1445 if let Some(ref subject) = document.metadata.subject {
1446 info_dict.set("Subject", Object::String(subject.clone()));
1447 }
1448 if let Some(ref keywords) = document.metadata.keywords {
1449 info_dict.set("Keywords", Object::String(keywords.clone()));
1450 }
1451 if let Some(ref creator) = document.metadata.creator {
1452 info_dict.set("Creator", Object::String(creator.clone()));
1453 }
1454 if let Some(ref producer) = document.metadata.producer {
1455 info_dict.set("Producer", Object::String(producer.clone()));
1456 }
1457
1458 if let Some(creation_date) = document.metadata.creation_date {
1460 let date_string = format_pdf_date(creation_date);
1461 info_dict.set("CreationDate", Object::String(date_string));
1462 }
1463
1464 if let Some(mod_date) = document.metadata.modification_date {
1466 let date_string = format_pdf_date(mod_date);
1467 info_dict.set("ModDate", Object::String(date_string));
1468 }
1469
1470 let edition = super::Edition::OpenSource;
1473
1474 let signature = super::PdfSignature::new(document, edition);
1475 signature.write_to_info_dict(&mut info_dict);
1476
1477 self.write_object(info_id, Object::Dictionary(info_dict))?;
1478 Ok(())
1479 }
1480
1481 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1482 let mut font_refs = HashMap::new();
1483
1484 for font_name in document.custom_font_names() {
1494 let has_usage = self
1495 .document_used_chars_by_font
1496 .get(&font_name)
1497 .map(|chars| !chars.is_empty())
1498 .unwrap_or(false);
1499 if !has_usage {
1500 continue;
1501 }
1502 if let Some(font) = document.get_custom_font(&font_name) {
1503 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1506 font_refs.insert(font_name.clone(), font_id);
1507 }
1508 }
1509
1510 Ok(font_refs)
1511 }
1512
1513 fn write_font_with_unicode_support(
1515 &mut self,
1516 font_name: &str,
1517 font: &crate::fonts::Font,
1518 ) -> Result<ObjectId> {
1519 self.write_type0_font_from_font(font_name, font)
1522 }
1523
1524 fn write_type0_font_from_font(
1526 &mut self,
1527 font_name: &str,
1528 font: &crate::fonts::Font,
1529 ) -> Result<ObjectId> {
1530 let used_chars = self
1537 .document_used_chars_by_font
1538 .get(font_name)
1539 .cloned()
1540 .unwrap_or_else(|| {
1541 let mut chars = std::collections::HashSet::new();
1542 for ch in
1543 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1544 {
1545 chars.insert(ch);
1546 }
1547 chars
1548 });
1549
1550 let used_text: String = used_chars.iter().copied().collect();
1558 let mut missing = font.missing_glyphs(&used_text);
1559 if !missing.is_empty() {
1560 missing.sort_unstable();
1561 let list = missing
1562 .iter()
1563 .map(|c| format!("U+{:04X} {:?}", *c as u32, c))
1564 .collect::<Vec<_>>()
1565 .join(", ");
1566 tracing::warn!(
1567 "Custom font '{}' has no glyph for {} character(s): {}. \
1568 They will render as .notdef (empty boxes); the embedded font \
1569 does not contain these glyphs.",
1570 font_name,
1571 missing.len(),
1572 list
1573 );
1574 }
1575
1576 let font_id = self.allocate_object_id();
1578 let descendant_font_id = self.allocate_object_id();
1579 let descriptor_id = self.allocate_object_id();
1580 let font_file_id = self.allocate_object_id();
1581 let to_unicode_id = self.allocate_object_id();
1582
1583 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1588 if font.data.len() > 100_000 && !used_chars.is_empty() {
1589 match crate::text::fonts::truetype_subsetter::subset_font(
1590 font.data.clone(),
1591 &used_chars,
1592 ) {
1593 Ok(subset_result) => (
1594 subset_result.font_data,
1595 Some(subset_result.glyph_mapping),
1596 font.clone(),
1597 ),
1598 Err(_) => {
1599 if font.data.len() < 25_000_000 {
1600 (font.data.clone(), None, font.clone())
1601 } else {
1602 (Vec::new(), None, font.clone())
1603 }
1604 }
1605 }
1606 } else {
1607 (font.data.clone(), None, font.clone())
1608 };
1609
1610 if !font_data_to_embed.is_empty() {
1611 let mut font_file_dict = Dictionary::new();
1616 match font.format {
1617 crate::fonts::FontFormat::OpenType => {
1618 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1619 }
1620 crate::fonts::FontFormat::TrueType => {
1621 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1622 }
1623 }
1624
1625 #[cfg(feature = "compression")]
1630 {
1631 let font_stream_obj = if self.config.compress_streams {
1632 let mut stream =
1633 crate::objects::Stream::with_dictionary(font_file_dict, font_data_to_embed);
1634 stream.compress_flate()?;
1635 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1636 } else {
1637 Object::Stream(font_file_dict, font_data_to_embed)
1638 };
1639 self.write_object(font_file_id, font_stream_obj)?;
1640 }
1641 #[cfg(not(feature = "compression"))]
1642 {
1643 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1644 self.write_object(font_file_id, font_stream_obj)?;
1645 }
1646 } else {
1647 let font_file_dict = Dictionary::new();
1649 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1650 self.write_object(font_file_id, font_stream_obj)?;
1651 }
1652
1653 let mut descriptor = Dictionary::new();
1655 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1656 descriptor.set("FontName", Object::Name(font_name.to_string()));
1657 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1659 "FontBBox",
1660 Object::Array(vec![
1661 Object::Integer(font.descriptor.font_bbox[0] as i64),
1662 Object::Integer(font.descriptor.font_bbox[1] as i64),
1663 Object::Integer(font.descriptor.font_bbox[2] as i64),
1664 Object::Integer(font.descriptor.font_bbox[3] as i64),
1665 ]),
1666 );
1667 descriptor.set(
1668 "ItalicAngle",
1669 Object::Real(font.descriptor.italic_angle as f64),
1670 );
1671 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1672 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1673 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1674 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1675 let font_file_key = match font.format {
1677 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1680 descriptor.set(font_file_key, Object::Reference(font_file_id));
1681 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1682
1683 let mut cid_font = Dictionary::new();
1685 cid_font.set("Type", Object::Name("Font".to_string()));
1686 let cid_font_subtype = match font.format {
1688 crate::fonts::FontFormat::OpenType => "CIDFontType0",
1689 crate::fonts::FontFormat::TrueType => "CIDFontType2",
1690 };
1691 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1692 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1693
1694 let mut cid_system_info = Dictionary::new();
1696 let (registry, ordering, supplement) =
1697 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1698 cjk_type.cid_system_info()
1699 } else {
1700 ("Adobe", "Identity", 0)
1701 };
1702
1703 cid_system_info.set("Registry", Object::String(registry.to_string()));
1704 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1705 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1706 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1707
1708 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1709
1710 let default_width = self.calculate_default_width(font);
1712 cid_font.set("DW", Object::Integer(default_width));
1713
1714 let w_array = self.generate_width_array(
1718 &original_font_for_widths,
1719 default_width,
1720 subset_glyph_mapping.as_ref(),
1721 );
1722 cid_font.set("W", Object::Array(w_array));
1723
1724 if cid_font_subtype == "CIDFontType2" {
1728 let cid_to_gid_map =
1730 self.generate_cid_to_gid_map(font_name, font, subset_glyph_mapping.as_ref())?;
1731 if !cid_to_gid_map.is_empty() {
1732 let cid_to_gid_map_id = self.allocate_object_id();
1740 let map_dict = Dictionary::new();
1741 #[cfg(feature = "compression")]
1742 let map_stream = if self.config.compress_streams {
1743 let mut stream =
1744 crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1745 stream.compress_flate()?;
1746 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1747 } else {
1748 let mut d = map_dict;
1749 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1750 Object::Stream(d, cid_to_gid_map)
1751 };
1752 #[cfg(not(feature = "compression"))]
1753 let map_stream = {
1754 let mut d = map_dict;
1755 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1756 Object::Stream(d, cid_to_gid_map)
1757 };
1758 self.write_object(cid_to_gid_map_id, map_stream)?;
1759 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1760 } else {
1761 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1762 }
1763 }
1764 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1767
1768 let cmap_data = self.generate_tounicode_cmap_from_font(font_name, font);
1774 let cmap_dict = Dictionary::new();
1775 #[cfg(feature = "compression")]
1776 let cmap_stream = if self.config.compress_streams {
1777 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1778 stream.compress_flate()?;
1779 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1780 } else {
1781 Object::Stream(cmap_dict, cmap_data)
1782 };
1783 #[cfg(not(feature = "compression"))]
1784 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1785 self.write_object(to_unicode_id, cmap_stream)?;
1786
1787 let mut type0_font = Dictionary::new();
1789 type0_font.set("Type", Object::Name("Font".to_string()));
1790 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1791 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1792 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1793 type0_font.set(
1794 "DescendantFonts",
1795 Object::Array(vec![Object::Reference(descendant_font_id)]),
1796 );
1797 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1798
1799 self.write_object(font_id, Object::Dictionary(type0_font))?;
1800
1801 Ok(font_id)
1802 }
1803
1804 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
1806 use crate::text::fonts::truetype::TrueTypeFont;
1807
1808 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1810 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1811 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
1812 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
1813 let common_chars =
1817 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
1818 let mut total_width = 0;
1819 let mut count = 0;
1820
1821 for ch in common_chars.chars() {
1822 let unicode = ch as u32;
1823 if let Some(&pdf_width) = widths.get(&unicode) {
1824 total_width += pdf_width as i64;
1825 count += 1;
1826 }
1827 }
1828
1829 if count > 0 {
1830 return total_width / count;
1831 }
1832 }
1833 }
1834 }
1835 }
1836
1837 500
1839 }
1840
1841 fn generate_width_array(
1843 &self,
1844 font: &crate::fonts::Font,
1845 _default_width: i64,
1846 subset_mapping: Option<&HashMap<u32, u16>>,
1847 ) -> Vec<Object> {
1848 use crate::text::fonts::truetype::TrueTypeFont;
1849
1850 let mut w_array = Vec::new();
1851
1852 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1854 let char_to_glyph = {
1858 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1860 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
1861 if let Some(subset_map) = subset_mapping {
1863 let mut filtered = HashMap::new();
1864 for unicode in subset_map.keys() {
1865 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
1867 filtered.insert(*unicode, orig_glyph);
1868 }
1869 }
1870 filtered
1871 } else {
1872 cmap.mappings.clone()
1873 }
1874 } else {
1875 HashMap::new()
1876 }
1877 } else {
1878 HashMap::new()
1879 }
1880 };
1881
1882 if !char_to_glyph.is_empty() {
1883 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
1885 let mut sorted_chars: Vec<_> = widths.iter().collect();
1890 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
1891
1892 let mut i = 0;
1893 while i < sorted_chars.len() {
1894 let start_unicode = *sorted_chars[i].0;
1895 let pdf_width = *sorted_chars[i].1 as i64;
1897
1898 let mut end_unicode = start_unicode;
1900 let mut j = i + 1;
1901 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
1902 let next_pdf_width = *sorted_chars[j].1 as i64;
1903 if next_pdf_width == pdf_width {
1904 end_unicode = *sorted_chars[j].0;
1905 j += 1;
1906 } else {
1907 break;
1908 }
1909 }
1910
1911 if start_unicode == end_unicode {
1913 w_array.push(Object::Integer(start_unicode as i64));
1915 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
1916 } else {
1917 w_array.push(Object::Integer(start_unicode as i64));
1919 w_array.push(Object::Integer(end_unicode as i64));
1920 w_array.push(Object::Integer(pdf_width));
1921 }
1922
1923 i = j;
1924 }
1925
1926 return w_array;
1927 }
1928 }
1929 }
1930
1931 let ranges = vec![
1933 (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), ];
1970
1971 for (start, end, width) in ranges {
1973 if start == end {
1974 w_array.push(Object::Integer(start));
1976 w_array.push(Object::Array(vec![Object::Integer(width)]));
1977 } else {
1978 w_array.push(Object::Integer(start));
1980 w_array.push(Object::Integer(end));
1981 w_array.push(Object::Integer(width));
1982 }
1983 }
1984
1985 w_array
1986 }
1987
1988 fn generate_cid_to_gid_map(
1990 &mut self,
1991 font_name: &str,
1992 font: &crate::fonts::Font,
1993 subset_mapping: Option<&HashMap<u32, u16>>,
1994 ) -> Result<Vec<u8>> {
1995 use crate::text::fonts::truetype::TrueTypeFont;
1996
1997 let cmap_mappings = if let Some(subset_map) = subset_mapping {
2000 subset_map.clone()
2002 } else {
2003 let tt_font = TrueTypeFont::parse(font.data.clone())?;
2005 let cmap_tables = tt_font.parse_cmap()?;
2006
2007 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
2009 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
2010 })?;
2011
2012 cmap.mappings.clone()
2013 };
2014
2015 let used_chars = self
2022 .document_used_chars_by_font
2023 .get(font_name)
2024 .cloned()
2025 .unwrap_or_default();
2026
2027 let max_unicode = if !used_chars.is_empty() {
2029 used_chars
2031 .iter()
2032 .map(|ch| *ch as u32)
2033 .max()
2034 .unwrap_or(0x00FF) .min(0xFFFF) as usize
2036 } else {
2037 cmap_mappings
2039 .keys()
2040 .max()
2041 .copied()
2042 .unwrap_or(0xFFFF)
2043 .min(0xFFFF) as usize
2044 };
2045
2046 let mut map = vec![0u8; (max_unicode + 1) * 2];
2048
2049 let mut sample_mappings = Vec::new();
2051 for (&unicode, &glyph_id) in &cmap_mappings {
2052 if unicode <= max_unicode as u32 {
2053 let idx = (unicode as usize) * 2;
2054 map[idx] = (glyph_id >> 8) as u8;
2056 map[idx + 1] = (glyph_id & 0xFF) as u8;
2057
2058 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
2060 {
2061 sample_mappings.push((unicode, glyph_id));
2062 }
2063 }
2064 }
2065
2066 Ok(map)
2067 }
2068
2069 fn generate_tounicode_cmap_from_font(
2071 &self,
2072 font_name: &str,
2073 font: &crate::fonts::Font,
2074 ) -> Vec<u8> {
2075 use crate::text::fonts::truetype::TrueTypeFont;
2076
2077 let mut cmap = String::new();
2078
2079 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
2081 cmap.push_str("12 dict begin\n");
2082 cmap.push_str("begincmap\n");
2083 cmap.push_str("/CIDSystemInfo\n");
2084 cmap.push_str("<< /Registry (Adobe)\n");
2085 cmap.push_str(" /Ordering (UCS)\n");
2086 cmap.push_str(" /Supplement 0\n");
2087 cmap.push_str(">> def\n");
2088 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
2089 cmap.push_str("/CMapType 2 def\n");
2090 cmap.push_str("1 begincodespacerange\n");
2091 cmap.push_str("<0000> <FFFF>\n");
2092 cmap.push_str("endcodespacerange\n");
2093
2094 let used_codepoints: Option<std::collections::HashSet<u32>> = self
2100 .document_used_chars_by_font
2101 .get(font_name)
2102 .map(|chars| {
2103 chars
2104 .iter()
2105 .map(|c| *c as u32)
2106 .filter(|cp| *cp <= 0xFFFF)
2107 .collect()
2108 });
2109
2110 let mut mappings: Vec<(u32, u32)> = Vec::new();
2111
2112 if let Some(used) = &used_codepoints {
2113 for cp in used {
2115 mappings.push((*cp, *cp));
2116 }
2117 } else if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2118 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2120 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
2121 for (&unicode, &glyph_id) in &cmap_table.mappings {
2122 if glyph_id > 0 && unicode <= 0xFFFF {
2123 mappings.push((unicode, unicode));
2124 }
2125 }
2126 }
2127 }
2128 }
2129
2130 mappings.sort_by_key(|&(cid, _)| cid);
2132
2133 let mut i = 0;
2135 while i < mappings.len() {
2136 let start_cid = mappings[i].0;
2138 let start_unicode = mappings[i].1;
2139 let mut end_idx = i;
2140
2141 while end_idx + 1 < mappings.len()
2143 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
2144 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
2145 && end_idx - i < 99
2146 {
2148 end_idx += 1;
2149 }
2150
2151 if end_idx > i {
2152 cmap.push_str("1 beginbfrange\n");
2154 cmap.push_str(&format!(
2155 "<{:04X}> <{:04X}> <{:04X}>\n",
2156 start_cid, mappings[end_idx].0, start_unicode
2157 ));
2158 cmap.push_str("endbfrange\n");
2159 i = end_idx + 1;
2160 } else {
2161 let mut chars = Vec::new();
2163 let chunk_end = (i + 100).min(mappings.len());
2164
2165 for item in &mappings[i..chunk_end] {
2166 chars.push(*item);
2167 }
2168
2169 if !chars.is_empty() {
2170 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
2171 for (cid, unicode) in chars {
2172 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
2173 }
2174 cmap.push_str("endbfchar\n");
2175 }
2176
2177 i = chunk_end;
2178 }
2179 }
2180
2181 cmap.push_str("endcmap\n");
2183 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
2184 cmap.push_str("end\n");
2185 cmap.push_str("end\n");
2186
2187 cmap.into_bytes()
2188 }
2189
2190 #[allow(dead_code)]
2192 fn write_truetype_font(
2193 &mut self,
2194 font_name: &str,
2195 font: &crate::text::font_manager::CustomFont,
2196 ) -> Result<ObjectId> {
2197 let font_id = self.allocate_object_id();
2199 let descriptor_id = self.allocate_object_id();
2200 let font_file_id = self.allocate_object_id();
2201
2202 if let Some(ref data) = font.font_data {
2204 let mut font_file_dict = Dictionary::new();
2205 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2206 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2207 self.write_object(font_file_id, font_stream_obj)?;
2208 }
2209
2210 let mut descriptor = Dictionary::new();
2212 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2213 descriptor.set("FontName", Object::Name(font_name.to_string()));
2214 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2216 "FontBBox",
2217 Object::Array(vec![
2218 Object::Integer(-1000),
2219 Object::Integer(-1000),
2220 Object::Integer(2000),
2221 Object::Integer(2000),
2222 ]),
2223 );
2224 descriptor.set("ItalicAngle", Object::Integer(0));
2225 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2226 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2227 descriptor.set(
2228 "CapHeight",
2229 Object::Integer(font.descriptor.cap_height as i64),
2230 );
2231 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2232 descriptor.set("FontFile2", Object::Reference(font_file_id));
2233 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2234
2235 let mut font_dict = Dictionary::new();
2237 font_dict.set("Type", Object::Name("Font".to_string()));
2238 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2239 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2240 font_dict.set("FirstChar", Object::Integer(0));
2241 font_dict.set("LastChar", Object::Integer(255));
2242
2243 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2245 font_dict.set("Widths", Object::Array(widths));
2246 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2247
2248 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2250
2251 self.write_object(font_id, Object::Dictionary(font_dict))?;
2252
2253 Ok(font_id)
2254 }
2255
2256 fn write_pages(
2257 &mut self,
2258 document: &Document,
2259 font_refs: &HashMap<String, ObjectId>,
2260 ) -> Result<()> {
2261 let pages_id = self.get_pages_id()?;
2262 let mut pages_dict = Dictionary::new();
2263 pages_dict.set("Type", Object::Name("Pages".to_string()));
2264 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2265
2266 let mut kids = Vec::new();
2267
2268 let mut page_ids = Vec::new();
2270 let mut content_ids = Vec::new();
2271 for _ in 0..document.pages.len() {
2272 page_ids.push(self.allocate_object_id());
2273 content_ids.push(self.allocate_object_id());
2274 }
2275
2276 for page_id in &page_ids {
2277 kids.push(Object::Reference(*page_id));
2278 }
2279
2280 pages_dict.set("Kids", Object::Array(kids));
2281
2282 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2283
2284 self.page_ids = page_ids.clone();
2286
2287 for (i, page) in document.pages.iter().enumerate() {
2289 let page_id = page_ids[i];
2290 let content_id = content_ids[i];
2291
2292 self.write_page_with_fonts(page_id, pages_id, content_id, page, document, font_refs)?;
2293 self.write_page_content(content_id, page)?;
2294 }
2295
2296 Ok(())
2297 }
2298
2299 #[allow(dead_code)]
2301 fn write_pages_with_fonts(
2302 &mut self,
2303 document: &Document,
2304 font_refs: &HashMap<String, ObjectId>,
2305 ) -> Result<()> {
2306 self.write_pages(document, font_refs)
2307 }
2308
2309 fn write_page_with_fonts(
2310 &mut self,
2311 page_id: ObjectId,
2312 parent_id: ObjectId,
2313 content_id: ObjectId,
2314 page: &crate::page::Page,
2315 _document: &Document,
2316 font_refs: &HashMap<String, ObjectId>,
2317 ) -> Result<()> {
2318 let mut page_dict = page.to_dict();
2320
2321 page_dict.set("Type", Object::Name("Page".to_string()));
2322 page_dict.set("Parent", Object::Reference(parent_id));
2323 page_dict.set("Contents", Object::Reference(content_id));
2324
2325 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2327 res.clone()
2328 } else {
2329 Dictionary::new()
2330 };
2331
2332 let mut font_dict = Dictionary::new();
2334
2335 let mut helvetica_dict = Dictionary::new();
2340 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2341 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2342 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2343 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2344 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2345
2346 let mut helvetica_bold_dict = Dictionary::new();
2347 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2348 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2349 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2350 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2351 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2352
2353 let mut helvetica_oblique_dict = Dictionary::new();
2354 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2355 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2356 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2357 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2358 font_dict.set(
2359 "Helvetica-Oblique",
2360 Object::Dictionary(helvetica_oblique_dict),
2361 );
2362
2363 let mut helvetica_bold_oblique_dict = Dictionary::new();
2364 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2365 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2366 helvetica_bold_oblique_dict.set(
2367 "BaseFont",
2368 Object::Name("Helvetica-BoldOblique".to_string()),
2369 );
2370 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2371 font_dict.set(
2372 "Helvetica-BoldOblique",
2373 Object::Dictionary(helvetica_bold_oblique_dict),
2374 );
2375
2376 let mut times_dict = Dictionary::new();
2378 times_dict.set("Type", Object::Name("Font".to_string()));
2379 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2380 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2381 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2382 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2383
2384 let mut times_bold_dict = Dictionary::new();
2385 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2386 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2387 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2388 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2389 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2390
2391 let mut times_italic_dict = Dictionary::new();
2392 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2393 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2394 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2395 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2396 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2397
2398 let mut times_bold_italic_dict = Dictionary::new();
2399 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2400 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2401 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2402 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2403 font_dict.set(
2404 "Times-BoldItalic",
2405 Object::Dictionary(times_bold_italic_dict),
2406 );
2407
2408 let mut courier_dict = Dictionary::new();
2410 courier_dict.set("Type", Object::Name("Font".to_string()));
2411 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2412 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2413 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2414 font_dict.set("Courier", Object::Dictionary(courier_dict));
2415
2416 let mut courier_bold_dict = Dictionary::new();
2417 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2418 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2419 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2420 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2421 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2422
2423 let mut courier_oblique_dict = Dictionary::new();
2424 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2425 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2426 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2427 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2428 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2429
2430 let mut courier_bold_oblique_dict = Dictionary::new();
2431 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2432 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2433 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2434 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2435 font_dict.set(
2436 "Courier-BoldOblique",
2437 Object::Dictionary(courier_bold_oblique_dict),
2438 );
2439
2440 for (font_name, font_id) in font_refs {
2442 font_dict.set(font_name, Object::Reference(*font_id));
2443 }
2444
2445 resources.set("Font", Object::Dictionary(font_dict));
2446
2447 let has_images = !page.images().is_empty();
2449 let has_forms = !page.form_xobjects().is_empty();
2450
2451 let mut form_xobject_ids: HashMap<String, ObjectId> = HashMap::new();
2458
2459 if has_images || has_forms {
2460 let mut xobject_dict = Dictionary::new();
2461
2462 let mut image_entries: Vec<(&String, &crate::graphics::Image)> =
2466 page.images().iter().collect();
2467 image_entries.sort_by_key(|(name, _)| name.as_str());
2468 for (name, image) in image_entries {
2469 let image_id = self.allocate_object_id();
2471
2472 if image.has_transparency() {
2474 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2476
2477 if let Some(smask_stream) = smask_obj {
2479 let smask_id = self.allocate_object_id();
2480 self.write_object(smask_id, smask_stream)?;
2481
2482 if let Object::Stream(ref mut dict, _) = main_obj {
2484 dict.set("SMask", Object::Reference(smask_id));
2485 }
2486 }
2487
2488 self.write_object(image_id, main_obj)?;
2490 } else {
2491 self.write_object(image_id, image.to_pdf_object())?;
2493 }
2494
2495 xobject_dict.set(name, Object::Reference(image_id));
2497 }
2498
2499 let mut form_entries: Vec<(&String, &crate::graphics::FormXObject)> =
2501 page.form_xobjects().iter().collect();
2502 form_entries.sort_by_key(|(name, _)| name.as_str());
2503 for (name, form) in form_entries {
2504 let form_id = self.allocate_object_id();
2505 let stream = form.to_stream()?;
2506 let stream_obj =
2507 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2508 self.write_object(form_id, stream_obj)?;
2509 xobject_dict.set(name, Object::Reference(form_id));
2510 form_xobject_ids.insert(name.clone(), form_id);
2513 }
2514
2515 resources.set("XObject", Object::Dictionary(xobject_dict));
2516 }
2517
2518 if let Some(extgstate_states) = page.get_extgstate_resources() {
2520 let mut extgstate_dict = Dictionary::new();
2521 let mut extgstate_entries: Vec<(&String, &crate::graphics::ExtGState)> =
2523 extgstate_states.iter().collect();
2524 extgstate_entries.sort_by_key(|(name, _)| name.as_str());
2525 for (name, state) in extgstate_entries {
2526 let mut state_dict = Dictionary::new();
2527 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2528
2529 if let Some(alpha_stroke) = state.alpha_stroke {
2531 state_dict.set("CA", Object::Real(alpha_stroke));
2532 }
2533 if let Some(alpha_fill) = state.alpha_fill {
2534 state_dict.set("ca", Object::Real(alpha_fill));
2535 }
2536
2537 if let Some(line_width) = state.line_width {
2539 state_dict.set("LW", Object::Real(line_width));
2540 }
2541 if let Some(line_cap) = state.line_cap {
2542 state_dict.set("LC", Object::Integer(line_cap as i64));
2543 }
2544 if let Some(line_join) = state.line_join {
2545 state_dict.set("LJ", Object::Integer(line_join as i64));
2546 }
2547 if let Some(dash_pattern) = &state.dash_pattern {
2548 let dash_objects: Vec<Object> = dash_pattern
2549 .array
2550 .iter()
2551 .map(|&d| Object::Real(d))
2552 .collect();
2553 state_dict.set(
2554 "D",
2555 Object::Array(vec![
2556 Object::Array(dash_objects),
2557 Object::Real(dash_pattern.phase),
2558 ]),
2559 );
2560 }
2561
2562 if let Some(ref bm) = state.blend_mode {
2566 state_dict.set("BM", Object::Name(bm.pdf_name().to_string()));
2567 }
2568
2569 if let Some(ref soft_mask) = state.soft_mask {
2587 let mut mask_dict = soft_mask.to_pdf_dictionary()?;
2588 if let Some(Object::Name(ref g_name)) = mask_dict.get("G").cloned() {
2589 let form_id = form_xobject_ids.get(g_name).ok_or_else(|| {
2590 crate::error::PdfError::InvalidStructure(format!(
2591 "SoftMask references transparency group {:?} but no matching \
2592 FormXObject is registered on the page; call \
2593 Page::add_form_xobject({:?}, ...) before saving",
2594 g_name, g_name
2595 ))
2596 })?;
2597 mask_dict.set("G", Object::Reference(*form_id));
2598 }
2599 state_dict.set("SMask", Object::Dictionary(mask_dict));
2600 }
2601
2602 extgstate_dict.set(name, Object::Dictionary(state_dict));
2603 }
2604 if !extgstate_dict.is_empty() {
2605 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2606 }
2607 }
2608
2609 if !page.color_spaces().is_empty() {
2624 let mut cs_dict = Dictionary::new();
2625 let mut entries: Vec<(&String, &crate::graphics::PageColorSpace)> =
2628 page.color_spaces().iter().collect();
2629 entries.sort_by_key(|(name, _)| name.as_str());
2630 for (name, cs) in entries {
2631 if let Some((icc_dict, icc_data)) = cs.icc_stream_parts() {
2636 let icc_id = self.allocate_object_id();
2637 self.write_object(icc_id, Object::Stream(icc_dict, icc_data))?;
2638 cs_dict.set(
2639 name,
2640 Object::Array(vec![
2641 Object::Name("ICCBased".to_string()),
2642 Object::Reference(icc_id),
2643 ]),
2644 );
2645 } else {
2646 cs_dict.set(name, cs.to_object());
2647 }
2648 }
2649 resources.set("ColorSpace", Object::Dictionary(cs_dict));
2650 }
2651
2652 if !page.patterns().is_empty() {
2653 let mut pat_dict = Dictionary::new();
2654 let mut entries: Vec<(&String, &crate::graphics::TilingPattern)> =
2655 page.patterns().iter().collect();
2656 entries.sort_by_key(|(name, _)| name.as_str());
2657 for (name, pattern) in entries {
2658 let pattern_id = self.allocate_object_id();
2659 let pattern_dict = pattern.to_pdf_dictionary()?;
2660 self.write_object(
2661 pattern_id,
2662 Object::Stream(pattern_dict, pattern.content_stream.clone()),
2663 )?;
2664 pat_dict.set(name, Object::Reference(pattern_id));
2665 }
2666 resources.set("Pattern", Object::Dictionary(pat_dict));
2667 }
2668
2669 if !page.shadings().is_empty() {
2670 let mut sh_dict = Dictionary::new();
2671 let mut entries: Vec<(&String, &crate::graphics::ShadingDefinition)> =
2672 page.shadings().iter().collect();
2673 entries.sort_by_key(|(name, _)| name.as_str());
2674 for (name, shading) in entries {
2675 let mut shading_dict = shading.to_pdf_dictionary()?;
2676 if let Some(Object::Dictionary(_)) = shading_dict.get("Function") {
2681 if let Some(func_obj) = shading_dict.remove("Function") {
2682 let func_id = self.allocate_object_id();
2683 self.write_object(func_id, func_obj)?;
2684 shading_dict.set("Function", Object::Reference(func_id));
2685 }
2686 }
2687 let shading_id = self.allocate_object_id();
2688 self.write_object(shading_id, Object::Dictionary(shading_dict))?;
2689 sh_dict.set(name, Object::Reference(shading_id));
2690 }
2691 resources.set("Shading", Object::Dictionary(sh_dict));
2692 }
2693
2694 if let Some(preserved_res) = page.get_preserved_resources() {
2697 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
2699
2700 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2702 let renamed_fonts = crate::writer::rename_preserved_fonts(fonts);
2704
2705 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
2707 }
2708
2709 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2713 let mut fonts_with_refs = crate::objects::Dictionary::new();
2714
2715 for (font_name, font_obj) in fonts.iter() {
2716 if let Object::Dictionary(font_dict) = font_obj {
2717 let updated_font = self.write_embedded_font_streams(font_dict)?;
2719 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
2720 } else {
2721 fonts_with_refs.set(font_name, font_obj.clone());
2723 }
2724 }
2725
2726 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
2728 }
2729
2730 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
2734 let mut xobjects_with_refs = crate::objects::Dictionary::new();
2735 tracing::debug!(
2736 "Externalizing {} preserved XObject entries as indirect objects",
2737 xobjects.len()
2738 );
2739
2740 for (xobj_name, xobj_obj) in xobjects.iter() {
2741 match xobj_obj {
2742 Object::Stream(dict, data) => {
2743 let obj_id = self.allocate_object_id();
2744 self.write_object(obj_id, Object::Stream(dict.clone(), data.clone()))?;
2745 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
2746 }
2747 Object::Dictionary(dict) => {
2748 let externalized = self.externalize_streams_in_dict(dict)?;
2750 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
2751 }
2752 _ => {
2753 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
2754 }
2755 }
2756 }
2757
2758 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
2759 }
2760
2761 for (key, value) in preserved_writer_dict.iter() {
2763 if let Some(Object::Dictionary(existing)) = resources.get(key) {
2765 if let Object::Dictionary(preserved_dict) = value {
2766 let mut merged = existing.clone();
2767 for (res_name, res_obj) in preserved_dict.iter() {
2769 if !merged.contains_key(res_name) {
2770 merged.set(res_name, res_obj.clone());
2771 }
2772 }
2773 resources.set(key, Object::Dictionary(merged));
2774 }
2775 } else {
2776 resources.set(key, value.clone());
2778 }
2779 }
2780 }
2781
2782 page_dict.set("Resources", Object::Dictionary(resources));
2783
2784 let mut annot_refs: Vec<Object> = Vec::new();
2786
2787 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
2789 for annot in annots {
2790 if let Object::Dictionary(ref annot_dict) = annot {
2791 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
2792 if subtype == "Widget" {
2793 let widget_id = self.allocate_object_id();
2794 self.write_object(widget_id, annot.clone())?;
2795 annot_refs.push(Object::Reference(widget_id));
2796
2797 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
2799 if let Some(Object::String(field_name)) = annot_dict.get("T") {
2800 self.field_widget_map
2801 .entry(field_name.clone())
2802 .or_default()
2803 .push(widget_id);
2804 self.field_id_map.insert(field_name.clone(), widget_id);
2805 self.form_field_ids.push(widget_id);
2806 }
2807 }
2808 continue;
2809 }
2810 }
2811 }
2812 annot_refs.push(annot.clone());
2813 }
2814 }
2815
2816 for annotation in page.annotations() {
2820 let annot_id = self.allocate_object_id();
2821 let mut annot_dict = annotation.to_dict();
2822
2823 if let Some(placeholder) = annotation.field_parent {
2841 if let Some(real_id) = self.form_field_placeholder_map.get(&placeholder) {
2842 annot_dict.set("Parent", Object::Reference(*real_id));
2843 }
2844 }
2845
2846 if let Some(Object::Dictionary(ap_dict)) = annot_dict.get("AP") {
2865 let mut updated_ap = crate::objects::Dictionary::new();
2866 for (state_key, state_val) in ap_dict.iter() {
2867 match state_val {
2868 Object::Stream(sd, data) => {
2869 let patched_sd = Self::rewrite_ap_stream_font_resources(sd, font_refs);
2875 let stream_id = self.allocate_object_id();
2876 self.write_object(stream_id, Object::Stream(patched_sd, data.clone()))?;
2877 updated_ap.set(state_key, Object::Reference(stream_id));
2878 }
2879 Object::Dictionary(down_dict) => {
2880 let externalized = self
2882 .externalize_streams_in_dict_with_font_refs(down_dict, font_refs)?;
2883 updated_ap.set(state_key, Object::Dictionary(externalized));
2884 }
2885 _ => {
2886 updated_ap.set(state_key, state_val.clone());
2887 }
2888 }
2889 }
2890 annot_dict.set("AP", Object::Dictionary(updated_ap));
2891 }
2892
2893 self.write_object(annot_id, Object::Dictionary(annot_dict))?;
2894 annot_refs.push(Object::Reference(annot_id));
2895
2896 if annotation.annotation_type == crate::annotations::AnnotationType::Widget {
2898 if let Some(Object::String(field_name)) = annotation.properties.get("T") {
2899 self.field_widget_map
2900 .entry(field_name.clone())
2901 .or_default()
2902 .push(annot_id);
2903 self.field_id_map.insert(field_name.clone(), annot_id);
2904 self.form_field_ids.push(annot_id);
2905 }
2906 }
2907 }
2908
2909 if !annot_refs.is_empty() {
2911 page_dict.set("Annots", Object::Array(annot_refs));
2912 } else {
2913 page_dict.remove("Annots");
2914 }
2915
2916 self.write_object(page_id, Object::Dictionary(page_dict))?;
2917 Ok(())
2918 }
2919}
2920
2921impl PdfWriter<BufWriter<std::fs::File>> {
2922 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
2923 let file = std::fs::File::create(path)?;
2924 let writer = BufWriter::new(file);
2925
2926 Ok(Self {
2927 writer,
2928 xref_positions: HashMap::new(),
2929 current_position: 0,
2930 next_object_id: 1,
2931 catalog_id: None,
2932 pages_id: None,
2933 info_id: None,
2934 field_widget_map: HashMap::new(),
2935 field_id_map: HashMap::new(),
2936 form_field_ids: Vec::new(),
2937 page_ids: Vec::new(),
2938 config: WriterConfig::default(),
2939 document_used_chars_by_font: std::collections::HashMap::new(),
2940 buffered_objects: HashMap::new(),
2941 compressed_object_map: HashMap::new(),
2942 prev_xref_offset: None,
2943 base_pdf_size: None,
2944 encrypt_obj_id: None,
2945 file_id: None,
2946 encryption_state: None,
2947 pending_encrypt_dict: None,
2948 form_field_placeholder_map: HashMap::new(),
2949 form_manager_field_refs: Vec::new(),
2950 })
2951 }
2952}
2953
2954impl<W: Write> PdfWriter<W> {
2955 fn externalize_streams_in_dict(
2974 &mut self,
2975 dict: &crate::objects::Dictionary,
2976 ) -> Result<crate::objects::Dictionary> {
2977 self.externalize_streams_in_dict_with_font_refs(dict, &HashMap::new())
2978 }
2979
2980 fn externalize_streams_in_dict_with_font_refs(
2984 &mut self,
2985 dict: &crate::objects::Dictionary,
2986 font_refs: &HashMap<String, ObjectId>,
2987 ) -> Result<crate::objects::Dictionary> {
2988 let mut result = crate::objects::Dictionary::new();
2989 for (key, value) in dict.iter() {
2990 match value {
2991 Object::Stream(d, data) => {
2992 let patched_d = Self::rewrite_ap_stream_font_resources(d, font_refs);
2993 let obj_id = self.allocate_object_id();
2994 self.write_object(obj_id, Object::Stream(patched_d, data.clone()))?;
2995 result.set(key, Object::Reference(obj_id));
2996 }
2997 _ => {
2998 result.set(key, value.clone());
2999 }
3000 }
3001 }
3002 Ok(result)
3003 }
3004
3005 fn rewrite_ap_stream_font_resources(
3021 stream_dict: &crate::objects::Dictionary,
3022 font_refs: &HashMap<String, ObjectId>,
3023 ) -> crate::objects::Dictionary {
3024 if font_refs.is_empty() {
3031 return stream_dict.clone();
3032 }
3033
3034 let mut out = stream_dict.clone();
3035
3036 let Some(Object::Dictionary(resources)) = stream_dict.get("Resources") else {
3042 return out;
3043 };
3044 let Some(Object::Dictionary(fonts)) = resources.get("Font") else {
3045 return out;
3046 };
3047
3048 let mut patched_fonts = crate::objects::Dictionary::new();
3049 let mut changed = false;
3050 for (font_name, entry) in fonts.iter() {
3051 let should_rewrite = match entry {
3055 Object::Dictionary(d) => {
3056 matches!(d.get("Subtype"), Some(Object::Name(s)) if s == "Type0")
3057 }
3058 _ => false,
3059 };
3060 if should_rewrite {
3061 if let Some(font_id) = font_refs.get(font_name.as_str()) {
3062 patched_fonts.set(font_name, Object::Reference(*font_id));
3063 changed = true;
3064 continue;
3065 }
3066 }
3067 patched_fonts.set(font_name, entry.clone());
3068 }
3069
3070 if changed {
3071 let mut patched_resources = resources.clone();
3072 patched_resources.set("Font", Object::Dictionary(patched_fonts));
3073 out.set("Resources", Object::Dictionary(patched_resources));
3074 }
3075 out
3076 }
3077
3078 fn write_embedded_font_streams(
3079 &mut self,
3080 font_dict: &crate::objects::Dictionary,
3081 ) -> Result<crate::objects::Dictionary> {
3082 let mut updated_font = font_dict.clone();
3083
3084 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
3086 if subtype == "Type0" {
3087 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
3089 let mut updated_descendants = Vec::new();
3090
3091 for descendant in descendants {
3092 match descendant {
3093 Object::Dictionary(cidfont) => {
3094 let updated_cidfont =
3096 self.write_cidfont_embedded_streams(cidfont)?;
3097 let cidfont_id = self.allocate_object_id();
3099 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
3100 updated_descendants.push(Object::Reference(cidfont_id));
3102 }
3103 Object::Reference(_) => {
3104 updated_descendants.push(descendant.clone());
3106 }
3107 _ => {
3108 updated_descendants.push(descendant.clone());
3109 }
3110 }
3111 }
3112
3113 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
3114 }
3115
3116 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
3118 let tounicode_id = self.allocate_object_id();
3119 self.write_object(
3120 tounicode_id,
3121 Object::Stream(stream_dict.clone(), stream_data.clone()),
3122 )?;
3123 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
3124 }
3125
3126 return Ok(updated_font);
3127 }
3128 }
3129
3130 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
3133 let mut updated_descriptor = descriptor.clone();
3134 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3135
3136 for key in &font_file_keys {
3138 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3139 let stream_id = self.allocate_object_id();
3141 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
3142 self.write_object(stream_id, stream_obj)?;
3143
3144 updated_descriptor.set(*key, Object::Reference(stream_id));
3146 }
3147 }
3149
3150 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
3152 }
3153
3154 Ok(updated_font)
3155 }
3156
3157 fn write_cidfont_embedded_streams(
3159 &mut self,
3160 cidfont: &crate::objects::Dictionary,
3161 ) -> Result<crate::objects::Dictionary> {
3162 let mut updated_cidfont = cidfont.clone();
3163
3164 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
3166 let mut updated_descriptor = descriptor.clone();
3167 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3168
3169 for key in &font_file_keys {
3171 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3172 let stream_id = self.allocate_object_id();
3173 self.write_object(
3174 stream_id,
3175 Object::Stream(stream_dict.clone(), stream_data.clone()),
3176 )?;
3177 updated_descriptor.set(*key, Object::Reference(stream_id));
3178 }
3179 }
3180
3181 let descriptor_id = self.allocate_object_id();
3183 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
3184
3185 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
3187 }
3188
3189 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
3191 let map_id = self.allocate_object_id();
3192 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
3193 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
3194 }
3195
3196 Ok(updated_cidfont)
3197 }
3198
3199 fn allocate_object_id(&mut self) -> ObjectId {
3200 let id = ObjectId::new(self.next_object_id, 0);
3201 self.next_object_id += 1;
3202 id
3203 }
3204
3205 fn get_catalog_id(&self) -> Result<ObjectId> {
3207 self.catalog_id.ok_or_else(|| {
3208 PdfError::InvalidOperation(
3209 "catalog_id not initialized - write_document() must be called first".to_string(),
3210 )
3211 })
3212 }
3213
3214 fn get_pages_id(&self) -> Result<ObjectId> {
3216 self.pages_id.ok_or_else(|| {
3217 PdfError::InvalidOperation(
3218 "pages_id not initialized - write_document() must be called first".to_string(),
3219 )
3220 })
3221 }
3222
3223 fn get_info_id(&self) -> Result<ObjectId> {
3225 self.info_id.ok_or_else(|| {
3226 PdfError::InvalidOperation(
3227 "info_id not initialized - write_document() must be called first".to_string(),
3228 )
3229 })
3230 }
3231
3232 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
3233 use crate::writer::ObjectStreamWriter;
3234
3235 let object = if let Some(ref enc_state) = self.encryption_state {
3237 let mut obj = object;
3238 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
3239 obj
3240 } else {
3241 object
3242 };
3243
3244 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
3246 let mut buffer = Vec::new();
3247 self.write_object_value_to_buffer(&object, &mut buffer)?;
3248 self.buffered_objects.insert(id, buffer);
3249 return Ok(());
3250 }
3251
3252 self.xref_positions.insert(id, self.current_position);
3254
3255 let header = format!("{} {} obj\n", id.number(), id.generation());
3257 self.write_bytes(header.as_bytes())?;
3258
3259 self.write_object_value(&object)?;
3260
3261 self.write_bytes(b"\nendobj\n")?;
3262 Ok(())
3263 }
3264
3265 fn write_object_value(&mut self, object: &Object) -> Result<()> {
3266 match object {
3267 Object::Null => self.write_bytes(b"null")?,
3268 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
3269 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
3270 Object::Real(f) => self.write_bytes(
3271 format!("{f:.6}")
3272 .trim_end_matches('0')
3273 .trim_end_matches('.')
3274 .as_bytes(),
3275 )?,
3276 Object::String(s) => {
3277 self.write_bytes(b"(")?;
3286 self.write_bytes(&escape_pdf_string_bytes(s.as_bytes()))?;
3287 self.write_bytes(b")")?;
3288 }
3289 Object::ByteString(bytes) => {
3290 self.write_bytes(b"<")?;
3292 for byte in bytes {
3293 self.write_bytes(format!("{byte:02X}").as_bytes())?;
3294 }
3295 self.write_bytes(b">")?;
3296 }
3297 Object::Name(n) => {
3298 self.write_bytes(b"/")?;
3299 self.write_bytes(n.as_bytes())?;
3300 }
3301 Object::Array(arr) => {
3302 self.write_bytes(b"[")?;
3303 for (i, obj) in arr.iter().enumerate() {
3304 if i > 0 {
3305 self.write_bytes(b" ")?;
3306 }
3307 self.write_object_value(obj)?;
3308 }
3309 self.write_bytes(b"]")?;
3310 }
3311 Object::Dictionary(dict) => {
3312 self.write_bytes(b"<<")?;
3320 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3321 entries.sort_by_key(|(k, _)| k.as_str());
3322 for (key, value) in entries {
3323 self.write_bytes(b"\n/")?;
3324 self.write_bytes(key.as_bytes())?;
3325 self.write_bytes(b" ")?;
3326 self.write_object_value(value)?;
3327 }
3328 self.write_bytes(b"\n>>")?;
3329 }
3330 Object::Stream(dict, data) => {
3331 let mut corrected_dict = dict.clone();
3334 corrected_dict.set("Length", Object::Integer(data.len() as i64));
3335
3336 self.write_object_value(&Object::Dictionary(corrected_dict))?;
3337 self.write_bytes(b"\nstream\n")?;
3338 self.write_bytes(data)?;
3339 self.write_bytes(b"\nendstream")?;
3340 }
3341 Object::Reference(id) => {
3342 let ref_str = format!("{} {} R", id.number(), id.generation());
3343 self.write_bytes(ref_str.as_bytes())?;
3344 }
3345 }
3346 Ok(())
3347 }
3348
3349 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
3351 match object {
3352 Object::Null => buffer.extend_from_slice(b"null"),
3353 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
3354 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
3355 Object::Real(f) => buffer.extend_from_slice(
3356 format!("{f:.6}")
3357 .trim_end_matches('0')
3358 .trim_end_matches('.')
3359 .as_bytes(),
3360 ),
3361 Object::String(s) => {
3362 buffer.push(b'(');
3365 buffer.extend_from_slice(&escape_pdf_string_bytes(s.as_bytes()));
3366 buffer.push(b')');
3367 }
3368 Object::ByteString(bytes) => {
3369 buffer.push(b'<');
3370 for byte in bytes {
3371 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
3372 }
3373 buffer.push(b'>');
3374 }
3375 Object::Name(n) => {
3376 buffer.push(b'/');
3377 buffer.extend_from_slice(n.as_bytes());
3378 }
3379 Object::Array(arr) => {
3380 buffer.push(b'[');
3381 for (i, obj) in arr.iter().enumerate() {
3382 if i > 0 {
3383 buffer.push(b' ');
3384 }
3385 self.write_object_value_to_buffer(obj, buffer)?;
3386 }
3387 buffer.push(b']');
3388 }
3389 Object::Dictionary(dict) => {
3390 buffer.extend_from_slice(b"<<");
3394 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3395 entries.sort_by_key(|(k, _)| k.as_str());
3396 for (key, value) in entries {
3397 buffer.extend_from_slice(b"\n/");
3398 buffer.extend_from_slice(key.as_bytes());
3399 buffer.push(b' ');
3400 self.write_object_value_to_buffer(value, buffer)?;
3401 }
3402 buffer.extend_from_slice(b"\n>>");
3403 }
3404 Object::Stream(_, _) => {
3405 return Err(crate::error::PdfError::ObjectStreamError(
3407 "Cannot compress stream objects in object streams".to_string(),
3408 ));
3409 }
3410 Object::Reference(id) => {
3411 let ref_str = format!("{} {} R", id.number(), id.generation());
3412 buffer.extend_from_slice(ref_str.as_bytes());
3413 }
3414 }
3415 Ok(())
3416 }
3417
3418 fn flush_object_streams(&mut self) -> Result<()> {
3420 if self.buffered_objects.is_empty() {
3421 return Ok(());
3422 }
3423
3424 let config = ObjectStreamConfig {
3426 max_objects_per_stream: 100,
3427 compression_level: 6,
3428 enabled: true,
3429 };
3430 let mut os_writer = ObjectStreamWriter::new(config);
3431
3432 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
3434 buffered.sort_by_key(|(id, _)| id.number());
3435
3436 for (id, data) in buffered {
3438 os_writer.add_object(*id, data.clone())?;
3439 }
3440
3441 let streams = os_writer.finalize()?;
3443
3444 for mut stream in streams {
3446 let stream_id = stream.stream_id;
3447
3448 let compressed_data = stream.generate_stream_data(6)?;
3450
3451 let dict = stream.generate_dictionary(&compressed_data);
3453
3454 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
3456 self.compressed_object_map
3457 .insert(*obj_id, (stream_id, index as u32));
3458 }
3459
3460 self.xref_positions.insert(stream_id, self.current_position);
3462
3463 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
3464 self.write_bytes(header.as_bytes())?;
3465
3466 self.write_object_value(&Object::Dictionary(dict))?;
3467
3468 self.write_bytes(b"\nstream\n")?;
3469 self.write_bytes(&compressed_data)?;
3470 self.write_bytes(b"\nendstream\nendobj\n")?;
3471 }
3472
3473 Ok(())
3474 }
3475
3476 fn write_xref(&mut self) -> Result<()> {
3477 self.write_bytes(b"xref\n")?;
3478
3479 let mut entries: Vec<_> = self
3481 .xref_positions
3482 .iter()
3483 .map(|(id, pos)| (*id, *pos))
3484 .collect();
3485 entries.sort_by_key(|(id, _)| id.number());
3486
3487 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
3489
3490 self.write_bytes(b"0 ")?;
3493 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
3494 self.write_bytes(b"\n")?;
3495
3496 self.write_bytes(b"0000000000 65535 f \n")?;
3498
3499 for obj_num in 1..=max_obj_num {
3502 let _obj_id = ObjectId::new(obj_num, 0);
3503 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
3504 let entry = format!("{:010} {:05} n \n", position, 0);
3505 self.write_bytes(entry.as_bytes())?;
3506 } else {
3507 self.write_bytes(b"0000000000 00000 f \n")?;
3509 }
3510 }
3511
3512 Ok(())
3513 }
3514
3515 fn write_xref_stream(&mut self) -> Result<()> {
3516 let catalog_id = self.get_catalog_id()?;
3517 let info_id = self.get_info_id()?;
3518
3519 let xref_stream_id = self.allocate_object_id();
3521 let xref_position = self.current_position;
3522
3523 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
3525 xref_writer.set_trailer_info(catalog_id, info_id);
3526
3527 xref_writer.add_free_entry(0, 65535);
3529
3530 let mut entries: Vec<_> = self
3532 .xref_positions
3533 .iter()
3534 .map(|(id, pos)| (*id, *pos))
3535 .collect();
3536 entries.sort_by_key(|(id, _)| id.number());
3537
3538 let max_obj_num = entries
3540 .iter()
3541 .map(|(id, _)| id.number())
3542 .max()
3543 .unwrap_or(0)
3544 .max(xref_stream_id.number());
3545
3546 for obj_num in 1..=max_obj_num {
3548 let obj_id = ObjectId::new(obj_num, 0);
3549
3550 if obj_num == xref_stream_id.number() {
3551 xref_writer.add_in_use_entry(xref_position, 0);
3553 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
3554 xref_writer.add_compressed_entry(stream_id.number(), *index);
3556 } else if let Some((id, position)) =
3557 entries.iter().find(|(id, _)| id.number() == obj_num)
3558 {
3559 xref_writer.add_in_use_entry(*position, id.generation());
3561 } else {
3562 xref_writer.add_free_entry(0, 0);
3564 }
3565 }
3566
3567 self.xref_positions.insert(xref_stream_id, xref_position);
3569
3570 self.write_bytes(
3572 format!(
3573 "{} {} obj\n",
3574 xref_stream_id.number(),
3575 xref_stream_id.generation()
3576 )
3577 .as_bytes(),
3578 )?;
3579
3580 let uncompressed_data = xref_writer.encode_entries();
3582 let final_data = if self.config.compress_streams {
3583 crate::compression::compress(&uncompressed_data)?
3584 } else {
3585 uncompressed_data
3586 };
3587
3588 let mut dict = xref_writer.create_dictionary(None);
3590 dict.set("Length", Object::Integer(final_data.len() as i64));
3591
3592 if self.config.compress_streams {
3594 dict.set("Filter", Object::Name("FlateDecode".to_string()));
3595 }
3596 self.write_bytes(b"<<")?;
3597 for (key, value) in dict.iter() {
3598 self.write_bytes(b"\n/")?;
3599 self.write_bytes(key.as_bytes())?;
3600 self.write_bytes(b" ")?;
3601 self.write_object_value(value)?;
3602 }
3603 self.write_bytes(b"\n>>\n")?;
3604
3605 self.write_bytes(b"stream\n")?;
3607 self.write_bytes(&final_data)?;
3608 self.write_bytes(b"\nendstream\n")?;
3609 self.write_bytes(b"endobj\n")?;
3610
3611 self.write_bytes(b"\nstartxref\n")?;
3613 self.write_bytes(xref_position.to_string().as_bytes())?;
3614 self.write_bytes(b"\n%%EOF\n")?;
3615
3616 Ok(())
3617 }
3618
3619 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
3626 use crate::encryption::{
3627 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
3628 };
3629 use std::sync::Arc;
3630
3631 let mut fid = vec![0u8; 16];
3633 use rand::Rng;
3634 rand::rng().fill_bytes(&mut fid);
3635
3636 let enc_dict = encryption
3637 .create_encryption_dict(Some(&fid))
3638 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
3639
3640 let enc_key = encryption
3642 .get_encryption_key(&enc_dict, Some(&fid))
3643 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
3644
3645 let handler = encryption.handler();
3647 let (method, key_len) = match encryption.strength {
3648 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
3649 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
3650 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
3651 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
3652 };
3653
3654 let std_filter = FunctionalCryptFilter {
3655 name: "StdCF".to_string(),
3656 method,
3657 length: key_len,
3658 auth_event: crate::encryption::AuthEvent::DocOpen,
3659 recipients: None,
3660 };
3661
3662 let mut filter_manager =
3663 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
3664 filter_manager.add_filter(std_filter);
3665
3666 let encryptor =
3667 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
3668
3669 let encrypt_id = self.allocate_object_id();
3671 self.encrypt_obj_id = Some(encrypt_id);
3672 self.file_id = Some(fid);
3673 self.encryption_state = Some(WriterEncryptionState { encryptor });
3674
3675 self.pending_encrypt_dict = Some(enc_dict.to_dict());
3677
3678 Ok(())
3679 }
3680
3681 fn write_encryption_dict(&mut self) -> Result<()> {
3683 if let (Some(encrypt_id), Some(dict)) =
3684 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
3685 {
3686 let enc_state = self.encryption_state.take();
3688 self.write_object(encrypt_id, Object::Dictionary(dict))?;
3689 self.encryption_state = enc_state;
3690 }
3691 Ok(())
3692 }
3693
3694 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
3695 let catalog_id = self.get_catalog_id()?;
3696 let info_id = self.get_info_id()?;
3697 let max_obj_num = self
3699 .xref_positions
3700 .keys()
3701 .map(|id| id.number())
3702 .max()
3703 .unwrap_or(0);
3704
3705 let mut trailer = Dictionary::new();
3706 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
3707 trailer.set("Root", Object::Reference(catalog_id));
3708 trailer.set("Info", Object::Reference(info_id));
3709
3710 if let Some(prev_xref) = self.prev_xref_offset {
3712 trailer.set("Prev", Object::Integer(prev_xref as i64));
3713 }
3714
3715 if let Some(encrypt_id) = self.encrypt_obj_id {
3717 trailer.set("Encrypt", Object::Reference(encrypt_id));
3718 }
3719 if let Some(ref fid) = self.file_id {
3720 trailer.set(
3721 "ID",
3722 Object::Array(vec![
3723 Object::ByteString(fid.clone()),
3724 Object::ByteString(fid.clone()),
3725 ]),
3726 );
3727 }
3728
3729 self.write_bytes(b"trailer\n")?;
3730 self.write_object_value(&Object::Dictionary(trailer))?;
3731 self.write_bytes(b"\nstartxref\n")?;
3732 self.write_bytes(xref_position.to_string().as_bytes())?;
3733 self.write_bytes(b"\n%%EOF\n")?;
3734
3735 Ok(())
3736 }
3737
3738 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
3739 self.writer.write_all(data)?;
3740 self.current_position += data.len() as u64;
3741 Ok(())
3742 }
3743
3744 #[allow(dead_code)]
3745 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
3746 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
3748 if rect_array.len() >= 4 {
3749 if let (
3750 Some(Object::Real(x1)),
3751 Some(Object::Real(y1)),
3752 Some(Object::Real(x2)),
3753 Some(Object::Real(y2)),
3754 ) = (
3755 rect_array.first(),
3756 rect_array.get(1),
3757 rect_array.get(2),
3758 rect_array.get(3),
3759 ) {
3760 (*x1, *y1, *x2, *y2)
3761 } else {
3762 (0.0, 0.0, 100.0, 20.0) }
3764 } else {
3765 (0.0, 0.0, 100.0, 20.0) }
3767 } else {
3768 (0.0, 0.0, 100.0, 20.0) };
3770
3771 let width = rect.2 - rect.0;
3772 let height = rect.3 - rect.1;
3773
3774 let mut content = String::new();
3776
3777 content.push_str("q\n");
3779
3780 crate::graphics::color::write_stroke_color(&mut content, crate::graphics::Color::black());
3782 content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
3786 content.push_str("S\n"); crate::graphics::color::write_fill_color(&mut content, crate::graphics::Color::white());
3790 content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
3791 content.push_str("f\n"); content.push_str("Q\n");
3795
3796 let mut stream_dict = Dictionary::new();
3798 stream_dict.set("Type", Object::Name("XObject".to_string()));
3799 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3800 stream_dict.set(
3801 "BBox",
3802 Object::Array(vec![
3803 Object::Real(0.0),
3804 Object::Real(0.0),
3805 Object::Real(width),
3806 Object::Real(height),
3807 ]),
3808 );
3809 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3810 stream_dict.set("Length", Object::Integer(content.len() as i64));
3811
3812 let stream_id = self.allocate_object_id();
3814 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3815
3816 Ok(stream_id)
3817 }
3818
3819 #[allow(dead_code)]
3820 fn create_field_appearance_stream(
3821 &mut self,
3822 field_dict: &Dictionary,
3823 widget: &crate::forms::Widget,
3824 ) -> Result<ObjectId> {
3825 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
3826 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
3827
3828 let mut content = String::new();
3830
3831 content.push_str("q\n");
3833
3834 if let Some(bg_color) = &widget.appearance.background_color {
3837 crate::graphics::color::write_fill_color(&mut content, *bg_color);
3838 content.push_str(&format!("0 0 {width} {height} re\n"));
3839 content.push_str("f\n");
3840 }
3841
3842 if let Some(border_color) = &widget.appearance.border_color {
3844 crate::graphics::color::write_stroke_color(&mut content, *border_color);
3845 content.push_str(&format!("{} w\n", widget.appearance.border_width));
3846 content.push_str(&format!("0 0 {width} {height} re\n"));
3847 content.push_str("S\n");
3848 }
3849
3850 if let Some(Object::Name(ft)) = field_dict.get("FT") {
3852 if ft == "Btn" {
3853 if let Some(Object::Name(v)) = field_dict.get("V") {
3854 if v == "Yes" {
3855 crate::graphics::color::write_stroke_color(
3857 &mut content,
3858 crate::graphics::Color::black(),
3859 );
3860 content.push_str("2 w\n");
3861 let margin = width * 0.2;
3862 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
3863 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
3864 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
3865 content.push_str("S\n");
3866 }
3867 }
3868 }
3869 }
3870
3871 content.push_str("Q\n");
3873
3874 let mut stream_dict = Dictionary::new();
3876 stream_dict.set("Type", Object::Name("XObject".to_string()));
3877 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3878 stream_dict.set(
3879 "BBox",
3880 Object::Array(vec![
3881 Object::Real(0.0),
3882 Object::Real(0.0),
3883 Object::Real(width),
3884 Object::Real(height),
3885 ]),
3886 );
3887 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3888 stream_dict.set("Length", Object::Integer(content.len() as i64));
3889
3890 let stream_id = self.allocate_object_id();
3892 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3893
3894 Ok(stream_id)
3895 }
3896}
3897
3898fn format_pdf_date(date: DateTime<Utc>) -> String {
3900 let formatted = date.format("D:%Y%m%d%H%M%S");
3903
3904 format!("{formatted}+00'00")
3906}
3907
3908#[cfg(test)]
3909mod tests;
3910
3911#[cfg(test)]
3912mod rigorous_tests;