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