1use crate::document::Document;
2use crate::error::{PdfError, Result};
3use crate::objects::{Dictionary, Object, ObjectId};
4use crate::text::fonts::embedding::CjkFontType;
5use crate::writer::{ObjectStreamConfig, ObjectStreamWriter, XRefStreamWriter};
6use chrono::{DateTime, Utc};
7use std::collections::HashMap;
8use std::io::{BufWriter, Write};
9use std::path::Path;
10
11#[derive(Debug, Clone)]
13pub struct WriterConfig {
14 pub use_xref_streams: bool,
16 pub use_object_streams: bool,
18 pub pdf_version: String,
20 pub compress_streams: bool,
22 pub incremental_update: bool,
24}
25
26impl Default for WriterConfig {
27 fn default() -> Self {
28 Self {
29 use_xref_streams: false,
30 use_object_streams: false,
31 pdf_version: "1.7".to_string(),
32 compress_streams: true,
33 incremental_update: false,
34 }
35 }
36}
37
38impl WriterConfig {
39 pub fn modern() -> Self {
41 Self {
42 use_xref_streams: true,
43 use_object_streams: true,
44 pdf_version: "1.5".to_string(),
45 compress_streams: true,
46 incremental_update: false,
47 }
48 }
49
50 pub fn legacy() -> Self {
52 Self {
53 use_xref_streams: false,
54 use_object_streams: false,
55 pdf_version: "1.4".to_string(),
56 compress_streams: true,
57 incremental_update: false,
58 }
59 }
60
61 pub fn incremental() -> Self {
63 Self {
64 use_xref_streams: false,
65 use_object_streams: false,
66 pdf_version: "1.4".to_string(),
67 compress_streams: true,
68 incremental_update: true,
69 }
70 }
71}
72
73pub struct PdfWriter<W: Write> {
74 writer: W,
75 xref_positions: HashMap<ObjectId, u64>,
76 current_position: u64,
77 next_object_id: u32,
78 catalog_id: Option<ObjectId>,
80 pages_id: Option<ObjectId>,
81 info_id: Option<ObjectId>,
82 #[allow(dead_code)]
84 field_widget_map: HashMap<String, Vec<ObjectId>>, #[allow(dead_code)]
86 field_id_map: HashMap<String, ObjectId>, form_field_ids: Vec<ObjectId>, page_ids: Vec<ObjectId>, config: WriterConfig,
91 document_used_chars: Option<std::collections::HashSet<char>>,
93 buffered_objects: HashMap<ObjectId, Vec<u8>>,
95 compressed_object_map: HashMap<ObjectId, (ObjectId, u32)>, prev_xref_offset: Option<u64>,
98 base_pdf_size: Option<u64>,
99 encrypt_obj_id: Option<ObjectId>,
101 file_id: Option<Vec<u8>>,
102 encryption_state: Option<WriterEncryptionState>,
103 pending_encrypt_dict: Option<Dictionary>,
104}
105
106struct WriterEncryptionState {
108 encryptor: crate::encryption::ObjectEncryptor,
109}
110
111impl<W: Write> PdfWriter<W> {
112 pub fn new_with_writer(writer: W) -> Self {
113 Self::with_config(writer, WriterConfig::default())
114 }
115
116 pub fn with_config(writer: W, config: WriterConfig) -> Self {
117 Self {
118 writer,
119 xref_positions: HashMap::new(),
120 current_position: 0,
121 next_object_id: 1, catalog_id: None,
123 pages_id: None,
124 info_id: None,
125 field_widget_map: HashMap::new(),
126 field_id_map: HashMap::new(),
127 form_field_ids: Vec::new(),
128 page_ids: Vec::new(),
129 config,
130 document_used_chars: None,
131 buffered_objects: HashMap::new(),
132 compressed_object_map: HashMap::new(),
133 prev_xref_offset: None,
134 base_pdf_size: None,
135 encrypt_obj_id: None,
136 file_id: None,
137 encryption_state: None,
138 pending_encrypt_dict: None,
139 }
140 }
141
142 pub fn write_document(&mut self, document: &mut Document) -> Result<()> {
143 if !document.used_characters.is_empty() {
145 self.document_used_chars = Some(document.used_characters.clone());
146 }
147
148 self.write_header()?;
149
150 self.catalog_id = Some(self.allocate_object_id());
152 self.pages_id = Some(self.allocate_object_id());
153 self.info_id = Some(self.allocate_object_id());
154
155 if let Some(ref encryption) = document.encryption {
158 self.init_encryption(encryption)?;
159 }
160
161 let font_refs = self.write_fonts(document)?;
163
164 self.write_pages(document, &font_refs)?;
166
167 self.write_form_fields(document)?;
169
170 self.write_catalog(document)?;
172
173 self.write_info(document)?;
175
176 self.write_encryption_dict()?;
178
179 if self.config.use_object_streams {
181 self.flush_object_streams()?;
182 }
183
184 let xref_position = self.current_position;
186 if self.config.use_xref_streams {
187 self.write_xref_stream()?;
188 } else {
189 self.write_xref()?;
190 }
191
192 if !self.config.use_xref_streams {
194 self.write_trailer(xref_position)?;
195 }
196
197 if let Ok(()) = self.writer.flush() {
198 }
200 Ok(())
201 }
202
203 pub fn write_incremental_update(
237 &mut self,
238 base_pdf_path: impl AsRef<std::path::Path>,
239 document: &mut Document,
240 ) -> Result<()> {
241 use std::io::{BufReader, Read, Seek, SeekFrom};
242
243 let base_pdf_file = std::fs::File::open(base_pdf_path.as_ref())?;
245 let mut pdf_reader = crate::parser::PdfReader::new(BufReader::new(base_pdf_file))?;
246
247 let base_catalog = pdf_reader.catalog()?;
249
250 let (base_pages_id, base_pages_gen) = base_catalog
252 .get("Pages")
253 .and_then(|obj| {
254 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
255 Some((*id, *gen))
256 } else {
257 None
258 }
259 })
260 .ok_or_else(|| {
261 crate::error::PdfError::InvalidStructure(
262 "Base PDF catalog missing /Pages reference".to_string(),
263 )
264 })?;
265
266 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
268 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
269 base_pages_obj
270 {
271 dict.get("Kids")
272 .and_then(|obj| {
273 if let crate::parser::objects::PdfObject::Array(arr) = obj {
274 Some(
277 arr.0
278 .iter()
279 .filter_map(|item| {
280 if let crate::parser::objects::PdfObject::Reference(id, gen) =
281 item
282 {
283 Some(crate::objects::Object::Reference(
284 crate::objects::ObjectId::new(*id, *gen),
285 ))
286 } else {
287 None
288 }
289 })
290 .collect::<Vec<_>>(),
291 )
292 } else {
293 None
294 }
295 })
296 .unwrap_or_default()
297 } else {
298 Vec::new()
299 };
300
301 let base_page_count = base_pages_kids.len();
303
304 let base_pdf = std::fs::File::open(base_pdf_path.as_ref())?;
306 let mut base_reader = BufReader::new(base_pdf);
307
308 base_reader.seek(SeekFrom::End(-100))?;
310 let mut end_buffer = vec![0u8; 100];
311 let bytes_read = base_reader.read(&mut end_buffer)?;
312 end_buffer.truncate(bytes_read);
313
314 let end_str = String::from_utf8_lossy(&end_buffer);
315 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
316 let after_startxref = &end_str[startxref_pos + 9..];
317
318 let number_str: String = after_startxref
319 .chars()
320 .skip_while(|c| c.is_whitespace())
321 .take_while(|c| c.is_ascii_digit())
322 .collect();
323
324 number_str.parse::<u64>().map_err(|_| {
325 crate::error::PdfError::InvalidStructure(
326 "Could not parse startxref offset".to_string(),
327 )
328 })?
329 } else {
330 return Err(crate::error::PdfError::InvalidStructure(
331 "startxref not found in base PDF".to_string(),
332 ));
333 };
334
335 base_reader.seek(SeekFrom::Start(0))?;
337 let base_size = std::io::copy(&mut base_reader, &mut self.writer)? as u64;
338
339 self.prev_xref_offset = Some(prev_xref);
341 self.base_pdf_size = Some(base_size);
342 self.current_position = base_size;
343
344 if !document.used_characters.is_empty() {
346 self.document_used_chars = Some(document.used_characters.clone());
347 }
348
349 self.catalog_id = Some(self.allocate_object_id());
351 self.pages_id = Some(self.allocate_object_id());
352 self.info_id = Some(self.allocate_object_id());
353
354 let font_refs = self.write_fonts(document)?;
356
357 self.write_pages(document, &font_refs)?;
359
360 self.write_form_fields(document)?;
362
363 let catalog_id = self.get_catalog_id()?;
365 let new_pages_id = self.get_pages_id()?;
366
367 let mut catalog = crate::objects::Dictionary::new();
368 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
369 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
370
371 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
376
377 let mut all_pages_kids = base_pages_kids;
379
380 for page_id in &self.page_ids {
382 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
383 }
384
385 let mut pages_dict = crate::objects::Dictionary::new();
386 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
387 pages_dict.set("Kids", crate::objects::Object::Array(all_pages_kids));
388 pages_dict.set(
389 "Count",
390 crate::objects::Object::Integer((base_page_count + self.page_ids.len()) as i64),
391 );
392
393 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
394
395 self.write_info(document)?;
397
398 let xref_position = self.current_position;
400 self.write_xref()?;
401
402 self.write_trailer(xref_position)?;
404
405 self.writer.flush()?;
406 Ok(())
407 }
408
409 pub fn write_incremental_with_page_replacement(
475 &mut self,
476 base_pdf_path: impl AsRef<std::path::Path>,
477 document: &mut Document,
478 ) -> Result<()> {
479 use std::io::Cursor;
480
481 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
483 let base_size = base_pdf_bytes.len() as u64;
484
485 let mut pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
487
488 let base_catalog = pdf_reader.catalog()?;
489
490 let (base_pages_id, base_pages_gen) = base_catalog
491 .get("Pages")
492 .and_then(|obj| {
493 if let crate::parser::objects::PdfObject::Reference(id, gen) = obj {
494 Some((*id, *gen))
495 } else {
496 None
497 }
498 })
499 .ok_or_else(|| {
500 crate::error::PdfError::InvalidStructure(
501 "Base PDF catalog missing /Pages reference".to_string(),
502 )
503 })?;
504
505 let base_pages_obj = pdf_reader.get_object(base_pages_id, base_pages_gen)?;
506 let base_pages_kids = if let crate::parser::objects::PdfObject::Dictionary(dict) =
507 base_pages_obj
508 {
509 dict.get("Kids")
510 .and_then(|obj| {
511 if let crate::parser::objects::PdfObject::Array(arr) = obj {
512 Some(
513 arr.0
514 .iter()
515 .filter_map(|item| {
516 if let crate::parser::objects::PdfObject::Reference(id, gen) =
517 item
518 {
519 Some(crate::objects::Object::Reference(
520 crate::objects::ObjectId::new(*id, *gen),
521 ))
522 } else {
523 None
524 }
525 })
526 .collect::<Vec<_>>(),
527 )
528 } else {
529 None
530 }
531 })
532 .unwrap_or_default()
533 } else {
534 Vec::new()
535 };
536
537 let base_page_count = base_pages_kids.len();
538
539 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
541 let end_bytes = &base_pdf_bytes[start_search..];
542 let end_str = String::from_utf8_lossy(end_bytes);
543
544 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
545 let after_startxref = &end_str[startxref_pos + 9..];
546 let number_str: String = after_startxref
547 .chars()
548 .skip_while(|c| c.is_whitespace())
549 .take_while(|c| c.is_ascii_digit())
550 .collect();
551
552 number_str.parse::<u64>().map_err(|_| {
553 crate::error::PdfError::InvalidStructure(
554 "Could not parse startxref offset".to_string(),
555 )
556 })?
557 } else {
558 return Err(crate::error::PdfError::InvalidStructure(
559 "startxref not found in base PDF".to_string(),
560 ));
561 };
562
563 self.writer.write_all(&base_pdf_bytes)?;
565
566 self.prev_xref_offset = Some(prev_xref);
567 self.base_pdf_size = Some(base_size);
568 self.current_position = base_size;
569
570 if !document.used_characters.is_empty() {
572 self.document_used_chars = Some(document.used_characters.clone());
573 }
574
575 self.catalog_id = Some(self.allocate_object_id());
576 self.pages_id = Some(self.allocate_object_id());
577 self.info_id = Some(self.allocate_object_id());
578
579 let font_refs = self.write_fonts(document)?;
580 self.write_pages(document, &font_refs)?;
581 self.write_form_fields(document)?;
582
583 let catalog_id = self.get_catalog_id()?;
585 let new_pages_id = self.get_pages_id()?;
586
587 let mut catalog = crate::objects::Dictionary::new();
588 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
589 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
590 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
591
592 let mut all_pages_kids = Vec::new();
594 let replacement_count = document.pages.len();
595
596 for page_id in &self.page_ids {
598 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
599 }
600
601 if replacement_count < base_page_count {
603 for i in replacement_count..base_page_count {
604 if let Some(page_ref) = base_pages_kids.get(i) {
605 all_pages_kids.push(page_ref.clone());
606 }
607 }
608 }
609
610 let mut pages_dict = crate::objects::Dictionary::new();
611 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
612 pages_dict.set(
613 "Kids",
614 crate::objects::Object::Array(all_pages_kids.clone()),
615 );
616 pages_dict.set(
617 "Count",
618 crate::objects::Object::Integer(all_pages_kids.len() as i64),
619 );
620
621 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
622 self.write_info(document)?;
623
624 let xref_position = self.current_position;
625 self.write_xref()?;
626 self.write_trailer(xref_position)?;
627
628 self.writer.flush()?;
629 Ok(())
630 }
631
632 pub fn write_incremental_with_overlay<P: AsRef<std::path::Path>>(
680 &mut self,
681 base_pdf_path: P,
682 mut overlay_fn: impl FnMut(&mut crate::Page) -> Result<()>,
683 ) -> Result<()> {
684 use std::io::Cursor;
685
686 let base_pdf_bytes = std::fs::read(base_pdf_path.as_ref())?;
688 let base_size = base_pdf_bytes.len() as u64;
689
690 let pdf_reader = crate::parser::PdfReader::new(Cursor::new(&base_pdf_bytes))?;
692 let parsed_doc = crate::parser::PdfDocument::new(pdf_reader);
693
694 let page_count = parsed_doc.page_count()?;
696
697 let start_search = if base_size > 100 { base_size - 100 } else { 0 } as usize;
699 let end_bytes = &base_pdf_bytes[start_search..];
700 let end_str = String::from_utf8_lossy(end_bytes);
701
702 let prev_xref = if let Some(startxref_pos) = end_str.find("startxref") {
703 let after_startxref = &end_str[startxref_pos + 9..];
704 let number_str: String = after_startxref
705 .chars()
706 .skip_while(|c| c.is_whitespace())
707 .take_while(|c| c.is_ascii_digit())
708 .collect();
709
710 number_str.parse::<u64>().map_err(|_| {
711 crate::error::PdfError::InvalidStructure(
712 "Could not parse startxref offset".to_string(),
713 )
714 })?
715 } else {
716 return Err(crate::error::PdfError::InvalidStructure(
717 "startxref not found in base PDF".to_string(),
718 ));
719 };
720
721 self.writer.write_all(&base_pdf_bytes)?;
723
724 self.prev_xref_offset = Some(prev_xref);
725 self.base_pdf_size = Some(base_size);
726 self.current_position = base_size;
727
728 let mut temp_doc = crate::Document::new();
730
731 for page_idx in 0..page_count {
732 let parsed_page = parsed_doc.get_page(page_idx)?;
734 let mut writable_page =
735 crate::Page::from_parsed_with_content(&parsed_page, &parsed_doc)?;
736
737 overlay_fn(&mut writable_page)?;
739
740 temp_doc.add_page(writable_page);
742 }
743
744 if !temp_doc.used_characters.is_empty() {
747 self.document_used_chars = Some(temp_doc.used_characters.clone());
748 }
749
750 self.catalog_id = Some(self.allocate_object_id());
751 self.pages_id = Some(self.allocate_object_id());
752 self.info_id = Some(self.allocate_object_id());
753
754 let font_refs = self.write_fonts(&temp_doc)?;
755 self.write_pages(&temp_doc, &font_refs)?;
756 self.write_form_fields(&mut temp_doc)?;
757
758 let catalog_id = self.get_catalog_id()?;
760 let new_pages_id = self.get_pages_id()?;
761
762 let mut catalog = crate::objects::Dictionary::new();
763 catalog.set("Type", crate::objects::Object::Name("Catalog".to_string()));
764 catalog.set("Pages", crate::objects::Object::Reference(new_pages_id));
765 self.write_object(catalog_id, crate::objects::Object::Dictionary(catalog))?;
766
767 let mut all_pages_kids = Vec::new();
769 for page_id in &self.page_ids {
770 all_pages_kids.push(crate::objects::Object::Reference(*page_id));
771 }
772
773 let mut pages_dict = crate::objects::Dictionary::new();
774 pages_dict.set("Type", crate::objects::Object::Name("Pages".to_string()));
775 pages_dict.set(
776 "Kids",
777 crate::objects::Object::Array(all_pages_kids.clone()),
778 );
779 pages_dict.set(
780 "Count",
781 crate::objects::Object::Integer(all_pages_kids.len() as i64),
782 );
783
784 self.write_object(new_pages_id, crate::objects::Object::Dictionary(pages_dict))?;
785 self.write_info(&temp_doc)?;
786
787 let xref_position = self.current_position;
788 self.write_xref()?;
789 self.write_trailer(xref_position)?;
790
791 self.writer.flush()?;
792 Ok(())
793 }
794
795 fn write_header(&mut self) -> Result<()> {
796 let header = format!("%PDF-{}\n", self.config.pdf_version);
797 self.write_bytes(header.as_bytes())?;
798 self.write_bytes(&[b'%', 0xE2, 0xE3, 0xCF, 0xD3, b'\n'])?;
800 Ok(())
801 }
802
803 fn convert_pdf_objects_dict_to_writer(
806 &self,
807 pdf_dict: &crate::pdf_objects::Dictionary,
808 ) -> crate::objects::Dictionary {
809 let mut writer_dict = crate::objects::Dictionary::new();
810
811 for (key, value) in pdf_dict.iter() {
812 let writer_obj = self.convert_pdf_object_to_writer(value);
813 writer_dict.set(key.as_str(), writer_obj);
814 }
815
816 writer_dict
817 }
818
819 fn convert_pdf_object_to_writer(
820 &self,
821 obj: &crate::pdf_objects::Object,
822 ) -> crate::objects::Object {
823 use crate::objects::Object as WriterObj;
824 use crate::pdf_objects::Object as PdfObj;
825
826 match obj {
827 PdfObj::Null => WriterObj::Null,
828 PdfObj::Boolean(b) => WriterObj::Boolean(*b),
829 PdfObj::Integer(i) => WriterObj::Integer(*i),
830 PdfObj::Real(f) => WriterObj::Real(*f),
831 PdfObj::String(s) => {
832 WriterObj::String(String::from_utf8_lossy(s.as_bytes()).to_string())
833 }
834 PdfObj::Name(n) => WriterObj::Name(n.as_str().to_string()),
835 PdfObj::Array(arr) => {
836 let items: Vec<WriterObj> = arr
837 .iter()
838 .map(|item| self.convert_pdf_object_to_writer(item))
839 .collect();
840 WriterObj::Array(items)
841 }
842 PdfObj::Dictionary(dict) => {
843 WriterObj::Dictionary(self.convert_pdf_objects_dict_to_writer(dict))
844 }
845 PdfObj::Stream(stream) => {
846 let dict = self.convert_pdf_objects_dict_to_writer(&stream.dict);
847 WriterObj::Stream(dict, stream.data.clone())
848 }
849 PdfObj::Reference(id) => {
850 WriterObj::Reference(crate::objects::ObjectId::new(id.number(), id.generation()))
851 }
852 }
853 }
854
855 fn write_catalog(&mut self, document: &mut Document) -> Result<()> {
856 let catalog_id = self.get_catalog_id()?;
857 let pages_id = self.get_pages_id()?;
858
859 let mut catalog = Dictionary::new();
860 catalog.set("Type", Object::Name("Catalog".to_string()));
861 catalog.set("Pages", Object::Reference(pages_id));
862
863 if let Some(_form_manager) = &document.form_manager {
866 if document.acro_form.is_none() {
868 document.acro_form = Some(crate::forms::AcroForm::new());
869 }
870 }
871
872 if let Some(acro_form) = &document.acro_form {
874 let acro_form_id = self.allocate_object_id();
876
877 self.write_object(acro_form_id, Object::Dictionary(acro_form.to_dict()))?;
879
880 catalog.set("AcroForm", Object::Reference(acro_form_id));
882 }
883
884 if let Some(outline_tree) = &document.outline {
886 if !outline_tree.items.is_empty() {
887 let outline_root_id = self.write_outline_tree(outline_tree)?;
888 catalog.set("Outlines", Object::Reference(outline_root_id));
889 }
890 }
891
892 if let Some(struct_tree) = &document.struct_tree {
894 if !struct_tree.is_empty() {
895 let struct_tree_root_id = self.write_struct_tree(struct_tree)?;
896 catalog.set("StructTreeRoot", Object::Reference(struct_tree_root_id));
897 catalog.set("MarkInfo", {
899 let mut mark_info = Dictionary::new();
900 mark_info.set("Marked", Object::Boolean(true));
901 Object::Dictionary(mark_info)
902 });
903 }
904 }
905
906 let xmp_metadata = document.create_xmp_metadata();
909 let xmp_packet = xmp_metadata.to_xmp_packet();
910 let metadata_id = self.allocate_object_id();
911
912 let mut metadata_dict = Dictionary::new();
914 metadata_dict.set("Type", Object::Name("Metadata".to_string()));
915 metadata_dict.set("Subtype", Object::Name("XML".to_string()));
916 metadata_dict.set("Length", Object::Integer(xmp_packet.len() as i64));
917
918 self.write_object(
920 metadata_id,
921 Object::Stream(metadata_dict, xmp_packet.into_bytes()),
922 )?;
923
924 catalog.set("Metadata", Object::Reference(metadata_id));
926
927 self.write_object(catalog_id, Object::Dictionary(catalog))?;
928 Ok(())
929 }
930
931 fn write_page_content(&mut self, content_id: ObjectId, page: &crate::page::Page) -> Result<()> {
932 let mut page_copy = page.clone();
933 let content = page_copy.generate_content()?;
934
935 #[cfg(feature = "compression")]
937 {
938 use crate::objects::Stream;
939 let mut stream = Stream::new(content);
940 if self.config.compress_streams {
942 stream.compress_flate()?;
943 }
944
945 self.write_object(
946 content_id,
947 Object::Stream(stream.dictionary().clone(), stream.data().to_vec()),
948 )?;
949 }
950
951 #[cfg(not(feature = "compression"))]
952 {
953 let mut stream_dict = Dictionary::new();
954 stream_dict.set("Length", Object::Integer(content.len() as i64));
955
956 self.write_object(content_id, Object::Stream(stream_dict, content))?;
957 }
958
959 Ok(())
960 }
961
962 fn write_outline_tree(
963 &mut self,
964 outline_tree: &crate::structure::OutlineTree,
965 ) -> Result<ObjectId> {
966 let outline_root_id = self.allocate_object_id();
968
969 let mut outline_root = Dictionary::new();
970 outline_root.set("Type", Object::Name("Outlines".to_string()));
971
972 if !outline_tree.items.is_empty() {
973 let mut item_ids = Vec::new();
975
976 fn count_items(items: &[crate::structure::OutlineItem]) -> usize {
978 let mut count = items.len();
979 for item in items {
980 count += count_items(&item.children);
981 }
982 count
983 }
984
985 let total_items = count_items(&outline_tree.items);
986
987 for _ in 0..total_items {
989 item_ids.push(self.allocate_object_id());
990 }
991
992 let mut id_index = 0;
993
994 let first_id = item_ids[0];
996 let last_id = item_ids[outline_tree.items.len() - 1];
997
998 outline_root.set("First", Object::Reference(first_id));
999 outline_root.set("Last", Object::Reference(last_id));
1000
1001 let visible_count = outline_tree.visible_count();
1003 outline_root.set("Count", Object::Integer(visible_count));
1004
1005 let mut written_items = Vec::new();
1007
1008 for (i, item) in outline_tree.items.iter().enumerate() {
1009 let item_id = item_ids[id_index];
1010 id_index += 1;
1011
1012 let prev_id = if i > 0 { Some(item_ids[i - 1]) } else { None };
1013 let next_id = if i < outline_tree.items.len() - 1 {
1014 Some(item_ids[i + 1])
1015 } else {
1016 None
1017 };
1018
1019 let children_ids = self.write_outline_item(
1021 item,
1022 item_id,
1023 outline_root_id,
1024 prev_id,
1025 next_id,
1026 &mut item_ids,
1027 &mut id_index,
1028 )?;
1029
1030 written_items.extend(children_ids);
1031 }
1032 }
1033
1034 self.write_object(outline_root_id, Object::Dictionary(outline_root))?;
1035 Ok(outline_root_id)
1036 }
1037
1038 #[allow(clippy::too_many_arguments)]
1039 fn write_outline_item(
1040 &mut self,
1041 item: &crate::structure::OutlineItem,
1042 item_id: ObjectId,
1043 parent_id: ObjectId,
1044 prev_id: Option<ObjectId>,
1045 next_id: Option<ObjectId>,
1046 all_ids: &mut Vec<ObjectId>,
1047 id_index: &mut usize,
1048 ) -> Result<Vec<ObjectId>> {
1049 let mut written_ids = vec![item_id];
1050
1051 let (first_child_id, last_child_id) = if !item.children.is_empty() {
1053 let first_idx = *id_index;
1054 let first_id = all_ids[first_idx];
1055 let last_idx = first_idx + item.children.len() - 1;
1056 let last_id = all_ids[last_idx];
1057
1058 for (i, child) in item.children.iter().enumerate() {
1060 let child_id = all_ids[*id_index];
1061 *id_index += 1;
1062
1063 let child_prev = if i > 0 {
1064 Some(all_ids[first_idx + i - 1])
1065 } else {
1066 None
1067 };
1068 let child_next = if i < item.children.len() - 1 {
1069 Some(all_ids[first_idx + i + 1])
1070 } else {
1071 None
1072 };
1073
1074 let child_ids = self.write_outline_item(
1075 child, child_id, item_id, child_prev, child_next, all_ids, id_index,
1077 )?;
1078
1079 written_ids.extend(child_ids);
1080 }
1081
1082 (Some(first_id), Some(last_id))
1083 } else {
1084 (None, None)
1085 };
1086
1087 let item_dict = crate::structure::outline_item_to_dict(
1089 item,
1090 parent_id,
1091 first_child_id,
1092 last_child_id,
1093 prev_id,
1094 next_id,
1095 );
1096
1097 self.write_object(item_id, Object::Dictionary(item_dict))?;
1098
1099 Ok(written_ids)
1100 }
1101
1102 fn write_struct_tree(
1104 &mut self,
1105 struct_tree: &crate::structure::StructTree,
1106 ) -> Result<ObjectId> {
1107 let struct_tree_root_id = self.allocate_object_id();
1109 let mut element_ids = Vec::new();
1110 for _ in 0..struct_tree.len() {
1111 element_ids.push(self.allocate_object_id());
1112 }
1113
1114 let mut parent_map: std::collections::HashMap<usize, ObjectId> =
1116 std::collections::HashMap::new();
1117
1118 if let Some(root_index) = struct_tree.root_index() {
1120 parent_map.insert(root_index, struct_tree_root_id);
1121
1122 fn map_children_parents(
1124 tree: &crate::structure::StructTree,
1125 parent_index: usize,
1126 parent_id: ObjectId,
1127 element_ids: &[ObjectId],
1128 parent_map: &mut std::collections::HashMap<usize, ObjectId>,
1129 ) {
1130 if let Some(parent_elem) = tree.get(parent_index) {
1131 for &child_index in &parent_elem.children {
1132 parent_map.insert(child_index, parent_id);
1133 map_children_parents(
1134 tree,
1135 child_index,
1136 element_ids[child_index],
1137 element_ids,
1138 parent_map,
1139 );
1140 }
1141 }
1142 }
1143
1144 map_children_parents(
1145 struct_tree,
1146 root_index,
1147 element_ids[root_index],
1148 &element_ids,
1149 &mut parent_map,
1150 );
1151 }
1152
1153 for (index, element) in struct_tree.iter().enumerate() {
1155 let element_id = element_ids[index];
1156 let mut element_dict = Dictionary::new();
1157
1158 element_dict.set("Type", Object::Name("StructElem".to_string()));
1159 element_dict.set("S", Object::Name(element.structure_type.as_pdf_name()));
1160
1161 if let Some(&parent_id) = parent_map.get(&index) {
1163 element_dict.set("P", Object::Reference(parent_id));
1164 }
1165
1166 if let Some(ref id) = element.id {
1168 element_dict.set("ID", Object::String(id.clone()));
1169 }
1170
1171 if let Some(ref lang) = element.attributes.lang {
1173 element_dict.set("Lang", Object::String(lang.clone()));
1174 }
1175 if let Some(ref alt) = element.attributes.alt {
1176 element_dict.set("Alt", Object::String(alt.clone()));
1177 }
1178 if let Some(ref actual_text) = element.attributes.actual_text {
1179 element_dict.set("ActualText", Object::String(actual_text.clone()));
1180 }
1181 if let Some(ref title) = element.attributes.title {
1182 element_dict.set("T", Object::String(title.clone()));
1183 }
1184 if let Some(bbox) = element.attributes.bbox {
1185 element_dict.set(
1186 "BBox",
1187 Object::Array(vec![
1188 Object::Real(bbox[0]),
1189 Object::Real(bbox[1]),
1190 Object::Real(bbox[2]),
1191 Object::Real(bbox[3]),
1192 ]),
1193 );
1194 }
1195
1196 let mut kids = Vec::new();
1198
1199 for &child_index in &element.children {
1201 kids.push(Object::Reference(element_ids[child_index]));
1202 }
1203
1204 for mcid_ref in &element.mcids {
1206 let mut mcr = Dictionary::new();
1207 mcr.set("Type", Object::Name("MCR".to_string()));
1208 mcr.set("Pg", Object::Integer(mcid_ref.page_index as i64));
1209 mcr.set("MCID", Object::Integer(mcid_ref.mcid as i64));
1210 kids.push(Object::Dictionary(mcr));
1211 }
1212
1213 if !kids.is_empty() {
1214 element_dict.set("K", Object::Array(kids));
1215 }
1216
1217 self.write_object(element_id, Object::Dictionary(element_dict))?;
1218 }
1219
1220 let mut struct_tree_root = Dictionary::new();
1222 struct_tree_root.set("Type", Object::Name("StructTreeRoot".to_string()));
1223
1224 if let Some(root_index) = struct_tree.root_index() {
1226 struct_tree_root.set("K", Object::Reference(element_ids[root_index]));
1227 }
1228
1229 if !struct_tree.role_map.mappings().is_empty() {
1231 let mut role_map = Dictionary::new();
1232 for (custom_type, standard_type) in struct_tree.role_map.mappings() {
1233 role_map.set(
1234 custom_type.as_str(),
1235 Object::Name(standard_type.as_pdf_name().to_string()),
1236 );
1237 }
1238 struct_tree_root.set("RoleMap", Object::Dictionary(role_map));
1239 }
1240
1241 self.write_object(struct_tree_root_id, Object::Dictionary(struct_tree_root))?;
1242 Ok(struct_tree_root_id)
1243 }
1244
1245 fn write_form_fields(&mut self, document: &mut Document) -> Result<()> {
1246 if !self.form_field_ids.is_empty() {
1248 if let Some(acro_form) = &mut document.acro_form {
1249 acro_form.fields.clear();
1251 for field_id in &self.form_field_ids {
1252 acro_form.add_field(*field_id);
1253 }
1254
1255 acro_form.need_appearances = true;
1257 if acro_form.da.is_none() {
1258 acro_form.da = Some("/Helv 12 Tf 0 g".to_string());
1259 }
1260 }
1261 }
1262 Ok(())
1263 }
1264
1265 fn write_info(&mut self, document: &Document) -> Result<()> {
1266 let info_id = self.get_info_id()?;
1267 let mut info_dict = Dictionary::new();
1268
1269 if let Some(ref title) = document.metadata.title {
1270 info_dict.set("Title", Object::String(title.clone()));
1271 }
1272 if let Some(ref author) = document.metadata.author {
1273 info_dict.set("Author", Object::String(author.clone()));
1274 }
1275 if let Some(ref subject) = document.metadata.subject {
1276 info_dict.set("Subject", Object::String(subject.clone()));
1277 }
1278 if let Some(ref keywords) = document.metadata.keywords {
1279 info_dict.set("Keywords", Object::String(keywords.clone()));
1280 }
1281 if let Some(ref creator) = document.metadata.creator {
1282 info_dict.set("Creator", Object::String(creator.clone()));
1283 }
1284 if let Some(ref producer) = document.metadata.producer {
1285 info_dict.set("Producer", Object::String(producer.clone()));
1286 }
1287
1288 if let Some(creation_date) = document.metadata.creation_date {
1290 let date_string = format_pdf_date(creation_date);
1291 info_dict.set("CreationDate", Object::String(date_string));
1292 }
1293
1294 if let Some(mod_date) = document.metadata.modification_date {
1296 let date_string = format_pdf_date(mod_date);
1297 info_dict.set("ModDate", Object::String(date_string));
1298 }
1299
1300 let edition = super::Edition::OpenSource;
1303
1304 let signature = super::PdfSignature::new(document, edition);
1305 signature.write_to_info_dict(&mut info_dict);
1306
1307 self.write_object(info_id, Object::Dictionary(info_dict))?;
1308 Ok(())
1309 }
1310
1311 fn write_fonts(&mut self, document: &Document) -> Result<HashMap<String, ObjectId>> {
1312 let mut font_refs = HashMap::new();
1313
1314 for font_name in document.custom_font_names() {
1316 if let Some(font) = document.get_custom_font(&font_name) {
1317 let font_id = self.write_font_with_unicode_support(&font_name, &font)?;
1320 font_refs.insert(font_name.clone(), font_id);
1321 }
1322 }
1323
1324 Ok(font_refs)
1325 }
1326
1327 fn write_font_with_unicode_support(
1329 &mut self,
1330 font_name: &str,
1331 font: &crate::fonts::Font,
1332 ) -> Result<ObjectId> {
1333 self.write_type0_font_from_font(font_name, font)
1336 }
1337
1338 fn write_type0_font_from_font(
1340 &mut self,
1341 font_name: &str,
1342 font: &crate::fonts::Font,
1343 ) -> Result<ObjectId> {
1344 let used_chars = self.document_used_chars.clone().unwrap_or_else(|| {
1346 let mut chars = std::collections::HashSet::new();
1348 for ch in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 .,!?".chars()
1349 {
1350 chars.insert(ch);
1351 }
1352 chars
1353 });
1354 let font_id = self.allocate_object_id();
1356 let descendant_font_id = self.allocate_object_id();
1357 let descriptor_id = self.allocate_object_id();
1358 let font_file_id = self.allocate_object_id();
1359 let to_unicode_id = self.allocate_object_id();
1360
1361 let (font_data_to_embed, subset_glyph_mapping, original_font_for_widths) =
1365 if font.data.len() > 100_000 && !used_chars.is_empty() {
1366 match crate::text::fonts::truetype_subsetter::subset_font(
1368 font.data.clone(),
1369 &used_chars,
1370 ) {
1371 Ok(subset_result) => {
1372 (
1375 subset_result.font_data,
1376 Some(subset_result.glyph_mapping),
1377 font.clone(),
1378 )
1379 }
1380 Err(_) => {
1381 if font.data.len() < 25_000_000 {
1383 (font.data.clone(), None, font.clone())
1384 } else {
1385 (Vec::new(), None, font.clone())
1387 }
1388 }
1389 }
1390 } else {
1391 (font.data.clone(), None, font.clone())
1393 };
1394
1395 if !font_data_to_embed.is_empty() {
1396 let mut font_file_dict = Dictionary::new();
1397 match font.format {
1399 crate::fonts::FontFormat::OpenType => {
1400 font_file_dict.set("Subtype", Object::Name("OpenType".to_string()));
1402 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1403 }
1404 crate::fonts::FontFormat::TrueType => {
1405 font_file_dict.set("Length1", Object::Integer(font_data_to_embed.len() as i64));
1407 }
1408 }
1409 let font_stream_obj = Object::Stream(font_file_dict, font_data_to_embed);
1410 self.write_object(font_file_id, font_stream_obj)?;
1411 } else {
1412 let font_file_dict = Dictionary::new();
1414 let font_stream_obj = Object::Stream(font_file_dict, Vec::new());
1415 self.write_object(font_file_id, font_stream_obj)?;
1416 }
1417
1418 let mut descriptor = Dictionary::new();
1420 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
1421 descriptor.set("FontName", Object::Name(font_name.to_string()));
1422 descriptor.set("Flags", Object::Integer(4)); descriptor.set(
1424 "FontBBox",
1425 Object::Array(vec![
1426 Object::Integer(font.descriptor.font_bbox[0] as i64),
1427 Object::Integer(font.descriptor.font_bbox[1] as i64),
1428 Object::Integer(font.descriptor.font_bbox[2] as i64),
1429 Object::Integer(font.descriptor.font_bbox[3] as i64),
1430 ]),
1431 );
1432 descriptor.set(
1433 "ItalicAngle",
1434 Object::Real(font.descriptor.italic_angle as f64),
1435 );
1436 descriptor.set("Ascent", Object::Real(font.descriptor.ascent as f64));
1437 descriptor.set("Descent", Object::Real(font.descriptor.descent as f64));
1438 descriptor.set("CapHeight", Object::Real(font.descriptor.cap_height as f64));
1439 descriptor.set("StemV", Object::Real(font.descriptor.stem_v as f64));
1440 let font_file_key = match font.format {
1442 crate::fonts::FontFormat::OpenType => "FontFile3", crate::fonts::FontFormat::TrueType => "FontFile2", };
1445 descriptor.set(font_file_key, Object::Reference(font_file_id));
1446 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
1447
1448 let mut cid_font = Dictionary::new();
1450 cid_font.set("Type", Object::Name("Font".to_string()));
1451 let cid_font_subtype =
1453 if CjkFontType::should_use_cidfonttype2_for_preview_compatibility(font_name) {
1454 "CIDFontType2" } else {
1456 match font.format {
1457 crate::fonts::FontFormat::OpenType => "CIDFontType0", crate::fonts::FontFormat::TrueType => "CIDFontType2", }
1460 };
1461 cid_font.set("Subtype", Object::Name(cid_font_subtype.to_string()));
1462 cid_font.set("BaseFont", Object::Name(font_name.to_string()));
1463
1464 let mut cid_system_info = Dictionary::new();
1466 let (registry, ordering, supplement) =
1467 if let Some(cjk_type) = CjkFontType::detect_from_name(font_name) {
1468 cjk_type.cid_system_info()
1469 } else {
1470 ("Adobe", "Identity", 0)
1471 };
1472
1473 cid_system_info.set("Registry", Object::String(registry.to_string()));
1474 cid_system_info.set("Ordering", Object::String(ordering.to_string()));
1475 cid_system_info.set("Supplement", Object::Integer(supplement as i64));
1476 cid_font.set("CIDSystemInfo", Object::Dictionary(cid_system_info));
1477
1478 cid_font.set("FontDescriptor", Object::Reference(descriptor_id));
1479
1480 let default_width = self.calculate_default_width(font);
1482 cid_font.set("DW", Object::Integer(default_width));
1483
1484 let w_array = self.generate_width_array(
1488 &original_font_for_widths,
1489 default_width,
1490 subset_glyph_mapping.as_ref(),
1491 );
1492 cid_font.set("W", Object::Array(w_array));
1493
1494 if cid_font_subtype == "CIDFontType2" {
1498 let cid_to_gid_map =
1500 self.generate_cid_to_gid_map(font, subset_glyph_mapping.as_ref())?;
1501 if !cid_to_gid_map.is_empty() {
1502 let cid_to_gid_map_id = self.allocate_object_id();
1504 let mut map_dict = Dictionary::new();
1505 map_dict.set("Length", Object::Integer(cid_to_gid_map.len() as i64));
1506 let map_stream = Object::Stream(map_dict, cid_to_gid_map);
1507 self.write_object(cid_to_gid_map_id, map_stream)?;
1508 cid_font.set("CIDToGIDMap", Object::Reference(cid_to_gid_map_id));
1509 } else {
1510 cid_font.set("CIDToGIDMap", Object::Name("Identity".to_string()));
1511 }
1512 }
1513 self.write_object(descendant_font_id, Object::Dictionary(cid_font))?;
1516
1517 let cmap_data = self.generate_tounicode_cmap_from_font(font);
1519 let cmap_dict = Dictionary::new();
1520 let cmap_stream = Object::Stream(cmap_dict, cmap_data);
1521 self.write_object(to_unicode_id, cmap_stream)?;
1522
1523 let mut type0_font = Dictionary::new();
1525 type0_font.set("Type", Object::Name("Font".to_string()));
1526 type0_font.set("Subtype", Object::Name("Type0".to_string()));
1527 type0_font.set("BaseFont", Object::Name(font_name.to_string()));
1528 type0_font.set("Encoding", Object::Name("Identity-H".to_string()));
1529 type0_font.set(
1530 "DescendantFonts",
1531 Object::Array(vec![Object::Reference(descendant_font_id)]),
1532 );
1533 type0_font.set("ToUnicode", Object::Reference(to_unicode_id));
1534
1535 self.write_object(font_id, Object::Dictionary(type0_font))?;
1536
1537 Ok(font_id)
1538 }
1539
1540 fn calculate_default_width(&self, font: &crate::fonts::Font) -> i64 {
1542 use crate::text::fonts::truetype::TrueTypeFont;
1543
1544 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1546 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1547 if let Some(cmap) = cmap_tables
1548 .iter()
1549 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
1550 .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0))
1551 {
1552 if let Ok(widths) = tt_font.get_glyph_widths(&cmap.mappings) {
1553 let common_chars =
1557 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ";
1558 let mut total_width = 0;
1559 let mut count = 0;
1560
1561 for ch in common_chars.chars() {
1562 let unicode = ch as u32;
1563 if let Some(&pdf_width) = widths.get(&unicode) {
1564 total_width += pdf_width as i64;
1565 count += 1;
1566 }
1567 }
1568
1569 if count > 0 {
1570 return total_width / count;
1571 }
1572 }
1573 }
1574 }
1575 }
1576
1577 500
1579 }
1580
1581 fn generate_width_array(
1583 &self,
1584 font: &crate::fonts::Font,
1585 _default_width: i64,
1586 subset_mapping: Option<&HashMap<u32, u16>>,
1587 ) -> Vec<Object> {
1588 use crate::text::fonts::truetype::TrueTypeFont;
1589
1590 let mut w_array = Vec::new();
1591
1592 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1594 let char_to_glyph = {
1598 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1600 if let Some(cmap) = cmap_tables
1601 .iter()
1602 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
1603 .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0))
1604 {
1605 if let Some(subset_map) = subset_mapping {
1607 let mut filtered = HashMap::new();
1608 for unicode in subset_map.keys() {
1609 if let Some(&orig_glyph) = cmap.mappings.get(unicode) {
1611 filtered.insert(*unicode, orig_glyph);
1612 }
1613 }
1614 filtered
1615 } else {
1616 cmap.mappings.clone()
1617 }
1618 } else {
1619 HashMap::new()
1620 }
1621 } else {
1622 HashMap::new()
1623 }
1624 };
1625
1626 if !char_to_glyph.is_empty() {
1627 if let Ok(widths) = tt_font.get_glyph_widths(&char_to_glyph) {
1629 let mut sorted_chars: Vec<_> = widths.iter().collect();
1634 sorted_chars.sort_by_key(|(unicode, _)| *unicode);
1635
1636 let mut i = 0;
1637 while i < sorted_chars.len() {
1638 let start_unicode = *sorted_chars[i].0;
1639 let pdf_width = *sorted_chars[i].1 as i64;
1641
1642 let mut end_unicode = start_unicode;
1644 let mut j = i + 1;
1645 while j < sorted_chars.len() && *sorted_chars[j].0 == end_unicode + 1 {
1646 let next_pdf_width = *sorted_chars[j].1 as i64;
1647 if next_pdf_width == pdf_width {
1648 end_unicode = *sorted_chars[j].0;
1649 j += 1;
1650 } else {
1651 break;
1652 }
1653 }
1654
1655 if start_unicode == end_unicode {
1657 w_array.push(Object::Integer(start_unicode as i64));
1659 w_array.push(Object::Array(vec![Object::Integer(pdf_width)]));
1660 } else {
1661 w_array.push(Object::Integer(start_unicode as i64));
1663 w_array.push(Object::Integer(end_unicode as i64));
1664 w_array.push(Object::Integer(pdf_width));
1665 }
1666
1667 i = j;
1668 }
1669
1670 return w_array;
1671 }
1672 }
1673 }
1674
1675 let ranges = vec![
1677 (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), ];
1714
1715 for (start, end, width) in ranges {
1717 if start == end {
1718 w_array.push(Object::Integer(start));
1720 w_array.push(Object::Array(vec![Object::Integer(width)]));
1721 } else {
1722 w_array.push(Object::Integer(start));
1724 w_array.push(Object::Integer(end));
1725 w_array.push(Object::Integer(width));
1726 }
1727 }
1728
1729 w_array
1730 }
1731
1732 fn generate_cid_to_gid_map(
1734 &mut self,
1735 font: &crate::fonts::Font,
1736 subset_mapping: Option<&HashMap<u32, u16>>,
1737 ) -> Result<Vec<u8>> {
1738 use crate::text::fonts::truetype::TrueTypeFont;
1739
1740 let cmap_mappings = if let Some(subset_map) = subset_mapping {
1743 subset_map.clone()
1745 } else {
1746 let tt_font = TrueTypeFont::parse(font.data.clone())?;
1748 let cmap_tables = tt_font.parse_cmap()?;
1749
1750 let cmap = cmap_tables
1752 .iter()
1753 .find(|t| t.platform_id == 3 && t.encoding_id == 1) .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0)) .ok_or_else(|| {
1756 crate::error::PdfError::FontError("No Unicode cmap table found".to_string())
1757 })?;
1758
1759 cmap.mappings.clone()
1760 };
1761
1762 let used_chars = self.document_used_chars.clone().unwrap_or_default();
1769
1770 let max_unicode = if !used_chars.is_empty() {
1772 used_chars
1774 .iter()
1775 .map(|ch| *ch as u32)
1776 .max()
1777 .unwrap_or(0x00FF) .min(0xFFFF) as usize
1779 } else {
1780 cmap_mappings
1782 .keys()
1783 .max()
1784 .copied()
1785 .unwrap_or(0xFFFF)
1786 .min(0xFFFF) as usize
1787 };
1788
1789 let mut map = vec![0u8; (max_unicode + 1) * 2];
1791
1792 let mut sample_mappings = Vec::new();
1794 for (&unicode, &glyph_id) in &cmap_mappings {
1795 if unicode <= max_unicode as u32 {
1796 let idx = (unicode as usize) * 2;
1797 map[idx] = (glyph_id >> 8) as u8;
1799 map[idx + 1] = (glyph_id & 0xFF) as u8;
1800
1801 if unicode == 0x0041 || unicode == 0x0061 || unicode == 0x00E1 || unicode == 0x00F1
1803 {
1804 sample_mappings.push((unicode, glyph_id));
1805 }
1806 }
1807 }
1808
1809 Ok(map)
1810 }
1811
1812 fn generate_tounicode_cmap_from_font(&self, font: &crate::fonts::Font) -> Vec<u8> {
1814 use crate::text::fonts::truetype::TrueTypeFont;
1815
1816 let mut cmap = String::new();
1817
1818 cmap.push_str("/CIDInit /ProcSet findresource begin\n");
1820 cmap.push_str("12 dict begin\n");
1821 cmap.push_str("begincmap\n");
1822 cmap.push_str("/CIDSystemInfo\n");
1823 cmap.push_str("<< /Registry (Adobe)\n");
1824 cmap.push_str(" /Ordering (UCS)\n");
1825 cmap.push_str(" /Supplement 0\n");
1826 cmap.push_str(">> def\n");
1827 cmap.push_str("/CMapName /Adobe-Identity-UCS def\n");
1828 cmap.push_str("/CMapType 2 def\n");
1829 cmap.push_str("1 begincodespacerange\n");
1830 cmap.push_str("<0000> <FFFF>\n");
1831 cmap.push_str("endcodespacerange\n");
1832
1833 let mut mappings = Vec::new();
1835 let mut has_font_mappings = false;
1836
1837 if let Ok(tt_font) = TrueTypeFont::parse(font.data.clone()) {
1838 if let Ok(cmap_tables) = tt_font.parse_cmap() {
1839 if let Some(cmap_table) = cmap_tables
1841 .iter()
1842 .find(|t| t.platform_id == 3 && t.encoding_id == 1) .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0))
1844 {
1846 for (&unicode, &glyph_id) in &cmap_table.mappings {
1849 if glyph_id > 0 && unicode <= 0xFFFF {
1850 mappings.push((unicode, unicode));
1853 }
1854 }
1855 has_font_mappings = true;
1856 }
1857 }
1858 }
1859
1860 if !has_font_mappings {
1862 for i in 0x0020..=0x00FF {
1864 mappings.push((i, i));
1865 }
1866
1867 for i in 0x0100..=0x017F {
1869 mappings.push((i, i));
1870 }
1871
1872 for i in 0x3040..=0x309F {
1875 mappings.push((i, i));
1876 }
1877
1878 for i in 0x30A0..=0x30FF {
1880 mappings.push((i, i));
1881 }
1882
1883 for i in 0x4E00..=0x9FFF {
1885 mappings.push((i, i));
1886 }
1887
1888 for i in 0xAC00..=0xD7AF {
1890 mappings.push((i, i));
1891 }
1892
1893 for i in 0x2000..=0x206F {
1895 mappings.push((i, i));
1896 }
1897
1898 for i in 0x2200..=0x22FF {
1900 mappings.push((i, i));
1901 }
1902
1903 for i in 0x2190..=0x21FF {
1905 mappings.push((i, i));
1906 }
1907
1908 for i in 0x2500..=0x259F {
1910 mappings.push((i, i));
1911 }
1912
1913 for i in 0x25A0..=0x25FF {
1915 mappings.push((i, i));
1916 }
1917
1918 for i in 0x2600..=0x26FF {
1920 mappings.push((i, i));
1921 }
1922 }
1923
1924 mappings.sort_by_key(|&(cid, _)| cid);
1926
1927 let mut i = 0;
1929 while i < mappings.len() {
1930 let start_cid = mappings[i].0;
1932 let start_unicode = mappings[i].1;
1933 let mut end_idx = i;
1934
1935 while end_idx + 1 < mappings.len()
1937 && mappings[end_idx + 1].0 == mappings[end_idx].0 + 1
1938 && mappings[end_idx + 1].1 == mappings[end_idx].1 + 1
1939 && end_idx - i < 99
1940 {
1942 end_idx += 1;
1943 }
1944
1945 if end_idx > i {
1946 cmap.push_str("1 beginbfrange\n");
1948 cmap.push_str(&format!(
1949 "<{:04X}> <{:04X}> <{:04X}>\n",
1950 start_cid, mappings[end_idx].0, start_unicode
1951 ));
1952 cmap.push_str("endbfrange\n");
1953 i = end_idx + 1;
1954 } else {
1955 let mut chars = Vec::new();
1957 let chunk_end = (i + 100).min(mappings.len());
1958
1959 for item in &mappings[i..chunk_end] {
1960 chars.push(*item);
1961 }
1962
1963 if !chars.is_empty() {
1964 cmap.push_str(&format!("{} beginbfchar\n", chars.len()));
1965 for (cid, unicode) in chars {
1966 cmap.push_str(&format!("<{:04X}> <{:04X}>\n", cid, unicode));
1967 }
1968 cmap.push_str("endbfchar\n");
1969 }
1970
1971 i = chunk_end;
1972 }
1973 }
1974
1975 cmap.push_str("endcmap\n");
1977 cmap.push_str("CMapName currentdict /CMap defineresource pop\n");
1978 cmap.push_str("end\n");
1979 cmap.push_str("end\n");
1980
1981 cmap.into_bytes()
1982 }
1983
1984 #[allow(dead_code)]
1986 fn write_truetype_font(
1987 &mut self,
1988 font_name: &str,
1989 font: &crate::text::font_manager::CustomFont,
1990 ) -> Result<ObjectId> {
1991 let font_id = self.allocate_object_id();
1993 let descriptor_id = self.allocate_object_id();
1994 let font_file_id = self.allocate_object_id();
1995
1996 if let Some(ref data) = font.font_data {
1998 let mut font_file_dict = Dictionary::new();
1999 font_file_dict.set("Length1", Object::Integer(data.len() as i64));
2000 let font_stream_obj = Object::Stream(font_file_dict, data.clone());
2001 self.write_object(font_file_id, font_stream_obj)?;
2002 }
2003
2004 let mut descriptor = Dictionary::new();
2006 descriptor.set("Type", Object::Name("FontDescriptor".to_string()));
2007 descriptor.set("FontName", Object::Name(font_name.to_string()));
2008 descriptor.set("Flags", Object::Integer(32)); descriptor.set(
2010 "FontBBox",
2011 Object::Array(vec![
2012 Object::Integer(-1000),
2013 Object::Integer(-1000),
2014 Object::Integer(2000),
2015 Object::Integer(2000),
2016 ]),
2017 );
2018 descriptor.set("ItalicAngle", Object::Integer(0));
2019 descriptor.set("Ascent", Object::Integer(font.descriptor.ascent as i64));
2020 descriptor.set("Descent", Object::Integer(font.descriptor.descent as i64));
2021 descriptor.set(
2022 "CapHeight",
2023 Object::Integer(font.descriptor.cap_height as i64),
2024 );
2025 descriptor.set("StemV", Object::Integer(font.descriptor.stem_v as i64));
2026 descriptor.set("FontFile2", Object::Reference(font_file_id));
2027 self.write_object(descriptor_id, Object::Dictionary(descriptor))?;
2028
2029 let mut font_dict = Dictionary::new();
2031 font_dict.set("Type", Object::Name("Font".to_string()));
2032 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
2033 font_dict.set("BaseFont", Object::Name(font_name.to_string()));
2034 font_dict.set("FirstChar", Object::Integer(0));
2035 font_dict.set("LastChar", Object::Integer(255));
2036
2037 let widths: Vec<Object> = (0..256).map(|_| Object::Integer(600)).collect();
2039 font_dict.set("Widths", Object::Array(widths));
2040 font_dict.set("FontDescriptor", Object::Reference(descriptor_id));
2041
2042 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2044
2045 self.write_object(font_id, Object::Dictionary(font_dict))?;
2046
2047 Ok(font_id)
2048 }
2049
2050 fn write_pages(
2051 &mut self,
2052 document: &Document,
2053 font_refs: &HashMap<String, ObjectId>,
2054 ) -> Result<()> {
2055 let pages_id = self.get_pages_id()?;
2056 let mut pages_dict = Dictionary::new();
2057 pages_dict.set("Type", Object::Name("Pages".to_string()));
2058 pages_dict.set("Count", Object::Integer(document.pages.len() as i64));
2059
2060 let mut kids = Vec::new();
2061
2062 let mut page_ids = Vec::new();
2064 let mut content_ids = Vec::new();
2065 for _ in 0..document.pages.len() {
2066 page_ids.push(self.allocate_object_id());
2067 content_ids.push(self.allocate_object_id());
2068 }
2069
2070 for page_id in &page_ids {
2071 kids.push(Object::Reference(*page_id));
2072 }
2073
2074 pages_dict.set("Kids", Object::Array(kids));
2075
2076 self.write_object(pages_id, Object::Dictionary(pages_dict))?;
2077
2078 self.page_ids = page_ids.clone();
2080
2081 for (i, page) in document.pages.iter().enumerate() {
2083 let page_id = page_ids[i];
2084 let content_id = content_ids[i];
2085
2086 self.write_page_with_fonts(page_id, pages_id, content_id, page, document, font_refs)?;
2087 self.write_page_content(content_id, page)?;
2088 }
2089
2090 Ok(())
2091 }
2092
2093 #[allow(dead_code)]
2095 fn write_pages_with_fonts(
2096 &mut self,
2097 document: &Document,
2098 font_refs: &HashMap<String, ObjectId>,
2099 ) -> Result<()> {
2100 self.write_pages(document, font_refs)
2101 }
2102
2103 fn write_page_with_fonts(
2104 &mut self,
2105 page_id: ObjectId,
2106 parent_id: ObjectId,
2107 content_id: ObjectId,
2108 page: &crate::page::Page,
2109 _document: &Document,
2110 font_refs: &HashMap<String, ObjectId>,
2111 ) -> Result<()> {
2112 let mut page_dict = page.to_dict();
2114
2115 page_dict.set("Type", Object::Name("Page".to_string()));
2116 page_dict.set("Parent", Object::Reference(parent_id));
2117 page_dict.set("Contents", Object::Reference(content_id));
2118
2119 let mut resources = if let Some(Object::Dictionary(res)) = page_dict.get("Resources") {
2121 res.clone()
2122 } else {
2123 Dictionary::new()
2124 };
2125
2126 let mut font_dict = Dictionary::new();
2128
2129 let mut helvetica_dict = Dictionary::new();
2134 helvetica_dict.set("Type", Object::Name("Font".to_string()));
2135 helvetica_dict.set("Subtype", Object::Name("Type1".to_string()));
2136 helvetica_dict.set("BaseFont", Object::Name("Helvetica".to_string()));
2137 helvetica_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2138 font_dict.set("Helvetica", Object::Dictionary(helvetica_dict));
2139
2140 let mut helvetica_bold_dict = Dictionary::new();
2141 helvetica_bold_dict.set("Type", Object::Name("Font".to_string()));
2142 helvetica_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2143 helvetica_bold_dict.set("BaseFont", Object::Name("Helvetica-Bold".to_string()));
2144 helvetica_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2145 font_dict.set("Helvetica-Bold", Object::Dictionary(helvetica_bold_dict));
2146
2147 let mut helvetica_oblique_dict = Dictionary::new();
2148 helvetica_oblique_dict.set("Type", Object::Name("Font".to_string()));
2149 helvetica_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2150 helvetica_oblique_dict.set("BaseFont", Object::Name("Helvetica-Oblique".to_string()));
2151 helvetica_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2152 font_dict.set(
2153 "Helvetica-Oblique",
2154 Object::Dictionary(helvetica_oblique_dict),
2155 );
2156
2157 let mut helvetica_bold_oblique_dict = Dictionary::new();
2158 helvetica_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2159 helvetica_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2160 helvetica_bold_oblique_dict.set(
2161 "BaseFont",
2162 Object::Name("Helvetica-BoldOblique".to_string()),
2163 );
2164 helvetica_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2165 font_dict.set(
2166 "Helvetica-BoldOblique",
2167 Object::Dictionary(helvetica_bold_oblique_dict),
2168 );
2169
2170 let mut times_dict = Dictionary::new();
2172 times_dict.set("Type", Object::Name("Font".to_string()));
2173 times_dict.set("Subtype", Object::Name("Type1".to_string()));
2174 times_dict.set("BaseFont", Object::Name("Times-Roman".to_string()));
2175 times_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2176 font_dict.set("Times-Roman", Object::Dictionary(times_dict));
2177
2178 let mut times_bold_dict = Dictionary::new();
2179 times_bold_dict.set("Type", Object::Name("Font".to_string()));
2180 times_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2181 times_bold_dict.set("BaseFont", Object::Name("Times-Bold".to_string()));
2182 times_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2183 font_dict.set("Times-Bold", Object::Dictionary(times_bold_dict));
2184
2185 let mut times_italic_dict = Dictionary::new();
2186 times_italic_dict.set("Type", Object::Name("Font".to_string()));
2187 times_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2188 times_italic_dict.set("BaseFont", Object::Name("Times-Italic".to_string()));
2189 times_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2190 font_dict.set("Times-Italic", Object::Dictionary(times_italic_dict));
2191
2192 let mut times_bold_italic_dict = Dictionary::new();
2193 times_bold_italic_dict.set("Type", Object::Name("Font".to_string()));
2194 times_bold_italic_dict.set("Subtype", Object::Name("Type1".to_string()));
2195 times_bold_italic_dict.set("BaseFont", Object::Name("Times-BoldItalic".to_string()));
2196 times_bold_italic_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2197 font_dict.set(
2198 "Times-BoldItalic",
2199 Object::Dictionary(times_bold_italic_dict),
2200 );
2201
2202 let mut courier_dict = Dictionary::new();
2204 courier_dict.set("Type", Object::Name("Font".to_string()));
2205 courier_dict.set("Subtype", Object::Name("Type1".to_string()));
2206 courier_dict.set("BaseFont", Object::Name("Courier".to_string()));
2207 courier_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2208 font_dict.set("Courier", Object::Dictionary(courier_dict));
2209
2210 let mut courier_bold_dict = Dictionary::new();
2211 courier_bold_dict.set("Type", Object::Name("Font".to_string()));
2212 courier_bold_dict.set("Subtype", Object::Name("Type1".to_string()));
2213 courier_bold_dict.set("BaseFont", Object::Name("Courier-Bold".to_string()));
2214 courier_bold_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2215 font_dict.set("Courier-Bold", Object::Dictionary(courier_bold_dict));
2216
2217 let mut courier_oblique_dict = Dictionary::new();
2218 courier_oblique_dict.set("Type", Object::Name("Font".to_string()));
2219 courier_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2220 courier_oblique_dict.set("BaseFont", Object::Name("Courier-Oblique".to_string()));
2221 courier_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2222 font_dict.set("Courier-Oblique", Object::Dictionary(courier_oblique_dict));
2223
2224 let mut courier_bold_oblique_dict = Dictionary::new();
2225 courier_bold_oblique_dict.set("Type", Object::Name("Font".to_string()));
2226 courier_bold_oblique_dict.set("Subtype", Object::Name("Type1".to_string()));
2227 courier_bold_oblique_dict.set("BaseFont", Object::Name("Courier-BoldOblique".to_string()));
2228 courier_bold_oblique_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
2229 font_dict.set(
2230 "Courier-BoldOblique",
2231 Object::Dictionary(courier_bold_oblique_dict),
2232 );
2233
2234 for (font_name, font_id) in font_refs {
2236 font_dict.set(font_name, Object::Reference(*font_id));
2237 }
2238
2239 resources.set("Font", Object::Dictionary(font_dict));
2240
2241 if !page.images().is_empty() {
2243 let mut xobject_dict = Dictionary::new();
2244
2245 for (name, image) in page.images() {
2246 let image_id = self.allocate_object_id();
2248
2249 if image.has_transparency() {
2251 let (mut main_obj, smask_obj) = image.to_pdf_object_with_transparency()?;
2253
2254 if let Some(smask_stream) = smask_obj {
2256 let smask_id = self.allocate_object_id();
2257 self.write_object(smask_id, smask_stream)?;
2258
2259 if let Object::Stream(ref mut dict, _) = main_obj {
2261 dict.set("SMask", Object::Reference(smask_id));
2262 }
2263 }
2264
2265 self.write_object(image_id, main_obj)?;
2267 } else {
2268 self.write_object(image_id, image.to_pdf_object())?;
2270 }
2271
2272 xobject_dict.set(name, Object::Reference(image_id));
2274 }
2275
2276 resources.set("XObject", Object::Dictionary(xobject_dict));
2277 }
2278
2279 if let Some(extgstate_states) = page.get_extgstate_resources() {
2281 let mut extgstate_dict = Dictionary::new();
2282 for (name, state) in extgstate_states {
2283 let mut state_dict = Dictionary::new();
2284 state_dict.set("Type", Object::Name("ExtGState".to_string()));
2285
2286 if let Some(alpha_stroke) = state.alpha_stroke {
2288 state_dict.set("CA", Object::Real(alpha_stroke));
2289 }
2290 if let Some(alpha_fill) = state.alpha_fill {
2291 state_dict.set("ca", Object::Real(alpha_fill));
2292 }
2293
2294 if let Some(line_width) = state.line_width {
2296 state_dict.set("LW", Object::Real(line_width));
2297 }
2298 if let Some(line_cap) = state.line_cap {
2299 state_dict.set("LC", Object::Integer(line_cap as i64));
2300 }
2301 if let Some(line_join) = state.line_join {
2302 state_dict.set("LJ", Object::Integer(line_join as i64));
2303 }
2304 if let Some(dash_pattern) = &state.dash_pattern {
2305 let dash_objects: Vec<Object> = dash_pattern
2306 .array
2307 .iter()
2308 .map(|&d| Object::Real(d))
2309 .collect();
2310 state_dict.set(
2311 "D",
2312 Object::Array(vec![
2313 Object::Array(dash_objects),
2314 Object::Real(dash_pattern.phase),
2315 ]),
2316 );
2317 }
2318
2319 extgstate_dict.set(name, Object::Dictionary(state_dict));
2320 }
2321 if !extgstate_dict.is_empty() {
2322 resources.set("ExtGState", Object::Dictionary(extgstate_dict));
2323 }
2324 }
2325
2326 if let Some(preserved_res) = page.get_preserved_resources() {
2329 let mut preserved_writer_dict = self.convert_pdf_objects_dict_to_writer(preserved_res);
2331
2332 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2334 let renamed_fonts = crate::writer::rename_preserved_fonts(fonts);
2336
2337 preserved_writer_dict.set("Font", Object::Dictionary(renamed_fonts));
2339 }
2340
2341 if let Some(Object::Dictionary(fonts)) = preserved_writer_dict.get("Font") {
2345 let mut fonts_with_refs = crate::objects::Dictionary::new();
2346
2347 for (font_name, font_obj) in fonts.iter() {
2348 if let Object::Dictionary(font_dict) = font_obj {
2349 let updated_font = self.write_embedded_font_streams(font_dict)?;
2351 fonts_with_refs.set(font_name, Object::Dictionary(updated_font));
2352 } else {
2353 fonts_with_refs.set(font_name, font_obj.clone());
2355 }
2356 }
2357
2358 preserved_writer_dict.set("Font", Object::Dictionary(fonts_with_refs));
2360 }
2361
2362 for (key, value) in preserved_writer_dict.iter() {
2364 if let Some(Object::Dictionary(existing)) = resources.get(key) {
2366 if let Object::Dictionary(preserved_dict) = value {
2367 let mut merged = existing.clone();
2368 for (res_name, res_obj) in preserved_dict.iter() {
2370 if !merged.contains_key(res_name) {
2371 merged.set(res_name, res_obj.clone());
2372 }
2373 }
2374 resources.set(key, Object::Dictionary(merged));
2375 }
2376 } else {
2377 resources.set(key, value.clone());
2379 }
2380 }
2381 }
2382
2383 page_dict.set("Resources", Object::Dictionary(resources));
2384
2385 if let Some(Object::Array(annots)) = page_dict.get("Annots") {
2387 let mut new_annots = Vec::new();
2388
2389 for annot in annots {
2390 if let Object::Dictionary(ref annot_dict) = annot {
2391 if let Some(Object::Name(subtype)) = annot_dict.get("Subtype") {
2392 if subtype == "Widget" {
2393 let widget_id = self.allocate_object_id();
2395 self.write_object(widget_id, annot.clone())?;
2396 new_annots.push(Object::Reference(widget_id));
2397
2398 if let Some(Object::Name(_ft)) = annot_dict.get("FT") {
2400 if let Some(Object::String(field_name)) = annot_dict.get("T") {
2401 self.field_widget_map
2402 .entry(field_name.clone())
2403 .or_default()
2404 .push(widget_id);
2405 self.field_id_map.insert(field_name.clone(), widget_id);
2406 self.form_field_ids.push(widget_id);
2407 }
2408 }
2409 continue;
2410 }
2411 }
2412 }
2413 new_annots.push(annot.clone());
2414 }
2415
2416 if !new_annots.is_empty() {
2417 page_dict.set("Annots", Object::Array(new_annots));
2418 }
2419 }
2420
2421 self.write_object(page_id, Object::Dictionary(page_dict))?;
2422 Ok(())
2423 }
2424}
2425
2426impl PdfWriter<BufWriter<std::fs::File>> {
2427 pub fn new(path: impl AsRef<Path>) -> Result<Self> {
2428 let file = std::fs::File::create(path)?;
2429 let writer = BufWriter::new(file);
2430
2431 Ok(Self {
2432 writer,
2433 xref_positions: HashMap::new(),
2434 current_position: 0,
2435 next_object_id: 1,
2436 catalog_id: None,
2437 pages_id: None,
2438 info_id: None,
2439 field_widget_map: HashMap::new(),
2440 field_id_map: HashMap::new(),
2441 form_field_ids: Vec::new(),
2442 page_ids: Vec::new(),
2443 config: WriterConfig::default(),
2444 document_used_chars: None,
2445 buffered_objects: HashMap::new(),
2446 compressed_object_map: HashMap::new(),
2447 prev_xref_offset: None,
2448 base_pdf_size: None,
2449 encrypt_obj_id: None,
2450 file_id: None,
2451 encryption_state: None,
2452 pending_encrypt_dict: None,
2453 })
2454 }
2455}
2456
2457impl<W: Write> PdfWriter<W> {
2458 fn write_embedded_font_streams(
2474 &mut self,
2475 font_dict: &crate::objects::Dictionary,
2476 ) -> Result<crate::objects::Dictionary> {
2477 let mut updated_font = font_dict.clone();
2478
2479 if let Some(Object::Name(subtype)) = font_dict.get("Subtype") {
2481 if subtype == "Type0" {
2482 if let Some(Object::Array(descendants)) = font_dict.get("DescendantFonts") {
2484 let mut updated_descendants = Vec::new();
2485
2486 for descendant in descendants {
2487 match descendant {
2488 Object::Dictionary(cidfont) => {
2489 let updated_cidfont =
2491 self.write_cidfont_embedded_streams(cidfont)?;
2492 let cidfont_id = self.allocate_object_id();
2494 self.write_object(cidfont_id, Object::Dictionary(updated_cidfont))?;
2495 updated_descendants.push(Object::Reference(cidfont_id));
2497 }
2498 Object::Reference(_) => {
2499 updated_descendants.push(descendant.clone());
2501 }
2502 _ => {
2503 updated_descendants.push(descendant.clone());
2504 }
2505 }
2506 }
2507
2508 updated_font.set("DescendantFonts", Object::Array(updated_descendants));
2509 }
2510
2511 if let Some(Object::Stream(stream_dict, stream_data)) = font_dict.get("ToUnicode") {
2513 let tounicode_id = self.allocate_object_id();
2514 self.write_object(
2515 tounicode_id,
2516 Object::Stream(stream_dict.clone(), stream_data.clone()),
2517 )?;
2518 updated_font.set("ToUnicode", Object::Reference(tounicode_id));
2519 }
2520
2521 return Ok(updated_font);
2522 }
2523 }
2524
2525 if let Some(Object::Dictionary(descriptor)) = font_dict.get("FontDescriptor") {
2528 let mut updated_descriptor = descriptor.clone();
2529 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
2530
2531 for key in &font_file_keys {
2533 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
2534 let stream_id = self.allocate_object_id();
2536 let stream_obj = Object::Stream(stream_dict.clone(), stream_data.clone());
2537 self.write_object(stream_id, stream_obj)?;
2538
2539 updated_descriptor.set(*key, Object::Reference(stream_id));
2541 }
2542 }
2544
2545 updated_font.set("FontDescriptor", Object::Dictionary(updated_descriptor));
2547 }
2548
2549 Ok(updated_font)
2550 }
2551
2552 fn write_cidfont_embedded_streams(
2554 &mut self,
2555 cidfont: &crate::objects::Dictionary,
2556 ) -> Result<crate::objects::Dictionary> {
2557 let mut updated_cidfont = cidfont.clone();
2558
2559 if let Some(Object::Dictionary(descriptor)) = cidfont.get("FontDescriptor") {
2561 let mut updated_descriptor = descriptor.clone();
2562 let font_file_keys = ["FontFile", "FontFile2", "FontFile3"];
2563
2564 for key in &font_file_keys {
2566 if let Some(Object::Stream(stream_dict, stream_data)) = descriptor.get(*key) {
2567 let stream_id = self.allocate_object_id();
2568 self.write_object(
2569 stream_id,
2570 Object::Stream(stream_dict.clone(), stream_data.clone()),
2571 )?;
2572 updated_descriptor.set(*key, Object::Reference(stream_id));
2573 }
2574 }
2575
2576 let descriptor_id = self.allocate_object_id();
2578 self.write_object(descriptor_id, Object::Dictionary(updated_descriptor))?;
2579
2580 updated_cidfont.set("FontDescriptor", Object::Reference(descriptor_id));
2582 }
2583
2584 if let Some(Object::Stream(map_dict, map_data)) = cidfont.get("CIDToGIDMap") {
2586 let map_id = self.allocate_object_id();
2587 self.write_object(map_id, Object::Stream(map_dict.clone(), map_data.clone()))?;
2588 updated_cidfont.set("CIDToGIDMap", Object::Reference(map_id));
2589 }
2590
2591 Ok(updated_cidfont)
2592 }
2593
2594 fn allocate_object_id(&mut self) -> ObjectId {
2595 let id = ObjectId::new(self.next_object_id, 0);
2596 self.next_object_id += 1;
2597 id
2598 }
2599
2600 fn get_catalog_id(&self) -> Result<ObjectId> {
2602 self.catalog_id.ok_or_else(|| {
2603 PdfError::InvalidOperation(
2604 "catalog_id not initialized - write_document() must be called first".to_string(),
2605 )
2606 })
2607 }
2608
2609 fn get_pages_id(&self) -> Result<ObjectId> {
2611 self.pages_id.ok_or_else(|| {
2612 PdfError::InvalidOperation(
2613 "pages_id not initialized - write_document() must be called first".to_string(),
2614 )
2615 })
2616 }
2617
2618 fn get_info_id(&self) -> Result<ObjectId> {
2620 self.info_id.ok_or_else(|| {
2621 PdfError::InvalidOperation(
2622 "info_id not initialized - write_document() must be called first".to_string(),
2623 )
2624 })
2625 }
2626
2627 fn write_object(&mut self, id: ObjectId, object: Object) -> Result<()> {
2628 use crate::writer::ObjectStreamWriter;
2629
2630 let object = if let Some(ref enc_state) = self.encryption_state {
2632 let mut obj = object;
2633 enc_state.encryptor.encrypt_object(&mut obj, &id)?;
2634 obj
2635 } else {
2636 object
2637 };
2638
2639 if self.config.use_object_streams && ObjectStreamWriter::can_compress(&object) {
2641 let mut buffer = Vec::new();
2642 self.write_object_value_to_buffer(&object, &mut buffer)?;
2643 self.buffered_objects.insert(id, buffer);
2644 return Ok(());
2645 }
2646
2647 self.xref_positions.insert(id, self.current_position);
2649
2650 let header = format!("{} {} obj\n", id.number(), id.generation());
2652 self.write_bytes(header.as_bytes())?;
2653
2654 self.write_object_value(&object)?;
2655
2656 self.write_bytes(b"\nendobj\n")?;
2657 Ok(())
2658 }
2659
2660 fn write_object_value(&mut self, object: &Object) -> Result<()> {
2661 match object {
2662 Object::Null => self.write_bytes(b"null")?,
2663 Object::Boolean(b) => self.write_bytes(if *b { b"true" } else { b"false" })?,
2664 Object::Integer(i) => self.write_bytes(i.to_string().as_bytes())?,
2665 Object::Real(f) => self.write_bytes(
2666 format!("{f:.6}")
2667 .trim_end_matches('0')
2668 .trim_end_matches('.')
2669 .as_bytes(),
2670 )?,
2671 Object::String(s) => {
2672 self.write_bytes(b"(")?;
2673 self.write_bytes(s.as_bytes())?;
2674 self.write_bytes(b")")?;
2675 }
2676 Object::ByteString(bytes) => {
2677 self.write_bytes(b"<")?;
2679 for byte in bytes {
2680 self.write_bytes(format!("{byte:02X}").as_bytes())?;
2681 }
2682 self.write_bytes(b">")?;
2683 }
2684 Object::Name(n) => {
2685 self.write_bytes(b"/")?;
2686 self.write_bytes(n.as_bytes())?;
2687 }
2688 Object::Array(arr) => {
2689 self.write_bytes(b"[")?;
2690 for (i, obj) in arr.iter().enumerate() {
2691 if i > 0 {
2692 self.write_bytes(b" ")?;
2693 }
2694 self.write_object_value(obj)?;
2695 }
2696 self.write_bytes(b"]")?;
2697 }
2698 Object::Dictionary(dict) => {
2699 self.write_bytes(b"<<")?;
2700 for (key, value) in dict.entries() {
2701 self.write_bytes(b"\n/")?;
2702 self.write_bytes(key.as_bytes())?;
2703 self.write_bytes(b" ")?;
2704 self.write_object_value(value)?;
2705 }
2706 self.write_bytes(b"\n>>")?;
2707 }
2708 Object::Stream(dict, data) => {
2709 let mut corrected_dict = dict.clone();
2712 corrected_dict.set("Length", Object::Integer(data.len() as i64));
2713
2714 self.write_object_value(&Object::Dictionary(corrected_dict))?;
2715 self.write_bytes(b"\nstream\n")?;
2716 self.write_bytes(data)?;
2717 self.write_bytes(b"\nendstream")?;
2718 }
2719 Object::Reference(id) => {
2720 let ref_str = format!("{} {} R", id.number(), id.generation());
2721 self.write_bytes(ref_str.as_bytes())?;
2722 }
2723 }
2724 Ok(())
2725 }
2726
2727 fn write_object_value_to_buffer(&self, object: &Object, buffer: &mut Vec<u8>) -> Result<()> {
2729 match object {
2730 Object::Null => buffer.extend_from_slice(b"null"),
2731 Object::Boolean(b) => buffer.extend_from_slice(if *b { b"true" } else { b"false" }),
2732 Object::Integer(i) => buffer.extend_from_slice(i.to_string().as_bytes()),
2733 Object::Real(f) => buffer.extend_from_slice(
2734 format!("{f:.6}")
2735 .trim_end_matches('0')
2736 .trim_end_matches('.')
2737 .as_bytes(),
2738 ),
2739 Object::String(s) => {
2740 buffer.push(b'(');
2741 buffer.extend_from_slice(s.as_bytes());
2742 buffer.push(b')');
2743 }
2744 Object::ByteString(bytes) => {
2745 buffer.push(b'<');
2746 for byte in bytes {
2747 buffer.extend_from_slice(format!("{byte:02X}").as_bytes());
2748 }
2749 buffer.push(b'>');
2750 }
2751 Object::Name(n) => {
2752 buffer.push(b'/');
2753 buffer.extend_from_slice(n.as_bytes());
2754 }
2755 Object::Array(arr) => {
2756 buffer.push(b'[');
2757 for (i, obj) in arr.iter().enumerate() {
2758 if i > 0 {
2759 buffer.push(b' ');
2760 }
2761 self.write_object_value_to_buffer(obj, buffer)?;
2762 }
2763 buffer.push(b']');
2764 }
2765 Object::Dictionary(dict) => {
2766 buffer.extend_from_slice(b"<<");
2767 for (key, value) in dict.entries() {
2768 buffer.extend_from_slice(b"\n/");
2769 buffer.extend_from_slice(key.as_bytes());
2770 buffer.push(b' ');
2771 self.write_object_value_to_buffer(value, buffer)?;
2772 }
2773 buffer.extend_from_slice(b"\n>>");
2774 }
2775 Object::Stream(_, _) => {
2776 return Err(crate::error::PdfError::ObjectStreamError(
2778 "Cannot compress stream objects in object streams".to_string(),
2779 ));
2780 }
2781 Object::Reference(id) => {
2782 let ref_str = format!("{} {} R", id.number(), id.generation());
2783 buffer.extend_from_slice(ref_str.as_bytes());
2784 }
2785 }
2786 Ok(())
2787 }
2788
2789 fn flush_object_streams(&mut self) -> Result<()> {
2791 if self.buffered_objects.is_empty() {
2792 return Ok(());
2793 }
2794
2795 let config = ObjectStreamConfig {
2797 max_objects_per_stream: 100,
2798 compression_level: 6,
2799 enabled: true,
2800 };
2801 let mut os_writer = ObjectStreamWriter::new(config);
2802
2803 let mut buffered: Vec<_> = self.buffered_objects.iter().collect();
2805 buffered.sort_by_key(|(id, _)| id.number());
2806
2807 for (id, data) in buffered {
2809 os_writer.add_object(*id, data.clone())?;
2810 }
2811
2812 let streams = os_writer.finalize()?;
2814
2815 for mut stream in streams {
2817 let stream_id = stream.stream_id;
2818
2819 let compressed_data = stream.generate_stream_data(6)?;
2821
2822 let dict = stream.generate_dictionary(&compressed_data);
2824
2825 for (index, (obj_id, _)) in stream.objects.iter().enumerate() {
2827 self.compressed_object_map
2828 .insert(*obj_id, (stream_id, index as u32));
2829 }
2830
2831 self.xref_positions.insert(stream_id, self.current_position);
2833
2834 let header = format!("{} {} obj\n", stream_id.number(), stream_id.generation());
2835 self.write_bytes(header.as_bytes())?;
2836
2837 self.write_object_value(&Object::Dictionary(dict))?;
2838
2839 self.write_bytes(b"\nstream\n")?;
2840 self.write_bytes(&compressed_data)?;
2841 self.write_bytes(b"\nendstream\nendobj\n")?;
2842 }
2843
2844 Ok(())
2845 }
2846
2847 fn write_xref(&mut self) -> Result<()> {
2848 self.write_bytes(b"xref\n")?;
2849
2850 let mut entries: Vec<_> = self
2852 .xref_positions
2853 .iter()
2854 .map(|(id, pos)| (*id, *pos))
2855 .collect();
2856 entries.sort_by_key(|(id, _)| id.number());
2857
2858 let max_obj_num = entries.iter().map(|(id, _)| id.number()).max().unwrap_or(0);
2860
2861 self.write_bytes(b"0 ")?;
2864 self.write_bytes((max_obj_num + 1).to_string().as_bytes())?;
2865 self.write_bytes(b"\n")?;
2866
2867 self.write_bytes(b"0000000000 65535 f \n")?;
2869
2870 for obj_num in 1..=max_obj_num {
2873 let _obj_id = ObjectId::new(obj_num, 0);
2874 if let Some((_, position)) = entries.iter().find(|(id, _)| id.number() == obj_num) {
2875 let entry = format!("{:010} {:05} n \n", position, 0);
2876 self.write_bytes(entry.as_bytes())?;
2877 } else {
2878 self.write_bytes(b"0000000000 00000 f \n")?;
2880 }
2881 }
2882
2883 Ok(())
2884 }
2885
2886 fn write_xref_stream(&mut self) -> Result<()> {
2887 let catalog_id = self.get_catalog_id()?;
2888 let info_id = self.get_info_id()?;
2889
2890 let xref_stream_id = self.allocate_object_id();
2892 let xref_position = self.current_position;
2893
2894 let mut xref_writer = XRefStreamWriter::new(xref_stream_id);
2896 xref_writer.set_trailer_info(catalog_id, info_id);
2897
2898 xref_writer.add_free_entry(0, 65535);
2900
2901 let mut entries: Vec<_> = self
2903 .xref_positions
2904 .iter()
2905 .map(|(id, pos)| (*id, *pos))
2906 .collect();
2907 entries.sort_by_key(|(id, _)| id.number());
2908
2909 let max_obj_num = entries
2911 .iter()
2912 .map(|(id, _)| id.number())
2913 .max()
2914 .unwrap_or(0)
2915 .max(xref_stream_id.number());
2916
2917 for obj_num in 1..=max_obj_num {
2919 let obj_id = ObjectId::new(obj_num, 0);
2920
2921 if obj_num == xref_stream_id.number() {
2922 xref_writer.add_in_use_entry(xref_position, 0);
2924 } else if let Some((stream_id, index)) = self.compressed_object_map.get(&obj_id) {
2925 xref_writer.add_compressed_entry(stream_id.number(), *index);
2927 } else if let Some((id, position)) =
2928 entries.iter().find(|(id, _)| id.number() == obj_num)
2929 {
2930 xref_writer.add_in_use_entry(*position, id.generation());
2932 } else {
2933 xref_writer.add_free_entry(0, 0);
2935 }
2936 }
2937
2938 self.xref_positions.insert(xref_stream_id, xref_position);
2940
2941 self.write_bytes(
2943 format!(
2944 "{} {} obj\n",
2945 xref_stream_id.number(),
2946 xref_stream_id.generation()
2947 )
2948 .as_bytes(),
2949 )?;
2950
2951 let uncompressed_data = xref_writer.encode_entries();
2953 let final_data = if self.config.compress_streams {
2954 crate::compression::compress(&uncompressed_data)?
2955 } else {
2956 uncompressed_data
2957 };
2958
2959 let mut dict = xref_writer.create_dictionary(None);
2961 dict.set("Length", Object::Integer(final_data.len() as i64));
2962
2963 if self.config.compress_streams {
2965 dict.set("Filter", Object::Name("FlateDecode".to_string()));
2966 }
2967 self.write_bytes(b"<<")?;
2968 for (key, value) in dict.iter() {
2969 self.write_bytes(b"\n/")?;
2970 self.write_bytes(key.as_bytes())?;
2971 self.write_bytes(b" ")?;
2972 self.write_object_value(value)?;
2973 }
2974 self.write_bytes(b"\n>>\n")?;
2975
2976 self.write_bytes(b"stream\n")?;
2978 self.write_bytes(&final_data)?;
2979 self.write_bytes(b"\nendstream\n")?;
2980 self.write_bytes(b"endobj\n")?;
2981
2982 self.write_bytes(b"\nstartxref\n")?;
2984 self.write_bytes(xref_position.to_string().as_bytes())?;
2985 self.write_bytes(b"\n%%EOF\n")?;
2986
2987 Ok(())
2988 }
2989
2990 fn init_encryption(&mut self, encryption: &crate::document::DocumentEncryption) -> Result<()> {
2997 use crate::encryption::{
2998 CryptFilterManager, CryptFilterMethod, FunctionalCryptFilter, ObjectEncryptor,
2999 };
3000 use std::sync::Arc;
3001
3002 let mut fid = vec![0u8; 16];
3004 use rand::Rng;
3005 rand::rng().fill_bytes(&mut fid);
3006
3007 let enc_dict = encryption
3008 .create_encryption_dict(Some(&fid))
3009 .map_err(|e| PdfError::EncryptionError(format!("encryption dict: {}", e)))?;
3010
3011 let enc_key = encryption
3013 .get_encryption_key(&enc_dict, Some(&fid))
3014 .map_err(|e| PdfError::EncryptionError(format!("encryption key: {}", e)))?;
3015
3016 let handler = encryption.handler();
3018 let (method, key_len) = match encryption.strength {
3019 crate::document::EncryptionStrength::Rc4_40bit => (CryptFilterMethod::V2, Some(5)),
3020 crate::document::EncryptionStrength::Rc4_128bit => (CryptFilterMethod::V2, Some(16)),
3021 crate::document::EncryptionStrength::Aes128 => (CryptFilterMethod::AESV2, Some(16)),
3022 crate::document::EncryptionStrength::Aes256 => (CryptFilterMethod::AESV3, Some(32)),
3023 };
3024
3025 let std_filter = FunctionalCryptFilter {
3026 name: "StdCF".to_string(),
3027 method,
3028 length: key_len,
3029 auth_event: crate::encryption::AuthEvent::DocOpen,
3030 recipients: None,
3031 };
3032
3033 let mut filter_manager =
3034 CryptFilterManager::new(Box::new(handler), "StdCF".to_string(), "StdCF".to_string());
3035 filter_manager.add_filter(std_filter);
3036
3037 let encryptor =
3038 ObjectEncryptor::new(Arc::new(filter_manager), enc_key, enc_dict.encrypt_metadata);
3039
3040 let encrypt_id = self.allocate_object_id();
3042 self.encrypt_obj_id = Some(encrypt_id);
3043 self.file_id = Some(fid);
3044 self.encryption_state = Some(WriterEncryptionState { encryptor });
3045
3046 self.pending_encrypt_dict = Some(enc_dict.to_dict());
3048
3049 Ok(())
3050 }
3051
3052 fn write_encryption_dict(&mut self) -> Result<()> {
3054 if let (Some(encrypt_id), Some(dict)) =
3055 (self.encrypt_obj_id, self.pending_encrypt_dict.take())
3056 {
3057 let enc_state = self.encryption_state.take();
3059 self.write_object(encrypt_id, Object::Dictionary(dict))?;
3060 self.encryption_state = enc_state;
3061 }
3062 Ok(())
3063 }
3064
3065 fn write_trailer(&mut self, xref_position: u64) -> Result<()> {
3066 let catalog_id = self.get_catalog_id()?;
3067 let info_id = self.get_info_id()?;
3068 let max_obj_num = self
3070 .xref_positions
3071 .keys()
3072 .map(|id| id.number())
3073 .max()
3074 .unwrap_or(0);
3075
3076 let mut trailer = Dictionary::new();
3077 trailer.set("Size", Object::Integer((max_obj_num + 1) as i64));
3078 trailer.set("Root", Object::Reference(catalog_id));
3079 trailer.set("Info", Object::Reference(info_id));
3080
3081 if let Some(prev_xref) = self.prev_xref_offset {
3083 trailer.set("Prev", Object::Integer(prev_xref as i64));
3084 }
3085
3086 if let Some(encrypt_id) = self.encrypt_obj_id {
3088 trailer.set("Encrypt", Object::Reference(encrypt_id));
3089 }
3090 if let Some(ref fid) = self.file_id {
3091 trailer.set(
3092 "ID",
3093 Object::Array(vec![
3094 Object::ByteString(fid.clone()),
3095 Object::ByteString(fid.clone()),
3096 ]),
3097 );
3098 }
3099
3100 self.write_bytes(b"trailer\n")?;
3101 self.write_object_value(&Object::Dictionary(trailer))?;
3102 self.write_bytes(b"\nstartxref\n")?;
3103 self.write_bytes(xref_position.to_string().as_bytes())?;
3104 self.write_bytes(b"\n%%EOF\n")?;
3105
3106 Ok(())
3107 }
3108
3109 fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
3110 self.writer.write_all(data)?;
3111 self.current_position += data.len() as u64;
3112 Ok(())
3113 }
3114
3115 #[allow(dead_code)]
3116 fn create_widget_appearance_stream(&mut self, widget_dict: &Dictionary) -> Result<ObjectId> {
3117 let rect = if let Some(Object::Array(rect_array)) = widget_dict.get("Rect") {
3119 if rect_array.len() >= 4 {
3120 if let (
3121 Some(Object::Real(x1)),
3122 Some(Object::Real(y1)),
3123 Some(Object::Real(x2)),
3124 Some(Object::Real(y2)),
3125 ) = (
3126 rect_array.first(),
3127 rect_array.get(1),
3128 rect_array.get(2),
3129 rect_array.get(3),
3130 ) {
3131 (*x1, *y1, *x2, *y2)
3132 } else {
3133 (0.0, 0.0, 100.0, 20.0) }
3135 } else {
3136 (0.0, 0.0, 100.0, 20.0) }
3138 } else {
3139 (0.0, 0.0, 100.0, 20.0) };
3141
3142 let width = rect.2 - rect.0;
3143 let height = rect.3 - rect.1;
3144
3145 let mut content = String::new();
3147
3148 content.push_str("q\n");
3150
3151 content.push_str("0 0 0 RG\n"); content.push_str("1 w\n"); content.push_str(&format!("0 0 {width} {height} re\n"));
3157 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));
3162 content.push_str("f\n"); content.push_str("Q\n");
3166
3167 let mut stream_dict = Dictionary::new();
3169 stream_dict.set("Type", Object::Name("XObject".to_string()));
3170 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3171 stream_dict.set(
3172 "BBox",
3173 Object::Array(vec![
3174 Object::Real(0.0),
3175 Object::Real(0.0),
3176 Object::Real(width),
3177 Object::Real(height),
3178 ]),
3179 );
3180 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3181 stream_dict.set("Length", Object::Integer(content.len() as i64));
3182
3183 let stream_id = self.allocate_object_id();
3185 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3186
3187 Ok(stream_id)
3188 }
3189
3190 #[allow(dead_code)]
3191 fn create_field_appearance_stream(
3192 &mut self,
3193 field_dict: &Dictionary,
3194 widget: &crate::forms::Widget,
3195 ) -> Result<ObjectId> {
3196 let width = widget.rect.upper_right.x - widget.rect.lower_left.x;
3197 let height = widget.rect.upper_right.y - widget.rect.lower_left.y;
3198
3199 let mut content = String::new();
3201
3202 content.push_str("q\n");
3204
3205 if let Some(bg_color) = &widget.appearance.background_color {
3207 match bg_color {
3208 crate::graphics::Color::Gray(g) => {
3209 content.push_str(&format!("{g} g\n"));
3210 }
3211 crate::graphics::Color::Rgb(r, g, b) => {
3212 content.push_str(&format!("{r} {g} {b} rg\n"));
3213 }
3214 crate::graphics::Color::Cmyk(c, m, y, k) => {
3215 content.push_str(&format!("{c} {m} {y} {k} k\n"));
3216 }
3217 }
3218 content.push_str(&format!("0 0 {width} {height} re\n"));
3219 content.push_str("f\n");
3220 }
3221
3222 if let Some(border_color) = &widget.appearance.border_color {
3224 match border_color {
3225 crate::graphics::Color::Gray(g) => {
3226 content.push_str(&format!("{g} G\n"));
3227 }
3228 crate::graphics::Color::Rgb(r, g, b) => {
3229 content.push_str(&format!("{r} {g} {b} RG\n"));
3230 }
3231 crate::graphics::Color::Cmyk(c, m, y, k) => {
3232 content.push_str(&format!("{c} {m} {y} {k} K\n"));
3233 }
3234 }
3235 content.push_str(&format!("{} w\n", widget.appearance.border_width));
3236 content.push_str(&format!("0 0 {width} {height} re\n"));
3237 content.push_str("S\n");
3238 }
3239
3240 if let Some(Object::Name(ft)) = field_dict.get("FT") {
3242 if ft == "Btn" {
3243 if let Some(Object::Name(v)) = field_dict.get("V") {
3244 if v == "Yes" {
3245 content.push_str("0 0 0 RG\n"); content.push_str("2 w\n");
3248 let margin = width * 0.2;
3249 content.push_str(&format!("{} {} m\n", margin, height / 2.0));
3250 content.push_str(&format!("{} {} l\n", width / 2.0, margin));
3251 content.push_str(&format!("{} {} l\n", width - margin, height - margin));
3252 content.push_str("S\n");
3253 }
3254 }
3255 }
3256 }
3257
3258 content.push_str("Q\n");
3260
3261 let mut stream_dict = Dictionary::new();
3263 stream_dict.set("Type", Object::Name("XObject".to_string()));
3264 stream_dict.set("Subtype", Object::Name("Form".to_string()));
3265 stream_dict.set(
3266 "BBox",
3267 Object::Array(vec![
3268 Object::Real(0.0),
3269 Object::Real(0.0),
3270 Object::Real(width),
3271 Object::Real(height),
3272 ]),
3273 );
3274 stream_dict.set("Resources", Object::Dictionary(Dictionary::new()));
3275 stream_dict.set("Length", Object::Integer(content.len() as i64));
3276
3277 let stream_id = self.allocate_object_id();
3279 self.write_object(stream_id, Object::Stream(stream_dict, content.into_bytes()))?;
3280
3281 Ok(stream_id)
3282 }
3283}
3284
3285fn format_pdf_date(date: DateTime<Utc>) -> String {
3287 let formatted = date.format("D:%Y%m%d%H%M%S");
3290
3291 format!("{formatted}+00'00")
3293}
3294
3295#[cfg(test)]
3296mod tests;
3297
3298#[cfg(test)]
3299mod rigorous_tests;