use crate::flags::FieldFlags;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FieldId(pub(crate) usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldType {
Text,
Button,
Choice,
Signature,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Quadding {
#[default]
Left,
Center,
Right,
}
#[derive(Debug, Clone, PartialEq)]
pub enum FieldValue {
Text(String),
StringArray(Vec<String>),
}
#[derive(Debug, Clone)]
pub struct ChoiceOption {
pub export: String,
pub display: String,
}
#[derive(Debug, Clone, Default)]
pub struct MkDict {
pub border_color: Option<Vec<f32>>,
pub background_color: Option<Vec<f32>>,
pub caption: Option<String>,
pub rollover_caption: Option<String>,
pub alternate_caption: Option<String>,
pub text_position: Option<u32>,
pub rotation: Option<u32>,
}
#[derive(Debug, Clone)]
pub struct BorderStyle {
pub width: f32,
pub style: u8,
}
impl Default for BorderStyle {
fn default() -> Self {
Self {
width: 1.0,
style: b'S',
}
}
}
#[derive(Debug, Clone)]
pub struct FieldNode {
pub partial_name: String,
pub alternate_name: Option<String>,
pub mapping_name: Option<String>,
pub field_type: Option<FieldType>,
pub flags: FieldFlags,
pub value: Option<FieldValue>,
pub default_value: Option<FieldValue>,
pub default_appearance: Option<String>,
pub quadding: Option<Quadding>,
pub max_len: Option<u32>,
pub options: Vec<ChoiceOption>,
pub top_index: Option<u32>,
pub rect: Option<[f32; 4]>,
pub appearance_state: Option<String>,
pub page_index: Option<usize>,
pub parent: Option<FieldId>,
pub children: Vec<FieldId>,
pub object_id: Option<(i32, i32)>,
pub has_actions: bool,
pub mk: Option<MkDict>,
pub border_style: Option<BorderStyle>,
}
#[derive(Debug)]
pub struct FieldTree {
nodes: Vec<FieldNode>,
pub calculation_order: Vec<FieldId>,
pub document_da: Option<String>,
pub document_quadding: Option<Quadding>,
pub need_appearances: bool,
pub sig_flags: u32,
}
impl FieldTree {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
calculation_order: Vec::new(),
document_da: None,
document_quadding: None,
need_appearances: false,
sig_flags: 0,
}
}
pub fn alloc(&mut self, node: FieldNode) -> FieldId {
let id = FieldId(self.nodes.len());
self.nodes.push(node);
id
}
pub fn get(&self, id: FieldId) -> &FieldNode {
&self.nodes[id.0]
}
pub fn get_mut(&mut self, id: FieldId) -> &mut FieldNode {
&mut self.nodes[id.0]
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn roots(&self) -> Vec<FieldId> {
self.nodes
.iter()
.enumerate()
.filter(|(_, n)| n.parent.is_none())
.map(|(i, _)| FieldId(i))
.collect()
}
pub fn terminal_fields(&self) -> Vec<FieldId> {
self.nodes
.iter()
.enumerate()
.filter(|(_, n)| n.children.is_empty())
.map(|(i, _)| FieldId(i))
.collect()
}
pub fn fully_qualified_name(&self, id: FieldId) -> String {
let mut parts = Vec::new();
let mut cur = Some(id);
while let Some(cid) = cur {
let node = self.get(cid);
if !node.partial_name.is_empty() {
parts.push(node.partial_name.as_str());
}
cur = node.parent;
}
parts.reverse();
parts.join(".")
}
pub fn effective_field_type(&self, id: FieldId) -> Option<FieldType> {
let mut cur = Some(id);
while let Some(cid) = cur {
if let Some(ft) = self.get(cid).field_type {
return Some(ft);
}
cur = self.get(cid).parent;
}
None
}
pub fn effective_value(&self, id: FieldId) -> Option<&FieldValue> {
let mut cur = Some(id);
while let Some(cid) = cur {
let node = self.get(cid);
if node.value.is_some() {
return node.value.as_ref();
}
cur = node.parent;
}
None
}
pub fn effective_da(&self, id: FieldId) -> Option<&str> {
let mut cur = Some(id);
while let Some(cid) = cur {
if let Some(ref da) = self.get(cid).default_appearance {
return Some(da.as_str());
}
cur = self.get(cid).parent;
}
self.document_da.as_deref()
}
pub fn effective_quadding(&self, id: FieldId) -> Quadding {
let mut cur = Some(id);
while let Some(cid) = cur {
if let Some(q) = self.get(cid).quadding {
return q;
}
cur = self.get(cid).parent;
}
self.document_quadding.unwrap_or_default()
}
pub fn effective_flags(&self, id: FieldId) -> FieldFlags {
self.get(id).flags
}
pub fn effective_max_len(&self, id: FieldId) -> Option<u32> {
let mut cur = Some(id);
while let Some(cid) = cur {
if let Some(ml) = self.get(cid).max_len {
return Some(ml);
}
cur = self.get(cid).parent;
}
None
}
pub fn find_by_name(&self, name: &str) -> Option<FieldId> {
self.terminal_fields()
.into_iter()
.find(|&id| self.fully_qualified_name(id) == name)
}
pub fn all_ids(&self) -> impl Iterator<Item = FieldId> {
(0..self.nodes.len()).map(FieldId)
}
}
impl Default for FieldTree {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_node(name: &str) -> FieldNode {
FieldNode {
partial_name: name.into(),
alternate_name: None,
mapping_name: None,
field_type: None,
flags: FieldFlags::empty(),
value: None,
default_value: None,
default_appearance: None,
quadding: None,
max_len: None,
options: vec![],
top_index: None,
rect: None,
appearance_state: None,
page_index: None,
parent: None,
children: vec![],
object_id: None,
has_actions: false,
mk: None,
border_style: None,
}
}
#[test]
fn fqn_simple() {
let mut tree = FieldTree::new();
let root = tree.alloc(make_node("form"));
let mut child = make_node("name");
child.parent = Some(root);
child.field_type = Some(FieldType::Text);
let child_id = tree.alloc(child);
tree.get_mut(root).children.push(child_id);
assert_eq!(tree.fully_qualified_name(child_id), "form.name");
}
#[test]
fn inherited_field_type() {
let mut tree = FieldTree::new();
let mut parent = make_node("group");
parent.field_type = Some(FieldType::Button);
let parent_id = tree.alloc(parent);
let mut child = make_node("opt1");
child.parent = Some(parent_id);
let child_id = tree.alloc(child);
tree.get_mut(parent_id).children.push(child_id);
assert_eq!(tree.effective_field_type(child_id), Some(FieldType::Button));
}
#[test]
fn inherited_da() {
let mut tree = FieldTree::new();
tree.document_da = Some("0 g /Helv 12 Tf".into());
let id = tree.alloc(make_node("field"));
assert_eq!(tree.effective_da(id), Some("0 g /Helv 12 Tf"));
}
#[test]
fn inherited_max_len() {
let mut tree = FieldTree::new();
let mut parent = make_node("group");
parent.max_len = Some(10);
let parent_id = tree.alloc(parent);
let mut child = make_node("field");
child.parent = Some(parent_id);
let child_id = tree.alloc(child);
tree.get_mut(parent_id).children.push(child_id);
assert_eq!(tree.effective_max_len(child_id), Some(10));
}
#[test]
fn own_max_len_overrides_parent() {
let mut tree = FieldTree::new();
let mut parent = make_node("group");
parent.max_len = Some(10);
let parent_id = tree.alloc(parent);
let mut child = make_node("field");
child.parent = Some(parent_id);
child.max_len = Some(5);
let child_id = tree.alloc(child);
tree.get_mut(parent_id).children.push(child_id);
assert_eq!(tree.effective_max_len(child_id), Some(5));
}
}