use crate::{FormError, Result};
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
pub struct FormFieldDefaults {
#[builder(default = 12.0)]
pub font_size: f64,
#[builder(default = "Helvetica".to_string())]
pub font_name: String,
#[builder(default = 50.0)]
pub min_width: f64,
#[builder(default = 15.0)]
pub min_height: f64,
#[builder(default = "Yes".to_string())]
pub checkbox_export_value: String,
}
impl Default for FormFieldDefaults {
fn default() -> Self {
Self::builder().build()
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Rect {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
#[derive(Debug, Clone, Copy, Default, TypedBuilder)]
#[builder(field_defaults(default))]
pub struct InputBehavior {
pub multiline: bool,
pub password: bool,
}
#[derive(Debug, Clone, Copy, Default, TypedBuilder)]
#[builder(field_defaults(default))]
pub struct FieldConstraints {
pub required: bool,
pub read_only: bool,
}
#[derive(Debug, Clone, Copy, Default, TypedBuilder)]
#[builder(field_defaults(default))]
pub struct TextFieldFlags {
pub behavior: InputBehavior,
pub constraints: FieldConstraints,
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
pub struct TextField {
pub name: String,
pub page: u32,
pub rect: Rect,
#[builder(default)]
pub default_value: Option<String>,
#[builder(default)]
pub max_length: Option<u32>,
#[builder(default)]
pub flags: TextFieldFlags,
#[builder(default = 12.0)]
pub font_size: f64,
#[builder(default = "Helvetica".into())]
pub font_name: String,
}
impl TextField {
#[must_use]
pub fn to_ffi(&self) -> printwell_sys::TextFieldDef {
printwell_sys::TextFieldDef {
name: self.name.clone(),
page: self.page,
x: self.rect.x,
y: self.rect.y,
width: self.rect.width,
height: self.rect.height,
default_value: self.default_value.clone().unwrap_or_default(),
max_length: self.max_length.unwrap_or(0),
multiline: self.flags.behavior.multiline,
password: self.flags.behavior.password,
required: self.flags.constraints.required,
read_only: self.flags.constraints.read_only,
font_size: self.font_size,
font_name: self.font_name.clone(),
}
}
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
pub struct Checkbox {
pub name: String,
pub page: u32,
pub rect: Rect,
#[builder(default = false)]
pub checked: bool,
#[builder(default = "Yes".into())]
pub export_value: String,
}
impl Checkbox {
#[must_use]
pub fn to_ffi(&self) -> printwell_sys::CheckboxDef {
printwell_sys::CheckboxDef {
name: self.name.clone(),
page: self.page,
x: self.rect.x,
y: self.rect.y,
size: self.rect.width.min(self.rect.height), checked: self.checked,
export_value: self.export_value.clone(),
}
}
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
pub struct Dropdown {
pub name: String,
pub page: u32,
pub rect: Rect,
#[builder(default)]
pub options: Vec<String>,
#[builder(default)]
pub selected_index: Option<usize>,
#[builder(default = false)]
pub editable: bool,
}
impl Dropdown {
#[must_use]
pub fn to_ffi(&self) -> printwell_sys::DropdownDef {
printwell_sys::DropdownDef {
name: self.name.clone(),
page: self.page,
x: self.rect.x,
y: self.rect.y,
width: self.rect.width,
height: self.rect.height,
options: self.options.clone(),
selected_index: self
.selected_index
.map_or(-1, |i| i32::try_from(i).unwrap_or(i32::MAX)),
editable: self.editable,
}
}
}
#[derive(Debug, Clone, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
pub struct SignatureField {
pub name: String,
pub page: u32,
pub rect: Rect,
}
impl SignatureField {
#[must_use]
pub fn to_ffi(&self) -> printwell_sys::SignatureFieldDef {
printwell_sys::SignatureFieldDef {
name: self.name.clone(),
page: self.page,
x: self.rect.x,
y: self.rect.y,
width: self.rect.width,
height: self.rect.height,
}
}
}
pub struct FormBuilder {
_private: (),
}
impl FormBuilder {
pub fn new(_pdf_data: &[u8]) -> Result<Self> {
Err(FormError::RequiresLicense.into())
}
pub fn add_text_field(&mut self, _field: TextField) -> Result<&mut Self> {
Err(FormError::RequiresLicense.into())
}
pub fn add_checkbox(&mut self, _field: Checkbox) -> Result<&mut Self> {
Err(FormError::RequiresLicense.into())
}
pub fn add_dropdown(&mut self, _field: Dropdown) -> Result<&mut Self> {
Err(FormError::RequiresLicense.into())
}
pub fn add_signature_field(&mut self, _field: SignatureField) -> Result<&mut Self> {
Err(FormError::RequiresLicense.into())
}
pub fn build(self) -> Result<Vec<u8>> {
Err(FormError::RequiresLicense.into())
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FieldInteractionState {
pub disabled: bool,
pub checked: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FieldConstraintState {
pub required: bool,
pub readonly: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FieldInputState {
pub multiline: bool,
pub password: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FormFieldState {
pub interaction: FieldInteractionState,
pub constraints: FieldConstraintState,
pub input: FieldInputState,
}
#[derive(Debug, Clone, Default)]
pub struct DetectedFormElement {
pub element_type: String,
pub id: String,
pub name: String,
pub page: u32,
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
pub default_value: String,
pub placeholder: String,
pub state: FormFieldState,
pub export_value: String,
pub radio_group: String,
pub options: Vec<String>,
pub selected_index: i32,
pub max_length: u32,
pub font_size: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FormElementType {
#[default]
TextField,
Checkbox,
RadioButton,
Dropdown,
Signature,
}
pub fn apply_detected_forms(
_pdf_data: &[u8],
_elements: &[DetectedFormElement],
) -> Result<Vec<u8>> {
Err(FormError::RequiresLicense.into())
}
pub fn apply_detected_forms_with_defaults(
_pdf_data: &[u8],
_elements: &[DetectedFormElement],
_defaults: &FormFieldDefaults,
) -> Result<Vec<u8>> {
Err(FormError::RequiresLicense.into())
}
#[derive(Debug, Clone, Default)]
pub struct ValidationRule {
pub field_name: String,
pub required: bool,
pub min_value: Option<f64>,
pub max_value: Option<f64>,
pub min_length: Option<usize>,
pub max_length: Option<usize>,
pub pattern: Option<String>,
pub error_message: Option<String>,
pub required_message: Option<String>,
pub pattern_message: Option<String>,
pub length_message: Option<String>,
pub value_message: Option<String>,
pub allowed_values: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub field_name: String,
pub is_valid: bool,
pub errors: Vec<String>,
pub value: String,
}
#[derive(Debug, Clone)]
pub struct ValidationSummary {
pub results: Vec<ValidationResult>,
pub is_valid: bool,
pub error_count: usize,
pub total_fields: usize,
pub valid_count: usize,
pub invalid_count: usize,
pub all_valid: bool,
}
#[must_use]
pub const fn validate_form_fields(
_elements: &[DetectedFormElement],
_rules: &[ValidationRule],
) -> ValidationSummary {
ValidationSummary {
results: vec![],
is_valid: false,
error_count: 1,
total_fields: 0,
valid_count: 0,
invalid_count: 0,
all_valid: false,
}
}
#[must_use]
pub fn validate_field(_element: &DetectedFormElement, _rule: &ValidationRule) -> ValidationResult {
ValidationResult {
field_name: String::new(),
is_valid: false,
errors: vec!["PDF form manipulation requires a commercial license. Purchase at: https://printwell.dev/pricing".to_string()],
value: String::new(),
}
}
pub mod patterns {
pub const EMAIL: &str = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
pub const URL: &str = r"^https?://[^\s/$.?#].[^\s]*$";
pub const PHONE: &str = r"^\+?[0-9\s\-().]{7,}$";
pub const US_PHONE: &str = r"^\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$";
pub const US_ZIP: &str = r"^[0-9]{5}(-[0-9]{4})?$";
pub const POSITIVE_INTEGER: &str = r"^[1-9][0-9]*$";
pub const NON_NEGATIVE_INTEGER: &str = r"^(0|[1-9][0-9]*)$";
pub const DECIMAL: &str = r"^-?[0-9]+(\.[0-9]+)?$";
pub const DATE_ISO: &str = r"^[0-9]{4}-[0-9]{2}-[0-9]{2}$";
pub const DATE_US: &str = r"^[0-9]{2}/[0-9]{2}/[0-9]{4}$";
pub const ALPHA: &str = r"^[a-zA-Z]+$";
pub const ALPHANUMERIC: &str = r"^[a-zA-Z0-9]+$";
pub const NO_WHITESPACE: &str = r"^\S+$";
pub const CREDIT_CARD: &str = r"^[0-9]{13,19}$";
pub const SSN: &str = r"^[0-9]{3}-[0-9]{2}-[0-9]{4}$";
}