1use crate::geometry::Rectangle;
6use crate::objects::{Dictionary, Object};
7
8pub fn create_text_field_dict(
10 name: &str,
11 rect: Rectangle,
12 default_value: Option<&str>,
13) -> Dictionary {
14 let mut dict = Dictionary::new();
15
16 dict.set("Type", Object::Name("Annot".to_string()));
18 dict.set("Subtype", Object::Name("Widget".to_string()));
19
20 dict.set("FT", Object::Name("Tx".to_string())); dict.set("T", Object::String(name.to_string())); dict.set(
26 "Rect",
27 Object::Array(vec![
28 Object::Real(rect.lower_left.x),
29 Object::Real(rect.lower_left.y),
30 Object::Real(rect.upper_right.x),
31 Object::Real(rect.upper_right.y),
32 ]),
33 );
34
35 dict.set("DA", Object::String("/Helv 12 Tf 0 g".to_string()));
37
38 if let Some(value) = default_value {
40 dict.set("V", Object::String(value.to_string()));
41 dict.set("DV", Object::String(value.to_string()));
42 }
43
44 dict.set("F", Object::Integer(4)); let mut mk = Dictionary::new();
49 mk.set("BC", Object::Array(vec![Object::Real(0.0)])); mk.set("BG", Object::Array(vec![Object::Real(1.0)])); dict.set("MK", Object::Dictionary(mk));
52
53 let mut bs = Dictionary::new();
55 bs.set("W", Object::Real(1.0));
56 bs.set("S", Object::Name("S".to_string())); dict.set("BS", Object::Dictionary(bs));
58
59 dict
60}
61
62pub fn create_checkbox_dict(name: &str, rect: Rectangle, checked: bool) -> Dictionary {
64 let mut dict = Dictionary::new();
65
66 dict.set("Type", Object::Name("Annot".to_string()));
68 dict.set("Subtype", Object::Name("Widget".to_string()));
69
70 dict.set("FT", Object::Name("Btn".to_string())); dict.set("T", Object::String(name.to_string())); dict.set(
76 "Rect",
77 Object::Array(vec![
78 Object::Real(rect.lower_left.x),
79 Object::Real(rect.lower_left.y),
80 Object::Real(rect.upper_right.x),
81 Object::Real(rect.upper_right.y),
82 ]),
83 );
84
85 if checked {
87 dict.set("V", Object::Name("Yes".to_string()));
88 dict.set("AS", Object::Name("Yes".to_string())); } else {
90 dict.set("V", Object::Name("Off".to_string()));
91 dict.set("AS", Object::Name("Off".to_string()));
92 }
93
94 dict.set("F", Object::Integer(4)); let mut mk = Dictionary::new();
99 mk.set("BC", Object::Array(vec![Object::Real(0.0)])); mk.set("BG", Object::Array(vec![Object::Real(1.0)])); dict.set("MK", Object::Dictionary(mk));
102
103 dict
104}
105
106pub fn create_radio_button_dict(
108 name: &str,
109 rect: Rectangle,
110 export_value: &str,
111 checked: bool,
112) -> Dictionary {
113 let mut dict = Dictionary::new();
114
115 dict.set("Type", Object::Name("Annot".to_string()));
117 dict.set("Subtype", Object::Name("Widget".to_string()));
118
119 dict.set("FT", Object::Name("Btn".to_string())); dict.set("T", Object::String(name.to_string())); dict.set(
125 "Rect",
126 Object::Array(vec![
127 Object::Real(rect.lower_left.x),
128 Object::Real(rect.lower_left.y),
129 Object::Real(rect.upper_right.x),
130 Object::Real(rect.upper_right.y),
131 ]),
132 );
133
134 dict.set("Ff", Object::Integer((1 << 15) | (1 << 16)));
136
137 if checked {
139 dict.set("V", Object::Name(export_value.to_string()));
140 dict.set("AS", Object::Name(export_value.to_string()));
141 } else {
142 dict.set("AS", Object::Name("Off".to_string()));
143 }
144
145 dict.set("F", Object::Integer(4)); let mut mk = Dictionary::new();
150 mk.set("BC", Object::Array(vec![Object::Real(0.0)])); mk.set("BG", Object::Array(vec![Object::Real(1.0)])); mk.set("CA", Object::String("l".to_string())); dict.set("MK", Object::Dictionary(mk));
154
155 dict
156}
157
158pub fn create_combo_box_dict(
160 name: &str,
161 rect: Rectangle,
162 options: Vec<(&str, &str)>, default_value: Option<&str>,
164) -> Dictionary {
165 let mut dict = Dictionary::new();
166
167 dict.set("Type", Object::Name("Annot".to_string()));
169 dict.set("Subtype", Object::Name("Widget".to_string()));
170
171 dict.set("FT", Object::Name("Ch".to_string())); dict.set("T", Object::String(name.to_string())); dict.set(
177 "Rect",
178 Object::Array(vec![
179 Object::Real(rect.lower_left.x),
180 Object::Real(rect.lower_left.y),
181 Object::Real(rect.upper_right.x),
182 Object::Real(rect.upper_right.y),
183 ]),
184 );
185
186 dict.set("Ff", Object::Integer(1 << 17)); let opt_array: Vec<Object> = options
191 .iter()
192 .map(|(export, display)| {
193 if export == display {
194 Object::String((*display).to_string())
195 } else {
196 Object::Array(vec![
197 Object::String((*export).to_string()),
198 Object::String((*display).to_string()),
199 ])
200 }
201 })
202 .collect();
203 dict.set("Opt", Object::Array(opt_array));
204
205 if let Some(value) = default_value {
207 dict.set("V", Object::String(value.to_string()));
208 dict.set("DV", Object::String(value.to_string()));
209 }
210
211 dict.set("DA", Object::String("/Helv 12 Tf 0 g".to_string()));
213
214 dict.set("F", Object::Integer(4)); let mut mk = Dictionary::new();
219 mk.set("BC", Object::Array(vec![Object::Real(0.0)])); mk.set("BG", Object::Array(vec![Object::Real(1.0)])); dict.set("MK", Object::Dictionary(mk));
222
223 let mut bs = Dictionary::new();
225 bs.set("W", Object::Real(1.0));
226 bs.set("S", Object::Name("S".to_string())); dict.set("BS", Object::Dictionary(bs));
228
229 dict
230}
231
232pub fn create_list_box_dict(
234 name: &str,
235 rect: Rectangle,
236 options: Vec<(&str, &str)>, selected: Vec<usize>, multi_select: bool,
239) -> Dictionary {
240 let mut dict = Dictionary::new();
241
242 dict.set("Type", Object::Name("Annot".to_string()));
244 dict.set("Subtype", Object::Name("Widget".to_string()));
245
246 dict.set("FT", Object::Name("Ch".to_string())); dict.set("T", Object::String(name.to_string())); dict.set(
252 "Rect",
253 Object::Array(vec![
254 Object::Real(rect.lower_left.x),
255 Object::Real(rect.lower_left.y),
256 Object::Real(rect.upper_right.x),
257 Object::Real(rect.upper_right.y),
258 ]),
259 );
260
261 let mut flags = 0u32;
263 if multi_select {
264 flags |= 1 << 21; }
266 dict.set("Ff", Object::Integer(flags as i64));
267
268 let opt_array: Vec<Object> = options
270 .iter()
271 .map(|(export, display)| {
272 if export == display {
273 Object::String((*display).to_string())
274 } else {
275 Object::Array(vec![
276 Object::String((*export).to_string()),
277 Object::String((*display).to_string()),
278 ])
279 }
280 })
281 .collect();
282 dict.set("Opt", Object::Array(opt_array));
283
284 if !selected.is_empty() {
286 if multi_select {
287 let indices: Vec<Object> = selected
288 .iter()
289 .map(|&i| Object::Integer(i as i64))
290 .collect();
291 dict.set("I", Object::Array(indices));
292 } else if let Some(&index) = selected.first() {
293 if let Some((export_value, _)) = options.get(index) {
294 dict.set("V", Object::String((*export_value).to_string()));
295 }
296 }
297 }
298
299 dict.set("DA", Object::String("/Helv 12 Tf 0 g".to_string()));
301
302 dict.set("F", Object::Integer(4)); let mut mk = Dictionary::new();
307 mk.set("BC", Object::Array(vec![Object::Real(0.0)])); mk.set("BG", Object::Array(vec![Object::Real(1.0)])); dict.set("MK", Object::Dictionary(mk));
310
311 let mut bs = Dictionary::new();
313 bs.set("W", Object::Real(1.0));
314 bs.set("S", Object::Name("I".to_string())); dict.set("BS", Object::Dictionary(bs));
316
317 dict
318}
319
320pub fn create_push_button_dict(name: &str, rect: Rectangle, caption: &str) -> Dictionary {
322 let mut dict = Dictionary::new();
323
324 dict.set("Type", Object::Name("Annot".to_string()));
326 dict.set("Subtype", Object::Name("Widget".to_string()));
327
328 dict.set("FT", Object::Name("Btn".to_string())); dict.set("T", Object::String(name.to_string())); dict.set(
334 "Rect",
335 Object::Array(vec![
336 Object::Real(rect.lower_left.x),
337 Object::Real(rect.lower_left.y),
338 Object::Real(rect.upper_right.x),
339 Object::Real(rect.upper_right.y),
340 ]),
341 );
342
343 dict.set("Ff", Object::Integer(1 << 16)); dict.set("F", Object::Integer(4)); let mut mk = Dictionary::new();
351 mk.set(
352 "BC",
353 Object::Array(vec![
354 Object::Real(0.2),
355 Object::Real(0.2),
356 Object::Real(0.2),
357 ]),
358 ); mk.set(
360 "BG",
361 Object::Array(vec![
362 Object::Real(0.9),
363 Object::Real(0.9),
364 Object::Real(0.9),
365 ]),
366 ); mk.set("CA", Object::String(caption.to_string())); dict.set("MK", Object::Dictionary(mk));
369
370 let mut bs = Dictionary::new();
372 bs.set("W", Object::Real(2.0));
373 bs.set("S", Object::Name("B".to_string())); dict.set("BS", Object::Dictionary(bs));
375
376 dict.set("DA", Object::String("/Helv 12 Tf 0 g".to_string()));
378
379 dict
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use crate::geometry::Point;
386
387 fn create_test_rect() -> Rectangle {
388 Rectangle {
389 lower_left: Point { x: 100.0, y: 200.0 },
390 upper_right: Point { x: 300.0, y: 250.0 },
391 }
392 }
393
394 #[test]
395 fn test_create_text_field_dict_basic() {
396 let rect = create_test_rect();
397 let dict = create_text_field_dict("test_field", rect, None);
398
399 assert_eq!(dict.get("Type"), Some(&Object::Name("Annot".to_string())));
401 assert_eq!(
402 dict.get("Subtype"),
403 Some(&Object::Name("Widget".to_string()))
404 );
405
406 assert_eq!(dict.get("FT"), Some(&Object::Name("Tx".to_string())));
408 assert_eq!(
409 dict.get("T"),
410 Some(&Object::String("test_field".to_string()))
411 );
412
413 let expected_rect = Object::Array(vec![
415 Object::Real(100.0),
416 Object::Real(200.0),
417 Object::Real(300.0),
418 Object::Real(250.0),
419 ]);
420 assert_eq!(dict.get("Rect"), Some(&expected_rect));
421
422 assert_eq!(
424 dict.get("DA"),
425 Some(&Object::String("/Helv 12 Tf 0 g".to_string()))
426 );
427
428 assert_eq!(dict.get("F"), Some(&Object::Integer(4)));
430
431 assert!(dict.get("V").is_none());
433 assert!(dict.get("DV").is_none());
434 }
435
436 #[test]
437 fn test_create_text_field_dict_with_value() {
438 let rect = create_test_rect();
439 let dict = create_text_field_dict("test_field", rect, Some("default text"));
440
441 assert_eq!(
443 dict.get("V"),
444 Some(&Object::String("default text".to_string()))
445 );
446 assert_eq!(
447 dict.get("DV"),
448 Some(&Object::String("default text".to_string()))
449 );
450 }
451
452 #[test]
453 fn test_create_text_field_dict_appearance_characteristics() {
454 let rect = create_test_rect();
455 let dict = create_text_field_dict("test_field", rect, None);
456
457 if let Some(Object::Dictionary(mk)) = dict.get("MK") {
459 assert_eq!(mk.get("BC"), Some(&Object::Array(vec![Object::Real(0.0)])));
460 assert_eq!(mk.get("BG"), Some(&Object::Array(vec![Object::Real(1.0)])));
461 } else {
462 panic!("MK dictionary not found or wrong type");
463 }
464
465 if let Some(Object::Dictionary(bs)) = dict.get("BS") {
467 assert_eq!(bs.get("W"), Some(&Object::Real(1.0)));
468 assert_eq!(bs.get("S"), Some(&Object::Name("S".to_string())));
469 } else {
470 panic!("BS dictionary not found or wrong type");
471 }
472 }
473
474 #[test]
475 fn test_create_checkbox_dict_unchecked() {
476 let rect = create_test_rect();
477 let dict = create_checkbox_dict("checkbox_field", rect, false);
478
479 assert_eq!(dict.get("FT"), Some(&Object::Name("Btn".to_string())));
481
482 assert_eq!(dict.get("V"), Some(&Object::Name("Off".to_string())));
484 assert_eq!(dict.get("AS"), Some(&Object::Name("Off".to_string())));
485 }
486
487 #[test]
488 fn test_create_checkbox_dict_checked() {
489 let rect = create_test_rect();
490 let dict = create_checkbox_dict("checkbox_field", rect, true);
491
492 assert_eq!(dict.get("V"), Some(&Object::Name("Yes".to_string())));
494 assert_eq!(dict.get("AS"), Some(&Object::Name("Yes".to_string())));
495 }
496
497 #[test]
498 fn test_create_radio_button_dict_unchecked() {
499 let rect = create_test_rect();
500 let dict = create_radio_button_dict("radio_field", rect, "option1", false);
501
502 assert_eq!(dict.get("FT"), Some(&Object::Name("Btn".to_string())));
504
505 let expected_flags = (1 << 15) | (1 << 16);
507 assert_eq!(dict.get("Ff"), Some(&Object::Integer(expected_flags)));
508
509 assert_eq!(dict.get("AS"), Some(&Object::Name("Off".to_string())));
511 assert!(dict.get("V").is_none());
512 }
513
514 #[test]
515 fn test_create_radio_button_dict_checked() {
516 let rect = create_test_rect();
517 let dict = create_radio_button_dict("radio_field", rect, "option1", true);
518
519 assert_eq!(dict.get("V"), Some(&Object::Name("option1".to_string())));
521 assert_eq!(dict.get("AS"), Some(&Object::Name("option1".to_string())));
522
523 if let Some(Object::Dictionary(mk)) = dict.get("MK") {
525 assert_eq!(mk.get("CA"), Some(&Object::String("l".to_string())));
526 } else {
527 panic!("MK dictionary not found");
528 }
529 }
530
531 #[test]
532 fn test_create_combo_box_dict_basic() {
533 let rect = create_test_rect();
534 let options = vec![("val1", "Display 1"), ("val2", "Display 2")];
535 let dict = create_combo_box_dict("combo_field", rect, options, None);
536
537 assert_eq!(dict.get("FT"), Some(&Object::Name("Ch".to_string())));
539
540 assert_eq!(dict.get("Ff"), Some(&Object::Integer(1 << 17)));
542
543 if let Some(Object::Array(opt_array)) = dict.get("Opt") {
545 assert_eq!(opt_array.len(), 2);
546
547 if let Object::Array(first_opt) = &opt_array[0] {
549 assert_eq!(first_opt[0], Object::String("val1".to_string()));
550 assert_eq!(first_opt[1], Object::String("Display 1".to_string()));
551 } else {
552 panic!("First option should be an array");
553 }
554 } else {
555 panic!("Opt array not found");
556 }
557 }
558
559 #[test]
560 fn test_create_combo_box_dict_with_default() {
561 let rect = create_test_rect();
562 let options = vec![("val1", "Display 1"), ("val2", "Display 2")];
563 let dict = create_combo_box_dict("combo_field", rect, options, Some("val1"));
564
565 assert_eq!(dict.get("V"), Some(&Object::String("val1".to_string())));
567 assert_eq!(dict.get("DV"), Some(&Object::String("val1".to_string())));
568 }
569
570 #[test]
571 fn test_create_combo_box_dict_same_export_display() {
572 let rect = create_test_rect();
573 let options = vec![("Option1", "Option1"), ("Option2", "Option2")];
574 let dict = create_combo_box_dict("combo_field", rect, options, None);
575
576 if let Some(Object::Array(opt_array)) = dict.get("Opt") {
578 assert_eq!(opt_array[0], Object::String("Option1".to_string()));
579 assert_eq!(opt_array[1], Object::String("Option2".to_string()));
580 } else {
581 panic!("Opt array not found");
582 }
583 }
584
585 #[test]
586 fn test_create_list_box_dict_single_select() {
587 let rect = create_test_rect();
588 let options = vec![("val1", "Display 1"), ("val2", "Display 2")];
589 let selected = vec![0];
590 let dict = create_list_box_dict("list_field", rect, options, selected, false);
591
592 assert_eq!(dict.get("FT"), Some(&Object::Name("Ch".to_string())));
594
595 assert_eq!(dict.get("Ff"), Some(&Object::Integer(0)));
597
598 assert_eq!(dict.get("V"), Some(&Object::String("val1".to_string())));
600
601 assert!(dict.get("I").is_none());
603 }
604
605 #[test]
606 fn test_create_list_box_dict_multi_select() {
607 let rect = create_test_rect();
608 let options = vec![
609 ("val1", "Display 1"),
610 ("val2", "Display 2"),
611 ("val3", "Display 3"),
612 ];
613 let selected = vec![0, 2];
614 let dict = create_list_box_dict("list_field", rect, options, selected, true);
615
616 assert_eq!(dict.get("Ff"), Some(&Object::Integer(1 << 21)));
618
619 if let Some(Object::Array(indices)) = dict.get("I") {
621 assert_eq!(indices.len(), 2);
622 assert_eq!(indices[0], Object::Integer(0));
623 assert_eq!(indices[1], Object::Integer(2));
624 } else {
625 panic!("I array not found for multiselect");
626 }
627
628 assert!(dict.get("V").is_none());
630 }
631
632 #[test]
633 fn test_create_list_box_dict_no_selection() {
634 let rect = create_test_rect();
635 let options = vec![("val1", "Display 1")];
636 let selected = vec![];
637 let dict = create_list_box_dict("list_field", rect, options, selected, false);
638
639 assert!(dict.get("V").is_none());
641 assert!(dict.get("I").is_none());
642 }
643
644 #[test]
645 fn test_create_push_button_dict() {
646 let rect = create_test_rect();
647 let dict = create_push_button_dict("button_field", rect, "Click Me");
648
649 assert_eq!(dict.get("FT"), Some(&Object::Name("Btn".to_string())));
651
652 assert_eq!(dict.get("Ff"), Some(&Object::Integer(1 << 16)));
654
655 if let Some(Object::Dictionary(mk)) = dict.get("MK") {
657 assert_eq!(mk.get("CA"), Some(&Object::String("Click Me".to_string())));
659
660 if let Some(Object::Array(bc)) = mk.get("BC") {
662 assert_eq!(bc.len(), 3);
663 assert_eq!(bc[0], Object::Real(0.2));
664 assert_eq!(bc[1], Object::Real(0.2));
665 assert_eq!(bc[2], Object::Real(0.2));
666 } else {
667 panic!("BC array not found");
668 }
669
670 if let Some(Object::Array(bg)) = mk.get("BG") {
672 assert_eq!(bg.len(), 3);
673 assert_eq!(bg[0], Object::Real(0.9));
674 assert_eq!(bg[1], Object::Real(0.9));
675 assert_eq!(bg[2], Object::Real(0.9));
676 } else {
677 panic!("BG array not found");
678 }
679 } else {
680 panic!("MK dictionary not found");
681 }
682
683 if let Some(Object::Dictionary(bs)) = dict.get("BS") {
685 assert_eq!(bs.get("W"), Some(&Object::Real(2.0)));
686 assert_eq!(bs.get("S"), Some(&Object::Name("B".to_string())));
687 } else {
688 panic!("BS dictionary not found");
689 }
690 }
691
692 #[test]
693 fn test_all_fields_have_required_properties() {
694 let rect = create_test_rect();
695
696 let fields = vec![
697 create_text_field_dict("text", rect, None),
698 create_checkbox_dict("checkbox", rect, false),
699 create_radio_button_dict("radio", rect, "val", false),
700 create_combo_box_dict("combo", rect, vec![("a", "A")], None),
701 create_list_box_dict("list", rect, vec![("a", "A")], vec![], false),
702 create_push_button_dict("button", rect, "Button"),
703 ];
704
705 for dict in fields {
706 assert!(dict.get("Type").is_some(), "Missing Type");
708 assert!(dict.get("Subtype").is_some(), "Missing Subtype");
709 assert!(dict.get("FT").is_some(), "Missing FT");
710 assert!(dict.get("T").is_some(), "Missing T");
711 assert!(dict.get("Rect").is_some(), "Missing Rect");
712 assert!(dict.get("F").is_some(), "Missing F");
713 assert!(dict.get("MK").is_some(), "Missing MK");
714 }
715 }
716
717 #[test]
718 fn test_field_name_preservation() {
719 let rect = create_test_rect();
720 let field_names = ["simple", "with spaces", "unicode_ñ", "123numbers"];
721
722 for name in &field_names {
723 let dict = create_text_field_dict(name, rect, None);
724 assert_eq!(dict.get("T"), Some(&Object::String((*name).to_string())));
725 }
726 }
727
728 #[test]
729 fn test_rectangle_coordinates() {
730 let rects = vec![
731 Rectangle {
732 lower_left: Point { x: 0.0, y: 0.0 },
733 upper_right: Point { x: 100.0, y: 50.0 },
734 },
735 Rectangle {
736 lower_left: Point { x: -50.0, y: -25.0 },
737 upper_right: Point { x: 50.0, y: 25.0 },
738 },
739 Rectangle {
740 lower_left: Point {
741 x: 200.5,
742 y: 300.75,
743 },
744 upper_right: Point {
745 x: 400.25,
746 y: 500.125,
747 },
748 },
749 ];
750
751 for rect in rects {
752 let dict = create_text_field_dict("test", rect, None);
753
754 let expected_rect = Object::Array(vec![
755 Object::Real(rect.lower_left.x),
756 Object::Real(rect.lower_left.y),
757 Object::Real(rect.upper_right.x),
758 Object::Real(rect.upper_right.y),
759 ]);
760
761 assert_eq!(dict.get("Rect"), Some(&expected_rect));
762 }
763 }
764}