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 let Some(struct_tree) = &document.struct_tree {
202 Self::validate_struct_tree(struct_tree, &document.pages)?;
203 }
204
205 if !document.used_characters_by_font.is_empty() {
207 self.document_used_chars_by_font = document.used_characters_by_font.clone();
208 }
209
210 self.write_header()?;
211
212 self.catalog_id = Some(self.allocate_object_id());
214 self.pages_id = Some(self.allocate_object_id());
215 self.info_id = Some(self.allocate_object_id());
216
217 if let Some(ref encryption) = document.encryption {
220 self.init_encryption(encryption)?;
221 }
222
223 let font_refs = self.write_fonts(document)?;
225
226 self.preallocate_form_manager_fields(document)?;
233
234 self.write_pages(document, &font_refs)?;
236
237 self.write_form_fields(document)?;
239
240 self.write_catalog(document)?;
242
243 self.write_info(document)?;
245
246 self.write_encryption_dict()?;
248
249 if self.config.use_object_streams {
251 self.flush_object_streams()?;
252 }
253
254 let xref_position = self.current_position;
256 if self.config.use_xref_streams {
257 self.write_xref_stream()?;
258 } else {
259 self.write_xref()?;
260 }
261
262 if !self.config.use_xref_streams {
264 self.write_trailer(xref_position)?;
265 }
266
267 if let Ok(()) = self.writer.flush() {
268 }
270 Ok(())
271 }
272
273 pub fn write_incremental_update(
307 &mut self,
308 base_pdf_path: impl AsRef<std::path::Path>,
309 document: &mut Document,
310 ) -> Result<()> {
311 use std::io::{BufReader, Read, Seek, SeekFrom};
312
313 let base_pdf_file = std::fs::File::open(base_pdf_path.as_ref())?;
315 let mut pdf_reader = crate::parser::PdfReader::new(BufReader::new(base_pdf_file))?;
316
317 let base_catalog = pdf_reader.catalog()?;
319
320 let (base_pages_id, base_pages_gen) = base_catalog
322 .get("Pages")
323 .and_then(|obj| {
324 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
325 Some((*id, *gen))
326 } else {
327 None
328 }
329 })
330 .ok_or_else(|| {
331 crate::error::PdfError::InvalidStructure(
332 "Base PDF catalog missing /Pages reference".to_string(),
333 )
334 })?;
335
336 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
338 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
339 base_pages_obj
340 {
341 dict.get("Kids")
342 .and_then(|obj| {
343 if let crate::parser::objects::PdfObject::Array(arr) = obj {
344 Some(
347 arr.0
348 .iter()
349 .filter_map(|item| {
350 if let crate::parser::objects::PdfObject::Reference(id, gen) =
351 item
352 {
353 Some(crate::objects::Object::Reference(
354 crate::objects::ObjectId::new(*id, *gen),
355 ))
356 } else {
357 None
358 }
359 })
360 .collect::<Vec<_>>(),
361 )
362 } else {
363 None
364 }
365 })
366 .unwrap_or_default()
367 } else {
368 Vec::new()
369 };
370
371 let base_page_count = base_pages_kids.len();
373
374 let base_pdf = std::fs::File::open(base_pdf_path.as_ref())?;
376 let mut base_reader = BufReader::new(base_pdf);
377
378 base_reader.seek(SeekFrom::End(-100))?;
380 let mut end_buffer = vec![0u8; 100];
381 let bytes_read = base_reader.read(&mut end_buffer)?;
382 end_buffer.truncate(bytes_read);
383
384 let end_str = String::from_utf8_lossy(&end_buffer);
385 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
386 let after_startxref = &end_str[startxref_pos + 9..];
387
388 let number_str: String = after_startxref
389 .chars()
390 .skip_while(|c| c.is_whitespace())
391 .take_while(|c| c.is_ascii_digit())
392 .collect();
393
394 number_str.parse::<u64>().map_err(|_| {
395 crate::error::PdfError::InvalidStructure(
396 "Could not parse startxref offset".to_string(),
397 )
398 })?
399 } else {
400 return Err(crate::error::PdfError::InvalidStructure(
401 "startxref not found in base PDF".to_string(),
402 ));
403 };
404
405 base_reader.seek(SeekFrom::Start(0))?;
407 let base_size = std::io::copy(&mut base_reader, &mut self.writer)? as u64;
408
409 self.prev_xref_offset = Some(prev_xref);
411 self.base_pdf_size = Some(base_size);
412 self.current_position = base_size;
413
414 if !document.used_characters_by_font.is_empty() {
416 self.document_used_chars_by_font = document.used_characters_by_font.clone();
417 }
418
419 self.catalog_id = Some(self.allocate_object_id());
421 self.pages_id = Some(self.allocate_object_id());
422 self.info_id = Some(self.allocate_object_id());
423
424 let font_refs = self.write_fonts(document)?;
426
427 self.write_pages(document, &font_refs)?;
429
430 self.write_form_fields(document)?;
432
433 let catalog_id = self.get_catalog_id()?;
435 let new_pages_id = self.get_pages_id()?;
436
437 let mut catalog = crate::objects::Dictionary::new();
438 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
439 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
440
441 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
446
447 let mut all_pages_kids = base_pages_kids;
449
450 for page_id in &self.page_ids {
452 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
453 }
454
455 let mut pages_dict = crate::objects::Dictionary::new();
456 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
457 pages_dict.set("Kids", crate::objects::Object::Array(all_pages_kids));
458 pages_dict.set(
459 "Count",
460 crate::objects::Object::Integer((base_page_count + self.page_ids.len()) as i64),
461 );
462
463 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
464
465 self.write_info(document)?;
467
468 let xref_position = self.current_position;
470 self.write_xref()?;
471
472 self.write_trailer(xref_position)?;
474
475 self.writer.flush()?;
476 Ok(())
477 }
478
479 pub fn write_incremental_with_page_replacement(
545 &mut self,
546 base_pdf_path: impl AsRef<std::path::Path>,
547 document: &mut Document,
548 ) -> Result<()> {
549 use std::io::Cursor;
550
551 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
553 let base_size = base_pdf_bytes.len() as u64;
554
555 let mut pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
557
558 let base_catalog = pdf_reader.catalog()?;
559
560 let (base_pages_id, base_pages_gen) = base_catalog
561 .get("Pages")
562 .and_then(|obj| {
563 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
564 Some((*id, *gen))
565 } else {
566 None
567 }
568 })
569 .ok_or_else(|| {
570 crate::error::PdfError::InvalidStructure(
571 "Base PDF catalog missing /Pages reference".to_string(),
572 )
573 })?;
574
575 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
576 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
577 base_pages_obj
578 {
579 dict.get("Kids")
580 .and_then(|obj| {
581 if let crate::parser::objects::PdfObject::Array(arr) = obj {
582 Some(
583 arr.0
584 .iter()
585 .filter_map(|item| {
586 if let crate::parser::objects::PdfObject::Reference(id, gen) =
587 item
588 {
589 Some(crate::objects::Object::Reference(
590 crate::objects::ObjectId::new(*id, *gen),
591 ))
592 } else {
593 None
594 }
595 })
596 .collect::<Vec<_>>(),
597 )
598 } else {
599 None
600 }
601 })
602 .unwrap_or_default()
603 } else {
604 Vec::new()
605 };
606
607 let base_page_count = base_pages_kids.len();
608
609 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
611 let end_bytes = &base_pdf_bytes[start_search..];
612 let end_str = String::from_utf8_lossy(end_bytes);
613
614 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
615 let after_startxref = &end_str[startxref_pos + 9..];
616 let number_str: String = after_startxref
617 .chars()
618 .skip_while(|c| c.is_whitespace())
619 .take_while(|c| c.is_ascii_digit())
620 .collect();
621
622 number_str.parse::<u64>().map_err(|_| {
623 crate::error::PdfError::InvalidStructure(
624 "Could not parse startxref offset".to_string(),
625 )
626 })?
627 } else {
628 return Err(crate::error::PdfError::InvalidStructure(
629 "startxref not found in base PDF".to_string(),
630 ));
631 };
632
633 self.writer.write_all(&base_pdf_bytes)?;
635
636 self.prev_xref_offset = Some(prev_xref);
637 self.base_pdf_size = Some(base_size);
638 self.current_position = base_size;
639
640 if !document.used_characters_by_font.is_empty() {
642 self.document_used_chars_by_font = document.used_characters_by_font.clone();
643 }
644
645 self.catalog_id = Some(self.allocate_object_id());
646 self.pages_id = Some(self.allocate_object_id());
647 self.info_id = Some(self.allocate_object_id());
648
649 let font_refs = self.write_fonts(document)?;
650 self.write_pages(document, &font_refs)?;
651 self.write_form_fields(document)?;
652
653 let catalog_id = self.get_catalog_id()?;
655 let new_pages_id = self.get_pages_id()?;
656
657 let mut catalog = crate::objects::Dictionary::new();
658 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
659 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
660 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
661
662 let mut all_pages_kids = Vec::new();
664 let replacement_count = document.pages.len();
665
666 for page_id in &self.page_ids {
668 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
669 }
670
671 if replacement_count < base_page_count {
673 for i in replacement_count..base_page_count {
674 if let Some(page_ref) = base_pages_kids.get(i) {
675 all_pages_kids.push(page_ref.clone());
676 }
677 }
678 }
679
680 let mut pages_dict = crate::objects::Dictionary::new();
681 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
682 pages_dict.set(
683 "Kids",
684 crate::objects::Object::Array(all_pages_kids.clone()),
685 );
686 pages_dict.set(
687 "Count",
688 crate::objects::Object::Integer(all_pages_kids.len() as i64),
689 );
690
691 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
692 self.write_info(document)?;
693
694 let xref_position = self.current_position;
695 self.write_xref()?;
696 self.write_trailer(xref_position)?;
697
698 self.writer.flush()?;
699 Ok(())
700 }
701
702 pub fn write_incremental_with_overlay<P: AsRef<std::path::Path>>(
750 &mut self,
751 base_pdf_path: P,
752 mut overlay_fn: impl FnMut(&mut crate::Page) -> Result<()>,
753 ) -> Result<()> {
754 use std::io::Cursor;
755
756 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
758 let base_size = base_pdf_bytes.len() as u64;
759
760 let pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
762 let parsed_doc = crate::parser::PdfDocument::new(pdf_reader);
763
764 let page_count = parsed_doc.page_count()?;
766
767 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
769 let end_bytes = &base_pdf_bytes[start_search..];
770 let end_str = String::from_utf8_lossy(end_bytes);
771
772 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
773 let after_startxref = &end_str[startxref_pos + 9..];
774 let number_str: String = after_startxref
775 .chars()
776 .skip_while(|c| c.is_whitespace())
777 .take_while(|c| c.is_ascii_digit())
778 .collect();
779
780 number_str.parse::<u64>().map_err(|_| {
781 crate::error::PdfError::InvalidStructure(
782 "Could not parse startxref offset".to_string(),
783 )
784 })?
785 } else {
786 return Err(crate::error::PdfError::InvalidStructure(
787 "startxref not found in base PDF".to_string(),
788 ));
789 };
790
791 self.writer.write_all(&base_pdf_bytes)?;
793
794 self.prev_xref_offset = Some(prev_xref);
795 self.base_pdf_size = Some(base_size);
796 self.current_position = base_size;
797
798 let mut temp_doc = crate::Document::new();
800
801 for page_idx in 0..page_count {
802 let parsed_page = parsed_doc.get_page(page_idx)?;
804 let mut writable_page =
805 crate::Page::from_parsed_with_content(&parsed_page, &parsed_doc)?;
806
807 overlay_fn(&mut writable_page)?;
809
810 temp_doc.add_page(writable_page);
812 }
813
814 if !temp_doc.used_characters_by_font.is_empty() {
817 self.document_used_chars_by_font = temp_doc.used_characters_by_font.clone();
818 }
819
820 self.catalog_id = Some(self.allocate_object_id());
821 self.pages_id = Some(self.allocate_object_id());
822 self.info_id = Some(self.allocate_object_id());
823
824 let font_refs = self.write_fonts(&temp_doc)?;
825 self.write_pages(&temp_doc, &font_refs)?;
826 self.write_form_fields(&mut temp_doc)?;
827
828 let catalog_id = self.get_catalog_id()?;
830 let new_pages_id = self.get_pages_id()?;
831
832 let mut catalog = crate::objects::Dictionary::new();
833 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
834 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
835 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
836
837 let mut all_pages_kids = Vec::new();
839 for page_id in &self.page_ids {
840 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
841 }
842
843 let mut pages_dict = crate::objects::Dictionary::new();
844 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
845 pages_dict.set(
846 "Kids",
847 crate::objects::Object::Array(all_pages_kids.clone()),
848 );
849 pages_dict.set(
850 "Count",
851 crate::objects::Object::Integer(all_pages_kids.len() as i64),
852 );
853
854 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
855 self.write_info(&temp_doc)?;
856
857 let xref_position = self.current_position;
858 self.write_xref()?;
859 self.write_trailer(xref_position)?;
860
861 self.writer.flush()?;
862 Ok(())
863 }
864
865 fn write_header(&mut self) -> Result<()> {
866 let header = format!("%PDF-{}\n", self.config.pdf_version);
867 self.write_bytes(header.as_bytes())?;
868 self.write_bytes(&[b'%', 0xE2, 0xE3, 0xCF, 0xD3, b'\n'])?;
870 Ok(())
871 }
872
873 fn convert_pdf_objects_dict_to_writer(
876 &self,
877 pdf_dict: &crate::pdf_objects::Dictionary,
878 ) -> crate::objects::Dictionary {
879 let mut writer_dict = crate::objects::Dictionary::new();
880
881 for (key, value) in pdf_dict.iter() {
882 let writer_obj = self.convert_pdf_object_to_writer(value);
883 writer_dict.set(key.as_str(), writer_obj);
884 }
885
886 writer_dict
887 }
888
889 fn convert_pdf_object_to_writer(
890 &self,
891 obj: &crate::pdf_objects::Object,
892 ) -> crate::objects::Object {
893 use crate::objects::Object as WriterObj;
894 use crate::pdf_objects::Object as PdfObj;
895
896 match obj {
897 PdfObj::Null => WriterObj::Null,
898 PdfObj::Boolean(b) => WriterObj::Boolean(*b),
899 PdfObj::Integer(i) => WriterObj::Integer(*i),
900 PdfObj::Real(f) => WriterObj::Real(*f),
901 PdfObj::String(s) => {
905 WriterObj::String(crate::parser::objects::decode_text_string(s.as_bytes()))
906 }
907 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
908 PdfObj::Array(arr) => {
909 let items: Vec<WriterObj> = arr
910 .iter()
911 .map(|item| self.convert_pdf_object_to_writer(item))
912 .collect();
913 WriterObj::Array(items)
914 }
915 PdfObj::Dictionary(dict) => {
916 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
917 }
918 PdfObj::Stream(stream) => {
919 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
920 WriterObj::Stream(dict, stream.data.clone())
921 }
922 PdfObj::Reference(id) => {
923 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
924 }
925 }
926 }
927
928 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
929 let catalog_id = self.get_catalog_id()?;
930 let pages_id = self.get_pages_id()?;
931
932 let mut catalog = Dictionary::new();
933 catalog.set("Type", Object::Name("Catalog".to_string()));
934 catalog.set("Pages", Object::Reference(pages_id));
935
936 if let Some(form_manager) = &document.form_manager {
956 if document.acro_form.is_none() {
957 document.acro_form = Some(crate::forms::AcroForm::new());
958 }
959
960 let mut sorted: Vec<(Dictionary, crate::objects::ObjectReference)> = Vec::new();
966 for (name, form_field, placeholder) in form_manager.iter_fields_sorted() {
967 let real_id = *self.form_field_placeholder_map.get(&placeholder).ok_or_else(
968 || {
969 PdfError::Internal(format!(
970 "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"
971 ))
972 },
973 )?;
974 sorted.push((form_field.field_dict.clone(), real_id));
975 }
976 for (field_dict, real_id) in sorted {
977 self.write_object(real_id, Object::Dictionary(field_dict))?;
978 }
979
980 if let Some(acro) = document.acro_form.as_mut() {
981 for r in &self.form_manager_field_refs {
982 if !acro.fields.contains(r) {
983 acro.fields.push(*r);
984 }
985 }
986 }
987 }
988
989 if let Some(acro_form) = &document.acro_form {
991 let acro_form_id = self.allocate_object_id();
993
994 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
996
997 catalog.set("AcroForm", Object::Reference(acro_form_id));
999 }
1000
1001 if let Some(outline_tree) = &document.outline {
1003 if !outline_tree.items.is_empty() {
1004 let outline_root_id = self.write_outline_tree(outline_tree)?;
1005 catalog.set("Outlines", Object::Reference(outline_root_id));
1006 }
1007 }
1008
1009 if let Some(struct_tree) = &document.struct_tree {
1011 if !struct_tree.is_empty() {
1012 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
1013 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
1014 catalog.set("MarkInfo", {
1016 let mut mark_info = Dictionary::new();
1017 mark_info.set("Marked", Object::Boolean(true));
1018 Object::Dictionary(mark_info)
1019 });
1020 }
1021 }
1022
1023 let xmp_metadata = document.create_xmp_metadata();
1026 let xmp_packet = xmp_metadata.to_xmp_packet();
1027 let metadata_id = self.allocate_object_id();
1028
1029 let mut metadata_dict = Dictionary::new();
1031 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
1032 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
1033 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
1034
1035 self.write_object(
1037 metadata_id,
1038 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
1039 )?;
1040
1041 catalog.set("Metadata", Object::Reference(metadata_id));
1043
1044 if let Some(action) = &document.open_action {
1046 catalog.set("OpenAction", Object::Dictionary(action.to_dict()));
1047 }
1048
1049 if let Some(prefs) = &document.viewer_preferences {
1051 catalog.set("ViewerPreferences", Object::Dictionary(prefs.to_dict()));
1052 }
1053
1054 if let Some(named_dests) = &document.named_destinations {
1059 let dests_tree_id = self.allocate_object_id();
1060 self.write_object(dests_tree_id, Object::Dictionary(named_dests.to_dict()))?;
1061
1062 let mut names_dict = Dictionary::new();
1063 names_dict.set("Dests", Object::Reference(dests_tree_id));
1064 let names_dict_id = self.allocate_object_id();
1065 self.write_object(names_dict_id, Object::Dictionary(names_dict))?;
1066
1067 catalog.set("Names", Object::Reference(names_dict_id));
1068 }
1069
1070 if let Some(page_labels) = &document.page_labels {
1074 let labels_id = self.allocate_object_id();
1075 self.write_object(labels_id, Object::Dictionary(page_labels.to_dict()))?;
1076 catalog.set("PageLabels", Object::Reference(labels_id));
1077 }
1078
1079 self.write_object(catalog_id, Object::Dictionary(catalog))?;
1080 Ok(())
1081 }
1082
1083 fn preserved_font_disambiguation_map(
1091 page: &crate::page::Page,
1092 font_refs: &HashMap<String, ObjectId>,
1093 ) -> HashMap<String, String> {
1094 let preserved_fonts = match page
1095 .get_preserved_resources()
1096 .and_then(|res| res.get("Font"))
1097 {
1098 Some(crate::pdf_objects::Object::Dictionary(fonts)) => fonts,
1099 _ => return HashMap::new(),
1100 };
1101
1102 let mut reserved: std::collections::HashSet<String> =
1103 crate::writer::INJECTED_BASE_FONT_KEYS
1104 .iter()
1105 .map(|s| s.to_string())
1106 .collect();
1107 reserved.extend(font_refs.keys().cloned());
1108
1109 let preserved_keys: Vec<String> = preserved_fonts
1110 .keys()
1111 .map(|k| k.as_str().to_string())
1112 .collect();
1113 crate::writer::collision_font_mapping(preserved_keys.iter().map(|s| s.as_str()), &reserved)
1114 }
1115
1116 fn write_page_content(
1117 &mut self,
1118 content_id: ObjectId,
1119 page: &crate::page::Page,
1120 preserved_font_map: &HashMap<String, String>,
1121 ) -> Result<()> {
1122 let mut page_copy = page.clone();
1123 page_copy.preserved_font_rewrite_map = preserved_font_map.clone();
1126 let content = page_copy.generate_content()?;
1127
1128 #[cfg(feature = "compression")]
1130 {
1131 use crate::objects::Stream;
1132 let mut stream = Stream::new(content);
1133 if self.config.compress_streams {
1135 stream.compress_flate()?;
1136 }
1137
1138 self.write_object(
1139 content_id,
1140 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
1141 )?;
1142 }
1143
1144 #[cfg(not(feature = "compression"))]
1145 {
1146 let mut stream_dict = Dictionary::new();
1147 stream_dict.set("Length", Object::Integer(content.len() as i64));
1148
1149 self.write_object(content_id, Object::Stream(stream_dict, content))?;
1150 }
1151
1152 Ok(())
1153 }
1154
1155 fn write_outline_tree(
1156 &mut self,
1157 outline_tree: &crate::structure::OutlineTree,
1158 ) -> Result<ObjectId> {
1159 let outline_root_id = self.allocate_object_id();
1161
1162 let mut outline_root = Dictionary::new();
1163 outline_root.set("Type", Object::Name("Outlines".to_string()));
1164
1165 if !outline_tree.items.is_empty() {
1166 let mut item_ids = Vec::new();
1168
1169 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
1171 let mut count = items.len();
1172 for item in items {
1173 count += count_items(&item.children);
1174 }
1175 count
1176 }
1177
1178 let total_items = count_items(&outline_tree.items);
1179
1180 for _ in 0..total_items {
1182 item_ids.push(self.allocate_object_id());
1183 }
1184
1185 let mut id_index = 0;
1186
1187 let first_id = item_ids[0];
1189 let last_id = item_ids[outline_tree.items.len() - 1];
1190
1191 outline_root.set("First", Object::Reference(first_id));
1192 outline_root.set("Last", Object::Reference(last_id));
1193
1194 let visible_count = outline_tree.visible_count();
1196 outline_root.set("Count", Object::Integer(visible_count));
1197
1198 let mut written_items = Vec::new();
1200
1201 for (i, item) in outline_tree.items.iter().enumerate() {
1202 let item_id = item_ids[id_index];
1203 id_index += 1;
1204
1205 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1206 let next_id = if i < outline_tree.items.len() - 1 {
1207 Some(item_ids[i + 1])
1208 } else {
1209 None
1210 };
1211
1212 let children_ids = self.write_outline_item(
1214 item,
1215 item_id,
1216 outline_root_id,
1217 prev_id,
1218 next_id,
1219 &mut item_ids,
1220 &mut id_index,
1221 )?;
1222
1223 written_items.extend(children_ids);
1224 }
1225 }
1226
1227 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1228 Ok(outline_root_id)
1229 }
1230
1231 #[allow(clippy::too_many_arguments)]
1232 fn write_outline_item(
1233 &mut self,
1234 item: &crate::structure::OutlineItem,
1235 item_id: ObjectId,
1236 parent_id: ObjectId,
1237 prev_id: Option<ObjectId>,
1238 next_id: Option<ObjectId>,
1239 all_ids: &mut Vec<ObjectId>,
1240 id_index: &mut usize,
1241 ) -> Result<Vec<ObjectId>> {
1242 let mut written_ids = vec![item_id];
1243
1244 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1246 let first_idx = *id_index;
1247 let first_id = all_ids[first_idx];
1248 let last_idx = first_idx + item.children.len() - 1;
1249 let last_id = all_ids[last_idx];
1250
1251 for (i, child) in item.children.iter().enumerate() {
1253 let child_id = all_ids[*id_index];
1254 *id_index += 1;
1255
1256 let child_prev = if i > 0 {
1257 Some(all_ids[first_idx + i - 1])
1258 } else {
1259 None
1260 };
1261 let child_next = if i < item.children.len() - 1 {
1262 Some(all_ids[first_idx + i + 1])
1263 } else {
1264 None
1265 };
1266
1267 let child_ids = self.write_outline_item(
1268 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1270 )?;
1271
1272 written_ids.extend(child_ids);
1273 }
1274
1275 (Some(first_id), Some(last_id))
1276 } else {
1277 (None, None)
1278 };
1279
1280 let item_dict = crate::structure::outline_item_to_dict(
1282 item,
1283 parent_id,
1284 first_child_id,
1285 last_child_id,
1286 prev_id,
1287 next_id,
1288 );
1289
1290 self.write_object(item_id, Object::Dictionary(item_dict))?;
1291
1292 Ok(written_ids)
1293 }
1294
1295 fn write_struct_tree(
1297 &mut self,
1298 struct_tree: &crate::structure::StructTree,
1299 ) -> Result<ObjectId> {
1300 let struct_tree_root_id = self.allocate_object_id();
1302 let mut element_ids = Vec::new();
1303 for _ in 0..struct_tree.len() {
1304 element_ids.push(self.allocate_object_id());
1305 }
1306
1307 let mut page_owners: std::collections::BTreeMap<
1311 usize,
1312 std::collections::BTreeMap<u32, usize>,
1313 > = std::collections::BTreeMap::new();
1314 for (element_index, element) in struct_tree.iter().enumerate() {
1315 for mcid_ref in &element.mcids {
1316 page_owners
1317 .entry(mcid_ref.page_index)
1318 .or_default()
1319 .insert(mcid_ref.mcid, element_index);
1320 }
1321 }
1322
1323 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1325 std::collections::HashMap::new();
1326
1327 if let Some(root_index) = struct_tree.root_index() {
1329 parent_map.insert(root_index, struct_tree_root_id);
1330
1331 fn map_children_parents(
1333 tree: &crate::structure::StructTree,
1334 parent_index: usize,
1335 parent_id: ObjectId,
1336 element_ids: &[ObjectId],
1337 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1338 ) {
1339 if let Some(parent_elem) = tree.get(parent_index) {
1340 for &child_index in &parent_elem.children {
1341 parent_map.insert(child_index, parent_id);
1342 map_children_parents(
1343 tree,
1344 child_index,
1345 element_ids[child_index],
1346 element_ids,
1347 parent_map,
1348 );
1349 }
1350 }
1351 }
1352
1353 map_children_parents(
1354 struct_tree,
1355 root_index,
1356 element_ids[root_index],
1357 &element_ids,
1358 &mut parent_map,
1359 );
1360 }
1361
1362 for (index, element) in struct_tree.iter().enumerate() {
1364 let element_id = element_ids[index];
1365 let mut element_dict = Dictionary::new();
1366
1367 element_dict.set("Type", Object::Name("StructElem".to_string()));
1368 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1369
1370 if let Some(&parent_id) = parent_map.get(&index) {
1372 element_dict.set("P", Object::Reference(parent_id));
1373 }
1374
1375 if let Some(ref id) = element.id {
1377 element_dict.set("ID", Object::String(id.clone()));
1378 }
1379
1380 if let Some(ref lang) = element.attributes.lang {
1382 element_dict.set("Lang", Object::String(lang.clone()));
1383 }
1384 if let Some(ref alt) = element.attributes.alt {
1385 element_dict.set("Alt", Object::String(alt.clone()));
1386 }
1387 if let Some(ref actual_text) = element.attributes.actual_text {
1388 let mut encoded = Vec::with_capacity(2 + actual_text.len() * 2);
1389 encoded.extend_from_slice(&[0xFE, 0xFF]);
1390 for unit in actual_text.encode_utf16() {
1391 encoded.extend_from_slice(&unit.to_be_bytes());
1392 }
1393 element_dict.set("ActualText", Object::ByteString(encoded));
1394 }
1395 if let Some(ref title) = element.attributes.title {
1396 element_dict.set("T", Object::String(title.clone()));
1397 }
1398 if let Some(bbox) = element.attributes.bbox {
1399 element_dict.set(
1400 "BBox",
1401 Object::Array(vec![
1402 Object::Real(bbox[0]),
1403 Object::Real(bbox[1]),
1404 Object::Real(bbox[2]),
1405 Object::Real(bbox[3]),
1406 ]),
1407 );
1408 }
1409
1410 let mut kids = Vec::new();
1412
1413 for &child_index in &element.children {
1415 kids.push(Object::Reference(element_ids[child_index]));
1416 }
1417
1418 for mcid_ref in &element.mcids {
1420 let mut mcr = Dictionary::new();
1421 mcr.set("Type", Object::Name("MCR".to_string()));
1422 mcr.set("Pg", Object::Reference(self.page_ids[mcid_ref.page_index]));
1423 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1424 kids.push(Object::Dictionary(mcr));
1425 }
1426
1427 if !kids.is_empty() {
1428 element_dict.set("K", Object::Array(kids));
1429 }
1430
1431 self.write_object(element_id, Object::Dictionary(element_dict))?;
1432 }
1433
1434 let mut struct_tree_root = Dictionary::new();
1436 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1437
1438 if !page_owners.is_empty() {
1439 let parent_tree_id = self.allocate_object_id();
1440 let mut nums = Vec::new();
1441 for (struct_parent_key, (_page_index, owners)) in page_owners.iter().enumerate() {
1442 let max_mcid = *owners.keys().next_back().expect("non-empty owner map");
1443 let mut owner_array = vec![Object::Null; max_mcid as usize + 1];
1444 for (&mcid, &element_index) in owners {
1445 owner_array[mcid as usize] = Object::Reference(element_ids[element_index]);
1446 }
1447 nums.push(Object::Integer(struct_parent_key as i64));
1448 nums.push(Object::Array(owner_array));
1449 }
1450
1451 let mut parent_tree = Dictionary::new();
1452 parent_tree.set("Nums", Object::Array(nums));
1453 self.write_object(parent_tree_id, Object::Dictionary(parent_tree))?;
1454 struct_tree_root.set("ParentTree", Object::Reference(parent_tree_id));
1455 struct_tree_root.set(
1456 "ParentTreeNextKey",
1457 Object::Integer(page_owners.len() as i64),
1458 );
1459 }
1460
1461 if let Some(root_index) = struct_tree.root_index() {
1463 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1464 }
1465
1466 if !struct_tree.role_map.mappings().is_empty() {
1468 let mut role_map = Dictionary::new();
1469 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1470 role_map.set(
1471 custom_type.as_str(),
1472 Object::Name(standard_type.as_pdf_name().to_string()),
1473 );
1474 }
1475 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1476 }
1477
1478 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1479 Ok(struct_tree_root_id)
1480 }
1481
1482 fn validate_struct_tree(
1483 struct_tree: &crate::structure::StructTree,
1484 pages: &[crate::page::Page],
1485 ) -> Result<()> {
1486 let mut owners = std::collections::HashSet::new();
1487 for element in struct_tree.iter() {
1488 for mcid_ref in &element.mcids {
1489 if mcid_ref.page_index >= pages.len() {
1490 return Err(PdfError::InvalidStructure(format!(
1491 "structure element references page {} but document has {} pages",
1492 mcid_ref.page_index,
1493 pages.len()
1494 )));
1495 }
1496 if mcid_ref.mcid >= pages[mcid_ref.page_index].next_mcid() {
1497 return Err(PdfError::InvalidStructure(format!(
1498 "structure element references MCID {} on page {}, but that page has only {} marked-content sequences",
1499 mcid_ref.mcid,
1500 mcid_ref.page_index,
1501 pages[mcid_ref.page_index].next_mcid()
1502 )));
1503 }
1504 if !owners.insert((mcid_ref.page_index, mcid_ref.mcid)) {
1505 return Err(PdfError::InvalidStructure(format!(
1506 "MCID {} on page {} is owned by more than one structure element",
1507 mcid_ref.mcid, mcid_ref.page_index
1508 )));
1509 }
1510 }
1511 }
1512 Ok(())
1513 }
1514
1515 fn struct_parent_keys(
1516 struct_tree: &crate::structure::StructTree,
1517 ) -> std::collections::BTreeMap<usize, i64> {
1518 struct_tree
1519 .iter()
1520 .flat_map(|element| element.mcids.iter().map(|mcid| mcid.page_index))
1521 .collect::<std::collections::BTreeSet<_>>()
1522 .into_iter()
1523 .enumerate()
1524 .map(|(key, page_index)| (page_index, key as i64))
1525 .collect()
1526 }
1527
1528 fn preallocate_form_manager_fields(&mut self, document: &Document) -> Result<()> {
1541 let Some(form_manager) = &document.form_manager else {
1542 return Ok(());
1543 };
1544
1545 for (_name, _form_field, placeholder) in form_manager.iter_fields_sorted() {
1546 let real_id = self.allocate_object_id();
1547 self.form_field_placeholder_map.insert(placeholder, real_id);
1548 self.form_manager_field_refs.push(real_id);
1549 }
1550 Ok(())
1551 }
1552
1553 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1554 if !self.form_field_ids.is_empty() {
1556 if let Some(acro_form) = &mut document.acro_form {
1557 acro_form.fields.clear();
1559 for field_id in &self.form_field_ids {
1560 acro_form.add_field(*field_id);
1561 }
1562
1563 acro_form.need_appearances = true;
1565 if acro_form.da.is_none() {
1566 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1567 }
1568 }
1569 }
1570 Ok(())
1571 }
1572
1573 fn write_info(&mut self, document: &Document) -> Result<()> {
1574 let info_id = self.get_info_id()?;
1575 let mut info_dict = Dictionary::new();
1576
1577 if let Some(ref title) = document.metadata.title {
1578 info_dict.set("Title", Object::String(title.clone()));
1579 }
1580 if let Some(ref author) = document.metadata.author {
1581 info_dict.set("Author", Object::String(author.clone()));
1582 }
1583 if let Some(ref subject) = document.metadata.subject {
1584 info_dict.set("Subject", Object::String(subject.clone()));
1585 }
1586 if let Some(ref keywords) = document.metadata.keywords {
1587 info_dict.set("Keywords", Object::String(keywords.clone()));
1588 }
1589 if let Some(ref creator) = document.metadata.creator {
1590 info_dict.set("Creator", Object::String(creator.clone()));
1591 }
1592 if let Some(ref producer) = document.metadata.producer {
1593 info_dict.set("Producer", Object::String(producer.clone()));
1594 }
1595
1596 if let Some(creation_date) = document.metadata.creation_date {
1598 let date_string = format_pdf_date(creation_date);
1599 info_dict.set("CreationDate", Object::String(date_string));
1600 }
1601
1602 if let Some(mod_date) = document.metadata.modification_date {
1604 let date_string = format_pdf_date(mod_date);
1605 info_dict.set("ModDate", Object::String(date_string));
1606 }
1607
1608 let edition = super::Edition::OpenSource;
1611
1612 let signature = super::PdfSignature::new(document, edition);
1613 signature.write_to_info_dict(&mut info_dict);
1614
1615 self.write_object(info_id, Object::Dictionary(info_dict))?;
1616 Ok(())
1617 }
1618
1619 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1620 let mut font_refs = HashMap::new();
1621
1622 for font_name in document.custom_font_names() {
1632 let has_usage = self
1633 .document_used_chars_by_font
1634 .get(&font_name)
1635 .map(|chars| !chars.is_empty())
1636 .unwrap_or(false);
1637 if !has_usage {
1638 continue;
1639 }
1640 if let Some(font) = document.get_custom_font(&font_name) {
1641 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1644 font_refs.insert(font_name.clone(), font_id);
1645 }
1646 }
1647
1648 let mut cid_fonts: Vec<(&String, &(Vec<u8>, crate::fonts::CidMapping))> =
1654 document.cid_keyed_fonts().iter().collect();
1655 cid_fonts.sort_by(|a, b| a.0.cmp(b.0));
1656 for (font_name, (data, mapping)) in cid_fonts {
1657 let font_id = self.write_cid_keyed_font(font_name, data, mapping)?;
1658 font_refs.insert(font_name.clone(), font_id);
1659 }
1660
1661 Ok(font_refs)
1662 }
1663
1664 fn write_font_with_unicode_support(
1666 &mut self,
1667 font_name: &str,
1668 font: &crate::fonts::Font,
1669 ) -> Result<ObjectId> {
1670 self.write_type0_font_from_font(font_name, font)
1673 }
1674
1675 fn write_type0_font_from_font(
1677 &mut self,
1678 font_name: &str,
1679 font: &crate::fonts::Font,
1680 ) -> Result<ObjectId> {
1681 let used_chars = self
1688 .document_used_chars_by_font
1689 .get(font_name)
1690 .cloned()
1691 .unwrap_or_else(|| {
1692 let mut chars = std::collections::HashSet::new();
1693 for ch in
1694 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1695 {
1696 chars.insert(ch);
1697 }
1698 chars
1699 });
1700
1701 let used_text: String = used_chars.iter().copied().collect();
1709 let mut missing = font.missing_glyphs(&used_text);
1710 if !missing.is_empty() {
1711 missing.sort_unstable();
1712 let list = missing
1713 .iter()
1714 .map(|c| format!("U+{:04X} {:?}", *c as u32, c))
1715 .collect::<Vec<_>>()
1716 .join(", ");
1717 tracing::warn!(
1718 "Custom font '{}' has no glyph for {} character(s): {}. \
1719 They will render as .notdef (empty boxes); the embedded font \
1720 does not contain these glyphs.",
1721 font_name,
1722 missing.len(),
1723 list
1724 );
1725 }
1726
1727 let font_id = self.allocate_object_id();
1729 let descendant_font_id = self.allocate_object_id();
1730 let descriptor_id = self.allocate_object_id();
1731 let font_file_id = self.allocate_object_id();
1732 let to_unicode_id = self.allocate_object_id();
1733
1734 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1739 if font.data.len() > 100_000 && !used_chars.is_empty() {
1740 match crate::text::fonts::truetype_subsetter::subset_font(
1741 font.data.clone(),
1742 &used_chars,
1743 ) {
1744 Ok(subset_result) => (
1745 subset_result.font_data,
1746 Some(subset_result.glyph_mapping),
1747 font.clone(),
1748 ),
1749 Err(_) => {
1750 if font.data.len() < 25_000_000 {
1751 (font.data.clone(), None, font.clone())
1752 } else {
1753 (Vec::new(), None, font.clone())
1754 }
1755 }
1756 }
1757 } else {
1758 (font.data.clone(), None, font.clone())
1759 };
1760
1761 if !font_data_to_embed.is_empty() {
1762 let mut font_file_dict = Dictionary::new();
1767 match font.format {
1768 crate::fonts::FontFormat::OpenType => {
1769 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1770 }
1771 crate::fonts::FontFormat::TrueType => {
1772 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1773 }
1774 }
1775
1776 #[cfg(feature = "compression")]
1781 {
1782 let font_stream_obj = if self.config.compress_streams {
1783 let mut stream =
1784 crate::objects::Stream::with_dictionary(font_file_dict, font_data_to_embed);
1785 stream.compress_flate()?;
1786 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1787 } else {
1788 Object::Stream(font_file_dict, font_data_to_embed)
1789 };
1790 self.write_object(font_file_id, font_stream_obj)?;
1791 }
1792 #[cfg(not(feature = "compression"))]
1793 {
1794 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1795 self.write_object(font_file_id, font_stream_obj)?;
1796 }
1797 } else {
1798 let font_file_dict = Dictionary::new();
1800 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1801 self.write_object(font_file_id, font_stream_obj)?;
1802 }
1803
1804 let mut descriptor = Dictionary::new();
1806 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1807 descriptor.set("FontName", Object::Name(font_name.to_string()));
1808 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1810 "FontBBox",
1811 Object::Array(vec![
1812 Object::Integer(font.descriptor.font_bbox[0] as i64),
1813 Object::Integer(font.descriptor.font_bbox[1] as i64),
1814 Object::Integer(font.descriptor.font_bbox[2] as i64),
1815 Object::Integer(font.descriptor.font_bbox[3] as i64),
1816 ]),
1817 );
1818 descriptor.set(
1819 "ItalicAngle",
1820 Object::Real(font.descriptor.italic_angle as f64),
1821 );
1822 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1823 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1824 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1825 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1826 let font_file_key = match font.format {
1828 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1831 descriptor.set(font_file_key, Object::Reference(font_file_id));
1832 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1833
1834 let mut cid_font = Dictionary::new();
1836 cid_font.set("Type", Object::Name("Font".to_string()));
1837 let cid_font_subtype = match font.format {
1839 crate::fonts::FontFormat::OpenType => "CIDFontType0",
1840 crate::fonts::FontFormat::TrueType => "CIDFontType2",
1841 };
1842 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1843 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1844
1845 let mut cid_system_info = Dictionary::new();
1847 let (registry, ordering, supplement) =
1848 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1849 cjk_type.cid_system_info()
1850 } else {
1851 ("Adobe", "Identity", 0)
1852 };
1853
1854 cid_system_info.set("Registry", Object::String(registry.to_string()));
1855 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1856 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1857 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1858
1859 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1860
1861 let default_width = self.calculate_default_width(font);
1863 cid_font.set("DW", Object::Integer(default_width));
1864
1865 let w_array = self.generate_width_array(
1869 &original_font_for_widths,
1870 default_width,
1871 subset_glyph_mapping.as_ref(),
1872 );
1873 cid_font.set("W", Object::Array(w_array));
1874
1875 if cid_font_subtype == "CIDFontType2" {
1879 let cid_to_gid_map =
1881 self.generate_cid_to_gid_map(font_name, font, subset_glyph_mapping.as_ref())?;
1882 if !cid_to_gid_map.is_empty() {
1883 let cid_to_gid_map_id = self.allocate_object_id();
1891 let map_dict = Dictionary::new();
1892 #[cfg(feature = "compression")]
1893 let map_stream = if self.config.compress_streams {
1894 let mut stream =
1895 crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1896 stream.compress_flate()?;
1897 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1898 } else {
1899 let mut d = map_dict;
1900 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1901 Object::Stream(d, cid_to_gid_map)
1902 };
1903 #[cfg(not(feature = "compression"))]
1904 let map_stream = {
1905 let mut d = map_dict;
1906 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1907 Object::Stream(d, cid_to_gid_map)
1908 };
1909 self.write_object(cid_to_gid_map_id, map_stream)?;
1910 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1911 } else {
1912 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1913 }
1914 }
1915 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1918
1919 let cmap_data = self.generate_tounicode_cmap_from_font(font_name, font);
1925 let cmap_dict = Dictionary::new();
1926 #[cfg(feature = "compression")]
1927 let cmap_stream = if self.config.compress_streams {
1928 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1929 stream.compress_flate()?;
1930 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1931 } else {
1932 Object::Stream(cmap_dict, cmap_data)
1933 };
1934 #[cfg(not(feature = "compression"))]
1935 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1936 self.write_object(to_unicode_id, cmap_stream)?;
1937
1938 let mut type0_font = Dictionary::new();
1940 type0_font.set("Type", Object::Name("Font".to_string()));
1941 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1942 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1943 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1944 type0_font.set(
1945 "DescendantFonts",
1946 Object::Array(vec![Object::Reference(descendant_font_id)]),
1947 );
1948 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1949
1950 self.write_object(font_id, Object::Dictionary(type0_font))?;
1951
1952 Ok(font_id)
1953 }
1954
1955 fn write_cid_keyed_font(
1965 &mut self,
1966 font_name: &str,
1967 data: &[u8],
1968 mapping: &crate::fonts::CidMapping,
1969 ) -> Result<ObjectId> {
1970 use crate::text::fonts::truetype::TrueTypeFont;
1971
1972 let font = crate::fonts::Font::from_bytes(font_name, data.to_vec())?;
1976 let tt_font = TrueTypeFont::parse(data.to_vec())?;
1977
1978 let used_gids: std::collections::HashSet<u16> =
1985 mapping.cid_to_gid.values().copied().collect();
1986 let (embed_bytes, gid_remap): (Vec<u8>, Option<std::collections::HashMap<u16, u16>>) =
1987 match crate::text::fonts::truetype_subsetter::subset_font_by_gids(
1988 data.to_vec(),
1989 &used_gids,
1990 ) {
1991 Ok(subset) => (subset.font_data, Some(subset.old_to_new)),
1992 Err(e) => {
1993 tracing::debug!("CID-keyed subsetting failed ({e:?}); embedding full font");
1994 (data.to_vec(), None)
1995 }
1996 };
1997
1998 let font_id = self.allocate_object_id();
1999 let descendant_font_id = self.allocate_object_id();
2000 let descriptor_id = self.allocate_object_id();
2001 let font_file_id = self.allocate_object_id();
2002 let to_unicode_id = self.allocate_object_id();
2003
2004 let mut font_file_dict = Dictionary::new();
2007 font_file_dict.set("Length1", Object::Integer(embed_bytes.len() as i64));
2008 #[cfg(feature = "compression")]
2009 {
2010 let font_stream_obj = if self.config.compress_streams {
2011 let mut stream =
2012 crate::objects::Stream::with_dictionary(font_file_dict, embed_bytes);
2013 stream.compress_flate()?;
2014 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2015 } else {
2016 let mut d = font_file_dict;
2017 d.set("Length", Object::Integer(embed_bytes.len() as i64));
2018 Object::Stream(d, embed_bytes)
2019 };
2020 self.write_object(font_file_id, font_stream_obj)?;
2021 }
2022 #[cfg(not(feature = "compression"))]
2023 {
2024 let mut d = font_file_dict;
2025 d.set("Length", Object::Integer(embed_bytes.len() as i64));
2026 self.write_object(font_file_id, Object::Stream(d, embed_bytes))?;
2027 }
2028
2029 let mut descriptor = Dictionary::new();
2031 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2032 descriptor.set("FontName", Object::Name(font_name.to_string()));
2033 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
2035 "FontBBox",
2036 Object::Array(vec![
2037 Object::Integer(font.descriptor.font_bbox[0] as i64),
2038 Object::Integer(font.descriptor.font_bbox[1] as i64),
2039 Object::Integer(font.descriptor.font_bbox[2] as i64),
2040 Object::Integer(font.descriptor.font_bbox[3] as i64),
2041 ]),
2042 );
2043 descriptor.set(
2044 "ItalicAngle",
2045 Object::Real(font.descriptor.italic_angle as f64),
2046 );
2047 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
2048 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
2049 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
2050 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
2051 descriptor.set("FontFile2", Object::Reference(font_file_id));
2052 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2053
2054 let mut cid_font = Dictionary::new();
2057 cid_font.set("Type", Object::Name("Font".to_string()));
2058 cid_font.set("Subtype", Object::Name("CIDFontType2".to_string()));
2059 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
2060 let mut cid_system_info = Dictionary::new();
2061 cid_system_info.set("Registry", Object::String("Adobe".to_string()));
2062 cid_system_info.set("Ordering", Object::String("Identity".to_string()));
2063 cid_system_info.set("Supplement", Object::Integer(0));
2064 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
2065 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
2066 cid_font.set("DW", Object::Integer(self.calculate_default_width(&font)));
2067
2068 let w_tuples = mapping.generate_width_array(&tt_font)?;
2072 let mut w_array: Vec<Object> = Vec::new();
2073 for (cid, _cid_last, width) in w_tuples {
2074 w_array.push(Object::Integer(cid as i64));
2075 w_array.push(Object::Array(vec![Object::Integer(width as i64)]));
2076 }
2077 cid_font.set("W", Object::Array(w_array));
2078
2079 let cid_to_gid_map = match &gid_remap {
2084 Some(remap) => {
2085 let mut remapped = mapping.clone();
2086 remapped.cid_to_gid = mapping
2087 .cid_to_gid
2088 .iter()
2089 .map(|(&cid, &old_gid)| (cid, remap.get(&old_gid).copied().unwrap_or(0)))
2090 .collect();
2091 remapped.generate_cid_to_gid_map()
2092 }
2093 None => mapping.generate_cid_to_gid_map(),
2094 };
2095 if cid_to_gid_map.is_empty() {
2096 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
2097 } else {
2098 let cid_to_gid_map_id = self.allocate_object_id();
2099 let map_dict = Dictionary::new();
2100 #[cfg(feature = "compression")]
2101 let map_stream = if self.config.compress_streams {
2102 let mut stream = crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
2103 stream.compress_flate()?;
2104 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2105 } else {
2106 let mut d = map_dict;
2107 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
2108 Object::Stream(d, cid_to_gid_map)
2109 };
2110 #[cfg(not(feature = "compression"))]
2111 let map_stream = {
2112 let mut d = map_dict;
2113 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
2114 Object::Stream(d, cid_to_gid_map)
2115 };
2116 self.write_object(cid_to_gid_map_id, map_stream)?;
2117 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
2118 }
2119 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
2120
2121 let cmap_data = mapping.generate_tounicode_cmap();
2123 let cmap_dict = Dictionary::new();
2124 #[cfg(feature = "compression")]
2125 let cmap_stream = if self.config.compress_streams {
2126 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
2127 stream.compress_flate()?;
2128 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2129 } else {
2130 let mut d = cmap_dict;
2131 d.set("Length", Object::Integer(cmap_data.len() as i64));
2132 Object::Stream(d, cmap_data)
2133 };
2134 #[cfg(not(feature = "compression"))]
2135 let cmap_stream = {
2136 let mut d = cmap_dict;
2137 d.set("Length", Object::Integer(cmap_data.len() as i64));
2138 Object::Stream(d, cmap_data)
2139 };
2140 self.write_object(to_unicode_id, cmap_stream)?;
2141
2142 let mut type0_font = Dictionary::new();
2144 type0_font.set("Type", Object::Name("Font".to_string()));
2145 type0_font.set("Subtype", Object::Name("Type0".to_string()));
2146 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
2147 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
2148 type0_font.set(
2149 "DescendantFonts",
2150 Object::Array(vec![Object::Reference(descendant_font_id)]),
2151 );
2152 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
2153 self.write_object(font_id, Object::Dictionary(type0_font))?;
2154
2155 Ok(font_id)
2156 }
2157
2158 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
2160 use crate::text::fonts::truetype::TrueTypeFont;
2161
2162 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2164 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2165 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2166 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
2167 let common_chars =
2171 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
2172 let mut total_width = 0;
2173 let mut count = 0;
2174
2175 for ch in common_chars.chars() {
2176 let unicode = ch as u32;
2177 if let Some(&pdf_width) = widths.get(&unicode) {
2178 total_width += pdf_width as i64;
2179 count += 1;
2180 }
2181 }
2182
2183 if count > 0 {
2184 return total_width / count;
2185 }
2186 }
2187 }
2188 }
2189 }
2190
2191 500
2193 }
2194
2195 fn generate_width_array(
2197 &self,
2198 font: &crate::fonts::Font,
2199 _default_width: i64,
2200 subset_mapping: Option<&HashMap<u32, u16>>,
2201 ) -> Vec<Object> {
2202 use crate::text::fonts::truetype::TrueTypeFont;
2203
2204 let mut w_array = Vec::new();
2205
2206 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2208 let char_to_glyph = {
2212 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2214 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2215 if let Some(subset_map) = subset_mapping {
2217 let mut filtered = HashMap::new();
2218 for unicode in subset_map.keys() {
2219 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
2221 filtered.insert(*unicode, orig_glyph);
2222 }
2223 }
2224 filtered
2225 } else {
2226 cmap.mappings.clone()
2227 }
2228 } else {
2229 HashMap::new()
2230 }
2231 } else {
2232 HashMap::new()
2233 }
2234 };
2235
2236 if !char_to_glyph.is_empty() {
2237 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
2239 let mut sorted_chars: Vec<_> = widths.iter().collect();
2244 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
2245
2246 let mut i = 0;
2247 while i < sorted_chars.len() {
2248 let start_unicode = *sorted_chars[i].0;
2249 let pdf_width = *sorted_chars[i].1 as i64;
2251
2252 let mut end_unicode = start_unicode;
2254 let mut j = i + 1;
2255 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
2256 let next_pdf_width = *sorted_chars[j].1 as i64;
2257 if next_pdf_width == pdf_width {
2258 end_unicode = *sorted_chars[j].0;
2259 j += 1;
2260 } else {
2261 break;
2262 }
2263 }
2264
2265 if start_unicode == end_unicode {
2267 w_array.push(Object::Integer(start_unicode as i64));
2269 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
2270 } else {
2271 w_array.push(Object::Integer(start_unicode as i64));
2273 w_array.push(Object::Integer(end_unicode as i64));
2274 w_array.push(Object::Integer(pdf_width));
2275 }
2276
2277 i = j;
2278 }
2279
2280 return w_array;
2281 }
2282 }
2283 }
2284
2285 let ranges = vec![
2287 (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), ];
2324
2325 for (start, end, width) in ranges {
2327 if start == end {
2328 w_array.push(Object::Integer(start));
2330 w_array.push(Object::Array(vec![Object::Integer(width)]));
2331 } else {
2332 w_array.push(Object::Integer(start));
2334 w_array.push(Object::Integer(end));
2335 w_array.push(Object::Integer(width));
2336 }
2337 }
2338
2339 w_array
2340 }
2341
2342 fn generate_cid_to_gid_map(
2344 &mut self,
2345 font_name: &str,
2346 font: &crate::fonts::Font,
2347 subset_mapping: Option<&HashMap<u32, u16>>,
2348 ) -> Result<Vec<u8>> {
2349 use crate::text::fonts::truetype::TrueTypeFont;
2350
2351 let cmap_mappings = if let Some(subset_map) = subset_mapping {
2354 subset_map.clone()
2356 } else {
2357 let tt_font = TrueTypeFont::parse(font.data.clone())?;
2359 let cmap_tables = tt_font.parse_cmap()?;
2360
2361 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
2363 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
2364 })?;
2365
2366 cmap.mappings.clone()
2367 };
2368
2369 let used_chars = self
2376 .document_used_chars_by_font
2377 .get(font_name)
2378 .cloned()
2379 .unwrap_or_default();
2380
2381 let max_unicode = if !used_chars.is_empty() {
2383 used_chars
2385 .iter()
2386 .map(|ch| *ch as u32)
2387 .max()
2388 .unwrap_or(0x00FF) .min(0xFFFF) as usize
2390 } else {
2391 cmap_mappings
2393 .keys()
2394 .max()
2395 .copied()
2396 .unwrap_or(0xFFFF)
2397 .min(0xFFFF) as usize
2398 };
2399
2400 let mut map = vec![0u8; (max_unicode + 1) * 2];
2402
2403 let mut sample_mappings = Vec::new();
2405 for (&unicode, &glyph_id) in &cmap_mappings {
2406 if unicode <= max_unicode as u32 {
2407 let idx = (unicode as usize) * 2;
2408 map[idx] = (glyph_id >> 8) as u8;
2410 map[idx + 1] = (glyph_id & 0xFF) as u8;
2411
2412 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
2414 {
2415 sample_mappings.push((unicode, glyph_id));
2416 }
2417 }
2418 }
2419
2420 Ok(map)
2421 }
2422
2423 fn generate_tounicode_cmap_from_font(
2425 &self,
2426 font_name: &str,
2427 font: &crate::fonts::Font,
2428 ) -> Vec<u8> {
2429 use crate::text::fonts::truetype::TrueTypeFont;
2430
2431 let mut cmap = String::new();
2432
2433 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
2435 cmap.push_str("12 dict begin\n");
2436 cmap.push_str("begincmap\n");
2437 cmap.push_str("/CIDSystemInfo\n");
2438 cmap.push_str("<< /Registry (Adobe)\n");
2439 cmap.push_str(" /Ordering (UCS)\n");
2440 cmap.push_str(" /Supplement 0\n");
2441 cmap.push_str(">> def\n");
2442 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
2443 cmap.push_str("/CMapType 2 def\n");
2444 cmap.push_str("1 begincodespacerange\n");
2445 cmap.push_str("<0000> <FFFF>\n");
2446 cmap.push_str("endcodespacerange\n");
2447
2448 let used_codepoints: Option<std::collections::HashSet<u32>> = self
2454 .document_used_chars_by_font
2455 .get(font_name)
2456 .map(|chars| {
2457 chars
2458 .iter()
2459 .map(|c| *c as u32)
2460 .filter(|cp| *cp <= 0xFFFF)
2461 .collect()
2462 });
2463
2464 let mut mappings: Vec<(u32, u32)> = Vec::new();
2465
2466 if let Some(used) = &used_codepoints {
2467 for cp in used {
2469 mappings.push((*cp, *cp));
2470 }
2471 } else if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2472 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2474 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
2475 for (&unicode, &glyph_id) in &cmap_table.mappings {
2476 if glyph_id > 0 && unicode <= 0xFFFF {
2477 mappings.push((unicode, unicode));
2478 }
2479 }
2480 }
2481 }
2482 }
2483
2484 mappings.sort_by_key(|&(cid, _)| cid);
2486
2487 let mut i = 0;
2489 while i < mappings.len() {
2490 let start_cid = mappings[i].0;
2492 let start_unicode = mappings[i].1;
2493 let mut end_idx = i;
2494
2495 while end_idx + 1 < mappings.len()
2497 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
2498 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
2499 && end_idx - i < 99
2500 {
2502 end_idx += 1;
2503 }
2504
2505 if end_idx > i {
2506 cmap.push_str("1 beginbfrange\n");
2508 cmap.push_str(&format!(
2509 "<{:04X}> <{:04X}> <{:04X}>\n",
2510 start_cid, mappings[end_idx].0, start_unicode
2511 ));
2512 cmap.push_str("endbfrange\n");
2513 i = end_idx + 1;
2514 } else {
2515 let mut chars = Vec::new();
2517 let chunk_end = (i + 100).min(mappings.len());
2518
2519 for item in &mappings[i..chunk_end] {
2520 chars.push(*item);
2521 }
2522
2523 if !chars.is_empty() {
2524 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
2525 for (cid, unicode) in chars {
2526 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
2527 }
2528 cmap.push_str("endbfchar\n");
2529 }
2530
2531 i = chunk_end;
2532 }
2533 }
2534
2535 cmap.push_str("endcmap\n");
2537 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
2538 cmap.push_str("end\n");
2539 cmap.push_str("end\n");
2540
2541 cmap.into_bytes()
2542 }
2543
2544 #[allow(dead_code)]
2546 fn write_truetype_font(
2547 &mut self,
2548 font_name: &str,
2549 font: &crate::text::font_manager::CustomFont,
2550 ) -> Result<ObjectId> {
2551 let font_id = self.allocate_object_id();
2553 let descriptor_id = self.allocate_object_id();
2554 let font_file_id = self.allocate_object_id();
2555
2556 if let Some(ref data) = font.font_data {
2558 let mut font_file_dict = Dictionary::new();
2559 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2560 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2561 self.write_object(font_file_id, font_stream_obj)?;
2562 }
2563
2564 let mut descriptor = Dictionary::new();
2566 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2567 descriptor.set("FontName", Object::Name(font_name.to_string()));
2568 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2570 "FontBBox",
2571 Object::Array(vec![
2572 Object::Integer(-1000),
2573 Object::Integer(-1000),
2574 Object::Integer(2000),
2575 Object::Integer(2000),
2576 ]),
2577 );
2578 descriptor.set("ItalicAngle", Object::Integer(0));
2579 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2580 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2581 descriptor.set(
2582 "CapHeight",
2583 Object::Integer(font.descriptor.cap_height as i64),
2584 );
2585 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2586 descriptor.set("FontFile2", Object::Reference(font_file_id));
2587 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2588
2589 let mut font_dict = Dictionary::new();
2591 font_dict.set("Type", Object::Name("Font".to_string()));
2592 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2593 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2594 font_dict.set("FirstChar", Object::Integer(0));
2595 font_dict.set("LastChar", Object::Integer(255));
2596
2597 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2599 font_dict.set("Widths", Object::Array(widths));
2600 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2601
2602 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2604
2605 self.write_object(font_id, Object::Dictionary(font_dict))?;
2606
2607 Ok(font_id)
2608 }
2609
2610 fn write_pages(
2611 &mut self,
2612 document: &Document,
2613 font_refs: &HashMap<String, ObjectId>,
2614 ) -> Result<()> {
2615 let pages_id = self.get_pages_id()?;
2616 let mut pages_dict = Dictionary::new();
2617 pages_dict.set("Type", Object::Name("Pages".to_string()));
2618 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2619
2620 let mut kids = Vec::new();
2621
2622 let mut page_ids = Vec::new();
2624 let mut content_ids = Vec::new();
2625 for _ in 0..document.pages.len() {
2626 page_ids.push(self.allocate_object_id());
2627 content_ids.push(self.allocate_object_id());
2628 }
2629
2630 for page_id in &page_ids {
2631 kids.push(Object::Reference(*page_id));
2632 }
2633
2634 pages_dict.set("Kids", Object::Array(kids));
2635
2636 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2637
2638 self.page_ids = page_ids.clone();
2640
2641 let struct_parent_keys = document
2644 .struct_tree
2645 .as_ref()
2646 .map(Self::struct_parent_keys)
2647 .unwrap_or_default();
2648
2649 for (i, page) in document.pages.iter().enumerate() {
2651 let page_id = page_ids[i];
2652 let content_id = content_ids[i];
2653
2654 let preserved_font_map = Self::preserved_font_disambiguation_map(page, font_refs);
2659 self.write_page_with_fonts(
2660 page_id,
2661 pages_id,
2662 content_id,
2663 page,
2664 struct_parent_keys.get(&i).copied(),
2665 font_refs,
2666 &preserved_font_map,
2667 )?;
2668 self.write_page_content(content_id, page, &preserved_font_map)?;
2669 }
2670
2671 Ok(())
2672 }
2673
2674 #[allow(dead_code)]
2676 fn write_pages_with_fonts(
2677 &mut self,
2678 document: &Document,
2679 font_refs: &HashMap<String, ObjectId>,
2680 ) -> Result<()> {
2681 self.write_pages(document, font_refs)
2682 }
2683
2684 fn write_page_with_fonts(
2685 &mut self,
2686 page_id: ObjectId,
2687 parent_id: ObjectId,
2688 content_id: ObjectId,
2689 page: &crate::page::Page,
2690 struct_parent_key: Option<i64>,
2691 font_refs: &HashMap<String, ObjectId>,
2692 preserved_font_map: &HashMap<String, String>,
2693 ) -> Result<()> {
2694 let mut page_dict = page.to_dict();
2696
2697 page_dict.set("Type", Object::Name("Page".to_string()));
2698 page_dict.set("Parent", Object::Reference(parent_id));
2699 page_dict.set("Contents", Object::Reference(content_id));
2700
2701 if let Some(key) = struct_parent_key {
2702 page_dict.set("StructParents", Object::Integer(key));
2703 }
2704
2705 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2707 res.clone()
2708 } else {
2709 Dictionary::new()
2710 };
2711
2712 let mut font_dict = Dictionary::new();
2714
2715 let mut helvetica_dict = Dictionary::new();
2720 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2721 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2722 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2723 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2724 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2725
2726 let mut helvetica_bold_dict = Dictionary::new();
2727 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2728 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2729 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2730 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2731 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2732
2733 let mut helvetica_oblique_dict = Dictionary::new();
2734 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2735 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2736 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2737 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2738 font_dict.set(
2739 "Helvetica-Oblique",
2740 Object::Dictionary(helvetica_oblique_dict),
2741 );
2742
2743 let mut helvetica_bold_oblique_dict = Dictionary::new();
2744 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2745 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2746 helvetica_bold_oblique_dict.set(
2747 "BaseFont",
2748 Object::Name("Helvetica-BoldOblique".to_string()),
2749 );
2750 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2751 font_dict.set(
2752 "Helvetica-BoldOblique",
2753 Object::Dictionary(helvetica_bold_oblique_dict),
2754 );
2755
2756 let mut times_dict = Dictionary::new();
2758 times_dict.set("Type", Object::Name("Font".to_string()));
2759 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2760 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2761 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2762 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2763
2764 let mut times_bold_dict = Dictionary::new();
2765 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2766 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2767 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2768 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2769 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2770
2771 let mut times_italic_dict = Dictionary::new();
2772 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2773 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2774 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2775 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2776 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2777
2778 let mut times_bold_italic_dict = Dictionary::new();
2779 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2780 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2781 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2782 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2783 font_dict.set(
2784 "Times-BoldItalic",
2785 Object::Dictionary(times_bold_italic_dict),
2786 );
2787
2788 let mut courier_dict = Dictionary::new();
2790 courier_dict.set("Type", Object::Name("Font".to_string()));
2791 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2792 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2793 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2794 font_dict.set("Courier", Object::Dictionary(courier_dict));
2795
2796 let mut courier_bold_dict = Dictionary::new();
2797 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2798 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2799 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2800 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2801 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2802
2803 let mut courier_oblique_dict = Dictionary::new();
2804 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2805 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2806 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2807 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2808 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2809
2810 let mut courier_bold_oblique_dict = Dictionary::new();
2811 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2812 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2813 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2814 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2815 font_dict.set(
2816 "Courier-BoldOblique",
2817 Object::Dictionary(courier_bold_oblique_dict),
2818 );
2819
2820 for (font_name, font_id) in font_refs {
2822 font_dict.set(font_name, Object::Reference(*font_id));
2823 }
2824
2825 resources.set("Font", Object::Dictionary(font_dict));
2826
2827 let has_images = !page.images().is_empty();
2829 let has_forms = !page.form_xobjects().is_empty();
2830
2831 let mut form_xobject_ids: HashMap<String, ObjectId> = HashMap::new();
2838
2839 if has_images || has_forms {
2840 let mut xobject_dict = Dictionary::new();
2841
2842 let mut image_entries: Vec<(&String, &crate::graphics::Image)> =
2846 page.images().iter().collect();
2847 image_entries.sort_by_key(|(name, _)| name.as_str());
2848 for (name, image) in image_entries {
2849 let image_id = self.allocate_object_id();
2851
2852 if image.has_transparency() {
2854 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2856
2857 if let Some(smask_stream) = smask_obj {
2859 let smask_id = self.allocate_object_id();
2860 self.write_object(smask_id, smask_stream)?;
2861
2862 if let Object::Stream(ref mut dict, _) = main_obj {
2864 dict.set("SMask", Object::Reference(smask_id));
2865 }
2866 }
2867
2868 self.write_object(image_id, main_obj)?;
2870 } else {
2871 self.write_object(image_id, image.to_pdf_object())?;
2873 }
2874
2875 xobject_dict.set(name, Object::Reference(image_id));
2877 }
2878
2879 let mut form_entries: Vec<(&String, &crate::graphics::FormXObject)> =
2881 page.form_xobjects().iter().collect();
2882 form_entries.sort_by_key(|(name, _)| name.as_str());
2883 for (name, form) in form_entries {
2884 let form_id = self.allocate_object_id();
2885 let stream = form.to_stream()?;
2886 let stream_obj =
2887 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2888 self.write_object(form_id, stream_obj)?;
2889 xobject_dict.set(name, Object::Reference(form_id));
2890 form_xobject_ids.insert(name.clone(), form_id);
2893 }
2894
2895 resources.set("XObject", Object::Dictionary(xobject_dict));
2896 }
2897
2898 if let Some(extgstate_states) = page.get_extgstate_resources() {
2900 let mut extgstate_dict = Dictionary::new();
2901 let mut extgstate_entries: Vec<(&String, &crate::graphics::ExtGState)> =
2903 extgstate_states.iter().collect();
2904 extgstate_entries.sort_by_key(|(name, _)| name.as_str());
2905 for (name, state) in extgstate_entries {
2906 let mut state_dict = Dictionary::new();
2907 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2908
2909 if let Some(alpha_stroke) = state.alpha_stroke {
2911 state_dict.set("CA", Object::Real(alpha_stroke));
2912 }
2913 if let Some(alpha_fill) = state.alpha_fill {
2914 state_dict.set("ca", Object::Real(alpha_fill));
2915 }
2916
2917 if let Some(line_width) = state.line_width {
2919 state_dict.set("LW", Object::Real(line_width));
2920 }
2921 if let Some(line_cap) = state.line_cap {
2922 state_dict.set("LC", Object::Integer(line_cap as i64));
2923 }
2924 if let Some(line_join) = state.line_join {
2925 state_dict.set("LJ", Object::Integer(line_join as i64));
2926 }
2927 if let Some(dash_pattern) = &state.dash_pattern {
2928 let dash_objects: Vec<Object> = dash_pattern
2929 .array
2930 .iter()
2931 .map(|&d| Object::Real(d))
2932 .collect();
2933 state_dict.set(
2934 "D",
2935 Object::Array(vec![
2936 Object::Array(dash_objects),
2937 Object::Real(dash_pattern.phase),
2938 ]),
2939 );
2940 }
2941
2942 if let Some(ref bm) = state.blend_mode {
2946 state_dict.set("BM", Object::Name(bm.pdf_name().to_string()));
2947 }
2948
2949 if let Some(ref soft_mask) = state.soft_mask {
2967 let mut mask_dict = soft_mask.to_pdf_dictionary()?;
2968 if let Some(Object::Name(ref g_name)) = mask_dict.get("G").cloned() {
2969 let form_id = form_xobject_ids.get(g_name).ok_or_else(|| {
2970 crate::error::PdfError::InvalidStructure(format!(
2971 "SoftMask references transparency group {:?} but no matching \
2972 FormXObject is registered on the page; call \
2973 Page::add_form_xobject({:?}, ...) before saving",
2974 g_name, g_name
2975 ))
2976 })?;
2977 mask_dict.set("G", Object::Reference(*form_id));
2978 }
2979 state_dict.set("SMask", Object::Dictionary(mask_dict));
2980 }
2981
2982 extgstate_dict.set(name, Object::Dictionary(state_dict));
2983 }
2984 if !extgstate_dict.is_empty() {
2985 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2986 }
2987 }
2988
2989 if !page.color_spaces().is_empty() {
3004 let mut cs_dict = Dictionary::new();
3005 let mut entries: Vec<(&String, &crate::graphics::PageColorSpace)> =
3008 page.color_spaces().iter().collect();
3009 entries.sort_by_key(|(name, _)| name.as_str());
3010 for (name, cs) in entries {
3011 if let Some((icc_dict, icc_data)) = cs.icc_stream_parts() {
3016 let icc_id = self.allocate_object_id();
3017 self.write_object(icc_id, Object::Stream(icc_dict, icc_data))?;
3018 cs_dict.set(
3019 name,
3020 Object::Array(vec![
3021 Object::Name("ICCBased".to_string()),
3022 Object::Reference(icc_id),
3023 ]),
3024 );
3025 } else {
3026 cs_dict.set(name, cs.to_object());
3027 }
3028 }
3029 resources.set("ColorSpace", Object::Dictionary(cs_dict));
3030 }
3031
3032 if !page.patterns().is_empty() {
3033 let mut pat_dict = Dictionary::new();
3034 let mut entries: Vec<(&String, &crate::graphics::TilingPattern)> =
3035 page.patterns().iter().collect();
3036 entries.sort_by_key(|(name, _)| name.as_str());
3037 for (name, pattern) in entries {
3038 let pattern_id = self.allocate_object_id();
3039 let pattern_dict = pattern.to_pdf_dictionary()?;
3040 self.write_object(
3041 pattern_id,
3042 Object::Stream(pattern_dict, pattern.content_stream.clone()),
3043 )?;
3044 pat_dict.set(name, Object::Reference(pattern_id));
3045 }
3046 resources.set("Pattern", Object::Dictionary(pat_dict));
3047 }
3048
3049 if !page.shadings().is_empty() || !page.advanced_shadings().is_empty() {
3050 let mut sh_dict = Dictionary::new();
3051
3052 let mut entries: Vec<(&String, &crate::graphics::ShadingDefinition)> =
3054 page.shadings().iter().collect();
3055 entries.sort_by_key(|(name, _)| name.as_str());
3056 for (name, shading) in entries {
3057 let obj = Object::Dictionary(shading.to_pdf_dictionary()?);
3058 let shading_id = self.write_shading_object(obj)?;
3059 sh_dict.set(name, Object::Reference(shading_id));
3060 }
3061
3062 let mut adv: Vec<(&String, &crate::graphics::AdvancedShading)> =
3065 page.advanced_shadings().iter().collect();
3066 adv.sort_by_key(|(name, _)| name.as_str());
3067 for (name, shading) in adv {
3068 let obj = shading.to_pdf_object()?;
3069 let shading_id = self.write_shading_object(obj)?;
3070 sh_dict.set(name, Object::Reference(shading_id));
3071 }
3072
3073 resources.set("Shading", Object::Dictionary(sh_dict));
3074 }
3075
3076 if let Some(preserved_res) = page.get_preserved_resources() {
3079 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
3081
3082 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
3087 let renamed_fonts = crate::writer::apply_font_rename_map(fonts, preserved_font_map);
3088 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
3089 }
3090
3091 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
3095 let mut fonts_with_refs = crate::objects::Dictionary::new();
3096
3097 for (font_name, font_obj) in fonts.iter() {
3098 if let Object::Dictionary(font_dict) = font_obj {
3099 let updated_font = self.write_embedded_font_streams(font_dict)?;
3101 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
3102 } else {
3103 fonts_with_refs.set(font_name, font_obj.clone());
3105 }
3106 }
3107
3108 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
3110 }
3111
3112 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
3116 let mut xobjects_with_refs = crate::objects::Dictionary::new();
3117 tracing::debug!(
3118 "Externalizing {} preserved XObject entries as indirect objects",
3119 xobjects.len()
3120 );
3121
3122 for (xobj_name, xobj_obj) in xobjects.iter() {
3123 match xobj_obj {
3124 Object::Stream(dict, data) => {
3125 let dict = self.externalize_nested_streams_in_dict(dict)?;
3133 let obj_id = self.allocate_object_id();
3134 self.write_object(obj_id, Object::Stream(dict, data.clone()))?;
3135 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
3136 }
3137 Object::Dictionary(dict) => {
3138 let externalized = self.externalize_streams_in_dict(dict)?;
3145 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
3146 }
3147 _ => {
3148 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
3149 }
3150 }
3151 }
3152
3153 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
3154 }
3155
3156 for (key, value) in preserved_writer_dict.iter() {
3158 if let Some(Object::Dictionary(existing)) = resources.get(key) {
3160 if let Object::Dictionary(preserved_dict) = value {
3161 let mut merged = existing.clone();
3162 for (res_name, res_obj) in preserved_dict.iter() {
3164 if !merged.contains_key(res_name) {
3165 merged.set(res_name, res_obj.clone());
3166 }
3167 }
3168 resources.set(key, Object::Dictionary(merged));
3169 }
3170 } else {
3171 resources.set(key, value.clone());
3173 }
3174 }
3175 }
3176
3177 page_dict.set("Resources", Object::Dictionary(resources));
3178
3179 let mut annot_refs: Vec<Object> = Vec::new();
3181
3182 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
3184 for annot in annots {
3185 if let Object::Dictionary(ref annot_dict) = annot {
3186 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
3187 if subtype == "Widget" {
3188 let widget_id = self.allocate_object_id();
3189 self.write_object(widget_id, annot.clone())?;
3190 annot_refs.push(Object::Reference(widget_id));
3191
3192 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
3194 if let Some(Object::String(field_name)) = annot_dict.get("T") {
3195 self.field_widget_map
3196 .entry(field_name.clone())
3197 .or_default()
3198 .push(widget_id);
3199 self.field_id_map.insert(field_name.clone(), widget_id);
3200 self.form_field_ids.push(widget_id);
3201 }
3202 }
3203 continue;
3204 }
3205 }
3206 }
3207 annot_refs.push(annot.clone());
3208 }
3209 }
3210
3211 for annotation in page.annotations() {
3215 let annot_id = self.allocate_object_id();
3216 let mut annot_dict = annotation.to_dict();
3217
3218 if let Some(placeholder) = annotation.field_parent {
3236 if let Some(real_id) = self.form_field_placeholder_map.get(&placeholder) {
3237 annot_dict.set("Parent", Object::Reference(*real_id));
3238 }
3239 }
3240
3241 if let Some(Object::Dictionary(ap_dict)) = annot_dict.get("AP") {
3260 let mut updated_ap = crate::objects::Dictionary::new();
3261 for (state_key, state_val) in ap_dict.iter() {
3262 match state_val {
3263 Object::Stream(sd, data) => {
3264 let patched_sd = Self::rewrite_ap_stream_font_resources(sd, font_refs);
3270 let stream_id = self.allocate_object_id();
3271 self.write_object(stream_id, Object::Stream(patched_sd, data.clone()))?;
3272 updated_ap.set(state_key, Object::Reference(stream_id));
3273 }
3274 Object::Dictionary(down_dict) => {
3275 let externalized = self
3277 .externalize_streams_in_dict_with_font_refs(down_dict, font_refs)?;
3278 updated_ap.set(state_key, Object::Dictionary(externalized));
3279 }
3280 _ => {
3281 updated_ap.set(state_key, state_val.clone());
3282 }
3283 }
3284 }
3285 annot_dict.set("AP", Object::Dictionary(updated_ap));
3286 }
3287
3288 self.write_object(annot_id, Object::Dictionary(annot_dict))?;
3289 annot_refs.push(Object::Reference(annot_id));
3290
3291 if annotation.annotation_type == crate::annotations::AnnotationType::Widget {
3293 if let Some(Object::String(field_name)) = annotation.properties.get("T") {
3294 self.field_widget_map
3295 .entry(field_name.clone())
3296 .or_default()
3297 .push(annot_id);
3298 self.field_id_map.insert(field_name.clone(), annot_id);
3299 self.form_field_ids.push(annot_id);
3300 }
3301 }
3302 }
3303
3304 if !annot_refs.is_empty() {
3306 page_dict.set("Annots", Object::Array(annot_refs));
3307 } else {
3308 page_dict.remove("Annots");
3309 }
3310
3311 self.write_object(page_id, Object::Dictionary(page_dict))?;
3312 Ok(())
3313 }
3314}
3315
3316impl PdfWriter<BufWriter<std::fs::File>> {
3317 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
3318 let file = std::fs::File::create(path)?;
3319 let writer = BufWriter::new(file);
3320
3321 Ok(Self {
3322 writer,
3323 xref_positions: HashMap::new(),
3324 current_position: 0,
3325 next_object_id: 1,
3326 catalog_id: None,
3327 pages_id: None,
3328 info_id: None,
3329 field_widget_map: HashMap::new(),
3330 field_id_map: HashMap::new(),
3331 form_field_ids: Vec::new(),
3332 page_ids: Vec::new(),
3333 config: WriterConfig::default(),
3334 document_used_chars_by_font: std::collections::HashMap::new(),
3335 buffered_objects: HashMap::new(),
3336 compressed_object_map: HashMap::new(),
3337 prev_xref_offset: None,
3338 base_pdf_size: None,
3339 encrypt_obj_id: None,
3340 file_id: None,
3341 encryption_state: None,
3342 pending_encrypt_dict: None,
3343 form_field_placeholder_map: HashMap::new(),
3344 form_manager_field_refs: Vec::new(),
3345 })
3346 }
3347}
3348
3349impl<W: Write> PdfWriter<W> {
3350 fn externalize_streams_in_dict(
3369 &mut self,
3370 dict: &crate::objects::Dictionary,
3371 ) -> Result<crate::objects::Dictionary> {
3372 self.externalize_streams_in_dict_with_font_refs(dict, &HashMap::new())
3373 }
3374
3375 fn externalize_nested_streams_in_dict(
3387 &mut self,
3388 dict: &crate::objects::Dictionary,
3389 ) -> Result<crate::objects::Dictionary> {
3390 let mut result = crate::objects::Dictionary::new();
3391 for (key, value) in dict.iter() {
3392 result.set(key, self.externalize_nested_streams_value(value)?);
3393 }
3394 Ok(result)
3395 }
3396
3397 fn externalize_nested_streams_value(&mut self, value: &Object) -> Result<Object> {
3398 match value {
3399 Object::Stream(d, data) => {
3400 let d = self.externalize_nested_streams_in_dict(d)?;
3401 let obj_id = self.allocate_object_id();
3402 self.write_object(obj_id, Object::Stream(d, data.clone()))?;
3403 Ok(Object::Reference(obj_id))
3404 }
3405 Object::Dictionary(d) => Ok(Object::Dictionary(
3406 self.externalize_nested_streams_in_dict(d)?,
3407 )),
3408 Object::Array(items) => {
3409 let mut out = Vec::with_capacity(items.len());
3410 for item in items {
3411 out.push(self.externalize_nested_streams_value(item)?);
3412 }
3413 Ok(Object::Array(out))
3414 }
3415 other => Ok(other.clone()),
3416 }
3417 }
3418
3419 fn externalize_streams_in_dict_with_font_refs(
3423 &mut self,
3424 dict: &crate::objects::Dictionary,
3425 font_refs: &HashMap<String, ObjectId>,
3426 ) -> Result<crate::objects::Dictionary> {
3427 let mut result = crate::objects::Dictionary::new();
3428 for (key, value) in dict.iter() {
3429 match value {
3430 Object::Stream(d, data) => {
3431 let patched_d = Self::rewrite_ap_stream_font_resources(d, font_refs);
3432 let obj_id = self.allocate_object_id();
3433 self.write_object(obj_id, Object::Stream(patched_d, data.clone()))?;
3434 result.set(key, Object::Reference(obj_id));
3435 }
3436 _ => {
3437 result.set(key, value.clone());
3438 }
3439 }
3440 }
3441 Ok(result)
3442 }
3443
3444 fn rewrite_ap_stream_font_resources(
3460 stream_dict: &crate::objects::Dictionary,
3461 font_refs: &HashMap<String, ObjectId>,
3462 ) -> crate::objects::Dictionary {
3463 if font_refs.is_empty() {
3470 return stream_dict.clone();
3471 }
3472
3473 let mut out = stream_dict.clone();
3474
3475 let Some(Object::Dictionary(resources)) = stream_dict.get("Resources") else {
3481 return out;
3482 };
3483 let Some(Object::Dictionary(fonts)) = resources.get("Font") else {
3484 return out;
3485 };
3486
3487 let mut patched_fonts = crate::objects::Dictionary::new();
3488 let mut changed = false;
3489 for (font_name, entry) in fonts.iter() {
3490 let should_rewrite = match entry {
3494 Object::Dictionary(d) => {
3495 matches!(d.get("Subtype"), Some(Object::Name(s)) if s == "Type0")
3496 }
3497 _ => false,
3498 };
3499 if should_rewrite {
3500 if let Some(font_id) = font_refs.get(font_name.as_str()) {
3501 patched_fonts.set(font_name, Object::Reference(*font_id));
3502 changed = true;
3503 continue;
3504 }
3505 }
3506 patched_fonts.set(font_name, entry.clone());
3507 }
3508
3509 if changed {
3510 let mut patched_resources = resources.clone();
3511 patched_resources.set("Font", Object::Dictionary(patched_fonts));
3512 out.set("Resources", Object::Dictionary(patched_resources));
3513 }
3514 out
3515 }
3516
3517 fn write_embedded_font_streams(
3518 &mut self,
3519 font_dict: &crate::objects::Dictionary,
3520 ) -> Result<crate::objects::Dictionary> {
3521 let mut updated_font = font_dict.clone();
3522
3523 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
3525 if subtype == "Type0" {
3526 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
3528 let mut updated_descendants = Vec::new();
3529
3530 for descendant in descendants {
3531 match descendant {
3532 Object::Dictionary(cidfont) => {
3533 let updated_cidfont =
3535 self.write_cidfont_embedded_streams(cidfont)?;
3536 let cidfont_id = self.allocate_object_id();
3538 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
3539 updated_descendants.push(Object::Reference(cidfont_id));
3541 }
3542 Object::Reference(_) => {
3543 updated_descendants.push(descendant.clone());
3545 }
3546 _ => {
3547 updated_descendants.push(descendant.clone());
3548 }
3549 }
3550 }
3551
3552 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
3553 }
3554
3555 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
3557 let tounicode_id = self.allocate_object_id();
3558 self.write_object(
3559 tounicode_id,
3560 Object::Stream(stream_dict.clone(), stream_data.clone()),
3561 )?;
3562 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
3563 }
3564
3565 return Ok(updated_font);
3566 }
3567 }
3568
3569 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
3572 let mut updated_descriptor = descriptor.clone();
3573 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3574
3575 for key in &font_file_keys {
3577 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3578 let stream_id = self.allocate_object_id();
3580 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
3581 self.write_object(stream_id, stream_obj)?;
3582
3583 updated_descriptor.set(*key, Object::Reference(stream_id));
3585 }
3586 }
3588
3589 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
3591 }
3592
3593 Ok(updated_font)
3594 }
3595
3596 fn write_cidfont_embedded_streams(
3598 &mut self,
3599 cidfont: &crate::objects::Dictionary,
3600 ) -> Result<crate::objects::Dictionary> {
3601 let mut updated_cidfont = cidfont.clone();
3602
3603 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
3605 let mut updated_descriptor = descriptor.clone();
3606 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3607
3608 for key in &font_file_keys {
3610 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3611 let stream_id = self.allocate_object_id();
3612 self.write_object(
3613 stream_id,
3614 Object::Stream(stream_dict.clone(), stream_data.clone()),
3615 )?;
3616 updated_descriptor.set(*key, Object::Reference(stream_id));
3617 }
3618 }
3619
3620 let descriptor_id = self.allocate_object_id();
3622 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
3623
3624 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
3626 }
3627
3628 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
3630 let map_id = self.allocate_object_id();
3631 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
3632 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
3633 }
3634
3635 Ok(updated_cidfont)
3636 }
3637
3638 fn allocate_object_id(&mut self) -> ObjectId {
3639 let id = ObjectId::new(self.next_object_id, 0);
3640 self.next_object_id += 1;
3641 id
3642 }
3643
3644 fn write_shading_object(&mut self, obj: Object) -> Result<ObjectId> {
3653 match obj {
3654 Object::Dictionary(mut dict) => {
3655 if matches!(
3656 dict.get("Function"),
3657 Some(Object::Dictionary(_)) | Some(Object::Stream(_, _))
3658 ) {
3659 if let Some(func_obj) = dict.remove("Function") {
3660 let func_id = self.allocate_object_id();
3661 self.write_object(func_id, func_obj)?;
3662 dict.set("Function", Object::Reference(func_id));
3663 }
3664 }
3665 let shading_id = self.allocate_object_id();
3666 self.write_object(shading_id, Object::Dictionary(dict))?;
3667 Ok(shading_id)
3668 }
3669 stream @ Object::Stream(_, _) => {
3670 let shading_id = self.allocate_object_id();
3671 self.write_object(shading_id, stream)?;
3672 Ok(shading_id)
3673 }
3674 other => {
3675 let shading_id = self.allocate_object_id();
3676 self.write_object(shading_id, other)?;
3677 Ok(shading_id)
3678 }
3679 }
3680 }
3681
3682 fn get_catalog_id(&self) -> Result<ObjectId> {
3684 self.catalog_id.ok_or_else(|| {
3685 PdfError::InvalidOperation(
3686 "catalog_id not initialized - write_document() must be called first".to_string(),
3687 )
3688 })
3689 }
3690
3691 fn get_pages_id(&self) -> Result<ObjectId> {
3693 self.pages_id.ok_or_else(|| {
3694 PdfError::InvalidOperation(
3695 "pages_id not initialized - write_document() must be called first".to_string(),
3696 )
3697 })
3698 }
3699
3700 fn get_info_id(&self) -> Result<ObjectId> {
3702 self.info_id.ok_or_else(|| {
3703 PdfError::InvalidOperation(
3704 "info_id not initialized - write_document() must be called first".to_string(),
3705 )
3706 })
3707 }
3708
3709 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
3710 use crate::writer::ObjectStreamWriter;
3711
3712 let object = if let Some(ref enc_state) = self.encryption_state {
3714 let mut obj = object;
3715 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
3716 obj
3717 } else {
3718 object
3719 };
3720
3721 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
3723 let mut buffer = Vec::new();
3724 self.write_object_value_to_buffer(&object, &mut buffer)?;
3725 self.buffered_objects.insert(id, buffer);
3726 return Ok(());
3727 }
3728
3729 self.xref_positions.insert(id, self.current_position);
3731
3732 let header = format!("{} {} obj\n", id.number(), id.generation());
3734 self.write_bytes(header.as_bytes())?;
3735
3736 self.write_object_value(&object)?;
3737
3738 self.write_bytes(b"\nendobj\n")?;
3739 Ok(())
3740 }
3741
3742 fn write_object_value(&mut self, object: &Object) -> Result<()> {
3743 match object {
3744 Object::Null => self.write_bytes(b"null")?,
3745 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
3746 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
3747 Object::Real(f) => self.write_bytes(
3748 format!("{f:.6}")
3749 .trim_end_matches('0')
3750 .trim_end_matches('.')
3751 .as_bytes(),
3752 )?,
3753 Object::String(s) => {
3754 self.write_bytes(b"(")?;
3763 self.write_bytes(&escape_pdf_string_bytes(s.as_bytes()))?;
3764 self.write_bytes(b")")?;
3765 }
3766 Object::ByteString(bytes) => {
3767 self.write_bytes(b"<")?;
3769 for byte in bytes {
3770 self.write_bytes(format!("{byte:02X}").as_bytes())?;
3771 }
3772 self.write_bytes(b">")?;
3773 }
3774 Object::Name(n) => {
3775 self.write_bytes(b"/")?;
3776 self.write_bytes(n.as_bytes())?;
3777 }
3778 Object::Array(arr) => {
3779 self.write_bytes(b"[")?;
3780 for (i, obj) in arr.iter().enumerate() {
3781 if i > 0 {
3782 self.write_bytes(b" ")?;
3783 }
3784 self.write_object_value(obj)?;
3785 }
3786 self.write_bytes(b"]")?;
3787 }
3788 Object::Dictionary(dict) => {
3789 self.write_bytes(b"<<")?;
3797 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3798 entries.sort_by_key(|(k, _)| k.as_str());
3799 for (key, value) in entries {
3800 self.write_bytes(b"\n/")?;
3801 self.write_bytes(key.as_bytes())?;
3802 self.write_bytes(b" ")?;
3803 self.write_object_value(value)?;
3804 }
3805 self.write_bytes(b"\n>>")?;
3806 }
3807 Object::Stream(dict, data) => {
3808 let mut corrected_dict = dict.clone();
3811 corrected_dict.set("Length", Object::Integer(data.len() as i64));
3812
3813 self.write_object_value(&Object::Dictionary(corrected_dict))?;
3814 self.write_bytes(b"\nstream\n")?;
3815 self.write_bytes(data)?;
3816 self.write_bytes(b"\nendstream")?;
3817 }
3818 Object::Reference(id) => {
3819 let ref_str = format!("{} {} R", id.number(), id.generation());
3820 self.write_bytes(ref_str.as_bytes())?;
3821 }
3822 }
3823 Ok(())
3824 }
3825
3826 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
3828 match object {
3829 Object::Null => buffer.extend_from_slice(b"null"),
3830 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
3831 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
3832 Object::Real(f) => buffer.extend_from_slice(
3833 format!("{f:.6}")
3834 .trim_end_matches('0')
3835 .trim_end_matches('.')
3836 .as_bytes(),
3837 ),
3838 Object::String(s) => {
3839 buffer.push(b'(');
3842 buffer.extend_from_slice(&escape_pdf_string_bytes(s.as_bytes()));
3843 buffer.push(b')');
3844 }
3845 Object::ByteString(bytes) => {
3846 buffer.push(b'<');
3847 for byte in bytes {
3848 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
3849 }
3850 buffer.push(b'>');
3851 }
3852 Object::Name(n) => {
3853 buffer.push(b'/');
3854 buffer.extend_from_slice(n.as_bytes());
3855 }
3856 Object::Array(arr) => {
3857 buffer.push(b'[');
3858 for (i, obj) in arr.iter().enumerate() {
3859 if i > 0 {
3860 buffer.push(b' ');
3861 }
3862 self.write_object_value_to_buffer(obj, buffer)?;
3863 }
3864 buffer.push(b']');
3865 }
3866 Object::Dictionary(dict) => {
3867 buffer.extend_from_slice(b"<<");
3871 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3872 entries.sort_by_key(|(k, _)| k.as_str());
3873 for (key, value) in entries {
3874 buffer.extend_from_slice(b"\n/");
3875 buffer.extend_from_slice(key.as_bytes());
3876 buffer.push(b' ');
3877 self.write_object_value_to_buffer(value, buffer)?;
3878 }
3879 buffer.extend_from_slice(b"\n>>");
3880 }
3881 Object::Stream(_, _) => {
3882 return Err(crate::error::PdfError::ObjectStreamError(
3884 "Cannot compress stream objects in object streams".to_string(),
3885 ));
3886 }
3887 Object::Reference(id) => {
3888 let ref_str = format!("{} {} R", id.number(), id.generation());
3889 buffer.extend_from_slice(ref_str.as_bytes());
3890 }
3891 }
3892 Ok(())
3893 }
3894
3895 fn flush_object_streams(&mut self) -> Result<()> {
3897 if self.buffered_objects.is_empty() {
3898 return Ok(());
3899 }
3900
3901 let config = ObjectStreamConfig {
3903 max_objects_per_stream: 100,
3904 compression_level: 6,
3905 enabled: true,
3906 };
3907 let mut os_writer = ObjectStreamWriter::new(config);
3908
3909 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
3911 buffered.sort_by_key(|(id, _)| id.number());
3912
3913 for (id, data) in buffered {
3915 os_writer.add_object(*id, data.clone())?;
3916 }
3917
3918 let streams = os_writer.finalize()?;
3920
3921 for mut stream in streams {
3923 let stream_id = stream.stream_id;
3924
3925 let compressed_data = stream.generate_stream_data(6)?;
3927
3928 let dict = stream.generate_dictionary(&compressed_data);
3930
3931 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
3933 self.compressed_object_map
3934 .insert(*obj_id, (stream_id, index as u32));
3935 }
3936
3937 self.xref_positions.insert(stream_id, self.current_position);
3939
3940 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
3941 self.write_bytes(header.as_bytes())?;
3942
3943 self.write_object_value(&Object::Dictionary(dict))?;
3944
3945 self.write_bytes(b"\nstream\n")?;
3946 self.write_bytes(&compressed_data)?;
3947 self.write_bytes(b"\nendstream\nendobj\n")?;
3948 }
3949
3950 Ok(())
3951 }
3952
3953 fn write_xref(&mut self) -> Result<()> {
3954 self.write_bytes(b"xref\n")?;
3955
3956 let mut entries: Vec<_> = self
3958 .xref_positions
3959 .iter()
3960 .map(|(id, pos)| (*id, *pos))
3961 .collect();
3962 entries.sort_by_key(|(id, _)| id.number());
3963
3964 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
3966
3967 self.write_bytes(b"0 ")?;
3970 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
3971 self.write_bytes(b"\n")?;
3972
3973 self.write_bytes(b"0000000000 65535 f \n")?;
3975
3976 for obj_num in 1..=max_obj_num {
3979 let _obj_id = ObjectId::new(obj_num, 0);
3980 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
3981 let entry = format!("{:010} {:05} n \n", position, 0);
3982 self.write_bytes(entry.as_bytes())?;
3983 } else {
3984 self.write_bytes(b"0000000000 00000 f \n")?;
3986 }
3987 }
3988
3989 Ok(())
3990 }
3991
3992 fn write_xref_stream(&mut self) -> Result<()> {
3993 let catalog_id = self.get_catalog_id()?;
3994 let info_id = self.get_info_id()?;
3995
3996 let xref_stream_id = self.allocate_object_id();
3998 let xref_position = self.current_position;
3999
4000 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
4002 xref_writer.set_trailer_info(catalog_id, info_id);
4003
4004 xref_writer.add_free_entry(0, 65535);
4006
4007 let mut entries: Vec<_> = self
4009 .xref_positions
4010 .iter()
4011 .map(|(id, pos)| (*id, *pos))
4012 .collect();
4013 entries.sort_by_key(|(id, _)| id.number());
4014
4015 let max_obj_num = entries
4017 .iter()
4018 .map(|(id, _)| id.number())
4019 .max()
4020 .unwrap_or(0)
4021 .max(xref_stream_id.number());
4022
4023 for obj_num in 1..=max_obj_num {
4025 let obj_id = ObjectId::new(obj_num, 0);
4026
4027 if obj_num == xref_stream_id.number() {
4028 xref_writer.add_in_use_entry(xref_position, 0);
4030 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
4031 xref_writer.add_compressed_entry(stream_id.number(), *index);
4033 } else if let Some((id, position)) =
4034 entries.iter().find(|(id, _)| id.number() == obj_num)
4035 {
4036 xref_writer.add_in_use_entry(*position, id.generation());
4038 } else {
4039 xref_writer.add_free_entry(0, 0);
4041 }
4042 }
4043
4044 self.xref_positions.insert(xref_stream_id, xref_position);
4046
4047 self.write_bytes(
4049 format!(
4050 "{} {} obj\n",
4051 xref_stream_id.number(),
4052 xref_stream_id.generation()
4053 )
4054 .as_bytes(),
4055 )?;
4056
4057 let uncompressed_data = xref_writer.encode_entries();
4059 let final_data = if self.config.compress_streams {
4060 crate::compression::compress(&uncompressed_data)?
4061 } else {
4062 uncompressed_data
4063 };
4064
4065 let mut dict = xref_writer.create_dictionary(None);
4067 dict.set("Length", Object::Integer(final_data.len() as i64));
4068
4069 if self.config.compress_streams {
4071 dict.set("Filter", Object::Name("FlateDecode".to_string()));
4072 }
4073 self.write_bytes(b"<<")?;
4074 for (key, value) in dict.iter() {
4075 self.write_bytes(b"\n/")?;
4076 self.write_bytes(key.as_bytes())?;
4077 self.write_bytes(b" ")?;
4078 self.write_object_value(value)?;
4079 }
4080 self.write_bytes(b"\n>>\n")?;
4081
4082 self.write_bytes(b"stream\n")?;
4084 self.write_bytes(&final_data)?;
4085 self.write_bytes(b"\nendstream\n")?;
4086 self.write_bytes(b"endobj\n")?;
4087
4088 self.write_bytes(b"\nstartxref\n")?;
4090 self.write_bytes(xref_position.to_string().as_bytes())?;
4091 self.write_bytes(b"\n%%EOF\n")?;
4092
4093 Ok(())
4094 }
4095
4096 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
4103 use crate::encryption::{
4104 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
4105 };
4106 use std::sync::Arc;
4107
4108 let mut fid = vec![0u8; 16];
4110 use rand::Rng;
4111 rand::rng().fill_bytes(&mut fid);
4112
4113 let enc_dict = encryption
4114 .create_encryption_dict(Some(&fid))
4115 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
4116
4117 let enc_key = encryption
4119 .get_encryption_key(&enc_dict, Some(&fid))
4120 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
4121
4122 let handler = encryption.handler();
4124 let (method, key_len) = match encryption.strength {
4125 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
4126 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
4127 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
4128 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
4129 };
4130
4131 let std_filter = FunctionalCryptFilter {
4132 name: "StdCF".to_string(),
4133 method,
4134 length: key_len,
4135 auth_event: crate::encryption::AuthEvent::DocOpen,
4136 recipients: None,
4137 };
4138
4139 let mut filter_manager =
4140 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
4141 filter_manager.add_filter(std_filter);
4142
4143 let encryptor =
4144 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
4145
4146 let encrypt_id = self.allocate_object_id();
4148 self.encrypt_obj_id = Some(encrypt_id);
4149 self.file_id = Some(fid);
4150 self.encryption_state = Some(WriterEncryptionState { encryptor });
4151
4152 self.pending_encrypt_dict = Some(enc_dict.to_dict());
4154
4155 Ok(())
4156 }
4157
4158 fn write_encryption_dict(&mut self) -> Result<()> {
4160 if let (Some(encrypt_id), Some(dict)) =
4161 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
4162 {
4163 let enc_state = self.encryption_state.take();
4165 self.write_object(encrypt_id, Object::Dictionary(dict))?;
4166 self.encryption_state = enc_state;
4167 }
4168 Ok(())
4169 }
4170
4171 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
4172 let catalog_id = self.get_catalog_id()?;
4173 let info_id = self.get_info_id()?;
4174 let max_obj_num = self
4176 .xref_positions
4177 .keys()
4178 .map(|id| id.number())
4179 .max()
4180 .unwrap_or(0);
4181
4182 let mut trailer = Dictionary::new();
4183 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
4184 trailer.set("Root", Object::Reference(catalog_id));
4185 trailer.set("Info", Object::Reference(info_id));
4186
4187 if let Some(prev_xref) = self.prev_xref_offset {
4189 trailer.set("Prev", Object::Integer(prev_xref as i64));
4190 }
4191
4192 if let Some(encrypt_id) = self.encrypt_obj_id {
4194 trailer.set("Encrypt", Object::Reference(encrypt_id));
4195 }
4196 if let Some(ref fid) = self.file_id {
4197 trailer.set(
4198 "ID",
4199 Object::Array(vec![
4200 Object::ByteString(fid.clone()),
4201 Object::ByteString(fid.clone()),
4202 ]),
4203 );
4204 }
4205
4206 self.write_bytes(b"trailer\n")?;
4207 self.write_object_value(&Object::Dictionary(trailer))?;
4208 self.write_bytes(b"\nstartxref\n")?;
4209 self.write_bytes(xref_position.to_string().as_bytes())?;
4210 self.write_bytes(b"\n%%EOF\n")?;
4211
4212 Ok(())
4213 }
4214
4215 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
4216 self.writer.write_all(data)?;
4217 self.current_position += data.len() as u64;
4218 Ok(())
4219 }
4220
4221 #[allow(dead_code)]
4222 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
4223 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
4225 if rect_array.len() >= 4 {
4226 if let (
4227 Some(Object::Real(x1)),
4228 Some(Object::Real(y1)),
4229 Some(Object::Real(x2)),
4230 Some(Object::Real(y2)),
4231 ) = (
4232 rect_array.first(),
4233 rect_array.get(1),
4234 rect_array.get(2),
4235 rect_array.get(3),
4236 ) {
4237 (*x1, *y1, *x2, *y2)
4238 } else {
4239 (0.0, 0.0, 100.0, 20.0) }
4241 } else {
4242 (0.0, 0.0, 100.0, 20.0) }
4244 } else {
4245 (0.0, 0.0, 100.0, 20.0) };
4247
4248 let width = rect.2 - rect.0;
4249 let height = rect.3 - rect.1;
4250
4251 let mut content = String::new();
4253
4254 content.push_str("q\n");
4256
4257 crate::graphics::color::write_stroke_color(&mut content, crate::graphics::Color::black());
4259 content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
4263 content.push_str("S\n"); crate::graphics::color::write_fill_color(&mut content, crate::graphics::Color::white());
4267 content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
4268 content.push_str("f\n"); content.push_str("Q\n");
4272
4273 let mut stream_dict = Dictionary::new();
4275 stream_dict.set("Type", Object::Name("XObject".to_string()));
4276 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4277 stream_dict.set(
4278 "BBox",
4279 Object::Array(vec![
4280 Object::Real(0.0),
4281 Object::Real(0.0),
4282 Object::Real(width),
4283 Object::Real(height),
4284 ]),
4285 );
4286 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4287 stream_dict.set("Length", Object::Integer(content.len() as i64));
4288
4289 let stream_id = self.allocate_object_id();
4291 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4292
4293 Ok(stream_id)
4294 }
4295
4296 #[allow(dead_code)]
4297 fn create_field_appearance_stream(
4298 &mut self,
4299 field_dict: &Dictionary,
4300 widget: &crate::forms::Widget,
4301 ) -> Result<ObjectId> {
4302 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
4303 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
4304
4305 let mut content = String::new();
4307
4308 content.push_str("q\n");
4310
4311 if let Some(bg_color) = &widget.appearance.background_color {
4314 crate::graphics::color::write_fill_color(&mut content, *bg_color);
4315 content.push_str(&format!("0 0 {width} {height} re\n"));
4316 content.push_str("f\n");
4317 }
4318
4319 if let Some(border_color) = &widget.appearance.border_color {
4321 crate::graphics::color::write_stroke_color(&mut content, *border_color);
4322 content.push_str(&format!("{} w\n", widget.appearance.border_width));
4323 content.push_str(&format!("0 0 {width} {height} re\n"));
4324 content.push_str("S\n");
4325 }
4326
4327 if let Some(Object::Name(ft)) = field_dict.get("FT") {
4329 if ft == "Btn" {
4330 if let Some(Object::Name(v)) = field_dict.get("V") {
4331 if v == "Yes" {
4332 crate::graphics::color::write_stroke_color(
4334 &mut content,
4335 crate::graphics::Color::black(),
4336 );
4337 content.push_str("2 w\n");
4338 let margin = width * 0.2;
4339 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
4340 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
4341 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
4342 content.push_str("S\n");
4343 }
4344 }
4345 }
4346 }
4347
4348 content.push_str("Q\n");
4350
4351 let mut stream_dict = Dictionary::new();
4353 stream_dict.set("Type", Object::Name("XObject".to_string()));
4354 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4355 stream_dict.set(
4356 "BBox",
4357 Object::Array(vec![
4358 Object::Real(0.0),
4359 Object::Real(0.0),
4360 Object::Real(width),
4361 Object::Real(height),
4362 ]),
4363 );
4364 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4365 stream_dict.set("Length", Object::Integer(content.len() as i64));
4366
4367 let stream_id = self.allocate_object_id();
4369 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4370
4371 Ok(stream_id)
4372 }
4373}
4374
4375fn format_pdf_date(date: DateTime<Utc>) -> String {
4377 let formatted = date.format("D:%Y%m%d%H%M%S");
4380
4381 format!("{formatted}+00'00")
4383}
4384
4385#[cfg(test)]
4386mod tests;
4387
4388#[cfg(test)]
4389mod rigorous_tests;