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
74pub struct PdfWriter<W: Write> {
75 writer: W,
76 xref_positions: HashMap<ObjectId, u64>,
77 current_position: u64,
78 next_object_id: u32,
79 catalog_id: Option<ObjectId>,
81 pages_id: Option<ObjectId>,
82 info_id: Option<ObjectId>,
83 #[allow(dead_code)]
85 field_widget_map: HashMap<String, Vec<ObjectId>>, #[allow(dead_code)]
87 field_id_map: HashMap<String, ObjectId>, form_field_ids: Vec<ObjectId>, page_ids: Vec<ObjectId>, config: WriterConfig,
92 document_used_chars: Option<std::collections::HashSet<char>>,
94 buffered_objects: HashMap<ObjectId, Vec<u8>>,
96 compressed_object_map: HashMap<ObjectId, (ObjectId, u32)>, prev_xref_offset: Option<u64>,
99 base_pdf_size: Option<u64>,
100 encrypt_obj_id: Option<ObjectId>,
102 file_id: Option<Vec<u8>>,
103 encryption_state: Option<WriterEncryptionState>,
104 pending_encrypt_dict: Option<Dictionary>,
105}
106
107struct WriterEncryptionState {
109 encryptor: crate::encryption::ObjectEncryptor,
110}
111
112impl<W: Write> PdfWriter<W> {
113 pub fn new_with_writer(writer: W) -> Self {
114 Self::with_config(writer, WriterConfig::default())
115 }
116
117 pub fn with_config(writer: W, config: WriterConfig) -> Self {
118 Self {
119 writer,
120 xref_positions: HashMap::new(),
121 current_position: 0,
122 next_object_id: 1, catalog_id: None,
124 pages_id: None,
125 info_id: None,
126 field_widget_map: HashMap::new(),
127 field_id_map: HashMap::new(),
128 form_field_ids: Vec::new(),
129 page_ids: Vec::new(),
130 config,
131 document_used_chars: None,
132 buffered_objects: HashMap::new(),
133 compressed_object_map: HashMap::new(),
134 prev_xref_offset: None,
135 base_pdf_size: None,
136 encrypt_obj_id: None,
137 file_id: None,
138 encryption_state: None,
139 pending_encrypt_dict: None,
140 }
141 }
142
143 pub fn write_document(&mut self, document: &mut Document) -> Result<()> {
144 if !document.used_characters.is_empty() {
146 self.document_used_chars = Some(document.used_characters.clone());
147 }
148
149 self.write_header()?;
150
151 self.catalog_id = Some(self.allocate_object_id());
153 self.pages_id = Some(self.allocate_object_id());
154 self.info_id = Some(self.allocate_object_id());
155
156 if let Some(ref encryption) = document.encryption {
159 self.init_encryption(encryption)?;
160 }
161
162 let font_refs = self.write_fonts(document)?;
164
165 self.write_pages(document, &font_refs)?;
167
168 self.write_form_fields(document)?;
170
171 self.write_catalog(document)?;
173
174 self.write_info(document)?;
176
177 self.write_encryption_dict()?;
179
180 if self.config.use_object_streams {
182 self.flush_object_streams()?;
183 }
184
185 let xref_position = self.current_position;
187 if self.config.use_xref_streams {
188 self.write_xref_stream()?;
189 } else {
190 self.write_xref()?;
191 }
192
193 if !self.config.use_xref_streams {
195 self.write_trailer(xref_position)?;
196 }
197
198 if let Ok(()) = self.writer.flush() {
199 }
201 Ok(())
202 }
203
204 pub fn write_incremental_update(
238 &mut self,
239 base_pdf_path: impl AsRef<std::path::Path>,
240 document: &mut Document,
241 ) -> Result<()> {
242 use std::io::{BufReader, Read, Seek, SeekFrom};
243
244 let base_pdf_file = std::fs::File::open(base_pdf_path.as_ref())?;
246 let mut pdf_reader = crate::parser::PdfReader::new(BufReader::new(base_pdf_file))?;
247
248 let base_catalog = pdf_reader.catalog()?;
250
251 let (base_pages_id, base_pages_gen) = base_catalog
253 .get("Pages")
254 .and_then(|obj| {
255 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
256 Some((*id, *gen))
257 } else {
258 None
259 }
260 })
261 .ok_or_else(|| {
262 crate::error::PdfError::InvalidStructure(
263 "Base PDF catalog missing /Pages reference".to_string(),
264 )
265 })?;
266
267 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
269 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
270 base_pages_obj
271 {
272 dict.get("Kids")
273 .and_then(|obj| {
274 if let crate::parser::objects::PdfObject::Array(arr) = obj {
275 Some(
278 arr.0
279 .iter()
280 .filter_map(|item| {
281 if let crate::parser::objects::PdfObject::Reference(id, gen) =
282 item
283 {
284 Some(crate::objects::Object::Reference(
285 crate::objects::ObjectId::new(*id, *gen),
286 ))
287 } else {
288 None
289 }
290 })
291 .collect::<Vec<_>>(),
292 )
293 } else {
294 None
295 }
296 })
297 .unwrap_or_default()
298 } else {
299 Vec::new()
300 };
301
302 let base_page_count = base_pages_kids.len();
304
305 let base_pdf = std::fs::File::open(base_pdf_path.as_ref())?;
307 let mut base_reader = BufReader::new(base_pdf);
308
309 base_reader.seek(SeekFrom::End(-100))?;
311 let mut end_buffer = vec![0u8; 100];
312 let bytes_read = base_reader.read(&mut end_buffer)?;
313 end_buffer.truncate(bytes_read);
314
315 let end_str = String::from_utf8_lossy(&end_buffer);
316 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
317 let after_startxref = &end_str[startxref_pos + 9..];
318
319 let number_str: String = after_startxref
320 .chars()
321 .skip_while(|c| c.is_whitespace())
322 .take_while(|c| c.is_ascii_digit())
323 .collect();
324
325 number_str.parse::<u64>().map_err(|_| {
326 crate::error::PdfError::InvalidStructure(
327 "Could not parse startxref offset".to_string(),
328 )
329 })?
330 } else {
331 return Err(crate::error::PdfError::InvalidStructure(
332 "startxref not found in base PDF".to_string(),
333 ));
334 };
335
336 base_reader.seek(SeekFrom::Start(0))?;
338 let base_size = std::io::copy(&mut base_reader, &mut self.writer)? as u64;
339
340 self.prev_xref_offset = Some(prev_xref);
342 self.base_pdf_size = Some(base_size);
343 self.current_position = base_size;
344
345 if !document.used_characters.is_empty() {
347 self.document_used_chars = Some(document.used_characters.clone());
348 }
349
350 self.catalog_id = Some(self.allocate_object_id());
352 self.pages_id = Some(self.allocate_object_id());
353 self.info_id = Some(self.allocate_object_id());
354
355 let font_refs = self.write_fonts(document)?;
357
358 self.write_pages(document, &font_refs)?;
360
361 self.write_form_fields(document)?;
363
364 let catalog_id = self.get_catalog_id()?;
366 let new_pages_id = self.get_pages_id()?;
367
368 let mut catalog = crate::objects::Dictionary::new();
369 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
370 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
371
372 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
377
378 let mut all_pages_kids = base_pages_kids;
380
381 for page_id in &self.page_ids {
383 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
384 }
385
386 let mut pages_dict = crate::objects::Dictionary::new();
387 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
388 pages_dict.set("Kids", crate::objects::Object::Array(all_pages_kids));
389 pages_dict.set(
390 "Count",
391 crate::objects::Object::Integer((base_page_count + self.page_ids.len()) as i64),
392 );
393
394 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
395
396 self.write_info(document)?;
398
399 let xref_position = self.current_position;
401 self.write_xref()?;
402
403 self.write_trailer(xref_position)?;
405
406 self.writer.flush()?;
407 Ok(())
408 }
409
410 pub fn write_incremental_with_page_replacement(
476 &mut self,
477 base_pdf_path: impl AsRef<std::path::Path>,
478 document: &mut Document,
479 ) -> Result<()> {
480 use std::io::Cursor;
481
482 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
484 let base_size = base_pdf_bytes.len() as u64;
485
486 let mut pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
488
489 let base_catalog = pdf_reader.catalog()?;
490
491 let (base_pages_id, base_pages_gen) = base_catalog
492 .get("Pages")
493 .and_then(|obj| {
494 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
495 Some((*id, *gen))
496 } else {
497 None
498 }
499 })
500 .ok_or_else(|| {
501 crate::error::PdfError::InvalidStructure(
502 "Base PDF catalog missing /Pages reference".to_string(),
503 )
504 })?;
505
506 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
507 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
508 base_pages_obj
509 {
510 dict.get("Kids")
511 .and_then(|obj| {
512 if let crate::parser::objects::PdfObject::Array(arr) = obj {
513 Some(
514 arr.0
515 .iter()
516 .filter_map(|item| {
517 if let crate::parser::objects::PdfObject::Reference(id, gen) =
518 item
519 {
520 Some(crate::objects::Object::Reference(
521 crate::objects::ObjectId::new(*id, *gen),
522 ))
523 } else {
524 None
525 }
526 })
527 .collect::<Vec<_>>(),
528 )
529 } else {
530 None
531 }
532 })
533 .unwrap_or_default()
534 } else {
535 Vec::new()
536 };
537
538 let base_page_count = base_pages_kids.len();
539
540 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
542 let end_bytes = &base_pdf_bytes[start_search..];
543 let end_str = String::from_utf8_lossy(end_bytes);
544
545 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
546 let after_startxref = &end_str[startxref_pos + 9..];
547 let number_str: String = after_startxref
548 .chars()
549 .skip_while(|c| c.is_whitespace())
550 .take_while(|c| c.is_ascii_digit())
551 .collect();
552
553 number_str.parse::<u64>().map_err(|_| {
554 crate::error::PdfError::InvalidStructure(
555 "Could not parse startxref offset".to_string(),
556 )
557 })?
558 } else {
559 return Err(crate::error::PdfError::InvalidStructure(
560 "startxref not found in base PDF".to_string(),
561 ));
562 };
563
564 self.writer.write_all(&base_pdf_bytes)?;
566
567 self.prev_xref_offset = Some(prev_xref);
568 self.base_pdf_size = Some(base_size);
569 self.current_position = base_size;
570
571 if !document.used_characters.is_empty() {
573 self.document_used_chars = Some(document.used_characters.clone());
574 }
575
576 self.catalog_id = Some(self.allocate_object_id());
577 self.pages_id = Some(self.allocate_object_id());
578 self.info_id = Some(self.allocate_object_id());
579
580 let font_refs = self.write_fonts(document)?;
581 self.write_pages(document, &font_refs)?;
582 self.write_form_fields(document)?;
583
584 let catalog_id = self.get_catalog_id()?;
586 let new_pages_id = self.get_pages_id()?;
587
588 let mut catalog = crate::objects::Dictionary::new();
589 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
590 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
591 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
592
593 let mut all_pages_kids = Vec::new();
595 let replacement_count = document.pages.len();
596
597 for page_id in &self.page_ids {
599 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
600 }
601
602 if replacement_count < base_page_count {
604 for i in replacement_count..base_page_count {
605 if let Some(page_ref) = base_pages_kids.get(i) {
606 all_pages_kids.push(page_ref.clone());
607 }
608 }
609 }
610
611 let mut pages_dict = crate::objects::Dictionary::new();
612 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
613 pages_dict.set(
614 "Kids",
615 crate::objects::Object::Array(all_pages_kids.clone()),
616 );
617 pages_dict.set(
618 "Count",
619 crate::objects::Object::Integer(all_pages_kids.len() as i64),
620 );
621
622 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
623 self.write_info(document)?;
624
625 let xref_position = self.current_position;
626 self.write_xref()?;
627 self.write_trailer(xref_position)?;
628
629 self.writer.flush()?;
630 Ok(())
631 }
632
633 pub fn write_incremental_with_overlay<P: AsRef<std::path::Path>>(
681 &mut self,
682 base_pdf_path: P,
683 mut overlay_fn: impl FnMut(&mut crate::Page) -> Result<()>,
684 ) -> Result<()> {
685 use std::io::Cursor;
686
687 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
689 let base_size = base_pdf_bytes.len() as u64;
690
691 let pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
693 let parsed_doc = crate::parser::PdfDocument::new(pdf_reader);
694
695 let page_count = parsed_doc.page_count()?;
697
698 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
700 let end_bytes = &base_pdf_bytes[start_search..];
701 let end_str = String::from_utf8_lossy(end_bytes);
702
703 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
704 let after_startxref = &end_str[startxref_pos + 9..];
705 let number_str: String = after_startxref
706 .chars()
707 .skip_while(|c| c.is_whitespace())
708 .take_while(|c| c.is_ascii_digit())
709 .collect();
710
711 number_str.parse::<u64>().map_err(|_| {
712 crate::error::PdfError::InvalidStructure(
713 "Could not parse startxref offset".to_string(),
714 )
715 })?
716 } else {
717 return Err(crate::error::PdfError::InvalidStructure(
718 "startxref not found in base PDF".to_string(),
719 ));
720 };
721
722 self.writer.write_all(&base_pdf_bytes)?;
724
725 self.prev_xref_offset = Some(prev_xref);
726 self.base_pdf_size = Some(base_size);
727 self.current_position = base_size;
728
729 let mut temp_doc = crate::Document::new();
731
732 for page_idx in 0..page_count {
733 let parsed_page = parsed_doc.get_page(page_idx)?;
735 let mut writable_page =
736 crate::Page::from_parsed_with_content(&parsed_page, &parsed_doc)?;
737
738 overlay_fn(&mut writable_page)?;
740
741 temp_doc.add_page(writable_page);
743 }
744
745 if !temp_doc.used_characters.is_empty() {
748 self.document_used_chars = Some(temp_doc.used_characters.clone());
749 }
750
751 self.catalog_id = Some(self.allocate_object_id());
752 self.pages_id = Some(self.allocate_object_id());
753 self.info_id = Some(self.allocate_object_id());
754
755 let font_refs = self.write_fonts(&temp_doc)?;
756 self.write_pages(&temp_doc, &font_refs)?;
757 self.write_form_fields(&mut temp_doc)?;
758
759 let catalog_id = self.get_catalog_id()?;
761 let new_pages_id = self.get_pages_id()?;
762
763 let mut catalog = crate::objects::Dictionary::new();
764 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
765 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
766 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
767
768 let mut all_pages_kids = Vec::new();
770 for page_id in &self.page_ids {
771 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
772 }
773
774 let mut pages_dict = crate::objects::Dictionary::new();
775 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
776 pages_dict.set(
777 "Kids",
778 crate::objects::Object::Array(all_pages_kids.clone()),
779 );
780 pages_dict.set(
781 "Count",
782 crate::objects::Object::Integer(all_pages_kids.len() as i64),
783 );
784
785 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
786 self.write_info(&temp_doc)?;
787
788 let xref_position = self.current_position;
789 self.write_xref()?;
790 self.write_trailer(xref_position)?;
791
792 self.writer.flush()?;
793 Ok(())
794 }
795
796 fn write_header(&mut self) -> Result<()> {
797 let header = format!("%PDF-{}\n", self.config.pdf_version);
798 self.write_bytes(header.as_bytes())?;
799 self.write_bytes(&[b'%', 0xE2, 0xE3, 0xCF, 0xD3, b'\n'])?;
801 Ok(())
802 }
803
804 fn convert_pdf_objects_dict_to_writer(
807 &self,
808 pdf_dict: &crate::pdf_objects::Dictionary,
809 ) -> crate::objects::Dictionary {
810 let mut writer_dict = crate::objects::Dictionary::new();
811
812 for (key, value) in pdf_dict.iter() {
813 let writer_obj = self.convert_pdf_object_to_writer(value);
814 writer_dict.set(key.as_str(), writer_obj);
815 }
816
817 writer_dict
818 }
819
820 fn convert_pdf_object_to_writer(
821 &self,
822 obj: &crate::pdf_objects::Object,
823 ) -> crate::objects::Object {
824 use crate::objects::Object as WriterObj;
825 use crate::pdf_objects::Object as PdfObj;
826
827 match obj {
828 PdfObj::Null => WriterObj::Null,
829 PdfObj::Boolean(b) => WriterObj::Boolean(*b),
830 PdfObj::Integer(i) => WriterObj::Integer(*i),
831 PdfObj::Real(f) => WriterObj::Real(*f),
832 PdfObj::String(s) => {
833 WriterObj::String(String::from_utf8_lossy(s.as_bytes()).to_string())
834 }
835 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
836 PdfObj::Array(arr) => {
837 let items: Vec<WriterObj> = arr
838 .iter()
839 .map(|item| self.convert_pdf_object_to_writer(item))
840 .collect();
841 WriterObj::Array(items)
842 }
843 PdfObj::Dictionary(dict) => {
844 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
845 }
846 PdfObj::Stream(stream) => {
847 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
848 WriterObj::Stream(dict, stream.data.clone())
849 }
850 PdfObj::Reference(id) => {
851 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
852 }
853 }
854 }
855
856 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
857 let catalog_id = self.get_catalog_id()?;
858 let pages_id = self.get_pages_id()?;
859
860 let mut catalog = Dictionary::new();
861 catalog.set("Type", Object::Name("Catalog".to_string()));
862 catalog.set("Pages", Object::Reference(pages_id));
863
864 if let Some(_form_manager) = &document.form_manager {
867 if document.acro_form.is_none() {
869 document.acro_form = Some(crate::forms::AcroForm::new());
870 }
871 }
872
873 if let Some(acro_form) = &document.acro_form {
875 let acro_form_id = self.allocate_object_id();
877
878 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
880
881 catalog.set("AcroForm", Object::Reference(acro_form_id));
883 }
884
885 if let Some(outline_tree) = &document.outline {
887 if !outline_tree.items.is_empty() {
888 let outline_root_id = self.write_outline_tree(outline_tree)?;
889 catalog.set("Outlines", Object::Reference(outline_root_id));
890 }
891 }
892
893 if let Some(struct_tree) = &document.struct_tree {
895 if !struct_tree.is_empty() {
896 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
897 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
898 catalog.set("MarkInfo", {
900 let mut mark_info = Dictionary::new();
901 mark_info.set("Marked", Object::Boolean(true));
902 Object::Dictionary(mark_info)
903 });
904 }
905 }
906
907 let xmp_metadata = document.create_xmp_metadata();
910 let xmp_packet = xmp_metadata.to_xmp_packet();
911 let metadata_id = self.allocate_object_id();
912
913 let mut metadata_dict = Dictionary::new();
915 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
916 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
917 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
918
919 self.write_object(
921 metadata_id,
922 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
923 )?;
924
925 catalog.set("Metadata", Object::Reference(metadata_id));
927
928 self.write_object(catalog_id, Object::Dictionary(catalog))?;
929 Ok(())
930 }
931
932 fn write_page_content(&mut self, content_id: ObjectId, page: &crate::page::Page) -> Result<()> {
933 let mut page_copy = page.clone();
934 let content = page_copy.generate_content()?;
935
936 #[cfg(feature = "compression")]
938 {
939 use crate::objects::Stream;
940 let mut stream = Stream::new(content);
941 if self.config.compress_streams {
943 stream.compress_flate()?;
944 }
945
946 self.write_object(
947 content_id,
948 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
949 )?;
950 }
951
952 #[cfg(not(feature = "compression"))]
953 {
954 let mut stream_dict = Dictionary::new();
955 stream_dict.set("Length", Object::Integer(content.len() as i64));
956
957 self.write_object(content_id, Object::Stream(stream_dict, content))?;
958 }
959
960 Ok(())
961 }
962
963 fn write_outline_tree(
964 &mut self,
965 outline_tree: &crate::structure::OutlineTree,
966 ) -> Result<ObjectId> {
967 let outline_root_id = self.allocate_object_id();
969
970 let mut outline_root = Dictionary::new();
971 outline_root.set("Type", Object::Name("Outlines".to_string()));
972
973 if !outline_tree.items.is_empty() {
974 let mut item_ids = Vec::new();
976
977 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
979 let mut count = items.len();
980 for item in items {
981 count += count_items(&item.children);
982 }
983 count
984 }
985
986 let total_items = count_items(&outline_tree.items);
987
988 for _ in 0..total_items {
990 item_ids.push(self.allocate_object_id());
991 }
992
993 let mut id_index = 0;
994
995 let first_id = item_ids[0];
997 let last_id = item_ids[outline_tree.items.len() - 1];
998
999 outline_root.set("First", Object::Reference(first_id));
1000 outline_root.set("Last", Object::Reference(last_id));
1001
1002 let visible_count = outline_tree.visible_count();
1004 outline_root.set("Count", Object::Integer(visible_count));
1005
1006 let mut written_items = Vec::new();
1008
1009 for (i, item) in outline_tree.items.iter().enumerate() {
1010 let item_id = item_ids[id_index];
1011 id_index += 1;
1012
1013 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1014 let next_id = if i < outline_tree.items.len() - 1 {
1015 Some(item_ids[i + 1])
1016 } else {
1017 None
1018 };
1019
1020 let children_ids = self.write_outline_item(
1022 item,
1023 item_id,
1024 outline_root_id,
1025 prev_id,
1026 next_id,
1027 &mut item_ids,
1028 &mut id_index,
1029 )?;
1030
1031 written_items.extend(children_ids);
1032 }
1033 }
1034
1035 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1036 Ok(outline_root_id)
1037 }
1038
1039 #[allow(clippy::too_many_arguments)]
1040 fn write_outline_item(
1041 &mut self,
1042 item: &crate::structure::OutlineItem,
1043 item_id: ObjectId,
1044 parent_id: ObjectId,
1045 prev_id: Option<ObjectId>,
1046 next_id: Option<ObjectId>,
1047 all_ids: &mut Vec<ObjectId>,
1048 id_index: &mut usize,
1049 ) -> Result<Vec<ObjectId>> {
1050 let mut written_ids = vec![item_id];
1051
1052 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1054 let first_idx = *id_index;
1055 let first_id = all_ids[first_idx];
1056 let last_idx = first_idx + item.children.len() - 1;
1057 let last_id = all_ids[last_idx];
1058
1059 for (i, child) in item.children.iter().enumerate() {
1061 let child_id = all_ids[*id_index];
1062 *id_index += 1;
1063
1064 let child_prev = if i > 0 {
1065 Some(all_ids[first_idx + i - 1])
1066 } else {
1067 None
1068 };
1069 let child_next = if i < item.children.len() - 1 {
1070 Some(all_ids[first_idx + i + 1])
1071 } else {
1072 None
1073 };
1074
1075 let child_ids = self.write_outline_item(
1076 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1078 )?;
1079
1080 written_ids.extend(child_ids);
1081 }
1082
1083 (Some(first_id), Some(last_id))
1084 } else {
1085 (None, None)
1086 };
1087
1088 let item_dict = crate::structure::outline_item_to_dict(
1090 item,
1091 parent_id,
1092 first_child_id,
1093 last_child_id,
1094 prev_id,
1095 next_id,
1096 );
1097
1098 self.write_object(item_id, Object::Dictionary(item_dict))?;
1099
1100 Ok(written_ids)
1101 }
1102
1103 fn write_struct_tree(
1105 &mut self,
1106 struct_tree: &crate::structure::StructTree,
1107 ) -> Result<ObjectId> {
1108 let struct_tree_root_id = self.allocate_object_id();
1110 let mut element_ids = Vec::new();
1111 for _ in 0..struct_tree.len() {
1112 element_ids.push(self.allocate_object_id());
1113 }
1114
1115 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1117 std::collections::HashMap::new();
1118
1119 if let Some(root_index) = struct_tree.root_index() {
1121 parent_map.insert(root_index, struct_tree_root_id);
1122
1123 fn map_children_parents(
1125 tree: &crate::structure::StructTree,
1126 parent_index: usize,
1127 parent_id: ObjectId,
1128 element_ids: &[ObjectId],
1129 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1130 ) {
1131 if let Some(parent_elem) = tree.get(parent_index) {
1132 for &child_index in &parent_elem.children {
1133 parent_map.insert(child_index, parent_id);
1134 map_children_parents(
1135 tree,
1136 child_index,
1137 element_ids[child_index],
1138 element_ids,
1139 parent_map,
1140 );
1141 }
1142 }
1143 }
1144
1145 map_children_parents(
1146 struct_tree,
1147 root_index,
1148 element_ids[root_index],
1149 &element_ids,
1150 &mut parent_map,
1151 );
1152 }
1153
1154 for (index, element) in struct_tree.iter().enumerate() {
1156 let element_id = element_ids[index];
1157 let mut element_dict = Dictionary::new();
1158
1159 element_dict.set("Type", Object::Name("StructElem".to_string()));
1160 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1161
1162 if let Some(&parent_id) = parent_map.get(&index) {
1164 element_dict.set("P", Object::Reference(parent_id));
1165 }
1166
1167 if let Some(ref id) = element.id {
1169 element_dict.set("ID", Object::String(id.clone()));
1170 }
1171
1172 if let Some(ref lang) = element.attributes.lang {
1174 element_dict.set("Lang", Object::String(lang.clone()));
1175 }
1176 if let Some(ref alt) = element.attributes.alt {
1177 element_dict.set("Alt", Object::String(alt.clone()));
1178 }
1179 if let Some(ref actual_text) = element.attributes.actual_text {
1180 element_dict.set("ActualText", Object::String(actual_text.clone()));
1181 }
1182 if let Some(ref title) = element.attributes.title {
1183 element_dict.set("T", Object::String(title.clone()));
1184 }
1185 if let Some(bbox) = element.attributes.bbox {
1186 element_dict.set(
1187 "BBox",
1188 Object::Array(vec![
1189 Object::Real(bbox[0]),
1190 Object::Real(bbox[1]),
1191 Object::Real(bbox[2]),
1192 Object::Real(bbox[3]),
1193 ]),
1194 );
1195 }
1196
1197 let mut kids = Vec::new();
1199
1200 for &child_index in &element.children {
1202 kids.push(Object::Reference(element_ids[child_index]));
1203 }
1204
1205 for mcid_ref in &element.mcids {
1207 let mut mcr = Dictionary::new();
1208 mcr.set("Type", Object::Name("MCR".to_string()));
1209 mcr.set("Pg", Object::Integer(mcid_ref.page_index as i64));
1210 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1211 kids.push(Object::Dictionary(mcr));
1212 }
1213
1214 if !kids.is_empty() {
1215 element_dict.set("K", Object::Array(kids));
1216 }
1217
1218 self.write_object(element_id, Object::Dictionary(element_dict))?;
1219 }
1220
1221 let mut struct_tree_root = Dictionary::new();
1223 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1224
1225 if let Some(root_index) = struct_tree.root_index() {
1227 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1228 }
1229
1230 if !struct_tree.role_map.mappings().is_empty() {
1232 let mut role_map = Dictionary::new();
1233 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1234 role_map.set(
1235 custom_type.as_str(),
1236 Object::Name(standard_type.as_pdf_name().to_string()),
1237 );
1238 }
1239 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1240 }
1241
1242 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1243 Ok(struct_tree_root_id)
1244 }
1245
1246 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1247 if !self.form_field_ids.is_empty() {
1249 if let Some(acro_form) = &mut document.acro_form {
1250 acro_form.fields.clear();
1252 for field_id in &self.form_field_ids {
1253 acro_form.add_field(*field_id);
1254 }
1255
1256 acro_form.need_appearances = true;
1258 if acro_form.da.is_none() {
1259 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1260 }
1261 }
1262 }
1263 Ok(())
1264 }
1265
1266 fn write_info(&mut self, document: &Document) -> Result<()> {
1267 let info_id = self.get_info_id()?;
1268 let mut info_dict = Dictionary::new();
1269
1270 if let Some(ref title) = document.metadata.title {
1271 info_dict.set("Title", Object::String(title.clone()));
1272 }
1273 if let Some(ref author) = document.metadata.author {
1274 info_dict.set("Author", Object::String(author.clone()));
1275 }
1276 if let Some(ref subject) = document.metadata.subject {
1277 info_dict.set("Subject", Object::String(subject.clone()));
1278 }
1279 if let Some(ref keywords) = document.metadata.keywords {
1280 info_dict.set("Keywords", Object::String(keywords.clone()));
1281 }
1282 if let Some(ref creator) = document.metadata.creator {
1283 info_dict.set("Creator", Object::String(creator.clone()));
1284 }
1285 if let Some(ref producer) = document.metadata.producer {
1286 info_dict.set("Producer", Object::String(producer.clone()));
1287 }
1288
1289 if let Some(creation_date) = document.metadata.creation_date {
1291 let date_string = format_pdf_date(creation_date);
1292 info_dict.set("CreationDate", Object::String(date_string));
1293 }
1294
1295 if let Some(mod_date) = document.metadata.modification_date {
1297 let date_string = format_pdf_date(mod_date);
1298 info_dict.set("ModDate", Object::String(date_string));
1299 }
1300
1301 let edition = super::Edition::OpenSource;
1304
1305 let signature = super::PdfSignature::new(document, edition);
1306 signature.write_to_info_dict(&mut info_dict);
1307
1308 self.write_object(info_id, Object::Dictionary(info_dict))?;
1309 Ok(())
1310 }
1311
1312 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1313 let mut font_refs = HashMap::new();
1314
1315 for font_name in document.custom_font_names() {
1317 if let Some(font) = document.get_custom_font(&font_name) {
1318 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1321 font_refs.insert(font_name.clone(), font_id);
1322 }
1323 }
1324
1325 Ok(font_refs)
1326 }
1327
1328 fn write_font_with_unicode_support(
1330 &mut self,
1331 font_name: &str,
1332 font: &crate::fonts::Font,
1333 ) -> Result<ObjectId> {
1334 self.write_type0_font_from_font(font_name, font)
1337 }
1338
1339 fn write_type0_font_from_font(
1341 &mut self,
1342 font_name: &str,
1343 font: &crate::fonts::Font,
1344 ) -> Result<ObjectId> {
1345 let used_chars = self.document_used_chars.clone().unwrap_or_else(|| {
1347 let mut chars = std::collections::HashSet::new();
1349 for ch in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1350 {
1351 chars.insert(ch);
1352 }
1353 chars
1354 });
1355 let font_id = self.allocate_object_id();
1357 let descendant_font_id = self.allocate_object_id();
1358 let descriptor_id = self.allocate_object_id();
1359 let font_file_id = self.allocate_object_id();
1360 let to_unicode_id = self.allocate_object_id();
1361
1362 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths, embed_as_raw_cff) =
1366 if font.data.len() > 100_000 && !used_chars.is_empty() {
1367 match crate::text::fonts::truetype_subsetter::subset_font(
1368 font.data.clone(),
1369 &used_chars,
1370 ) {
1371 Ok(subset_result) => (
1372 subset_result.font_data,
1373 Some(subset_result.glyph_mapping),
1374 font.clone(),
1375 subset_result.is_raw_cff,
1376 ),
1377 Err(_) => {
1378 if font.data.len() < 25_000_000 {
1379 (font.data.clone(), None, font.clone(), false)
1380 } else {
1381 (Vec::new(), None, font.clone(), false)
1382 }
1383 }
1384 }
1385 } else {
1386 (font.data.clone(), None, font.clone(), false)
1387 };
1388
1389 if !font_data_to_embed.is_empty() {
1390 let mut font_file_dict = Dictionary::new();
1391 if embed_as_raw_cff {
1392 font_file_dict.set("Subtype", Object::Name("CIDFontType0C".to_string()));
1395 } else {
1396 match font.format {
1397 crate::fonts::FontFormat::OpenType => {
1398 font_file_dict.set("Subtype", Object::Name("OpenType".to_string()));
1399 font_file_dict
1400 .set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1401 }
1402 crate::fonts::FontFormat::TrueType => {
1403 font_file_dict
1404 .set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1405 }
1406 }
1407 }
1408 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1409 self.write_object(font_file_id, font_stream_obj)?;
1410 } else {
1411 let font_file_dict = Dictionary::new();
1413 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1414 self.write_object(font_file_id, font_stream_obj)?;
1415 }
1416
1417 let mut descriptor = Dictionary::new();
1419 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1420 descriptor.set("FontName", Object::Name(font_name.to_string()));
1421 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1423 "FontBBox",
1424 Object::Array(vec![
1425 Object::Integer(font.descriptor.font_bbox[0] as i64),
1426 Object::Integer(font.descriptor.font_bbox[1] as i64),
1427 Object::Integer(font.descriptor.font_bbox[2] as i64),
1428 Object::Integer(font.descriptor.font_bbox[3] as i64),
1429 ]),
1430 );
1431 descriptor.set(
1432 "ItalicAngle",
1433 Object::Real(font.descriptor.italic_angle as f64),
1434 );
1435 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1436 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1437 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1438 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1439 let font_file_key = match font.format {
1441 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1444 descriptor.set(font_file_key, Object::Reference(font_file_id));
1445 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1446
1447 let mut cid_font = Dictionary::new();
1449 cid_font.set("Type", Object::Name("Font".to_string()));
1450 let is_cff = matches!(font.format, crate::fonts::FontFormat::OpenType);
1452 let cid_font_subtype = if CjkFontType::should_use_cidfonttype2(is_cff) {
1453 "CIDFontType2" } else {
1455 "CIDFontType0" };
1457 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1458 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1459
1460 let mut cid_system_info = Dictionary::new();
1462 let (registry, ordering, supplement) =
1463 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1464 cjk_type.cid_system_info()
1465 } else {
1466 ("Adobe", "Identity", 0)
1467 };
1468
1469 cid_system_info.set("Registry", Object::String(registry.to_string()));
1470 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1471 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1472 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1473
1474 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1475
1476 let default_width = self.calculate_default_width(font);
1478 cid_font.set("DW", Object::Integer(default_width));
1479
1480 let w_array = self.generate_width_array(
1484 &original_font_for_widths,
1485 default_width,
1486 subset_glyph_mapping.as_ref(),
1487 );
1488 cid_font.set("W", Object::Array(w_array));
1489
1490 if cid_font_subtype == "CIDFontType2" {
1494 let cid_to_gid_map =
1496 self.generate_cid_to_gid_map(font, subset_glyph_mapping.as_ref())?;
1497 if !cid_to_gid_map.is_empty() {
1498 let cid_to_gid_map_id = self.allocate_object_id();
1500 let mut map_dict = Dictionary::new();
1501 map_dict.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1502 let map_stream = Object::Stream(map_dict, cid_to_gid_map);
1503 self.write_object(cid_to_gid_map_id, map_stream)?;
1504 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1505 } else {
1506 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1507 }
1508 }
1509 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1512
1513 let cmap_data = self.generate_tounicode_cmap_from_font(font);
1515 let cmap_dict = Dictionary::new();
1516 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1517 self.write_object(to_unicode_id, cmap_stream)?;
1518
1519 let mut type0_font = Dictionary::new();
1521 type0_font.set("Type", Object::Name("Font".to_string()));
1522 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1523 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1524 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1525 type0_font.set(
1526 "DescendantFonts",
1527 Object::Array(vec![Object::Reference(descendant_font_id)]),
1528 );
1529 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1530
1531 self.write_object(font_id, Object::Dictionary(type0_font))?;
1532
1533 Ok(font_id)
1534 }
1535
1536 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
1538 use crate::text::fonts::truetype::TrueTypeFont;
1539
1540 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1542 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1543 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
1544 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
1545 let common_chars =
1549 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
1550 let mut total_width = 0;
1551 let mut count = 0;
1552
1553 for ch in common_chars.chars() {
1554 let unicode = ch as u32;
1555 if let Some(&pdf_width) = widths.get(&unicode) {
1556 total_width += pdf_width as i64;
1557 count += 1;
1558 }
1559 }
1560
1561 if count > 0 {
1562 return total_width / count;
1563 }
1564 }
1565 }
1566 }
1567 }
1568
1569 500
1571 }
1572
1573 fn generate_width_array(
1575 &self,
1576 font: &crate::fonts::Font,
1577 _default_width: i64,
1578 subset_mapping: Option<&HashMap<u32, u16>>,
1579 ) -> Vec<Object> {
1580 use crate::text::fonts::truetype::TrueTypeFont;
1581
1582 let mut w_array = Vec::new();
1583
1584 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1586 let char_to_glyph = {
1590 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1592 if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
1593 if let Some(subset_map) = subset_mapping {
1595 let mut filtered = HashMap::new();
1596 for unicode in subset_map.keys() {
1597 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
1599 filtered.insert(*unicode, orig_glyph);
1600 }
1601 }
1602 filtered
1603 } else {
1604 cmap.mappings.clone()
1605 }
1606 } else {
1607 HashMap::new()
1608 }
1609 } else {
1610 HashMap::new()
1611 }
1612 };
1613
1614 if !char_to_glyph.is_empty() {
1615 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
1617 let mut sorted_chars: Vec<_> = widths.iter().collect();
1622 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
1623
1624 let mut i = 0;
1625 while i < sorted_chars.len() {
1626 let start_unicode = *sorted_chars[i].0;
1627 let pdf_width = *sorted_chars[i].1 as i64;
1629
1630 let mut end_unicode = start_unicode;
1632 let mut j = i + 1;
1633 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
1634 let next_pdf_width = *sorted_chars[j].1 as i64;
1635 if next_pdf_width == pdf_width {
1636 end_unicode = *sorted_chars[j].0;
1637 j += 1;
1638 } else {
1639 break;
1640 }
1641 }
1642
1643 if start_unicode == end_unicode {
1645 w_array.push(Object::Integer(start_unicode as i64));
1647 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
1648 } else {
1649 w_array.push(Object::Integer(start_unicode as i64));
1651 w_array.push(Object::Integer(end_unicode as i64));
1652 w_array.push(Object::Integer(pdf_width));
1653 }
1654
1655 i = j;
1656 }
1657
1658 return w_array;
1659 }
1660 }
1661 }
1662
1663 let ranges = vec![
1665 (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), ];
1702
1703 for (start, end, width) in ranges {
1705 if start == end {
1706 w_array.push(Object::Integer(start));
1708 w_array.push(Object::Array(vec![Object::Integer(width)]));
1709 } else {
1710 w_array.push(Object::Integer(start));
1712 w_array.push(Object::Integer(end));
1713 w_array.push(Object::Integer(width));
1714 }
1715 }
1716
1717 w_array
1718 }
1719
1720 fn generate_cid_to_gid_map(
1722 &mut self,
1723 font: &crate::fonts::Font,
1724 subset_mapping: Option<&HashMap<u32, u16>>,
1725 ) -> Result<Vec<u8>> {
1726 use crate::text::fonts::truetype::TrueTypeFont;
1727
1728 let cmap_mappings = if let Some(subset_map) = subset_mapping {
1731 subset_map.clone()
1733 } else {
1734 let tt_font = TrueTypeFont::parse(font.data.clone())?;
1736 let cmap_tables = tt_font.parse_cmap()?;
1737
1738 let cmap = CmapSubtable::select_best_or_first(&cmap_tables).ok_or_else(|| {
1740 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
1741 })?;
1742
1743 cmap.mappings.clone()
1744 };
1745
1746 let used_chars = self.document_used_chars.clone().unwrap_or_default();
1753
1754 let max_unicode = if !used_chars.is_empty() {
1756 used_chars
1758 .iter()
1759 .map(|ch| *ch as u32)
1760 .max()
1761 .unwrap_or(0x00FF) .min(0xFFFF) as usize
1763 } else {
1764 cmap_mappings
1766 .keys()
1767 .max()
1768 .copied()
1769 .unwrap_or(0xFFFF)
1770 .min(0xFFFF) as usize
1771 };
1772
1773 let mut map = vec![0u8; (max_unicode + 1) * 2];
1775
1776 let mut sample_mappings = Vec::new();
1778 for (&unicode, &glyph_id) in &cmap_mappings {
1779 if unicode <= max_unicode as u32 {
1780 let idx = (unicode as usize) * 2;
1781 map[idx] = (glyph_id >> 8) as u8;
1783 map[idx + 1] = (glyph_id & 0xFF) as u8;
1784
1785 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
1787 {
1788 sample_mappings.push((unicode, glyph_id));
1789 }
1790 }
1791 }
1792
1793 Ok(map)
1794 }
1795
1796 fn generate_tounicode_cmap_from_font(&self, font: &crate::fonts::Font) -> Vec<u8> {
1798 use crate::text::fonts::truetype::TrueTypeFont;
1799
1800 let mut cmap = String::new();
1801
1802 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
1804 cmap.push_str("12 dict begin\n");
1805 cmap.push_str("begincmap\n");
1806 cmap.push_str("/CIDSystemInfo\n");
1807 cmap.push_str("<< /Registry (Adobe)\n");
1808 cmap.push_str(" /Ordering (UCS)\n");
1809 cmap.push_str(" /Supplement 0\n");
1810 cmap.push_str(">> def\n");
1811 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
1812 cmap.push_str("/CMapType 2 def\n");
1813 cmap.push_str("1 begincodespacerange\n");
1814 cmap.push_str("<0000> <FFFF>\n");
1815 cmap.push_str("endcodespacerange\n");
1816
1817 let mut mappings = Vec::new();
1819 let mut has_font_mappings = false;
1820
1821 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1822 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1823 if let Some(cmap_table) = CmapSubtable::select_best_or_first(&cmap_tables) {
1825 for (&unicode, &glyph_id) in &cmap_table.mappings {
1828 if glyph_id > 0 && unicode <= 0xFFFF {
1829 mappings.push((unicode, unicode));
1832 }
1833 }
1834 has_font_mappings = true;
1835 }
1836 }
1837 }
1838
1839 if !has_font_mappings {
1841 for i in 0x0020..=0x00FF {
1843 mappings.push((i, i));
1844 }
1845
1846 for i in 0x0100..=0x017F {
1848 mappings.push((i, i));
1849 }
1850
1851 for i in 0x3040..=0x309F {
1854 mappings.push((i, i));
1855 }
1856
1857 for i in 0x30A0..=0x30FF {
1859 mappings.push((i, i));
1860 }
1861
1862 for i in 0x4E00..=0x9FFF {
1864 mappings.push((i, i));
1865 }
1866
1867 for i in 0xAC00..=0xD7AF {
1869 mappings.push((i, i));
1870 }
1871
1872 for i in 0x2000..=0x206F {
1874 mappings.push((i, i));
1875 }
1876
1877 for i in 0x2200..=0x22FF {
1879 mappings.push((i, i));
1880 }
1881
1882 for i in 0x2190..=0x21FF {
1884 mappings.push((i, i));
1885 }
1886
1887 for i in 0x2500..=0x259F {
1889 mappings.push((i, i));
1890 }
1891
1892 for i in 0x25A0..=0x25FF {
1894 mappings.push((i, i));
1895 }
1896
1897 for i in 0x2600..=0x26FF {
1899 mappings.push((i, i));
1900 }
1901 }
1902
1903 mappings.sort_by_key(|&(cid, _)| cid);
1905
1906 let mut i = 0;
1908 while i < mappings.len() {
1909 let start_cid = mappings[i].0;
1911 let start_unicode = mappings[i].1;
1912 let mut end_idx = i;
1913
1914 while end_idx + 1 < mappings.len()
1916 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
1917 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
1918 && end_idx - i < 99
1919 {
1921 end_idx += 1;
1922 }
1923
1924 if end_idx > i {
1925 cmap.push_str("1 beginbfrange\n");
1927 cmap.push_str(&format!(
1928 "<{:04X}> <{:04X}> <{:04X}>\n",
1929 start_cid, mappings[end_idx].0, start_unicode
1930 ));
1931 cmap.push_str("endbfrange\n");
1932 i = end_idx + 1;
1933 } else {
1934 let mut chars = Vec::new();
1936 let chunk_end = (i + 100).min(mappings.len());
1937
1938 for item in &mappings[i..chunk_end] {
1939 chars.push(*item);
1940 }
1941
1942 if !chars.is_empty() {
1943 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
1944 for (cid, unicode) in chars {
1945 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
1946 }
1947 cmap.push_str("endbfchar\n");
1948 }
1949
1950 i = chunk_end;
1951 }
1952 }
1953
1954 cmap.push_str("endcmap\n");
1956 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
1957 cmap.push_str("end\n");
1958 cmap.push_str("end\n");
1959
1960 cmap.into_bytes()
1961 }
1962
1963 #[allow(dead_code)]
1965 fn write_truetype_font(
1966 &mut self,
1967 font_name: &str,
1968 font: &crate::text::font_manager::CustomFont,
1969 ) -> Result<ObjectId> {
1970 let font_id = self.allocate_object_id();
1972 let descriptor_id = self.allocate_object_id();
1973 let font_file_id = self.allocate_object_id();
1974
1975 if let Some(ref data) = font.font_data {
1977 let mut font_file_dict = Dictionary::new();
1978 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
1979 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
1980 self.write_object(font_file_id, font_stream_obj)?;
1981 }
1982
1983 let mut descriptor = Dictionary::new();
1985 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1986 descriptor.set("FontName", Object::Name(font_name.to_string()));
1987 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
1989 "FontBBox",
1990 Object::Array(vec![
1991 Object::Integer(-1000),
1992 Object::Integer(-1000),
1993 Object::Integer(2000),
1994 Object::Integer(2000),
1995 ]),
1996 );
1997 descriptor.set("ItalicAngle", Object::Integer(0));
1998 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
1999 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2000 descriptor.set(
2001 "CapHeight",
2002 Object::Integer(font.descriptor.cap_height as i64),
2003 );
2004 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2005 descriptor.set("FontFile2", Object::Reference(font_file_id));
2006 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2007
2008 let mut font_dict = Dictionary::new();
2010 font_dict.set("Type", Object::Name("Font".to_string()));
2011 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2012 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2013 font_dict.set("FirstChar", Object::Integer(0));
2014 font_dict.set("LastChar", Object::Integer(255));
2015
2016 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2018 font_dict.set("Widths", Object::Array(widths));
2019 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2020
2021 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2023
2024 self.write_object(font_id, Object::Dictionary(font_dict))?;
2025
2026 Ok(font_id)
2027 }
2028
2029 fn write_pages(
2030 &mut self,
2031 document: &Document,
2032 font_refs: &HashMap<String, ObjectId>,
2033 ) -> Result<()> {
2034 let pages_id = self.get_pages_id()?;
2035 let mut pages_dict = Dictionary::new();
2036 pages_dict.set("Type", Object::Name("Pages".to_string()));
2037 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2038
2039 let mut kids = Vec::new();
2040
2041 let mut page_ids = Vec::new();
2043 let mut content_ids = Vec::new();
2044 for _ in 0..document.pages.len() {
2045 page_ids.push(self.allocate_object_id());
2046 content_ids.push(self.allocate_object_id());
2047 }
2048
2049 for page_id in &page_ids {
2050 kids.push(Object::Reference(*page_id));
2051 }
2052
2053 pages_dict.set("Kids", Object::Array(kids));
2054
2055 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2056
2057 self.page_ids = page_ids.clone();
2059
2060 for (i, page) in document.pages.iter().enumerate() {
2062 let page_id = page_ids[i];
2063 let content_id = content_ids[i];
2064
2065 self.write_page_with_fonts(page_id, pages_id, content_id, page, document, font_refs)?;
2066 self.write_page_content(content_id, page)?;
2067 }
2068
2069 Ok(())
2070 }
2071
2072 #[allow(dead_code)]
2074 fn write_pages_with_fonts(
2075 &mut self,
2076 document: &Document,
2077 font_refs: &HashMap<String, ObjectId>,
2078 ) -> Result<()> {
2079 self.write_pages(document, font_refs)
2080 }
2081
2082 fn write_page_with_fonts(
2083 &mut self,
2084 page_id: ObjectId,
2085 parent_id: ObjectId,
2086 content_id: ObjectId,
2087 page: &crate::page::Page,
2088 _document: &Document,
2089 font_refs: &HashMap<String, ObjectId>,
2090 ) -> Result<()> {
2091 let mut page_dict = page.to_dict();
2093
2094 page_dict.set("Type", Object::Name("Page".to_string()));
2095 page_dict.set("Parent", Object::Reference(parent_id));
2096 page_dict.set("Contents", Object::Reference(content_id));
2097
2098 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2100 res.clone()
2101 } else {
2102 Dictionary::new()
2103 };
2104
2105 let mut font_dict = Dictionary::new();
2107
2108 let mut helvetica_dict = Dictionary::new();
2113 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2114 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2115 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2116 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2117 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2118
2119 let mut helvetica_bold_dict = Dictionary::new();
2120 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2121 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2122 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2123 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2124 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2125
2126 let mut helvetica_oblique_dict = Dictionary::new();
2127 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2128 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2129 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2130 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2131 font_dict.set(
2132 "Helvetica-Oblique",
2133 Object::Dictionary(helvetica_oblique_dict),
2134 );
2135
2136 let mut helvetica_bold_oblique_dict = Dictionary::new();
2137 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2138 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2139 helvetica_bold_oblique_dict.set(
2140 "BaseFont",
2141 Object::Name("Helvetica-BoldOblique".to_string()),
2142 );
2143 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2144 font_dict.set(
2145 "Helvetica-BoldOblique",
2146 Object::Dictionary(helvetica_bold_oblique_dict),
2147 );
2148
2149 let mut times_dict = Dictionary::new();
2151 times_dict.set("Type", Object::Name("Font".to_string()));
2152 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2153 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2154 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2155 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2156
2157 let mut times_bold_dict = Dictionary::new();
2158 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2159 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2160 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2161 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2162 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2163
2164 let mut times_italic_dict = Dictionary::new();
2165 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2166 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2167 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2168 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2169 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2170
2171 let mut times_bold_italic_dict = Dictionary::new();
2172 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2173 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2174 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2175 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2176 font_dict.set(
2177 "Times-BoldItalic",
2178 Object::Dictionary(times_bold_italic_dict),
2179 );
2180
2181 let mut courier_dict = Dictionary::new();
2183 courier_dict.set("Type", Object::Name("Font".to_string()));
2184 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2185 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2186 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2187 font_dict.set("Courier", Object::Dictionary(courier_dict));
2188
2189 let mut courier_bold_dict = Dictionary::new();
2190 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2191 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2192 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2193 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2194 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2195
2196 let mut courier_oblique_dict = Dictionary::new();
2197 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2198 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2199 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2200 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2201 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2202
2203 let mut courier_bold_oblique_dict = Dictionary::new();
2204 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2205 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2206 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2207 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2208 font_dict.set(
2209 "Courier-BoldOblique",
2210 Object::Dictionary(courier_bold_oblique_dict),
2211 );
2212
2213 for (font_name, font_id) in font_refs {
2215 font_dict.set(font_name, Object::Reference(*font_id));
2216 }
2217
2218 resources.set("Font", Object::Dictionary(font_dict));
2219
2220 let has_images = !page.images().is_empty();
2222 let has_forms = !page.form_xobjects().is_empty();
2223
2224 if has_images || has_forms {
2225 let mut xobject_dict = Dictionary::new();
2226
2227 for (name, image) in page.images() {
2228 let image_id = self.allocate_object_id();
2230
2231 if image.has_transparency() {
2233 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2235
2236 if let Some(smask_stream) = smask_obj {
2238 let smask_id = self.allocate_object_id();
2239 self.write_object(smask_id, smask_stream)?;
2240
2241 if let Object::Stream(ref mut dict, _) = main_obj {
2243 dict.set("SMask", Object::Reference(smask_id));
2244 }
2245 }
2246
2247 self.write_object(image_id, main_obj)?;
2249 } else {
2250 self.write_object(image_id, image.to_pdf_object())?;
2252 }
2253
2254 xobject_dict.set(name, Object::Reference(image_id));
2256 }
2257
2258 for (name, form) in page.form_xobjects() {
2260 let form_id = self.allocate_object_id();
2261 let stream = form.to_stream()?;
2262 let stream_obj =
2263 Object::Stream(stream.dictionary().clone(), stream.data().to_vec());
2264 self.write_object(form_id, stream_obj)?;
2265 xobject_dict.set(name, Object::Reference(form_id));
2266 }
2267
2268 resources.set("XObject", Object::Dictionary(xobject_dict));
2269 }
2270
2271 if let Some(extgstate_states) = page.get_extgstate_resources() {
2273 let mut extgstate_dict = Dictionary::new();
2274 for (name, state) in extgstate_states {
2275 let mut state_dict = Dictionary::new();
2276 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2277
2278 if let Some(alpha_stroke) = state.alpha_stroke {
2280 state_dict.set("CA", Object::Real(alpha_stroke));
2281 }
2282 if let Some(alpha_fill) = state.alpha_fill {
2283 state_dict.set("ca", Object::Real(alpha_fill));
2284 }
2285
2286 if let Some(line_width) = state.line_width {
2288 state_dict.set("LW", Object::Real(line_width));
2289 }
2290 if let Some(line_cap) = state.line_cap {
2291 state_dict.set("LC", Object::Integer(line_cap as i64));
2292 }
2293 if let Some(line_join) = state.line_join {
2294 state_dict.set("LJ", Object::Integer(line_join as i64));
2295 }
2296 if let Some(dash_pattern) = &state.dash_pattern {
2297 let dash_objects: Vec<Object> = dash_pattern
2298 .array
2299 .iter()
2300 .map(|&d| Object::Real(d))
2301 .collect();
2302 state_dict.set(
2303 "D",
2304 Object::Array(vec![
2305 Object::Array(dash_objects),
2306 Object::Real(dash_pattern.phase),
2307 ]),
2308 );
2309 }
2310
2311 extgstate_dict.set(name, Object::Dictionary(state_dict));
2312 }
2313 if !extgstate_dict.is_empty() {
2314 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2315 }
2316 }
2317
2318 if let Some(preserved_res) = page.get_preserved_resources() {
2321 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
2323
2324 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2326 let renamed_fonts = crate::writer::rename_preserved_fonts(fonts);
2328
2329 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
2331 }
2332
2333 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2337 let mut fonts_with_refs = crate::objects::Dictionary::new();
2338
2339 for (font_name, font_obj) in fonts.iter() {
2340 if let Object::Dictionary(font_dict) = font_obj {
2341 let updated_font = self.write_embedded_font_streams(font_dict)?;
2343 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
2344 } else {
2345 fonts_with_refs.set(font_name, font_obj.clone());
2347 }
2348 }
2349
2350 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
2352 }
2353
2354 if let Some(Object::Dictionary(xobjects)) = preserved_writer_dict.get("XObject") {
2358 let mut xobjects_with_refs = crate::objects::Dictionary::new();
2359 tracing::debug!(
2360 "Externalizing {} preserved XObject entries as indirect objects",
2361 xobjects.len()
2362 );
2363
2364 for (xobj_name, xobj_obj) in xobjects.iter() {
2365 match xobj_obj {
2366 Object::Stream(dict, data) => {
2367 let obj_id = self.allocate_object_id();
2368 self.write_object(obj_id, Object::Stream(dict.clone(), data.clone()))?;
2369 xobjects_with_refs.set(xobj_name, Object::Reference(obj_id));
2370 }
2371 Object::Dictionary(dict) => {
2372 let externalized = self.externalize_streams_in_dict(dict)?;
2374 xobjects_with_refs.set(xobj_name, Object::Dictionary(externalized));
2375 }
2376 _ => {
2377 xobjects_with_refs.set(xobj_name, xobj_obj.clone());
2378 }
2379 }
2380 }
2381
2382 preserved_writer_dict.set("XObject", Object::Dictionary(xobjects_with_refs));
2383 }
2384
2385 for (key, value) in preserved_writer_dict.iter() {
2387 if let Some(Object::Dictionary(existing)) = resources.get(key) {
2389 if let Object::Dictionary(preserved_dict) = value {
2390 let mut merged = existing.clone();
2391 for (res_name, res_obj) in preserved_dict.iter() {
2393 if !merged.contains_key(res_name) {
2394 merged.set(res_name, res_obj.clone());
2395 }
2396 }
2397 resources.set(key, Object::Dictionary(merged));
2398 }
2399 } else {
2400 resources.set(key, value.clone());
2402 }
2403 }
2404 }
2405
2406 page_dict.set("Resources", Object::Dictionary(resources));
2407
2408 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
2410 let mut new_annots = Vec::new();
2411
2412 for annot in annots {
2413 if let Object::Dictionary(ref annot_dict) = annot {
2414 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
2415 if subtype == "Widget" {
2416 let widget_id = self.allocate_object_id();
2418 self.write_object(widget_id, annot.clone())?;
2419 new_annots.push(Object::Reference(widget_id));
2420
2421 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
2423 if let Some(Object::String(field_name)) = annot_dict.get("T") {
2424 self.field_widget_map
2425 .entry(field_name.clone())
2426 .or_default()
2427 .push(widget_id);
2428 self.field_id_map.insert(field_name.clone(), widget_id);
2429 self.form_field_ids.push(widget_id);
2430 }
2431 }
2432 continue;
2433 }
2434 }
2435 }
2436 new_annots.push(annot.clone());
2437 }
2438
2439 if !new_annots.is_empty() {
2440 page_dict.set("Annots", Object::Array(new_annots));
2441 }
2442 }
2443
2444 self.write_object(page_id, Object::Dictionary(page_dict))?;
2445 Ok(())
2446 }
2447}
2448
2449impl PdfWriter<BufWriter<std::fs::File>> {
2450 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
2451 let file = std::fs::File::create(path)?;
2452 let writer = BufWriter::new(file);
2453
2454 Ok(Self {
2455 writer,
2456 xref_positions: HashMap::new(),
2457 current_position: 0,
2458 next_object_id: 1,
2459 catalog_id: None,
2460 pages_id: None,
2461 info_id: None,
2462 field_widget_map: HashMap::new(),
2463 field_id_map: HashMap::new(),
2464 form_field_ids: Vec::new(),
2465 page_ids: Vec::new(),
2466 config: WriterConfig::default(),
2467 document_used_chars: None,
2468 buffered_objects: HashMap::new(),
2469 compressed_object_map: HashMap::new(),
2470 prev_xref_offset: None,
2471 base_pdf_size: None,
2472 encrypt_obj_id: None,
2473 file_id: None,
2474 encryption_state: None,
2475 pending_encrypt_dict: None,
2476 })
2477 }
2478}
2479
2480impl<W: Write> PdfWriter<W> {
2481 fn externalize_streams_in_dict(
2500 &mut self,
2501 dict: &crate::objects::Dictionary,
2502 ) -> Result<crate::objects::Dictionary> {
2503 let mut result = crate::objects::Dictionary::new();
2504 for (key, value) in dict.iter() {
2505 match value {
2506 Object::Stream(d, data) => {
2507 let obj_id = self.allocate_object_id();
2508 self.write_object(obj_id, Object::Stream(d.clone(), data.clone()))?;
2509 result.set(key, Object::Reference(obj_id));
2510 }
2511 _ => {
2512 result.set(key, value.clone());
2513 }
2514 }
2515 }
2516 Ok(result)
2517 }
2518
2519 fn write_embedded_font_streams(
2520 &mut self,
2521 font_dict: &crate::objects::Dictionary,
2522 ) -> Result<crate::objects::Dictionary> {
2523 let mut updated_font = font_dict.clone();
2524
2525 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
2527 if subtype == "Type0" {
2528 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
2530 let mut updated_descendants = Vec::new();
2531
2532 for descendant in descendants {
2533 match descendant {
2534 Object::Dictionary(cidfont) => {
2535 let updated_cidfont =
2537 self.write_cidfont_embedded_streams(cidfont)?;
2538 let cidfont_id = self.allocate_object_id();
2540 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
2541 updated_descendants.push(Object::Reference(cidfont_id));
2543 }
2544 Object::Reference(_) => {
2545 updated_descendants.push(descendant.clone());
2547 }
2548 _ => {
2549 updated_descendants.push(descendant.clone());
2550 }
2551 }
2552 }
2553
2554 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
2555 }
2556
2557 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
2559 let tounicode_id = self.allocate_object_id();
2560 self.write_object(
2561 tounicode_id,
2562 Object::Stream(stream_dict.clone(), stream_data.clone()),
2563 )?;
2564 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
2565 }
2566
2567 return Ok(updated_font);
2568 }
2569 }
2570
2571 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
2574 let mut updated_descriptor = descriptor.clone();
2575 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
2576
2577 for key in &font_file_keys {
2579 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
2580 let stream_id = self.allocate_object_id();
2582 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
2583 self.write_object(stream_id, stream_obj)?;
2584
2585 updated_descriptor.set(*key, Object::Reference(stream_id));
2587 }
2588 }
2590
2591 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
2593 }
2594
2595 Ok(updated_font)
2596 }
2597
2598 fn write_cidfont_embedded_streams(
2600 &mut self,
2601 cidfont: &crate::objects::Dictionary,
2602 ) -> Result<crate::objects::Dictionary> {
2603 let mut updated_cidfont = cidfont.clone();
2604
2605 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
2607 let mut updated_descriptor = descriptor.clone();
2608 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
2609
2610 for key in &font_file_keys {
2612 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
2613 let stream_id = self.allocate_object_id();
2614 self.write_object(
2615 stream_id,
2616 Object::Stream(stream_dict.clone(), stream_data.clone()),
2617 )?;
2618 updated_descriptor.set(*key, Object::Reference(stream_id));
2619 }
2620 }
2621
2622 let descriptor_id = self.allocate_object_id();
2624 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
2625
2626 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
2628 }
2629
2630 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
2632 let map_id = self.allocate_object_id();
2633 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
2634 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
2635 }
2636
2637 Ok(updated_cidfont)
2638 }
2639
2640 fn allocate_object_id(&mut self) -> ObjectId {
2641 let id = ObjectId::new(self.next_object_id, 0);
2642 self.next_object_id += 1;
2643 id
2644 }
2645
2646 fn get_catalog_id(&self) -> Result<ObjectId> {
2648 self.catalog_id.ok_or_else(|| {
2649 PdfError::InvalidOperation(
2650 "catalog_id not initialized - write_document() must be called first".to_string(),
2651 )
2652 })
2653 }
2654
2655 fn get_pages_id(&self) -> Result<ObjectId> {
2657 self.pages_id.ok_or_else(|| {
2658 PdfError::InvalidOperation(
2659 "pages_id not initialized - write_document() must be called first".to_string(),
2660 )
2661 })
2662 }
2663
2664 fn get_info_id(&self) -> Result<ObjectId> {
2666 self.info_id.ok_or_else(|| {
2667 PdfError::InvalidOperation(
2668 "info_id not initialized - write_document() must be called first".to_string(),
2669 )
2670 })
2671 }
2672
2673 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
2674 use crate::writer::ObjectStreamWriter;
2675
2676 let object = if let Some(ref enc_state) = self.encryption_state {
2678 let mut obj = object;
2679 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
2680 obj
2681 } else {
2682 object
2683 };
2684
2685 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
2687 let mut buffer = Vec::new();
2688 self.write_object_value_to_buffer(&object, &mut buffer)?;
2689 self.buffered_objects.insert(id, buffer);
2690 return Ok(());
2691 }
2692
2693 self.xref_positions.insert(id, self.current_position);
2695
2696 let header = format!("{} {} obj\n", id.number(), id.generation());
2698 self.write_bytes(header.as_bytes())?;
2699
2700 self.write_object_value(&object)?;
2701
2702 self.write_bytes(b"\nendobj\n")?;
2703 Ok(())
2704 }
2705
2706 fn write_object_value(&mut self, object: &Object) -> Result<()> {
2707 match object {
2708 Object::Null => self.write_bytes(b"null")?,
2709 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
2710 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
2711 Object::Real(f) => self.write_bytes(
2712 format!("{f:.6}")
2713 .trim_end_matches('0')
2714 .trim_end_matches('.')
2715 .as_bytes(),
2716 )?,
2717 Object::String(s) => {
2718 self.write_bytes(b"(")?;
2719 self.write_bytes(s.as_bytes())?;
2720 self.write_bytes(b")")?;
2721 }
2722 Object::ByteString(bytes) => {
2723 self.write_bytes(b"<")?;
2725 for byte in bytes {
2726 self.write_bytes(format!("{byte:02X}").as_bytes())?;
2727 }
2728 self.write_bytes(b">")?;
2729 }
2730 Object::Name(n) => {
2731 self.write_bytes(b"/")?;
2732 self.write_bytes(n.as_bytes())?;
2733 }
2734 Object::Array(arr) => {
2735 self.write_bytes(b"[")?;
2736 for (i, obj) in arr.iter().enumerate() {
2737 if i > 0 {
2738 self.write_bytes(b" ")?;
2739 }
2740 self.write_object_value(obj)?;
2741 }
2742 self.write_bytes(b"]")?;
2743 }
2744 Object::Dictionary(dict) => {
2745 self.write_bytes(b"<<")?;
2746 for (key, value) in dict.entries() {
2747 self.write_bytes(b"\n/")?;
2748 self.write_bytes(key.as_bytes())?;
2749 self.write_bytes(b" ")?;
2750 self.write_object_value(value)?;
2751 }
2752 self.write_bytes(b"\n>>")?;
2753 }
2754 Object::Stream(dict, data) => {
2755 let mut corrected_dict = dict.clone();
2758 corrected_dict.set("Length", Object::Integer(data.len() as i64));
2759
2760 self.write_object_value(&Object::Dictionary(corrected_dict))?;
2761 self.write_bytes(b"\nstream\n")?;
2762 self.write_bytes(data)?;
2763 self.write_bytes(b"\nendstream")?;
2764 }
2765 Object::Reference(id) => {
2766 let ref_str = format!("{} {} R", id.number(), id.generation());
2767 self.write_bytes(ref_str.as_bytes())?;
2768 }
2769 }
2770 Ok(())
2771 }
2772
2773 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
2775 match object {
2776 Object::Null => buffer.extend_from_slice(b"null"),
2777 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
2778 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
2779 Object::Real(f) => buffer.extend_from_slice(
2780 format!("{f:.6}")
2781 .trim_end_matches('0')
2782 .trim_end_matches('.')
2783 .as_bytes(),
2784 ),
2785 Object::String(s) => {
2786 buffer.push(b'(');
2787 buffer.extend_from_slice(s.as_bytes());
2788 buffer.push(b')');
2789 }
2790 Object::ByteString(bytes) => {
2791 buffer.push(b'<');
2792 for byte in bytes {
2793 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
2794 }
2795 buffer.push(b'>');
2796 }
2797 Object::Name(n) => {
2798 buffer.push(b'/');
2799 buffer.extend_from_slice(n.as_bytes());
2800 }
2801 Object::Array(arr) => {
2802 buffer.push(b'[');
2803 for (i, obj) in arr.iter().enumerate() {
2804 if i > 0 {
2805 buffer.push(b' ');
2806 }
2807 self.write_object_value_to_buffer(obj, buffer)?;
2808 }
2809 buffer.push(b']');
2810 }
2811 Object::Dictionary(dict) => {
2812 buffer.extend_from_slice(b"<<");
2813 for (key, value) in dict.entries() {
2814 buffer.extend_from_slice(b"\n/");
2815 buffer.extend_from_slice(key.as_bytes());
2816 buffer.push(b' ');
2817 self.write_object_value_to_buffer(value, buffer)?;
2818 }
2819 buffer.extend_from_slice(b"\n>>");
2820 }
2821 Object::Stream(_, _) => {
2822 return Err(crate::error::PdfError::ObjectStreamError(
2824 "Cannot compress stream objects in object streams".to_string(),
2825 ));
2826 }
2827 Object::Reference(id) => {
2828 let ref_str = format!("{} {} R", id.number(), id.generation());
2829 buffer.extend_from_slice(ref_str.as_bytes());
2830 }
2831 }
2832 Ok(())
2833 }
2834
2835 fn flush_object_streams(&mut self) -> Result<()> {
2837 if self.buffered_objects.is_empty() {
2838 return Ok(());
2839 }
2840
2841 let config = ObjectStreamConfig {
2843 max_objects_per_stream: 100,
2844 compression_level: 6,
2845 enabled: true,
2846 };
2847 let mut os_writer = ObjectStreamWriter::new(config);
2848
2849 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
2851 buffered.sort_by_key(|(id, _)| id.number());
2852
2853 for (id, data) in buffered {
2855 os_writer.add_object(*id, data.clone())?;
2856 }
2857
2858 let streams = os_writer.finalize()?;
2860
2861 for mut stream in streams {
2863 let stream_id = stream.stream_id;
2864
2865 let compressed_data = stream.generate_stream_data(6)?;
2867
2868 let dict = stream.generate_dictionary(&compressed_data);
2870
2871 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
2873 self.compressed_object_map
2874 .insert(*obj_id, (stream_id, index as u32));
2875 }
2876
2877 self.xref_positions.insert(stream_id, self.current_position);
2879
2880 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
2881 self.write_bytes(header.as_bytes())?;
2882
2883 self.write_object_value(&Object::Dictionary(dict))?;
2884
2885 self.write_bytes(b"\nstream\n")?;
2886 self.write_bytes(&compressed_data)?;
2887 self.write_bytes(b"\nendstream\nendobj\n")?;
2888 }
2889
2890 Ok(())
2891 }
2892
2893 fn write_xref(&mut self) -> Result<()> {
2894 self.write_bytes(b"xref\n")?;
2895
2896 let mut entries: Vec<_> = self
2898 .xref_positions
2899 .iter()
2900 .map(|(id, pos)| (*id, *pos))
2901 .collect();
2902 entries.sort_by_key(|(id, _)| id.number());
2903
2904 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
2906
2907 self.write_bytes(b"0 ")?;
2910 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
2911 self.write_bytes(b"\n")?;
2912
2913 self.write_bytes(b"0000000000 65535 f \n")?;
2915
2916 for obj_num in 1..=max_obj_num {
2919 let _obj_id = ObjectId::new(obj_num, 0);
2920 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
2921 let entry = format!("{:010} {:05} n \n", position, 0);
2922 self.write_bytes(entry.as_bytes())?;
2923 } else {
2924 self.write_bytes(b"0000000000 00000 f \n")?;
2926 }
2927 }
2928
2929 Ok(())
2930 }
2931
2932 fn write_xref_stream(&mut self) -> Result<()> {
2933 let catalog_id = self.get_catalog_id()?;
2934 let info_id = self.get_info_id()?;
2935
2936 let xref_stream_id = self.allocate_object_id();
2938 let xref_position = self.current_position;
2939
2940 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
2942 xref_writer.set_trailer_info(catalog_id, info_id);
2943
2944 xref_writer.add_free_entry(0, 65535);
2946
2947 let mut entries: Vec<_> = self
2949 .xref_positions
2950 .iter()
2951 .map(|(id, pos)| (*id, *pos))
2952 .collect();
2953 entries.sort_by_key(|(id, _)| id.number());
2954
2955 let max_obj_num = entries
2957 .iter()
2958 .map(|(id, _)| id.number())
2959 .max()
2960 .unwrap_or(0)
2961 .max(xref_stream_id.number());
2962
2963 for obj_num in 1..=max_obj_num {
2965 let obj_id = ObjectId::new(obj_num, 0);
2966
2967 if obj_num == xref_stream_id.number() {
2968 xref_writer.add_in_use_entry(xref_position, 0);
2970 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
2971 xref_writer.add_compressed_entry(stream_id.number(), *index);
2973 } else if let Some((id, position)) =
2974 entries.iter().find(|(id, _)| id.number() == obj_num)
2975 {
2976 xref_writer.add_in_use_entry(*position, id.generation());
2978 } else {
2979 xref_writer.add_free_entry(0, 0);
2981 }
2982 }
2983
2984 self.xref_positions.insert(xref_stream_id, xref_position);
2986
2987 self.write_bytes(
2989 format!(
2990 "{} {} obj\n",
2991 xref_stream_id.number(),
2992 xref_stream_id.generation()
2993 )
2994 .as_bytes(),
2995 )?;
2996
2997 let uncompressed_data = xref_writer.encode_entries();
2999 let final_data = if self.config.compress_streams {
3000 crate::compression::compress(&uncompressed_data)?
3001 } else {
3002 uncompressed_data
3003 };
3004
3005 let mut dict = xref_writer.create_dictionary(None);
3007 dict.set("Length", Object::Integer(final_data.len() as i64));
3008
3009 if self.config.compress_streams {
3011 dict.set("Filter", Object::Name("FlateDecode".to_string()));
3012 }
3013 self.write_bytes(b"<<")?;
3014 for (key, value) in dict.iter() {
3015 self.write_bytes(b"\n/")?;
3016 self.write_bytes(key.as_bytes())?;
3017 self.write_bytes(b" ")?;
3018 self.write_object_value(value)?;
3019 }
3020 self.write_bytes(b"\n>>\n")?;
3021
3022 self.write_bytes(b"stream\n")?;
3024 self.write_bytes(&final_data)?;
3025 self.write_bytes(b"\nendstream\n")?;
3026 self.write_bytes(b"endobj\n")?;
3027
3028 self.write_bytes(b"\nstartxref\n")?;
3030 self.write_bytes(xref_position.to_string().as_bytes())?;
3031 self.write_bytes(b"\n%%EOF\n")?;
3032
3033 Ok(())
3034 }
3035
3036 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
3043 use crate::encryption::{
3044 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
3045 };
3046 use std::sync::Arc;
3047
3048 let mut fid = vec![0u8; 16];
3050 use rand::Rng;
3051 rand::rng().fill_bytes(&mut fid);
3052
3053 let enc_dict = encryption
3054 .create_encryption_dict(Some(&fid))
3055 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
3056
3057 let enc_key = encryption
3059 .get_encryption_key(&enc_dict, Some(&fid))
3060 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
3061
3062 let handler = encryption.handler();
3064 let (method, key_len) = match encryption.strength {
3065 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
3066 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
3067 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
3068 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
3069 };
3070
3071 let std_filter = FunctionalCryptFilter {
3072 name: "StdCF".to_string(),
3073 method,
3074 length: key_len,
3075 auth_event: crate::encryption::AuthEvent::DocOpen,
3076 recipients: None,
3077 };
3078
3079 let mut filter_manager =
3080 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
3081 filter_manager.add_filter(std_filter);
3082
3083 let encryptor =
3084 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
3085
3086 let encrypt_id = self.allocate_object_id();
3088 self.encrypt_obj_id = Some(encrypt_id);
3089 self.file_id = Some(fid);
3090 self.encryption_state = Some(WriterEncryptionState { encryptor });
3091
3092 self.pending_encrypt_dict = Some(enc_dict.to_dict());
3094
3095 Ok(())
3096 }
3097
3098 fn write_encryption_dict(&mut self) -> Result<()> {
3100 if let (Some(encrypt_id), Some(dict)) =
3101 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
3102 {
3103 let enc_state = self.encryption_state.take();
3105 self.write_object(encrypt_id, Object::Dictionary(dict))?;
3106 self.encryption_state = enc_state;
3107 }
3108 Ok(())
3109 }
3110
3111 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
3112 let catalog_id = self.get_catalog_id()?;
3113 let info_id = self.get_info_id()?;
3114 let max_obj_num = self
3116 .xref_positions
3117 .keys()
3118 .map(|id| id.number())
3119 .max()
3120 .unwrap_or(0);
3121
3122 let mut trailer = Dictionary::new();
3123 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
3124 trailer.set("Root", Object::Reference(catalog_id));
3125 trailer.set("Info", Object::Reference(info_id));
3126
3127 if let Some(prev_xref) = self.prev_xref_offset {
3129 trailer.set("Prev", Object::Integer(prev_xref as i64));
3130 }
3131
3132 if let Some(encrypt_id) = self.encrypt_obj_id {
3134 trailer.set("Encrypt", Object::Reference(encrypt_id));
3135 }
3136 if let Some(ref fid) = self.file_id {
3137 trailer.set(
3138 "ID",
3139 Object::Array(vec![
3140 Object::ByteString(fid.clone()),
3141 Object::ByteString(fid.clone()),
3142 ]),
3143 );
3144 }
3145
3146 self.write_bytes(b"trailer\n")?;
3147 self.write_object_value(&Object::Dictionary(trailer))?;
3148 self.write_bytes(b"\nstartxref\n")?;
3149 self.write_bytes(xref_position.to_string().as_bytes())?;
3150 self.write_bytes(b"\n%%EOF\n")?;
3151
3152 Ok(())
3153 }
3154
3155 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
3156 self.writer.write_all(data)?;
3157 self.current_position += data.len() as u64;
3158 Ok(())
3159 }
3160
3161 #[allow(dead_code)]
3162 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
3163 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
3165 if rect_array.len() >= 4 {
3166 if let (
3167 Some(Object::Real(x1)),
3168 Some(Object::Real(y1)),
3169 Some(Object::Real(x2)),
3170 Some(Object::Real(y2)),
3171 ) = (
3172 rect_array.first(),
3173 rect_array.get(1),
3174 rect_array.get(2),
3175 rect_array.get(3),
3176 ) {
3177 (*x1, *y1, *x2, *y2)
3178 } else {
3179 (0.0, 0.0, 100.0, 20.0) }
3181 } else {
3182 (0.0, 0.0, 100.0, 20.0) }
3184 } else {
3185 (0.0, 0.0, 100.0, 20.0) };
3187
3188 let width = rect.2 - rect.0;
3189 let height = rect.3 - rect.1;
3190
3191 let mut content = String::new();
3193
3194 content.push_str("q\n");
3196
3197 content.push_str("0 0 0 RG\n"); content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
3203 content.push_str("S\n"); content.push_str("1 1 1 rg\n"); content.push_str(&format!("0.5 0.5 {} {} re\n", width - 1.0, height - 1.0));
3208 content.push_str("f\n"); content.push_str("Q\n");
3212
3213 let mut stream_dict = Dictionary::new();
3215 stream_dict.set("Type", Object::Name("XObject".to_string()));
3216 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3217 stream_dict.set(
3218 "BBox",
3219 Object::Array(vec![
3220 Object::Real(0.0),
3221 Object::Real(0.0),
3222 Object::Real(width),
3223 Object::Real(height),
3224 ]),
3225 );
3226 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3227 stream_dict.set("Length", Object::Integer(content.len() as i64));
3228
3229 let stream_id = self.allocate_object_id();
3231 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3232
3233 Ok(stream_id)
3234 }
3235
3236 #[allow(dead_code)]
3237 fn create_field_appearance_stream(
3238 &mut self,
3239 field_dict: &Dictionary,
3240 widget: &crate::forms::Widget,
3241 ) -> Result<ObjectId> {
3242 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
3243 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
3244
3245 let mut content = String::new();
3247
3248 content.push_str("q\n");
3250
3251 if let Some(bg_color) = &widget.appearance.background_color {
3253 match bg_color {
3254 crate::graphics::Color::Gray(g) => {
3255 content.push_str(&format!("{g} g\n"));
3256 }
3257 crate::graphics::Color::Rgb(r, g, b) => {
3258 content.push_str(&format!("{r} {g} {b} rg\n"));
3259 }
3260 crate::graphics::Color::Cmyk(c, m, y, k) => {
3261 content.push_str(&format!("{c} {m} {y} {k} k\n"));
3262 }
3263 }
3264 content.push_str(&format!("0 0 {width} {height} re\n"));
3265 content.push_str("f\n");
3266 }
3267
3268 if let Some(border_color) = &widget.appearance.border_color {
3270 match border_color {
3271 crate::graphics::Color::Gray(g) => {
3272 content.push_str(&format!("{g} G\n"));
3273 }
3274 crate::graphics::Color::Rgb(r, g, b) => {
3275 content.push_str(&format!("{r} {g} {b} RG\n"));
3276 }
3277 crate::graphics::Color::Cmyk(c, m, y, k) => {
3278 content.push_str(&format!("{c} {m} {y} {k} K\n"));
3279 }
3280 }
3281 content.push_str(&format!("{} w\n", widget.appearance.border_width));
3282 content.push_str(&format!("0 0 {width} {height} re\n"));
3283 content.push_str("S\n");
3284 }
3285
3286 if let Some(Object::Name(ft)) = field_dict.get("FT") {
3288 if ft == "Btn" {
3289 if let Some(Object::Name(v)) = field_dict.get("V") {
3290 if v == "Yes" {
3291 content.push_str("0 0 0 RG\n"); content.push_str("2 w\n");
3294 let margin = width * 0.2;
3295 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
3296 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
3297 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
3298 content.push_str("S\n");
3299 }
3300 }
3301 }
3302 }
3303
3304 content.push_str("Q\n");
3306
3307 let mut stream_dict = Dictionary::new();
3309 stream_dict.set("Type", Object::Name("XObject".to_string()));
3310 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3311 stream_dict.set(
3312 "BBox",
3313 Object::Array(vec![
3314 Object::Real(0.0),
3315 Object::Real(0.0),
3316 Object::Real(width),
3317 Object::Real(height),
3318 ]),
3319 );
3320 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3321 stream_dict.set("Length", Object::Integer(content.len() as i64));
3322
3323 let stream_id = self.allocate_object_id();
3325 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3326
3327 Ok(stream_id)
3328 }
3329}
3330
3331fn format_pdf_date(date: DateTime<Utc>) -> String {
3333 let formatted = date.format("D:%Y%m%d%H%M%S");
3336
3337 format!("{formatted}+00'00")
3339}
3340
3341#[cfg(test)]
3342mod tests;
3343
3344#[cfg(test)]
3345mod rigorous_tests;