Skip to main content

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    /// Placeholder `ObjectReference` returned to callers for each field,
134    /// keyed by field name. The reference is a *placeholder* — its object
135    /// number comes from the FormManager's local counter and is disjoint
136    /// from the writer's global object-id allocator. The writer builds a
137    /// placeholder → real-ObjectId map at serialization time so widget
138    /// annotations created via `Page::add_form_widget_with_ref` can
139    /// resolve their `/Parent` to the correct real id.
140    field_refs: HashMap<String, ObjectReference>,
141    /// AcroForm dictionary
142    acro_form: AcroForm,
143    /// Next field ID
144    next_field_id: u32,
145}
146
147impl FormManager {
148    /// Create a new form manager
149    pub fn new() -> Self {
150        Self {
151            fields: HashMap::new(),
152            field_refs: HashMap::new(),
153            acro_form: AcroForm::new(),
154            next_field_id: 1,
155        }
156    }
157
158    /// Add a text field
159    pub fn add_text_field(
160        &mut self,
161        field: TextField,
162        widget: Widget,
163        options: Option<FieldOptions>,
164    ) -> Result<ObjectReference> {
165        // Preserve the typed /DA on the FormField so `Document::fill_field`
166        // can pick the right font without re-parsing the PDF string in
167        // `field_dict["DA"]`. `to_dict()` consumes the visible state; stash
168        // the typed view first.
169        let typed_da = field.default_appearance.clone();
170        let mut field_dict = field.to_dict();
171
172        // Apply options
173        if let Some(opts) = options {
174            if opts.flags.to_flags() != 0 {
175                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
176            }
177            if let Some(da) = opts.default_appearance {
178                field_dict.set("DA", Object::String(da));
179            }
180            if let Some(q) = opts.quadding {
181                field_dict.set("Q", Object::Integer(q as i64));
182            }
183        }
184
185        let field_name = field.name;
186        let mut form_field = FormField::new(field_dict);
187        form_field.default_appearance = typed_da;
188        form_field.add_widget(widget);
189
190        // Create object reference
191        let obj_ref = ObjectReference::new(self.next_field_id, 0);
192        self.next_field_id += 1;
193        self.field_refs.insert(field_name.clone(), obj_ref);
194        self.fields.insert(field_name, form_field);
195        self.acro_form.add_field(obj_ref);
196
197        Ok(obj_ref)
198    }
199
200    /// Add a combo box field
201    pub fn add_combo_box(
202        &mut self,
203        combo: ComboBox,
204        widget: Widget,
205        options: Option<FieldOptions>,
206    ) -> Result<ObjectReference> {
207        let mut field_dict = combo.to_dict();
208
209        // Apply options
210        if let Some(opts) = options {
211            if opts.flags.to_flags() != 0 {
212                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
213            }
214            if let Some(da) = opts.default_appearance {
215                field_dict.set("DA", Object::String(da));
216            }
217        }
218
219        let field_name = combo.name;
220        let mut form_field = FormField::new(field_dict);
221        form_field.add_widget(widget);
222
223        // Create object reference
224        let obj_ref = ObjectReference::new(self.next_field_id, 0);
225        self.next_field_id += 1;
226        self.field_refs.insert(field_name.clone(), obj_ref);
227        self.fields.insert(field_name, form_field);
228        self.acro_form.add_field(obj_ref);
229
230        Ok(obj_ref)
231    }
232
233    /// Add a list box field
234    pub fn add_list_box(
235        &mut self,
236        listbox: ListBox,
237        widget: Widget,
238        options: Option<FieldOptions>,
239    ) -> Result<ObjectReference> {
240        let mut field_dict = listbox.to_dict();
241
242        // Apply options
243        if let Some(opts) = options {
244            if opts.flags.to_flags() != 0 {
245                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
246            }
247            if let Some(da) = opts.default_appearance {
248                field_dict.set("DA", Object::String(da));
249            }
250        }
251
252        let field_name = listbox.name;
253        let mut form_field = FormField::new(field_dict);
254        form_field.add_widget(widget);
255
256        // Create object reference
257        let obj_ref = ObjectReference::new(self.next_field_id, 0);
258        self.next_field_id += 1;
259        self.field_refs.insert(field_name.clone(), obj_ref);
260        self.fields.insert(field_name, form_field);
261        self.acro_form.add_field(obj_ref);
262
263        Ok(obj_ref)
264    }
265
266    /// Add a radio button group
267    pub fn add_radio_button(
268        &mut self,
269        radio: RadioButton,
270        widgets: Option<Vec<Widget>>,
271        options: Option<FieldOptions>,
272    ) -> Result<ObjectReference> {
273        let mut field_dict = radio.to_dict();
274
275        // Apply options
276        if let Some(opts) = options {
277            if opts.flags.to_flags() != 0 {
278                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
279            }
280            if let Some(da) = opts.default_appearance {
281                field_dict.set("DA", Object::String(da));
282            }
283        }
284
285        let field_name = radio.name;
286        let mut form_field = FormField::new(field_dict);
287
288        // Add widgets if provided
289        if let Some(widgets) = widgets {
290            for widget in widgets {
291                form_field.add_widget(widget);
292            }
293        }
294
295        // Create object reference
296        let obj_ref = ObjectReference::new(self.next_field_id, 0);
297        self.next_field_id += 1;
298        self.field_refs.insert(field_name.clone(), obj_ref);
299        self.fields.insert(field_name, form_field);
300        self.acro_form.add_field(obj_ref);
301
302        Ok(obj_ref)
303    }
304
305    /// Add a checkbox
306    pub fn add_checkbox(
307        &mut self,
308        checkbox: CheckBox,
309        widget: Widget,
310        options: Option<FieldOptions>,
311    ) -> Result<ObjectReference> {
312        let mut field_dict = checkbox.to_dict();
313
314        // Apply options
315        if let Some(opts) = options {
316            if opts.flags.to_flags() != 0 {
317                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
318            }
319        }
320
321        let field_name = checkbox.name;
322        let mut form_field = FormField::new(field_dict);
323        form_field.add_widget(widget);
324
325        // Create object reference
326        let obj_ref = ObjectReference::new(self.next_field_id, 0);
327        self.next_field_id += 1;
328        self.field_refs.insert(field_name.clone(), obj_ref);
329        self.fields.insert(field_name, form_field);
330        self.acro_form.add_field(obj_ref);
331
332        Ok(obj_ref)
333    }
334
335    /// Add a push button
336    pub fn add_push_button(
337        &mut self,
338        button: PushButton,
339        widget: Widget,
340        options: Option<FieldOptions>,
341    ) -> Result<ObjectReference> {
342        let mut field_dict = button.to_dict();
343
344        // Apply options
345        if let Some(opts) = options {
346            if opts.flags.to_flags() != 0 {
347                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
348            }
349        }
350
351        let field_name = button.name;
352        let mut form_field = FormField::new(field_dict);
353        form_field.add_widget(widget);
354
355        // Create object reference
356        let obj_ref = ObjectReference::new(self.next_field_id, 0);
357        self.next_field_id += 1;
358        self.field_refs.insert(field_name.clone(), obj_ref);
359        self.fields.insert(field_name, form_field);
360        self.acro_form.add_field(obj_ref);
361
362        Ok(obj_ref)
363    }
364
365    /// Add a radio button group
366    pub fn add_radio_buttons(
367        &mut self,
368        radio: RadioButton,
369        widgets: Vec<Widget>,
370        options: Option<FieldOptions>,
371    ) -> Result<ObjectReference> {
372        let mut field_dict = radio.to_dict();
373
374        // Apply options
375        if let Some(opts) = options {
376            if opts.flags.to_flags() != 0 {
377                field_dict.set("Ff", Object::Integer(opts.flags.to_flags() as i64));
378            }
379        }
380
381        let field_name = radio.name;
382        let mut form_field = FormField::new(field_dict);
383
384        // Add all widgets
385        for widget in widgets {
386            form_field.add_widget(widget);
387        }
388
389        // Create object reference
390        let obj_ref = ObjectReference::new(self.next_field_id, 0);
391        self.next_field_id += 1;
392        self.field_refs.insert(field_name.clone(), obj_ref);
393        self.fields.insert(field_name, form_field);
394        self.acro_form.add_field(obj_ref);
395
396        Ok(obj_ref)
397    }
398
399    /// Get the AcroForm dictionary
400    pub fn get_acro_form(&self) -> &AcroForm {
401        &self.acro_form
402    }
403
404    /// Get all fields
405    pub fn fields(&self) -> &HashMap<String, FormField> {
406        &self.fields
407    }
408
409    /// Get field by name
410    pub fn get_field(&self, name: &str) -> Option<&FormField> {
411        self.fields.get(name)
412    }
413
414    /// Get a mutable reference to a field by name.
415    ///
416    /// This is the primary entry point for mutating an AcroForm field's
417    /// dictionary (for example, to set `/V` during form filling) and its
418    /// associated widgets' appearance streams. It returns `None` if the
419    /// field does not exist — callers are expected to surface that as an
420    /// error to the user (see `Document::fill_field`).
421    pub fn get_field_mut(&mut self, name: &str) -> Option<&mut FormField> {
422        self.fields.get_mut(name)
423    }
424
425    /// Get the placeholder `ObjectReference` recorded for a field by name.
426    ///
427    /// The reference is a *placeholder* produced by `FormManager`'s local
428    /// counter at `add_*_field` time. It is the same value that widget
429    /// annotations created via `Page::add_form_widget_with_ref` store as
430    /// their `/Parent`, so this accessor is the bridge used by
431    /// `Document::fill_field` to locate the matching widget annotations on
432    /// page dictionaries without relying on field-name matching (which
433    /// would be fragile for nested field hierarchies in the future).
434    ///
435    /// Returns `None` if the field does not exist. Scoped `pub(crate)`
436    /// because the placeholder id is a writer-internal concept — the
437    /// public surface is `Document::fill_field`, not direct manipulation
438    /// of placeholder refs.
439    pub(crate) fn field_ref(&self, name: &str) -> Option<ObjectReference> {
440        self.field_refs.get(name).copied()
441    }
442
443    /// Set default appearance for all fields
444    pub fn set_default_appearance(&mut self, da: impl Into<String>) {
445        self.acro_form.da = Some(da.into());
446    }
447
448    /// Set default resources
449    pub fn set_default_resources(&mut self, resources: Dictionary) {
450        self.acro_form.dr = Some(resources);
451    }
452}
453
454impl Default for FormManager {
455    fn default() -> Self {
456        Self::new()
457    }
458}
459
460impl FormManager {
461    /// Get the number of fields managed by this FormManager
462    pub fn field_count(&self) -> usize {
463        self.fields.len()
464    }
465
466    /// Iterate over fields in a deterministic (alphabetical by name) order,
467    /// paired with the placeholder `ObjectReference` that was returned to
468    /// the caller when the field was added.
469    ///
470    /// The underlying storage is a `HashMap`, which has non-deterministic
471    /// iteration. Serializers that need reproducible output (diff-stable
472    /// `/AcroForm/Fields` arrays, object-id allocations, etc.) MUST use
473    /// this method instead of `fields().iter()`. The placeholder ref is
474    /// the one the writer uses to build the placeholder → real-id map
475    /// consumed by widget-annotation `/Parent` resolution.
476    ///
477    /// Scoped to `pub(crate)` because the placeholder `ObjectReference` is
478    /// a writer-internal concept — leaking it in the public API would
479    /// couple external callers to a serialization detail that is expected
480    /// to evolve. Public iteration can go through [`FormManager::fields`].
481    ///
482    /// # Panics
483    ///
484    /// The iterator panics if the internal `fields` / `field_refs` maps
485    /// desynchronize — a "can't happen" invariant that every
486    /// `add_*_field` method is responsible for upholding. Panicking
487    /// here is acceptable because (a) the invariant is entirely under
488    /// this crate's control and (b) a broken invariant signals memory
489    /// corruption or a logic bug in `add_*_field`, not an ISO 32000-1
490    /// conformance issue — the writer has no meaningful recovery path.
491    /// The previous `Result` wrapper was removed (PR3 / QUAL-6) because
492    /// its error arm was observably unreachable and forced every call
493    /// site to thread `?` for no benefit.
494    pub(crate) fn iter_fields_sorted(
495        &self,
496    ) -> impl Iterator<Item = (&String, &FormField, ObjectReference)> {
497        let mut keys: Vec<&String> = self.fields.keys().collect();
498        keys.sort();
499        keys.into_iter().map(move |k| {
500            // `k` was just produced by `self.fields.keys()`, so this
501            // lookup is infallible under a single immutable borrow.
502            let field = self
503                .fields
504                .get(k)
505                .expect("FormManager invariant: key from fields.keys() must resolve in fields");
506            let placeholder = *self.field_refs.get(k).expect(
507                "FormManager invariant: every field in `fields` must have an entry in \
508                 `field_refs` — check add_*_field",
509            );
510            (k, field, placeholder)
511        })
512    }
513}
514
515#[cfg(test)]
516mod tests {
517    use super::*;
518    use crate::forms::FieldFlags;
519    use crate::geometry::{Point, Rectangle};
520
521    #[test]
522    fn test_acro_form() {
523        let mut acro_form = AcroForm::new();
524        acro_form.add_field(ObjectReference::new(1, 0));
525        acro_form.add_field(ObjectReference::new(2, 0));
526
527        let dict = acro_form.to_dict();
528        assert!(dict.get("Fields").is_some());
529        assert_eq!(dict.get("NeedAppearances"), Some(&Object::Boolean(true)));
530        assert!(dict.get("DA").is_some());
531    }
532
533    #[test]
534    fn test_form_data() {
535        let mut form_data = FormData::new();
536        form_data.set_value("name", "John Doe");
537        form_data.set_value("email", "john@example.com");
538
539        assert_eq!(form_data.get_value("name"), Some("John Doe"));
540        assert_eq!(form_data.get_value("email"), Some("john@example.com"));
541        assert_eq!(form_data.get_value("phone"), None);
542    }
543
544    #[test]
545    fn test_form_manager_text_field() {
546        let mut manager = FormManager::new();
547
548        let field = TextField::new("username").with_default_value("guest");
549        let widget = Widget::new(Rectangle::new(
550            Point::new(100.0, 100.0),
551            Point::new(300.0, 120.0),
552        ));
553
554        let obj_ref = manager.add_text_field(field, widget, None).unwrap();
555        assert_eq!(obj_ref.number(), 1);
556        assert!(manager.get_field("username").is_some());
557    }
558
559    #[test]
560    fn test_form_manager_checkbox() {
561        let mut manager = FormManager::new();
562
563        let checkbox = CheckBox::new("agree").checked();
564        let widget = Widget::new(Rectangle::new(
565            Point::new(100.0, 100.0),
566            Point::new(115.0, 115.0),
567        ));
568
569        let obj_ref = manager.add_checkbox(checkbox, widget, None).unwrap();
570        assert_eq!(obj_ref.number(), 1);
571        assert!(manager.get_field("agree").is_some());
572    }
573
574    /// PR3 / QUAL-6: `iter_fields_sorted` is non-fallible after cleanup.
575    /// Exercises two invariants at once:
576    ///   * The iterator yields the flat tuple `(&String, &FormField, ObjectReference)`
577    ///     — not a `Result<_>`, because the only previously-documented
578    ///     error path ("sister maps desynchronised") is a "can't happen"
579    ///     invariant upheld by every `add_*_field` call.
580    ///   * Fields are emitted in ASCII-lexicographic order of their
581    ///     partial names, regardless of insertion order. This is what
582    ///     keeps writer object-id allocation reproducible across runs.
583    #[test]
584    fn iter_fields_sorted_is_non_fallible_and_ordered() {
585        let mut manager = FormManager::new();
586        // Deliberately unsorted insertion: "zeta", "alpha", "mu".
587        let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(100.0, 20.0));
588        manager
589            .add_text_field(TextField::new("zeta"), Widget::new(rect), None)
590            .expect("add zeta");
591        manager
592            .add_text_field(TextField::new("alpha"), Widget::new(rect), None)
593            .expect("add alpha");
594        manager
595            .add_text_field(TextField::new("mu"), Widget::new(rect), None)
596            .expect("add mu");
597
598        let names: Vec<String> = manager
599            .iter_fields_sorted()
600            .map(|(name, _field, _placeholder)| name.clone())
601            .collect();
602        assert_eq!(
603            names,
604            vec!["alpha".to_string(), "mu".to_string(), "zeta".to_string()],
605            "iter_fields_sorted must yield keys in ASCII-lexicographic order"
606        );
607    }
608
609    #[test]
610    fn test_form_manager_multiple_fields() {
611        let mut manager = FormManager::new();
612
613        // Add text field
614        let text_field = TextField::new("name");
615        let text_widget = Widget::new(Rectangle::new(
616            Point::new(100.0, 200.0),
617            Point::new(300.0, 220.0),
618        ));
619        manager
620            .add_text_field(text_field, text_widget, None)
621            .unwrap();
622
623        // Add checkbox
624        let checkbox = CheckBox::new("subscribe");
625        let check_widget = Widget::new(Rectangle::new(
626            Point::new(100.0, 150.0),
627            Point::new(115.0, 165.0),
628        ));
629        manager.add_checkbox(checkbox, check_widget, None).unwrap();
630
631        assert_eq!(manager.fields().len(), 2);
632        assert!(manager.get_field("name").is_some());
633        assert!(manager.get_field("subscribe").is_some());
634    }
635
636    #[test]
637    fn test_acro_form_comprehensive() {
638        let mut acro_form = AcroForm::new();
639
640        // Test initial state
641        assert_eq!(acro_form.fields.len(), 0);
642        assert!(acro_form.need_appearances);
643        assert!(acro_form.sig_flags.is_none());
644        assert!(acro_form.co.is_none());
645        assert!(acro_form.dr.is_none());
646        assert!(acro_form.da.is_some());
647        assert!(acro_form.q.is_none());
648
649        // Add multiple fields
650        for i in 1..=5 {
651            acro_form.add_field(ObjectReference::new(i, 0));
652        }
653        assert_eq!(acro_form.fields.len(), 5);
654
655        // Set optional properties
656        acro_form.sig_flags = Some(3);
657        acro_form.co = Some(vec![ObjectReference::new(1, 0), ObjectReference::new(2, 0)]);
658        acro_form.q = Some(2); // Right alignment
659
660        let mut dr = Dictionary::new();
661        dr.set("Font", Object::String("Helvetica".to_string()));
662        acro_form.dr = Some(dr);
663
664        // Test dictionary conversion
665        let dict = acro_form.to_dict();
666
667        // Verify Fields array
668        if let Some(Object::Array(fields)) = dict.get("Fields") {
669            assert_eq!(fields.len(), 5);
670            for (i, field) in fields.iter().enumerate() {
671                assert_eq!(
672                    *field,
673                    Object::Reference(ObjectReference::new((i + 1) as u32, 0))
674                );
675            }
676        } else {
677            panic!("Expected Fields array");
678        }
679
680        // Verify other properties
681        assert_eq!(dict.get("NeedAppearances"), Some(&Object::Boolean(true)));
682        assert_eq!(dict.get("SigFlags"), Some(&Object::Integer(3)));
683        assert_eq!(dict.get("Q"), Some(&Object::Integer(2)));
684        assert!(dict.get("CO").is_some());
685        assert!(dict.get("DR").is_some());
686        assert!(dict.get("DA").is_some());
687    }
688
689    #[test]
690    fn test_acro_form_default() {
691        let acro_form = AcroForm::default();
692        let default_form = AcroForm::new();
693
694        assert_eq!(acro_form.fields.len(), default_form.fields.len());
695        assert_eq!(acro_form.need_appearances, default_form.need_appearances);
696        assert_eq!(acro_form.sig_flags, default_form.sig_flags);
697        assert_eq!(acro_form.da, default_form.da);
698    }
699
700    #[test]
701    fn test_acro_form_debug_clone() {
702        let mut acro_form = AcroForm::new();
703        acro_form.add_field(ObjectReference::new(1, 0));
704        acro_form.sig_flags = Some(1);
705
706        let debug_str = format!("{acro_form:?}");
707        assert!(debug_str.contains("AcroForm"));
708
709        let cloned = acro_form.clone();
710        assert_eq!(acro_form.fields, cloned.fields);
711        assert_eq!(acro_form.sig_flags, cloned.sig_flags);
712        assert_eq!(acro_form.need_appearances, cloned.need_appearances);
713    }
714
715    #[test]
716    fn test_form_data_comprehensive() {
717        let mut form_data = FormData::new();
718
719        // Test initial state
720        assert_eq!(form_data.values.len(), 0);
721        assert_eq!(form_data.get_value("any"), None);
722
723        // Test setting various string types
724        form_data.set_value("string_literal", "test");
725        form_data.set_value(String::from("string_owned"), "test2");
726        form_data.set_value("number", "42");
727        form_data.set_value("empty", "");
728        form_data.set_value("unicode", "café ñoño 你好");
729
730        assert_eq!(form_data.values.len(), 5);
731        assert_eq!(form_data.get_value("string_literal"), Some("test"));
732        assert_eq!(form_data.get_value("string_owned"), Some("test2"));
733        assert_eq!(form_data.get_value("number"), Some("42"));
734        assert_eq!(form_data.get_value("empty"), Some(""));
735        assert_eq!(form_data.get_value("unicode"), Some("café ñoño 你好"));
736
737        // Test overwriting values
738        form_data.set_value("string_literal", "overwritten");
739        assert_eq!(form_data.get_value("string_literal"), Some("overwritten"));
740        assert_eq!(form_data.values.len(), 5); // Count shouldn't change
741
742        // Test case sensitivity
743        form_data.set_value("Test", "uppercase");
744        form_data.set_value("test", "lowercase");
745        assert_eq!(form_data.get_value("Test"), Some("uppercase"));
746        assert_eq!(form_data.get_value("test"), Some("lowercase"));
747        assert_eq!(form_data.values.len(), 7);
748    }
749
750    #[test]
751    fn test_form_data_edge_cases() {
752        let mut form_data = FormData::new();
753
754        // Empty field names
755        form_data.set_value("", "empty_name");
756        assert_eq!(form_data.get_value(""), Some("empty_name"));
757
758        // Very long field names and values
759        let long_name = "a".repeat(1000);
760        let long_value = "b".repeat(2000);
761        form_data.set_value(&long_name, &long_value);
762        assert_eq!(form_data.get_value(&long_name), Some(long_value.as_str()));
763
764        // Special characters in field names
765        form_data.set_value("field/with/slashes", "value1");
766        form_data.set_value("field with spaces", "value2");
767        form_data.set_value("field.with.dots", "value3");
768        form_data.set_value("field-with-dashes", "value4");
769        form_data.set_value("field_with_underscores", "value5");
770
771        assert_eq!(form_data.get_value("field/with/slashes"), Some("value1"));
772        assert_eq!(form_data.get_value("field with spaces"), Some("value2"));
773        assert_eq!(form_data.get_value("field.with.dots"), Some("value3"));
774        assert_eq!(form_data.get_value("field-with-dashes"), Some("value4"));
775        assert_eq!(
776            form_data.get_value("field_with_underscores"),
777            Some("value5")
778        );
779    }
780
781    #[test]
782    fn test_form_data_default_debug_clone() {
783        let form_data = FormData::default();
784        assert_eq!(form_data.values.len(), 0);
785
786        let default_form = FormData::new();
787        assert_eq!(form_data.values.len(), default_form.values.len());
788
789        let debug_str = format!("{form_data:?}");
790        assert!(debug_str.contains("FormData"));
791
792        let cloned = form_data.clone();
793        assert_eq!(form_data.values.len(), cloned.values.len());
794    }
795
796    #[test]
797    fn test_form_manager_comprehensive() {
798        let mut manager = FormManager::new();
799
800        // Test initial state
801        assert_eq!(manager.field_count(), 0);
802        assert_eq!(manager.fields().len(), 0);
803        assert!(manager.get_field("nonexistent").is_none());
804
805        let acroform = manager.get_acro_form();
806        assert_eq!(acroform.fields.len(), 0);
807        assert!(acroform.need_appearances);
808
809        // Test field ID generation
810        let field1 = TextField::new("field1");
811        let widget1 = Widget::new(Rectangle::new(
812            Point::new(0.0, 0.0),
813            Point::new(100.0, 20.0),
814        ));
815        let ref1 = manager.add_text_field(field1, widget1, None).unwrap();
816        assert_eq!(ref1.number(), 1);
817
818        let field2 = TextField::new("field2");
819        let widget2 = Widget::new(Rectangle::new(
820            Point::new(0.0, 30.0),
821            Point::new(100.0, 50.0),
822        ));
823        let ref2 = manager.add_text_field(field2, widget2, None).unwrap();
824        assert_eq!(ref2.number(), 2);
825
826        let field3 = TextField::new("field3");
827        let widget3 = Widget::new(Rectangle::new(
828            Point::new(0.0, 60.0),
829            Point::new(100.0, 80.0),
830        ));
831        let ref3 = manager.add_text_field(field3, widget3, None).unwrap();
832        assert_eq!(ref3.number(), 3);
833
834        assert_eq!(manager.field_count(), 3);
835        assert_eq!(manager.get_acro_form().fields.len(), 3);
836    }
837
838    #[test]
839    fn test_form_manager_push_button() {
840        let mut manager = FormManager::new();
841
842        let button = PushButton::new("submit").with_caption("Submit");
843        let widget = Widget::new(Rectangle::new(
844            Point::new(200.0, 100.0),
845            Point::new(280.0, 130.0),
846        ));
847
848        let obj_ref = manager.add_push_button(button, widget, None).unwrap();
849        assert_eq!(obj_ref.number(), 1);
850        assert!(manager.get_field("submit").is_some());
851
852        let form_field = manager.get_field("submit").unwrap();
853        let dict = &form_field.field_dict;
854        assert_eq!(dict.get("T"), Some(&Object::String("submit".to_string())));
855        assert_eq!(dict.get("FT"), Some(&Object::Name("Btn".to_string())));
856    }
857
858    #[test]
859    fn test_form_manager_radio_buttons() {
860        let mut manager = FormManager::new();
861
862        let radio = RadioButton::new("gender")
863            .add_option("M", "Male")
864            .add_option("F", "Female")
865            .with_selected(0);
866
867        let widgets = vec![
868            Widget::new(Rectangle::new(
869                Point::new(100.0, 100.0),
870                Point::new(115.0, 115.0),
871            )),
872            Widget::new(Rectangle::new(
873                Point::new(150.0, 100.0),
874                Point::new(165.0, 115.0),
875            )),
876        ];
877
878        let obj_ref = manager.add_radio_buttons(radio, widgets, None).unwrap();
879        assert_eq!(obj_ref.number(), 1);
880        assert!(manager.get_field("gender").is_some());
881
882        let form_field = manager.get_field("gender").unwrap();
883        assert_eq!(form_field.widgets.len(), 2);
884
885        let dict = &form_field.field_dict;
886        assert_eq!(dict.get("T"), Some(&Object::String("gender".to_string())));
887        assert_eq!(dict.get("V"), Some(&Object::Name("M".to_string())));
888    }
889
890    #[test]
891    fn test_form_manager_list_box() {
892        let mut manager = FormManager::new();
893
894        let listbox = ListBox::new("languages")
895            .add_option("en", "English")
896            .add_option("es", "Spanish")
897            .add_option("fr", "French")
898            .multi_select()
899            .with_selected(vec![0, 2]);
900
901        let widget = Widget::new(Rectangle::new(
902            Point::new(100.0, 100.0),
903            Point::new(200.0, 150.0),
904        ));
905
906        let obj_ref = manager.add_list_box(listbox, widget, None).unwrap();
907        assert_eq!(obj_ref.number(), 1);
908        assert!(manager.get_field("languages").is_some());
909
910        let form_field = manager.get_field("languages").unwrap();
911        let dict = &form_field.field_dict;
912        assert_eq!(
913            dict.get("T"),
914            Some(&Object::String("languages".to_string()))
915        );
916        assert_eq!(dict.get("FT"), Some(&Object::Name("Ch".to_string())));
917    }
918
919    #[test]
920    fn test_form_manager_combo_box() {
921        let mut manager = FormManager::new();
922
923        let combo = ComboBox::new("country")
924            .add_option("US", "United States")
925            .add_option("CA", "Canada")
926            .editable()
927            .with_value("US");
928
929        let widget = Widget::new(Rectangle::new(
930            Point::new(100.0, 100.0),
931            Point::new(300.0, 120.0),
932        ));
933
934        let obj_ref = manager.add_combo_box(combo, widget, None).unwrap();
935        assert_eq!(obj_ref.number(), 1);
936        assert!(manager.get_field("country").is_some());
937
938        let form_field = manager.get_field("country").unwrap();
939        let dict = &form_field.field_dict;
940        assert_eq!(dict.get("T"), Some(&Object::String("country".to_string())));
941        assert_eq!(dict.get("V"), Some(&Object::String("US".to_string())));
942    }
943
944    #[test]
945    fn test_form_manager_with_field_options() {
946        let mut manager = FormManager::new();
947
948        let options = FieldOptions {
949            flags: FieldFlags {
950                read_only: true,
951                required: true,
952                no_export: false,
953            },
954            default_appearance: Some("/Times-Roman 12 Tf 0 g".to_string()),
955            quadding: Some(1), // Center
956        };
957
958        let field = TextField::new("readonly_field").with_value("Read Only");
959        let widget = Widget::new(Rectangle::new(
960            Point::new(50.0, 50.0),
961            Point::new(250.0, 70.0),
962        ));
963
964        manager
965            .add_text_field(field, widget, Some(options))
966            .unwrap();
967
968        let form_field = manager.get_field("readonly_field").unwrap();
969        let dict = &form_field.field_dict;
970
971        // Check flags were applied
972        if let Some(Object::Integer(flags)) = dict.get("Ff") {
973            assert_ne!(*flags & (1 << 0), 0); // Read-only flag
974            assert_ne!(*flags & (1 << 1), 0); // Required flag
975            assert_eq!(*flags & (1 << 2), 0); // No-export flag not set
976        } else {
977            panic!("Expected Ff field");
978        }
979
980        // Check appearance and quadding
981        assert_eq!(
982            dict.get("DA"),
983            Some(&Object::String("/Times-Roman 12 Tf 0 g".to_string()))
984        );
985        assert_eq!(dict.get("Q"), Some(&Object::Integer(1)));
986    }
987
988    #[test]
989    fn test_form_manager_appearance_resources() {
990        let mut manager = FormManager::new();
991
992        // Test default appearance
993        manager.set_default_appearance("/Courier 10 Tf 0.5 g");
994        let acroform = manager.get_acro_form();
995        assert_eq!(acroform.da, Some("/Courier 10 Tf 0.5 g".to_string()));
996
997        // Test default resources
998        let mut resources = Dictionary::new();
999        let mut font_dict = Dictionary::new();
1000        font_dict.set("F1", Object::String("Helvetica".to_string()));
1001        font_dict.set("F2", Object::String("Times-Roman".to_string()));
1002        resources.set("Font", Object::Dictionary(font_dict));
1003
1004        let mut color_dict = Dictionary::new();
1005        color_dict.set(
1006            "Red",
1007            Object::Array(vec![
1008                Object::Real(1.0),
1009                Object::Real(0.0),
1010                Object::Real(0.0),
1011            ]),
1012        );
1013        resources.set("ColorSpace", Object::Dictionary(color_dict));
1014
1015        manager.set_default_resources(resources);
1016
1017        let acroform = manager.get_acro_form();
1018        assert!(acroform.dr.is_some());
1019
1020        let dict = acroform.to_dict();
1021        assert_eq!(
1022            dict.get("DA"),
1023            Some(&Object::String("/Courier 10 Tf 0.5 g".to_string()))
1024        );
1025
1026        if let Some(Object::Dictionary(dr_dict)) = dict.get("DR") {
1027            assert!(dr_dict.get("Font").is_some());
1028            assert!(dr_dict.get("ColorSpace").is_some());
1029        } else {
1030            panic!("Expected DR dictionary");
1031        }
1032    }
1033
1034    #[test]
1035    fn test_form_manager_mixed_field_types() {
1036        let mut manager = FormManager::new();
1037
1038        // Add one of each field type
1039        let text_field = TextField::new("name").with_value("John");
1040        let text_widget = Widget::new(Rectangle::new(
1041            Point::new(10.0, 100.0),
1042            Point::new(210.0, 120.0),
1043        ));
1044        manager
1045            .add_text_field(text_field, text_widget, None)
1046            .unwrap();
1047
1048        let checkbox = CheckBox::new("agree").checked();
1049        let check_widget = Widget::new(Rectangle::new(
1050            Point::new(10.0, 80.0),
1051            Point::new(25.0, 95.0),
1052        ));
1053        manager.add_checkbox(checkbox, check_widget, None).unwrap();
1054
1055        let button = PushButton::new("submit");
1056        let button_widget = Widget::new(Rectangle::new(
1057            Point::new(10.0, 50.0),
1058            Point::new(80.0, 75.0),
1059        ));
1060        manager
1061            .add_push_button(button, button_widget, None)
1062            .unwrap();
1063
1064        let radio = RadioButton::new("choice").add_option("A", "Option A");
1065        let radio_widgets = vec![Widget::new(Rectangle::new(
1066            Point::new(10.0, 30.0),
1067            Point::new(25.0, 45.0),
1068        ))];
1069        manager
1070            .add_radio_buttons(radio, radio_widgets, None)
1071            .unwrap();
1072
1073        let listbox = ListBox::new("items").add_option("1", "Item 1");
1074        let list_widget = Widget::new(Rectangle::new(
1075            Point::new(10.0, 10.0),
1076            Point::new(110.0, 25.0),
1077        ));
1078        manager.add_list_box(listbox, list_widget, None).unwrap();
1079
1080        let combo = ComboBox::new("selection").add_option("opt", "Option");
1081        let combo_widget = Widget::new(Rectangle::new(
1082            Point::new(120.0, 10.0),
1083            Point::new(220.0, 25.0),
1084        ));
1085        manager.add_combo_box(combo, combo_widget, None).unwrap();
1086
1087        // Verify all fields were added
1088        assert_eq!(manager.field_count(), 6);
1089        assert!(manager.get_field("name").is_some());
1090        assert!(manager.get_field("agree").is_some());
1091        assert!(manager.get_field("submit").is_some());
1092        assert!(manager.get_field("choice").is_some());
1093        assert!(manager.get_field("items").is_some());
1094        assert!(manager.get_field("selection").is_some());
1095
1096        // Verify AcroForm has all references
1097        let acroform = manager.get_acro_form();
1098        assert_eq!(acroform.fields.len(), 6);
1099
1100        // Verify object references are sequential
1101        for (i, field_ref) in acroform.fields.iter().enumerate() {
1102            assert_eq!(field_ref.number(), (i + 1) as u32);
1103            assert_eq!(field_ref.generation(), 0);
1104        }
1105    }
1106
1107    #[test]
1108    fn test_form_manager_debug_default() {
1109        let manager = FormManager::new();
1110        let debug_str = format!("{manager:?}");
1111        assert!(debug_str.contains("FormManager"));
1112
1113        let default_manager = FormManager::default();
1114        assert_eq!(manager.field_count(), default_manager.field_count());
1115    }
1116
1117    #[test]
1118    fn test_form_manager_error_scenarios() {
1119        let mut manager = FormManager::new();
1120
1121        // Test with empty field names
1122        let empty_field = TextField::new("");
1123        let widget = Widget::new(Rectangle::new(
1124            Point::new(0.0, 0.0),
1125            Point::new(100.0, 20.0),
1126        ));
1127        let result = manager.add_text_field(empty_field, widget, None);
1128        assert!(result.is_ok());
1129        assert!(manager.get_field("").is_some());
1130
1131        // Test duplicate field names (should be allowed)
1132        let field1 = TextField::new("duplicate");
1133        let widget1 = Widget::new(Rectangle::new(
1134            Point::new(0.0, 0.0),
1135            Point::new(100.0, 20.0),
1136        ));
1137        manager.add_text_field(field1, widget1, None).unwrap();
1138
1139        let field2 = TextField::new("duplicate");
1140        let widget2 = Widget::new(Rectangle::new(
1141            Point::new(0.0, 30.0),
1142            Point::new(100.0, 50.0),
1143        ));
1144        manager.add_text_field(field2, widget2, None).unwrap();
1145
1146        // Second field should replace the first
1147        assert_eq!(manager.field_count(), 2); // Empty field + duplicate
1148        assert!(manager.get_field("duplicate").is_some());
1149    }
1150
1151    #[test]
1152    fn test_acro_form_calculation_order() {
1153        let mut acro_form = AcroForm::new();
1154
1155        // Set calculation order
1156        let calc_order = vec![
1157            ObjectReference::new(3, 0),
1158            ObjectReference::new(1, 0),
1159            ObjectReference::new(2, 0),
1160        ];
1161        acro_form.co = Some(calc_order);
1162
1163        let dict = acro_form.to_dict();
1164
1165        if let Some(Object::Array(co_array)) = dict.get("CO") {
1166            assert_eq!(co_array.len(), 3);
1167            assert_eq!(co_array[0], Object::Reference(ObjectReference::new(3, 0)));
1168            assert_eq!(co_array[1], Object::Reference(ObjectReference::new(1, 0)));
1169            assert_eq!(co_array[2], Object::Reference(ObjectReference::new(2, 0)));
1170        } else {
1171            panic!("Expected CO array");
1172        }
1173    }
1174
1175    #[test]
1176    fn test_acro_form_without_optional_fields() {
1177        let acro_form = AcroForm::new();
1178        let dict = acro_form.to_dict();
1179
1180        // These should not be present
1181        assert!(dict.get("SigFlags").is_none());
1182        assert!(dict.get("CO").is_none());
1183        assert!(dict.get("DR").is_none());
1184        assert!(dict.get("Q").is_none());
1185
1186        // These should be present
1187        assert!(dict.get("Fields").is_some());
1188        assert!(dict.get("NeedAppearances").is_some());
1189        assert!(dict.get("DA").is_some());
1190    }
1191}