oxidize_pdf/forms/
form_data.rs

1//! Form data management and AcroForm generation
2
3use crate::error::Result;
4use crate::forms::{
5    CheckBox, ComboBox, FieldOptions, FormField, ListBox, PushButton, RadioButton, TextField,
6    Widget,
7};
8use crate::objects::{Dictionary, Object, ObjectReference};
9use std::collections::HashMap;
10
11/// Interactive form dictionary (AcroForm)
12#[derive(Debug, Clone)]
13pub struct AcroForm {
14    /// Form fields
15    pub fields: Vec<ObjectReference>,
16    /// Need appearances flag
17    pub need_appearances: bool,
18    /// Signature flags
19    pub sig_flags: Option<i32>,
20    /// Calculation order
21    pub co: Option<Vec<ObjectReference>>,
22    /// Default resources
23    pub dr: Option<Dictionary>,
24    /// Default appearance
25    pub da: Option<String>,
26    /// Quadding
27    pub q: Option<i32>,
28}
29
30impl AcroForm {
31    /// Create a new AcroForm
32    pub fn new() -> Self {
33        Self {
34            fields: Vec::new(),
35            need_appearances: true,
36            sig_flags: None,
37            co: None,
38            dr: None,
39            da: Some("/Helv 12 Tf 0 g".to_string()), // Default: Helvetica 12pt black
40            q: None,
41        }
42    }
43
44    /// Add a field reference
45    pub fn add_field(&mut self, field_ref: ObjectReference) {
46        self.fields.push(field_ref);
47    }
48
49    /// Convert to dictionary
50    pub fn to_dict(&self) -> Dictionary {
51        let mut dict = Dictionary::new();
52
53        // Fields array
54        let fields: Vec<Object> = self.fields.iter().map(|r| Object::Reference(*r)).collect();
55        dict.set("Fields", Object::Array(fields));
56
57        // Need appearances
58        dict.set("NeedAppearances", Object::Boolean(self.need_appearances));
59
60        // Signature flags
61        if let Some(sig_flags) = self.sig_flags {
62            dict.set("SigFlags", Object::Integer(sig_flags as i64));
63        }
64
65        // Calculation order
66        if let Some(ref co) = self.co {
67            let co_refs: Vec<Object> = co.iter().map(|r| Object::Reference(*r)).collect();
68            dict.set("CO", Object::Array(co_refs));
69        }
70
71        // Default resources
72        if let Some(ref dr) = self.dr {
73            dict.set("DR", Object::Dictionary(dr.clone()));
74        }
75
76        // Default appearance
77        if let Some(ref da) = self.da {
78            dict.set("DA", Object::String(da.clone()));
79        }
80
81        // Quadding
82        if let Some(q) = self.q {
83            dict.set("Q", Object::Integer(q as i64));
84        }
85
86        dict
87    }
88}
89
90impl Default for AcroForm {
91    fn default() -> Self {
92        Self::new()
93    }
94}
95
96/// Form data extracted from PDF
97#[derive(Debug, Clone)]
98pub struct FormData {
99    /// Field values by name
100    pub values: HashMap<String, String>,
101}
102
103impl FormData {
104    /// Create new form data
105    pub fn new() -> Self {
106        Self {
107            values: HashMap::new(),
108        }
109    }
110
111    /// Set field value
112    pub fn set_value(&mut self, name: impl Into<String>, value: impl Into<String>) {
113        self.values.insert(name.into(), value.into());
114    }
115
116    /// Get field value
117    pub fn get_value(&self, name: &str) -> Option<&str> {
118        self.values.get(name).map(|s| s.as_str())
119    }
120}
121
122impl Default for FormData {
123    fn default() -> Self {
124        Self::new()
125    }
126}
127
128/// Form manager for creating and managing forms
129#[derive(Debug)]
130pub struct FormManager {
131    /// Registered fields
132    fields: HashMap<String, FormField>,
133    /// AcroForm dictionary
134    acro_form: AcroForm,
135    /// Next field ID
136    next_field_id: u32,
137}
138
139impl FormManager {
140    /// Create a new form manager
141    pub fn new() -> Self {
142        Self {
143            fields: HashMap::new(),
144            acro_form: AcroForm::new(),
145            next_field_id: 1,
146        }
147    }
148
149    /// Add a text field
150    pub fn add_text_field(
151        &mut self,
152        field: TextField,
153        widget: Widget,
154        options: Option<FieldOptions>,
155    ) -> Result<ObjectReference> {
156        let mut field_dict = field.to_dict();
157
158        // Apply options
159        if let Some(opts) = options {
160            if opts.flags.to_flags() != 0 {
161                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
162            }
163            if let Some(da) = opts.default_appearance {
164                field_dict.set("DA", Object::String(da));
165            }
166            if let Some(q) = opts.quadding {
167                field_dict.set("Q", Object::Integer(q as i64));
168            }
169        }
170
171        let field_name = field.name.clone();
172        let mut form_field = FormField::new(field_dict);
173        form_field.add_widget(widget);
174
175        self.fields.insert(field_name, form_field);
176
177        // Create object reference
178        let obj_ref = ObjectReference::new(self.next_field_id, 0);
179        self.next_field_id += 1;
180        self.acro_form.add_field(obj_ref);
181
182        Ok(obj_ref)
183    }
184
185    /// Add a combo box field
186    pub fn add_combo_box(
187        &mut self,
188        combo: ComboBox,
189        widget: Widget,
190        options: Option<FieldOptions>,
191    ) -> Result<ObjectReference> {
192        let mut field_dict = combo.to_dict();
193
194        // Apply options
195        if let Some(opts) = options {
196            if opts.flags.to_flags() != 0 {
197                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
198            }
199            if let Some(da) = opts.default_appearance {
200                field_dict.set("DA", Object::String(da));
201            }
202        }
203
204        let field_name = combo.name.clone();
205        let mut form_field = FormField::new(field_dict);
206        form_field.add_widget(widget);
207
208        self.fields.insert(field_name, form_field);
209
210        // Create object reference
211        let obj_ref = ObjectReference::new(self.next_field_id, 0);
212        self.next_field_id += 1;
213        self.acro_form.add_field(obj_ref);
214
215        Ok(obj_ref)
216    }
217
218    /// Add a list box field
219    pub fn add_list_box(
220        &mut self,
221        listbox: ListBox,
222        widget: Widget,
223        options: Option<FieldOptions>,
224    ) -> Result<ObjectReference> {
225        let mut field_dict = listbox.to_dict();
226
227        // Apply options
228        if let Some(opts) = options {
229            if opts.flags.to_flags() != 0 {
230                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
231            }
232            if let Some(da) = opts.default_appearance {
233                field_dict.set("DA", Object::String(da));
234            }
235        }
236
237        let field_name = listbox.name.clone();
238        let mut form_field = FormField::new(field_dict);
239        form_field.add_widget(widget);
240
241        self.fields.insert(field_name, form_field);
242
243        // Create object reference
244        let obj_ref = ObjectReference::new(self.next_field_id, 0);
245        self.next_field_id += 1;
246        self.acro_form.add_field(obj_ref);
247
248        Ok(obj_ref)
249    }
250
251    /// Add a radio button group
252    pub fn add_radio_button(
253        &mut self,
254        radio: RadioButton,
255        widgets: Option<Vec<Widget>>,
256        options: Option<FieldOptions>,
257    ) -> Result<ObjectReference> {
258        let mut field_dict = radio.to_dict();
259
260        // Apply options
261        if let Some(opts) = options {
262            if opts.flags.to_flags() != 0 {
263                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
264            }
265            if let Some(da) = opts.default_appearance {
266                field_dict.set("DA", Object::String(da));
267            }
268        }
269
270        let field_name = radio.name.clone();
271        let mut form_field = FormField::new(field_dict);
272
273        // Add widgets if provided
274        if let Some(widgets) = widgets {
275            for widget in widgets {
276                form_field.add_widget(widget);
277            }
278        }
279
280        self.fields.insert(field_name, form_field);
281
282        // Create object reference
283        let obj_ref = ObjectReference::new(self.next_field_id, 0);
284        self.next_field_id += 1;
285        self.acro_form.add_field(obj_ref);
286
287        Ok(obj_ref)
288    }
289
290    /// Add a checkbox
291    pub fn add_checkbox(
292        &mut self,
293        checkbox: CheckBox,
294        widget: Widget,
295        options: Option<FieldOptions>,
296    ) -> Result<ObjectReference> {
297        let mut field_dict = checkbox.to_dict();
298
299        // Apply options
300        if let Some(opts) = options {
301            if opts.flags.to_flags() != 0 {
302                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
303            }
304        }
305
306        let field_name = checkbox.name.clone();
307        let mut form_field = FormField::new(field_dict);
308        form_field.add_widget(widget);
309
310        self.fields.insert(field_name, form_field);
311
312        // Create object reference
313        let obj_ref = ObjectReference::new(self.next_field_id, 0);
314        self.next_field_id += 1;
315        self.acro_form.add_field(obj_ref);
316
317        Ok(obj_ref)
318    }
319
320    /// Add a push button
321    pub fn add_push_button(
322        &mut self,
323        button: PushButton,
324        widget: Widget,
325        options: Option<FieldOptions>,
326    ) -> Result<ObjectReference> {
327        let mut field_dict = button.to_dict();
328
329        // Apply options
330        if let Some(opts) = options {
331            if opts.flags.to_flags() != 0 {
332                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
333            }
334        }
335
336        let field_name = button.name.clone();
337        let mut form_field = FormField::new(field_dict);
338        form_field.add_widget(widget);
339
340        self.fields.insert(field_name, form_field);
341
342        // Create object reference
343        let obj_ref = ObjectReference::new(self.next_field_id, 0);
344        self.next_field_id += 1;
345        self.acro_form.add_field(obj_ref);
346
347        Ok(obj_ref)
348    }
349
350    /// Add a radio button group
351    pub fn add_radio_buttons(
352        &mut self,
353        radio: RadioButton,
354        widgets: Vec<Widget>,
355        options: Option<FieldOptions>,
356    ) -> Result<ObjectReference> {
357        let mut field_dict = radio.to_dict();
358
359        // Apply options
360        if let Some(opts) = options {
361            if opts.flags.to_flags() != 0 {
362                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
363            }
364        }
365
366        let field_name = radio.name.clone();
367        let mut form_field = FormField::new(field_dict);
368
369        // Add all widgets
370        for widget in widgets {
371            form_field.add_widget(widget);
372        }
373
374        self.fields.insert(field_name, form_field);
375
376        // Create object reference
377        let obj_ref = ObjectReference::new(self.next_field_id, 0);
378        self.next_field_id += 1;
379        self.acro_form.add_field(obj_ref);
380
381        Ok(obj_ref)
382    }
383
384    /// Get the AcroForm dictionary
385    pub fn get_acro_form(&self) -> &AcroForm {
386        &self.acro_form
387    }
388
389    /// Get all fields
390    pub fn fields(&self) -> &HashMap<String, FormField> {
391        &self.fields
392    }
393
394    /// Get field by name
395    pub fn get_field(&self, name: &str) -> Option<&FormField> {
396        self.fields.get(name)
397    }
398
399    /// Set default appearance for all fields
400    pub fn set_default_appearance(&mut self, da: impl Into<String>) {
401        self.acro_form.da = Some(da.into());
402    }
403
404    /// Set default resources
405    pub fn set_default_resources(&mut self, resources: Dictionary) {
406        self.acro_form.dr = Some(resources);
407    }
408}
409
410impl Default for FormManager {
411    fn default() -> Self {
412        Self::new()
413    }
414}
415
416impl FormManager {
417    /// Get the number of fields managed by this FormManager
418    pub fn field_count(&self) -> usize {
419        self.fields.len()
420    }
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426    use crate::forms::FieldFlags;
427    use crate::geometry::{Point, Rectangle};
428
429    #[test]
430    fn test_acro_form() {
431        let mut acro_form = AcroForm::new();
432        acro_form.add_field(ObjectReference::new(1, 0));
433        acro_form.add_field(ObjectReference::new(2, 0));
434
435        let dict = acro_form.to_dict();
436        assert!(dict.get("Fields").is_some());
437        assert_eq!(dict.get("NeedAppearances"), Some(&Object::Boolean(true)));
438        assert!(dict.get("DA").is_some());
439    }
440
441    #[test]
442    fn test_form_data() {
443        let mut form_data = FormData::new();
444        form_data.set_value("name", "John Doe");
445        form_data.set_value("email", "john@example.com");
446
447        assert_eq!(form_data.get_value("name"), Some("John Doe"));
448        assert_eq!(form_data.get_value("email"), Some("john@example.com"));
449        assert_eq!(form_data.get_value("phone"), None);
450    }
451
452    #[test]
453    fn test_form_manager_text_field() {
454        let mut manager = FormManager::new();
455
456        let field = TextField::new("username").with_default_value("guest");
457        let widget = Widget::new(Rectangle::new(
458            Point::new(100.0, 100.0),
459            Point::new(300.0, 120.0),
460        ));
461
462        let obj_ref = manager.add_text_field(field, widget, None).unwrap();
463        assert_eq!(obj_ref.number(), 1);
464        assert!(manager.get_field("username").is_some());
465    }
466
467    #[test]
468    fn test_form_manager_checkbox() {
469        let mut manager = FormManager::new();
470
471        let checkbox = CheckBox::new("agree").checked();
472        let widget = Widget::new(Rectangle::new(
473            Point::new(100.0, 100.0),
474            Point::new(115.0, 115.0),
475        ));
476
477        let obj_ref = manager.add_checkbox(checkbox, widget, None).unwrap();
478        assert_eq!(obj_ref.number(), 1);
479        assert!(manager.get_field("agree").is_some());
480    }
481
482    #[test]
483    fn test_form_manager_multiple_fields() {
484        let mut manager = FormManager::new();
485
486        // Add text field
487        let text_field = TextField::new("name");
488        let text_widget = Widget::new(Rectangle::new(
489            Point::new(100.0, 200.0),
490            Point::new(300.0, 220.0),
491        ));
492        manager
493            .add_text_field(text_field, text_widget, None)
494            .unwrap();
495
496        // Add checkbox
497        let checkbox = CheckBox::new("subscribe");
498        let check_widget = Widget::new(Rectangle::new(
499            Point::new(100.0, 150.0),
500            Point::new(115.0, 165.0),
501        ));
502        manager.add_checkbox(checkbox, check_widget, None).unwrap();
503
504        assert_eq!(manager.fields().len(), 2);
505        assert!(manager.get_field("name").is_some());
506        assert!(manager.get_field("subscribe").is_some());
507    }
508
509    #[test]
510    fn test_acro_form_comprehensive() {
511        let mut acro_form = AcroForm::new();
512
513        // Test initial state
514        assert_eq!(acro_form.fields.len(), 0);
515        assert!(acro_form.need_appearances);
516        assert!(acro_form.sig_flags.is_none());
517        assert!(acro_form.co.is_none());
518        assert!(acro_form.dr.is_none());
519        assert!(acro_form.da.is_some());
520        assert!(acro_form.q.is_none());
521
522        // Add multiple fields
523        for i in 1..=5 {
524            acro_form.add_field(ObjectReference::new(i, 0));
525        }
526        assert_eq!(acro_form.fields.len(), 5);
527
528        // Set optional properties
529        acro_form.sig_flags = Some(3);
530        acro_form.co = Some(vec![ObjectReference::new(1, 0), ObjectReference::new(2, 0)]);
531        acro_form.q = Some(2); // Right alignment
532
533        let mut dr = Dictionary::new();
534        dr.set("Font", Object::String("Helvetica".to_string()));
535        acro_form.dr = Some(dr);
536
537        // Test dictionary conversion
538        let dict = acro_form.to_dict();
539
540        // Verify Fields array
541        if let Some(Object::Array(fields)) = dict.get("Fields") {
542            assert_eq!(fields.len(), 5);
543            for (i, field) in fields.iter().enumerate() {
544                assert_eq!(
545                    *field,
546                    Object::Reference(ObjectReference::new((i + 1) as u32, 0))
547                );
548            }
549        } else {
550            panic!("Expected Fields array");
551        }
552
553        // Verify other properties
554        assert_eq!(dict.get("NeedAppearances"), Some(&Object::Boolean(true)));
555        assert_eq!(dict.get("SigFlags"), Some(&Object::Integer(3)));
556        assert_eq!(dict.get("Q"), Some(&Object::Integer(2)));
557        assert!(dict.get("CO").is_some());
558        assert!(dict.get("DR").is_some());
559        assert!(dict.get("DA").is_some());
560    }
561
562    #[test]
563    fn test_acro_form_default() {
564        let acro_form = AcroForm::default();
565        let default_form = AcroForm::new();
566
567        assert_eq!(acro_form.fields.len(), default_form.fields.len());
568        assert_eq!(acro_form.need_appearances, default_form.need_appearances);
569        assert_eq!(acro_form.sig_flags, default_form.sig_flags);
570        assert_eq!(acro_form.da, default_form.da);
571    }
572
573    #[test]
574    fn test_acro_form_debug_clone() {
575        let mut acro_form = AcroForm::new();
576        acro_form.add_field(ObjectReference::new(1, 0));
577        acro_form.sig_flags = Some(1);
578
579        let debug_str = format!("{acro_form:?}");
580        assert!(debug_str.contains("AcroForm"));
581
582        let cloned = acro_form.clone();
583        assert_eq!(acro_form.fields, cloned.fields);
584        assert_eq!(acro_form.sig_flags, cloned.sig_flags);
585        assert_eq!(acro_form.need_appearances, cloned.need_appearances);
586    }
587
588    #[test]
589    fn test_form_data_comprehensive() {
590        let mut form_data = FormData::new();
591
592        // Test initial state
593        assert_eq!(form_data.values.len(), 0);
594        assert_eq!(form_data.get_value("any"), None);
595
596        // Test setting various string types
597        form_data.set_value("string_literal", "test");
598        form_data.set_value(String::from("string_owned"), "test2");
599        form_data.set_value("number", "42");
600        form_data.set_value("empty", "");
601        form_data.set_value("unicode", "café ñoño 你好");
602
603        assert_eq!(form_data.values.len(), 5);
604        assert_eq!(form_data.get_value("string_literal"), Some("test"));
605        assert_eq!(form_data.get_value("string_owned"), Some("test2"));
606        assert_eq!(form_data.get_value("number"), Some("42"));
607        assert_eq!(form_data.get_value("empty"), Some(""));
608        assert_eq!(form_data.get_value("unicode"), Some("café ñoño 你好"));
609
610        // Test overwriting values
611        form_data.set_value("string_literal", "overwritten");
612        assert_eq!(form_data.get_value("string_literal"), Some("overwritten"));
613        assert_eq!(form_data.values.len(), 5); // Count shouldn't change
614
615        // Test case sensitivity
616        form_data.set_value("Test", "uppercase");
617        form_data.set_value("test", "lowercase");
618        assert_eq!(form_data.get_value("Test"), Some("uppercase"));
619        assert_eq!(form_data.get_value("test"), Some("lowercase"));
620        assert_eq!(form_data.values.len(), 7);
621    }
622
623    #[test]
624    fn test_form_data_edge_cases() {
625        let mut form_data = FormData::new();
626
627        // Empty field names
628        form_data.set_value("", "empty_name");
629        assert_eq!(form_data.get_value(""), Some("empty_name"));
630
631        // Very long field names and values
632        let long_name = "a".repeat(1000);
633        let long_value = "b".repeat(2000);
634        form_data.set_value(&long_name, &long_value);
635        assert_eq!(form_data.get_value(&long_name), Some(long_value.as_str()));
636
637        // Special characters in field names
638        form_data.set_value("field/with/slashes", "value1");
639        form_data.set_value("field with spaces", "value2");
640        form_data.set_value("field.with.dots", "value3");
641        form_data.set_value("field-with-dashes", "value4");
642        form_data.set_value("field_with_underscores", "value5");
643
644        assert_eq!(form_data.get_value("field/with/slashes"), Some("value1"));
645        assert_eq!(form_data.get_value("field with spaces"), Some("value2"));
646        assert_eq!(form_data.get_value("field.with.dots"), Some("value3"));
647        assert_eq!(form_data.get_value("field-with-dashes"), Some("value4"));
648        assert_eq!(
649            form_data.get_value("field_with_underscores"),
650            Some("value5")
651        );
652    }
653
654    #[test]
655    fn test_form_data_default_debug_clone() {
656        let form_data = FormData::default();
657        assert_eq!(form_data.values.len(), 0);
658
659        let default_form = FormData::new();
660        assert_eq!(form_data.values.len(), default_form.values.len());
661
662        let debug_str = format!("{form_data:?}");
663        assert!(debug_str.contains("FormData"));
664
665        let cloned = form_data.clone();
666        assert_eq!(form_data.values.len(), cloned.values.len());
667    }
668
669    #[test]
670    fn test_form_manager_comprehensive() {
671        let mut manager = FormManager::new();
672
673        // Test initial state
674        assert_eq!(manager.field_count(), 0);
675        assert_eq!(manager.fields().len(), 0);
676        assert!(manager.get_field("nonexistent").is_none());
677
678        let acroform = manager.get_acro_form();
679        assert_eq!(acroform.fields.len(), 0);
680        assert!(acroform.need_appearances);
681
682        // Test field ID generation
683        let field1 = TextField::new("field1");
684        let widget1 = Widget::new(Rectangle::new(
685            Point::new(0.0, 0.0),
686            Point::new(100.0, 20.0),
687        ));
688        let ref1 = manager.add_text_field(field1, widget1, None).unwrap();
689        assert_eq!(ref1.number(), 1);
690
691        let field2 = TextField::new("field2");
692        let widget2 = Widget::new(Rectangle::new(
693            Point::new(0.0, 30.0),
694            Point::new(100.0, 50.0),
695        ));
696        let ref2 = manager.add_text_field(field2, widget2, None).unwrap();
697        assert_eq!(ref2.number(), 2);
698
699        let field3 = TextField::new("field3");
700        let widget3 = Widget::new(Rectangle::new(
701            Point::new(0.0, 60.0),
702            Point::new(100.0, 80.0),
703        ));
704        let ref3 = manager.add_text_field(field3, widget3, None).unwrap();
705        assert_eq!(ref3.number(), 3);
706
707        assert_eq!(manager.field_count(), 3);
708        assert_eq!(manager.get_acro_form().fields.len(), 3);
709    }
710
711    #[test]
712    fn test_form_manager_push_button() {
713        let mut manager = FormManager::new();
714
715        let button = PushButton::new("submit").with_caption("Submit");
716        let widget = Widget::new(Rectangle::new(
717            Point::new(200.0, 100.0),
718            Point::new(280.0, 130.0),
719        ));
720
721        let obj_ref = manager.add_push_button(button, widget, None).unwrap();
722        assert_eq!(obj_ref.number(), 1);
723        assert!(manager.get_field("submit").is_some());
724
725        let form_field = manager.get_field("submit").unwrap();
726        let dict = &form_field.field_dict;
727        assert_eq!(dict.get("T"), Some(&Object::String("submit".to_string())));
728        assert_eq!(dict.get("FT"), Some(&Object::Name("Btn".to_string())));
729    }
730
731    #[test]
732    fn test_form_manager_radio_buttons() {
733        let mut manager = FormManager::new();
734
735        let radio = RadioButton::new("gender")
736            .add_option("M", "Male")
737            .add_option("F", "Female")
738            .with_selected(0);
739
740        let widgets = vec![
741            Widget::new(Rectangle::new(
742                Point::new(100.0, 100.0),
743                Point::new(115.0, 115.0),
744            )),
745            Widget::new(Rectangle::new(
746                Point::new(150.0, 100.0),
747                Point::new(165.0, 115.0),
748            )),
749        ];
750
751        let obj_ref = manager.add_radio_buttons(radio, widgets, None).unwrap();
752        assert_eq!(obj_ref.number(), 1);
753        assert!(manager.get_field("gender").is_some());
754
755        let form_field = manager.get_field("gender").unwrap();
756        assert_eq!(form_field.widgets.len(), 2);
757
758        let dict = &form_field.field_dict;
759        assert_eq!(dict.get("T"), Some(&Object::String("gender".to_string())));
760        assert_eq!(dict.get("V"), Some(&Object::Name("M".to_string())));
761    }
762
763    #[test]
764    fn test_form_manager_list_box() {
765        let mut manager = FormManager::new();
766
767        let listbox = ListBox::new("languages")
768            .add_option("en", "English")
769            .add_option("es", "Spanish")
770            .add_option("fr", "French")
771            .multi_select()
772            .with_selected(vec![0, 2]);
773
774        let widget = Widget::new(Rectangle::new(
775            Point::new(100.0, 100.0),
776            Point::new(200.0, 150.0),
777        ));
778
779        let obj_ref = manager.add_list_box(listbox, widget, None).unwrap();
780        assert_eq!(obj_ref.number(), 1);
781        assert!(manager.get_field("languages").is_some());
782
783        let form_field = manager.get_field("languages").unwrap();
784        let dict = &form_field.field_dict;
785        assert_eq!(
786            dict.get("T"),
787            Some(&Object::String("languages".to_string()))
788        );
789        assert_eq!(dict.get("FT"), Some(&Object::Name("Ch".to_string())));
790    }
791
792    #[test]
793    fn test_form_manager_combo_box() {
794        let mut manager = FormManager::new();
795
796        let combo = ComboBox::new("country")
797            .add_option("US", "United States")
798            .add_option("CA", "Canada")
799            .editable()
800            .with_value("US");
801
802        let widget = Widget::new(Rectangle::new(
803            Point::new(100.0, 100.0),
804            Point::new(300.0, 120.0),
805        ));
806
807        let obj_ref = manager.add_combo_box(combo, widget, None).unwrap();
808        assert_eq!(obj_ref.number(), 1);
809        assert!(manager.get_field("country").is_some());
810
811        let form_field = manager.get_field("country").unwrap();
812        let dict = &form_field.field_dict;
813        assert_eq!(dict.get("T"), Some(&Object::String("country".to_string())));
814        assert_eq!(dict.get("V"), Some(&Object::String("US".to_string())));
815    }
816
817    #[test]
818    fn test_form_manager_with_field_options() {
819        let mut manager = FormManager::new();
820
821        let options = FieldOptions {
822            flags: FieldFlags {
823                read_only: true,
824                required: true,
825                no_export: false,
826            },
827            default_appearance: Some("/Times-Roman 12 Tf 0 g".to_string()),
828            quadding: Some(1), // Center
829        };
830
831        let field = TextField::new("readonly_field").with_value("Read Only");
832        let widget = Widget::new(Rectangle::new(
833            Point::new(50.0, 50.0),
834            Point::new(250.0, 70.0),
835        ));
836
837        manager
838            .add_text_field(field, widget, Some(options))
839            .unwrap();
840
841        let form_field = manager.get_field("readonly_field").unwrap();
842        let dict = &form_field.field_dict;
843
844        // Check flags were applied
845        if let Some(Object::Integer(flags)) = dict.get("Ff") {
846            assert_ne!(*flags & (1 << 0), 0); // Read-only flag
847            assert_ne!(*flags & (1 << 1), 0); // Required flag
848            assert_eq!(*flags & (1 << 2), 0); // No-export flag not set
849        } else {
850            panic!("Expected Ff field");
851        }
852
853        // Check appearance and quadding
854        assert_eq!(
855            dict.get("DA"),
856            Some(&Object::String("/Times-Roman 12 Tf 0 g".to_string()))
857        );
858        assert_eq!(dict.get("Q"), Some(&Object::Integer(1)));
859    }
860
861    #[test]
862    fn test_form_manager_appearance_resources() {
863        let mut manager = FormManager::new();
864
865        // Test default appearance
866        manager.set_default_appearance("/Courier 10 Tf 0.5 g");
867        let acroform = manager.get_acro_form();
868        assert_eq!(acroform.da, Some("/Courier 10 Tf 0.5 g".to_string()));
869
870        // Test default resources
871        let mut resources = Dictionary::new();
872        let mut font_dict = Dictionary::new();
873        font_dict.set("F1", Object::String("Helvetica".to_string()));
874        font_dict.set("F2", Object::String("Times-Roman".to_string()));
875        resources.set("Font", Object::Dictionary(font_dict));
876
877        let mut color_dict = Dictionary::new();
878        color_dict.set(
879            "Red",
880            Object::Array(vec![
881                Object::Real(1.0),
882                Object::Real(0.0),
883                Object::Real(0.0),
884            ]),
885        );
886        resources.set("ColorSpace", Object::Dictionary(color_dict));
887
888        manager.set_default_resources(resources);
889
890        let acroform = manager.get_acro_form();
891        assert!(acroform.dr.is_some());
892
893        let dict = acroform.to_dict();
894        assert_eq!(
895            dict.get("DA"),
896            Some(&Object::String("/Courier 10 Tf 0.5 g".to_string()))
897        );
898
899        if let Some(Object::Dictionary(dr_dict)) = dict.get("DR") {
900            assert!(dr_dict.get("Font").is_some());
901            assert!(dr_dict.get("ColorSpace").is_some());
902        } else {
903            panic!("Expected DR dictionary");
904        }
905    }
906
907    #[test]
908    fn test_form_manager_mixed_field_types() {
909        let mut manager = FormManager::new();
910
911        // Add one of each field type
912        let text_field = TextField::new("name").with_value("John");
913        let text_widget = Widget::new(Rectangle::new(
914            Point::new(10.0, 100.0),
915            Point::new(210.0, 120.0),
916        ));
917        manager
918            .add_text_field(text_field, text_widget, None)
919            .unwrap();
920
921        let checkbox = CheckBox::new("agree").checked();
922        let check_widget = Widget::new(Rectangle::new(
923            Point::new(10.0, 80.0),
924            Point::new(25.0, 95.0),
925        ));
926        manager.add_checkbox(checkbox, check_widget, None).unwrap();
927
928        let button = PushButton::new("submit");
929        let button_widget = Widget::new(Rectangle::new(
930            Point::new(10.0, 50.0),
931            Point::new(80.0, 75.0),
932        ));
933        manager
934            .add_push_button(button, button_widget, None)
935            .unwrap();
936
937        let radio = RadioButton::new("choice").add_option("A", "Option A");
938        let radio_widgets = vec![Widget::new(Rectangle::new(
939            Point::new(10.0, 30.0),
940            Point::new(25.0, 45.0),
941        ))];
942        manager
943            .add_radio_buttons(radio, radio_widgets, None)
944            .unwrap();
945
946        let listbox = ListBox::new("items").add_option("1", "Item 1");
947        let list_widget = Widget::new(Rectangle::new(
948            Point::new(10.0, 10.0),
949            Point::new(110.0, 25.0),
950        ));
951        manager.add_list_box(listbox, list_widget, None).unwrap();
952
953        let combo = ComboBox::new("selection").add_option("opt", "Option");
954        let combo_widget = Widget::new(Rectangle::new(
955            Point::new(120.0, 10.0),
956            Point::new(220.0, 25.0),
957        ));
958        manager.add_combo_box(combo, combo_widget, None).unwrap();
959
960        // Verify all fields were added
961        assert_eq!(manager.field_count(), 6);
962        assert!(manager.get_field("name").is_some());
963        assert!(manager.get_field("agree").is_some());
964        assert!(manager.get_field("submit").is_some());
965        assert!(manager.get_field("choice").is_some());
966        assert!(manager.get_field("items").is_some());
967        assert!(manager.get_field("selection").is_some());
968
969        // Verify AcroForm has all references
970        let acroform = manager.get_acro_form();
971        assert_eq!(acroform.fields.len(), 6);
972
973        // Verify object references are sequential
974        for (i, field_ref) in acroform.fields.iter().enumerate() {
975            assert_eq!(field_ref.number(), (i + 1) as u32);
976            assert_eq!(field_ref.generation(), 0);
977        }
978    }
979
980    #[test]
981    fn test_form_manager_debug_default() {
982        let manager = FormManager::new();
983        let debug_str = format!("{manager:?}");
984        assert!(debug_str.contains("FormManager"));
985
986        let default_manager = FormManager::default();
987        assert_eq!(manager.field_count(), default_manager.field_count());
988    }
989
990    #[test]
991    fn test_form_manager_error_scenarios() {
992        let mut manager = FormManager::new();
993
994        // Test with empty field names
995        let empty_field = TextField::new("");
996        let widget = Widget::new(Rectangle::new(
997            Point::new(0.0, 0.0),
998            Point::new(100.0, 20.0),
999        ));
1000        let result = manager.add_text_field(empty_field, widget, None);
1001        assert!(result.is_ok());
1002        assert!(manager.get_field("").is_some());
1003
1004        // Test duplicate field names (should be allowed)
1005        let field1 = TextField::new("duplicate");
1006        let widget1 = Widget::new(Rectangle::new(
1007            Point::new(0.0, 0.0),
1008            Point::new(100.0, 20.0),
1009        ));
1010        manager.add_text_field(field1, widget1, None).unwrap();
1011
1012        let field2 = TextField::new("duplicate");
1013        let widget2 = Widget::new(Rectangle::new(
1014            Point::new(0.0, 30.0),
1015            Point::new(100.0, 50.0),
1016        ));
1017        manager.add_text_field(field2, widget2, None).unwrap();
1018
1019        // Second field should replace the first
1020        assert_eq!(manager.field_count(), 2); // Empty field + duplicate
1021        assert!(manager.get_field("duplicate").is_some());
1022    }
1023
1024    #[test]
1025    fn test_acro_form_calculation_order() {
1026        let mut acro_form = AcroForm::new();
1027
1028        // Set calculation order
1029        let calc_order = vec![
1030            ObjectReference::new(3, 0),
1031            ObjectReference::new(1, 0),
1032            ObjectReference::new(2, 0),
1033        ];
1034        acro_form.co = Some(calc_order.clone());
1035
1036        let dict = acro_form.to_dict();
1037
1038        if let Some(Object::Array(co_array)) = dict.get("CO") {
1039            assert_eq!(co_array.len(), 3);
1040            assert_eq!(co_array[0], Object::Reference(ObjectReference::new(3, 0)));
1041            assert_eq!(co_array[1], Object::Reference(ObjectReference::new(1, 0)));
1042            assert_eq!(co_array[2], Object::Reference(ObjectReference::new(2, 0)));
1043        } else {
1044            panic!("Expected CO array");
1045        }
1046    }
1047
1048    #[test]
1049    fn test_acro_form_without_optional_fields() {
1050        let acro_form = AcroForm::new();
1051        let dict = acro_form.to_dict();
1052
1053        // These should not be present
1054        assert!(dict.get("SigFlags").is_none());
1055        assert!(dict.get("CO").is_none());
1056        assert!(dict.get("DR").is_none());
1057        assert!(dict.get("Q").is_none());
1058
1059        // These should be present
1060        assert!(dict.get("Fields").is_some());
1061        assert!(dict.get("NeedAppearances").is_some());
1062        assert!(dict.get("DA").is_some());
1063    }
1064}