xfa_layout_engine/form.rs
1//! Form node types — the input to the layout engine.
2//!
3//! These represent the merged Form DOM nodes that the layout engine processes.
4//! In a full implementation, these would come from xfa-dom-resolver's merge step.
5
6use std::collections::HashMap;
7
8use crate::text::FontMetrics;
9use crate::types::{BoxModel, LayoutStrategy, TextAlign, VerticalAlign};
10
11/// A unique identifier for a form node.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct FormNodeId(pub usize);
14
15/// The form tree: a node-based representation of the merged template+data.
16#[derive(Debug)]
17pub struct FormTree {
18 /// Nodes in the form tree.
19 pub nodes: Vec<FormNode>,
20 /// Per-node metadata (parallel to `nodes`).
21 pub metadata: Vec<FormNodeMeta>,
22 /// Lookup table: XFA `id` attribute -> `FormNodeId`.
23 pub node_ids: HashMap<String, FormNodeId>,
24 /// XFA 3.3 §5.5 `<variables>` `<script name="X">…</script>` blocks
25 /// gathered at merge time. Each entry is `(subform_scope, name, body)`.
26 /// `subform_scope` is `None` for root-level scripts (globally accessible)
27 /// and `Some(subform_name)` for scripts scoped to a named subform
28 /// (accessible as `subform.variables.scriptName`). Empty in default mode.
29 pub variables_scripts: Vec<(Option<String>, String, String)>,
30 /// XFA 3.3 §5.5.2 `<variables>` `<text name="X">value</text>` data items
31 /// gathered at merge time. Each entry is `(subform_scope, name, initial)`.
32 /// `subform_scope` follows the same convention as
33 /// [`Self::variables_scripts`]. Data items are form-level mutable string
34 /// containers — the canonical Canadian IMM template pattern is
35 /// `<variables><text name="globValidatePressed"/></variables>` referenced
36 /// from event scripts as `globValidatePressed.value = "true";`. Empty in
37 /// default mode.
38 ///
39 /// W3-D RETRY: registering these alongside variables-scripts eliminates
40 /// the post-W2-B `implicit_function` residual for the IMM5709/IMM5257/
41 /// IMM5710 family.
42 pub variables_data_items: Vec<(Option<String>, String, String)>,
43}
44
45impl FormTree {
46 /// Create a new form tree.
47 pub fn new() -> Self {
48 Self {
49 nodes: Vec::new(),
50 metadata: Vec::new(),
51 node_ids: HashMap::new(),
52 variables_scripts: Vec::new(),
53 variables_data_items: Vec::new(),
54 }
55 }
56
57 /// Add a node to the form tree.
58 pub fn add_node(&mut self, node: FormNode) -> FormNodeId {
59 let id = FormNodeId(self.nodes.len());
60 self.nodes.push(node);
61 self.metadata.push(FormNodeMeta::default());
62 id
63 }
64
65 /// Add a node together with its metadata. If the meta has an `xfa_id`,
66 /// it is registered in the `node_ids` lookup table.
67 pub fn add_node_with_meta(&mut self, node: FormNode, meta: FormNodeMeta) -> FormNodeId {
68 let id = FormNodeId(self.nodes.len());
69 if let Some(ref xfa_id) = meta.xfa_id {
70 self.node_ids.insert(xfa_id.clone(), id);
71 }
72 self.nodes.push(node);
73 self.metadata.push(meta);
74 id
75 }
76
77 /// Get a node by ID.
78 pub fn get(&self, id: FormNodeId) -> &FormNode {
79 &self.nodes[id.0]
80 }
81
82 /// Get a mutable reference to a node by ID.
83 pub fn get_mut(&mut self, id: FormNodeId) -> &mut FormNode {
84 &mut self.nodes[id.0]
85 }
86
87 /// Access the metadata for a node.
88 pub fn meta(&self, id: FormNodeId) -> &FormNodeMeta {
89 &self.metadata[id.0]
90 }
91
92 /// Mutably access the metadata for a node.
93 pub fn meta_mut(&mut self, id: FormNodeId) -> &mut FormNodeMeta {
94 &mut self.metadata[id.0]
95 }
96
97 /// Look up a node by its XFA `id` attribute.
98 pub fn find_by_xfa_id(&self, id: &str) -> Option<FormNodeId> {
99 self.node_ids.get(id).copied()
100 }
101}
102
103impl Default for FormTree {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109/// A single node in the Form DOM.
110#[derive(Debug, Clone)]
111pub struct FormNode {
112 /// Node name.
113 pub name: String,
114 /// Node type.
115 pub node_type: FormNodeType,
116 /// Box model.
117 pub box_model: BoxModel,
118 /// Layout strategy.
119 pub layout: LayoutStrategy,
120 /// Child node IDs.
121 pub children: Vec<FormNodeId>,
122 /// Occurrence rules for repeating subforms.
123 pub occur: Occur,
124 /// Font metrics for text measurement (Draw/Field nodes).
125 pub font: FontMetrics,
126 /// FormCalc calculate script (XFA S14.3.2): runs to compute the field's value.
127 pub calculate: Option<String>,
128 /// FormCalc validate script: runs to validate the field's value, returns bool.
129 pub validate: Option<String>,
130 /// Column widths for table-layout subforms (XFA columnWidths attribute).
131 /// Positive values are fixed widths in points; -1.0 means auto-size.
132 /// Empty for non-table nodes.
133 pub column_widths: Vec<f64>,
134 /// Column span for cells inside a table row (XFA colSpan attribute).
135 /// 1 = single column (default), N = span N columns, -1 = span remaining.
136 pub col_span: i32,
137}
138
139/// The scripting language used by an XFA `<script>` element.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
141pub enum ScriptLanguage {
142 /// FormCalc is the XFA default when `contentType` is omitted.
143 #[default]
144 FormCalc,
145 /// JavaScript event handlers and calculations.
146 JavaScript,
147 /// Any other declared script language (for example VBScript).
148 Other,
149}
150
151/// Script metadata collected from `<event>` / `<calculate>` elements.
152#[derive(Debug, Clone, PartialEq, Eq, Default)]
153pub struct EventScript {
154 /// Script source.
155 pub script: String,
156 /// Script language.
157 pub language: ScriptLanguage,
158 /// Activity event.
159 pub activity: Option<String>,
160 /// Event reference.
161 pub event_ref: Option<String>,
162 /// Run-at location.
163 pub run_at: Option<String>,
164}
165
166impl EventScript {
167 /// Create a new event script.
168 pub fn new(
169 script: String,
170 language: ScriptLanguage,
171 activity: Option<String>,
172 event_ref: Option<String>,
173 run_at: Option<String>,
174 ) -> Self {
175 Self {
176 script,
177 language,
178 activity,
179 event_ref,
180 run_at,
181 }
182 }
183
184 /// Create a FormCalc event script.
185 pub fn formcalc(script: impl Into<String>, activity: Option<&str>) -> Self {
186 Self::new(
187 script.into(),
188 ScriptLanguage::FormCalc,
189 activity.map(str::to_string),
190 None,
191 None,
192 )
193 }
194
195 /// Create a JavaScript event script.
196 pub fn javascript(script: impl Into<String>, activity: Option<&str>) -> Self {
197 Self::new(
198 script.into(),
199 ScriptLanguage::JavaScript,
200 activity.map(str::to_string),
201 None,
202 None,
203 )
204 }
205}
206
207/// Content for draw nodes (static graphic elements).
208///
209/// XFA Spec 3.3 §2.1 (p24) — Draw element: fixed content (boilerplate).
210/// Includes text, lines, rectangles, arcs. Images are handled separately
211/// via `FormNodeType::Image`.
212///
213/// TODO(§2.3): `circle` draw content not implemented (spec allows via arc with
214/// startAngle=0, sweepAngle=360).
215#[derive(Debug, Clone)]
216pub enum DrawContent {
217 /// Text draw content.
218 Text(String),
219 /// Line draw content.
220 Line {
221 /// Start x coordinate.
222 x1: f64,
223 /// Start y coordinate.
224 y1: f64,
225 /// End x coordinate.
226 x2: f64,
227 /// End y coordinate.
228 y2: f64,
229 },
230 /// Rectangle draw content.
231 Rectangle {
232 /// X coordinate.
233 x: f64,
234 /// Y coordinate.
235 y: f64,
236 /// Width.
237 w: f64,
238 /// Height.
239 h: f64,
240 /// Corner radius.
241 radius: f64,
242 },
243 /// Arc draw content.
244 Arc {
245 /// X coordinate.
246 x: f64,
247 /// Y coordinate.
248 y: f64,
249 /// Width.
250 w: f64,
251 /// Height.
252 h: f64,
253 /// Start angle.
254 start_angle: f64,
255 /// Sweep angle.
256 sweep_angle: f64,
257 },
258}
259
260/// The type of form node.
261#[derive(Debug, Clone)]
262pub enum FormNodeType {
263 /// Root subform.
264 Root,
265 /// A page set containing page areas.
266 PageSet,
267 /// A page area (page template) with content areas.
268 PageArea {
269 /// Content areas.
270 content_areas: Vec<ContentArea>,
271 },
272 /// A generic subform container.
273 Subform,
274 /// XFA `<area>` — a positioned container (XFA 3.3 Appendix B).
275 ///
276 /// Semantically identical to a `Subform` with positioned layout: children
277 /// have absolute positions within the area. The layout engine treats this
278 /// exactly like `Subform` for layout purposes.
279 Area,
280 /// XFA `<exclGroup>` — an exclusive (radio-button) group (XFA 3.3 §7.2).
281 ///
282 /// Contains multiple radio-button `<field>` children where exactly one can
283 /// be selected. In layout this behaves like a `Subform` with top-to-bottom
284 /// flow; each child field is rendered normally.
285 ExclGroup,
286 /// XFA `<subformSet>` — a transparent set of subforms (XFA 3.3 §7.1).
287 ///
288 /// Used for conditional instantiation. In layout the set is transparent:
289 /// its children are processed as if they were direct children of the
290 /// containing subform (same data context).
291 SubformSet,
292 /// A form field (text field, checkbox, etc.).
293 Field {
294 /// Field value.
295 value: String,
296 },
297 /// A static draw element (text, image, line, etc.).
298 Draw(DrawContent),
299 /// A static image draw element.
300 Image {
301 /// Image data.
302 data: Vec<u8>,
303 /// Image MIME type.
304 mime_type: String,
305 },
306}
307
308/// Occurrence rules for repeating subforms (XFA S3.3 occur element).
309///
310/// Controls how many instances of a subform are created. The layout engine
311/// expands templates based on the `initial` count, bounded by `min` and `max`.
312#[derive(Debug, Clone)]
313pub struct Occur {
314 /// Minimum number of occurrences (default 1).
315 pub min: u32,
316 /// Maximum number of occurrences (-1 = unlimited). Default 1.
317 /// Using `Option<u32>` where `None` means unlimited.
318 pub max: Option<u32>,
319 /// Initial number of occurrences (default = min).
320 pub initial: u32,
321}
322
323impl Default for Occur {
324 fn default() -> Self {
325 Self {
326 min: 1,
327 max: Some(1),
328 initial: 1,
329 }
330 }
331}
332
333impl Occur {
334 /// Occur rule that means "exactly once" (the default).
335 pub fn once() -> Self {
336 Self::default()
337 }
338
339 /// Occur rule for a repeating subform.
340 pub fn repeating(min: u32, max: Option<u32>, initial: u32) -> Self {
341 let initial = initial.max(min);
342 let initial = match max {
343 Some(m) => initial.min(m),
344 None => initial,
345 };
346 Self { min, max, initial }
347 }
348
349 /// How many instances should be created.
350 pub fn count(&self) -> u32 {
351 self.initial
352 }
353
354 /// Whether the subform can repeat (max > 1 or unlimited).
355 pub fn is_repeating(&self) -> bool {
356 match self.max {
357 Some(m) => m > 1,
358 None => true,
359 }
360 }
361}
362
363/// A content area within a page area.
364#[derive(Debug, Clone)]
365pub struct ContentArea {
366 /// Content area name.
367 pub name: String,
368 /// X coordinate.
369 pub x: f64,
370 /// Y coordinate.
371 pub y: f64,
372 /// Width.
373 pub width: f64,
374 /// Height.
375 pub height: f64,
376 /// Leader (header) node placed at the top of each page's content area.
377 pub leader: Option<FormNodeId>,
378 /// Trailer (footer) node placed at the bottom of each page's content area.
379 pub trailer: Option<FormNodeId>,
380}
381
382impl Default for ContentArea {
383 fn default() -> Self {
384 Self {
385 name: String::new(),
386 x: 0.0,
387 y: 0.0,
388 width: 612.0, // US Letter width in points
389 height: 792.0, // US Letter height in points
390 leader: None,
391 trailer: None,
392 }
393 }
394}
395
396// ---------------------------------------------------------------------------
397// Metadata, style, and kind types
398// ---------------------------------------------------------------------------
399
400/// XFA `presence` attribute values (XFA 3.3 §2.6 p67-68).
401///
402/// Controls visibility and layout space allocation:
403/// - `Visible` -- all phases: binding, automation, layout, rendering, interaction.
404/// - `Hidden` -- binding + automation only; no layout space, no rendering.
405/// - `Invisible` -- binding + automation + layout; takes up space but not visible.
406/// - `Inactive` -- binding only; completely absent from form.
407///
408/// Note: spec defines `hidden` as "effectively absent" (no space) and `invisible`
409/// as "takes space but not visible". Our `is_layout_hidden` implementation reflects this.
410#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
411pub enum Presence {
412 #[default]
413 /// Visible.
414 Visible,
415 /// Hidden.
416 Hidden,
417 /// Invisible.
418 Invisible,
419 /// Inactive.
420 Inactive,
421}
422
423impl Presence {
424 /// True when the element should not be rendered.
425 pub fn is_not_visible(self) -> bool {
426 !matches!(self, Presence::Visible)
427 }
428
429 /// True when the element should not occupy layout space.
430 ///
431 /// XFA Spec 3.3 §2.6 (p68):
432 /// - `hidden`: no layout space, no rendering (effectively absent)
433 /// - `invisible`: no layout space in Adobe (spec says "takes space",
434 /// but empirical testing shows Adobe skips it)
435 /// - `inactive`: completely absent (no binding, no space)
436 ///
437 /// `Hidden` was previously excluded from this predicate based on an
438 /// incorrect assumption that Adobe reserves space for hidden elements.
439 /// GATE #27 testing proved this wrong: hidden subforms with `<break>`
440 /// elements caused 2-23x overpagination in forms with many
441 /// `presence="hidden"` subforms (fixes #806).
442 pub fn is_layout_hidden(self) -> bool {
443 matches!(
444 self,
445 Presence::Hidden | Presence::Invisible | Presence::Inactive
446 )
447 }
448}
449
450/// Extended metadata for a form node.
451///
452/// Carries XFA attributes that the layout engine and dynamic scripting
453/// system need but that are not part of the core `FormNode` shape.
454#[derive(Debug, Clone, Default)]
455pub struct FormNodeMeta {
456 /// Optional XFA `id` attribute.
457 pub xfa_id: Option<String>,
458 /// XFA presence attribute (visible/hidden/invisible/inactive).
459 pub presence: Presence,
460 /// Whether a page break should be inserted before this node.
461 pub page_break_before: bool,
462 /// Whether a page break should be inserted after this node.
463 pub page_break_after: bool,
464 /// Target page area name/id for the break (e.g. "MP3", "Page4_ID").
465 pub break_target: Option<String>,
466 /// Whether this node targets a specific content area via
467 /// `breakBefore targetType="contentArea"`. Such nodes should be
468 /// excluded from the primary content flow (they go into small
469 /// decorative areas like "flatten" or "eSign").
470 pub content_area_break: bool,
471 /// Overflow leader reference name.
472 pub overflow_leader: Option<String>,
473 /// Overflow trailer reference name.
474 pub overflow_trailer: Option<String>,
475 /// Keep with next content area.
476 pub keep_next_content_area: bool,
477 /// Keep with previous content area.
478 pub keep_previous_content_area: bool,
479 /// Keep intact within content area.
480 pub keep_intact_content_area: bool,
481 /// Layout-ready script (XFA S14.3).
482 pub layout_ready_script: Option<String>,
483 /// Event scripts collected from `<event>` and `<calculate>` children.
484 pub event_scripts: Vec<EventScript>,
485 /// Explicit XFA data binding ref from `<bind ref="...">`.
486 pub data_bind_ref: Option<String>,
487 /// Whether the node explicitly opts out of data binding via `<bind match="none">`.
488 pub data_bind_none: bool,
489 /// Visual style (font, colors, borders).
490 pub style: FormNodeStyle,
491 /// The kind of field (text, checkbox, radio, etc.).
492 pub field_kind: FieldKind,
493 /// The kind of group (none or exclusive choice).
494 pub group_kind: GroupKind,
495 /// Item value for fields inside an exclGroup.
496 pub item_value: Option<String>,
497 /// Check box / radio button size in points.
498 pub check_size: Option<f64>,
499 /// Display items for choice list fields (XFA 3.3 §7.7).
500 pub display_items: Vec<String>,
501 /// Save items for choice list fields (XFA 3.3 §7.7).
502 pub save_items: Vec<String>,
503 /// Runtime-populated choice list items from sandboxed script `addItem` calls.
504 /// Each tuple is `(display_value, save_value)`. Phase D-β only; layout does
505 /// not yet read this in default mode.
506 pub runtime_listbox_items: Vec<(String, String)>,
507 /// XFA anchorType for positioned layout (XFA 3.3 §2.6, App A p1510).
508 pub anchor_type: AnchorType,
509 /// Raw `DataDom` arena index of the data node this form node was bound to
510 /// during the merge phase. `None` means unbound. Phase D-γ: populated by
511 /// `FormMerger` and consumed by the JS runtime to resolve `$record`.
512 pub bound_data_node: Option<usize>,
513 /// True when this node is a synthetic prototype for a repeating subform
514 /// whose live instance count is currently zero. Used by the JS runtime
515 /// to expose an InstanceManager via `parent._child` even when no data
516 /// bindings produced live rows. Layout treats this as `presence = Hidden`.
517 pub is_zero_instance_prototype: bool,
518 /// True when this pageArea was allocated by the XFA runtime (recorded in
519 /// the form-DOM packet of an Adobe-Reader-saved PDF) rather than declared
520 /// once in the template.
521 ///
522 /// XFA 3.3 §8.6 / §3.1: the form DOM enumerates the runtime page-tree
523 /// state. When the form DOM lists more `<pageArea>` siblings than the
524 /// template defines, those extra instances were created by the
525 /// `pageSet`/`occur` machinery and each one must emit a layout page even
526 /// when the flowing-body queue is exhausted (it is the runtime's record
527 /// of an already-paginated page).
528 pub runtime_instantiated_page: bool,
529}
530
531/// XFA `anchorType` attribute (XFA 3.3 §2.6, Appendix A p1510).
532///
533/// Determines which point of an element is placed at the (x,y) coordinate
534/// in positioned layout. Default is `TopLeft`.
535#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
536pub enum AnchorType {
537 #[default]
538 /// Top-left anchor.
539 TopLeft,
540 /// Top-center anchor.
541 TopCenter,
542 /// Top-right anchor.
543 TopRight,
544 /// Middle-left anchor.
545 MiddleLeft,
546 /// Middle-center anchor.
547 MiddleCenter,
548 /// Middle-right anchor.
549 MiddleRight,
550 /// Bottom-left anchor.
551 BottomLeft,
552 /// Bottom-center anchor.
553 BottomCenter,
554 /// Bottom-right anchor.
555 BottomRight,
556}
557
558/// The kind of group container.
559#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
560pub enum GroupKind {
561 #[default]
562 /// No group.
563 None,
564 /// Exclusive choice group.
565 ExclusiveChoice,
566}
567
568/// The kind of form field.
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
570pub enum FieldKind {
571 #[default]
572 /// Text field.
573 Text,
574 /// Checkbox field.
575 Checkbox,
576 /// Radio button field.
577 Radio,
578 /// Button field.
579 Button,
580 /// Dropdown field.
581 Dropdown,
582 /// Signature field.
583 Signature,
584 /// Date/time picker field.
585 DateTimePicker,
586 /// Numeric edit field.
587 NumericEdit,
588 /// Password edit field.
589 PasswordEdit,
590 /// Image edit field.
591 ImageEdit,
592 /// Barcode field.
593 Barcode,
594}
595
596/// A span of rich text with per-span style overrides.
597///
598/// XFA Spec 3.3 §4.2.7 (p155) — `<exData contentType="text/html">` stores
599/// XHTML content with inline CSS. Each span carries its own formatting
600/// (font, color, weight, etc.) that overrides the node-level defaults.
601#[derive(Debug, Clone, PartialEq)]
602pub struct RichTextSpan {
603 /// Text content.
604 pub text: String,
605 /// Font size.
606 pub font_size: Option<f64>,
607 /// Font family.
608 pub font_family: Option<String>,
609 /// Font weight.
610 pub font_weight: Option<String>,
611 /// Font style.
612 pub font_style: Option<String>,
613 /// Text color.
614 pub text_color: Option<(u8, u8, u8)>,
615 /// Underline flag.
616 pub underline: bool,
617 /// Line-through flag.
618 pub line_through: bool,
619}
620
621/// Visual style properties for a form node.
622#[derive(Debug, Clone, PartialEq)]
623pub struct FormNodeStyle {
624 /// Font family.
625 pub font_family: Option<String>,
626 /// Font size.
627 pub font_size: Option<f64>,
628 /// Font weight.
629 pub font_weight: Option<String>,
630 /// Font style.
631 pub font_style: Option<String>,
632 /// Text color.
633 pub text_color: Option<(u8, u8, u8)>,
634 /// Background color.
635 pub bg_color: Option<(u8, u8, u8)>,
636 /// Border color.
637 pub border_color: Option<(u8, u8, u8)>,
638 /// Per-edge border colors (top, right, bottom, left) in RGB 0-255.
639 /// When set, overrides `border_color` for individual edges.
640 pub border_colors: Option<[(u8, u8, u8); 4]>,
641 /// Border width in points.
642 pub border_width_pt: Option<f64>,
643 /// Per-edge border widths (top, right, bottom, left) in points.
644 /// When set, overrides `border_width_pt` for individual edges.
645 pub border_widths: Option<[f64; 4]>,
646 /// Paragraph space above in points (XFA `<para spaceAbove>`).
647 pub space_above_pt: Option<f64>,
648 /// Paragraph space below in points (XFA `<para spaceBelow>`).
649 pub space_below_pt: Option<f64>,
650 /// Paragraph left margin in points (XFA `<para marginLeft>`).
651 pub margin_left_pt: Option<f64>,
652 /// Paragraph right margin in points (XFA `<para marginRight>`).
653 pub margin_right_pt: Option<f64>,
654 /// XFA Spec 3.3 §17 "para" (p803) — lineHeight: baseline-to-baseline
655 /// distance in points. When `None`, use font metrics.
656 pub line_height_pt: Option<f64>,
657 /// XFA Spec 3.3 §17 "para" (p803) — textIndent: indentation of the first
658 /// line of each paragraph in points.
659 pub text_indent_pt: Option<f64>,
660 /// Margin top inset in points (XFA `<margin topInset>`).
661 pub inset_top_pt: Option<f64>,
662 /// Margin bottom inset in points (XFA `<margin bottomInset>`).
663 pub inset_bottom_pt: Option<f64>,
664 /// Margin left inset in points (XFA `<margin leftInset>`).
665 pub inset_left_pt: Option<f64>,
666 /// Margin right inset in points (XFA `<margin rightInset>`).
667 pub inset_right_pt: Option<f64>,
668 /// Vertical text alignment (XFA `<para vAlign>`).
669 pub v_align: Option<VerticalAlign>,
670 /// Horizontal alignment for layout positioning (XFA `<para hAlign>`, §8.3).
671 pub h_align: Option<TextAlign>,
672 /// Border corner radius in points (XFA `<border><corner radius>`).
673 pub border_radius_pt: Option<f64>,
674 /// Border edge stroke style (XFA `<border><edge stroke>`).
675 pub border_style: Option<String>,
676 /// Per-edge visibility: [top, right, bottom, left]. All true when absent.
677 pub border_edges: [bool; 4],
678 /// XFA Spec 3.3 §17 (p716) — genericFamily fallback hint.
679 /// Values: serif, sansSerif, monospaced, decorative, fantasy, cursive.
680 pub generic_family: Option<String>,
681 /// Font horizontal scale factor (XFA `<font fontHorizontalScale>`).
682 /// 1.0 = 100% (default), 0.96 = 96%, etc.
683 pub font_horizontal_scale: Option<f64>,
684 /// Letter spacing in points (XFA `<font letterSpacing>`).
685 /// 0.0 = normal (default). Negative values tighten, positive widen.
686 pub letter_spacing_pt: Option<f64>,
687 /// Caption text (XFA `<caption><value><text>`).
688 pub caption_text: Option<String>,
689 /// Caption placement (left/right/top/bottom/inline).
690 pub caption_placement: Option<String>,
691 /// Caption reserve width/height in points.
692 pub caption_reserve: Option<f64>,
693 /// CheckButton mark style (XFA `<checkButton mark="...">`).
694 /// Values: "check", "circle", "cross", "diamond", "square", "star".
695 pub check_button_mark: Option<String>,
696 /// CheckButton on-value (first `<items>` entry, XFA 3.3 §17.8).
697 pub check_button_on_value: Option<String>,
698 /// CheckButton off-value (second `<items>` entry). When omitted, the
699 /// spec default is the null string.
700 pub check_button_off_value: Option<String>,
701 /// CheckButton neutral-value (third `<items>` entry, checkbox only).
702 pub check_button_neutral_value: Option<String>,
703 /// Rich text spans parsed from `<exData contentType="text/html">` XHTML.
704 pub rich_text_spans: Option<Vec<RichTextSpan>>,
705 /// Font underline (XFA `<font underline="1">`).
706 pub underline: bool,
707 /// Font line-through / strikethrough (XFA `<font lineThrough="1">`).
708 pub line_through: bool,
709 /// XFA format picture clause (e.g. `num{z,zzz.99}`).
710 pub format_pattern: Option<String>,
711}
712
713impl Default for FormNodeStyle {
714 fn default() -> Self {
715 Self {
716 font_family: None,
717 font_size: None,
718 font_weight: None,
719 font_style: None,
720 text_color: None,
721 bg_color: None,
722 border_color: None,
723 border_colors: None,
724 border_width_pt: None,
725 border_widths: None,
726 space_above_pt: None,
727 space_below_pt: None,
728 margin_left_pt: None,
729 margin_right_pt: None,
730 line_height_pt: None,
731 text_indent_pt: None,
732 inset_top_pt: None,
733 inset_bottom_pt: None,
734 inset_left_pt: None,
735 inset_right_pt: None,
736 v_align: None,
737 h_align: None,
738 border_radius_pt: None,
739 border_style: None,
740 border_edges: [true, true, true, true],
741 generic_family: None,
742 font_horizontal_scale: None,
743 letter_spacing_pt: None,
744 caption_text: None,
745 caption_placement: None,
746 caption_reserve: None,
747 check_button_mark: None,
748 check_button_on_value: None,
749 check_button_off_value: None,
750 check_button_neutral_value: None,
751 rich_text_spans: None,
752 underline: false,
753 line_through: false,
754 format_pattern: None,
755 }
756 }
757}