1use crate::document::Document;
2use crate::error::{PdfError, Result};
3use crate::objects::{Dictionary, Object, ObjectId};
4use crate::text::fonts::embedding::CjkFontType;
5use crate::text::fonts::truetype::CmapSubtable;
6use crate::writer::{ObjectStreamConfig, ObjectStreamWriter, XRefStreamWriter};
7use chrono::{DateTime, Utc};
8use std::collections::HashMap;
9use std::io::{BufWriter, Write};
10use std::path::Path;
11
12#[derive(Debug, Clone)]
14pub struct WriterConfig {
15 pub use_xref_streams: bool,
17 pub use_object_streams: bool,
19 pub pdf_version: String,
21 pub compress_streams: bool,
23 pub incremental_update: bool,
25}
26
27impl Default for WriterConfig {
28 fn default() -> Self {
29 Self {
30 use_xref_streams: false,
31 use_object_streams: false,
32 pdf_version: "1.7".to_string(),
33 compress_streams: true,
34 incremental_update: false,
35 }
36 }
37}
38
39impl WriterConfig {
40 pub fn modern() -> Self {
42 Self {
43 use_xref_streams: true,
44 use_object_streams: true,
45 pdf_version: "1.5".to_string(),
46 compress_streams: true,
47 incremental_update: false,
48 }
49 }
50
51 pub fn legacy() -> Self {
53 Self {
54 use_xref_streams: false,
55 use_object_streams: false,
56 pdf_version: "1.4".to_string(),
57 compress_streams: true,
58 incremental_update: false,
59 }
60 }
61
62 pub fn incremental() -> Self {
64 Self {
65 use_xref_streams: false,
66 use_object_streams: false,
67 pdf_version: "1.4".to_string(),
68 compress_streams: true,
69 incremental_update: true,
70 }
71 }
72}
73
74fn escape_pdf_string_bytes(input: &[u8]) -> Vec<u8> {
99 let mut out = Vec::with_capacity(input.len());
100 for &byte in input {
101 match byte {
102 b'\\' => out.extend_from_slice(b"\\\\"),
103 b'(' => out.extend_from_slice(b"\\("),
104 b')' => out.extend_from_slice(b"\\)"),
105 other => out.push(other),
106 }
107 }
108 out
109}
110
111pub struct PdfWriter<W: Write> {
112 writer: W,
113 xref_positions: HashMap<ObjectId, u64>,
114 current_position: u64,
115 next_object_id: u32,
116 catalog_id: Option<ObjectId>,
118 pages_id: Option<ObjectId>,
119 info_id: Option<ObjectId>,
120 #[allow(dead_code)]
122 field_widget_map: HashMap<String, Vec<ObjectId>>, #[allow(dead_code)]
124 field_id_map: HashMap<String, ObjectId>, form_field_ids: Vec<ObjectId>, page_ids: Vec<ObjectId>, config: WriterConfig,
129 document_used_chars_by_font: std::collections::HashMap<String, std::collections::HashSet<char>>,
135 buffered_objects: HashMap<ObjectId, Vec<u8>>,
137 compressed_object_map: HashMap<ObjectId, (ObjectId, u32)>, prev_xref_offset: Option<u64>,
140 base_pdf_size: Option<u64>,
141 encrypt_obj_id: Option<ObjectId>,
143 file_id: Option<Vec<u8>>,
144 encryption_state: Option<WriterEncryptionState>,
145 pending_encrypt_dict: Option<Dictionary>,
146 form_field_placeholder_map: HashMap<crate::objects::ObjectReference, ObjectId>,
159 form_manager_field_refs: Vec<crate::objects::ObjectReference>,
160}
161
162struct WriterEncryptionState {
164 encryptor: crate::encryption::ObjectEncryptor,
165}
166
167impl<W: Write> PdfWriter<W> {
168 pub fn new_with_writer(writer: W) -> Self {
169 Self::with_config(writer, WriterConfig::default())
170 }
171
172 pub fn with_config(writer: W, config: WriterConfig) -> Self {
173 Self {
174 writer,
175 xref_positions: HashMap::new(),
176 current_position: 0,
177 next_object_id: 1, catalog_id: None,
179 pages_id: None,
180 info_id: None,
181 field_widget_map: HashMap::new(),
182 field_id_map: HashMap::new(),
183 form_field_ids: Vec::new(),
184 page_ids: Vec::new(),
185 config,
186 document_used_chars_by_font: std::collections::HashMap::new(),
187 buffered_objects: HashMap::new(),
188 compressed_object_map: HashMap::new(),
189 prev_xref_offset: None,
190 base_pdf_size: None,
191 encrypt_obj_id: None,
192 file_id: None,
193 encryption_state: None,
194 pending_encrypt_dict: None,
195 form_field_placeholder_map: HashMap::new(),
196 form_manager_field_refs: Vec::new(),
197 }
198 }
199
200 pub fn write_document(&mut self, document: &mut Document) -> Result<()> {
201 if !document.used_characters_by_font.is_empty() {
203 self.document_used_chars_by_font = document.used_characters_by_font.clone();
204 }
205
206 self.write_header()?;
207
208 self.catalog_id = Some(self.allocate_object_id());
210 self.pages_id = Some(self.allocate_object_id());
211 self.info_id = Some(self.allocate_object_id());
212
213 if let Some(ref encryption) = document.encryption {
216 self.init_encryption(encryption)?;
217 }
218
219 let font_refs = self.write_fonts(document)?;
221
222 self.preallocate_form_manager_fields(document)?;
229
230 self.write_pages(document, &font_refs)?;
232
233 self.write_form_fields(document)?;
235
236 self.write_catalog(document)?;
238
239 self.write_info(document)?;
241
242 self.write_encryption_dict()?;
244
245 if self.config.use_object_streams {
247 self.flush_object_streams()?;
248 }
249
250 let xref_position = self.current_position;
252 if self.config.use_xref_streams {
253 self.write_xref_stream()?;
254 } else {
255 self.write_xref()?;
256 }
257
258 if !self.config.use_xref_streams {
260 self.write_trailer(xref_position)?;
261 }
262
263 if let Ok(()) = self.writer.flush() {
264 }
266 Ok(())
267 }
268
269 pub fn write_incremental_update(
303 &mut self,
304 base_pdf_path: impl AsRef<std::path::Path>,
305 document: &mut Document,
306 ) -> Result<()> {
307 use std::io::{BufReader, Read, Seek, SeekFrom};
308
309 let base_pdf_file = std::fs::File::open(base_pdf_path.as_ref())?;
311 let mut pdf_reader = crate::parser::PdfReader::new(BufReader::new(base_pdf_file))?;
312
313 let base_catalog = pdf_reader.catalog()?;
315
316 let (base_pages_id, base_pages_gen) = base_catalog
318 .get("Pages")
319 .and_then(|obj| {
320 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
321 Some((*id, *gen))
322 } else {
323 None
324 }
325 })
326 .ok_or_else(|| {
327 crate::error::PdfError::InvalidStructure(
328 "Base PDF catalog missing /Pages reference".to_string(),
329 )
330 })?;
331
332 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
334 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
335 base_pages_obj
336 {
337 dict.get("Kids")
338 .and_then(|obj| {
339 if let crate::parser::objects::PdfObject::Array(arr) = obj {
340 Some(
343 arr.0
344 .iter()
345 .filter_map(|item| {
346 if let crate::parser::objects::PdfObject::Reference(id, gen) =
347 item
348 {
349 Some(crate::objects::Object::Reference(
350 crate::objects::ObjectId::new(*id, *gen),
351 ))
352 } else {
353 None
354 }
355 })
356 .collect::<Vec<_>>(),
357 )
358 } else {
359 None
360 }
361 })
362 .unwrap_or_default()
363 } else {
364 Vec::new()
365 };
366
367 let base_page_count = base_pages_kids.len();
369
370 let base_pdf = std::fs::File::open(base_pdf_path.as_ref())?;
372 let mut base_reader = BufReader::new(base_pdf);
373
374 base_reader.seek(SeekFrom::End(-100))?;
376 let mut end_buffer = vec![0u8; 100];
377 let bytes_read = base_reader.read(&mut end_buffer)?;
378 end_buffer.truncate(bytes_read);
379
380 let end_str = String::from_utf8_lossy(&end_buffer);
381 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
382 let after_startxref = &end_str[startxref_pos + 9..];
383
384 let number_str: String = after_startxref
385 .chars()
386 .skip_while(|c| c.is_whitespace())
387 .take_while(|c| c.is_ascii_digit())
388 .collect();
389
390 number_str.parse::<u64>().map_err(|_| {
391 crate::error::PdfError::InvalidStructure(
392 "Could not parse startxref offset".to_string(),
393 )
394 })?
395 } else {
396 return Err(crate::error::PdfError::InvalidStructure(
397 "startxref not found in base PDF".to_string(),
398 ));
399 };
400
401 base_reader.seek(SeekFrom::Start(0))?;
403 let base_size = std::io::copy(&mut base_reader, &mut self.writer)? as u64;
404
405 self.prev_xref_offset = Some(prev_xref);
407 self.base_pdf_size = Some(base_size);
408 self.current_position = base_size;
409
410 if !document.used_characters_by_font.is_empty() {
412 self.document_used_chars_by_font = document.used_characters_by_font.clone();
413 }
414
415 self.catalog_id = Some(self.allocate_object_id());
417 self.pages_id = Some(self.allocate_object_id());
418 self.info_id = Some(self.allocate_object_id());
419
420 let font_refs = self.write_fonts(document)?;
422
423 self.write_pages(document, &font_refs)?;
425
426 self.write_form_fields(document)?;
428
429 let catalog_id = self.get_catalog_id()?;
431 let new_pages_id = self.get_pages_id()?;
432
433 let mut catalog = crate::objects::Dictionary::new();
434 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
435 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
436
437 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
442
443 let mut all_pages_kids = base_pages_kids;
445
446 for page_id in &self.page_ids {
448 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
449 }
450
451 let mut pages_dict = crate::objects::Dictionary::new();
452 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
453 pages_dict.set("Kids", crate::objects::Object::Array(all_pages_kids));
454 pages_dict.set(
455 "Count",
456 crate::objects::Object::Integer((base_page_count + self.page_ids.len()) as i64),
457 );
458
459 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
460
461 self.write_info(document)?;
463
464 let xref_position = self.current_position;
466 self.write_xref()?;
467
468 self.write_trailer(xref_position)?;
470
471 self.writer.flush()?;
472 Ok(())
473 }
474
475 pub fn write_incremental_with_page_replacement(
541 &mut self,
542 base_pdf_path: impl AsRef<std::path::Path>,
543 document: &mut Document,
544 ) -> Result<()> {
545 use std::io::Cursor;
546
547 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
549 let base_size = base_pdf_bytes.len() as u64;
550
551 let mut pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
553
554 let base_catalog = pdf_reader.catalog()?;
555
556 let (base_pages_id, base_pages_gen) = base_catalog
557 .get("Pages")
558 .and_then(|obj| {
559 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
560 Some((*id, *gen))
561 } else {
562 None
563 }
564 })
565 .ok_or_else(|| {
566 crate::error::PdfError::InvalidStructure(
567 "Base PDF catalog missing /Pages reference".to_string(),
568 )
569 })?;
570
571 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
572 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
573 base_pages_obj
574 {
575 dict.get("Kids")
576 .and_then(|obj| {
577 if let crate::parser::objects::PdfObject::Array(arr) = obj {
578 Some(
579 arr.0
580 .iter()
581 .filter_map(|item| {
582 if let crate::parser::objects::PdfObject::Reference(id, gen) =
583 item
584 {
585 Some(crate::objects::Object::Reference(
586 crate::objects::ObjectId::new(*id, *gen),
587 ))
588 } else {
589 None
590 }
591 })
592 .collect::<Vec<_>>(),
593 )
594 } else {
595 None
596 }
597 })
598 .unwrap_or_default()
599 } else {
600 Vec::new()
601 };
602
603 let base_page_count = base_pages_kids.len();
604
605 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
607 let end_bytes = &base_pdf_bytes[start_search..];
608 let end_str = String::from_utf8_lossy(end_bytes);
609
610 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
611 let after_startxref = &end_str[startxref_pos + 9..];
612 let number_str: String = after_startxref
613 .chars()
614 .skip_while(|c| c.is_whitespace())
615 .take_while(|c| c.is_ascii_digit())
616 .collect();
617
618 number_str.parse::<u64>().map_err(|_| {
619 crate::error::PdfError::InvalidStructure(
620 "Could not parse startxref offset".to_string(),
621 )
622 })?
623 } else {
624 return Err(crate::error::PdfError::InvalidStructure(
625 "startxref not found in base PDF".to_string(),
626 ));
627 };
628
629 self.writer.write_all(&base_pdf_bytes)?;
631
632 self.prev_xref_offset = Some(prev_xref);
633 self.base_pdf_size = Some(base_size);
634 self.current_position = base_size;
635
636 if !document.used_characters_by_font.is_empty() {
638 self.document_used_chars_by_font = document.used_characters_by_font.clone();
639 }
640
641 self.catalog_id = Some(self.allocate_object_id());
642 self.pages_id = Some(self.allocate_object_id());
643 self.info_id = Some(self.allocate_object_id());
644
645 let font_refs = self.write_fonts(document)?;
646 self.write_pages(document, &font_refs)?;
647 self.write_form_fields(document)?;
648
649 let catalog_id = self.get_catalog_id()?;
651 let new_pages_id = self.get_pages_id()?;
652
653 let mut catalog = crate::objects::Dictionary::new();
654 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
655 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
656 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
657
658 let mut all_pages_kids = Vec::new();
660 let replacement_count = document.pages.len();
661
662 for page_id in &self.page_ids {
664 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
665 }
666
667 if replacement_count < base_page_count {
669 for i in replacement_count..base_page_count {
670 if let Some(page_ref) = base_pages_kids.get(i) {
671 all_pages_kids.push(page_ref.clone());
672 }
673 }
674 }
675
676 let mut pages_dict = crate::objects::Dictionary::new();
677 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
678 pages_dict.set(
679 "Kids",
680 crate::objects::Object::Array(all_pages_kids.clone()),
681 );
682 pages_dict.set(
683 "Count",
684 crate::objects::Object::Integer(all_pages_kids.len() as i64),
685 );
686
687 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
688 self.write_info(document)?;
689
690 let xref_position = self.current_position;
691 self.write_xref()?;
692 self.write_trailer(xref_position)?;
693
694 self.writer.flush()?;
695 Ok(())
696 }
697
698 pub fn write_incremental_with_overlay<P: AsRef<std::path::Path>>(
746 &mut self,
747 base_pdf_path: P,
748 mut overlay_fn: impl FnMut(&mut crate::Page) -> Result<()>,
749 ) -> Result<()> {
750 use std::io::Cursor;
751
752 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
754 let base_size = base_pdf_bytes.len() as u64;
755
756 let pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
758 let parsed_doc = crate::parser::PdfDocument::new(pdf_reader);
759
760 let page_count = parsed_doc.page_count()?;
762
763 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
765 let end_bytes = &base_pdf_bytes[start_search..];
766 let end_str = String::from_utf8_lossy(end_bytes);
767
768 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
769 let after_startxref = &end_str[startxref_pos + 9..];
770 let number_str: String = after_startxref
771 .chars()
772 .skip_while(|c| c.is_whitespace())
773 .take_while(|c| c.is_ascii_digit())
774 .collect();
775
776 number_str.parse::<u64>().map_err(|_| {
777 crate::error::PdfError::InvalidStructure(
778 "Could not parse startxref offset".to_string(),
779 )
780 })?
781 } else {
782 return Err(crate::error::PdfError::InvalidStructure(
783 "startxref not found in base PDF".to_string(),
784 ));
785 };
786
787 self.writer.write_all(&base_pdf_bytes)?;
789
790 self.prev_xref_offset = Some(prev_xref);
791 self.base_pdf_size = Some(base_size);
792 self.current_position = base_size;
793
794 let mut temp_doc = crate::Document::new();
796
797 for page_idx in 0..page_count {
798 let parsed_page = parsed_doc.get_page(page_idx)?;
800 let mut writable_page =
801 crate::Page::from_parsed_with_content(&parsed_page, &parsed_doc)?;
802
803 overlay_fn(&mut writable_page)?;
805
806 temp_doc.add_page(writable_page);
808 }
809
810 if !temp_doc.used_characters_by_font.is_empty() {
813 self.document_used_chars_by_font = temp_doc.used_characters_by_font.clone();
814 }
815
816 self.catalog_id = Some(self.allocate_object_id());
817 self.pages_id = Some(self.allocate_object_id());
818 self.info_id = Some(self.allocate_object_id());
819
820 let font_refs = self.write_fonts(&temp_doc)?;
821 self.write_pages(&temp_doc, &font_refs)?;
822 self.write_form_fields(&mut temp_doc)?;
823
824 let catalog_id = self.get_catalog_id()?;
826 let new_pages_id = self.get_pages_id()?;
827
828 let mut catalog = crate::objects::Dictionary::new();
829 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
830 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
831 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
832
833 let mut all_pages_kids = Vec::new();
835 for page_id in &self.page_ids {
836 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
837 }
838
839 let mut pages_dict = crate::objects::Dictionary::new();
840 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
841 pages_dict.set(
842 "Kids",
843 crate::objects::Object::Array(all_pages_kids.clone()),
844 );
845 pages_dict.set(
846 "Count",
847 crate::objects::Object::Integer(all_pages_kids.len() as i64),
848 );
849
850 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
851 self.write_info(&temp_doc)?;
852
853 let xref_position = self.current_position;
854 self.write_xref()?;
855 self.write_trailer(xref_position)?;
856
857 self.writer.flush()?;
858 Ok(())
859 }
860
861 fn write_header(&mut self) -> Result<()> {
862 let header = format!("%PDF-{}\n", self.config.pdf_version);
863 self.write_bytes(header.as_bytes())?;
864 self.write_bytes(&[b'%', 0xE2, 0xE3, 0xCF, 0xD3, b'\n'])?;
866 Ok(())
867 }
868
869 fn convert_pdf_objects_dict_to_writer(
872 &self,
873 pdf_dict: &crate::pdf_objects::Dictionary,
874 ) -> crate::objects::Dictionary {
875 let mut writer_dict = crate::objects::Dictionary::new();
876
877 for (key, value) in pdf_dict.iter() {
878 let writer_obj = self.convert_pdf_object_to_writer(value);
879 writer_dict.set(key.as_str(), writer_obj);
880 }
881
882 writer_dict
883 }
884
885 fn convert_pdf_object_to_writer(
886 &self,
887 obj: &crate::pdf_objects::Object,
888 ) -> crate::objects::Object {
889 use crate::objects::Object as WriterObj;
890 use crate::pdf_objects::Object as PdfObj;
891
892 match obj {
893 PdfObj::Null => WriterObj::Null,
894 PdfObj::Boolean(b) => WriterObj::Boolean(*b),
895 PdfObj::Integer(i) => WriterObj::Integer(*i),
896 PdfObj::Real(f) => WriterObj::Real(*f),
897 PdfObj::String(s) => {
898 WriterObj::String(String::from_utf8_lossy(s.as_bytes()).to_string())
899 }
900 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
901 PdfObj::Array(arr) => {
902 let items: Vec<WriterObj> = arr
903 .iter()
904 .map(|item| self.convert_pdf_object_to_writer(item))
905 .collect();
906 WriterObj::Array(items)
907 }
908 PdfObj::Dictionary(dict) => {
909 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
910 }
911 PdfObj::Stream(stream) => {
912 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
913 WriterObj::Stream(dict, stream.data.clone())
914 }
915 PdfObj::Reference(id) => {
916 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
917 }
918 }
919 }
920
921 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
922 let catalog_id = self.get_catalog_id()?;
923 let pages_id = self.get_pages_id()?;
924
925 let mut catalog = Dictionary::new();
926 catalog.set("Type", Object::Name("Catalog".to_string()));
927 catalog.set("Pages", Object::Reference(pages_id));
928
929 if let Some(form_manager) = &document.form_manager {
949 if document.acro_form.is_none() {
950 document.acro_form = Some(crate::forms::AcroForm::new());
951 }
952
953 let mut sorted: Vec<(Dictionary, crate::objects::ObjectReference)> = Vec::new();
959 for (name, form_field, placeholder) in form_manager.iter_fields_sorted() {
960 let real_id = *self.form_field_placeholder_map.get(&placeholder).ok_or_else(
961 || {
962 PdfError::Internal(format!(
963 "AcroForm writer internal invariant broken: field '{name}' (placeholder {placeholder}) has no pre-allocated real object id — preallocate_form_manager_fields must run before write_catalog"
964 ))
965 },
966 )?;
967 sorted.push((form_field.field_dict.clone(), real_id));
968 }
969 for (field_dict, real_id) in sorted {
970 self.write_object(real_id, Object::Dictionary(field_dict))?;
971 }
972
973 if let Some(acro) = document.acro_form.as_mut() {
974 for r in &self.form_manager_field_refs {
975 if !acro.fields.contains(r) {
976 acro.fields.push(*r);
977 }
978 }
979 }
980 }
981
982 if let Some(acro_form) = &document.acro_form {
984 let acro_form_id = self.allocate_object_id();
986
987 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
989
990 catalog.set("AcroForm", Object::Reference(acro_form_id));
992 }
993
994 if let Some(outline_tree) = &document.outline {
996 if !outline_tree.items.is_empty() {
997 let outline_root_id = self.write_outline_tree(outline_tree)?;
998 catalog.set("Outlines", Object::Reference(outline_root_id));
999 }
1000 }
1001
1002 if let Some(struct_tree) = &document.struct_tree {
1004 if !struct_tree.is_empty() {
1005 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
1006 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
1007 catalog.set("MarkInfo", {
1009 let mut mark_info = Dictionary::new();
1010 mark_info.set("Marked", Object::Boolean(true));
1011 Object::Dictionary(mark_info)
1012 });
1013 }
1014 }
1015
1016 let xmp_metadata = document.create_xmp_metadata();
1019 let xmp_packet = xmp_metadata.to_xmp_packet();
1020 let metadata_id = self.allocate_object_id();
1021
1022 let mut metadata_dict = Dictionary::new();
1024 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
1025 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
1026 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
1027
1028 self.write_object(
1030 metadata_id,
1031 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
1032 )?;
1033
1034 catalog.set("Metadata", Object::Reference(metadata_id));
1036
1037 if let Some(action) = &document.open_action {
1039 catalog.set("OpenAction", Object::Dictionary(action.to_dict()));
1040 }
1041
1042 if let Some(prefs) = &document.viewer_preferences {
1044 catalog.set("ViewerPreferences", Object::Dictionary(prefs.to_dict()));
1045 }
1046
1047 if let Some(named_dests) = &document.named_destinations {
1052 let dests_tree_id = self.allocate_object_id();
1053 self.write_object(dests_tree_id, Object::Dictionary(named_dests.to_dict()))?;
1054
1055 let mut names_dict = Dictionary::new();
1056 names_dict.set("Dests", Object::Reference(dests_tree_id));
1057 let names_dict_id = self.allocate_object_id();
1058 self.write_object(names_dict_id, Object::Dictionary(names_dict))?;
1059
1060 catalog.set("Names", Object::Reference(names_dict_id));
1061 }
1062
1063 if let Some(page_labels) = &document.page_labels {
1067 let labels_id = self.allocate_object_id();
1068 self.write_object(labels_id, Object::Dictionary(page_labels.to_dict()))?;
1069 catalog.set("PageLabels", Object::Reference(labels_id));
1070 }
1071
1072 self.write_object(catalog_id, Object::Dictionary(catalog))?;
1073 Ok(())
1074 }
1075
1076 fn write_page_content(&mut self, content_id: ObjectId, page: &crate::page::Page) -> Result<()> {
1077 let mut page_copy = page.clone();
1078 let content = page_copy.generate_content()?;
1079
1080 #[cfg(feature = "compression")]
1082 {
1083 use crate::objects::Stream;
1084 let mut stream = Stream::new(content);
1085 if self.config.compress_streams {
1087 stream.compress_flate()?;
1088 }
1089
1090 self.write_object(
1091 content_id,
1092 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
1093 )?;
1094 }
1095
1096 #[cfg(not(feature = "compression"))]
1097 {
1098 let mut stream_dict = Dictionary::new();
1099 stream_dict.set("Length", Object::Integer(content.len() as i64));
1100
1101 self.write_object(content_id, Object::Stream(stream_dict, content))?;
1102 }
1103
1104 Ok(())
1105 }
1106
1107 fn write_outline_tree(
1108 &mut self,
1109 outline_tree: &crate::structure::OutlineTree,
1110 ) -> Result<ObjectId> {
1111 let outline_root_id = self.allocate_object_id();
1113
1114 let mut outline_root = Dictionary::new();
1115 outline_root.set("Type", Object::Name("Outlines".to_string()));
1116
1117 if !outline_tree.items.is_empty() {
1118 let mut item_ids = Vec::new();
1120
1121 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
1123 let mut count = items.len();
1124 for item in items {
1125 count += count_items(&item.children);
1126 }
1127 count
1128 }
1129
1130 let total_items = count_items(&outline_tree.items);
1131
1132 for _ in 0..total_items {
1134 item_ids.push(self.allocate_object_id());
1135 }
1136
1137 let mut id_index = 0;
1138
1139 let first_id = item_ids[0];
1141 let last_id = item_ids[outline_tree.items.len() - 1];
1142
1143 outline_root.set("First", Object::Reference(first_id));
1144 outline_root.set("Last", Object::Reference(last_id));
1145
1146 let visible_count = outline_tree.visible_count();
1148 outline_root.set("Count", Object::Integer(visible_count));
1149
1150 let mut written_items = Vec::new();
1152
1153 for (i, item) in outline_tree.items.iter().enumerate() {
1154 let item_id = item_ids[id_index];
1155 id_index += 1;
1156
1157 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1158 let next_id = if i < outline_tree.items.len() - 1 {
1159 Some(item_ids[i + 1])
1160 } else {
1161 None
1162 };
1163
1164 let children_ids = self.write_outline_item(
1166 item,
1167 item_id,
1168 outline_root_id,
1169 prev_id,
1170 next_id,
1171 &mut item_ids,
1172 &mut id_index,
1173 )?;
1174
1175 written_items.extend(children_ids);
1176 }
1177 }
1178
1179 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1180 Ok(outline_root_id)
1181 }
1182
1183 #[allow(clippy::too_many_arguments)]
1184 fn write_outline_item(
1185 &mut self,
1186 item: &crate::structure::OutlineItem,
1187 item_id: ObjectId,
1188 parent_id: ObjectId,
1189 prev_id: Option<ObjectId>,
1190 next_id: Option<ObjectId>,
1191 all_ids: &mut Vec<ObjectId>,
1192 id_index: &mut usize,
1193 ) -> Result<Vec<ObjectId>> {
1194 let mut written_ids = vec![item_id];
1195
1196 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1198 let first_idx = *id_index;
1199 let first_id = all_ids[first_idx];
1200 let last_idx = first_idx + item.children.len() - 1;
1201 let last_id = all_ids[last_idx];
1202
1203 for (i, child) in item.children.iter().enumerate() {
1205 let child_id = all_ids[*id_index];
1206 *id_index += 1;
1207
1208 let child_prev = if i > 0 {
1209 Some(all_ids[first_idx + i - 1])
1210 } else {
1211 None
1212 };
1213 let child_next = if i < item.children.len() - 1 {
1214 Some(all_ids[first_idx + i + 1])
1215 } else {
1216 None
1217 };
1218
1219 let child_ids = self.write_outline_item(
1220 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1222 )?;
1223
1224 written_ids.extend(child_ids);
1225 }
1226
1227 (Some(first_id), Some(last_id))
1228 } else {
1229 (None, None)
1230 };
1231
1232 let item_dict = crate::structure::outline_item_to_dict(
1234 item,
1235 parent_id,
1236 first_child_id,
1237 last_child_id,
1238 prev_id,
1239 next_id,
1240 );
1241
1242 self.write_object(item_id, Object::Dictionary(item_dict))?;
1243
1244 Ok(written_ids)
1245 }
1246
1247 fn write_struct_tree(
1249 &mut self,
1250 struct_tree: &crate::structure::StructTree,
1251 ) -> Result<ObjectId> {
1252 let struct_tree_root_id = self.allocate_object_id();
1254 let mut element_ids = Vec::new();
1255 for _ in 0..struct_tree.len() {
1256 element_ids.push(self.allocate_object_id());
1257 }
1258
1259 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1261 std::collections::HashMap::new();
1262
1263 if let Some(root_index) = struct_tree.root_index() {
1265 parent_map.insert(root_index, struct_tree_root_id);
1266
1267 fn map_children_parents(
1269 tree: &crate::structure::StructTree,
1270 parent_index: usize,
1271 parent_id: ObjectId,
1272 element_ids: &[ObjectId],
1273 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1274 ) {
1275 if let Some(parent_elem) = tree.get(parent_index) {
1276 for &child_index in &parent_elem.children {
1277 parent_map.insert(child_index, parent_id);
1278 map_children_parents(
1279 tree,
1280 child_index,
1281 element_ids[child_index],
1282 element_ids,
1283 parent_map,
1284 );
1285 }
1286 }
1287 }
1288
1289 map_children_parents(
1290 struct_tree,
1291 root_index,
1292 element_ids[root_index],
1293 &element_ids,
1294 &mut parent_map,
1295 );
1296 }
1297
1298 for (index, element) in struct_tree.iter().enumerate() {
1300 let element_id = element_ids[index];
1301 let mut element_dict = Dictionary::new();
1302
1303 element_dict.set("Type", Object::Name("StructElem".to_string()));
1304 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1305
1306 if let Some(&parent_id) = parent_map.get(&index) {
1308 element_dict.set("P", Object::Reference(parent_id));
1309 }
1310
1311 if let Some(ref id) = element.id {
1313 element_dict.set("ID", Object::String(id.clone()));
1314 }
1315
1316 if let Some(ref lang) = element.attributes.lang {
1318 element_dict.set("Lang", Object::String(lang.clone()));
1319 }
1320 if let Some(ref alt) = element.attributes.alt {
1321 element_dict.set("Alt", Object::String(alt.clone()));
1322 }
1323 if let Some(ref actual_text) = element.attributes.actual_text {
1324 element_dict.set("ActualText", Object::String(actual_text.clone()));
1325 }
1326 if let Some(ref title) = element.attributes.title {
1327 element_dict.set("T", Object::String(title.clone()));
1328 }
1329 if let Some(bbox) = element.attributes.bbox {
1330 element_dict.set(
1331 "BBox",
1332 Object::Array(vec![
1333 Object::Real(bbox[0]),
1334 Object::Real(bbox[1]),
1335 Object::Real(bbox[2]),
1336 Object::Real(bbox[3]),
1337 ]),
1338 );
1339 }
1340
1341 let mut kids = Vec::new();
1343
1344 for &child_index in &element.children {
1346 kids.push(Object::Reference(element_ids[child_index]));
1347 }
1348
1349 for mcid_ref in &element.mcids {
1351 let mut mcr = Dictionary::new();
1352 mcr.set("Type", Object::Name("MCR".to_string()));
1353 mcr.set("Pg", Object::Integer(mcid_ref.page_index as i64));
1354 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1355 kids.push(Object::Dictionary(mcr));
1356 }
1357
1358 if !kids.is_empty() {
1359 element_dict.set("K", Object::Array(kids));
1360 }
1361
1362 self.write_object(element_id, Object::Dictionary(element_dict))?;
1363 }
1364
1365 let mut struct_tree_root = Dictionary::new();
1367 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1368
1369 if let Some(root_index) = struct_tree.root_index() {
1371 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1372 }
1373
1374 if !struct_tree.role_map.mappings().is_empty() {
1376 let mut role_map = Dictionary::new();
1377 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1378 role_map.set(
1379 custom_type.as_str(),
1380 Object::Name(standard_type.as_pdf_name().to_string()),
1381 );
1382 }
1383 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1384 }
1385
1386 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1387 Ok(struct_tree_root_id)
1388 }
1389
1390 fn preallocate_form_manager_fields(&mut self, document: &Document) -> Result<()> {
1403 let Some(form_manager) = &document.form_manager else {
1404 return Ok(());
1405 };
1406
1407 for (_name, _form_field, placeholder) in form_manager.iter_fields_sorted() {
1408 let real_id = self.allocate_object_id();
1409 self.form_field_placeholder_map.insert(placeholder, real_id);
1410 self.form_manager_field_refs.push(real_id);
1411 }
1412 Ok(())
1413 }
1414
1415 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1416 if !self.form_field_ids.is_empty() {
1418 if let Some(acro_form) = &mut document.acro_form {
1419 acro_form.fields.clear();
1421 for field_id in &self.form_field_ids {
1422 acro_form.add_field(*field_id);
1423 }
1424
1425 acro_form.need_appearances = true;
1427 if acro_form.da.is_none() {
1428 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1429 }
1430 }
1431 }
1432 Ok(())
1433 }
1434
1435 fn write_info(&mut self, document: &Document) -> Result<()> {
1436 let info_id = self.get_info_id()?;
1437 let mut info_dict = Dictionary::new();
1438
1439 if let Some(ref title) = document.metadata.title {
1440 info_dict.set("Title", Object::String(title.clone()));
1441 }
1442 if let Some(ref author) = document.metadata.author {
1443 info_dict.set("Author", Object::String(author.clone()));
1444 }
1445 if let Some(ref subject) = document.metadata.subject {
1446 info_dict.set("Subject", Object::String(subject.clone()));
1447 }
1448 if let Some(ref keywords) = document.metadata.keywords {
1449 info_dict.set("Keywords", Object::String(keywords.clone()));
1450 }
1451 if let Some(ref creator) = document.metadata.creator {
1452 info_dict.set("Creator", Object::String(creator.clone()));
1453 }
1454 if let Some(ref producer) = document.metadata.producer {
1455 info_dict.set("Producer", Object::String(producer.clone()));
1456 }
1457
1458 if let Some(creation_date) = document.metadata.creation_date {
1460 let date_string = format_pdf_date(creation_date);
1461 info_dict.set("CreationDate", Object::String(date_string));
1462 }
1463
1464 if let Some(mod_date) = document.metadata.modification_date {
1466 let date_string = format_pdf_date(mod_date);
1467 info_dict.set("ModDate", Object::String(date_string));
1468 }
1469
1470 let edition = super::Edition::OpenSource;
1473
1474 let signature = super::PdfSignature::new(document, edition);
1475 signature.write_to_info_dict(&mut info_dict);
1476
1477 self.write_object(info_id, Object::Dictionary(info_dict))?;
1478 Ok(())
1479 }
1480
1481 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1482 let mut font_refs = HashMap::new();
1483
1484 for font_name in document.custom_font_names() {
1494 let has_usage = self
1495 .document_used_chars_by_font
1496 .get(&font_name)
1497 .map(|chars| !chars.is_empty())
1498 .unwrap_or(false);
1499 if !has_usage {
1500 continue;
1501 }
1502 if let Some(font) = document.get_custom_font(&font_name) {
1503 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1506 font_refs.insert(font_name.clone(), font_id);
1507 }
1508 }
1509
1510 let mut cid_fonts: Vec<(&String, &(Vec<u8>, crate::fonts::CidMapping))> =
1516 document.cid_keyed_fonts().iter().collect();
1517 cid_fonts.sort_by(|a, b| a.0.cmp(b.0));
1518 for (font_name, (data, mapping)) in cid_fonts {
1519 let font_id = self.write_cid_keyed_font(font_name, data, mapping)?;
1520 font_refs.insert(font_name.clone(), font_id);
1521 }
1522
1523 Ok(font_refs)
1524 }
1525
1526 fn write_font_with_unicode_support(
1528 &mut self,
1529 font_name: &str,
1530 font: &crate::fonts::Font,
1531 ) -> Result<ObjectId> {
1532 self.write_type0_font_from_font(font_name, font)
1535 }
1536
1537 fn write_type0_font_from_font(
1539 &mut self,
1540 font_name: &str,
1541 font: &crate::fonts::Font,
1542 ) -> Result<ObjectId> {
1543 let used_chars = self
1550 .document_used_chars_by_font
1551 .get(font_name)
1552 .cloned()
1553 .unwrap_or_else(|| {
1554 let mut chars = std::collections::HashSet::new();
1555 for ch in
1556 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1557 {
1558 chars.insert(ch);
1559 }
1560 chars
1561 });
1562
1563 let used_text: String = used_chars.iter().copied().collect();
1571 let mut missing = font.missing_glyphs(&used_text);
1572 if !missing.is_empty() {
1573 missing.sort_unstable();
1574 let list = missing
1575 .iter()
1576 .map(|c| format!("U+{:04X} {:?}", *c as u32, c))
1577 .collect::<Vec<_>>()
1578 .join(", ");
1579 tracing::warn!(
1580 "Custom font '{}' has no glyph for {} character(s): {}. \
1581 They will render as .notdef (empty boxes); the embedded font \
1582 does not contain these glyphs.",
1583 font_name,
1584 missing.len(),
1585 list
1586 );
1587 }
1588
1589 let font_id = self.allocate_object_id();
1591 let descendant_font_id = self.allocate_object_id();
1592 let descriptor_id = self.allocate_object_id();
1593 let font_file_id = self.allocate_object_id();
1594 let to_unicode_id = self.allocate_object_id();
1595
1596 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1601 if font.data.len() > 100_000 && !used_chars.is_empty() {
1602 match crate::text::fonts::truetype_subsetter::subset_font(
1603 font.data.clone(),
1604 &used_chars,
1605 ) {
1606 Ok(subset_result) => (
1607 subset_result.font_data,
1608 Some(subset_result.glyph_mapping),
1609 font.clone(),
1610 ),
1611 Err(_) => {
1612 if font.data.len() < 25_000_000 {
1613 (font.data.clone(), None, font.clone())
1614 } else {
1615 (Vec::new(), None, font.clone())
1616 }
1617 }
1618 }
1619 } else {
1620 (font.data.clone(), None, font.clone())
1621 };
1622
1623 if !font_data_to_embed.is_empty() {
1624 let mut font_file_dict = Dictionary::new();
1629 match font.format {
1630 crate::fonts::FontFormat::OpenType => {
1631 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1632 }
1633 crate::fonts::FontFormat::TrueType => {
1634 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1635 }
1636 }
1637
1638 #[cfg(feature = "compression")]
1643 {
1644 let font_stream_obj = if self.config.compress_streams {
1645 let mut stream =
1646 crate::objects::Stream::with_dictionary(font_file_dict, font_data_to_embed);
1647 stream.compress_flate()?;
1648 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1649 } else {
1650 Object::Stream(font_file_dict, font_data_to_embed)
1651 };
1652 self.write_object(font_file_id, font_stream_obj)?;
1653 }
1654 #[cfg(not(feature = "compression"))]
1655 {
1656 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1657 self.write_object(font_file_id, font_stream_obj)?;
1658 }
1659 } else {
1660 let font_file_dict = Dictionary::new();
1662 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1663 self.write_object(font_file_id, font_stream_obj)?;
1664 }
1665
1666 let mut descriptor = Dictionary::new();
1668 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1669 descriptor.set("FontName", Object::Name(font_name.to_string()));
1670 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1672 "FontBBox",
1673 Object::Array(vec![
1674 Object::Integer(font.descriptor.font_bbox[0] as i64),
1675 Object::Integer(font.descriptor.font_bbox[1] as i64),
1676 Object::Integer(font.descriptor.font_bbox[2] as i64),
1677 Object::Integer(font.descriptor.font_bbox[3] as i64),
1678 ]),
1679 );
1680 descriptor.set(
1681 "ItalicAngle",
1682 Object::Real(font.descriptor.italic_angle as f64),
1683 );
1684 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1685 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1686 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1687 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1688 let font_file_key = match font.format {
1690 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1693 descriptor.set(font_file_key, Object::Reference(font_file_id));
1694 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1695
1696 let mut cid_font = Dictionary::new();
1698 cid_font.set("Type", Object::Name("Font".to_string()));
1699 let cid_font_subtype = match font.format {
1701 crate::fonts::FontFormat::OpenType => "CIDFontType0",
1702 crate::fonts::FontFormat::TrueType => "CIDFontType2",
1703 };
1704 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1705 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1706
1707 let mut cid_system_info = Dictionary::new();
1709 let (registry, ordering, supplement) =
1710 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1711 cjk_type.cid_system_info()
1712 } else {
1713 ("Adobe", "Identity", 0)
1714 };
1715
1716 cid_system_info.set("Registry", Object::String(registry.to_string()));
1717 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1718 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1719 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1720
1721 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1722
1723 let default_width = self.calculate_default_width(font);
1725 cid_font.set("DW", Object::Integer(default_width));
1726
1727 let w_array = self.generate_width_array(
1731 &original_font_for_widths,
1732 default_width,
1733 subset_glyph_mapping.as_ref(),
1734 );
1735 cid_font.set("W", Object::Array(w_array));
1736
1737 if cid_font_subtype == "CIDFontType2" {
1741 let cid_to_gid_map =
1743 self.generate_cid_to_gid_map(font_name, font, subset_glyph_mapping.as_ref())?;
1744 if !cid_to_gid_map.is_empty() {
1745 let cid_to_gid_map_id = self.allocate_object_id();
1753 let map_dict = Dictionary::new();
1754 #[cfg(feature = "compression")]
1755 let map_stream = if self.config.compress_streams {
1756 let mut stream =
1757 crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1758 stream.compress_flate()?;
1759 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1760 } else {
1761 let mut d = map_dict;
1762 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1763 Object::Stream(d, cid_to_gid_map)
1764 };
1765 #[cfg(not(feature = "compression"))]
1766 let map_stream = {
1767 let mut d = map_dict;
1768 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1769 Object::Stream(d, cid_to_gid_map)
1770 };
1771 self.write_object(cid_to_gid_map_id, map_stream)?;
1772 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1773 } else {
1774 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1775 }
1776 }
1777 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1780
1781 let cmap_data = self.generate_tounicode_cmap_from_font(font_name, font);
1787 let cmap_dict = Dictionary::new();
1788 #[cfg(feature = "compression")]
1789 let cmap_stream = if self.config.compress_streams {
1790 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1791 stream.compress_flate()?;
1792 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1793 } else {
1794 Object::Stream(cmap_dict, cmap_data)
1795 };
1796 #[cfg(not(feature = "compression"))]
1797 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1798 self.write_object(to_unicode_id, cmap_stream)?;
1799
1800 let mut type0_font = Dictionary::new();
1802 type0_font.set("Type", Object::Name("Font".to_string()));
1803 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1804 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1805 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1806 type0_font.set(
1807 "DescendantFonts",
1808 Object::Array(vec![Object::Reference(descendant_font_id)]),
1809 );
1810 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1811
1812 self.write_object(font_id, Object::Dictionary(type0_font))?;
1813
1814 Ok(font_id)
1815 }
1816
1817 fn write_cid_keyed_font(
1827 &mut self,
1828 font_name: &str,
1829 data: &[u8],
1830 mapping: &crate::fonts::CidMapping,
1831 ) -> Result<ObjectId> {
1832 use crate::text::fonts::truetype::TrueTypeFont;
1833
1834 let font = crate::fonts::Font::from_bytes(font_name, data.to_vec())?;
1838 let tt_font = TrueTypeFont::parse(data.to_vec())?;
1839
1840 let used_gids: std::collections::HashSet<u16> =
1847 mapping.cid_to_gid.values().copied().collect();
1848 let (embed_bytes, gid_remap): (Vec<u8>, Option<std::collections::HashMap<u16, u16>>) =
1849 match crate::text::fonts::truetype_subsetter::subset_font_by_gids(
1850 data.to_vec(),
1851 &used_gids,
1852 ) {
1853 Ok(subset) => (subset.font_data, Some(subset.old_to_new)),
1854 Err(e) => {
1855 tracing::debug!("CID-keyed subsetting failed ({e:?}); embedding full font");
1856 (data.to_vec(), None)
1857 }
1858 };
1859
1860 let font_id = self.allocate_object_id();
1861 let descendant_font_id = self.allocate_object_id();
1862 let descriptor_id = self.allocate_object_id();
1863 let font_file_id = self.allocate_object_id();
1864 let to_unicode_id = self.allocate_object_id();
1865
1866 let mut font_file_dict = Dictionary::new();
1869 font_file_dict.set("Length1", Object::Integer(embed_bytes.len() as i64));
1870 #[cfg(feature = "compression")]
1871 {
1872 let font_stream_obj = if self.config.compress_streams {
1873 let mut stream =
1874 crate::objects::Stream::with_dictionary(font_file_dict, embed_bytes);
1875 stream.compress_flate()?;
1876 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1877 } else {
1878 let mut d = font_file_dict;
1879 d.set("Length", Object::Integer(embed_bytes.len() as i64));
1880 Object::Stream(d, embed_bytes)
1881 };
1882 self.write_object(font_file_id, font_stream_obj)?;
1883 }
1884 #[cfg(not(feature = "compression"))]
1885 {
1886 let mut d = font_file_dict;
1887 d.set("Length", Object::Integer(embed_bytes.len() as i64));
1888 self.write_object(font_file_id, Object::Stream(d, embed_bytes))?;
1889 }
1890
1891 let mut descriptor = Dictionary::new();
1893 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1894 descriptor.set("FontName", Object::Name(font_name.to_string()));
1895 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1897 "FontBBox",
1898 Object::Array(vec![
1899 Object::Integer(font.descriptor.font_bbox[0] as i64),
1900 Object::Integer(font.descriptor.font_bbox[1] as i64),
1901 Object::Integer(font.descriptor.font_bbox[2] as i64),
1902 Object::Integer(font.descriptor.font_bbox[3] as i64),
1903 ]),
1904 );
1905 descriptor.set(
1906 "ItalicAngle",
1907 Object::Real(font.descriptor.italic_angle as f64),
1908 );
1909 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1910 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1911 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1912 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1913 descriptor.set("FontFile2", Object::Reference(font_file_id));
1914 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1915
1916 let mut cid_font = Dictionary::new();
1919 cid_font.set("Type", Object::Name("Font".to_string()));
1920 cid_font.set("Subtype", Object::Name("CIDFontType2".to_string()));
1921 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1922 let mut cid_system_info = Dictionary::new();
1923 cid_system_info.set("Registry", Object::String("Adobe".to_string()));
1924 cid_system_info.set("Ordering", Object::String("Identity".to_string()));
1925 cid_system_info.set("Supplement", Object::Integer(0));
1926 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1927 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1928 cid_font.set("DW", Object::Integer(self.calculate_default_width(&font)));
1929
1930 let w_tuples = mapping.generate_width_array(&tt_font)?;
1934 let mut w_array: Vec<Object> = Vec::new();
1935 for (cid, _cid_last, width) in w_tuples {
1936 w_array.push(Object::Integer(cid as i64));
1937 w_array.push(Object::Array(vec![Object::Integer(width as i64)]));
1938 }
1939 cid_font.set("W", Object::Array(w_array));
1940
1941 let cid_to_gid_map = match &gid_remap {
1946 Some(remap) => {
1947 let mut remapped = mapping.clone();
1948 remapped.cid_to_gid = mapping
1949 .cid_to_gid
1950 .iter()
1951 .map(|(&cid, &old_gid)| (cid, remap.get(&old_gid).copied().unwrap_or(0)))
1952 .collect();
1953 remapped.generate_cid_to_gid_map()
1954 }
1955 None => mapping.generate_cid_to_gid_map(),
1956 };
1957 if cid_to_gid_map.is_empty() {
1958 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1959 } else {
1960 let cid_to_gid_map_id = self.allocate_object_id();
1961 let map_dict = Dictionary::new();
1962 #[cfg(feature = "compression")]
1963 let map_stream = if self.config.compress_streams {
1964 let mut stream = crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1965 stream.compress_flate()?;
1966 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1967 } else {
1968 let mut d = map_dict;
1969 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1970 Object::Stream(d, cid_to_gid_map)
1971 };
1972 #[cfg(not(feature = "compression"))]
1973 let map_stream = {
1974 let mut d = map_dict;
1975 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1976 Object::Stream(d, cid_to_gid_map)
1977 };
1978 self.write_object(cid_to_gid_map_id, map_stream)?;
1979 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1980 }
1981 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1982
1983 let cmap_data = mapping.generate_tounicode_cmap();
1985 let cmap_dict = Dictionary::new();
1986 #[cfg(feature = "compression")]
1987 let cmap_stream = if self.config.compress_streams {
1988 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1989 stream.compress_flate()?;
1990 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1991 } else {
1992 let mut d = cmap_dict;
1993 d.set("Length", Object::Integer(cmap_data.len() as i64));
1994 Object::Stream(d, cmap_data)
1995 };
1996 #[cfg(not(feature = "compression"))]
1997 let cmap_stream = {
1998 let mut d = cmap_dict;
1999 d.set("Length", Object::Integer(cmap_data.len() as i64));
2000 Object::Stream(d, cmap_data)
2001 };
2002 self.write_object(to_unicode_id, cmap_stream)?;
2003
2004 let mut type0_font = Dictionary::new();
2006 type0_font.set("Type", Object::Name("Font".to_string()));
2007 type0_font.set("Subtype", Object::Name("Type0".to_string()));
2008 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
2009 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
2010 type0_font.set(
2011 "DescendantFonts",
2012 Object::Array(vec![Object::Reference(descendant_font_id)]),
2013 );
2014 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
2015 self.write_object(font_id, Object::Dictionary(type0_font))?;
2016
2017 Ok(font_id)
2018 }
2019
2020 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
2022 use crate::text::fonts::truetype::TrueTypeFont;
2023
2024 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2026 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2027 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2028 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
2029 let common_chars =
2033 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
2034 let mut total_width = 0;
2035 let mut count = 0;
2036
2037 for ch in common_chars.chars() {
2038 let unicode = ch as u32;
2039 if let Some(&pdf_width) = widths.get(&unicode) {
2040 total_width += pdf_width as i64;
2041 count += 1;
2042 }
2043 }
2044
2045 if count > 0 {
2046 return total_width / count;
2047 }
2048 }
2049 }
2050 }
2051 }
2052
2053 500
2055 }
2056
2057 fn generate_width_array(
2059 &self,
2060 font: &crate::fonts::Font,
2061 _default_width: i64,
2062 subset_mapping: Option<&HashMap<u32, u16>>,
2063 ) -> Vec<Object> {
2064 use crate::text::fonts::truetype::TrueTypeFont;
2065
2066 let mut w_array = Vec::new();
2067
2068 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2070 let char_to_glyph = {
2074 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2076 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
2077 if let Some(subset_map) = subset_mapping {
2079 let mut filtered = HashMap::new();
2080 for unicode in subset_map.keys() {
2081 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
2083 filtered.insert(*unicode, orig_glyph);
2084 }
2085 }
2086 filtered
2087 } else {
2088 cmap.mappings.clone()
2089 }
2090 } else {
2091 HashMap::new()
2092 }
2093 } else {
2094 HashMap::new()
2095 }
2096 };
2097
2098 if !char_to_glyph.is_empty() {
2099 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
2101 let mut sorted_chars: Vec<_> = widths.iter().collect();
2106 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
2107
2108 let mut i = 0;
2109 while i < sorted_chars.len() {
2110 let start_unicode = *sorted_chars[i].0;
2111 let pdf_width = *sorted_chars[i].1 as i64;
2113
2114 let mut end_unicode = start_unicode;
2116 let mut j = i + 1;
2117 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
2118 let next_pdf_width = *sorted_chars[j].1 as i64;
2119 if next_pdf_width == pdf_width {
2120 end_unicode = *sorted_chars[j].0;
2121 j += 1;
2122 } else {
2123 break;
2124 }
2125 }
2126
2127 if start_unicode == end_unicode {
2129 w_array.push(Object::Integer(start_unicode as i64));
2131 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
2132 } else {
2133 w_array.push(Object::Integer(start_unicode as i64));
2135 w_array.push(Object::Integer(end_unicode as i64));
2136 w_array.push(Object::Integer(pdf_width));
2137 }
2138
2139 i = j;
2140 }
2141
2142 return w_array;
2143 }
2144 }
2145 }
2146
2147 let ranges = vec![
2149 (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), ];
2186
2187 for (start, end, width) in ranges {
2189 if start == end {
2190 w_array.push(Object::Integer(start));
2192 w_array.push(Object::Array(vec![Object::Integer(width)]));
2193 } else {
2194 w_array.push(Object::Integer(start));
2196 w_array.push(Object::Integer(end));
2197 w_array.push(Object::Integer(width));
2198 }
2199 }
2200
2201 w_array
2202 }
2203
2204 fn generate_cid_to_gid_map(
2206 &mut self,
2207 font_name: &str,
2208 font: &crate::fonts::Font,
2209 subset_mapping: Option<&HashMap<u32, u16>>,
2210 ) -> Result<Vec<u8>> {
2211 use crate::text::fonts::truetype::TrueTypeFont;
2212
2213 let cmap_mappings = if let Some(subset_map) = subset_mapping {
2216 subset_map.clone()
2218 } else {
2219 let tt_font = TrueTypeFont::parse(font.data.clone())?;
2221 let cmap_tables = tt_font.parse_cmap()?;
2222
2223 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
2225 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
2226 })?;
2227
2228 cmap.mappings.clone()
2229 };
2230
2231 let used_chars = self
2238 .document_used_chars_by_font
2239 .get(font_name)
2240 .cloned()
2241 .unwrap_or_default();
2242
2243 let max_unicode = if !used_chars.is_empty() {
2245 used_chars
2247 .iter()
2248 .map(|ch| *ch as u32)
2249 .max()
2250 .unwrap_or(0x00FF) .min(0xFFFF) as usize
2252 } else {
2253 cmap_mappings
2255 .keys()
2256 .max()
2257 .copied()
2258 .unwrap_or(0xFFFF)
2259 .min(0xFFFF) as usize
2260 };
2261
2262 let mut map = vec![0u8; (max_unicode + 1) * 2];
2264
2265 let mut sample_mappings = Vec::new();
2267 for (&unicode, &glyph_id) in &cmap_mappings {
2268 if unicode <= max_unicode as u32 {
2269 let idx = (unicode as usize) * 2;
2270 map[idx] = (glyph_id >> 8) as u8;
2272 map[idx + 1] = (glyph_id & 0xFF) as u8;
2273
2274 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
2276 {
2277 sample_mappings.push((unicode, glyph_id));
2278 }
2279 }
2280 }
2281
2282 Ok(map)
2283 }
2284
2285 fn generate_tounicode_cmap_from_font(
2287 &self,
2288 font_name: &str,
2289 font: &crate::fonts::Font,
2290 ) -> Vec<u8> {
2291 use crate::text::fonts::truetype::TrueTypeFont;
2292
2293 let mut cmap = String::new();
2294
2295 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
2297 cmap.push_str("12 dict begin\n");
2298 cmap.push_str("begincmap\n");
2299 cmap.push_str("/CIDSystemInfo\n");
2300 cmap.push_str("<< /Registry (Adobe)\n");
2301 cmap.push_str(" /Ordering (UCS)\n");
2302 cmap.push_str(" /Supplement 0\n");
2303 cmap.push_str(">> def\n");
2304 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
2305 cmap.push_str("/CMapType 2 def\n");
2306 cmap.push_str("1 begincodespacerange\n");
2307 cmap.push_str("<0000> <FFFF>\n");
2308 cmap.push_str("endcodespacerange\n");
2309
2310 let used_codepoints: Option<std::collections::HashSet<u32>> = self
2316 .document_used_chars_by_font
2317 .get(font_name)
2318 .map(|chars| {
2319 chars
2320 .iter()
2321 .map(|c| *c as u32)
2322 .filter(|cp| *cp <= 0xFFFF)
2323 .collect()
2324 });
2325
2326 let mut mappings: Vec<(u32, u32)> = Vec::new();
2327
2328 if let Some(used) = &used_codepoints {
2329 for cp in used {
2331 mappings.push((*cp, *cp));
2332 }
2333 } else if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2334 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2336 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
2337 for (&unicode, &glyph_id) in &cmap_table.mappings {
2338 if glyph_id > 0 && unicode <= 0xFFFF {
2339 mappings.push((unicode, unicode));
2340 }
2341 }
2342 }
2343 }
2344 }
2345
2346 mappings.sort_by_key(|&(cid, _)| cid);
2348
2349 let mut i = 0;
2351 while i < mappings.len() {
2352 let start_cid = mappings[i].0;
2354 let start_unicode = mappings[i].1;
2355 let mut end_idx = i;
2356
2357 while end_idx + 1 < mappings.len()
2359 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
2360 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
2361 && end_idx - i < 99
2362 {
2364 end_idx += 1;
2365 }
2366
2367 if end_idx > i {
2368 cmap.push_str("1 beginbfrange\n");
2370 cmap.push_str(&format!(
2371 "<{:04X}> <{:04X}> <{:04X}>\n",
2372 start_cid, mappings[end_idx].0, start_unicode
2373 ));
2374 cmap.push_str("endbfrange\n");
2375 i = end_idx + 1;
2376 } else {
2377 let mut chars = Vec::new();
2379 let chunk_end = (i + 100).min(mappings.len());
2380
2381 for item in &mappings[i..chunk_end] {
2382 chars.push(*item);
2383 }
2384
2385 if !chars.is_empty() {
2386 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
2387 for (cid, unicode) in chars {
2388 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
2389 }
2390 cmap.push_str("endbfchar\n");
2391 }
2392
2393 i = chunk_end;
2394 }
2395 }
2396
2397 cmap.push_str("endcmap\n");
2399 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
2400 cmap.push_str("end\n");
2401 cmap.push_str("end\n");
2402
2403 cmap.into_bytes()
2404 }
2405
2406 #[allow(dead_code)]
2408 fn write_truetype_font(
2409 &mut self,
2410 font_name: &str,
2411 font: &crate::text::font_manager::CustomFont,
2412 ) -> Result<ObjectId> {
2413 let font_id = self.allocate_object_id();
2415 let descriptor_id = self.allocate_object_id();
2416 let font_file_id = self.allocate_object_id();
2417
2418 if let Some(ref data) = font.font_data {
2420 let mut font_file_dict = Dictionary::new();
2421 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2422 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2423 self.write_object(font_file_id, font_stream_obj)?;
2424 }
2425
2426 let mut descriptor = Dictionary::new();
2428 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2429 descriptor.set("FontName", Object::Name(font_name.to_string()));
2430 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2432 "FontBBox",
2433 Object::Array(vec![
2434 Object::Integer(-1000),
2435 Object::Integer(-1000),
2436 Object::Integer(2000),
2437 Object::Integer(2000),
2438 ]),
2439 );
2440 descriptor.set("ItalicAngle", Object::Integer(0));
2441 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2442 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2443 descriptor.set(
2444 "CapHeight",
2445 Object::Integer(font.descriptor.cap_height as i64),
2446 );
2447 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2448 descriptor.set("FontFile2", Object::Reference(font_file_id));
2449 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2450
2451 let mut font_dict = Dictionary::new();
2453 font_dict.set("Type", Object::Name("Font".to_string()));
2454 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2455 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2456 font_dict.set("FirstChar", Object::Integer(0));
2457 font_dict.set("LastChar", Object::Integer(255));
2458
2459 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2461 font_dict.set("Widths", Object::Array(widths));
2462 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2463
2464 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2466
2467 self.write_object(font_id, Object::Dictionary(font_dict))?;
2468
2469 Ok(font_id)
2470 }
2471
2472 fn write_pages(
2473 &mut self,
2474 document: &Document,
2475 font_refs: &HashMap<String, ObjectId>,
2476 ) -> Result<()> {
2477 let pages_id = self.get_pages_id()?;
2478 let mut pages_dict = Dictionary::new();
2479 pages_dict.set("Type", Object::Name("Pages".to_string()));
2480 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2481
2482 let mut kids = Vec::new();
2483
2484 let mut page_ids = Vec::new();
2486 let mut content_ids = Vec::new();
2487 for _ in 0..document.pages.len() {
2488 page_ids.push(self.allocate_object_id());
2489 content_ids.push(self.allocate_object_id());
2490 }
2491
2492 for page_id in &page_ids {
2493 kids.push(Object::Reference(*page_id));
2494 }
2495
2496 pages_dict.set("Kids", Object::Array(kids));
2497
2498 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2499
2500 self.page_ids = page_ids.clone();
2502
2503 for (i, page) in document.pages.iter().enumerate() {
2505 let page_id = page_ids[i];
2506 let content_id = content_ids[i];
2507
2508 self.write_page_with_fonts(page_id, pages_id, content_id, page, document, font_refs)?;
2509 self.write_page_content(content_id, page)?;
2510 }
2511
2512 Ok(())
2513 }
2514
2515 #[allow(dead_code)]
2517 fn write_pages_with_fonts(
2518 &mut self,
2519 document: &Document,
2520 font_refs: &HashMap<String, ObjectId>,
2521 ) -> Result<()> {
2522 self.write_pages(document, font_refs)
2523 }
2524
2525 fn write_page_with_fonts(
2526 &mut self,
2527 page_id: ObjectId,
2528 parent_id: ObjectId,
2529 content_id: ObjectId,
2530 page: &crate::page::Page,
2531 _document: &Document,
2532 font_refs: &HashMap<String, ObjectId>,
2533 ) -> Result<()> {
2534 let mut page_dict = page.to_dict();
2536
2537 page_dict.set("Type", Object::Name("Page".to_string()));
2538 page_dict.set("Parent", Object::Reference(parent_id));
2539 page_dict.set("Contents", Object::Reference(content_id));
2540
2541 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2543 res.clone()
2544 } else {
2545 Dictionary::new()
2546 };
2547
2548 let mut font_dict = Dictionary::new();
2550
2551 let mut helvetica_dict = Dictionary::new();
2556 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2557 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2558 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2559 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2560 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2561
2562 let mut helvetica_bold_dict = Dictionary::new();
2563 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2564 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2565 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2566 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2567 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2568
2569 let mut helvetica_oblique_dict = Dictionary::new();
2570 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2571 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2572 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2573 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2574 font_dict.set(
2575 "Helvetica-Oblique",
2576 Object::Dictionary(helvetica_oblique_dict),
2577 );
2578
2579 let mut helvetica_bold_oblique_dict = Dictionary::new();
2580 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2581 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2582 helvetica_bold_oblique_dict.set(
2583 "BaseFont",
2584 Object::Name("Helvetica-BoldOblique".to_string()),
2585 );
2586 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2587 font_dict.set(
2588 "Helvetica-BoldOblique",
2589 Object::Dictionary(helvetica_bold_oblique_dict),
2590 );
2591
2592 let mut times_dict = Dictionary::new();
2594 times_dict.set("Type", Object::Name("Font".to_string()));
2595 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2596 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2597 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2598 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2599
2600 let mut times_bold_dict = Dictionary::new();
2601 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2602 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2603 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2604 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2605 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2606
2607 let mut times_italic_dict = Dictionary::new();
2608 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2609 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2610 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2611 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2612 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2613
2614 let mut times_bold_italic_dict = Dictionary::new();
2615 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2616 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2617 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2618 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2619 font_dict.set(
2620 "Times-BoldItalic",
2621 Object::Dictionary(times_bold_italic_dict),
2622 );
2623
2624 let mut courier_dict = Dictionary::new();
2626 courier_dict.set("Type", Object::Name("Font".to_string()));
2627 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2628 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2629 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2630 font_dict.set("Courier", Object::Dictionary(courier_dict));
2631
2632 let mut courier_bold_dict = Dictionary::new();
2633 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2634 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2635 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2636 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2637 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2638
2639 let mut courier_oblique_dict = Dictionary::new();
2640 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2641 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2642 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2643 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2644 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2645
2646 let mut courier_bold_oblique_dict = Dictionary::new();
2647 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2648 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2649 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2650 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2651 font_dict.set(
2652 "Courier-BoldOblique",
2653 Object::Dictionary(courier_bold_oblique_dict),
2654 );
2655
2656 for (font_name, font_id) in font_refs {
2658 font_dict.set(font_name, Object::Reference(*font_id));
2659 }
2660
2661 resources.set("Font", Object::Dictionary(font_dict));
2662
2663 let has_images = !page.images().is_empty();
2665 let has_forms = !page.form_xobjects().is_empty();
2666
2667 let mut form_xobject_ids: HashMap<String, ObjectId> = HashMap::new();
2674
2675 if has_images || has_forms {
2676 let mut xobject_dict = Dictionary::new();
2677
2678 let mut image_entries: Vec<(&String, &crate::graphics::Image)> =
2682 page.images().iter().collect();
2683 image_entries.sort_by_key(|(name, _)| name.as_str());
2684 for (name, image) in image_entries {
2685 let image_id = self.allocate_object_id();
2687
2688 if image.has_transparency() {
2690 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2692
2693 if let Some(smask_stream) = smask_obj {
2695 let smask_id = self.allocate_object_id();
2696 self.write_object(smask_id, smask_stream)?;
2697
2698 if let Object::Stream(ref mut dict, _) = main_obj {
2700 dict.set("SMask", Object::Reference(smask_id));
2701 }
2702 }
2703
2704 self.write_object(image_id, main_obj)?;
2706 } else {
2707 self.write_object(image_id, image.to_pdf_object())?;
2709 }
2710
2711 xobject_dict.set(name, Object::Reference(image_id));
2713 }
2714
2715 let mut form_entries: Vec<(&String, &crate::graphics::FormXObject)> =
2717 page.form_xobjects().iter().collect();
2718 form_entries.sort_by_key(|(name, _)| name.as_str());
2719 for (name, form) in form_entries {
2720 let form_id = self.allocate_object_id();
2721 let stream = form.to_stream()?;
2722 let stream_obj =
2723 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2724 self.write_object(form_id, stream_obj)?;
2725 xobject_dict.set(name, Object::Reference(form_id));
2726 form_xobject_ids.insert(name.clone(), form_id);
2729 }
2730
2731 resources.set("XObject", Object::Dictionary(xobject_dict));
2732 }
2733
2734 if let Some(extgstate_states) = page.get_extgstate_resources() {
2736 let mut extgstate_dict = Dictionary::new();
2737 let mut extgstate_entries: Vec<(&String, &crate::graphics::ExtGState)> =
2739 extgstate_states.iter().collect();
2740 extgstate_entries.sort_by_key(|(name, _)| name.as_str());
2741 for (name, state) in extgstate_entries {
2742 let mut state_dict = Dictionary::new();
2743 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2744
2745 if let Some(alpha_stroke) = state.alpha_stroke {
2747 state_dict.set("CA", Object::Real(alpha_stroke));
2748 }
2749 if let Some(alpha_fill) = state.alpha_fill {
2750 state_dict.set("ca", Object::Real(alpha_fill));
2751 }
2752
2753 if let Some(line_width) = state.line_width {
2755 state_dict.set("LW", Object::Real(line_width));
2756 }
2757 if let Some(line_cap) = state.line_cap {
2758 state_dict.set("LC", Object::Integer(line_cap as i64));
2759 }
2760 if let Some(line_join) = state.line_join {
2761 state_dict.set("LJ", Object::Integer(line_join as i64));
2762 }
2763 if let Some(dash_pattern) = &state.dash_pattern {
2764 let dash_objects: Vec<Object> = dash_pattern
2765 .array
2766 .iter()
2767 .map(|&d| Object::Real(d))
2768 .collect();
2769 state_dict.set(
2770 "D",
2771 Object::Array(vec![
2772 Object::Array(dash_objects),
2773 Object::Real(dash_pattern.phase),
2774 ]),
2775 );
2776 }
2777
2778 if let Some(ref bm) = state.blend_mode {
2782 state_dict.set("BM", Object::Name(bm.pdf_name().to_string()));
2783 }
2784
2785 if let Some(ref soft_mask) = state.soft_mask {
2803 let mut mask_dict = soft_mask.to_pdf_dictionary()?;
2804 if let Some(Object::Name(ref g_name)) = mask_dict.get("G").cloned() {
2805 let form_id = form_xobject_ids.get(g_name).ok_or_else(|| {
2806 crate::error::PdfError::InvalidStructure(format!(
2807 "SoftMask references transparency group {:?} but no matching \
2808 FormXObject is registered on the page; call \
2809 Page::add_form_xobject({:?}, ...) before saving",
2810 g_name, g_name
2811 ))
2812 })?;
2813 mask_dict.set("G", Object::Reference(*form_id));
2814 }
2815 state_dict.set("SMask", Object::Dictionary(mask_dict));
2816 }
2817
2818 extgstate_dict.set(name, Object::Dictionary(state_dict));
2819 }
2820 if !extgstate_dict.is_empty() {
2821 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2822 }
2823 }
2824
2825 if !page.color_spaces().is_empty() {
2840 let mut cs_dict = Dictionary::new();
2841 let mut entries: Vec<(&String, &crate::graphics::PageColorSpace)> =
2844 page.color_spaces().iter().collect();
2845 entries.sort_by_key(|(name, _)| name.as_str());
2846 for (name, cs) in entries {
2847 if let Some((icc_dict, icc_data)) = cs.icc_stream_parts() {
2852 let icc_id = self.allocate_object_id();
2853 self.write_object(icc_id, Object::Stream(icc_dict, icc_data))?;
2854 cs_dict.set(
2855 name,
2856 Object::Array(vec![
2857 Object::Name("ICCBased".to_string()),
2858 Object::Reference(icc_id),
2859 ]),
2860 );
2861 } else {
2862 cs_dict.set(name, cs.to_object());
2863 }
2864 }
2865 resources.set("ColorSpace", Object::Dictionary(cs_dict));
2866 }
2867
2868 if !page.patterns().is_empty() {
2869 let mut pat_dict = Dictionary::new();
2870 let mut entries: Vec<(&String, &crate::graphics::TilingPattern)> =
2871 page.patterns().iter().collect();
2872 entries.sort_by_key(|(name, _)| name.as_str());
2873 for (name, pattern) in entries {
2874 let pattern_id = self.allocate_object_id();
2875 let pattern_dict = pattern.to_pdf_dictionary()?;
2876 self.write_object(
2877 pattern_id,
2878 Object::Stream(pattern_dict, pattern.content_stream.clone()),
2879 )?;
2880 pat_dict.set(name, Object::Reference(pattern_id));
2881 }
2882 resources.set("Pattern", Object::Dictionary(pat_dict));
2883 }
2884
2885 if !page.shadings().is_empty() {
2886 let mut sh_dict = Dictionary::new();
2887 let mut entries: Vec<(&String, &crate::graphics::ShadingDefinition)> =
2888 page.shadings().iter().collect();
2889 entries.sort_by_key(|(name, _)| name.as_str());
2890 for (name, shading) in entries {
2891 let mut shading_dict = shading.to_pdf_dictionary()?;
2892 if let Some(Object::Dictionary(_)) = shading_dict.get("Function") {
2897 if let Some(func_obj) = shading_dict.remove("Function") {
2898 let func_id = self.allocate_object_id();
2899 self.write_object(func_id, func_obj)?;
2900 shading_dict.set("Function", Object::Reference(func_id));
2901 }
2902 }
2903 let shading_id = self.allocate_object_id();
2904 self.write_object(shading_id, Object::Dictionary(shading_dict))?;
2905 sh_dict.set(name, Object::Reference(shading_id));
2906 }
2907 resources.set("Shading", Object::Dictionary(sh_dict));
2908 }
2909
2910 if let Some(preserved_res) = page.get_preserved_resources() {
2913 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
2915
2916 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2918 let renamed_fonts = crate::writer::rename_preserved_fonts(fonts);
2920
2921 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
2923 }
2924
2925 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2929 let mut fonts_with_refs = crate::objects::Dictionary::new();
2930
2931 for (font_name, font_obj) in fonts.iter() {
2932 if let Object::Dictionary(font_dict) = font_obj {
2933 let updated_font = self.write_embedded_font_streams(font_dict)?;
2935 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
2936 } else {
2937 fonts_with_refs.set(font_name, font_obj.clone());
2939 }
2940 }
2941
2942 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
2944 }
2945
2946 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
2950 let mut xobjects_with_refs = crate::objects::Dictionary::new();
2951 tracing::debug!(
2952 "Externalizing {} preserved XObject entries as indirect objects",
2953 xobjects.len()
2954 );
2955
2956 for (xobj_name, xobj_obj) in xobjects.iter() {
2957 match xobj_obj {
2958 Object::Stream(dict, data) => {
2959 let obj_id = self.allocate_object_id();
2960 self.write_object(obj_id, Object::Stream(dict.clone(), data.clone()))?;
2961 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
2962 }
2963 Object::Dictionary(dict) => {
2964 let externalized = self.externalize_streams_in_dict(dict)?;
2966 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
2967 }
2968 _ => {
2969 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
2970 }
2971 }
2972 }
2973
2974 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
2975 }
2976
2977 for (key, value) in preserved_writer_dict.iter() {
2979 if let Some(Object::Dictionary(existing)) = resources.get(key) {
2981 if let Object::Dictionary(preserved_dict) = value {
2982 let mut merged = existing.clone();
2983 for (res_name, res_obj) in preserved_dict.iter() {
2985 if !merged.contains_key(res_name) {
2986 merged.set(res_name, res_obj.clone());
2987 }
2988 }
2989 resources.set(key, Object::Dictionary(merged));
2990 }
2991 } else {
2992 resources.set(key, value.clone());
2994 }
2995 }
2996 }
2997
2998 page_dict.set("Resources", Object::Dictionary(resources));
2999
3000 let mut annot_refs: Vec<Object> = Vec::new();
3002
3003 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
3005 for annot in annots {
3006 if let Object::Dictionary(ref annot_dict) = annot {
3007 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
3008 if subtype == "Widget" {
3009 let widget_id = self.allocate_object_id();
3010 self.write_object(widget_id, annot.clone())?;
3011 annot_refs.push(Object::Reference(widget_id));
3012
3013 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
3015 if let Some(Object::String(field_name)) = annot_dict.get("T") {
3016 self.field_widget_map
3017 .entry(field_name.clone())
3018 .or_default()
3019 .push(widget_id);
3020 self.field_id_map.insert(field_name.clone(), widget_id);
3021 self.form_field_ids.push(widget_id);
3022 }
3023 }
3024 continue;
3025 }
3026 }
3027 }
3028 annot_refs.push(annot.clone());
3029 }
3030 }
3031
3032 for annotation in page.annotations() {
3036 let annot_id = self.allocate_object_id();
3037 let mut annot_dict = annotation.to_dict();
3038
3039 if let Some(placeholder) = annotation.field_parent {
3057 if let Some(real_id) = self.form_field_placeholder_map.get(&placeholder) {
3058 annot_dict.set("Parent", Object::Reference(*real_id));
3059 }
3060 }
3061
3062 if let Some(Object::Dictionary(ap_dict)) = annot_dict.get("AP") {
3081 let mut updated_ap = crate::objects::Dictionary::new();
3082 for (state_key, state_val) in ap_dict.iter() {
3083 match state_val {
3084 Object::Stream(sd, data) => {
3085 let patched_sd = Self::rewrite_ap_stream_font_resources(sd, font_refs);
3091 let stream_id = self.allocate_object_id();
3092 self.write_object(stream_id, Object::Stream(patched_sd, data.clone()))?;
3093 updated_ap.set(state_key, Object::Reference(stream_id));
3094 }
3095 Object::Dictionary(down_dict) => {
3096 let externalized = self
3098 .externalize_streams_in_dict_with_font_refs(down_dict, font_refs)?;
3099 updated_ap.set(state_key, Object::Dictionary(externalized));
3100 }
3101 _ => {
3102 updated_ap.set(state_key, state_val.clone());
3103 }
3104 }
3105 }
3106 annot_dict.set("AP", Object::Dictionary(updated_ap));
3107 }
3108
3109 self.write_object(annot_id, Object::Dictionary(annot_dict))?;
3110 annot_refs.push(Object::Reference(annot_id));
3111
3112 if annotation.annotation_type == crate::annotations::AnnotationType::Widget {
3114 if let Some(Object::String(field_name)) = annotation.properties.get("T") {
3115 self.field_widget_map
3116 .entry(field_name.clone())
3117 .or_default()
3118 .push(annot_id);
3119 self.field_id_map.insert(field_name.clone(), annot_id);
3120 self.form_field_ids.push(annot_id);
3121 }
3122 }
3123 }
3124
3125 if !annot_refs.is_empty() {
3127 page_dict.set("Annots", Object::Array(annot_refs));
3128 } else {
3129 page_dict.remove("Annots");
3130 }
3131
3132 self.write_object(page_id, Object::Dictionary(page_dict))?;
3133 Ok(())
3134 }
3135}
3136
3137impl PdfWriter<BufWriter<std::fs::File>> {
3138 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
3139 let file = std::fs::File::create(path)?;
3140 let writer = BufWriter::new(file);
3141
3142 Ok(Self {
3143 writer,
3144 xref_positions: HashMap::new(),
3145 current_position: 0,
3146 next_object_id: 1,
3147 catalog_id: None,
3148 pages_id: None,
3149 info_id: None,
3150 field_widget_map: HashMap::new(),
3151 field_id_map: HashMap::new(),
3152 form_field_ids: Vec::new(),
3153 page_ids: Vec::new(),
3154 config: WriterConfig::default(),
3155 document_used_chars_by_font: std::collections::HashMap::new(),
3156 buffered_objects: HashMap::new(),
3157 compressed_object_map: HashMap::new(),
3158 prev_xref_offset: None,
3159 base_pdf_size: None,
3160 encrypt_obj_id: None,
3161 file_id: None,
3162 encryption_state: None,
3163 pending_encrypt_dict: None,
3164 form_field_placeholder_map: HashMap::new(),
3165 form_manager_field_refs: Vec::new(),
3166 })
3167 }
3168}
3169
3170impl<W: Write> PdfWriter<W> {
3171 fn externalize_streams_in_dict(
3190 &mut self,
3191 dict: &crate::objects::Dictionary,
3192 ) -> Result<crate::objects::Dictionary> {
3193 self.externalize_streams_in_dict_with_font_refs(dict, &HashMap::new())
3194 }
3195
3196 fn externalize_streams_in_dict_with_font_refs(
3200 &mut self,
3201 dict: &crate::objects::Dictionary,
3202 font_refs: &HashMap<String, ObjectId>,
3203 ) -> Result<crate::objects::Dictionary> {
3204 let mut result = crate::objects::Dictionary::new();
3205 for (key, value) in dict.iter() {
3206 match value {
3207 Object::Stream(d, data) => {
3208 let patched_d = Self::rewrite_ap_stream_font_resources(d, font_refs);
3209 let obj_id = self.allocate_object_id();
3210 self.write_object(obj_id, Object::Stream(patched_d, data.clone()))?;
3211 result.set(key, Object::Reference(obj_id));
3212 }
3213 _ => {
3214 result.set(key, value.clone());
3215 }
3216 }
3217 }
3218 Ok(result)
3219 }
3220
3221 fn rewrite_ap_stream_font_resources(
3237 stream_dict: &crate::objects::Dictionary,
3238 font_refs: &HashMap<String, ObjectId>,
3239 ) -> crate::objects::Dictionary {
3240 if font_refs.is_empty() {
3247 return stream_dict.clone();
3248 }
3249
3250 let mut out = stream_dict.clone();
3251
3252 let Some(Object::Dictionary(resources)) = stream_dict.get("Resources") else {
3258 return out;
3259 };
3260 let Some(Object::Dictionary(fonts)) = resources.get("Font") else {
3261 return out;
3262 };
3263
3264 let mut patched_fonts = crate::objects::Dictionary::new();
3265 let mut changed = false;
3266 for (font_name, entry) in fonts.iter() {
3267 let should_rewrite = match entry {
3271 Object::Dictionary(d) => {
3272 matches!(d.get("Subtype"), Some(Object::Name(s)) if s == "Type0")
3273 }
3274 _ => false,
3275 };
3276 if should_rewrite {
3277 if let Some(font_id) = font_refs.get(font_name.as_str()) {
3278 patched_fonts.set(font_name, Object::Reference(*font_id));
3279 changed = true;
3280 continue;
3281 }
3282 }
3283 patched_fonts.set(font_name, entry.clone());
3284 }
3285
3286 if changed {
3287 let mut patched_resources = resources.clone();
3288 patched_resources.set("Font", Object::Dictionary(patched_fonts));
3289 out.set("Resources", Object::Dictionary(patched_resources));
3290 }
3291 out
3292 }
3293
3294 fn write_embedded_font_streams(
3295 &mut self,
3296 font_dict: &crate::objects::Dictionary,
3297 ) -> Result<crate::objects::Dictionary> {
3298 let mut updated_font = font_dict.clone();
3299
3300 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
3302 if subtype == "Type0" {
3303 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
3305 let mut updated_descendants = Vec::new();
3306
3307 for descendant in descendants {
3308 match descendant {
3309 Object::Dictionary(cidfont) => {
3310 let updated_cidfont =
3312 self.write_cidfont_embedded_streams(cidfont)?;
3313 let cidfont_id = self.allocate_object_id();
3315 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
3316 updated_descendants.push(Object::Reference(cidfont_id));
3318 }
3319 Object::Reference(_) => {
3320 updated_descendants.push(descendant.clone());
3322 }
3323 _ => {
3324 updated_descendants.push(descendant.clone());
3325 }
3326 }
3327 }
3328
3329 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
3330 }
3331
3332 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
3334 let tounicode_id = self.allocate_object_id();
3335 self.write_object(
3336 tounicode_id,
3337 Object::Stream(stream_dict.clone(), stream_data.clone()),
3338 )?;
3339 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
3340 }
3341
3342 return Ok(updated_font);
3343 }
3344 }
3345
3346 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
3349 let mut updated_descriptor = descriptor.clone();
3350 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3351
3352 for key in &font_file_keys {
3354 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3355 let stream_id = self.allocate_object_id();
3357 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
3358 self.write_object(stream_id, stream_obj)?;
3359
3360 updated_descriptor.set(*key, Object::Reference(stream_id));
3362 }
3363 }
3365
3366 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
3368 }
3369
3370 Ok(updated_font)
3371 }
3372
3373 fn write_cidfont_embedded_streams(
3375 &mut self,
3376 cidfont: &crate::objects::Dictionary,
3377 ) -> Result<crate::objects::Dictionary> {
3378 let mut updated_cidfont = cidfont.clone();
3379
3380 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
3382 let mut updated_descriptor = descriptor.clone();
3383 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3384
3385 for key in &font_file_keys {
3387 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3388 let stream_id = self.allocate_object_id();
3389 self.write_object(
3390 stream_id,
3391 Object::Stream(stream_dict.clone(), stream_data.clone()),
3392 )?;
3393 updated_descriptor.set(*key, Object::Reference(stream_id));
3394 }
3395 }
3396
3397 let descriptor_id = self.allocate_object_id();
3399 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
3400
3401 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
3403 }
3404
3405 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
3407 let map_id = self.allocate_object_id();
3408 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
3409 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
3410 }
3411
3412 Ok(updated_cidfont)
3413 }
3414
3415 fn allocate_object_id(&mut self) -> ObjectId {
3416 let id = ObjectId::new(self.next_object_id, 0);
3417 self.next_object_id += 1;
3418 id
3419 }
3420
3421 fn get_catalog_id(&self) -> Result<ObjectId> {
3423 self.catalog_id.ok_or_else(|| {
3424 PdfError::InvalidOperation(
3425 "catalog_id not initialized - write_document() must be called first".to_string(),
3426 )
3427 })
3428 }
3429
3430 fn get_pages_id(&self) -> Result<ObjectId> {
3432 self.pages_id.ok_or_else(|| {
3433 PdfError::InvalidOperation(
3434 "pages_id not initialized - write_document() must be called first".to_string(),
3435 )
3436 })
3437 }
3438
3439 fn get_info_id(&self) -> Result<ObjectId> {
3441 self.info_id.ok_or_else(|| {
3442 PdfError::InvalidOperation(
3443 "info_id not initialized - write_document() must be called first".to_string(),
3444 )
3445 })
3446 }
3447
3448 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
3449 use crate::writer::ObjectStreamWriter;
3450
3451 let object = if let Some(ref enc_state) = self.encryption_state {
3453 let mut obj = object;
3454 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
3455 obj
3456 } else {
3457 object
3458 };
3459
3460 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
3462 let mut buffer = Vec::new();
3463 self.write_object_value_to_buffer(&object, &mut buffer)?;
3464 self.buffered_objects.insert(id, buffer);
3465 return Ok(());
3466 }
3467
3468 self.xref_positions.insert(id, self.current_position);
3470
3471 let header = format!("{} {} obj\n", id.number(), id.generation());
3473 self.write_bytes(header.as_bytes())?;
3474
3475 self.write_object_value(&object)?;
3476
3477 self.write_bytes(b"\nendobj\n")?;
3478 Ok(())
3479 }
3480
3481 fn write_object_value(&mut self, object: &Object) -> Result<()> {
3482 match object {
3483 Object::Null => self.write_bytes(b"null")?,
3484 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
3485 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
3486 Object::Real(f) => self.write_bytes(
3487 format!("{f:.6}")
3488 .trim_end_matches('0')
3489 .trim_end_matches('.')
3490 .as_bytes(),
3491 )?,
3492 Object::String(s) => {
3493 self.write_bytes(b"(")?;
3502 self.write_bytes(&escape_pdf_string_bytes(s.as_bytes()))?;
3503 self.write_bytes(b")")?;
3504 }
3505 Object::ByteString(bytes) => {
3506 self.write_bytes(b"<")?;
3508 for byte in bytes {
3509 self.write_bytes(format!("{byte:02X}").as_bytes())?;
3510 }
3511 self.write_bytes(b">")?;
3512 }
3513 Object::Name(n) => {
3514 self.write_bytes(b"/")?;
3515 self.write_bytes(n.as_bytes())?;
3516 }
3517 Object::Array(arr) => {
3518 self.write_bytes(b"[")?;
3519 for (i, obj) in arr.iter().enumerate() {
3520 if i > 0 {
3521 self.write_bytes(b" ")?;
3522 }
3523 self.write_object_value(obj)?;
3524 }
3525 self.write_bytes(b"]")?;
3526 }
3527 Object::Dictionary(dict) => {
3528 self.write_bytes(b"<<")?;
3536 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3537 entries.sort_by_key(|(k, _)| k.as_str());
3538 for (key, value) in entries {
3539 self.write_bytes(b"\n/")?;
3540 self.write_bytes(key.as_bytes())?;
3541 self.write_bytes(b" ")?;
3542 self.write_object_value(value)?;
3543 }
3544 self.write_bytes(b"\n>>")?;
3545 }
3546 Object::Stream(dict, data) => {
3547 let mut corrected_dict = dict.clone();
3550 corrected_dict.set("Length", Object::Integer(data.len() as i64));
3551
3552 self.write_object_value(&Object::Dictionary(corrected_dict))?;
3553 self.write_bytes(b"\nstream\n")?;
3554 self.write_bytes(data)?;
3555 self.write_bytes(b"\nendstream")?;
3556 }
3557 Object::Reference(id) => {
3558 let ref_str = format!("{} {} R", id.number(), id.generation());
3559 self.write_bytes(ref_str.as_bytes())?;
3560 }
3561 }
3562 Ok(())
3563 }
3564
3565 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
3567 match object {
3568 Object::Null => buffer.extend_from_slice(b"null"),
3569 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
3570 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
3571 Object::Real(f) => buffer.extend_from_slice(
3572 format!("{f:.6}")
3573 .trim_end_matches('0')
3574 .trim_end_matches('.')
3575 .as_bytes(),
3576 ),
3577 Object::String(s) => {
3578 buffer.push(b'(');
3581 buffer.extend_from_slice(&escape_pdf_string_bytes(s.as_bytes()));
3582 buffer.push(b')');
3583 }
3584 Object::ByteString(bytes) => {
3585 buffer.push(b'<');
3586 for byte in bytes {
3587 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
3588 }
3589 buffer.push(b'>');
3590 }
3591 Object::Name(n) => {
3592 buffer.push(b'/');
3593 buffer.extend_from_slice(n.as_bytes());
3594 }
3595 Object::Array(arr) => {
3596 buffer.push(b'[');
3597 for (i, obj) in arr.iter().enumerate() {
3598 if i > 0 {
3599 buffer.push(b' ');
3600 }
3601 self.write_object_value_to_buffer(obj, buffer)?;
3602 }
3603 buffer.push(b']');
3604 }
3605 Object::Dictionary(dict) => {
3606 buffer.extend_from_slice(b"<<");
3610 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3611 entries.sort_by_key(|(k, _)| k.as_str());
3612 for (key, value) in entries {
3613 buffer.extend_from_slice(b"\n/");
3614 buffer.extend_from_slice(key.as_bytes());
3615 buffer.push(b' ');
3616 self.write_object_value_to_buffer(value, buffer)?;
3617 }
3618 buffer.extend_from_slice(b"\n>>");
3619 }
3620 Object::Stream(_, _) => {
3621 return Err(crate::error::PdfError::ObjectStreamError(
3623 "Cannot compress stream objects in object streams".to_string(),
3624 ));
3625 }
3626 Object::Reference(id) => {
3627 let ref_str = format!("{} {} R", id.number(), id.generation());
3628 buffer.extend_from_slice(ref_str.as_bytes());
3629 }
3630 }
3631 Ok(())
3632 }
3633
3634 fn flush_object_streams(&mut self) -> Result<()> {
3636 if self.buffered_objects.is_empty() {
3637 return Ok(());
3638 }
3639
3640 let config = ObjectStreamConfig {
3642 max_objects_per_stream: 100,
3643 compression_level: 6,
3644 enabled: true,
3645 };
3646 let mut os_writer = ObjectStreamWriter::new(config);
3647
3648 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
3650 buffered.sort_by_key(|(id, _)| id.number());
3651
3652 for (id, data) in buffered {
3654 os_writer.add_object(*id, data.clone())?;
3655 }
3656
3657 let streams = os_writer.finalize()?;
3659
3660 for mut stream in streams {
3662 let stream_id = stream.stream_id;
3663
3664 let compressed_data = stream.generate_stream_data(6)?;
3666
3667 let dict = stream.generate_dictionary(&compressed_data);
3669
3670 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
3672 self.compressed_object_map
3673 .insert(*obj_id, (stream_id, index as u32));
3674 }
3675
3676 self.xref_positions.insert(stream_id, self.current_position);
3678
3679 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
3680 self.write_bytes(header.as_bytes())?;
3681
3682 self.write_object_value(&Object::Dictionary(dict))?;
3683
3684 self.write_bytes(b"\nstream\n")?;
3685 self.write_bytes(&compressed_data)?;
3686 self.write_bytes(b"\nendstream\nendobj\n")?;
3687 }
3688
3689 Ok(())
3690 }
3691
3692 fn write_xref(&mut self) -> Result<()> {
3693 self.write_bytes(b"xref\n")?;
3694
3695 let mut entries: Vec<_> = self
3697 .xref_positions
3698 .iter()
3699 .map(|(id, pos)| (*id, *pos))
3700 .collect();
3701 entries.sort_by_key(|(id, _)| id.number());
3702
3703 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
3705
3706 self.write_bytes(b"0 ")?;
3709 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
3710 self.write_bytes(b"\n")?;
3711
3712 self.write_bytes(b"0000000000 65535 f \n")?;
3714
3715 for obj_num in 1..=max_obj_num {
3718 let _obj_id = ObjectId::new(obj_num, 0);
3719 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
3720 let entry = format!("{:010} {:05} n \n", position, 0);
3721 self.write_bytes(entry.as_bytes())?;
3722 } else {
3723 self.write_bytes(b"0000000000 00000 f \n")?;
3725 }
3726 }
3727
3728 Ok(())
3729 }
3730
3731 fn write_xref_stream(&mut self) -> Result<()> {
3732 let catalog_id = self.get_catalog_id()?;
3733 let info_id = self.get_info_id()?;
3734
3735 let xref_stream_id = self.allocate_object_id();
3737 let xref_position = self.current_position;
3738
3739 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
3741 xref_writer.set_trailer_info(catalog_id, info_id);
3742
3743 xref_writer.add_free_entry(0, 65535);
3745
3746 let mut entries: Vec<_> = self
3748 .xref_positions
3749 .iter()
3750 .map(|(id, pos)| (*id, *pos))
3751 .collect();
3752 entries.sort_by_key(|(id, _)| id.number());
3753
3754 let max_obj_num = entries
3756 .iter()
3757 .map(|(id, _)| id.number())
3758 .max()
3759 .unwrap_or(0)
3760 .max(xref_stream_id.number());
3761
3762 for obj_num in 1..=max_obj_num {
3764 let obj_id = ObjectId::new(obj_num, 0);
3765
3766 if obj_num == xref_stream_id.number() {
3767 xref_writer.add_in_use_entry(xref_position, 0);
3769 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
3770 xref_writer.add_compressed_entry(stream_id.number(), *index);
3772 } else if let Some((id, position)) =
3773 entries.iter().find(|(id, _)| id.number() == obj_num)
3774 {
3775 xref_writer.add_in_use_entry(*position, id.generation());
3777 } else {
3778 xref_writer.add_free_entry(0, 0);
3780 }
3781 }
3782
3783 self.xref_positions.insert(xref_stream_id, xref_position);
3785
3786 self.write_bytes(
3788 format!(
3789 "{} {} obj\n",
3790 xref_stream_id.number(),
3791 xref_stream_id.generation()
3792 )
3793 .as_bytes(),
3794 )?;
3795
3796 let uncompressed_data = xref_writer.encode_entries();
3798 let final_data = if self.config.compress_streams {
3799 crate::compression::compress(&uncompressed_data)?
3800 } else {
3801 uncompressed_data
3802 };
3803
3804 let mut dict = xref_writer.create_dictionary(None);
3806 dict.set("Length", Object::Integer(final_data.len() as i64));
3807
3808 if self.config.compress_streams {
3810 dict.set("Filter", Object::Name("FlateDecode".to_string()));
3811 }
3812 self.write_bytes(b"<<")?;
3813 for (key, value) in dict.iter() {
3814 self.write_bytes(b"\n/")?;
3815 self.write_bytes(key.as_bytes())?;
3816 self.write_bytes(b" ")?;
3817 self.write_object_value(value)?;
3818 }
3819 self.write_bytes(b"\n>>\n")?;
3820
3821 self.write_bytes(b"stream\n")?;
3823 self.write_bytes(&final_data)?;
3824 self.write_bytes(b"\nendstream\n")?;
3825 self.write_bytes(b"endobj\n")?;
3826
3827 self.write_bytes(b"\nstartxref\n")?;
3829 self.write_bytes(xref_position.to_string().as_bytes())?;
3830 self.write_bytes(b"\n%%EOF\n")?;
3831
3832 Ok(())
3833 }
3834
3835 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
3842 use crate::encryption::{
3843 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
3844 };
3845 use std::sync::Arc;
3846
3847 let mut fid = vec![0u8; 16];
3849 use rand::Rng;
3850 rand::rng().fill_bytes(&mut fid);
3851
3852 let enc_dict = encryption
3853 .create_encryption_dict(Some(&fid))
3854 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
3855
3856 let enc_key = encryption
3858 .get_encryption_key(&enc_dict, Some(&fid))
3859 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
3860
3861 let handler = encryption.handler();
3863 let (method, key_len) = match encryption.strength {
3864 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
3865 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
3866 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
3867 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
3868 };
3869
3870 let std_filter = FunctionalCryptFilter {
3871 name: "StdCF".to_string(),
3872 method,
3873 length: key_len,
3874 auth_event: crate::encryption::AuthEvent::DocOpen,
3875 recipients: None,
3876 };
3877
3878 let mut filter_manager =
3879 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
3880 filter_manager.add_filter(std_filter);
3881
3882 let encryptor =
3883 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
3884
3885 let encrypt_id = self.allocate_object_id();
3887 self.encrypt_obj_id = Some(encrypt_id);
3888 self.file_id = Some(fid);
3889 self.encryption_state = Some(WriterEncryptionState { encryptor });
3890
3891 self.pending_encrypt_dict = Some(enc_dict.to_dict());
3893
3894 Ok(())
3895 }
3896
3897 fn write_encryption_dict(&mut self) -> Result<()> {
3899 if let (Some(encrypt_id), Some(dict)) =
3900 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
3901 {
3902 let enc_state = self.encryption_state.take();
3904 self.write_object(encrypt_id, Object::Dictionary(dict))?;
3905 self.encryption_state = enc_state;
3906 }
3907 Ok(())
3908 }
3909
3910 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
3911 let catalog_id = self.get_catalog_id()?;
3912 let info_id = self.get_info_id()?;
3913 let max_obj_num = self
3915 .xref_positions
3916 .keys()
3917 .map(|id| id.number())
3918 .max()
3919 .unwrap_or(0);
3920
3921 let mut trailer = Dictionary::new();
3922 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
3923 trailer.set("Root", Object::Reference(catalog_id));
3924 trailer.set("Info", Object::Reference(info_id));
3925
3926 if let Some(prev_xref) = self.prev_xref_offset {
3928 trailer.set("Prev", Object::Integer(prev_xref as i64));
3929 }
3930
3931 if let Some(encrypt_id) = self.encrypt_obj_id {
3933 trailer.set("Encrypt", Object::Reference(encrypt_id));
3934 }
3935 if let Some(ref fid) = self.file_id {
3936 trailer.set(
3937 "ID",
3938 Object::Array(vec![
3939 Object::ByteString(fid.clone()),
3940 Object::ByteString(fid.clone()),
3941 ]),
3942 );
3943 }
3944
3945 self.write_bytes(b"trailer\n")?;
3946 self.write_object_value(&Object::Dictionary(trailer))?;
3947 self.write_bytes(b"\nstartxref\n")?;
3948 self.write_bytes(xref_position.to_string().as_bytes())?;
3949 self.write_bytes(b"\n%%EOF\n")?;
3950
3951 Ok(())
3952 }
3953
3954 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
3955 self.writer.write_all(data)?;
3956 self.current_position += data.len() as u64;
3957 Ok(())
3958 }
3959
3960 #[allow(dead_code)]
3961 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
3962 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
3964 if rect_array.len() >= 4 {
3965 if let (
3966 Some(Object::Real(x1)),
3967 Some(Object::Real(y1)),
3968 Some(Object::Real(x2)),
3969 Some(Object::Real(y2)),
3970 ) = (
3971 rect_array.first(),
3972 rect_array.get(1),
3973 rect_array.get(2),
3974 rect_array.get(3),
3975 ) {
3976 (*x1, *y1, *x2, *y2)
3977 } else {
3978 (0.0, 0.0, 100.0, 20.0) }
3980 } else {
3981 (0.0, 0.0, 100.0, 20.0) }
3983 } else {
3984 (0.0, 0.0, 100.0, 20.0) };
3986
3987 let width = rect.2 - rect.0;
3988 let height = rect.3 - rect.1;
3989
3990 let mut content = String::new();
3992
3993 content.push_str("q\n");
3995
3996 crate::graphics::color::write_stroke_color(&mut content, crate::graphics::Color::black());
3998 content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
4002 content.push_str("S\n"); crate::graphics::color::write_fill_color(&mut content, crate::graphics::Color::white());
4006 content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
4007 content.push_str("f\n"); content.push_str("Q\n");
4011
4012 let mut stream_dict = Dictionary::new();
4014 stream_dict.set("Type", Object::Name("XObject".to_string()));
4015 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4016 stream_dict.set(
4017 "BBox",
4018 Object::Array(vec![
4019 Object::Real(0.0),
4020 Object::Real(0.0),
4021 Object::Real(width),
4022 Object::Real(height),
4023 ]),
4024 );
4025 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4026 stream_dict.set("Length", Object::Integer(content.len() as i64));
4027
4028 let stream_id = self.allocate_object_id();
4030 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4031
4032 Ok(stream_id)
4033 }
4034
4035 #[allow(dead_code)]
4036 fn create_field_appearance_stream(
4037 &mut self,
4038 field_dict: &Dictionary,
4039 widget: &crate::forms::Widget,
4040 ) -> Result<ObjectId> {
4041 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
4042 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
4043
4044 let mut content = String::new();
4046
4047 content.push_str("q\n");
4049
4050 if let Some(bg_color) = &widget.appearance.background_color {
4053 crate::graphics::color::write_fill_color(&mut content, *bg_color);
4054 content.push_str(&format!("0 0 {width} {height} re\n"));
4055 content.push_str("f\n");
4056 }
4057
4058 if let Some(border_color) = &widget.appearance.border_color {
4060 crate::graphics::color::write_stroke_color(&mut content, *border_color);
4061 content.push_str(&format!("{} w\n", widget.appearance.border_width));
4062 content.push_str(&format!("0 0 {width} {height} re\n"));
4063 content.push_str("S\n");
4064 }
4065
4066 if let Some(Object::Name(ft)) = field_dict.get("FT") {
4068 if ft == "Btn" {
4069 if let Some(Object::Name(v)) = field_dict.get("V") {
4070 if v == "Yes" {
4071 crate::graphics::color::write_stroke_color(
4073 &mut content,
4074 crate::graphics::Color::black(),
4075 );
4076 content.push_str("2 w\n");
4077 let margin = width * 0.2;
4078 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
4079 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
4080 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
4081 content.push_str("S\n");
4082 }
4083 }
4084 }
4085 }
4086
4087 content.push_str("Q\n");
4089
4090 let mut stream_dict = Dictionary::new();
4092 stream_dict.set("Type", Object::Name("XObject".to_string()));
4093 stream_dict.set("Subtype", Object::Name("Form".to_string()));
4094 stream_dict.set(
4095 "BBox",
4096 Object::Array(vec![
4097 Object::Real(0.0),
4098 Object::Real(0.0),
4099 Object::Real(width),
4100 Object::Real(height),
4101 ]),
4102 );
4103 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
4104 stream_dict.set("Length", Object::Integer(content.len() as i64));
4105
4106 let stream_id = self.allocate_object_id();
4108 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
4109
4110 Ok(stream_id)
4111 }
4112}
4113
4114fn format_pdf_date(date: DateTime<Utc>) -> String {
4116 let formatted = date.format("D:%Y%m%d%H%M%S");
4119
4120 format!("{formatted}+00'00")
4122}
4123
4124#[cfg(test)]
4125mod tests;
4126
4127#[cfg(test)]
4128mod rigorous_tests;