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> {
88 let mut out = Vec::with_capacity(input.len());
89 for &byte in input {
90 match byte {
91 b'\\' => out.extend_from_slice(b"\\\\"),
92 b'(' => out.extend_from_slice(b"\\("),
93 b')' => out.extend_from_slice(b"\\)"),
94 other => out.push(other),
95 }
96 }
97 out
98}
99
100pub struct PdfWriter<W: Write> {
101 writer: W,
102 xref_positions: HashMap<ObjectId, u64>,
103 current_position: u64,
104 next_object_id: u32,
105 catalog_id: Option<ObjectId>,
107 pages_id: Option<ObjectId>,
108 info_id: Option<ObjectId>,
109 #[allow(dead_code)]
111 field_widget_map: HashMap<String, Vec<ObjectId>>, #[allow(dead_code)]
113 field_id_map: HashMap<String, ObjectId>, form_field_ids: Vec<ObjectId>, page_ids: Vec<ObjectId>, config: WriterConfig,
118 document_used_chars_by_font: std::collections::HashMap<String, std::collections::HashSet<char>>,
124 buffered_objects: HashMap<ObjectId, Vec<u8>>,
126 compressed_object_map: HashMap<ObjectId, (ObjectId, u32)>, prev_xref_offset: Option<u64>,
129 base_pdf_size: Option<u64>,
130 encrypt_obj_id: Option<ObjectId>,
132 file_id: Option<Vec<u8>>,
133 encryption_state: Option<WriterEncryptionState>,
134 pending_encrypt_dict: Option<Dictionary>,
135 form_field_placeholder_map: HashMap<crate::objects::ObjectReference, ObjectId>,
148 form_manager_field_refs: Vec<crate::objects::ObjectReference>,
149}
150
151struct WriterEncryptionState {
153 encryptor: crate::encryption::ObjectEncryptor,
154}
155
156impl<W: Write> PdfWriter<W> {
157 pub fn new_with_writer(writer: W) -> Self {
158 Self::with_config(writer, WriterConfig::default())
159 }
160
161 pub fn with_config(writer: W, config: WriterConfig) -> Self {
162 Self {
163 writer,
164 xref_positions: HashMap::new(),
165 current_position: 0,
166 next_object_id: 1, catalog_id: None,
168 pages_id: None,
169 info_id: None,
170 field_widget_map: HashMap::new(),
171 field_id_map: HashMap::new(),
172 form_field_ids: Vec::new(),
173 page_ids: Vec::new(),
174 config,
175 document_used_chars_by_font: std::collections::HashMap::new(),
176 buffered_objects: HashMap::new(),
177 compressed_object_map: HashMap::new(),
178 prev_xref_offset: None,
179 base_pdf_size: None,
180 encrypt_obj_id: None,
181 file_id: None,
182 encryption_state: None,
183 pending_encrypt_dict: None,
184 form_field_placeholder_map: HashMap::new(),
185 form_manager_field_refs: Vec::new(),
186 }
187 }
188
189 pub fn write_document(&mut self, document: &mut Document) -> Result<()> {
190 if !document.used_characters_by_font.is_empty() {
192 self.document_used_chars_by_font = document.used_characters_by_font.clone();
193 }
194
195 self.write_header()?;
196
197 self.catalog_id = Some(self.allocate_object_id());
199 self.pages_id = Some(self.allocate_object_id());
200 self.info_id = Some(self.allocate_object_id());
201
202 if let Some(ref encryption) = document.encryption {
205 self.init_encryption(encryption)?;
206 }
207
208 let font_refs = self.write_fonts(document)?;
210
211 self.preallocate_form_manager_fields(document)?;
218
219 self.write_pages(document, &font_refs)?;
221
222 self.write_form_fields(document)?;
224
225 self.write_catalog(document)?;
227
228 self.write_info(document)?;
230
231 self.write_encryption_dict()?;
233
234 if self.config.use_object_streams {
236 self.flush_object_streams()?;
237 }
238
239 let xref_position = self.current_position;
241 if self.config.use_xref_streams {
242 self.write_xref_stream()?;
243 } else {
244 self.write_xref()?;
245 }
246
247 if !self.config.use_xref_streams {
249 self.write_trailer(xref_position)?;
250 }
251
252 if let Ok(()) = self.writer.flush() {
253 }
255 Ok(())
256 }
257
258 pub fn write_incremental_update(
292 &mut self,
293 base_pdf_path: impl AsRef<std::path::Path>,
294 document: &mut Document,
295 ) -> Result<()> {
296 use std::io::{BufReader, Read, Seek, SeekFrom};
297
298 let base_pdf_file = std::fs::File::open(base_pdf_path.as_ref())?;
300 let mut pdf_reader = crate::parser::PdfReader::new(BufReader::new(base_pdf_file))?;
301
302 let base_catalog = pdf_reader.catalog()?;
304
305 let (base_pages_id, base_pages_gen) = base_catalog
307 .get("Pages")
308 .and_then(|obj| {
309 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
310 Some((*id, *gen))
311 } else {
312 None
313 }
314 })
315 .ok_or_else(|| {
316 crate::error::PdfError::InvalidStructure(
317 "Base PDF catalog missing /Pages reference".to_string(),
318 )
319 })?;
320
321 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
323 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
324 base_pages_obj
325 {
326 dict.get("Kids")
327 .and_then(|obj| {
328 if let crate::parser::objects::PdfObject::Array(arr) = obj {
329 Some(
332 arr.0
333 .iter()
334 .filter_map(|item| {
335 if let crate::parser::objects::PdfObject::Reference(id, gen) =
336 item
337 {
338 Some(crate::objects::Object::Reference(
339 crate::objects::ObjectId::new(*id, *gen),
340 ))
341 } else {
342 None
343 }
344 })
345 .collect::<Vec<_>>(),
346 )
347 } else {
348 None
349 }
350 })
351 .unwrap_or_default()
352 } else {
353 Vec::new()
354 };
355
356 let base_page_count = base_pages_kids.len();
358
359 let base_pdf = std::fs::File::open(base_pdf_path.as_ref())?;
361 let mut base_reader = BufReader::new(base_pdf);
362
363 base_reader.seek(SeekFrom::End(-100))?;
365 let mut end_buffer = vec![0u8; 100];
366 let bytes_read = base_reader.read(&mut end_buffer)?;
367 end_buffer.truncate(bytes_read);
368
369 let end_str = String::from_utf8_lossy(&end_buffer);
370 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
371 let after_startxref = &end_str[startxref_pos + 9..];
372
373 let number_str: String = after_startxref
374 .chars()
375 .skip_while(|c| c.is_whitespace())
376 .take_while(|c| c.is_ascii_digit())
377 .collect();
378
379 number_str.parse::<u64>().map_err(|_| {
380 crate::error::PdfError::InvalidStructure(
381 "Could not parse startxref offset".to_string(),
382 )
383 })?
384 } else {
385 return Err(crate::error::PdfError::InvalidStructure(
386 "startxref not found in base PDF".to_string(),
387 ));
388 };
389
390 base_reader.seek(SeekFrom::Start(0))?;
392 let base_size = std::io::copy(&mut base_reader, &mut self.writer)? as u64;
393
394 self.prev_xref_offset = Some(prev_xref);
396 self.base_pdf_size = Some(base_size);
397 self.current_position = base_size;
398
399 if !document.used_characters_by_font.is_empty() {
401 self.document_used_chars_by_font = document.used_characters_by_font.clone();
402 }
403
404 self.catalog_id = Some(self.allocate_object_id());
406 self.pages_id = Some(self.allocate_object_id());
407 self.info_id = Some(self.allocate_object_id());
408
409 let font_refs = self.write_fonts(document)?;
411
412 self.write_pages(document, &font_refs)?;
414
415 self.write_form_fields(document)?;
417
418 let catalog_id = self.get_catalog_id()?;
420 let new_pages_id = self.get_pages_id()?;
421
422 let mut catalog = crate::objects::Dictionary::new();
423 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
424 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
425
426 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
431
432 let mut all_pages_kids = base_pages_kids;
434
435 for page_id in &self.page_ids {
437 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
438 }
439
440 let mut pages_dict = crate::objects::Dictionary::new();
441 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
442 pages_dict.set("Kids", crate::objects::Object::Array(all_pages_kids));
443 pages_dict.set(
444 "Count",
445 crate::objects::Object::Integer((base_page_count + self.page_ids.len()) as i64),
446 );
447
448 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
449
450 self.write_info(document)?;
452
453 let xref_position = self.current_position;
455 self.write_xref()?;
456
457 self.write_trailer(xref_position)?;
459
460 self.writer.flush()?;
461 Ok(())
462 }
463
464 pub fn write_incremental_with_page_replacement(
530 &mut self,
531 base_pdf_path: impl AsRef<std::path::Path>,
532 document: &mut Document,
533 ) -> Result<()> {
534 use std::io::Cursor;
535
536 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
538 let base_size = base_pdf_bytes.len() as u64;
539
540 let mut pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
542
543 let base_catalog = pdf_reader.catalog()?;
544
545 let (base_pages_id, base_pages_gen) = base_catalog
546 .get("Pages")
547 .and_then(|obj| {
548 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
549 Some((*id, *gen))
550 } else {
551 None
552 }
553 })
554 .ok_or_else(|| {
555 crate::error::PdfError::InvalidStructure(
556 "Base PDF catalog missing /Pages reference".to_string(),
557 )
558 })?;
559
560 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
561 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
562 base_pages_obj
563 {
564 dict.get("Kids")
565 .and_then(|obj| {
566 if let crate::parser::objects::PdfObject::Array(arr) = obj {
567 Some(
568 arr.0
569 .iter()
570 .filter_map(|item| {
571 if let crate::parser::objects::PdfObject::Reference(id, gen) =
572 item
573 {
574 Some(crate::objects::Object::Reference(
575 crate::objects::ObjectId::new(*id, *gen),
576 ))
577 } else {
578 None
579 }
580 })
581 .collect::<Vec<_>>(),
582 )
583 } else {
584 None
585 }
586 })
587 .unwrap_or_default()
588 } else {
589 Vec::new()
590 };
591
592 let base_page_count = base_pages_kids.len();
593
594 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
596 let end_bytes = &base_pdf_bytes[start_search..];
597 let end_str = String::from_utf8_lossy(end_bytes);
598
599 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
600 let after_startxref = &end_str[startxref_pos + 9..];
601 let number_str: String = after_startxref
602 .chars()
603 .skip_while(|c| c.is_whitespace())
604 .take_while(|c| c.is_ascii_digit())
605 .collect();
606
607 number_str.parse::<u64>().map_err(|_| {
608 crate::error::PdfError::InvalidStructure(
609 "Could not parse startxref offset".to_string(),
610 )
611 })?
612 } else {
613 return Err(crate::error::PdfError::InvalidStructure(
614 "startxref not found in base PDF".to_string(),
615 ));
616 };
617
618 self.writer.write_all(&base_pdf_bytes)?;
620
621 self.prev_xref_offset = Some(prev_xref);
622 self.base_pdf_size = Some(base_size);
623 self.current_position = base_size;
624
625 if !document.used_characters_by_font.is_empty() {
627 self.document_used_chars_by_font = document.used_characters_by_font.clone();
628 }
629
630 self.catalog_id = Some(self.allocate_object_id());
631 self.pages_id = Some(self.allocate_object_id());
632 self.info_id = Some(self.allocate_object_id());
633
634 let font_refs = self.write_fonts(document)?;
635 self.write_pages(document, &font_refs)?;
636 self.write_form_fields(document)?;
637
638 let catalog_id = self.get_catalog_id()?;
640 let new_pages_id = self.get_pages_id()?;
641
642 let mut catalog = crate::objects::Dictionary::new();
643 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
644 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
645 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
646
647 let mut all_pages_kids = Vec::new();
649 let replacement_count = document.pages.len();
650
651 for page_id in &self.page_ids {
653 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
654 }
655
656 if replacement_count < base_page_count {
658 for i in replacement_count..base_page_count {
659 if let Some(page_ref) = base_pages_kids.get(i) {
660 all_pages_kids.push(page_ref.clone());
661 }
662 }
663 }
664
665 let mut pages_dict = crate::objects::Dictionary::new();
666 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
667 pages_dict.set(
668 "Kids",
669 crate::objects::Object::Array(all_pages_kids.clone()),
670 );
671 pages_dict.set(
672 "Count",
673 crate::objects::Object::Integer(all_pages_kids.len() as i64),
674 );
675
676 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
677 self.write_info(document)?;
678
679 let xref_position = self.current_position;
680 self.write_xref()?;
681 self.write_trailer(xref_position)?;
682
683 self.writer.flush()?;
684 Ok(())
685 }
686
687 pub fn write_incremental_with_overlay<P: AsRef<std::path::Path>>(
735 &mut self,
736 base_pdf_path: P,
737 mut overlay_fn: impl FnMut(&mut crate::Page) -> Result<()>,
738 ) -> Result<()> {
739 use std::io::Cursor;
740
741 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
743 let base_size = base_pdf_bytes.len() as u64;
744
745 let pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
747 let parsed_doc = crate::parser::PdfDocument::new(pdf_reader);
748
749 let page_count = parsed_doc.page_count()?;
751
752 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
754 let end_bytes = &base_pdf_bytes[start_search..];
755 let end_str = String::from_utf8_lossy(end_bytes);
756
757 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
758 let after_startxref = &end_str[startxref_pos + 9..];
759 let number_str: String = after_startxref
760 .chars()
761 .skip_while(|c| c.is_whitespace())
762 .take_while(|c| c.is_ascii_digit())
763 .collect();
764
765 number_str.parse::<u64>().map_err(|_| {
766 crate::error::PdfError::InvalidStructure(
767 "Could not parse startxref offset".to_string(),
768 )
769 })?
770 } else {
771 return Err(crate::error::PdfError::InvalidStructure(
772 "startxref not found in base PDF".to_string(),
773 ));
774 };
775
776 self.writer.write_all(&base_pdf_bytes)?;
778
779 self.prev_xref_offset = Some(prev_xref);
780 self.base_pdf_size = Some(base_size);
781 self.current_position = base_size;
782
783 let mut temp_doc = crate::Document::new();
785
786 for page_idx in 0..page_count {
787 let parsed_page = parsed_doc.get_page(page_idx)?;
789 let mut writable_page =
790 crate::Page::from_parsed_with_content(&parsed_page, &parsed_doc)?;
791
792 overlay_fn(&mut writable_page)?;
794
795 temp_doc.add_page(writable_page);
797 }
798
799 if !temp_doc.used_characters_by_font.is_empty() {
802 self.document_used_chars_by_font = temp_doc.used_characters_by_font.clone();
803 }
804
805 self.catalog_id = Some(self.allocate_object_id());
806 self.pages_id = Some(self.allocate_object_id());
807 self.info_id = Some(self.allocate_object_id());
808
809 let font_refs = self.write_fonts(&temp_doc)?;
810 self.write_pages(&temp_doc, &font_refs)?;
811 self.write_form_fields(&mut temp_doc)?;
812
813 let catalog_id = self.get_catalog_id()?;
815 let new_pages_id = self.get_pages_id()?;
816
817 let mut catalog = crate::objects::Dictionary::new();
818 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
819 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
820 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
821
822 let mut all_pages_kids = Vec::new();
824 for page_id in &self.page_ids {
825 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
826 }
827
828 let mut pages_dict = crate::objects::Dictionary::new();
829 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
830 pages_dict.set(
831 "Kids",
832 crate::objects::Object::Array(all_pages_kids.clone()),
833 );
834 pages_dict.set(
835 "Count",
836 crate::objects::Object::Integer(all_pages_kids.len() as i64),
837 );
838
839 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
840 self.write_info(&temp_doc)?;
841
842 let xref_position = self.current_position;
843 self.write_xref()?;
844 self.write_trailer(xref_position)?;
845
846 self.writer.flush()?;
847 Ok(())
848 }
849
850 fn write_header(&mut self) -> Result<()> {
851 let header = format!("%PDF-{}\n", self.config.pdf_version);
852 self.write_bytes(header.as_bytes())?;
853 self.write_bytes(&[b'%', 0xE2, 0xE3, 0xCF, 0xD3, b'\n'])?;
855 Ok(())
856 }
857
858 fn convert_pdf_objects_dict_to_writer(
861 &self,
862 pdf_dict: &crate::pdf_objects::Dictionary,
863 ) -> crate::objects::Dictionary {
864 let mut writer_dict = crate::objects::Dictionary::new();
865
866 for (key, value) in pdf_dict.iter() {
867 let writer_obj = self.convert_pdf_object_to_writer(value);
868 writer_dict.set(key.as_str(), writer_obj);
869 }
870
871 writer_dict
872 }
873
874 fn convert_pdf_object_to_writer(
875 &self,
876 obj: &crate::pdf_objects::Object,
877 ) -> crate::objects::Object {
878 use crate::objects::Object as WriterObj;
879 use crate::pdf_objects::Object as PdfObj;
880
881 match obj {
882 PdfObj::Null => WriterObj::Null,
883 PdfObj::Boolean(b) => WriterObj::Boolean(*b),
884 PdfObj::Integer(i) => WriterObj::Integer(*i),
885 PdfObj::Real(f) => WriterObj::Real(*f),
886 PdfObj::String(s) => {
887 WriterObj::String(String::from_utf8_lossy(s.as_bytes()).to_string())
888 }
889 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
890 PdfObj::Array(arr) => {
891 let items: Vec<WriterObj> = arr
892 .iter()
893 .map(|item| self.convert_pdf_object_to_writer(item))
894 .collect();
895 WriterObj::Array(items)
896 }
897 PdfObj::Dictionary(dict) => {
898 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
899 }
900 PdfObj::Stream(stream) => {
901 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
902 WriterObj::Stream(dict, stream.data.clone())
903 }
904 PdfObj::Reference(id) => {
905 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
906 }
907 }
908 }
909
910 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
911 let catalog_id = self.get_catalog_id()?;
912 let pages_id = self.get_pages_id()?;
913
914 let mut catalog = Dictionary::new();
915 catalog.set("Type", Object::Name("Catalog".to_string()));
916 catalog.set("Pages", Object::Reference(pages_id));
917
918 if let Some(form_manager) = &document.form_manager {
938 if document.acro_form.is_none() {
939 document.acro_form = Some(crate::forms::AcroForm::new());
940 }
941
942 let mut sorted: Vec<(Dictionary, crate::objects::ObjectReference)> = Vec::new();
948 for (name, form_field, placeholder) in form_manager.iter_fields_sorted() {
949 let real_id = *self.form_field_placeholder_map.get(&placeholder).ok_or_else(
950 || {
951 PdfError::Internal(format!(
952 "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"
953 ))
954 },
955 )?;
956 sorted.push((form_field.field_dict.clone(), real_id));
957 }
958 for (field_dict, real_id) in sorted {
959 self.write_object(real_id, Object::Dictionary(field_dict))?;
960 }
961
962 if let Some(acro) = document.acro_form.as_mut() {
963 for r in &self.form_manager_field_refs {
964 if !acro.fields.contains(r) {
965 acro.fields.push(*r);
966 }
967 }
968 }
969 }
970
971 if let Some(acro_form) = &document.acro_form {
973 let acro_form_id = self.allocate_object_id();
975
976 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
978
979 catalog.set("AcroForm", Object::Reference(acro_form_id));
981 }
982
983 if let Some(outline_tree) = &document.outline {
985 if !outline_tree.items.is_empty() {
986 let outline_root_id = self.write_outline_tree(outline_tree)?;
987 catalog.set("Outlines", Object::Reference(outline_root_id));
988 }
989 }
990
991 if let Some(struct_tree) = &document.struct_tree {
993 if !struct_tree.is_empty() {
994 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
995 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
996 catalog.set("MarkInfo", {
998 let mut mark_info = Dictionary::new();
999 mark_info.set("Marked", Object::Boolean(true));
1000 Object::Dictionary(mark_info)
1001 });
1002 }
1003 }
1004
1005 let xmp_metadata = document.create_xmp_metadata();
1008 let xmp_packet = xmp_metadata.to_xmp_packet();
1009 let metadata_id = self.allocate_object_id();
1010
1011 let mut metadata_dict = Dictionary::new();
1013 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
1014 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
1015 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
1016
1017 self.write_object(
1019 metadata_id,
1020 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
1021 )?;
1022
1023 catalog.set("Metadata", Object::Reference(metadata_id));
1025
1026 if let Some(action) = &document.open_action {
1028 catalog.set("OpenAction", Object::Dictionary(action.to_dict()));
1029 }
1030
1031 if let Some(prefs) = &document.viewer_preferences {
1033 catalog.set("ViewerPreferences", Object::Dictionary(prefs.to_dict()));
1034 }
1035
1036 if let Some(named_dests) = &document.named_destinations {
1041 let dests_tree_id = self.allocate_object_id();
1042 self.write_object(dests_tree_id, Object::Dictionary(named_dests.to_dict()))?;
1043
1044 let mut names_dict = Dictionary::new();
1045 names_dict.set("Dests", Object::Reference(dests_tree_id));
1046 let names_dict_id = self.allocate_object_id();
1047 self.write_object(names_dict_id, Object::Dictionary(names_dict))?;
1048
1049 catalog.set("Names", Object::Reference(names_dict_id));
1050 }
1051
1052 if let Some(page_labels) = &document.page_labels {
1056 let labels_id = self.allocate_object_id();
1057 self.write_object(labels_id, Object::Dictionary(page_labels.to_dict()))?;
1058 catalog.set("PageLabels", Object::Reference(labels_id));
1059 }
1060
1061 self.write_object(catalog_id, Object::Dictionary(catalog))?;
1062 Ok(())
1063 }
1064
1065 fn write_page_content(&mut self, content_id: ObjectId, page: &crate::page::Page) -> Result<()> {
1066 let mut page_copy = page.clone();
1067 let content = page_copy.generate_content()?;
1068
1069 #[cfg(feature = "compression")]
1071 {
1072 use crate::objects::Stream;
1073 let mut stream = Stream::new(content);
1074 if self.config.compress_streams {
1076 stream.compress_flate()?;
1077 }
1078
1079 self.write_object(
1080 content_id,
1081 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
1082 )?;
1083 }
1084
1085 #[cfg(not(feature = "compression"))]
1086 {
1087 let mut stream_dict = Dictionary::new();
1088 stream_dict.set("Length", Object::Integer(content.len() as i64));
1089
1090 self.write_object(content_id, Object::Stream(stream_dict, content))?;
1091 }
1092
1093 Ok(())
1094 }
1095
1096 fn write_outline_tree(
1097 &mut self,
1098 outline_tree: &crate::structure::OutlineTree,
1099 ) -> Result<ObjectId> {
1100 let outline_root_id = self.allocate_object_id();
1102
1103 let mut outline_root = Dictionary::new();
1104 outline_root.set("Type", Object::Name("Outlines".to_string()));
1105
1106 if !outline_tree.items.is_empty() {
1107 let mut item_ids = Vec::new();
1109
1110 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
1112 let mut count = items.len();
1113 for item in items {
1114 count += count_items(&item.children);
1115 }
1116 count
1117 }
1118
1119 let total_items = count_items(&outline_tree.items);
1120
1121 for _ in 0..total_items {
1123 item_ids.push(self.allocate_object_id());
1124 }
1125
1126 let mut id_index = 0;
1127
1128 let first_id = item_ids[0];
1130 let last_id = item_ids[outline_tree.items.len() - 1];
1131
1132 outline_root.set("First", Object::Reference(first_id));
1133 outline_root.set("Last", Object::Reference(last_id));
1134
1135 let visible_count = outline_tree.visible_count();
1137 outline_root.set("Count", Object::Integer(visible_count));
1138
1139 let mut written_items = Vec::new();
1141
1142 for (i, item) in outline_tree.items.iter().enumerate() {
1143 let item_id = item_ids[id_index];
1144 id_index += 1;
1145
1146 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1147 let next_id = if i < outline_tree.items.len() - 1 {
1148 Some(item_ids[i + 1])
1149 } else {
1150 None
1151 };
1152
1153 let children_ids = self.write_outline_item(
1155 item,
1156 item_id,
1157 outline_root_id,
1158 prev_id,
1159 next_id,
1160 &mut item_ids,
1161 &mut id_index,
1162 )?;
1163
1164 written_items.extend(children_ids);
1165 }
1166 }
1167
1168 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1169 Ok(outline_root_id)
1170 }
1171
1172 #[allow(clippy::too_many_arguments)]
1173 fn write_outline_item(
1174 &mut self,
1175 item: &crate::structure::OutlineItem,
1176 item_id: ObjectId,
1177 parent_id: ObjectId,
1178 prev_id: Option<ObjectId>,
1179 next_id: Option<ObjectId>,
1180 all_ids: &mut Vec<ObjectId>,
1181 id_index: &mut usize,
1182 ) -> Result<Vec<ObjectId>> {
1183 let mut written_ids = vec![item_id];
1184
1185 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1187 let first_idx = *id_index;
1188 let first_id = all_ids[first_idx];
1189 let last_idx = first_idx + item.children.len() - 1;
1190 let last_id = all_ids[last_idx];
1191
1192 for (i, child) in item.children.iter().enumerate() {
1194 let child_id = all_ids[*id_index];
1195 *id_index += 1;
1196
1197 let child_prev = if i > 0 {
1198 Some(all_ids[first_idx + i - 1])
1199 } else {
1200 None
1201 };
1202 let child_next = if i < item.children.len() - 1 {
1203 Some(all_ids[first_idx + i + 1])
1204 } else {
1205 None
1206 };
1207
1208 let child_ids = self.write_outline_item(
1209 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1211 )?;
1212
1213 written_ids.extend(child_ids);
1214 }
1215
1216 (Some(first_id), Some(last_id))
1217 } else {
1218 (None, None)
1219 };
1220
1221 let item_dict = crate::structure::outline_item_to_dict(
1223 item,
1224 parent_id,
1225 first_child_id,
1226 last_child_id,
1227 prev_id,
1228 next_id,
1229 );
1230
1231 self.write_object(item_id, Object::Dictionary(item_dict))?;
1232
1233 Ok(written_ids)
1234 }
1235
1236 fn write_struct_tree(
1238 &mut self,
1239 struct_tree: &crate::structure::StructTree,
1240 ) -> Result<ObjectId> {
1241 let struct_tree_root_id = self.allocate_object_id();
1243 let mut element_ids = Vec::new();
1244 for _ in 0..struct_tree.len() {
1245 element_ids.push(self.allocate_object_id());
1246 }
1247
1248 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1250 std::collections::HashMap::new();
1251
1252 if let Some(root_index) = struct_tree.root_index() {
1254 parent_map.insert(root_index, struct_tree_root_id);
1255
1256 fn map_children_parents(
1258 tree: &crate::structure::StructTree,
1259 parent_index: usize,
1260 parent_id: ObjectId,
1261 element_ids: &[ObjectId],
1262 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1263 ) {
1264 if let Some(parent_elem) = tree.get(parent_index) {
1265 for &child_index in &parent_elem.children {
1266 parent_map.insert(child_index, parent_id);
1267 map_children_parents(
1268 tree,
1269 child_index,
1270 element_ids[child_index],
1271 element_ids,
1272 parent_map,
1273 );
1274 }
1275 }
1276 }
1277
1278 map_children_parents(
1279 struct_tree,
1280 root_index,
1281 element_ids[root_index],
1282 &element_ids,
1283 &mut parent_map,
1284 );
1285 }
1286
1287 for (index, element) in struct_tree.iter().enumerate() {
1289 let element_id = element_ids[index];
1290 let mut element_dict = Dictionary::new();
1291
1292 element_dict.set("Type", Object::Name("StructElem".to_string()));
1293 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1294
1295 if let Some(&parent_id) = parent_map.get(&index) {
1297 element_dict.set("P", Object::Reference(parent_id));
1298 }
1299
1300 if let Some(ref id) = element.id {
1302 element_dict.set("ID", Object::String(id.clone()));
1303 }
1304
1305 if let Some(ref lang) = element.attributes.lang {
1307 element_dict.set("Lang", Object::String(lang.clone()));
1308 }
1309 if let Some(ref alt) = element.attributes.alt {
1310 element_dict.set("Alt", Object::String(alt.clone()));
1311 }
1312 if let Some(ref actual_text) = element.attributes.actual_text {
1313 element_dict.set("ActualText", Object::String(actual_text.clone()));
1314 }
1315 if let Some(ref title) = element.attributes.title {
1316 element_dict.set("T", Object::String(title.clone()));
1317 }
1318 if let Some(bbox) = element.attributes.bbox {
1319 element_dict.set(
1320 "BBox",
1321 Object::Array(vec![
1322 Object::Real(bbox[0]),
1323 Object::Real(bbox[1]),
1324 Object::Real(bbox[2]),
1325 Object::Real(bbox[3]),
1326 ]),
1327 );
1328 }
1329
1330 let mut kids = Vec::new();
1332
1333 for &child_index in &element.children {
1335 kids.push(Object::Reference(element_ids[child_index]));
1336 }
1337
1338 for mcid_ref in &element.mcids {
1340 let mut mcr = Dictionary::new();
1341 mcr.set("Type", Object::Name("MCR".to_string()));
1342 mcr.set("Pg", Object::Integer(mcid_ref.page_index as i64));
1343 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1344 kids.push(Object::Dictionary(mcr));
1345 }
1346
1347 if !kids.is_empty() {
1348 element_dict.set("K", Object::Array(kids));
1349 }
1350
1351 self.write_object(element_id, Object::Dictionary(element_dict))?;
1352 }
1353
1354 let mut struct_tree_root = Dictionary::new();
1356 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1357
1358 if let Some(root_index) = struct_tree.root_index() {
1360 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1361 }
1362
1363 if !struct_tree.role_map.mappings().is_empty() {
1365 let mut role_map = Dictionary::new();
1366 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1367 role_map.set(
1368 custom_type.as_str(),
1369 Object::Name(standard_type.as_pdf_name().to_string()),
1370 );
1371 }
1372 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1373 }
1374
1375 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1376 Ok(struct_tree_root_id)
1377 }
1378
1379 fn preallocate_form_manager_fields(&mut self, document: &Document) -> Result<()> {
1392 let Some(form_manager) = &document.form_manager else {
1393 return Ok(());
1394 };
1395
1396 for (_name, _form_field, placeholder) in form_manager.iter_fields_sorted() {
1397 let real_id = self.allocate_object_id();
1398 self.form_field_placeholder_map.insert(placeholder, real_id);
1399 self.form_manager_field_refs.push(real_id);
1400 }
1401 Ok(())
1402 }
1403
1404 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1405 if !self.form_field_ids.is_empty() {
1407 if let Some(acro_form) = &mut document.acro_form {
1408 acro_form.fields.clear();
1410 for field_id in &self.form_field_ids {
1411 acro_form.add_field(*field_id);
1412 }
1413
1414 acro_form.need_appearances = true;
1416 if acro_form.da.is_none() {
1417 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1418 }
1419 }
1420 }
1421 Ok(())
1422 }
1423
1424 fn write_info(&mut self, document: &Document) -> Result<()> {
1425 let info_id = self.get_info_id()?;
1426 let mut info_dict = Dictionary::new();
1427
1428 if let Some(ref title) = document.metadata.title {
1429 info_dict.set("Title", Object::String(title.clone()));
1430 }
1431 if let Some(ref author) = document.metadata.author {
1432 info_dict.set("Author", Object::String(author.clone()));
1433 }
1434 if let Some(ref subject) = document.metadata.subject {
1435 info_dict.set("Subject", Object::String(subject.clone()));
1436 }
1437 if let Some(ref keywords) = document.metadata.keywords {
1438 info_dict.set("Keywords", Object::String(keywords.clone()));
1439 }
1440 if let Some(ref creator) = document.metadata.creator {
1441 info_dict.set("Creator", Object::String(creator.clone()));
1442 }
1443 if let Some(ref producer) = document.metadata.producer {
1444 info_dict.set("Producer", Object::String(producer.clone()));
1445 }
1446
1447 if let Some(creation_date) = document.metadata.creation_date {
1449 let date_string = format_pdf_date(creation_date);
1450 info_dict.set("CreationDate", Object::String(date_string));
1451 }
1452
1453 if let Some(mod_date) = document.metadata.modification_date {
1455 let date_string = format_pdf_date(mod_date);
1456 info_dict.set("ModDate", Object::String(date_string));
1457 }
1458
1459 let edition = super::Edition::OpenSource;
1462
1463 let signature = super::PdfSignature::new(document, edition);
1464 signature.write_to_info_dict(&mut info_dict);
1465
1466 self.write_object(info_id, Object::Dictionary(info_dict))?;
1467 Ok(())
1468 }
1469
1470 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1471 let mut font_refs = HashMap::new();
1472
1473 for font_name in document.custom_font_names() {
1483 let has_usage = self
1484 .document_used_chars_by_font
1485 .get(&font_name)
1486 .map(|chars| !chars.is_empty())
1487 .unwrap_or(false);
1488 if !has_usage {
1489 continue;
1490 }
1491 if let Some(font) = document.get_custom_font(&font_name) {
1492 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1495 font_refs.insert(font_name.clone(), font_id);
1496 }
1497 }
1498
1499 Ok(font_refs)
1500 }
1501
1502 fn write_font_with_unicode_support(
1504 &mut self,
1505 font_name: &str,
1506 font: &crate::fonts::Font,
1507 ) -> Result<ObjectId> {
1508 self.write_type0_font_from_font(font_name, font)
1511 }
1512
1513 fn write_type0_font_from_font(
1515 &mut self,
1516 font_name: &str,
1517 font: &crate::fonts::Font,
1518 ) -> Result<ObjectId> {
1519 let used_chars = self
1526 .document_used_chars_by_font
1527 .get(font_name)
1528 .cloned()
1529 .unwrap_or_else(|| {
1530 let mut chars = std::collections::HashSet::new();
1531 for ch in
1532 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1533 {
1534 chars.insert(ch);
1535 }
1536 chars
1537 });
1538 let font_id = self.allocate_object_id();
1540 let descendant_font_id = self.allocate_object_id();
1541 let descriptor_id = self.allocate_object_id();
1542 let font_file_id = self.allocate_object_id();
1543 let to_unicode_id = self.allocate_object_id();
1544
1545 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1550 if font.data.len() > 100_000 && !used_chars.is_empty() {
1551 match crate::text::fonts::truetype_subsetter::subset_font(
1552 font.data.clone(),
1553 &used_chars,
1554 ) {
1555 Ok(subset_result) => (
1556 subset_result.font_data,
1557 Some(subset_result.glyph_mapping),
1558 font.clone(),
1559 ),
1560 Err(_) => {
1561 if font.data.len() < 25_000_000 {
1562 (font.data.clone(), None, font.clone())
1563 } else {
1564 (Vec::new(), None, font.clone())
1565 }
1566 }
1567 }
1568 } else {
1569 (font.data.clone(), None, font.clone())
1570 };
1571
1572 if !font_data_to_embed.is_empty() {
1573 let mut font_file_dict = Dictionary::new();
1578 match font.format {
1579 crate::fonts::FontFormat::OpenType => {
1580 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1581 }
1582 crate::fonts::FontFormat::TrueType => {
1583 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1584 }
1585 }
1586
1587 #[cfg(feature = "compression")]
1592 {
1593 let font_stream_obj = if self.config.compress_streams {
1594 let mut stream =
1595 crate::objects::Stream::with_dictionary(font_file_dict, font_data_to_embed);
1596 stream.compress_flate()?;
1597 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1598 } else {
1599 Object::Stream(font_file_dict, font_data_to_embed)
1600 };
1601 self.write_object(font_file_id, font_stream_obj)?;
1602 }
1603 #[cfg(not(feature = "compression"))]
1604 {
1605 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1606 self.write_object(font_file_id, font_stream_obj)?;
1607 }
1608 } else {
1609 let font_file_dict = Dictionary::new();
1611 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1612 self.write_object(font_file_id, font_stream_obj)?;
1613 }
1614
1615 let mut descriptor = Dictionary::new();
1617 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1618 descriptor.set("FontName", Object::Name(font_name.to_string()));
1619 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1621 "FontBBox",
1622 Object::Array(vec![
1623 Object::Integer(font.descriptor.font_bbox[0] as i64),
1624 Object::Integer(font.descriptor.font_bbox[1] as i64),
1625 Object::Integer(font.descriptor.font_bbox[2] as i64),
1626 Object::Integer(font.descriptor.font_bbox[3] as i64),
1627 ]),
1628 );
1629 descriptor.set(
1630 "ItalicAngle",
1631 Object::Real(font.descriptor.italic_angle as f64),
1632 );
1633 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1634 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1635 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1636 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1637 let font_file_key = match font.format {
1639 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1642 descriptor.set(font_file_key, Object::Reference(font_file_id));
1643 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1644
1645 let mut cid_font = Dictionary::new();
1647 cid_font.set("Type", Object::Name("Font".to_string()));
1648 let cid_font_subtype = match font.format {
1650 crate::fonts::FontFormat::OpenType => "CIDFontType0",
1651 crate::fonts::FontFormat::TrueType => "CIDFontType2",
1652 };
1653 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1654 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1655
1656 let mut cid_system_info = Dictionary::new();
1658 let (registry, ordering, supplement) =
1659 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1660 cjk_type.cid_system_info()
1661 } else {
1662 ("Adobe", "Identity", 0)
1663 };
1664
1665 cid_system_info.set("Registry", Object::String(registry.to_string()));
1666 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1667 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1668 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1669
1670 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1671
1672 let default_width = self.calculate_default_width(font);
1674 cid_font.set("DW", Object::Integer(default_width));
1675
1676 let w_array = self.generate_width_array(
1680 &original_font_for_widths,
1681 default_width,
1682 subset_glyph_mapping.as_ref(),
1683 );
1684 cid_font.set("W", Object::Array(w_array));
1685
1686 if cid_font_subtype == "CIDFontType2" {
1690 let cid_to_gid_map =
1692 self.generate_cid_to_gid_map(font_name, font, subset_glyph_mapping.as_ref())?;
1693 if !cid_to_gid_map.is_empty() {
1694 let cid_to_gid_map_id = self.allocate_object_id();
1702 let map_dict = Dictionary::new();
1703 #[cfg(feature = "compression")]
1704 let map_stream = if self.config.compress_streams {
1705 let mut stream =
1706 crate::objects::Stream::with_dictionary(map_dict, cid_to_gid_map);
1707 stream.compress_flate()?;
1708 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1709 } else {
1710 let mut d = map_dict;
1711 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1712 Object::Stream(d, cid_to_gid_map)
1713 };
1714 #[cfg(not(feature = "compression"))]
1715 let map_stream = {
1716 let mut d = map_dict;
1717 d.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1718 Object::Stream(d, cid_to_gid_map)
1719 };
1720 self.write_object(cid_to_gid_map_id, map_stream)?;
1721 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1722 } else {
1723 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1724 }
1725 }
1726 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1729
1730 let cmap_data = self.generate_tounicode_cmap_from_font(font_name, font);
1736 let cmap_dict = Dictionary::new();
1737 #[cfg(feature = "compression")]
1738 let cmap_stream = if self.config.compress_streams {
1739 let mut stream = crate::objects::Stream::with_dictionary(cmap_dict, cmap_data);
1740 stream.compress_flate()?;
1741 Object::Stream(stream.dictionary().clone(), stream.data().to_vec())
1742 } else {
1743 Object::Stream(cmap_dict, cmap_data)
1744 };
1745 #[cfg(not(feature = "compression"))]
1746 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1747 self.write_object(to_unicode_id, cmap_stream)?;
1748
1749 let mut type0_font = Dictionary::new();
1751 type0_font.set("Type", Object::Name("Font".to_string()));
1752 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1753 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1754 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1755 type0_font.set(
1756 "DescendantFonts",
1757 Object::Array(vec![Object::Reference(descendant_font_id)]),
1758 );
1759 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1760
1761 self.write_object(font_id, Object::Dictionary(type0_font))?;
1762
1763 Ok(font_id)
1764 }
1765
1766 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
1768 use crate::text::fonts::truetype::TrueTypeFont;
1769
1770 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1772 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1773 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
1774 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
1775 let common_chars =
1779 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
1780 let mut total_width = 0;
1781 let mut count = 0;
1782
1783 for ch in common_chars.chars() {
1784 let unicode = ch as u32;
1785 if let Some(&pdf_width) = widths.get(&unicode) {
1786 total_width += pdf_width as i64;
1787 count += 1;
1788 }
1789 }
1790
1791 if count > 0 {
1792 return total_width / count;
1793 }
1794 }
1795 }
1796 }
1797 }
1798
1799 500
1801 }
1802
1803 fn generate_width_array(
1805 &self,
1806 font: &crate::fonts::Font,
1807 _default_width: i64,
1808 subset_mapping: Option<&HashMap<u32, u16>>,
1809 ) -> Vec<Object> {
1810 use crate::text::fonts::truetype::TrueTypeFont;
1811
1812 let mut w_array = Vec::new();
1813
1814 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1816 let char_to_glyph = {
1820 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1822 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
1823 if let Some(subset_map) = subset_mapping {
1825 let mut filtered = HashMap::new();
1826 for unicode in subset_map.keys() {
1827 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
1829 filtered.insert(*unicode, orig_glyph);
1830 }
1831 }
1832 filtered
1833 } else {
1834 cmap.mappings.clone()
1835 }
1836 } else {
1837 HashMap::new()
1838 }
1839 } else {
1840 HashMap::new()
1841 }
1842 };
1843
1844 if !char_to_glyph.is_empty() {
1845 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
1847 let mut sorted_chars: Vec<_> = widths.iter().collect();
1852 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
1853
1854 let mut i = 0;
1855 while i < sorted_chars.len() {
1856 let start_unicode = *sorted_chars[i].0;
1857 let pdf_width = *sorted_chars[i].1 as i64;
1859
1860 let mut end_unicode = start_unicode;
1862 let mut j = i + 1;
1863 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
1864 let next_pdf_width = *sorted_chars[j].1 as i64;
1865 if next_pdf_width == pdf_width {
1866 end_unicode = *sorted_chars[j].0;
1867 j += 1;
1868 } else {
1869 break;
1870 }
1871 }
1872
1873 if start_unicode == end_unicode {
1875 w_array.push(Object::Integer(start_unicode as i64));
1877 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
1878 } else {
1879 w_array.push(Object::Integer(start_unicode as i64));
1881 w_array.push(Object::Integer(end_unicode as i64));
1882 w_array.push(Object::Integer(pdf_width));
1883 }
1884
1885 i = j;
1886 }
1887
1888 return w_array;
1889 }
1890 }
1891 }
1892
1893 let ranges = vec![
1895 (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), ];
1932
1933 for (start, end, width) in ranges {
1935 if start == end {
1936 w_array.push(Object::Integer(start));
1938 w_array.push(Object::Array(vec![Object::Integer(width)]));
1939 } else {
1940 w_array.push(Object::Integer(start));
1942 w_array.push(Object::Integer(end));
1943 w_array.push(Object::Integer(width));
1944 }
1945 }
1946
1947 w_array
1948 }
1949
1950 fn generate_cid_to_gid_map(
1952 &mut self,
1953 font_name: &str,
1954 font: &crate::fonts::Font,
1955 subset_mapping: Option<&HashMap<u32, u16>>,
1956 ) -> Result<Vec<u8>> {
1957 use crate::text::fonts::truetype::TrueTypeFont;
1958
1959 let cmap_mappings = if let Some(subset_map) = subset_mapping {
1962 subset_map.clone()
1964 } else {
1965 let tt_font = TrueTypeFont::parse(font.data.clone())?;
1967 let cmap_tables = tt_font.parse_cmap()?;
1968
1969 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
1971 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
1972 })?;
1973
1974 cmap.mappings.clone()
1975 };
1976
1977 let used_chars = self
1984 .document_used_chars_by_font
1985 .get(font_name)
1986 .cloned()
1987 .unwrap_or_default();
1988
1989 let max_unicode = if !used_chars.is_empty() {
1991 used_chars
1993 .iter()
1994 .map(|ch| *ch as u32)
1995 .max()
1996 .unwrap_or(0x00FF) .min(0xFFFF) as usize
1998 } else {
1999 cmap_mappings
2001 .keys()
2002 .max()
2003 .copied()
2004 .unwrap_or(0xFFFF)
2005 .min(0xFFFF) as usize
2006 };
2007
2008 let mut map = vec![0u8; (max_unicode + 1) * 2];
2010
2011 let mut sample_mappings = Vec::new();
2013 for (&unicode, &glyph_id) in &cmap_mappings {
2014 if unicode <= max_unicode as u32 {
2015 let idx = (unicode as usize) * 2;
2016 map[idx] = (glyph_id >> 8) as u8;
2018 map[idx + 1] = (glyph_id & 0xFF) as u8;
2019
2020 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
2022 {
2023 sample_mappings.push((unicode, glyph_id));
2024 }
2025 }
2026 }
2027
2028 Ok(map)
2029 }
2030
2031 fn generate_tounicode_cmap_from_font(
2033 &self,
2034 font_name: &str,
2035 font: &crate::fonts::Font,
2036 ) -> Vec<u8> {
2037 use crate::text::fonts::truetype::TrueTypeFont;
2038
2039 let mut cmap = String::new();
2040
2041 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
2043 cmap.push_str("12 dict begin\n");
2044 cmap.push_str("begincmap\n");
2045 cmap.push_str("/CIDSystemInfo\n");
2046 cmap.push_str("<< /Registry (Adobe)\n");
2047 cmap.push_str(" /Ordering (UCS)\n");
2048 cmap.push_str(" /Supplement 0\n");
2049 cmap.push_str(">> def\n");
2050 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
2051 cmap.push_str("/CMapType 2 def\n");
2052 cmap.push_str("1 begincodespacerange\n");
2053 cmap.push_str("<0000> <FFFF>\n");
2054 cmap.push_str("endcodespacerange\n");
2055
2056 let used_codepoints: Option<std::collections::HashSet<u32>> = self
2062 .document_used_chars_by_font
2063 .get(font_name)
2064 .map(|chars| {
2065 chars
2066 .iter()
2067 .map(|c| *c as u32)
2068 .filter(|cp| *cp <= 0xFFFF)
2069 .collect()
2070 });
2071
2072 let mut mappings: Vec<(u32, u32)> = Vec::new();
2073
2074 if let Some(used) = &used_codepoints {
2075 for cp in used {
2077 mappings.push((*cp, *cp));
2078 }
2079 } else if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
2080 if let Ok(cmap_tables) = tt_font.parse_cmap() {
2082 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
2083 for (&unicode, &glyph_id) in &cmap_table.mappings {
2084 if glyph_id > 0 && unicode <= 0xFFFF {
2085 mappings.push((unicode, unicode));
2086 }
2087 }
2088 }
2089 }
2090 }
2091
2092 mappings.sort_by_key(|&(cid, _)| cid);
2094
2095 let mut i = 0;
2097 while i < mappings.len() {
2098 let start_cid = mappings[i].0;
2100 let start_unicode = mappings[i].1;
2101 let mut end_idx = i;
2102
2103 while end_idx + 1 < mappings.len()
2105 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
2106 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
2107 && end_idx - i < 99
2108 {
2110 end_idx += 1;
2111 }
2112
2113 if end_idx > i {
2114 cmap.push_str("1 beginbfrange\n");
2116 cmap.push_str(&format!(
2117 "<{:04X}> <{:04X}> <{:04X}>\n",
2118 start_cid, mappings[end_idx].0, start_unicode
2119 ));
2120 cmap.push_str("endbfrange\n");
2121 i = end_idx + 1;
2122 } else {
2123 let mut chars = Vec::new();
2125 let chunk_end = (i + 100).min(mappings.len());
2126
2127 for item in &mappings[i..chunk_end] {
2128 chars.push(*item);
2129 }
2130
2131 if !chars.is_empty() {
2132 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
2133 for (cid, unicode) in chars {
2134 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
2135 }
2136 cmap.push_str("endbfchar\n");
2137 }
2138
2139 i = chunk_end;
2140 }
2141 }
2142
2143 cmap.push_str("endcmap\n");
2145 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
2146 cmap.push_str("end\n");
2147 cmap.push_str("end\n");
2148
2149 cmap.into_bytes()
2150 }
2151
2152 #[allow(dead_code)]
2154 fn write_truetype_font(
2155 &mut self,
2156 font_name: &str,
2157 font: &crate::text::font_manager::CustomFont,
2158 ) -> Result<ObjectId> {
2159 let font_id = self.allocate_object_id();
2161 let descriptor_id = self.allocate_object_id();
2162 let font_file_id = self.allocate_object_id();
2163
2164 if let Some(ref data) = font.font_data {
2166 let mut font_file_dict = Dictionary::new();
2167 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2168 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2169 self.write_object(font_file_id, font_stream_obj)?;
2170 }
2171
2172 let mut descriptor = Dictionary::new();
2174 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2175 descriptor.set("FontName", Object::Name(font_name.to_string()));
2176 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2178 "FontBBox",
2179 Object::Array(vec![
2180 Object::Integer(-1000),
2181 Object::Integer(-1000),
2182 Object::Integer(2000),
2183 Object::Integer(2000),
2184 ]),
2185 );
2186 descriptor.set("ItalicAngle", Object::Integer(0));
2187 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2188 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2189 descriptor.set(
2190 "CapHeight",
2191 Object::Integer(font.descriptor.cap_height as i64),
2192 );
2193 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2194 descriptor.set("FontFile2", Object::Reference(font_file_id));
2195 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2196
2197 let mut font_dict = Dictionary::new();
2199 font_dict.set("Type", Object::Name("Font".to_string()));
2200 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2201 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2202 font_dict.set("FirstChar", Object::Integer(0));
2203 font_dict.set("LastChar", Object::Integer(255));
2204
2205 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2207 font_dict.set("Widths", Object::Array(widths));
2208 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2209
2210 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2212
2213 self.write_object(font_id, Object::Dictionary(font_dict))?;
2214
2215 Ok(font_id)
2216 }
2217
2218 fn write_pages(
2219 &mut self,
2220 document: &Document,
2221 font_refs: &HashMap<String, ObjectId>,
2222 ) -> Result<()> {
2223 let pages_id = self.get_pages_id()?;
2224 let mut pages_dict = Dictionary::new();
2225 pages_dict.set("Type", Object::Name("Pages".to_string()));
2226 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2227
2228 let mut kids = Vec::new();
2229
2230 let mut page_ids = Vec::new();
2232 let mut content_ids = Vec::new();
2233 for _ in 0..document.pages.len() {
2234 page_ids.push(self.allocate_object_id());
2235 content_ids.push(self.allocate_object_id());
2236 }
2237
2238 for page_id in &page_ids {
2239 kids.push(Object::Reference(*page_id));
2240 }
2241
2242 pages_dict.set("Kids", Object::Array(kids));
2243
2244 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2245
2246 self.page_ids = page_ids.clone();
2248
2249 for (i, page) in document.pages.iter().enumerate() {
2251 let page_id = page_ids[i];
2252 let content_id = content_ids[i];
2253
2254 self.write_page_with_fonts(page_id, pages_id, content_id, page, document, font_refs)?;
2255 self.write_page_content(content_id, page)?;
2256 }
2257
2258 Ok(())
2259 }
2260
2261 #[allow(dead_code)]
2263 fn write_pages_with_fonts(
2264 &mut self,
2265 document: &Document,
2266 font_refs: &HashMap<String, ObjectId>,
2267 ) -> Result<()> {
2268 self.write_pages(document, font_refs)
2269 }
2270
2271 fn write_page_with_fonts(
2272 &mut self,
2273 page_id: ObjectId,
2274 parent_id: ObjectId,
2275 content_id: ObjectId,
2276 page: &crate::page::Page,
2277 _document: &Document,
2278 font_refs: &HashMap<String, ObjectId>,
2279 ) -> Result<()> {
2280 let mut page_dict = page.to_dict();
2282
2283 page_dict.set("Type", Object::Name("Page".to_string()));
2284 page_dict.set("Parent", Object::Reference(parent_id));
2285 page_dict.set("Contents", Object::Reference(content_id));
2286
2287 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2289 res.clone()
2290 } else {
2291 Dictionary::new()
2292 };
2293
2294 let mut font_dict = Dictionary::new();
2296
2297 let mut helvetica_dict = Dictionary::new();
2302 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2303 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2304 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2305 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2306 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2307
2308 let mut helvetica_bold_dict = Dictionary::new();
2309 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2310 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2311 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2312 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2313 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2314
2315 let mut helvetica_oblique_dict = Dictionary::new();
2316 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2317 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2318 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2319 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2320 font_dict.set(
2321 "Helvetica-Oblique",
2322 Object::Dictionary(helvetica_oblique_dict),
2323 );
2324
2325 let mut helvetica_bold_oblique_dict = Dictionary::new();
2326 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2327 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2328 helvetica_bold_oblique_dict.set(
2329 "BaseFont",
2330 Object::Name("Helvetica-BoldOblique".to_string()),
2331 );
2332 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2333 font_dict.set(
2334 "Helvetica-BoldOblique",
2335 Object::Dictionary(helvetica_bold_oblique_dict),
2336 );
2337
2338 let mut times_dict = Dictionary::new();
2340 times_dict.set("Type", Object::Name("Font".to_string()));
2341 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2342 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2343 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2344 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2345
2346 let mut times_bold_dict = Dictionary::new();
2347 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2348 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2349 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2350 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2351 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2352
2353 let mut times_italic_dict = Dictionary::new();
2354 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2355 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2356 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2357 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2358 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2359
2360 let mut times_bold_italic_dict = Dictionary::new();
2361 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2362 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2363 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2364 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2365 font_dict.set(
2366 "Times-BoldItalic",
2367 Object::Dictionary(times_bold_italic_dict),
2368 );
2369
2370 let mut courier_dict = Dictionary::new();
2372 courier_dict.set("Type", Object::Name("Font".to_string()));
2373 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2374 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2375 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2376 font_dict.set("Courier", Object::Dictionary(courier_dict));
2377
2378 let mut courier_bold_dict = Dictionary::new();
2379 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2380 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2381 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2382 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2383 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2384
2385 let mut courier_oblique_dict = Dictionary::new();
2386 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2387 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2388 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2389 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2390 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2391
2392 let mut courier_bold_oblique_dict = Dictionary::new();
2393 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2394 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2395 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2396 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2397 font_dict.set(
2398 "Courier-BoldOblique",
2399 Object::Dictionary(courier_bold_oblique_dict),
2400 );
2401
2402 for (font_name, font_id) in font_refs {
2404 font_dict.set(font_name, Object::Reference(*font_id));
2405 }
2406
2407 resources.set("Font", Object::Dictionary(font_dict));
2408
2409 let has_images = !page.images().is_empty();
2411 let has_forms = !page.form_xobjects().is_empty();
2412
2413 let mut form_xobject_ids: HashMap<String, ObjectId> = HashMap::new();
2420
2421 if has_images || has_forms {
2422 let mut xobject_dict = Dictionary::new();
2423
2424 let mut image_entries: Vec<(&String, &crate::graphics::Image)> =
2428 page.images().iter().collect();
2429 image_entries.sort_by_key(|(name, _)| name.as_str());
2430 for (name, image) in image_entries {
2431 let image_id = self.allocate_object_id();
2433
2434 if image.has_transparency() {
2436 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2438
2439 if let Some(smask_stream) = smask_obj {
2441 let smask_id = self.allocate_object_id();
2442 self.write_object(smask_id, smask_stream)?;
2443
2444 if let Object::Stream(ref mut dict, _) = main_obj {
2446 dict.set("SMask", Object::Reference(smask_id));
2447 }
2448 }
2449
2450 self.write_object(image_id, main_obj)?;
2452 } else {
2453 self.write_object(image_id, image.to_pdf_object())?;
2455 }
2456
2457 xobject_dict.set(name, Object::Reference(image_id));
2459 }
2460
2461 let mut form_entries: Vec<(&String, &crate::graphics::FormXObject)> =
2463 page.form_xobjects().iter().collect();
2464 form_entries.sort_by_key(|(name, _)| name.as_str());
2465 for (name, form) in form_entries {
2466 let form_id = self.allocate_object_id();
2467 let stream = form.to_stream()?;
2468 let stream_obj =
2469 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2470 self.write_object(form_id, stream_obj)?;
2471 xobject_dict.set(name, Object::Reference(form_id));
2472 form_xobject_ids.insert(name.clone(), form_id);
2475 }
2476
2477 resources.set("XObject", Object::Dictionary(xobject_dict));
2478 }
2479
2480 if let Some(extgstate_states) = page.get_extgstate_resources() {
2482 let mut extgstate_dict = Dictionary::new();
2483 let mut extgstate_entries: Vec<(&String, &crate::graphics::ExtGState)> =
2485 extgstate_states.iter().collect();
2486 extgstate_entries.sort_by_key(|(name, _)| name.as_str());
2487 for (name, state) in extgstate_entries {
2488 let mut state_dict = Dictionary::new();
2489 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2490
2491 if let Some(alpha_stroke) = state.alpha_stroke {
2493 state_dict.set("CA", Object::Real(alpha_stroke));
2494 }
2495 if let Some(alpha_fill) = state.alpha_fill {
2496 state_dict.set("ca", Object::Real(alpha_fill));
2497 }
2498
2499 if let Some(line_width) = state.line_width {
2501 state_dict.set("LW", Object::Real(line_width));
2502 }
2503 if let Some(line_cap) = state.line_cap {
2504 state_dict.set("LC", Object::Integer(line_cap as i64));
2505 }
2506 if let Some(line_join) = state.line_join {
2507 state_dict.set("LJ", Object::Integer(line_join as i64));
2508 }
2509 if let Some(dash_pattern) = &state.dash_pattern {
2510 let dash_objects: Vec<Object> = dash_pattern
2511 .array
2512 .iter()
2513 .map(|&d| Object::Real(d))
2514 .collect();
2515 state_dict.set(
2516 "D",
2517 Object::Array(vec![
2518 Object::Array(dash_objects),
2519 Object::Real(dash_pattern.phase),
2520 ]),
2521 );
2522 }
2523
2524 if let Some(ref bm) = state.blend_mode {
2528 state_dict.set("BM", Object::Name(bm.pdf_name().to_string()));
2529 }
2530
2531 if let Some(ref soft_mask) = state.soft_mask {
2549 let mut mask_dict = soft_mask.to_pdf_dictionary()?;
2550 if let Some(Object::Name(ref g_name)) = mask_dict.get("G").cloned() {
2551 let form_id = form_xobject_ids.get(g_name).ok_or_else(|| {
2552 crate::error::PdfError::InvalidStructure(format!(
2553 "SoftMask references transparency group {:?} but no matching \
2554 FormXObject is registered on the page; call \
2555 Page::add_form_xobject({:?}, ...) before saving",
2556 g_name, g_name
2557 ))
2558 })?;
2559 mask_dict.set("G", Object::Reference(*form_id));
2560 }
2561 state_dict.set("SMask", Object::Dictionary(mask_dict));
2562 }
2563
2564 extgstate_dict.set(name, Object::Dictionary(state_dict));
2565 }
2566 if !extgstate_dict.is_empty() {
2567 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2568 }
2569 }
2570
2571 if !page.color_spaces().is_empty() {
2586 let mut cs_dict = Dictionary::new();
2587 for (name, cs) in page.color_spaces() {
2588 cs_dict.set(name, cs.to_object());
2592 }
2593 resources.set("ColorSpace", Object::Dictionary(cs_dict));
2594 }
2595
2596 if !page.patterns().is_empty() {
2597 let mut pat_dict = Dictionary::new();
2598 let mut entries: Vec<(&String, &crate::graphics::TilingPattern)> =
2599 page.patterns().iter().collect();
2600 entries.sort_by_key(|(name, _)| name.as_str());
2601 for (name, pattern) in entries {
2602 let pattern_id = self.allocate_object_id();
2603 let pattern_dict = pattern.to_pdf_dictionary()?;
2604 self.write_object(
2605 pattern_id,
2606 Object::Stream(pattern_dict, pattern.content_stream.clone()),
2607 )?;
2608 pat_dict.set(name, Object::Reference(pattern_id));
2609 }
2610 resources.set("Pattern", Object::Dictionary(pat_dict));
2611 }
2612
2613 if !page.shadings().is_empty() {
2614 let mut sh_dict = Dictionary::new();
2615 let mut entries: Vec<(&String, &crate::graphics::ShadingDefinition)> =
2616 page.shadings().iter().collect();
2617 entries.sort_by_key(|(name, _)| name.as_str());
2618 for (name, shading) in entries {
2619 let shading_id = self.allocate_object_id();
2620 let shading_dict = shading.to_pdf_dictionary()?;
2621 self.write_object(shading_id, Object::Dictionary(shading_dict))?;
2622 sh_dict.set(name, Object::Reference(shading_id));
2623 }
2624 resources.set("Shading", Object::Dictionary(sh_dict));
2625 }
2626
2627 if let Some(preserved_res) = page.get_preserved_resources() {
2630 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
2632
2633 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2635 let renamed_fonts = crate::writer::rename_preserved_fonts(fonts);
2637
2638 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
2640 }
2641
2642 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2646 let mut fonts_with_refs = crate::objects::Dictionary::new();
2647
2648 for (font_name, font_obj) in fonts.iter() {
2649 if let Object::Dictionary(font_dict) = font_obj {
2650 let updated_font = self.write_embedded_font_streams(font_dict)?;
2652 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
2653 } else {
2654 fonts_with_refs.set(font_name, font_obj.clone());
2656 }
2657 }
2658
2659 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
2661 }
2662
2663 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
2667 let mut xobjects_with_refs = crate::objects::Dictionary::new();
2668 tracing::debug!(
2669 "Externalizing {} preserved XObject entries as indirect objects",
2670 xobjects.len()
2671 );
2672
2673 for (xobj_name, xobj_obj) in xobjects.iter() {
2674 match xobj_obj {
2675 Object::Stream(dict, data) => {
2676 let obj_id = self.allocate_object_id();
2677 self.write_object(obj_id, Object::Stream(dict.clone(), data.clone()))?;
2678 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
2679 }
2680 Object::Dictionary(dict) => {
2681 let externalized = self.externalize_streams_in_dict(dict)?;
2683 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
2684 }
2685 _ => {
2686 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
2687 }
2688 }
2689 }
2690
2691 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
2692 }
2693
2694 for (key, value) in preserved_writer_dict.iter() {
2696 if let Some(Object::Dictionary(existing)) = resources.get(key) {
2698 if let Object::Dictionary(preserved_dict) = value {
2699 let mut merged = existing.clone();
2700 for (res_name, res_obj) in preserved_dict.iter() {
2702 if !merged.contains_key(res_name) {
2703 merged.set(res_name, res_obj.clone());
2704 }
2705 }
2706 resources.set(key, Object::Dictionary(merged));
2707 }
2708 } else {
2709 resources.set(key, value.clone());
2711 }
2712 }
2713 }
2714
2715 page_dict.set("Resources", Object::Dictionary(resources));
2716
2717 let mut annot_refs: Vec<Object> = Vec::new();
2719
2720 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
2722 for annot in annots {
2723 if let Object::Dictionary(ref annot_dict) = annot {
2724 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
2725 if subtype == "Widget" {
2726 let widget_id = self.allocate_object_id();
2727 self.write_object(widget_id, annot.clone())?;
2728 annot_refs.push(Object::Reference(widget_id));
2729
2730 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
2732 if let Some(Object::String(field_name)) = annot_dict.get("T") {
2733 self.field_widget_map
2734 .entry(field_name.clone())
2735 .or_default()
2736 .push(widget_id);
2737 self.field_id_map.insert(field_name.clone(), widget_id);
2738 self.form_field_ids.push(widget_id);
2739 }
2740 }
2741 continue;
2742 }
2743 }
2744 }
2745 annot_refs.push(annot.clone());
2746 }
2747 }
2748
2749 for annotation in page.annotations() {
2753 let annot_id = self.allocate_object_id();
2754 let mut annot_dict = annotation.to_dict();
2755
2756 if let Some(placeholder) = annotation.field_parent {
2774 if let Some(real_id) = self.form_field_placeholder_map.get(&placeholder) {
2775 annot_dict.set("Parent", Object::Reference(*real_id));
2776 }
2777 }
2778
2779 if let Some(Object::Dictionary(ap_dict)) = annot_dict.get("AP") {
2798 let mut updated_ap = crate::objects::Dictionary::new();
2799 for (state_key, state_val) in ap_dict.iter() {
2800 match state_val {
2801 Object::Stream(sd, data) => {
2802 let patched_sd = Self::rewrite_ap_stream_font_resources(sd, font_refs);
2808 let stream_id = self.allocate_object_id();
2809 self.write_object(stream_id, Object::Stream(patched_sd, data.clone()))?;
2810 updated_ap.set(state_key, Object::Reference(stream_id));
2811 }
2812 Object::Dictionary(down_dict) => {
2813 let externalized = self
2815 .externalize_streams_in_dict_with_font_refs(down_dict, font_refs)?;
2816 updated_ap.set(state_key, Object::Dictionary(externalized));
2817 }
2818 _ => {
2819 updated_ap.set(state_key, state_val.clone());
2820 }
2821 }
2822 }
2823 annot_dict.set("AP", Object::Dictionary(updated_ap));
2824 }
2825
2826 self.write_object(annot_id, Object::Dictionary(annot_dict))?;
2827 annot_refs.push(Object::Reference(annot_id));
2828
2829 if annotation.annotation_type == crate::annotations::AnnotationType::Widget {
2831 if let Some(Object::String(field_name)) = annotation.properties.get("T") {
2832 self.field_widget_map
2833 .entry(field_name.clone())
2834 .or_default()
2835 .push(annot_id);
2836 self.field_id_map.insert(field_name.clone(), annot_id);
2837 self.form_field_ids.push(annot_id);
2838 }
2839 }
2840 }
2841
2842 if !annot_refs.is_empty() {
2844 page_dict.set("Annots", Object::Array(annot_refs));
2845 } else {
2846 page_dict.remove("Annots");
2847 }
2848
2849 self.write_object(page_id, Object::Dictionary(page_dict))?;
2850 Ok(())
2851 }
2852}
2853
2854impl PdfWriter<BufWriter<std::fs::File>> {
2855 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
2856 let file = std::fs::File::create(path)?;
2857 let writer = BufWriter::new(file);
2858
2859 Ok(Self {
2860 writer,
2861 xref_positions: HashMap::new(),
2862 current_position: 0,
2863 next_object_id: 1,
2864 catalog_id: None,
2865 pages_id: None,
2866 info_id: None,
2867 field_widget_map: HashMap::new(),
2868 field_id_map: HashMap::new(),
2869 form_field_ids: Vec::new(),
2870 page_ids: Vec::new(),
2871 config: WriterConfig::default(),
2872 document_used_chars_by_font: std::collections::HashMap::new(),
2873 buffered_objects: HashMap::new(),
2874 compressed_object_map: HashMap::new(),
2875 prev_xref_offset: None,
2876 base_pdf_size: None,
2877 encrypt_obj_id: None,
2878 file_id: None,
2879 encryption_state: None,
2880 pending_encrypt_dict: None,
2881 form_field_placeholder_map: HashMap::new(),
2882 form_manager_field_refs: Vec::new(),
2883 })
2884 }
2885}
2886
2887impl<W: Write> PdfWriter<W> {
2888 fn externalize_streams_in_dict(
2907 &mut self,
2908 dict: &crate::objects::Dictionary,
2909 ) -> Result<crate::objects::Dictionary> {
2910 self.externalize_streams_in_dict_with_font_refs(dict, &HashMap::new())
2911 }
2912
2913 fn externalize_streams_in_dict_with_font_refs(
2917 &mut self,
2918 dict: &crate::objects::Dictionary,
2919 font_refs: &HashMap<String, ObjectId>,
2920 ) -> Result<crate::objects::Dictionary> {
2921 let mut result = crate::objects::Dictionary::new();
2922 for (key, value) in dict.iter() {
2923 match value {
2924 Object::Stream(d, data) => {
2925 let patched_d = Self::rewrite_ap_stream_font_resources(d, font_refs);
2926 let obj_id = self.allocate_object_id();
2927 self.write_object(obj_id, Object::Stream(patched_d, data.clone()))?;
2928 result.set(key, Object::Reference(obj_id));
2929 }
2930 _ => {
2931 result.set(key, value.clone());
2932 }
2933 }
2934 }
2935 Ok(result)
2936 }
2937
2938 fn rewrite_ap_stream_font_resources(
2954 stream_dict: &crate::objects::Dictionary,
2955 font_refs: &HashMap<String, ObjectId>,
2956 ) -> crate::objects::Dictionary {
2957 if font_refs.is_empty() {
2964 return stream_dict.clone();
2965 }
2966
2967 let mut out = stream_dict.clone();
2968
2969 let Some(Object::Dictionary(resources)) = stream_dict.get("Resources") else {
2975 return out;
2976 };
2977 let Some(Object::Dictionary(fonts)) = resources.get("Font") else {
2978 return out;
2979 };
2980
2981 let mut patched_fonts = crate::objects::Dictionary::new();
2982 let mut changed = false;
2983 for (font_name, entry) in fonts.iter() {
2984 let should_rewrite = match entry {
2988 Object::Dictionary(d) => {
2989 matches!(d.get("Subtype"), Some(Object::Name(s)) if s == "Type0")
2990 }
2991 _ => false,
2992 };
2993 if should_rewrite {
2994 if let Some(font_id) = font_refs.get(font_name.as_str()) {
2995 patched_fonts.set(font_name, Object::Reference(*font_id));
2996 changed = true;
2997 continue;
2998 }
2999 }
3000 patched_fonts.set(font_name, entry.clone());
3001 }
3002
3003 if changed {
3004 let mut patched_resources = resources.clone();
3005 patched_resources.set("Font", Object::Dictionary(patched_fonts));
3006 out.set("Resources", Object::Dictionary(patched_resources));
3007 }
3008 out
3009 }
3010
3011 fn write_embedded_font_streams(
3012 &mut self,
3013 font_dict: &crate::objects::Dictionary,
3014 ) -> Result<crate::objects::Dictionary> {
3015 let mut updated_font = font_dict.clone();
3016
3017 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
3019 if subtype == "Type0" {
3020 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
3022 let mut updated_descendants = Vec::new();
3023
3024 for descendant in descendants {
3025 match descendant {
3026 Object::Dictionary(cidfont) => {
3027 let updated_cidfont =
3029 self.write_cidfont_embedded_streams(cidfont)?;
3030 let cidfont_id = self.allocate_object_id();
3032 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
3033 updated_descendants.push(Object::Reference(cidfont_id));
3035 }
3036 Object::Reference(_) => {
3037 updated_descendants.push(descendant.clone());
3039 }
3040 _ => {
3041 updated_descendants.push(descendant.clone());
3042 }
3043 }
3044 }
3045
3046 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
3047 }
3048
3049 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
3051 let tounicode_id = self.allocate_object_id();
3052 self.write_object(
3053 tounicode_id,
3054 Object::Stream(stream_dict.clone(), stream_data.clone()),
3055 )?;
3056 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
3057 }
3058
3059 return Ok(updated_font);
3060 }
3061 }
3062
3063 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
3066 let mut updated_descriptor = descriptor.clone();
3067 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3068
3069 for key in &font_file_keys {
3071 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3072 let stream_id = self.allocate_object_id();
3074 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
3075 self.write_object(stream_id, stream_obj)?;
3076
3077 updated_descriptor.set(*key, Object::Reference(stream_id));
3079 }
3080 }
3082
3083 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
3085 }
3086
3087 Ok(updated_font)
3088 }
3089
3090 fn write_cidfont_embedded_streams(
3092 &mut self,
3093 cidfont: &crate::objects::Dictionary,
3094 ) -> Result<crate::objects::Dictionary> {
3095 let mut updated_cidfont = cidfont.clone();
3096
3097 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
3099 let mut updated_descriptor = descriptor.clone();
3100 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
3101
3102 for key in &font_file_keys {
3104 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
3105 let stream_id = self.allocate_object_id();
3106 self.write_object(
3107 stream_id,
3108 Object::Stream(stream_dict.clone(), stream_data.clone()),
3109 )?;
3110 updated_descriptor.set(*key, Object::Reference(stream_id));
3111 }
3112 }
3113
3114 let descriptor_id = self.allocate_object_id();
3116 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
3117
3118 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
3120 }
3121
3122 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
3124 let map_id = self.allocate_object_id();
3125 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
3126 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
3127 }
3128
3129 Ok(updated_cidfont)
3130 }
3131
3132 fn allocate_object_id(&mut self) -> ObjectId {
3133 let id = ObjectId::new(self.next_object_id, 0);
3134 self.next_object_id += 1;
3135 id
3136 }
3137
3138 fn get_catalog_id(&self) -> Result<ObjectId> {
3140 self.catalog_id.ok_or_else(|| {
3141 PdfError::InvalidOperation(
3142 "catalog_id not initialized - write_document() must be called first".to_string(),
3143 )
3144 })
3145 }
3146
3147 fn get_pages_id(&self) -> Result<ObjectId> {
3149 self.pages_id.ok_or_else(|| {
3150 PdfError::InvalidOperation(
3151 "pages_id not initialized - write_document() must be called first".to_string(),
3152 )
3153 })
3154 }
3155
3156 fn get_info_id(&self) -> Result<ObjectId> {
3158 self.info_id.ok_or_else(|| {
3159 PdfError::InvalidOperation(
3160 "info_id not initialized - write_document() must be called first".to_string(),
3161 )
3162 })
3163 }
3164
3165 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
3166 use crate::writer::ObjectStreamWriter;
3167
3168 let object = if let Some(ref enc_state) = self.encryption_state {
3170 let mut obj = object;
3171 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
3172 obj
3173 } else {
3174 object
3175 };
3176
3177 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
3179 let mut buffer = Vec::new();
3180 self.write_object_value_to_buffer(&object, &mut buffer)?;
3181 self.buffered_objects.insert(id, buffer);
3182 return Ok(());
3183 }
3184
3185 self.xref_positions.insert(id, self.current_position);
3187
3188 let header = format!("{} {} obj\n", id.number(), id.generation());
3190 self.write_bytes(header.as_bytes())?;
3191
3192 self.write_object_value(&object)?;
3193
3194 self.write_bytes(b"\nendobj\n")?;
3195 Ok(())
3196 }
3197
3198 fn write_object_value(&mut self, object: &Object) -> Result<()> {
3199 match object {
3200 Object::Null => self.write_bytes(b"null")?,
3201 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
3202 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
3203 Object::Real(f) => self.write_bytes(
3204 format!("{f:.6}")
3205 .trim_end_matches('0')
3206 .trim_end_matches('.')
3207 .as_bytes(),
3208 )?,
3209 Object::String(s) => {
3210 self.write_bytes(b"(")?;
3219 self.write_bytes(&escape_pdf_string_bytes(s.as_bytes()))?;
3220 self.write_bytes(b")")?;
3221 }
3222 Object::ByteString(bytes) => {
3223 self.write_bytes(b"<")?;
3225 for byte in bytes {
3226 self.write_bytes(format!("{byte:02X}").as_bytes())?;
3227 }
3228 self.write_bytes(b">")?;
3229 }
3230 Object::Name(n) => {
3231 self.write_bytes(b"/")?;
3232 self.write_bytes(n.as_bytes())?;
3233 }
3234 Object::Array(arr) => {
3235 self.write_bytes(b"[")?;
3236 for (i, obj) in arr.iter().enumerate() {
3237 if i > 0 {
3238 self.write_bytes(b" ")?;
3239 }
3240 self.write_object_value(obj)?;
3241 }
3242 self.write_bytes(b"]")?;
3243 }
3244 Object::Dictionary(dict) => {
3245 self.write_bytes(b"<<")?;
3253 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3254 entries.sort_by_key(|(k, _)| k.as_str());
3255 for (key, value) in entries {
3256 self.write_bytes(b"\n/")?;
3257 self.write_bytes(key.as_bytes())?;
3258 self.write_bytes(b" ")?;
3259 self.write_object_value(value)?;
3260 }
3261 self.write_bytes(b"\n>>")?;
3262 }
3263 Object::Stream(dict, data) => {
3264 let mut corrected_dict = dict.clone();
3267 corrected_dict.set("Length", Object::Integer(data.len() as i64));
3268
3269 self.write_object_value(&Object::Dictionary(corrected_dict))?;
3270 self.write_bytes(b"\nstream\n")?;
3271 self.write_bytes(data)?;
3272 self.write_bytes(b"\nendstream")?;
3273 }
3274 Object::Reference(id) => {
3275 let ref_str = format!("{} {} R", id.number(), id.generation());
3276 self.write_bytes(ref_str.as_bytes())?;
3277 }
3278 }
3279 Ok(())
3280 }
3281
3282 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
3284 match object {
3285 Object::Null => buffer.extend_from_slice(b"null"),
3286 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
3287 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
3288 Object::Real(f) => buffer.extend_from_slice(
3289 format!("{f:.6}")
3290 .trim_end_matches('0')
3291 .trim_end_matches('.')
3292 .as_bytes(),
3293 ),
3294 Object::String(s) => {
3295 buffer.push(b'(');
3298 buffer.extend_from_slice(&escape_pdf_string_bytes(s.as_bytes()));
3299 buffer.push(b')');
3300 }
3301 Object::ByteString(bytes) => {
3302 buffer.push(b'<');
3303 for byte in bytes {
3304 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
3305 }
3306 buffer.push(b'>');
3307 }
3308 Object::Name(n) => {
3309 buffer.push(b'/');
3310 buffer.extend_from_slice(n.as_bytes());
3311 }
3312 Object::Array(arr) => {
3313 buffer.push(b'[');
3314 for (i, obj) in arr.iter().enumerate() {
3315 if i > 0 {
3316 buffer.push(b' ');
3317 }
3318 self.write_object_value_to_buffer(obj, buffer)?;
3319 }
3320 buffer.push(b']');
3321 }
3322 Object::Dictionary(dict) => {
3323 buffer.extend_from_slice(b"<<");
3327 let mut entries: Vec<(&String, &Object)> = dict.entries().collect();
3328 entries.sort_by_key(|(k, _)| k.as_str());
3329 for (key, value) in entries {
3330 buffer.extend_from_slice(b"\n/");
3331 buffer.extend_from_slice(key.as_bytes());
3332 buffer.push(b' ');
3333 self.write_object_value_to_buffer(value, buffer)?;
3334 }
3335 buffer.extend_from_slice(b"\n>>");
3336 }
3337 Object::Stream(_, _) => {
3338 return Err(crate::error::PdfError::ObjectStreamError(
3340 "Cannot compress stream objects in object streams".to_string(),
3341 ));
3342 }
3343 Object::Reference(id) => {
3344 let ref_str = format!("{} {} R", id.number(), id.generation());
3345 buffer.extend_from_slice(ref_str.as_bytes());
3346 }
3347 }
3348 Ok(())
3349 }
3350
3351 fn flush_object_streams(&mut self) -> Result<()> {
3353 if self.buffered_objects.is_empty() {
3354 return Ok(());
3355 }
3356
3357 let config = ObjectStreamConfig {
3359 max_objects_per_stream: 100,
3360 compression_level: 6,
3361 enabled: true,
3362 };
3363 let mut os_writer = ObjectStreamWriter::new(config);
3364
3365 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
3367 buffered.sort_by_key(|(id, _)| id.number());
3368
3369 for (id, data) in buffered {
3371 os_writer.add_object(*id, data.clone())?;
3372 }
3373
3374 let streams = os_writer.finalize()?;
3376
3377 for mut stream in streams {
3379 let stream_id = stream.stream_id;
3380
3381 let compressed_data = stream.generate_stream_data(6)?;
3383
3384 let dict = stream.generate_dictionary(&compressed_data);
3386
3387 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
3389 self.compressed_object_map
3390 .insert(*obj_id, (stream_id, index as u32));
3391 }
3392
3393 self.xref_positions.insert(stream_id, self.current_position);
3395
3396 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
3397 self.write_bytes(header.as_bytes())?;
3398
3399 self.write_object_value(&Object::Dictionary(dict))?;
3400
3401 self.write_bytes(b"\nstream\n")?;
3402 self.write_bytes(&compressed_data)?;
3403 self.write_bytes(b"\nendstream\nendobj\n")?;
3404 }
3405
3406 Ok(())
3407 }
3408
3409 fn write_xref(&mut self) -> Result<()> {
3410 self.write_bytes(b"xref\n")?;
3411
3412 let mut entries: Vec<_> = self
3414 .xref_positions
3415 .iter()
3416 .map(|(id, pos)| (*id, *pos))
3417 .collect();
3418 entries.sort_by_key(|(id, _)| id.number());
3419
3420 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
3422
3423 self.write_bytes(b"0 ")?;
3426 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
3427 self.write_bytes(b"\n")?;
3428
3429 self.write_bytes(b"0000000000 65535 f \n")?;
3431
3432 for obj_num in 1..=max_obj_num {
3435 let _obj_id = ObjectId::new(obj_num, 0);
3436 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
3437 let entry = format!("{:010} {:05} n \n", position, 0);
3438 self.write_bytes(entry.as_bytes())?;
3439 } else {
3440 self.write_bytes(b"0000000000 00000 f \n")?;
3442 }
3443 }
3444
3445 Ok(())
3446 }
3447
3448 fn write_xref_stream(&mut self) -> Result<()> {
3449 let catalog_id = self.get_catalog_id()?;
3450 let info_id = self.get_info_id()?;
3451
3452 let xref_stream_id = self.allocate_object_id();
3454 let xref_position = self.current_position;
3455
3456 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
3458 xref_writer.set_trailer_info(catalog_id, info_id);
3459
3460 xref_writer.add_free_entry(0, 65535);
3462
3463 let mut entries: Vec<_> = self
3465 .xref_positions
3466 .iter()
3467 .map(|(id, pos)| (*id, *pos))
3468 .collect();
3469 entries.sort_by_key(|(id, _)| id.number());
3470
3471 let max_obj_num = entries
3473 .iter()
3474 .map(|(id, _)| id.number())
3475 .max()
3476 .unwrap_or(0)
3477 .max(xref_stream_id.number());
3478
3479 for obj_num in 1..=max_obj_num {
3481 let obj_id = ObjectId::new(obj_num, 0);
3482
3483 if obj_num == xref_stream_id.number() {
3484 xref_writer.add_in_use_entry(xref_position, 0);
3486 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
3487 xref_writer.add_compressed_entry(stream_id.number(), *index);
3489 } else if let Some((id, position)) =
3490 entries.iter().find(|(id, _)| id.number() == obj_num)
3491 {
3492 xref_writer.add_in_use_entry(*position, id.generation());
3494 } else {
3495 xref_writer.add_free_entry(0, 0);
3497 }
3498 }
3499
3500 self.xref_positions.insert(xref_stream_id, xref_position);
3502
3503 self.write_bytes(
3505 format!(
3506 "{} {} obj\n",
3507 xref_stream_id.number(),
3508 xref_stream_id.generation()
3509 )
3510 .as_bytes(),
3511 )?;
3512
3513 let uncompressed_data = xref_writer.encode_entries();
3515 let final_data = if self.config.compress_streams {
3516 crate::compression::compress(&uncompressed_data)?
3517 } else {
3518 uncompressed_data
3519 };
3520
3521 let mut dict = xref_writer.create_dictionary(None);
3523 dict.set("Length", Object::Integer(final_data.len() as i64));
3524
3525 if self.config.compress_streams {
3527 dict.set("Filter", Object::Name("FlateDecode".to_string()));
3528 }
3529 self.write_bytes(b"<<")?;
3530 for (key, value) in dict.iter() {
3531 self.write_bytes(b"\n/")?;
3532 self.write_bytes(key.as_bytes())?;
3533 self.write_bytes(b" ")?;
3534 self.write_object_value(value)?;
3535 }
3536 self.write_bytes(b"\n>>\n")?;
3537
3538 self.write_bytes(b"stream\n")?;
3540 self.write_bytes(&final_data)?;
3541 self.write_bytes(b"\nendstream\n")?;
3542 self.write_bytes(b"endobj\n")?;
3543
3544 self.write_bytes(b"\nstartxref\n")?;
3546 self.write_bytes(xref_position.to_string().as_bytes())?;
3547 self.write_bytes(b"\n%%EOF\n")?;
3548
3549 Ok(())
3550 }
3551
3552 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
3559 use crate::encryption::{
3560 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
3561 };
3562 use std::sync::Arc;
3563
3564 let mut fid = vec![0u8; 16];
3566 use rand::Rng;
3567 rand::rng().fill_bytes(&mut fid);
3568
3569 let enc_dict = encryption
3570 .create_encryption_dict(Some(&fid))
3571 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
3572
3573 let enc_key = encryption
3575 .get_encryption_key(&enc_dict, Some(&fid))
3576 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
3577
3578 let handler = encryption.handler();
3580 let (method, key_len) = match encryption.strength {
3581 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
3582 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
3583 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
3584 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
3585 };
3586
3587 let std_filter = FunctionalCryptFilter {
3588 name: "StdCF".to_string(),
3589 method,
3590 length: key_len,
3591 auth_event: crate::encryption::AuthEvent::DocOpen,
3592 recipients: None,
3593 };
3594
3595 let mut filter_manager =
3596 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
3597 filter_manager.add_filter(std_filter);
3598
3599 let encryptor =
3600 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
3601
3602 let encrypt_id = self.allocate_object_id();
3604 self.encrypt_obj_id = Some(encrypt_id);
3605 self.file_id = Some(fid);
3606 self.encryption_state = Some(WriterEncryptionState { encryptor });
3607
3608 self.pending_encrypt_dict = Some(enc_dict.to_dict());
3610
3611 Ok(())
3612 }
3613
3614 fn write_encryption_dict(&mut self) -> Result<()> {
3616 if let (Some(encrypt_id), Some(dict)) =
3617 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
3618 {
3619 let enc_state = self.encryption_state.take();
3621 self.write_object(encrypt_id, Object::Dictionary(dict))?;
3622 self.encryption_state = enc_state;
3623 }
3624 Ok(())
3625 }
3626
3627 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
3628 let catalog_id = self.get_catalog_id()?;
3629 let info_id = self.get_info_id()?;
3630 let max_obj_num = self
3632 .xref_positions
3633 .keys()
3634 .map(|id| id.number())
3635 .max()
3636 .unwrap_or(0);
3637
3638 let mut trailer = Dictionary::new();
3639 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
3640 trailer.set("Root", Object::Reference(catalog_id));
3641 trailer.set("Info", Object::Reference(info_id));
3642
3643 if let Some(prev_xref) = self.prev_xref_offset {
3645 trailer.set("Prev", Object::Integer(prev_xref as i64));
3646 }
3647
3648 if let Some(encrypt_id) = self.encrypt_obj_id {
3650 trailer.set("Encrypt", Object::Reference(encrypt_id));
3651 }
3652 if let Some(ref fid) = self.file_id {
3653 trailer.set(
3654 "ID",
3655 Object::Array(vec![
3656 Object::ByteString(fid.clone()),
3657 Object::ByteString(fid.clone()),
3658 ]),
3659 );
3660 }
3661
3662 self.write_bytes(b"trailer\n")?;
3663 self.write_object_value(&Object::Dictionary(trailer))?;
3664 self.write_bytes(b"\nstartxref\n")?;
3665 self.write_bytes(xref_position.to_string().as_bytes())?;
3666 self.write_bytes(b"\n%%EOF\n")?;
3667
3668 Ok(())
3669 }
3670
3671 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
3672 self.writer.write_all(data)?;
3673 self.current_position += data.len() as u64;
3674 Ok(())
3675 }
3676
3677 #[allow(dead_code)]
3678 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
3679 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
3681 if rect_array.len() >= 4 {
3682 if let (
3683 Some(Object::Real(x1)),
3684 Some(Object::Real(y1)),
3685 Some(Object::Real(x2)),
3686 Some(Object::Real(y2)),
3687 ) = (
3688 rect_array.first(),
3689 rect_array.get(1),
3690 rect_array.get(2),
3691 rect_array.get(3),
3692 ) {
3693 (*x1, *y1, *x2, *y2)
3694 } else {
3695 (0.0, 0.0, 100.0, 20.0) }
3697 } else {
3698 (0.0, 0.0, 100.0, 20.0) }
3700 } else {
3701 (0.0, 0.0, 100.0, 20.0) };
3703
3704 let width = rect.2 - rect.0;
3705 let height = rect.3 - rect.1;
3706
3707 let mut content = String::new();
3709
3710 content.push_str("q\n");
3712
3713 crate::graphics::color::write_stroke_color(&mut content, crate::graphics::Color::black());
3715 content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
3719 content.push_str("S\n"); crate::graphics::color::write_fill_color(&mut content, crate::graphics::Color::white());
3723 content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
3724 content.push_str("f\n"); content.push_str("Q\n");
3728
3729 let mut stream_dict = Dictionary::new();
3731 stream_dict.set("Type", Object::Name("XObject".to_string()));
3732 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3733 stream_dict.set(
3734 "BBox",
3735 Object::Array(vec![
3736 Object::Real(0.0),
3737 Object::Real(0.0),
3738 Object::Real(width),
3739 Object::Real(height),
3740 ]),
3741 );
3742 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3743 stream_dict.set("Length", Object::Integer(content.len() as i64));
3744
3745 let stream_id = self.allocate_object_id();
3747 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3748
3749 Ok(stream_id)
3750 }
3751
3752 #[allow(dead_code)]
3753 fn create_field_appearance_stream(
3754 &mut self,
3755 field_dict: &Dictionary,
3756 widget: &crate::forms::Widget,
3757 ) -> Result<ObjectId> {
3758 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
3759 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
3760
3761 let mut content = String::new();
3763
3764 content.push_str("q\n");
3766
3767 if let Some(bg_color) = &widget.appearance.background_color {
3770 crate::graphics::color::write_fill_color(&mut content, *bg_color);
3771 content.push_str(&format!("0 0 {width} {height} re\n"));
3772 content.push_str("f\n");
3773 }
3774
3775 if let Some(border_color) = &widget.appearance.border_color {
3777 crate::graphics::color::write_stroke_color(&mut content, *border_color);
3778 content.push_str(&format!("{} w\n", widget.appearance.border_width));
3779 content.push_str(&format!("0 0 {width} {height} re\n"));
3780 content.push_str("S\n");
3781 }
3782
3783 if let Some(Object::Name(ft)) = field_dict.get("FT") {
3785 if ft == "Btn" {
3786 if let Some(Object::Name(v)) = field_dict.get("V") {
3787 if v == "Yes" {
3788 crate::graphics::color::write_stroke_color(
3790 &mut content,
3791 crate::graphics::Color::black(),
3792 );
3793 content.push_str("2 w\n");
3794 let margin = width * 0.2;
3795 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
3796 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
3797 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
3798 content.push_str("S\n");
3799 }
3800 }
3801 }
3802 }
3803
3804 content.push_str("Q\n");
3806
3807 let mut stream_dict = Dictionary::new();
3809 stream_dict.set("Type", Object::Name("XObject".to_string()));
3810 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3811 stream_dict.set(
3812 "BBox",
3813 Object::Array(vec![
3814 Object::Real(0.0),
3815 Object::Real(0.0),
3816 Object::Real(width),
3817 Object::Real(height),
3818 ]),
3819 );
3820 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3821 stream_dict.set("Length", Object::Integer(content.len() as i64));
3822
3823 let stream_id = self.allocate_object_id();
3825 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3826
3827 Ok(stream_id)
3828 }
3829}
3830
3831fn format_pdf_date(date: DateTime<Utc>) -> String {
3833 let formatted = date.format("D:%Y%m%d%H%M%S");
3836
3837 format!("{formatted}+00'00")
3839}
3840
3841#[cfg(test)]
3842mod tests;
3843
3844#[cfg(test)]
3845mod rigorous_tests;