1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Error types for the XFA engine.
use thiserror::Error;
/// XfaError.
#[derive(Debug, Error)]
pub enum XfaError {
/// LoadFailed.
// ---- Existing variants (preserved for backwards compatibility) ----
#[error("failed to load PDF: {0}")]
LoadFailed(String),
/// PacketNotFound.
#[error("XFA packet not found: {0}")]
PacketNotFound(String),
/// Encrypted.
#[error("encrypted PDF: {0}")]
Encrypted(String),
/// XmlParse.
#[error("XML parse error: {0}")]
XmlParse(String),
/// FontError.
#[error("font error: {0}")]
FontError(String),
/// LayoutError.
#[error("layout error: {0}")]
LayoutError(String),
/// LayoutFailed.
#[error("layout failed: {0}")]
LayoutFailed(String),
/// ParseFailed.
#[error("XML parse failed: {0}")]
ParseFailed(String),
/// FormCalcError.
#[error("FormCalc error: {0}")]
FormCalcError(String),
/// Io.
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
/// RenderingPolicyUnsupported. **D11.** The requested
/// [`crate::flatten::XfaRenderingPolicy`] is not available in the calling
/// context (e.g. a future/unknown policy, or a command such as `flatten`
/// that applies `SavedStateFaithful` only). Both `SavedStateFaithful` (the
/// default, production policy) and `FreshMergeExperimental` (experimental,
/// opt-in) are implemented as of D12; this variant exists so an unavailable
/// policy fails loudly rather than silently producing default output under
/// the wrong label.
#[error("XFA rendering policy not supported: {0}")]
RenderingPolicyUnsupported(String),
// ---- New structured variants (XFA-F9-01 #1120) ----
/// XFA packet extraction failed (e.g. missing /AcroForm, corrupt stream).
#[error("XFA extraction failed: {0}")]
ExtractionFailed(String),
/// Template XML could not be parsed.
#[error("Template parse error: {0}")]
TemplateParse(String),
/// Data binding from datasets to template failed.
#[error("Data binding failed: {0}")]
BindingFailed(String),
/// LayoutFailedAt.
/// LayoutFailedAt.
/// Layout failed at a specific pipeline stage.
///
/// Use this variant when you can identify which stage (e.g. "paginate",
/// "split", "occur") caused the failure so callers can give better
/// diagnostics.
#[error("Layout failed at {stage}: {reason}")]
LayoutFailedAt {
/// Pipeline stage where layout failed.
stage: String,
/// Reason for the failure.
reason: String,
},
/// PDF content stream generation failed.
#[error("Render failed: {0}")]
RenderFailed(String),
/// Final PDF serialisation / flatten step failed.
#[error("Flatten failed: {0}")]
FlattenFailed(String),
/// Feature is intentionally unsupported by the non-interactive XFA engine.
#[error("unsupported feature: {0}")]
UnsupportedFeature(String),
/// XFA-JS-HOST-STUBS — A script reached a host capability that requires
/// genuine user / viewer interaction (e.g. `xfa.host.messageBox`,
/// `xfa.host.openList`, `xfa.signature.sign`). The flatten pipeline runs
/// non-interactively, so the call cannot be satisfied honestly. Inside the
/// sandbox the call is short-circuited to a safe default and counted in
/// [`crate::DynamicScriptOutcome::js_unsupported_host_calls`]; this error
/// variant exists for synchronous Rust API surfaces that want to surface
/// the gap to embedding callers rather than silently absorb it.
///
/// The `capability` string is a stable, well-known identifier such as
/// `"xfa.host.messageBox"` and is suitable for inclusion in diagnostics.
#[error("unsupported host capability: {capability}")]
UnsupportedHostCapability {
/// Stable identifier of the host capability that was requested.
capability: String,
},
}
/// Result.
pub type Result<T> = std::result::Result<T, XfaError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_message_extraction_failed() {
let e = XfaError::ExtractionFailed("no /AcroForm key".to_string());
assert_eq!(format!("{e}"), "XFA extraction failed: no /AcroForm key");
}
#[test]
fn error_message_unsupported_host_capability() {
let e = XfaError::UnsupportedHostCapability {
capability: "xfa.host.messageBox".to_string(),
};
assert_eq!(
format!("{e}"),
"unsupported host capability: xfa.host.messageBox"
);
}
#[test]
fn error_message_layout_failed_at() {
let e = XfaError::LayoutFailedAt {
stage: "paginate".to_string(),
reason: "zero-height page area".to_string(),
};
assert_eq!(
format!("{e}"),
"Layout failed at paginate: zero-height page area"
);
}
}