1use crate::geometry::Rectangle;
4use crate::graphics::Color;
5use crate::objects::{Dictionary, Object};
6
7#[derive(Debug, Clone, Copy, Default)]
9pub struct FieldFlags {
10 pub read_only: bool,
12 pub required: bool,
14 pub no_export: bool,
16}
17
18impl FieldFlags {
19 pub fn to_flags(&self) -> u32 {
21 let mut flags = 0u32;
22 if self.read_only {
23 flags |= 1 << 0;
24 }
25 if self.required {
26 flags |= 1 << 1;
27 }
28 if self.no_export {
29 flags |= 1 << 2;
30 }
31 flags
32 }
33}
34
35#[derive(Debug, Clone, Default)]
37pub struct FieldOptions {
38 pub flags: FieldFlags,
40 pub default_appearance: Option<String>,
42 pub quadding: Option<i32>,
44}
45
46#[derive(Debug, Clone)]
48pub struct WidgetAppearance {
49 pub border_color: Option<Color>,
51 pub background_color: Option<Color>,
53 pub border_width: f64,
55 pub border_style: BorderStyle,
57}
58
59#[derive(Debug, Clone, Copy)]
61pub enum BorderStyle {
62 Solid,
64 Dashed,
66 Beveled,
68 Inset,
70 Underline,
72}
73
74impl BorderStyle {
75 pub fn pdf_name(&self) -> &'static str {
77 match self {
78 BorderStyle::Solid => "S",
79 BorderStyle::Dashed => "D",
80 BorderStyle::Beveled => "B",
81 BorderStyle::Inset => "I",
82 BorderStyle::Underline => "U",
83 }
84 }
85}
86
87impl Default for WidgetAppearance {
88 fn default() -> Self {
89 Self {
90 border_color: Some(Color::black()),
91 background_color: None,
92 border_width: 1.0,
93 border_style: BorderStyle::Solid,
94 }
95 }
96}
97
98#[derive(Debug, Clone)]
100pub struct Widget {
101 pub rect: Rectangle,
103 pub appearance: WidgetAppearance,
105 pub parent: Option<String>,
107 pub appearance_streams: Option<crate::forms::AppearanceDictionary>,
109}
110
111impl Widget {
112 pub fn new(rect: Rectangle) -> Self {
114 Self {
115 rect,
116 appearance: WidgetAppearance::default(),
117 parent: None,
118 appearance_streams: None,
119 }
120 }
121
122 pub fn with_appearance(mut self, appearance: WidgetAppearance) -> Self {
124 self.appearance = appearance;
125 self
126 }
127
128 pub fn with_appearance_streams(mut self, streams: crate::forms::AppearanceDictionary) -> Self {
130 self.appearance_streams = Some(streams);
131 self
132 }
133
134 pub fn generate_appearance(
136 &mut self,
137 field_type: crate::forms::FieldType,
138 value: Option<&str>,
139 ) -> crate::error::Result<()> {
140 use crate::forms::{generate_default_appearance, AppearanceDictionary, AppearanceState};
141
142 let mut app_dict = AppearanceDictionary::new();
143
144 let normal_stream = generate_default_appearance(field_type, self, value)?;
146 app_dict.set_appearance(AppearanceState::Normal, normal_stream);
147
148 if field_type == crate::forms::FieldType::Button {
150 let rollover_stream = generate_default_appearance(field_type, self, value)?;
151 app_dict.set_appearance(AppearanceState::Rollover, rollover_stream);
152
153 let down_stream = generate_default_appearance(field_type, self, value)?;
154 app_dict.set_appearance(AppearanceState::Down, down_stream);
155 }
156
157 self.appearance_streams = Some(app_dict);
158 Ok(())
159 }
160
161 pub fn to_annotation_dict(&self) -> Dictionary {
163 let mut dict = Dictionary::new();
164
165 dict.set("Type", Object::Name("Annot".to_string()));
167 dict.set("Subtype", Object::Name("Widget".to_string()));
168
169 let rect_array = vec![
171 Object::Real(self.rect.lower_left.x),
172 Object::Real(self.rect.lower_left.y),
173 Object::Real(self.rect.upper_right.x),
174 Object::Real(self.rect.upper_right.y),
175 ];
176 dict.set("Rect", Object::Array(rect_array));
177
178 let mut bs_dict = Dictionary::new();
180 bs_dict.set("W", Object::Real(self.appearance.border_width));
181 bs_dict.set(
182 "S",
183 Object::Name(self.appearance.border_style.pdf_name().to_string()),
184 );
185 dict.set("BS", Object::Dictionary(bs_dict));
186
187 let mut mk_dict = Dictionary::new();
189
190 if let Some(border_color) = &self.appearance.border_color {
191 let bc = match border_color {
192 Color::Rgb(r, g, b) => vec![Object::Real(*r), Object::Real(*g), Object::Real(*b)],
193 Color::Gray(g) => vec![Object::Real(*g)],
194 Color::Cmyk(c, m, y, k) => vec![
195 Object::Real(*c),
196 Object::Real(*m),
197 Object::Real(*y),
198 Object::Real(*k),
199 ],
200 };
201 mk_dict.set("BC", Object::Array(bc));
202 }
203
204 if let Some(bg_color) = &self.appearance.background_color {
205 let bg = match bg_color {
206 Color::Rgb(r, g, b) => vec![Object::Real(*r), Object::Real(*g), Object::Real(*b)],
207 Color::Gray(g) => vec![Object::Real(*g)],
208 Color::Cmyk(c, m, y, k) => vec![
209 Object::Real(*c),
210 Object::Real(*m),
211 Object::Real(*y),
212 Object::Real(*k),
213 ],
214 };
215 mk_dict.set("BG", Object::Array(bg));
216 }
217
218 dict.set("MK", Object::Dictionary(mk_dict));
219
220 dict.set("F", Object::Integer(4));
222
223 if let Some(ref app_streams) = self.appearance_streams {
225 dict.set("AP", Object::Dictionary(app_streams.to_dict()));
226 }
227
228 dict
229 }
230}
231
232pub trait Field {
234 fn name(&self) -> &str;
236
237 fn field_type(&self) -> &'static str;
239
240 fn to_dict(&self) -> Dictionary;
242}
243
244#[derive(Debug, Clone)]
246pub struct FormField {
247 pub field_dict: Dictionary,
249 pub widgets: Vec<Widget>,
251}
252
253impl FormField {
254 pub fn new(field_dict: Dictionary) -> Self {
256 Self {
257 field_dict,
258 widgets: Vec::new(),
259 }
260 }
261
262 pub fn add_widget(&mut self, widget: Widget) {
264 self.widgets.push(widget);
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use crate::geometry::Point;
272
273 #[test]
274 fn test_field_flags() {
275 let flags = FieldFlags {
276 read_only: true,
277 required: true,
278 no_export: false,
279 };
280
281 assert_eq!(flags.to_flags(), 3); }
283
284 #[test]
285 fn test_border_style() {
286 assert_eq!(BorderStyle::Solid.pdf_name(), "S");
287 assert_eq!(BorderStyle::Dashed.pdf_name(), "D");
288 assert_eq!(BorderStyle::Beveled.pdf_name(), "B");
289 assert_eq!(BorderStyle::Inset.pdf_name(), "I");
290 assert_eq!(BorderStyle::Underline.pdf_name(), "U");
291 }
292
293 #[test]
294 fn test_widget_creation() {
295 let rect = Rectangle::new(Point::new(100.0, 100.0), Point::new(200.0, 120.0));
296
297 let widget = Widget::new(rect);
298 assert_eq!(widget.rect.lower_left.x, 100.0);
299 assert_eq!(widget.appearance.border_width, 1.0);
300 }
301
302 #[test]
303 fn test_widget_annotation_dict() {
304 let rect = Rectangle::new(Point::new(50.0, 50.0), Point::new(150.0, 70.0));
305
306 let appearance = WidgetAppearance {
307 border_color: Some(Color::Rgb(0.0, 0.0, 1.0)),
308 background_color: Some(Color::Gray(0.9)),
309 border_width: 2.0,
310 border_style: BorderStyle::Solid,
311 };
312
313 let widget = Widget::new(rect).with_appearance(appearance);
314 let dict = widget.to_annotation_dict();
315
316 assert_eq!(dict.get("Type"), Some(&Object::Name("Annot".to_string())));
317 assert_eq!(
318 dict.get("Subtype"),
319 Some(&Object::Name("Widget".to_string()))
320 );
321 assert!(dict.get("Rect").is_some());
322 assert!(dict.get("BS").is_some());
323 assert!(dict.get("MK").is_some());
324 }
325
326 #[test]
327 fn test_field_options_default() {
328 let options = FieldOptions::default();
329 assert!(!options.flags.read_only);
330 assert!(!options.flags.required);
331 assert!(!options.flags.no_export);
332 assert!(options.default_appearance.is_none());
333 assert!(options.quadding.is_none());
334 }
335
336 #[test]
337 fn test_field_flags_all_combinations() {
338 let flag_combos = [
340 (false, false, false, 0),
341 (true, false, false, 1),
342 (false, true, false, 2),
343 (false, false, true, 4),
344 (true, true, false, 3),
345 (true, false, true, 5),
346 (false, true, true, 6),
347 (true, true, true, 7),
348 ];
349
350 for (read_only, required, no_export, expected) in flag_combos {
351 let flags = FieldFlags {
352 read_only,
353 required,
354 no_export,
355 };
356 assert_eq!(
357 flags.to_flags(),
358 expected,
359 "Failed for read_only={read_only}, required={required}, no_export={no_export}"
360 );
361 }
362 }
363
364 #[test]
365 fn test_field_flags_debug_clone_default() {
366 let flags = FieldFlags::default();
367 let debug_str = format!("{flags:?}");
368 assert!(debug_str.contains("FieldFlags"));
369
370 let cloned = flags;
371 assert_eq!(flags.read_only, cloned.read_only);
372 assert_eq!(flags.required, cloned.required);
373 assert_eq!(flags.no_export, cloned.no_export);
374
375 let copied = flags;
377 assert_eq!(flags.read_only, copied.read_only);
378 }
379
380 #[test]
381 fn test_border_style_debug_clone_copy() {
382 let style = BorderStyle::Solid;
383 let debug_str = format!("{style:?}");
384 assert!(debug_str.contains("Solid"));
385
386 let cloned = style;
387 assert_eq!(style.pdf_name(), cloned.pdf_name());
388
389 let copied = style;
391 assert_eq!(style.pdf_name(), copied.pdf_name());
392 }
393
394 #[test]
395 fn test_all_border_styles() {
396 let styles = [
397 (BorderStyle::Solid, "S"),
398 (BorderStyle::Dashed, "D"),
399 (BorderStyle::Beveled, "B"),
400 (BorderStyle::Inset, "I"),
401 (BorderStyle::Underline, "U"),
402 ];
403
404 for (style, expected) in styles {
405 assert_eq!(style.pdf_name(), expected);
406 }
407 }
408
409 #[test]
410 fn test_widget_appearance_default() {
411 let appearance = WidgetAppearance::default();
412 assert_eq!(appearance.border_color, Some(Color::black()));
413 assert_eq!(appearance.background_color, None);
414 assert_eq!(appearance.border_width, 1.0);
415 match appearance.border_style {
416 BorderStyle::Solid => {}
417 _ => panic!("Expected solid border style"),
418 }
419 }
420
421 #[test]
422 fn test_widget_appearance_debug_clone() {
423 let appearance = WidgetAppearance {
424 border_color: Some(Color::red()),
425 background_color: Some(Color::gray(0.5)),
426 border_width: 2.5,
427 border_style: BorderStyle::Dashed,
428 };
429
430 let debug_str = format!("{appearance:?}");
431 assert!(debug_str.contains("WidgetAppearance"));
432
433 let cloned = appearance.clone();
434 assert_eq!(appearance.border_color, cloned.border_color);
435 assert_eq!(appearance.background_color, cloned.background_color);
436 assert_eq!(appearance.border_width, cloned.border_width);
437 }
438
439 #[test]
440 fn test_widget_debug_clone() {
441 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(100.0, 50.0));
442 let widget = Widget::new(rect);
443
444 let debug_str = format!("{widget:?}");
445 assert!(debug_str.contains("Widget"));
446
447 let cloned = widget.clone();
448 assert_eq!(widget.rect.lower_left.x, cloned.rect.lower_left.x);
449 assert_eq!(widget.parent, cloned.parent);
450 }
451
452 #[test]
453 fn test_widget_with_parent() {
454 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(100.0, 50.0));
455 let mut widget = Widget::new(rect);
456 widget.parent = Some("TextField1".to_string());
457
458 assert_eq!(widget.parent, Some("TextField1".to_string()));
459 }
460
461 #[test]
462 fn test_widget_annotation_dict_rgb_colors() {
463 let rect = Rectangle::new(Point::new(10.0, 20.0), Point::new(110.0, 40.0));
464 let appearance = WidgetAppearance {
465 border_color: Some(Color::rgb(1.0, 0.0, 0.0)),
466 background_color: Some(Color::rgb(0.0, 1.0, 0.0)),
467 border_width: 1.5,
468 border_style: BorderStyle::Dashed,
469 };
470
471 let widget = Widget::new(rect).with_appearance(appearance);
472 let dict = widget.to_annotation_dict();
473
474 if let Some(Object::Array(rect_array)) = dict.get("Rect") {
476 assert_eq!(rect_array.len(), 4);
477 assert_eq!(rect_array[0], Object::Real(10.0));
478 assert_eq!(rect_array[1], Object::Real(20.0));
479 assert_eq!(rect_array[2], Object::Real(110.0));
480 assert_eq!(rect_array[3], Object::Real(40.0));
481 } else {
482 panic!("Expected Rect array");
483 }
484
485 if let Some(Object::Dictionary(bs_dict)) = dict.get("BS") {
487 assert_eq!(bs_dict.get("W"), Some(&Object::Real(1.5)));
488 assert_eq!(bs_dict.get("S"), Some(&Object::Name("D".to_string())));
489 } else {
490 panic!("Expected BS dictionary");
491 }
492
493 if let Some(Object::Dictionary(mk_dict)) = dict.get("MK") {
495 if let Some(Object::Array(bc_array)) = mk_dict.get("BC") {
497 assert_eq!(bc_array.len(), 3);
498 assert_eq!(bc_array[0], Object::Real(1.0));
499 assert_eq!(bc_array[1], Object::Real(0.0));
500 assert_eq!(bc_array[2], Object::Real(0.0));
501 } else {
502 panic!("Expected BC array");
503 }
504
505 if let Some(Object::Array(bg_array)) = mk_dict.get("BG") {
507 assert_eq!(bg_array.len(), 3);
508 assert_eq!(bg_array[0], Object::Real(0.0));
509 assert_eq!(bg_array[1], Object::Real(1.0));
510 assert_eq!(bg_array[2], Object::Real(0.0));
511 } else {
512 panic!("Expected BG array");
513 }
514 } else {
515 panic!("Expected MK dictionary");
516 }
517
518 assert_eq!(dict.get("F"), Some(&Object::Integer(4)));
520 }
521
522 #[test]
523 fn test_widget_annotation_dict_gray_colors() {
524 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(50.0, 25.0));
525 let appearance = WidgetAppearance {
526 border_color: Some(Color::gray(0.3)),
527 background_color: Some(Color::gray(0.9)),
528 border_width: 0.5,
529 border_style: BorderStyle::Beveled,
530 };
531
532 let widget = Widget::new(rect).with_appearance(appearance);
533 let dict = widget.to_annotation_dict();
534
535 if let Some(Object::Dictionary(mk_dict)) = dict.get("MK") {
536 if let Some(Object::Array(bc_array)) = mk_dict.get("BC") {
538 assert_eq!(bc_array.len(), 1);
539 assert_eq!(bc_array[0], Object::Real(0.3));
540 } else {
541 panic!("Expected BC array");
542 }
543
544 if let Some(Object::Array(bg_array)) = mk_dict.get("BG") {
546 assert_eq!(bg_array.len(), 1);
547 assert_eq!(bg_array[0], Object::Real(0.9));
548 } else {
549 panic!("Expected BG array");
550 }
551 } else {
552 panic!("Expected MK dictionary");
553 }
554 }
555
556 #[test]
557 fn test_widget_annotation_dict_cmyk_colors() {
558 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(50.0, 25.0));
559 let appearance = WidgetAppearance {
560 border_color: Some(Color::cmyk(0.1, 0.2, 0.3, 0.4)),
561 background_color: Some(Color::cmyk(0.5, 0.6, 0.7, 0.8)),
562 border_width: 3.0,
563 border_style: BorderStyle::Inset,
564 };
565
566 let widget = Widget::new(rect).with_appearance(appearance);
567 let dict = widget.to_annotation_dict();
568
569 if let Some(Object::Dictionary(mk_dict)) = dict.get("MK") {
570 if let Some(Object::Array(bc_array)) = mk_dict.get("BC") {
572 assert_eq!(bc_array.len(), 4);
573 assert_eq!(bc_array[0], Object::Real(0.1));
574 assert_eq!(bc_array[1], Object::Real(0.2));
575 assert_eq!(bc_array[2], Object::Real(0.3));
576 assert_eq!(bc_array[3], Object::Real(0.4));
577 } else {
578 panic!("Expected BC array");
579 }
580
581 if let Some(Object::Array(bg_array)) = mk_dict.get("BG") {
583 assert_eq!(bg_array.len(), 4);
584 assert_eq!(bg_array[0], Object::Real(0.5));
585 assert_eq!(bg_array[1], Object::Real(0.6));
586 assert_eq!(bg_array[2], Object::Real(0.7));
587 assert_eq!(bg_array[3], Object::Real(0.8));
588 } else {
589 panic!("Expected BG array");
590 }
591 } else {
592 panic!("Expected MK dictionary");
593 }
594 }
595
596 #[test]
597 fn test_widget_annotation_dict_no_colors() {
598 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(50.0, 25.0));
599 let appearance = WidgetAppearance {
600 border_color: None,
601 background_color: None,
602 border_width: 2.0,
603 border_style: BorderStyle::Underline,
604 };
605
606 let widget = Widget::new(rect).with_appearance(appearance);
607 let dict = widget.to_annotation_dict();
608
609 if let Some(Object::Dictionary(mk_dict)) = dict.get("MK") {
610 assert!(mk_dict.get("BC").is_none());
611 assert!(mk_dict.get("BG").is_none());
612 } else {
613 panic!("Expected MK dictionary");
614 }
615 }
616
617 #[test]
618 fn test_field_options_with_values() {
619 let flags = FieldFlags {
620 read_only: true,
621 required: false,
622 no_export: true,
623 };
624
625 let options = FieldOptions {
626 flags,
627 default_appearance: Some("/Helv 12 Tf 0 g".to_string()),
628 quadding: Some(1), };
630
631 assert!(options.flags.read_only);
632 assert!(!options.flags.required);
633 assert!(options.flags.no_export);
634 assert_eq!(
635 options.default_appearance,
636 Some("/Helv 12 Tf 0 g".to_string())
637 );
638 assert_eq!(options.quadding, Some(1));
639 }
640
641 #[test]
642 fn test_field_options_debug_clone_default() {
643 let options = FieldOptions::default();
644 let debug_str = format!("{options:?}");
645 assert!(debug_str.contains("FieldOptions"));
646
647 let cloned = options.clone();
648 assert_eq!(options.flags.read_only, cloned.flags.read_only);
649 assert_eq!(options.default_appearance, cloned.default_appearance);
650 assert_eq!(options.quadding, cloned.quadding);
651 }
652
653 #[test]
654 fn test_form_field_creation() {
655 let mut field_dict = Dictionary::new();
656 field_dict.set("T", Object::String("TestField".to_string()));
657 field_dict.set("FT", Object::Name("Tx".to_string()));
658
659 let form_field = FormField::new(field_dict);
660 assert_eq!(form_field.widgets.len(), 0);
661 assert_eq!(
662 form_field.field_dict.get("T"),
663 Some(&Object::String("TestField".to_string()))
664 );
665 }
666
667 #[test]
668 fn test_form_field_add_widget() {
669 let mut field_dict = Dictionary::new();
670 field_dict.set("T", Object::String("TestField".to_string()));
671
672 let mut form_field = FormField::new(field_dict);
673
674 let rect1 = Rectangle::new(Point::new(10.0, 10.0), Point::new(110.0, 30.0));
675 let widget1 = Widget::new(rect1);
676
677 let rect2 = Rectangle::new(Point::new(10.0, 50.0), Point::new(110.0, 70.0));
678 let widget2 = Widget::new(rect2);
679
680 form_field.add_widget(widget1);
681 form_field.add_widget(widget2);
682
683 assert_eq!(form_field.widgets.len(), 2);
684 assert_eq!(form_field.widgets[0].rect.lower_left.x, 10.0);
685 assert_eq!(form_field.widgets[1].rect.lower_left.y, 50.0);
686 }
687
688 #[test]
689 fn test_form_field_debug_clone() {
690 let field_dict = Dictionary::new();
691 let form_field = FormField::new(field_dict);
692
693 let debug_str = format!("{form_field:?}");
694 assert!(debug_str.contains("FormField"));
695
696 let cloned = form_field.clone();
697 assert_eq!(form_field.widgets.len(), cloned.widgets.len());
698 }
699
700 #[test]
701 fn test_widget_rect_boundary_values() {
702 let test_rects = [
704 Rectangle::new(Point::new(0.0, 0.0), Point::new(100.0, 50.0)),
706 Rectangle::new(Point::new(5.0, 5.0), Point::new(6.0, 6.0)),
708 Rectangle::new(Point::new(0.0, 0.0), Point::new(1000.0, 800.0)),
710 Rectangle::new(Point::new(-50.0, -25.0), Point::new(50.0, 25.0)),
712 ];
713
714 for rect in test_rects {
715 let widget = Widget::new(rect);
716 let dict = widget.to_annotation_dict();
717
718 if let Some(Object::Array(rect_array)) = dict.get("Rect") {
719 assert_eq!(rect_array.len(), 4);
720 assert_eq!(rect_array[0], Object::Real(rect.lower_left.x));
721 assert_eq!(rect_array[1], Object::Real(rect.lower_left.y));
722 assert_eq!(rect_array[2], Object::Real(rect.upper_right.x));
723 assert_eq!(rect_array[3], Object::Real(rect.upper_right.y));
724 } else {
725 panic!("Expected Rect array");
726 }
727 }
728 }
729
730 #[test]
731 fn test_border_width_variations() {
732 let rect = Rectangle::new(Point::new(0.0, 0.0), Point::new(100.0, 50.0));
733 let border_widths = [0.0, 0.5, 1.0, 2.5, 10.0];
734
735 for width in border_widths {
736 let appearance = WidgetAppearance {
737 border_color: Some(Color::black()),
738 background_color: None,
739 border_width: width,
740 border_style: BorderStyle::Solid,
741 };
742
743 let widget = Widget::new(rect).with_appearance(appearance);
744 let dict = widget.to_annotation_dict();
745
746 if let Some(Object::Dictionary(bs_dict)) = dict.get("BS") {
747 assert_eq!(bs_dict.get("W"), Some(&Object::Real(width)));
748 } else {
749 panic!("Expected BS dictionary");
750 }
751 }
752 }
753
754 #[test]
755 fn test_quadding_values() {
756 let test_quadding = [
757 (None, "no quadding"),
758 (Some(0), "left alignment"),
759 (Some(1), "center alignment"),
760 (Some(2), "right alignment"),
761 ];
762
763 for (quadding, description) in test_quadding {
764 let options = FieldOptions {
765 flags: FieldFlags::default(),
766 default_appearance: None,
767 quadding,
768 };
769
770 assert_eq!(options.quadding, quadding, "Failed for {description}");
771 }
772 }
773}