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 if let Some(language) = struct_tree
1015 .root()
1016 .and_then(|root| root.attributes.lang.as_ref())
1017 {
1018 catalog.set("Lang", Object::String(language.clone()));
1019 }
1020 catalog.set("MarkInfo", {
1022 let mut mark_info = Dictionary::new();
1023 mark_info.set("Marked", Object::Boolean(true));
1024 Object::Dictionary(mark_info)
1025 });
1026 }
1027 }
1028
1029 let xmp_metadata = document.create_xmp_metadata();
1032 let xmp_packet = xmp_metadata.to_xmp_packet();
1033 let metadata_id = self.allocate_object_id();
1034
1035 let mut metadata_dict = Dictionary::new();
1037 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
1038 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
1039 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
1040
1041 self.write_object(
1043 metadata_id,
1044 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
1045 )?;
1046
1047 catalog.set("Metadata", Object::Reference(metadata_id));
1049
1050 if let Some(action) = &document.open_action {
1052 catalog.set("OpenAction", Object::Dictionary(action.to_dict()));
1053 }
1054
1055 if let Some(prefs) = &document.viewer_preferences {
1057 catalog.set("ViewerPreferences", Object::Dictionary(prefs.to_dict()));
1058 }
1059
1060 if let Some(named_dests) = &document.named_destinations {
1065 let dests_tree_id = self.allocate_object_id();
1066 self.write_object(dests_tree_id, Object::Dictionary(named_dests.to_dict()))?;
1067
1068 let mut names_dict = Dictionary::new();
1069 names_dict.set("Dests", Object::Reference(dests_tree_id));
1070 let names_dict_id = self.allocate_object_id();
1071 self.write_object(names_dict_id, Object::Dictionary(names_dict))?;
1072
1073 catalog.set("Names", Object::Reference(names_dict_id));
1074 }
1075
1076 if let Some(page_labels) = &document.page_labels {
1080 let labels_id = self.allocate_object_id();
1081 self.write_object(labels_id, Object::Dictionary(page_labels.to_dict()))?;
1082 catalog.set("PageLabels", Object::Reference(labels_id));
1083 }
1084
1085 self.write_object(catalog_id, Object::Dictionary(catalog))?;
1086 Ok(())
1087 }
1088
1089 fn preserved_font_disambiguation_map(
1097 page: &crate::page::Page,
1098 font_refs: &HashMap<String, ObjectId>,
1099 ) -> HashMap<String, String> {
1100 let preserved_fonts = match page
1101 .get_preserved_resources()
1102 .and_then(|res| res.get("Font"))
1103 {
1104 Some(crate::pdf_objects::Object::Dictionary(fonts)) => fonts,
1105 _ => return HashMap::new(),
1106 };
1107
1108 let mut reserved: std::collections::HashSet<String> =
1109 crate::writer::INJECTED_BASE_FONT_KEYS
1110 .iter()
1111 .map(|s| s.to_string())
1112 .collect();
1113 reserved.extend(font_refs.keys().cloned());
1114
1115 let preserved_keys: Vec<String> = preserved_fonts
1116 .keys()
1117 .map(|k| k.as_str().to_string())
1118 .collect();
1119 crate::writer::collision_font_mapping(preserved_keys.iter().map(|s| s.as_str()), &reserved)
1120 }
1121
1122 fn write_page_content(
1123 &mut self,
1124 content_id: ObjectId,
1125 page: &crate::page::Page,
1126 preserved_font_map: &HashMap<String, String>,
1127 ) -> Result<()> {
1128 let mut page_copy = page.clone();
1129 page_copy.preserved_font_rewrite_map = preserved_font_map.clone();
1132 let content = page_copy.generate_content()?;
1133
1134 #[cfg(feature = "compression")]
1136 {
1137 use crate::objects::Stream;
1138 let mut stream = Stream::new(content);
1139 if self.config.compress_streams {
1141 stream.compress_flate()?;
1142 }
1143
1144 self.write_object(
1145 content_id,
1146 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
1147 )?;
1148 }
1149
1150 #[cfg(not(feature = "compression"))]
1151 {
1152 let mut stream_dict = Dictionary::new();
1153 stream_dict.set("Length", Object::Integer(content.len() as i64));
1154
1155 self.write_object(content_id, Object::Stream(stream_dict, content))?;
1156 }
1157
1158 Ok(())
1159 }
1160
1161 fn write_outline_tree(
1162 &mut self,
1163 outline_tree: &crate::structure::OutlineTree,
1164 ) -> Result<ObjectId> {
1165 let outline_root_id = self.allocate_object_id();
1167
1168 let mut outline_root = Dictionary::new();
1169 outline_root.set("Type", Object::Name("Outlines".to_string()));
1170
1171 if !outline_tree.items.is_empty() {
1172 let mut item_ids = Vec::new();
1174
1175 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
1177 let mut count = items.len();
1178 for item in items {
1179 count += count_items(&item.children);
1180 }
1181 count
1182 }
1183
1184 let total_items = count_items(&outline_tree.items);
1185
1186 for _ in 0..total_items {
1188 item_ids.push(self.allocate_object_id());
1189 }
1190
1191 let mut id_index = 0;
1192
1193 let first_id = item_ids[0];
1195 let last_id = item_ids[outline_tree.items.len() - 1];
1196
1197 outline_root.set("First", Object::Reference(first_id));
1198 outline_root.set("Last", Object::Reference(last_id));
1199
1200 let visible_count = outline_tree.visible_count();
1202 outline_root.set("Count", Object::Integer(visible_count));
1203
1204 let mut written_items = Vec::new();
1206
1207 for (i, item) in outline_tree.items.iter().enumerate() {
1208 let item_id = item_ids[id_index];
1209 id_index += 1;
1210
1211 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1212 let next_id = if i < outline_tree.items.len() - 1 {
1213 Some(item_ids[i + 1])
1214 } else {
1215 None
1216 };
1217
1218 let children_ids = self.write_outline_item(
1220 item,
1221 item_id,
1222 outline_root_id,
1223 prev_id,
1224 next_id,
1225 &mut item_ids,
1226 &mut id_index,
1227 )?;
1228
1229 written_items.extend(children_ids);
1230 }
1231 }
1232
1233 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1234 Ok(outline_root_id)
1235 }
1236
1237 #[allow(clippy::too_many_arguments)]
1238 fn write_outline_item(
1239 &mut self,
1240 item: &crate::structure::OutlineItem,
1241 item_id: ObjectId,
1242 parent_id: ObjectId,
1243 prev_id: Option<ObjectId>,
1244 next_id: Option<ObjectId>,
1245 all_ids: &mut Vec<ObjectId>,
1246 id_index: &mut usize,
1247 ) -> Result<Vec<ObjectId>> {
1248 let mut written_ids = vec![item_id];
1249
1250 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1252 let first_idx = *id_index;
1253 let first_id = all_ids[first_idx];
1254 let last_idx = first_idx + item.children.len() - 1;
1255 let last_id = all_ids[last_idx];
1256
1257 for (i, child) in item.children.iter().enumerate() {
1259 let child_id = all_ids[*id_index];
1260 *id_index += 1;
1261
1262 let child_prev = if i > 0 {
1263 Some(all_ids[first_idx + i - 1])
1264 } else {
1265 None
1266 };
1267 let child_next = if i < item.children.len() - 1 {
1268 Some(all_ids[first_idx + i + 1])
1269 } else {
1270 None
1271 };
1272
1273 let child_ids = self.write_outline_item(
1274 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1276 )?;
1277
1278 written_ids.extend(child_ids);
1279 }
1280
1281 (Some(first_id), Some(last_id))
1282 } else {
1283 (None, None)
1284 };
1285
1286 let item_dict = crate::structure::outline_item_to_dict(
1288 item,
1289 parent_id,
1290 first_child_id,
1291 last_child_id,
1292 prev_id,
1293 next_id,
1294 );
1295
1296 self.write_object(item_id, Object::Dictionary(item_dict))?;
1297
1298 Ok(written_ids)
1299 }
1300
1301 fn write_struct_tree(
1303 &mut self,
1304 struct_tree: &crate::structure::StructTree,
1305 ) -> Result<ObjectId> {
1306 let struct_tree_root_id = self.allocate_object_id();
1308 let mut element_ids = Vec::new();
1309 for _ in 0..struct_tree.len() {
1310 element_ids.push(self.allocate_object_id());
1311 }
1312
1313 let mut page_owners: std::collections::BTreeMap<
1317 usize,
1318 std::collections::BTreeMap<u32, usize>,
1319 > = std::collections::BTreeMap::new();
1320 for (element_index, element) in struct_tree.iter().enumerate() {
1321 for mcid_ref in &element.mcids {
1322 page_owners
1323 .entry(mcid_ref.page_index)
1324 .or_default()
1325 .insert(mcid_ref.mcid, element_index);
1326 }
1327 }
1328
1329 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1331 std::collections::HashMap::new();
1332
1333 if let Some(root_index) = struct_tree.root_index() {
1335 parent_map.insert(root_index, struct_tree_root_id);
1336
1337 fn map_children_parents(
1339 tree: &crate::structure::StructTree,
1340 parent_index: usize,
1341 parent_id: ObjectId,
1342 element_ids: &[ObjectId],
1343 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1344 ) {
1345 if let Some(parent_elem) = tree.get(parent_index) {
1346 for &child_index in &parent_elem.children {
1347 parent_map.insert(child_index, parent_id);
1348 map_children_parents(
1349 tree,
1350 child_index,
1351 element_ids[child_index],
1352 element_ids,
1353 parent_map,
1354 );
1355 }
1356 }
1357 }
1358
1359 map_children_parents(
1360 struct_tree,
1361 root_index,
1362 element_ids[root_index],
1363 &element_ids,
1364 &mut parent_map,
1365 );
1366 }
1367
1368 for (index, element) in struct_tree.iter().enumerate() {
1370 let element_id = element_ids[index];
1371 let mut element_dict = Dictionary::new();
1372
1373 element_dict.set("Type", Object::Name("StructElem".to_string()));
1374 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1375
1376 if let Some(&parent_id) = parent_map.get(&index) {
1378 element_dict.set("P", Object::Reference(parent_id));
1379 }
1380
1381 if let Some(ref id) = element.id {
1383 element_dict.set("ID", Object::String(id.clone()));
1384 }
1385
1386 if let Some(ref lang) = element.attributes.lang {
1388 element_dict.set("Lang", Object::String(lang.clone()));
1389 }
1390 if let Some(ref alt) = element.attributes.alt {
1391 element_dict.set("Alt", Object::String(alt.clone()));
1392 }
1393 if let Some(ref actual_text) = element.attributes.actual_text {
1394 let mut encoded = Vec::with_capacity(2 + actual_text.len() * 2);
1395 encoded.extend_from_slice(&[0xFE, 0xFF]);
1396 for unit in actual_text.encode_utf16() {
1397 encoded.extend_from_slice(&unit.to_be_bytes());
1398 }
1399 element_dict.set("ActualText", Object::ByteString(encoded));
1400 }
1401 if let Some(ref title) = element.attributes.title {
1402 element_dict.set("T", Object::String(title.clone()));
1403 }
1404 if let Some(bbox) = element.attributes.bbox {
1405 element_dict.set(
1406 "BBox",
1407 Object::Array(vec![
1408 Object::Real(bbox[0]),
1409 Object::Real(bbox[1]),
1410 Object::Real(bbox[2]),
1411 Object::Real(bbox[3]),
1412 ]),
1413 );
1414 }
1415
1416 let mut kids = Vec::new();
1418
1419 for &child_index in &element.children {
1421 kids.push(Object::Reference(element_ids[child_index]));
1422 }
1423
1424 for mcid_ref in &element.mcids {
1426 let mut mcr = Dictionary::new();
1427 mcr.set("Type", Object::Name("MCR".to_string()));
1428 mcr.set("Pg", Object::Reference(self.page_ids[mcid_ref.page_index]));
1429 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1430 kids.push(Object::Dictionary(mcr));
1431 }
1432
1433 if !kids.is_empty() {
1434 element_dict.set("K", Object::Array(kids));
1435 }
1436
1437 self.write_object(element_id, Object::Dictionary(element_dict))?;
1438 }
1439
1440 let mut struct_tree_root = Dictionary::new();
1442 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1443
1444 let parent_tree_id = self.allocate_object_id();
1445 let mut nums = Vec::new();
1446 for (struct_parent_key, (_page_index, owners)) in page_owners.iter().enumerate() {
1447 let max_mcid = *owners.keys().next_back().expect("non-empty owner map");
1448 let mut owner_array = vec![Object::Null; max_mcid as usize + 1];
1449 for (&mcid, &element_index) in owners {
1450 owner_array[mcid as usize] = Object::Reference(element_ids[element_index]);
1451 }
1452 nums.push(Object::Integer(struct_parent_key as i64));
1453 nums.push(Object::Array(owner_array));
1454 }
1455 let mut parent_tree = Dictionary::new();
1456 parent_tree.set("Nums", Object::Array(nums));
1457 self.write_object(parent_tree_id, Object::Dictionary(parent_tree))?;
1458 struct_tree_root.set("ParentTree", Object::Reference(parent_tree_id));
1459 struct_tree_root.set(
1460 "ParentTreeNextKey",
1461 Object::Integer(page_owners.len() as i64),
1462 );
1463
1464 if let Some(root_index) = struct_tree.root_index() {
1466 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1467 }
1468
1469 if !struct_tree.role_map.mappings().is_empty() {
1471 let mut role_map = Dictionary::new();
1472 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1473 role_map.set(
1474 custom_type.as_str(),
1475 Object::Name(standard_type.as_pdf_name().to_string()),
1476 );
1477 }
1478 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1479 }
1480
1481 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1482 Ok(struct_tree_root_id)
1483 }
1484
1485 fn validate_struct_tree(
1486 struct_tree: &crate::structure::StructTree,
1487 pages: &[crate::page::Page],
1488 ) -> Result<()> {
1489 let mut owners = std::collections::HashSet::new();
1490 for element in struct_tree.iter() {
1491 for mcid_ref in &element.mcids {
1492 if mcid_ref.page_index >= pages.len() {
1493 return Err(PdfError::InvalidStructure(format!(
1494 "structure element references page {} but document has {} pages",
1495 mcid_ref.page_index,
1496 pages.len()
1497 )));
1498 }
1499 if mcid_ref.mcid >= pages[mcid_ref.page_index].next_mcid() {
1500 return Err(PdfError::InvalidStructure(format!(
1501 "structure element references MCID {} on page {}, but that page has only {} marked-content sequences",
1502 mcid_ref.mcid,
1503 mcid_ref.page_index,
1504 pages[mcid_ref.page_index].next_mcid()
1505 )));
1506 }
1507 if !owners.insert((mcid_ref.page_index, mcid_ref.mcid)) {
1508 return Err(PdfError::InvalidStructure(format!(
1509 "MCID {} on page {} is owned by more than one structure element",
1510 mcid_ref.mcid, mcid_ref.page_index
1511 )));
1512 }
1513 }
1514 }
1515 Ok(())
1516 }
1517
1518 fn struct_parent_keys(
1519 struct_tree: &crate::structure::StructTree,
1520 ) -> std::collections::BTreeMap<usize, i64> {
1521 struct_tree
1522 .iter()
1523 .flat_map(|element| element.mcids.iter().map(|mcid| mcid.page_index))
1524 .collect::<std::collections::BTreeSet<_>>()
1525 .into_iter()
1526 .enumerate()
1527 .map(|(key, page_index)| (page_index, key as i64))
1528 .collect()
1529 }
1530
1531 fn preallocate_form_manager_fields(&mut self, document: &Document) -> Result<()> {
1544 let Some(form_manager) = &document.form_manager else {
1545 return Ok(());
1546 };
1547
1548 for (_name, _form_field, placeholder) in form_manager.iter_fields_sorted() {
1549 let real_id = self.allocate_object_id();
1550 self.form_field_placeholder_map.insert(placeholder, real_id);
1551 self.form_manager_field_refs.push(real_id);
1552 }
1553 Ok(())
1554 }
1555
1556 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1557 if !self.form_field_ids.is_empty() {
1559 if let Some(acro_form) = &mut document.acro_form {
1560 acro_form.fields.clear();
1562 for field_id in &self.form_field_ids {
1563 acro_form.add_field(*field_id);
1564 }
1565
1566 acro_form.need_appearances = true;
1568 if acro_form.da.is_none() {
1569 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1570 }
1571 }
1572 }
1573 Ok(())
1574 }
1575
1576 fn write_info(&mut self, document: &Document) -> Result<()> {
1577 let info_id = self.get_info_id()?;
1578 let mut info_dict = Dictionary::new();
1579
1580 if let Some(ref title) = document.metadata.title {
1581 info_dict.set("Title", Object::String(title.clone()));
1582 }
1583 if let Some(ref author) = document.metadata.author {
1584 info_dict.set("Author", Object::String(author.clone()));
1585 }
1586 if let Some(ref subject) = document.metadata.subject {
1587 info_dict.set("Subject", Object::String(subject.clone()));
1588 }
1589 if let Some(ref keywords) = document.metadata.keywords {
1590 info_dict.set("Keywords", Object::String(keywords.clone()));
1591 }
1592 if let Some(ref creator) = document.metadata.creator {
1593 info_dict.set("Creator", Object::String(creator.clone()));
1594 }
1595 if let Some(ref producer) = document.metadata.producer {
1596 info_dict.set("Producer", Object::String(producer.clone()));
1597 }
1598
1599 if let Some(creation_date) = document.metadata.creation_date {
1601 let date_string = format_pdf_date(creation_date);
1602 info_dict.set("CreationDate", Object::String(date_string));
1603 }
1604
1605 if let Some(mod_date) = document.metadata.modification_date {
1607 let date_string = format_pdf_date(mod_date);
1608 info_dict.set("ModDate", Object::String(date_string));
1609 }
1610
1611 let edition = super::Edition::OpenSource;
1614
1615 let signature = super::PdfSignature::new(document, edition);
1616 signature.write_to_info_dict(&mut info_dict);
1617
1618 self.write_object(info_id, Object::Dictionary(info_dict))?;
1619 Ok(())
1620 }
1621
1622 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1623 let mut font_refs = HashMap::new();
1624
1625 for font_name in document.custom_font_names() {
1635 let has_usage = self
1636 .document_used_chars_by_font
1637 .get(&font_name)
1638 .map(|chars| !chars.is_empty())
1639 .unwrap_or(false);
1640 if !has_usage {
1641 continue;
1642 }
1643 if let Some(font) = document.get_custom_font(&font_name) {
1644 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1647 font_refs.insert(font_name.clone(), font_id);
1648 }
1649 }
1650
1651 let mut cid_fonts: Vec<(&String, &(Vec<u8>, crate::fonts::CidMapping))> =
1657 document.cid_keyed_fonts().iter().collect();
1658 cid_fonts.sort_by(|a, b| a.0.cmp(b.0));
1659 for (font_name, (data, mapping)) in cid_fonts {
1660 let font_id = self.write_cid_keyed_font(font_name, data, mapping)?;
1661 font_refs.insert(font_name.clone(), font_id);
1662 }
1663
1664 Ok(font_refs)
1665 }
1666
1667 fn write_font_with_unicode_support(
1669 &mut self,
1670 font_name: &str,
1671 font: &crate::fonts::Font,
1672 ) -> Result<ObjectId> {
1673 self.write_type0_font_from_font(font_name, font)
1676 }
1677
1678 fn write_type0_font_from_font(
1680 &mut self,
1681 font_name: &str,
1682 font: &crate::fonts::Font,
1683 ) -> Result<ObjectId> {
1684 let used_chars = self
1691 .document_used_chars_by_font
1692 .get(font_name)
1693 .cloned()
1694 .unwrap_or_else(|| {
1695 let mut chars = std::collections::HashSet::new();
1696 for ch in
1697 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1698 {
1699 chars.insert(ch);
1700 }
1701 chars
1702 });
1703
1704 let used_text: String = used_chars.iter().copied().collect();
1712 let mut missing = font.missing_glyphs(&used_text);
1713 if !missing.is_empty() {
1714 missing.sort_unstable();
1715 let list = missing
1716 .iter()
1717 .map(|c| format!("U+{:04X} {:?}", *c as u32, c))
1718 .collect::<Vec<_>>()
1719 .join(", ");
1720 tracing::warn!(
1721 "Custom font '{}' has no glyph for {} character(s): {}. \
1722 They will render as .notdef (empty boxes); the embedded font \
1723 does not contain these glyphs.",
1724 font_name,
1725 missing.len(),
1726 list
1727 );
1728 }
1729
1730 let font_id = self.allocate_object_id();
1732 let descendant_font_id = self.allocate_object_id();
1733 let descriptor_id = self.allocate_object_id();
1734 let font_file_id = self.allocate_object_id();
1735 let to_unicode_id = self.allocate_object_id();
1736
1737 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1742 if font.data.len() > 100_000 && !used_chars.is_empty() {
1743 match crate::text::fonts::truetype_subsetter::subset_font(
1744 font.data.clone(),
1745 &used_chars,
1746 ) {
1747 Ok(subset_result) => (
1748 subset_result.font_data,
1749 Some(subset_result.glyph_mapping),
1750 font.clone(),
1751 ),
1752 Err(_) => {
1753 if font.data.len() < 25_000_000 {
1754 (font.data.clone(), None, font.clone())
1755 } else {
1756 (Vec::new(), None, font.clone())
1757 }
1758 }
1759 }
1760 } else {
1761 (font.data.clone(), None, font.clone())
1762 };
1763
1764 if !font_data_to_embed.is_empty() {
1765 let mut font_file_dict = Dictionary::new();
1770 match font.format {
1771 crate::fonts::FontFormat::OpenType => {
1772 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1773 }
1774 crate::fonts::FontFormat::TrueType => {
1775 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1776 }
1777 }
1778
1779 #[cfg(feature = "compression")]
1784 {
1785 let font_stream_obj = if self.config.compress_streams {
1786 let mut stream =
1787 crate::objects::Stream::with_dictionary(font_file_dict, font_data_to_embed);
1788 stream.compress_flate()?;
1789 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1790 } else {
1791 Object::Stream(font_file_dict, font_data_to_embed)
1792 };
1793 self.write_object(font_file_id, font_stream_obj)?;
1794 }
1795 #[cfg(not(feature = "compression"))]
1796 {
1797 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1798 self.write_object(font_file_id, font_stream_obj)?;
1799 }
1800 } else {
1801 let font_file_dict = Dictionary::new();
1803 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1804 self.write_object(font_file_id, font_stream_obj)?;
1805 }
1806
1807 let mut descriptor = Dictionary::new();
1809 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1810 descriptor.set("FontName", Object::Name(font_name.to_string()));
1811 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1813 "FontBBox",
1814 Object::Array(vec![
1815 Object::Integer(font.descriptor.font_bbox[0] as i64),
1816 Object::Integer(font.descriptor.font_bbox[1] as i64),
1817 Object::Integer(font.descriptor.font_bbox[2] as i64),
1818 Object::Integer(font.descriptor.font_bbox[3] as i64),
1819 ]),
1820 );
1821 descriptor.set(
1822 "ItalicAngle",
1823 Object::Real(font.descriptor.italic_angle as f64),
1824 );
1825 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1826 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1827 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1828 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1829 let font_file_key = match font.format {
1831 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1834 descriptor.set(font_file_key, Object::Reference(font_file_id));
1835 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1836
1837 let mut cid_font = Dictionary::new();
1839 cid_font.set("Type", Object::Name("Font".to_string()));
1840 let cid_font_subtype = match font.format {
1842 crate::fonts::FontFormat::OpenType => "CIDFontType0",
1843 crate::fonts::FontFormat::TrueType => "CIDFontType2",
1844 };
1845 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1846 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1847
1848 let mut cid_system_info = Dictionary::new();
1850 let (registry, ordering, supplement) =
1851 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1852 cjk_type.cid_system_info()
1853 } else {
1854 ("Adobe", "Identity", 0)
1855 };
1856
1857 cid_system_info.set("Registry", Object::String(registry.to_string()));
1858 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1859 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1860 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1861
1862 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1863
1864 let default_width = self.calculate_default_width(font);
1866 cid_font.set("DW", Object::Integer(default_width));
1867
1868 let w_array = self.generate_width_array(
1872 &original_font_for_widths,
1873 default_width,
1874 subset_glyph_mapping.as_ref(),
1875 );
1876 cid_font.set("W", Object::Array(w_array));
1877
1878 if cid_font_subtype == "CIDFontType2" {
1882 let cid_to_gid_map =
1884 self.generate_cid_to_gid_map(font_name, font, subset_glyph_mapping.as_ref())?;
1885 if !cid_to_gid_map.is_empty() {
1886 let cid_to_gid_map_id = self.allocate_object_id();
1894 let map_dict = Dictionary::new();
1895 #[cfg(feature = "compression")]
1896 let map_stream = if self.config.compress_streams {
1897 let mut stream =
1898 crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1899 stream.compress_flate()?;
1900 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1901 } else {
1902 let mut d = map_dict;
1903 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1904 Object::Stream(d, cid_to_gid_map)
1905 };
1906 #[cfg(not(feature = "compression"))]
1907 let map_stream = {
1908 let mut d = map_dict;
1909 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1910 Object::Stream(d, cid_to_gid_map)
1911 };
1912 self.write_object(cid_to_gid_map_id, map_stream)?;
1913 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1914 } else {
1915 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1916 }
1917 }
1918 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1921
1922 let cmap_data = self.generate_tounicode_cmap_from_font(font_name, font);
1928 let cmap_dict = Dictionary::new();
1929 #[cfg(feature = "compression")]
1930 let cmap_stream = if self.config.compress_streams {
1931 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1932 stream.compress_flate()?;
1933 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1934 } else {
1935 Object::Stream(cmap_dict, cmap_data)
1936 };
1937 #[cfg(not(feature = "compression"))]
1938 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1939 self.write_object(to_unicode_id, cmap_stream)?;
1940
1941 let mut type0_font = Dictionary::new();
1943 type0_font.set("Type", Object::Name("Font".to_string()));
1944 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1945 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1946 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1947 type0_font.set(
1948 "DescendantFonts",
1949 Object::Array(vec![Object::Reference(descendant_font_id)]),
1950 );
1951 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1952
1953 self.write_object(font_id, Object::Dictionary(type0_font))?;
1954
1955 Ok(font_id)
1956 }
1957
1958 fn write_cid_keyed_font(
1968 &mut self,
1969 font_name: &str,
1970 data: &[u8],
1971 mapping: &crate::fonts::CidMapping,
1972 ) -> Result<ObjectId> {
1973 use crate::text::fonts::truetype::TrueTypeFont;
1974
1975 let font = crate::fonts::Font::from_bytes(font_name, data.to_vec())?;
1979 let tt_font = TrueTypeFont::parse(data.to_vec())?;
1980
1981 let used_gids: std::collections::HashSet<u16> =
1988 mapping.cid_to_gid.values().copied().collect();
1989 let (embed_bytes, gid_remap): (Vec<u8>, Option<std::collections::HashMap<u16, u16>>) =
1990 match crate::text::fonts::truetype_subsetter::subset_font_by_gids(
1991 data.to_vec(),
1992 &used_gids,
1993 ) {
1994 Ok(subset) => (subset.font_data, Some(subset.old_to_new)),
1995 Err(e) => {
1996 tracing::debug!("CID-keyed subsetting failed ({e:?}); embedding full font");
1997 (data.to_vec(), None)
1998 }
1999 };
2000
2001 let font_id = self.allocate_object_id();
2002 let descendant_font_id = self.allocate_object_id();
2003 let descriptor_id = self.allocate_object_id();
2004 let font_file_id = self.allocate_object_id();
2005 let to_unicode_id = self.allocate_object_id();
2006
2007 let mut font_file_dict = Dictionary::new();
2010 font_file_dict.set("Length1", Object::Integer(embed_bytes.len() as i64));
2011 #[cfg(feature = "compression")]
2012 {
2013 let font_stream_obj = if self.config.compress_streams {
2014 let mut stream =
2015 crate::objects::Stream::with_dictionary(font_file_dict, embed_bytes);
2016 stream.compress_flate()?;
2017 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2018 } else {
2019 let mut d = font_file_dict;
2020 d.set("Length", Object::Integer(embed_bytes.len() as i64));
2021 Object::Stream(d, embed_bytes)
2022 };
2023 self.write_object(font_file_id, font_stream_obj)?;
2024 }
2025 #[cfg(not(feature = "compression"))]
2026 {
2027 let mut d = font_file_dict;
2028 d.set("Length", Object::Integer(embed_bytes.len() as i64));
2029 self.write_object(font_file_id, Object::Stream(d, embed_bytes))?;
2030 }
2031
2032 let mut descriptor = Dictionary::new();
2034 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2035 descriptor.set("FontName", Object::Name(font_name.to_string()));
2036 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
2038 "FontBBox",
2039 Object::Array(vec![
2040 Object::Integer(font.descriptor.font_bbox[0] as i64),
2041 Object::Integer(font.descriptor.font_bbox[1] as i64),
2042 Object::Integer(font.descriptor.font_bbox[2] as i64),
2043 Object::Integer(font.descriptor.font_bbox[3] as i64),
2044 ]),
2045 );
2046 descriptor.set(
2047 "ItalicAngle",
2048 Object::Real(font.descriptor.italic_angle as f64),
2049 );
2050 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
2051 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
2052 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
2053 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
2054 descriptor.set("FontFile2", Object::Reference(font_file_id));
2055 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2056
2057 let mut cid_font = Dictionary::new();
2060 cid_font.set("Type", Object::Name("Font".to_string()));
2061 cid_font.set("Subtype", Object::Name("CIDFontType2".to_string()));
2062 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
2063 let mut cid_system_info = Dictionary::new();
2064 cid_system_info.set("Registry", Object::String("Adobe".to_string()));
2065 cid_system_info.set("Ordering", Object::String("Identity".to_string()));
2066 cid_system_info.set("Supplement", Object::Integer(0));
2067 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
2068 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
2069 cid_font.set("DW", Object::Integer(self.calculate_default_width(&font)));
2070
2071 let w_tuples = mapping.generate_width_array(&tt_font)?;
2075 let mut w_array: Vec<Object> = Vec::new();
2076 for (cid, _cid_last, width) in w_tuples {
2077 w_array.push(Object::Integer(cid as i64));
2078 w_array.push(Object::Array(vec![Object::Integer(width as i64)]));
2079 }
2080 cid_font.set("W", Object::Array(w_array));
2081
2082 let cid_to_gid_map = match &gid_remap {
2087 Some(remap) => {
2088 let mut remapped = mapping.clone();
2089 remapped.cid_to_gid = mapping
2090 .cid_to_gid
2091 .iter()
2092 .map(|(&cid, &old_gid)| (cid, remap.get(&old_gid).copied().unwrap_or(0)))
2093 .collect();
2094 remapped.generate_cid_to_gid_map()
2095 }
2096 None => mapping.generate_cid_to_gid_map(),
2097 };
2098 if cid_to_gid_map.is_empty() {
2099 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
2100 } else {
2101 let cid_to_gid_map_id = self.allocate_object_id();
2102 let map_dict = Dictionary::new();
2103 #[cfg(feature = "compression")]
2104 let map_stream = if self.config.compress_streams {
2105 let mut stream = crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
2106 stream.compress_flate()?;
2107 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2108 } else {
2109 let mut d = map_dict;
2110 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
2111 Object::Stream(d, cid_to_gid_map)
2112 };
2113 #[cfg(not(feature = "compression"))]
2114 let map_stream = {
2115 let mut d = map_dict;
2116 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
2117 Object::Stream(d, cid_to_gid_map)
2118 };
2119 self.write_object(cid_to_gid_map_id, map_stream)?;
2120 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
2121 }
2122 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
2123
2124 let cmap_data = mapping.generate_tounicode_cmap();
2126 let cmap_dict = Dictionary::new();
2127 #[cfg(feature = "compression")]
2128 let cmap_stream = if self.config.compress_streams {
2129 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
2130 stream.compress_flate()?;
2131 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
2132 } else {
2133 let mut d = cmap_dict;
2134 d.set("Length", Object::Integer(cmap_data.len() as i64));
2135 Object::Stream(d, cmap_data)
2136 };
2137 #[cfg(not(feature = "compression"))]
2138 let cmap_stream = {
2139 let mut d = cmap_dict;
2140 d.set("Length", Object::Integer(cmap_data.len() as i64));
2141 Object::Stream(d, cmap_data)
2142 };
2143 self.write_object(to_unicode_id, cmap_stream)?;
2144
2145 let mut type0_font = Dictionary::new();
2147 type0_font.set("Type", Object::Name("Font".to_string()));
2148 type0_font.set("Subtype", Object::Name("Type0".to_string()));
2149 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
2150 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
2151 type0_font.set(
2152 "DescendantFonts",
2153 Object::Array(vec![Object::Reference(descendant_font_id)]),
2154 );
2155 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
2156 self.write_object(font_id, Object::Dictionary(type0_font))?;
2157
2158 Ok(font_id)
2159 }
2160
2161 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
2163 use crate::text::fonts::truetype::TrueTypeFont;
2164
2165 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2167 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2168 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2169 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
2170 let common_chars =
2174 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
2175 let mut total_width = 0;
2176 let mut count = 0;
2177
2178 for ch in common_chars.chars() {
2179 let unicode = ch as u32;
2180 if let Some(&pdf_width) = widths.get(&unicode) {
2181 total_width += pdf_width as i64;
2182 count += 1;
2183 }
2184 }
2185
2186 if count > 0 {
2187 return total_width / count;
2188 }
2189 }
2190 }
2191 }
2192 }
2193
2194 500
2196 }
2197
2198 fn generate_width_array(
2200 &self,
2201 font: &crate::fonts::Font,
2202 _default_width: i64,
2203 subset_mapping: Option<&HashMap<u32, u16>>,
2204 ) -> Vec<Object> {
2205 use crate::text::fonts::truetype::TrueTypeFont;
2206
2207 let mut w_array = Vec::new();
2208
2209 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2211 let char_to_glyph = {
2215 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2217 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2218 if let Some(subset_map) = subset_mapping {
2220 let mut filtered = HashMap::new();
2221 for unicode in subset_map.keys() {
2222 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
2224 filtered.insert(*unicode, orig_glyph);
2225 }
2226 }
2227 filtered
2228 } else {
2229 cmap.mappings.clone()
2230 }
2231 } else {
2232 HashMap::new()
2233 }
2234 } else {
2235 HashMap::new()
2236 }
2237 };
2238
2239 if !char_to_glyph.is_empty() {
2240 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
2242 let mut sorted_chars: Vec<_> = widths.iter().collect();
2247 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
2248
2249 let mut i = 0;
2250 while i < sorted_chars.len() {
2251 let start_unicode = *sorted_chars[i].0;
2252 let pdf_width = *sorted_chars[i].1 as i64;
2254
2255 let mut end_unicode = start_unicode;
2257 let mut j = i + 1;
2258 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
2259 let next_pdf_width = *sorted_chars[j].1 as i64;
2260 if next_pdf_width == pdf_width {
2261 end_unicode = *sorted_chars[j].0;
2262 j += 1;
2263 } else {
2264 break;
2265 }
2266 }
2267
2268 if start_unicode == end_unicode {
2270 w_array.push(Object::Integer(start_unicode as i64));
2272 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
2273 } else {
2274 w_array.push(Object::Integer(start_unicode as i64));
2276 w_array.push(Object::Integer(end_unicode as i64));
2277 w_array.push(Object::Integer(pdf_width));
2278 }
2279
2280 i = j;
2281 }
2282
2283 return w_array;
2284 }
2285 }
2286 }
2287
2288 let ranges = vec![
2290 (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), ];
2327
2328 for (start, end, width) in ranges {
2330 if start == end {
2331 w_array.push(Object::Integer(start));
2333 w_array.push(Object::Array(vec![Object::Integer(width)]));
2334 } else {
2335 w_array.push(Object::Integer(start));
2337 w_array.push(Object::Integer(end));
2338 w_array.push(Object::Integer(width));
2339 }
2340 }
2341
2342 w_array
2343 }
2344
2345 fn generate_cid_to_gid_map(
2347 &mut self,
2348 font_name: &str,
2349 font: &crate::fonts::Font,
2350 subset_mapping: Option<&HashMap<u32, u16>>,
2351 ) -> Result<Vec<u8>> {
2352 use crate::text::fonts::truetype::TrueTypeFont;
2353
2354 let cmap_mappings = if let Some(subset_map) = subset_mapping {
2357 subset_map.clone()
2359 } else {
2360 let tt_font = TrueTypeFont::parse(font.data.clone())?;
2362 let cmap_tables = tt_font.parse_cmap()?;
2363
2364 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
2366 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
2367 })?;
2368
2369 cmap.mappings.clone()
2370 };
2371
2372 let used_chars = self
2379 .document_used_chars_by_font
2380 .get(font_name)
2381 .cloned()
2382 .unwrap_or_default();
2383
2384 let max_unicode = if !used_chars.is_empty() {
2386 used_chars
2388 .iter()
2389 .map(|ch| *ch as u32)
2390 .max()
2391 .unwrap_or(0x00FF) .min(0xFFFF) as usize
2393 } else {
2394 cmap_mappings
2396 .keys()
2397 .max()
2398 .copied()
2399 .unwrap_or(0xFFFF)
2400 .min(0xFFFF) as usize
2401 };
2402
2403 let mut map = vec![0u8; (max_unicode + 1) * 2];
2405
2406 let mut sample_mappings = Vec::new();
2408 for (&unicode, &glyph_id) in &cmap_mappings {
2409 if unicode <= max_unicode as u32 {
2410 let idx = (unicode as usize) * 2;
2411 map[idx] = (glyph_id >> 8) as u8;
2413 map[idx + 1] = (glyph_id & 0xFF) as u8;
2414
2415 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
2417 {
2418 sample_mappings.push((unicode, glyph_id));
2419 }
2420 }
2421 }
2422
2423 Ok(map)
2424 }
2425
2426 fn generate_tounicode_cmap_from_font(
2428 &self,
2429 font_name: &str,
2430 font: &crate::fonts::Font,
2431 ) -> Vec<u8> {
2432 use crate::text::fonts::truetype::TrueTypeFont;
2433
2434 let mut cmap = String::new();
2435
2436 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
2438 cmap.push_str("12 dict begin\n");
2439 cmap.push_str("begincmap\n");
2440 cmap.push_str("/CIDSystemInfo\n");
2441 cmap.push_str("<< /Registry (Adobe)\n");
2442 cmap.push_str(" /Ordering (UCS)\n");
2443 cmap.push_str(" /Supplement 0\n");
2444 cmap.push_str(">> def\n");
2445 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
2446 cmap.push_str("/CMapType 2 def\n");
2447 cmap.push_str("1 begincodespacerange\n");
2448 cmap.push_str("<0000> <FFFF>\n");
2449 cmap.push_str("endcodespacerange\n");
2450
2451 let used_codepoints: Option<std::collections::HashSet<u32>> = self
2457 .document_used_chars_by_font
2458 .get(font_name)
2459 .map(|chars| {
2460 chars
2461 .iter()
2462 .map(|c| *c as u32)
2463 .filter(|cp| *cp <= 0xFFFF)
2464 .collect()
2465 });
2466
2467 let mut mappings: Vec<(u32, u32)> = Vec::new();
2468
2469 if let Some(used) = &used_codepoints {
2470 for cp in used {
2472 mappings.push((*cp, *cp));
2473 }
2474 } else if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2475 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2477 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
2478 for (&unicode, &glyph_id) in &cmap_table.mappings {
2479 if glyph_id > 0 && unicode <= 0xFFFF {
2480 mappings.push((unicode, unicode));
2481 }
2482 }
2483 }
2484 }
2485 }
2486
2487 mappings.sort_by_key(|&(cid, _)| cid);
2489
2490 let mut i = 0;
2492 while i < mappings.len() {
2493 let start_cid = mappings[i].0;
2495 let start_unicode = mappings[i].1;
2496 let mut end_idx = i;
2497
2498 while end_idx + 1 < mappings.len()
2500 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
2501 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
2502 && end_idx - i < 99
2503 {
2505 end_idx += 1;
2506 }
2507
2508 if end_idx > i {
2509 cmap.push_str("1 beginbfrange\n");
2511 cmap.push_str(&format!(
2512 "<{:04X}> <{:04X}> <{:04X}>\n",
2513 start_cid, mappings[end_idx].0, start_unicode
2514 ));
2515 cmap.push_str("endbfrange\n");
2516 i = end_idx + 1;
2517 } else {
2518 let mut chars = Vec::new();
2520 let chunk_end = (i + 100).min(mappings.len());
2521
2522 for item in &mappings[i..chunk_end] {
2523 chars.push(*item);
2524 }
2525
2526 if !chars.is_empty() {
2527 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
2528 for (cid, unicode) in chars {
2529 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
2530 }
2531 cmap.push_str("endbfchar\n");
2532 }
2533
2534 i = chunk_end;
2535 }
2536 }
2537
2538 cmap.push_str("endcmap\n");
2540 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
2541 cmap.push_str("end\n");
2542 cmap.push_str("end\n");
2543
2544 cmap.into_bytes()
2545 }
2546
2547 #[allow(dead_code)]
2549 fn write_truetype_font(
2550 &mut self,
2551 font_name: &str,
2552 font: &crate::text::font_manager::CustomFont,
2553 ) -> Result<ObjectId> {
2554 let font_id = self.allocate_object_id();
2556 let descriptor_id = self.allocate_object_id();
2557 let font_file_id = self.allocate_object_id();
2558
2559 if let Some(ref data) = font.font_data {
2561 let mut font_file_dict = Dictionary::new();
2562 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2563 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2564 self.write_object(font_file_id, font_stream_obj)?;
2565 }
2566
2567 let mut descriptor = Dictionary::new();
2569 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2570 descriptor.set("FontName", Object::Name(font_name.to_string()));
2571 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2573 "FontBBox",
2574 Object::Array(vec![
2575 Object::Integer(-1000),
2576 Object::Integer(-1000),
2577 Object::Integer(2000),
2578 Object::Integer(2000),
2579 ]),
2580 );
2581 descriptor.set("ItalicAngle", Object::Integer(0));
2582 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2583 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2584 descriptor.set(
2585 "CapHeight",
2586 Object::Integer(font.descriptor.cap_height as i64),
2587 );
2588 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2589 descriptor.set("FontFile2", Object::Reference(font_file_id));
2590 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2591
2592 let mut font_dict = Dictionary::new();
2594 font_dict.set("Type", Object::Name("Font".to_string()));
2595 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2596 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2597 font_dict.set("FirstChar", Object::Integer(0));
2598 font_dict.set("LastChar", Object::Integer(255));
2599
2600 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2602 font_dict.set("Widths", Object::Array(widths));
2603 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2604
2605 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2607
2608 self.write_object(font_id, Object::Dictionary(font_dict))?;
2609
2610 Ok(font_id)
2611 }
2612
2613 fn write_pages(
2614 &mut self,
2615 document: &Document,
2616 font_refs: &HashMap<String, ObjectId>,
2617 ) -> Result<()> {
2618 let pages_id = self.get_pages_id()?;
2619 let mut pages_dict = Dictionary::new();
2620 pages_dict.set("Type", Object::Name("Pages".to_string()));
2621 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2622
2623 let mut kids = Vec::new();
2624
2625 let mut page_ids = Vec::new();
2627 let mut content_ids = Vec::new();
2628 for _ in 0..document.pages.len() {
2629 page_ids.push(self.allocate_object_id());
2630 content_ids.push(self.allocate_object_id());
2631 }
2632
2633 for page_id in &page_ids {
2634 kids.push(Object::Reference(*page_id));
2635 }
2636
2637 pages_dict.set("Kids", Object::Array(kids));
2638
2639 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2640
2641 self.page_ids = page_ids.clone();
2643
2644 let struct_parent_keys = document
2647 .struct_tree
2648 .as_ref()
2649 .map(Self::struct_parent_keys)
2650 .unwrap_or_default();
2651
2652 for (i, page) in document.pages.iter().enumerate() {
2654 let page_id = page_ids[i];
2655 let content_id = content_ids[i];
2656
2657 let preserved_font_map = Self::preserved_font_disambiguation_map(page, font_refs);
2662 self.write_page_with_fonts(
2663 page_id,
2664 pages_id,
2665 content_id,
2666 page,
2667 struct_parent_keys.get(&i).copied(),
2668 font_refs,
2669 &preserved_font_map,
2670 )?;
2671 self.write_page_content(content_id, page, &preserved_font_map)?;
2672 }
2673
2674 Ok(())
2675 }
2676
2677 #[allow(dead_code)]
2679 fn write_pages_with_fonts(
2680 &mut self,
2681 document: &Document,
2682 font_refs: &HashMap<String, ObjectId>,
2683 ) -> Result<()> {
2684 self.write_pages(document, font_refs)
2685 }
2686
2687 fn write_page_with_fonts(
2688 &mut self,
2689 page_id: ObjectId,
2690 parent_id: ObjectId,
2691 content_id: ObjectId,
2692 page: &crate::page::Page,
2693 struct_parent_key: Option<i64>,
2694 font_refs: &HashMap<String, ObjectId>,
2695 preserved_font_map: &HashMap<String, String>,
2696 ) -> Result<()> {
2697 let mut page_dict = page.to_dict();
2699
2700 page_dict.set("Type", Object::Name("Page".to_string()));
2701 page_dict.set("Parent", Object::Reference(parent_id));
2702 page_dict.set("Contents", Object::Reference(content_id));
2703
2704 if let Some(key) = struct_parent_key {
2705 page_dict.set("StructParents", Object::Integer(key));
2706 }
2707
2708 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2710 res.clone()
2711 } else {
2712 Dictionary::new()
2713 };
2714
2715 let mut font_dict = Dictionary::new();
2717
2718 let mut helvetica_dict = Dictionary::new();
2723 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2724 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2725 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2726 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2727 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2728
2729 let mut helvetica_bold_dict = Dictionary::new();
2730 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2731 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2732 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2733 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2734 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2735
2736 let mut helvetica_oblique_dict = Dictionary::new();
2737 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2738 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2739 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2740 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2741 font_dict.set(
2742 "Helvetica-Oblique",
2743 Object::Dictionary(helvetica_oblique_dict),
2744 );
2745
2746 let mut helvetica_bold_oblique_dict = Dictionary::new();
2747 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2748 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2749 helvetica_bold_oblique_dict.set(
2750 "BaseFont",
2751 Object::Name("Helvetica-BoldOblique".to_string()),
2752 );
2753 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2754 font_dict.set(
2755 "Helvetica-BoldOblique",
2756 Object::Dictionary(helvetica_bold_oblique_dict),
2757 );
2758
2759 let mut times_dict = Dictionary::new();
2761 times_dict.set("Type", Object::Name("Font".to_string()));
2762 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2763 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2764 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2765 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2766
2767 let mut times_bold_dict = Dictionary::new();
2768 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2769 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2770 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2771 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2772 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2773
2774 let mut times_italic_dict = Dictionary::new();
2775 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2776 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2777 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2778 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2779 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2780
2781 let mut times_bold_italic_dict = Dictionary::new();
2782 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2783 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2784 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2785 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2786 font_dict.set(
2787 "Times-BoldItalic",
2788 Object::Dictionary(times_bold_italic_dict),
2789 );
2790
2791 let mut courier_dict = Dictionary::new();
2793 courier_dict.set("Type", Object::Name("Font".to_string()));
2794 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2795 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2796 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2797 font_dict.set("Courier", Object::Dictionary(courier_dict));
2798
2799 let mut courier_bold_dict = Dictionary::new();
2800 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2801 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2802 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2803 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2804 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2805
2806 let mut courier_oblique_dict = Dictionary::new();
2807 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2808 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2809 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2810 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2811 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2812
2813 let mut courier_bold_oblique_dict = Dictionary::new();
2814 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2815 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2816 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2817 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2818 font_dict.set(
2819 "Courier-BoldOblique",
2820 Object::Dictionary(courier_bold_oblique_dict),
2821 );
2822
2823 for (font_name, font_id) in font_refs {
2825 font_dict.set(font_name, Object::Reference(*font_id));
2826 }
2827
2828 resources.set("Font", Object::Dictionary(font_dict));
2829
2830 let has_images = !page.images().is_empty();
2832 let has_forms = !page.form_xobjects().is_empty();
2833
2834 let mut form_xobject_ids: HashMap<String, ObjectId> = HashMap::new();
2841
2842 if has_images || has_forms {
2843 let mut xobject_dict = Dictionary::new();
2844
2845 let mut image_entries: Vec<(&String, &crate::graphics::Image)> =
2849 page.images().iter().collect();
2850 image_entries.sort_by_key(|(name, _)| name.as_str());
2851 for (name, image) in image_entries {
2852 let image_id = self.allocate_object_id();
2854
2855 if image.has_transparency() {
2857 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2859
2860 if let Some(smask_stream) = smask_obj {
2862 let smask_id = self.allocate_object_id();
2863 self.write_object(smask_id, smask_stream)?;
2864
2865 if let Object::Stream(ref mut dict, _) = main_obj {
2867 dict.set("SMask", Object::Reference(smask_id));
2868 }
2869 }
2870
2871 self.write_object(image_id, main_obj)?;
2873 } else {
2874 self.write_object(image_id, image.to_pdf_object())?;
2876 }
2877
2878 xobject_dict.set(name, Object::Reference(image_id));
2880 }
2881
2882 let mut form_entries: Vec<(&String, &crate::graphics::FormXObject)> =
2884 page.form_xobjects().iter().collect();
2885 form_entries.sort_by_key(|(name, _)| name.as_str());
2886 for (name, form) in form_entries {
2887 let form_id = self.allocate_object_id();
2888 let stream = form.to_stream()?;
2889 let stream_obj =
2890 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2891 self.write_object(form_id, stream_obj)?;
2892 xobject_dict.set(name, Object::Reference(form_id));
2893 form_xobject_ids.insert(name.clone(), form_id);
2896 }
2897
2898 resources.set("XObject", Object::Dictionary(xobject_dict));
2899 }
2900
2901 if let Some(extgstate_states) = page.get_extgstate_resources() {
2903 let mut extgstate_dict = Dictionary::new();
2904 let mut extgstate_entries: Vec<(&String, &crate::graphics::ExtGState)> =
2906 extgstate_states.iter().collect();
2907 extgstate_entries.sort_by_key(|(name, _)| name.as_str());
2908 for (name, state) in extgstate_entries {
2909 let mut state_dict = Dictionary::new();
2910 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2911
2912 if let Some(alpha_stroke) = state.alpha_stroke {
2914 state_dict.set("CA", Object::Real(alpha_stroke));
2915 }
2916 if let Some(alpha_fill) = state.alpha_fill {
2917 state_dict.set("ca", Object::Real(alpha_fill));
2918 }
2919
2920 if let Some(line_width) = state.line_width {
2922 state_dict.set("LW", Object::Real(line_width));
2923 }
2924 if let Some(line_cap) = state.line_cap {
2925 state_dict.set("LC", Object::Integer(line_cap as i64));
2926 }
2927 if let Some(line_join) = state.line_join {
2928 state_dict.set("LJ", Object::Integer(line_join as i64));
2929 }
2930 if let Some(dash_pattern) = &state.dash_pattern {
2931 let dash_objects: Vec<Object> = dash_pattern
2932 .array
2933 .iter()
2934 .map(|&d| Object::Real(d))
2935 .collect();
2936 state_dict.set(
2937 "D",
2938 Object::Array(vec![
2939 Object::Array(dash_objects),
2940 Object::Real(dash_pattern.phase),
2941 ]),
2942 );
2943 }
2944
2945 if let Some(ref bm) = state.blend_mode {
2949 state_dict.set("BM", Object::Name(bm.pdf_name().to_string()));
2950 }
2951
2952 if let Some(ref soft_mask) = state.soft_mask {
2970 let mut mask_dict = soft_mask.to_pdf_dictionary()?;
2971 if let Some(Object::Name(ref g_name)) = mask_dict.get("G").cloned() {
2972 let form_id = form_xobject_ids.get(g_name).ok_or_else(|| {
2973 crate::error::PdfError::InvalidStructure(format!(
2974 "SoftMask references transparency group {:?} but no matching \
2975 FormXObject is registered on the page; call \
2976 Page::add_form_xobject({:?}, ...) before saving",
2977 g_name, g_name
2978 ))
2979 })?;
2980 mask_dict.set("G", Object::Reference(*form_id));
2981 }
2982 state_dict.set("SMask", Object::Dictionary(mask_dict));
2983 }
2984
2985 extgstate_dict.set(name, Object::Dictionary(state_dict));
2986 }
2987 if !extgstate_dict.is_empty() {
2988 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2989 }
2990 }
2991
2992 if !page.color_spaces().is_empty() {
3007 let mut cs_dict = Dictionary::new();
3008 let mut entries: Vec<(&String, &crate::graphics::PageColorSpace)> =
3011 page.color_spaces().iter().collect();
3012 entries.sort_by_key(|(name, _)| name.as_str());
3013 for (name, cs) in entries {
3014 if let Some((icc_dict, icc_data)) = cs.icc_stream_parts() {
3019 let icc_id = self.allocate_object_id();
3020 self.write_object(icc_id, Object::Stream(icc_dict, icc_data))?;
3021 cs_dict.set(
3022 name,
3023 Object::Array(vec![
3024 Object::Name("ICCBased".to_string()),
3025 Object::Reference(icc_id),
3026 ]),
3027 );
3028 } else {
3029 cs_dict.set(name, cs.to_object());
3030 }
3031 }
3032 resources.set("ColorSpace", Object::Dictionary(cs_dict));
3033 }
3034
3035 if !page.patterns().is_empty() {
3036 let mut pat_dict = Dictionary::new();
3037 let mut entries: Vec<(&String, &crate::graphics::TilingPattern)> =
3038 page.patterns().iter().collect();
3039 entries.sort_by_key(|(name, _)| name.as_str());
3040 for (name, pattern) in entries {
3041 let pattern_id = self.allocate_object_id();
3042 let pattern_dict = pattern.to_pdf_dictionary()?;
3043 self.write_object(
3044 pattern_id,
3045 Object::Stream(pattern_dict, pattern.content_stream.clone()),
3046 )?;
3047 pat_dict.set(name, Object::Reference(pattern_id));
3048 }
3049 resources.set("Pattern", Object::Dictionary(pat_dict));
3050 }
3051
3052 if !page.shadings().is_empty() || !page.advanced_shadings().is_empty() {
3053 let mut sh_dict = Dictionary::new();
3054
3055 let mut entries: Vec<(&String, &crate::graphics::ShadingDefinition)> =
3057 page.shadings().iter().collect();
3058 entries.sort_by_key(|(name, _)| name.as_str());
3059 for (name, shading) in entries {
3060 let obj = Object::Dictionary(shading.to_pdf_dictionary()?);
3061 let shading_id = self.write_shading_object(obj)?;
3062 sh_dict.set(name, Object::Reference(shading_id));
3063 }
3064
3065 let mut adv: Vec<(&String, &crate::graphics::AdvancedShading)> =
3068 page.advanced_shadings().iter().collect();
3069 adv.sort_by_key(|(name, _)| name.as_str());
3070 for (name, shading) in adv {
3071 let obj = shading.to_pdf_object()?;
3072 let shading_id = self.write_shading_object(obj)?;
3073 sh_dict.set(name, Object::Reference(shading_id));
3074 }
3075
3076 resources.set("Shading", Object::Dictionary(sh_dict));
3077 }
3078
3079 if let Some(preserved_res) = page.get_preserved_resources() {
3082 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
3084
3085 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
3090 let renamed_fonts = crate::writer::apply_font_rename_map(fonts, preserved_font_map);
3091 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
3092 }
3093
3094 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
3098 let mut fonts_with_refs = crate::objects::Dictionary::new();
3099
3100 for (font_name, font_obj) in fonts.iter() {
3101 if let Object::Dictionary(font_dict) = font_obj {
3102 let updated_font = self.write_embedded_font_streams(font_dict)?;
3104 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
3105 } else {
3106 fonts_with_refs.set(font_name, font_obj.clone());
3108 }
3109 }
3110
3111 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
3113 }
3114
3115 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
3119 let mut xobjects_with_refs = crate::objects::Dictionary::new();
3120 tracing::debug!(
3121 "Externalizing {} preserved XObject entries as indirect objects",
3122 xobjects.len()
3123 );
3124
3125 for (xobj_name, xobj_obj) in xobjects.iter() {
3126 match xobj_obj {
3127 Object::Stream(dict, data) => {
3128 let dict = self.externalize_nested_streams_in_dict(dict)?;
3136 let obj_id = self.allocate_object_id();
3137 self.write_object(obj_id, Object::Stream(dict, data.clone()))?;
3138 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
3139 }
3140 Object::Dictionary(dict) => {
3141 let externalized = self.externalize_streams_in_dict(dict)?;
3148 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
3149 }
3150 _ => {
3151 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
3152 }
3153 }
3154 }
3155
3156 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
3157 }
3158
3159 for (key, value) in preserved_writer_dict.iter() {
3161 if let Some(Object::Dictionary(existing)) = resources.get(key) {
3163 if let Object::Dictionary(preserved_dict) = value {
3164 let mut merged = existing.clone();
3165 for (res_name, res_obj) in preserved_dict.iter() {
3167 if !merged.contains_key(res_name) {
3168 merged.set(res_name, res_obj.clone());
3169 }
3170 }
3171 resources.set(key, Object::Dictionary(merged));
3172 }
3173 } else {
3174 resources.set(key, value.clone());
3176 }
3177 }
3178 }
3179
3180 page_dict.set("Resources", Object::Dictionary(resources));
3181
3182 let mut annot_refs: Vec<Object> = Vec::new();
3184
3185 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
3187 for annot in annots {
3188 if let Object::Dictionary(ref annot_dict) = annot {
3189 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
3190 if subtype == "Widget" {
3191 let widget_id = self.allocate_object_id();
3192 self.write_object(widget_id, annot.clone())?;
3193 annot_refs.push(Object::Reference(widget_id));
3194
3195 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
3197 if let Some(Object::String(field_name)) = annot_dict.get("T") {
3198 self.field_widget_map
3199 .entry(field_name.clone())
3200 .or_default()
3201 .push(widget_id);
3202 self.field_id_map.insert(field_name.clone(), widget_id);
3203 self.form_field_ids.push(widget_id);
3204 }
3205 }
3206 continue;
3207 }
3208 }
3209 }
3210 annot_refs.push(annot.clone());
3211 }
3212 }
3213
3214 for annotation in page.annotations() {
3218 let annot_id = self.allocate_object_id();
3219 let mut annot_dict = annotation.to_dict();
3220
3221 if let Some(placeholder) = annotation.field_parent {
3239 if let Some(real_id) = self.form_field_placeholder_map.get(&placeholder) {
3240 annot_dict.set("Parent", Object::Reference(*real_id));
3241 }
3242 }
3243
3244 if let Some(Object::Dictionary(ap_dict)) = annot_dict.get("AP") {
3263 let mut updated_ap = crate::objects::Dictionary::new();
3264 for (state_key, state_val) in ap_dict.iter() {
3265 match state_val {
3266 Object::Stream(sd, data) => {
3267 let patched_sd = Self::rewrite_ap_stream_font_resources(sd, font_refs);
3273 let stream_id = self.allocate_object_id();
3274 self.write_object(stream_id, Object::Stream(patched_sd, data.clone()))?;
3275 updated_ap.set(state_key, Object::Reference(stream_id));
3276 }
3277 Object::Dictionary(down_dict) => {
3278 let externalized = self
3280 .externalize_streams_in_dict_with_font_refs(down_dict, font_refs)?;
3281 updated_ap.set(state_key, Object::Dictionary(externalized));
3282 }
3283 _ => {
3284 updated_ap.set(state_key, state_val.clone());
3285 }
3286 }
3287 }
3288 annot_dict.set("AP", Object::Dictionary(updated_ap));
3289 }
3290
3291 self.write_object(annot_id, Object::Dictionary(annot_dict))?;
3292 annot_refs.push(Object::Reference(annot_id));
3293
3294 if annotation.annotation_type == crate::annotations::AnnotationType::Widget {
3296 if let Some(Object::String(field_name)) = annotation.properties.get("T") {
3297 self.field_widget_map
3298 .entry(field_name.clone())
3299 .or_default()
3300 .push(annot_id);
3301 self.field_id_map.insert(field_name.clone(), annot_id);
3302 self.form_field_ids.push(annot_id);
3303 }
3304 }
3305 }
3306
3307 if !annot_refs.is_empty() {
3309 page_dict.set("Annots", Object::Array(annot_refs));
3310 } else {
3311 page_dict.remove("Annots");
3312 }
3313
3314 self.write_object(page_id, Object::Dictionary(page_dict))?;
3315 Ok(())
3316 }
3317}
3318
3319impl PdfWriter<BufWriter<std::fs::File>> {
3320 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
3321 let file = std::fs::File::create(path)?;
3322 let writer = BufWriter::new(file);
3323
3324 Ok(Self {
3325 writer,
3326 xref_positions: HashMap::new(),
3327 current_position: 0,
3328 next_object_id: 1,
3329 catalog_id: None,
3330 pages_id: None,
3331 info_id: None,
3332 field_widget_map: HashMap::new(),
3333 field_id_map: HashMap::new(),
3334 form_field_ids: Vec::new(),
3335 page_ids: Vec::new(),
3336 config: WriterConfig::default(),
3337 document_used_chars_by_font: std::collections::HashMap::new(),
3338 buffered_objects: HashMap::new(),
3339 compressed_object_map: HashMap::new(),
3340 prev_xref_offset: None,
3341 base_pdf_size: None,
3342 encrypt_obj_id: None,
3343 file_id: None,
3344 encryption_state: None,
3345 pending_encrypt_dict: None,
3346 form_field_placeholder_map: HashMap::new(),
3347 form_manager_field_refs: Vec::new(),
3348 })
3349 }
3350}
3351
3352impl<W: Write> PdfWriter<W> {
3353 fn externalize_streams_in_dict(
3372 &mut self,
3373 dict: &crate::objects::Dictionary,
3374 ) -> Result<crate::objects::Dictionary> {
3375 self.externalize_streams_in_dict_with_font_refs(dict, &HashMap::new())
3376 }
3377
3378 fn externalize_nested_streams_in_dict(
3390 &mut self,
3391 dict: &crate::objects::Dictionary,
3392 ) -> Result<crate::objects::Dictionary> {
3393 let mut result = crate::objects::Dictionary::new();
3394 for (key, value) in dict.iter() {
3395 result.set(key, self.externalize_nested_streams_value(value)?);
3396 }
3397 Ok(result)
3398 }
3399
3400 fn externalize_nested_streams_value(&mut self, value: &Object) -> Result<Object> {
3401 match value {
3402 Object::Stream(d, data) => {
3403 let d = self.externalize_nested_streams_in_dict(d)?;
3404 let obj_id = self.allocate_object_id();
3405 self.write_object(obj_id, Object::Stream(d, data.clone()))?;
3406 Ok(Object::Reference(obj_id))
3407 }
3408 Object::Dictionary(d) => Ok(Object::Dictionary(
3409 self.externalize_nested_streams_in_dict(d)?,
3410 )),
3411 Object::Array(items) => {
3412 let mut out = Vec::with_capacity(items.len());
3413 for item in items {
3414 out.push(self.externalize_nested_streams_value(item)?);
3415 }
3416 Ok(Object::Array(out))
3417 }
3418 other => Ok(other.clone()),
3419 }
3420 }
3421
3422 fn externalize_streams_in_dict_with_font_refs(
3426 &mut self,
3427 dict: &crate::objects::Dictionary,
3428 font_refs: &HashMap<String, ObjectId>,
3429 ) -> Result<crate::objects::Dictionary> {
3430 let mut result = crate::objects::Dictionary::new();
3431 for (key, value) in dict.iter() {
3432 match value {
3433 Object::Stream(d, data) => {
3434 let patched_d = Self::rewrite_ap_stream_font_resources(d, font_refs);
3435 let obj_id = self.allocate_object_id();
3436 self.write_object(obj_id, Object::Stream(patched_d, data.clone()))?;
3437 result.set(key, Object::Reference(obj_id));
3438 }
3439 _ => {
3440 result.set(key, value.clone());
3441 }
3442 }
3443 }
3444 Ok(result)
3445 }
3446
3447 fn rewrite_ap_stream_font_resources(
3463 stream_dict: &crate::objects::Dictionary,
3464 font_refs: &HashMap<String, ObjectId>,
3465 ) -> crate::objects::Dictionary {
3466 if font_refs.is_empty() {
3473 return stream_dict.clone();
3474 }
3475
3476 let mut out = stream_dict.clone();
3477
3478 let Some(Object::Dictionary(resources)) = stream_dict.get("Resources") else {
3484 return out;
3485 };
3486 let Some(Object::Dictionary(fonts)) = resources.get("Font") else {
3487 return out;
3488 };
3489
3490 let mut patched_fonts = crate::objects::Dictionary::new();
3491 let mut changed = false;
3492 for (font_name, entry) in fonts.iter() {
3493 let should_rewrite = match entry {
3497 Object::Dictionary(d) => {
3498 matches!(d.get("Subtype"), Some(Object::Name(s)) if s == "Type0")
3499 }
3500 _ => false,
3501 };
3502 if should_rewrite {
3503 if let Some(font_id) = font_refs.get(font_name.as_str()) {
3504 patched_fonts.set(font_name, Object::Reference(*font_id));
3505 changed = true;
3506 continue;
3507 }
3508 }
3509 patched_fonts.set(font_name, entry.clone());
3510 }
3511
3512 if changed {
3513 let mut patched_resources = resources.clone();
3514 patched_resources.set("Font", Object::Dictionary(patched_fonts));
3515 out.set("Resources", Object::Dictionary(patched_resources));
3516 }
3517 out
3518 }
3519
3520 fn write_embedded_font_streams(
3521 &mut self,
3522 font_dict: &crate::objects::Dictionary,
3523 ) -> Result<crate::objects::Dictionary> {
3524 let mut updated_font = font_dict.clone();
3525
3526 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
3528 if subtype == "Type0" {
3529 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
3531 let mut updated_descendants = Vec::new();
3532
3533 for descendant in descendants {
3534 match descendant {
3535 Object::Dictionary(cidfont) => {
3536 let updated_cidfont =
3538 self.write_cidfont_embedded_streams(cidfont)?;
3539 let cidfont_id = self.allocate_object_id();
3541 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
3542 updated_descendants.push(Object::Reference(cidfont_id));
3544 }
3545 Object::Reference(_) => {
3546 updated_descendants.push(descendant.clone());
3548 }
3549 _ => {
3550 updated_descendants.push(descendant.clone());
3551 }
3552 }
3553 }
3554
3555 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
3556 }
3557
3558 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
3560 let tounicode_id = self.allocate_object_id();
3561 self.write_object(
3562 tounicode_id,
3563 Object::Stream(stream_dict.clone(), stream_data.clone()),
3564 )?;
3565 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
3566 }
3567
3568 return Ok(updated_font);
3569 }
3570 }
3571
3572 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
3575 let mut updated_descriptor = descriptor.clone();
3576 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3577
3578 for key in &font_file_keys {
3580 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3581 let stream_id = self.allocate_object_id();
3583 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
3584 self.write_object(stream_id, stream_obj)?;
3585
3586 updated_descriptor.set(*key, Object::Reference(stream_id));
3588 }
3589 }
3591
3592 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
3594 }
3595
3596 Ok(updated_font)
3597 }
3598
3599 fn write_cidfont_embedded_streams(
3601 &mut self,
3602 cidfont: &crate::objects::Dictionary,
3603 ) -> Result<crate::objects::Dictionary> {
3604 let mut updated_cidfont = cidfont.clone();
3605
3606 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
3608 let mut updated_descriptor = descriptor.clone();
3609 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3610
3611 for key in &font_file_keys {
3613 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3614 let stream_id = self.allocate_object_id();
3615 self.write_object(
3616 stream_id,
3617 Object::Stream(stream_dict.clone(), stream_data.clone()),
3618 )?;
3619 updated_descriptor.set(*key, Object::Reference(stream_id));
3620 }
3621 }
3622
3623 let descriptor_id = self.allocate_object_id();
3625 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
3626
3627 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
3629 }
3630
3631 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
3633 let map_id = self.allocate_object_id();
3634 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
3635 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
3636 }
3637
3638 Ok(updated_cidfont)
3639 }
3640
3641 fn allocate_object_id(&mut self) -> ObjectId {
3642 let id = ObjectId::new(self.next_object_id, 0);
3643 self.next_object_id += 1;
3644 id
3645 }
3646
3647 fn write_shading_object(&mut self, obj: Object) -> Result<ObjectId> {
3656 match obj {
3657 Object::Dictionary(mut dict) => {
3658 if matches!(
3659 dict.get("Function"),
3660 Some(Object::Dictionary(_)) | Some(Object::Stream(_, _))
3661 ) {
3662 if let Some(func_obj) = dict.remove("Function") {
3663 let func_id = self.allocate_object_id();
3664 self.write_object(func_id, func_obj)?;
3665 dict.set("Function", Object::Reference(func_id));
3666 }
3667 }
3668 let shading_id = self.allocate_object_id();
3669 self.write_object(shading_id, Object::Dictionary(dict))?;
3670 Ok(shading_id)
3671 }
3672 stream @ Object::Stream(_, _) => {
3673 let shading_id = self.allocate_object_id();
3674 self.write_object(shading_id, stream)?;
3675 Ok(shading_id)
3676 }
3677 other => {
3678 let shading_id = self.allocate_object_id();
3679 self.write_object(shading_id, other)?;
3680 Ok(shading_id)
3681 }
3682 }
3683 }
3684
3685 fn get_catalog_id(&self) -> Result<ObjectId> {
3687 self.catalog_id.ok_or_else(|| {
3688 PdfError::InvalidOperation(
3689 "catalog_id not initialized - write_document() must be called first".to_string(),
3690 )
3691 })
3692 }
3693
3694 fn get_pages_id(&self) -> Result<ObjectId> {
3696 self.pages_id.ok_or_else(|| {
3697 PdfError::InvalidOperation(
3698 "pages_id not initialized - write_document() must be called first".to_string(),
3699 )
3700 })
3701 }
3702
3703 fn get_info_id(&self) -> Result<ObjectId> {
3705 self.info_id.ok_or_else(|| {
3706 PdfError::InvalidOperation(
3707 "info_id not initialized - write_document() must be called first".to_string(),
3708 )
3709 })
3710 }
3711
3712 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
3713 use crate::writer::ObjectStreamWriter;
3714
3715 let object = if let Some(ref enc_state) = self.encryption_state {
3717 let mut obj = object;
3718 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
3719 obj
3720 } else {
3721 object
3722 };
3723
3724 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
3726 let mut buffer = Vec::new();
3727 self.write_object_value_to_buffer(&object, &mut buffer)?;
3728 self.buffered_objects.insert(id, buffer);
3729 return Ok(());
3730 }
3731
3732 self.xref_positions.insert(id, self.current_position);
3734
3735 let header = format!("{} {} obj\n", id.number(), id.generation());
3737 self.write_bytes(header.as_bytes())?;
3738
3739 self.write_object_value(&object)?;
3740
3741 self.write_bytes(b"\nendobj\n")?;
3742 Ok(())
3743 }
3744
3745 fn write_object_value(&mut self, object: &Object) -> Result<()> {
3746 match object {
3747 Object::Null => self.write_bytes(b"null")?,
3748 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
3749 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
3750 Object::Real(f) => self.write_bytes(
3751 format!("{f:.6}")
3752 .trim_end_matches('0')
3753 .trim_end_matches('.')
3754 .as_bytes(),
3755 )?,
3756 Object::String(s) => {
3757 self.write_bytes(b"(")?;
3766 self.write_bytes(&escape_pdf_string_bytes(s.as_bytes()))?;
3767 self.write_bytes(b")")?;
3768 }
3769 Object::ByteString(bytes) => {
3770 self.write_bytes(b"<")?;
3772 for byte in bytes {
3773 self.write_bytes(format!("{byte:02X}").as_bytes())?;
3774 }
3775 self.write_bytes(b">")?;
3776 }
3777 Object::Name(n) => {
3778 self.write_bytes(b"/")?;
3779 self.write_bytes(n.as_bytes())?;
3780 }
3781 Object::Array(arr) => {
3782 self.write_bytes(b"[")?;
3783 for (i, obj) in arr.iter().enumerate() {
3784 if i > 0 {
3785 self.write_bytes(b" ")?;
3786 }
3787 self.write_object_value(obj)?;
3788 }
3789 self.write_bytes(b"]")?;
3790 }
3791 Object::Dictionary(dict) => {
3792 self.write_bytes(b"<<")?;
3800 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3801 entries.sort_by_key(|(k, _)| k.as_str());
3802 for (key, value) in entries {
3803 self.write_bytes(b"\n/")?;
3804 self.write_bytes(key.as_bytes())?;
3805 self.write_bytes(b" ")?;
3806 self.write_object_value(value)?;
3807 }
3808 self.write_bytes(b"\n>>")?;
3809 }
3810 Object::Stream(dict, data) => {
3811 let mut corrected_dict = dict.clone();
3814 corrected_dict.set("Length", Object::Integer(data.len() as i64));
3815
3816 self.write_object_value(&Object::Dictionary(corrected_dict))?;
3817 self.write_bytes(b"\nstream\n")?;
3818 self.write_bytes(data)?;
3819 self.write_bytes(b"\nendstream")?;
3820 }
3821 Object::Reference(id) => {
3822 let ref_str = format!("{} {} R", id.number(), id.generation());
3823 self.write_bytes(ref_str.as_bytes())?;
3824 }
3825 }
3826 Ok(())
3827 }
3828
3829 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
3831 match object {
3832 Object::Null => buffer.extend_from_slice(b"null"),
3833 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
3834 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
3835 Object::Real(f) => buffer.extend_from_slice(
3836 format!("{f:.6}")
3837 .trim_end_matches('0')
3838 .trim_end_matches('.')
3839 .as_bytes(),
3840 ),
3841 Object::String(s) => {
3842 buffer.push(b'(');
3845 buffer.extend_from_slice(&escape_pdf_string_bytes(s.as_bytes()));
3846 buffer.push(b')');
3847 }
3848 Object::ByteString(bytes) => {
3849 buffer.push(b'<');
3850 for byte in bytes {
3851 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
3852 }
3853 buffer.push(b'>');
3854 }
3855 Object::Name(n) => {
3856 buffer.push(b'/');
3857 buffer.extend_from_slice(n.as_bytes());
3858 }
3859 Object::Array(arr) => {
3860 buffer.push(b'[');
3861 for (i, obj) in arr.iter().enumerate() {
3862 if i > 0 {
3863 buffer.push(b' ');
3864 }
3865 self.write_object_value_to_buffer(obj, buffer)?;
3866 }
3867 buffer.push(b']');
3868 }
3869 Object::Dictionary(dict) => {
3870 buffer.extend_from_slice(b"<<");
3874 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3875 entries.sort_by_key(|(k, _)| k.as_str());
3876 for (key, value) in entries {
3877 buffer.extend_from_slice(b"\n/");
3878 buffer.extend_from_slice(key.as_bytes());
3879 buffer.push(b' ');
3880 self.write_object_value_to_buffer(value, buffer)?;
3881 }
3882 buffer.extend_from_slice(b"\n>>");
3883 }
3884 Object::Stream(_, _) => {
3885 return Err(crate::error::PdfError::ObjectStreamError(
3887 "Cannot compress stream objects in object streams".to_string(),
3888 ));
3889 }
3890 Object::Reference(id) => {
3891 let ref_str = format!("{} {} R", id.number(), id.generation());
3892 buffer.extend_from_slice(ref_str.as_bytes());
3893 }
3894 }
3895 Ok(())
3896 }
3897
3898 fn flush_object_streams(&mut self) -> Result<()> {
3900 if self.buffered_objects.is_empty() {
3901 return Ok(());
3902 }
3903
3904 let config = ObjectStreamConfig {
3906 max_objects_per_stream: 100,
3907 compression_level: 6,
3908 enabled: true,
3909 };
3910 let mut os_writer = ObjectStreamWriter::new(config);
3911
3912 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
3914 buffered.sort_by_key(|(id, _)| id.number());
3915
3916 for (id, data) in buffered {
3918 os_writer.add_object(*id, data.clone())?;
3919 }
3920
3921 let streams = os_writer.finalize()?;
3923
3924 for mut stream in streams {
3926 let stream_id = stream.stream_id;
3927
3928 let compressed_data = stream.generate_stream_data(6)?;
3930
3931 let dict = stream.generate_dictionary(&compressed_data);
3933
3934 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
3936 self.compressed_object_map
3937 .insert(*obj_id, (stream_id, index as u32));
3938 }
3939
3940 self.xref_positions.insert(stream_id, self.current_position);
3942
3943 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
3944 self.write_bytes(header.as_bytes())?;
3945
3946 self.write_object_value(&Object::Dictionary(dict))?;
3947
3948 self.write_bytes(b"\nstream\n")?;
3949 self.write_bytes(&compressed_data)?;
3950 self.write_bytes(b"\nendstream\nendobj\n")?;
3951 }
3952
3953 Ok(())
3954 }
3955
3956 fn write_xref(&mut self) -> Result<()> {
3957 self.write_bytes(b"xref\n")?;
3958
3959 let mut entries: Vec<_> = self
3961 .xref_positions
3962 .iter()
3963 .map(|(id, pos)| (*id, *pos))
3964 .collect();
3965 entries.sort_by_key(|(id, _)| id.number());
3966
3967 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
3969
3970 self.write_bytes(b"0 ")?;
3973 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
3974 self.write_bytes(b"\n")?;
3975
3976 self.write_bytes(b"0000000000 65535 f \n")?;
3978
3979 for obj_num in 1..=max_obj_num {
3982 let _obj_id = ObjectId::new(obj_num, 0);
3983 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
3984 let entry = format!("{:010} {:05} n \n", position, 0);
3985 self.write_bytes(entry.as_bytes())?;
3986 } else {
3987 self.write_bytes(b"0000000000 00000 f \n")?;
3989 }
3990 }
3991
3992 Ok(())
3993 }
3994
3995 fn write_xref_stream(&mut self) -> Result<()> {
3996 let catalog_id = self.get_catalog_id()?;
3997 let info_id = self.get_info_id()?;
3998
3999 let xref_stream_id = self.allocate_object_id();
4001 let xref_position = self.current_position;
4002
4003 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
4005 xref_writer.set_trailer_info(catalog_id, info_id);
4006
4007 xref_writer.add_free_entry(0, 65535);
4009
4010 let mut entries: Vec<_> = self
4012 .xref_positions
4013 .iter()
4014 .map(|(id, pos)| (*id, *pos))
4015 .collect();
4016 entries.sort_by_key(|(id, _)| id.number());
4017
4018 let max_obj_num = entries
4020 .iter()
4021 .map(|(id, _)| id.number())
4022 .max()
4023 .unwrap_or(0)
4024 .max(xref_stream_id.number());
4025
4026 for obj_num in 1..=max_obj_num {
4028 let obj_id = ObjectId::new(obj_num, 0);
4029
4030 if obj_num == xref_stream_id.number() {
4031 xref_writer.add_in_use_entry(xref_position, 0);
4033 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
4034 xref_writer.add_compressed_entry(stream_id.number(), *index);
4036 } else if let Some((id, position)) =
4037 entries.iter().find(|(id, _)| id.number() == obj_num)
4038 {
4039 xref_writer.add_in_use_entry(*position, id.generation());
4041 } else {
4042 xref_writer.add_free_entry(0, 0);
4044 }
4045 }
4046
4047 self.xref_positions.insert(xref_stream_id, xref_position);
4049
4050 self.write_bytes(
4052 format!(
4053 "{} {} obj\n",
4054 xref_stream_id.number(),
4055 xref_stream_id.generation()
4056 )
4057 .as_bytes(),
4058 )?;
4059
4060 let uncompressed_data = xref_writer.encode_entries();
4062 let final_data = if self.config.compress_streams {
4063 crate::compression::compress(&uncompressed_data)?
4064 } else {
4065 uncompressed_data
4066 };
4067
4068 let mut dict = xref_writer.create_dictionary(None);
4070 dict.set("Length", Object::Integer(final_data.len() as i64));
4071
4072 if self.config.compress_streams {
4074 dict.set("Filter", Object::Name("FlateDecode".to_string()));
4075 }
4076 self.write_bytes(b"<<")?;
4077 for (key, value) in dict.iter() {
4078 self.write_bytes(b"\n/")?;
4079 self.write_bytes(key.as_bytes())?;
4080 self.write_bytes(b" ")?;
4081 self.write_object_value(value)?;
4082 }
4083 self.write_bytes(b"\n>>\n")?;
4084
4085 self.write_bytes(b"stream\n")?;
4087 self.write_bytes(&final_data)?;
4088 self.write_bytes(b"\nendstream\n")?;
4089 self.write_bytes(b"endobj\n")?;
4090
4091 self.write_bytes(b"\nstartxref\n")?;
4093 self.write_bytes(xref_position.to_string().as_bytes())?;
4094 self.write_bytes(b"\n%%EOF\n")?;
4095
4096 Ok(())
4097 }
4098
4099 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
4106 use crate::encryption::{
4107 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
4108 };
4109 use std::sync::Arc;
4110
4111 let mut fid = vec![0u8; 16];
4113 use rand::Rng;
4114 rand::rng().fill_bytes(&mut fid);
4115
4116 let enc_dict = encryption
4117 .create_encryption_dict(Some(&fid))
4118 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
4119
4120 let enc_key = encryption
4122 .get_encryption_key(&enc_dict, Some(&fid))
4123 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
4124
4125 let handler = encryption.handler();
4127 let (method, key_len) = match encryption.strength {
4128 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
4129 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
4130 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
4131 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
4132 };
4133
4134 let std_filter = FunctionalCryptFilter {
4135 name: "StdCF".to_string(),
4136 method,
4137 length: key_len,
4138 auth_event: crate::encryption::AuthEvent::DocOpen,
4139 recipients: None,
4140 };
4141
4142 let mut filter_manager =
4143 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
4144 filter_manager.add_filter(std_filter);
4145
4146 let encryptor =
4147 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
4148
4149 let encrypt_id = self.allocate_object_id();
4151 self.encrypt_obj_id = Some(encrypt_id);
4152 self.file_id = Some(fid);
4153 self.encryption_state = Some(WriterEncryptionState { encryptor });
4154
4155 self.pending_encrypt_dict = Some(enc_dict.to_dict());
4157
4158 Ok(())
4159 }
4160
4161 fn write_encryption_dict(&mut self) -> Result<()> {
4163 if let (Some(encrypt_id), Some(dict)) =
4164 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
4165 {
4166 let enc_state = self.encryption_state.take();
4168 self.write_object(encrypt_id, Object::Dictionary(dict))?;
4169 self.encryption_state = enc_state;
4170 }
4171 Ok(())
4172 }
4173
4174 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
4175 let catalog_id = self.get_catalog_id()?;
4176 let info_id = self.get_info_id()?;
4177 let max_obj_num = self
4179 .xref_positions
4180 .keys()
4181 .map(|id| id.number())
4182 .max()
4183 .unwrap_or(0);
4184
4185 let mut trailer = Dictionary::new();
4186 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
4187 trailer.set("Root", Object::Reference(catalog_id));
4188 trailer.set("Info", Object::Reference(info_id));
4189
4190 if let Some(prev_xref) = self.prev_xref_offset {
4192 trailer.set("Prev", Object::Integer(prev_xref as i64));
4193 }
4194
4195 if let Some(encrypt_id) = self.encrypt_obj_id {
4197 trailer.set("Encrypt", Object::Reference(encrypt_id));
4198 }
4199 if let Some(ref fid) = self.file_id {
4200 trailer.set(
4201 "ID",
4202 Object::Array(vec![
4203 Object::ByteString(fid.clone()),
4204 Object::ByteString(fid.clone()),
4205 ]),
4206 );
4207 }
4208
4209 self.write_bytes(b"trailer\n")?;
4210 self.write_object_value(&Object::Dictionary(trailer))?;
4211 self.write_bytes(b"\nstartxref\n")?;
4212 self.write_bytes(xref_position.to_string().as_bytes())?;
4213 self.write_bytes(b"\n%%EOF\n")?;
4214
4215 Ok(())
4216 }
4217
4218 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
4219 self.writer.write_all(data)?;
4220 self.current_position += data.len() as u64;
4221 Ok(())
4222 }
4223
4224 #[allow(dead_code)]
4225 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
4226 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
4228 if rect_array.len() >= 4 {
4229 if let (
4230 Some(Object::Real(x1)),
4231 Some(Object::Real(y1)),
4232 Some(Object::Real(x2)),
4233 Some(Object::Real(y2)),
4234 ) = (
4235 rect_array.first(),
4236 rect_array.get(1),
4237 rect_array.get(2),
4238 rect_array.get(3),
4239 ) {
4240 (*x1, *y1, *x2, *y2)
4241 } else {
4242 (0.0, 0.0, 100.0, 20.0) }
4244 } else {
4245 (0.0, 0.0, 100.0, 20.0) }
4247 } else {
4248 (0.0, 0.0, 100.0, 20.0) };
4250
4251 let width = rect.2 - rect.0;
4252 let height = rect.3 - rect.1;
4253
4254 let mut content = String::new();
4256
4257 content.push_str("q\n");
4259
4260 crate::graphics::color::write_stroke_color(&mut content, crate::graphics::Color::black());
4262 content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
4266 content.push_str("S\n"); crate::graphics::color::write_fill_color(&mut content, crate::graphics::Color::white());
4270 content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
4271 content.push_str("f\n"); content.push_str("Q\n");
4275
4276 let mut stream_dict = Dictionary::new();
4278 stream_dict.set("Type", Object::Name("XObject".to_string()));
4279 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4280 stream_dict.set(
4281 "BBox",
4282 Object::Array(vec![
4283 Object::Real(0.0),
4284 Object::Real(0.0),
4285 Object::Real(width),
4286 Object::Real(height),
4287 ]),
4288 );
4289 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4290 stream_dict.set("Length", Object::Integer(content.len() as i64));
4291
4292 let stream_id = self.allocate_object_id();
4294 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4295
4296 Ok(stream_id)
4297 }
4298
4299 #[allow(dead_code)]
4300 fn create_field_appearance_stream(
4301 &mut self,
4302 field_dict: &Dictionary,
4303 widget: &crate::forms::Widget,
4304 ) -> Result<ObjectId> {
4305 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
4306 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
4307
4308 let mut content = String::new();
4310
4311 content.push_str("q\n");
4313
4314 if let Some(bg_color) = &widget.appearance.background_color {
4317 crate::graphics::color::write_fill_color(&mut content, *bg_color);
4318 content.push_str(&format!("0 0 {width} {height} re\n"));
4319 content.push_str("f\n");
4320 }
4321
4322 if let Some(border_color) = &widget.appearance.border_color {
4324 crate::graphics::color::write_stroke_color(&mut content, *border_color);
4325 content.push_str(&format!("{} w\n", widget.appearance.border_width));
4326 content.push_str(&format!("0 0 {width} {height} re\n"));
4327 content.push_str("S\n");
4328 }
4329
4330 if let Some(Object::Name(ft)) = field_dict.get("FT") {
4332 if ft == "Btn" {
4333 if let Some(Object::Name(v)) = field_dict.get("V") {
4334 if v == "Yes" {
4335 crate::graphics::color::write_stroke_color(
4337 &mut content,
4338 crate::graphics::Color::black(),
4339 );
4340 content.push_str("2 w\n");
4341 let margin = width * 0.2;
4342 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
4343 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
4344 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
4345 content.push_str("S\n");
4346 }
4347 }
4348 }
4349 }
4350
4351 content.push_str("Q\n");
4353
4354 let mut stream_dict = Dictionary::new();
4356 stream_dict.set("Type", Object::Name("XObject".to_string()));
4357 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4358 stream_dict.set(
4359 "BBox",
4360 Object::Array(vec![
4361 Object::Real(0.0),
4362 Object::Real(0.0),
4363 Object::Real(width),
4364 Object::Real(height),
4365 ]),
4366 );
4367 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4368 stream_dict.set("Length", Object::Integer(content.len() as i64));
4369
4370 let stream_id = self.allocate_object_id();
4372 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4373
4374 Ok(stream_id)
4375 }
4376}
4377
4378fn format_pdf_date(date: DateTime<Utc>) -> String {
4380 let formatted = date.format("D:%Y%m%d%H%M%S");
4383
4384 format!("{formatted}+00'00")
4386}
4387
4388#[cfg(test)]
4389mod tests;
4390
4391#[cfg(test)]
4392mod rigorous_tests;