//! Prepare a form: create, change and delete fields (execution plan M6.6). //! //! New fields follow Acrobat's conventions: names "Text1", "Check Box1", "Group1" (radio groups), //! "Dropdown1", "List Box1", "Button1", "Signature1", "Date1"; Helvetica at Acrobat's Auto size //! for text and date fields (12 pt in a box that has room, smaller in a short one), 12 pt in //! choice fields, a thin grey border and a white background, and appearance streams generated //! immediately so every viewer shows the empty field. use pdfcraft_cos::{Dict, Document, ObjRef, Object, PdfString, Stream}; use crate::{Field, FieldKind, FormError, Widget, appearance, fields, flags, page_refs}; mod edit; mod lifecycle; mod look; mod tree; pub use edit::set_props; pub use lifecycle::{add_field, delete_field, delete_field_object, duplicate_field}; pub(crate) use look::button_appearance; pub use look::{BorderStyle, CheckStyle, FieldFont, Look, LookPatch, check_style, look, redraw_field, set_button_icon}; pub(crate) use tree::{ensure_form, form_fonts_as_objects}; /// The smallest width and height of a field's widget, in points: a smaller one is refused. pub const MIN_FIELD_SIDE: f64 = 4.0; /// The kind of field to add. #[derive(Clone, Debug, PartialEq)] pub enum NewField { Text { multiline: bool, }, /// A date field: a text field with Acrobat's date format action (`AFDate_FormatEx`). Date, CheckBox, /// One radio button in `group` (a new group is created when none of that name exists), /// selected by `export`. Radio { group: Option, export: String, }, Combo { options: Vec, editable: bool, }, List { options: Vec, multi: bool, }, Button { caption: String, }, /// An image field: an icon-only push button that asks for a picture when clicked. Image, Signature, } impl NewField { fn base_name(&self) -> &'static str { match self { NewField::Text { .. } => "Text", NewField::Date => "Date", NewField::CheckBox => "Check Box", NewField::Radio { .. } => "Group", NewField::Combo { .. } => "Dropdown", NewField::List { .. } => "List Box", NewField::Button { .. } => "Button", NewField::Image => "Image", NewField::Signature => "Signature", } } } /// Field properties the Properties dialog edits (`None` leaves a value unchanged). #[derive(Clone, Debug, Default, PartialEq)] pub struct FieldProps { pub name: Option, pub tooltip: Option, /// Normal push-button label (`/MK /CA`) of one widget (0-based index). /// An empty string clears the label; other widgets and icon layouts are preserved. pub caption: Option<(usize, String)>, pub read_only: Option, pub required: Option, pub multiline: Option, pub max_len: Option>, /// Choice options (display texts; export values equal them). pub options: Option>, /// Font size (0 = auto). pub font_size: Option, /// Position: move or resize one widget (its index in [`Field::widgets`]) to a new rect in /// user space. pub rect: Option<(usize, [f64; 4])>, /// Appearance tab (border, fill, line, text colour, font). pub look: Option, /// Change only these appearance properties. Unlike `look`, this preserves each widget's /// other colours, border settings and default appearance, including custom font resources. pub appearance: Option, /// Format, Validate and Calculate tabs (written as Acrobat's AF scripts). pub format: Option, pub validate: Option, pub calculate: Option, /// Options-tab flags (`/Ff` bits from [`crate::flags`]) to set or clear. pub flags: Vec<(u32, bool)>, /// Alignment (`/Q`): 0 left, 1 centre, 2 right. pub quadding: Option, /// Default value (`/DV`): what Reset form restores. pub default_value: Option>, /// Lock or unlock the field (its widgets' Locked flag). pub locked: Option, /// Actions tab: every trigger's action (replacing the field's). pub actions: Option>, /// Options tab: the mark of a check box or radio button (every widget). pub check_style: Option, /// Position tab: rotate one widget (`/MK /R`) to 0, 90, 180 or 270 degrees counterclockwise. /// Changing between horizontal and vertical swaps that widget's width and height around its /// center, unless [`Self::rect`] is also set. pub rotation: Option<(usize, i64)>, } fn invalid(m: impl Into) -> Result { Err(FormError::Invalid(m.into())) } fn rgb(c: [f64; 3]) -> Object { Object::Array(c.iter().map(|v| Object::Real(*v)).collect()) }