mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-12 18:14:59 +00:00
M12: form_focus refuses a field with no widget instead of reporting a focus change that ran nothing
This commit is contained in:
@@ -769,12 +769,13 @@ impl Automation {
|
||||
let name = a.str("field")?;
|
||||
let doc = self.doc(a)?;
|
||||
let field = doc.form.iter().find(|f| f.name == name).ok_or_else(|| failed(format!("there is no field named {name:?} (see form_fields)")))?;
|
||||
// A field with no widget (one only scripts or calculations use) has nothing to focus.
|
||||
let widgets = field.widgets.len();
|
||||
let widget = a.opt_int("widget")?.unwrap_or(1);
|
||||
let widget = usize::try_from(widget)
|
||||
.ok()
|
||||
.and_then(|w| w.checked_sub(1))
|
||||
.filter(|w| *w < field.widgets.len().max(1))
|
||||
.ok_or_else(|| ToolError::InvalidArgs(format!("widget must be 1 to {} for {name:?}", field.widgets.len().max(1))))?;
|
||||
let widget = usize::try_from(widget).ok().and_then(|w| w.checked_sub(1)).filter(|w| *w < widgets).ok_or_else(|| match widgets {
|
||||
0 => ToolError::InvalidArgs(format!("{name:?} has no widget to focus")),
|
||||
n => ToolError::InvalidArgs(format!("widget must be 1 to {n} for {name:?}")),
|
||||
})?;
|
||||
let id = doc.id;
|
||||
let changed = self.session.field_focus(id, name, widget, focus).map_err(failed)?;
|
||||
let out = self.session.take_js_output(id);
|
||||
|
||||
@@ -7387,6 +7387,46 @@ fn xfa_focus_scripts_run_through_form_focus() {
|
||||
}
|
||||
}
|
||||
|
||||
/// form_focus refuses a field with no widget, which nothing can focus, instead of reporting a
|
||||
/// focus change that ran nothing.
|
||||
#[test]
|
||||
fn form_focus_refuses_a_field_with_no_widget() {
|
||||
let dir = workdir("focus-no-widget");
|
||||
let objs = [
|
||||
"<< /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [4 0 R 5 0 R] >> >>",
|
||||
"<< /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 300 400] >>",
|
||||
"<< /Type /Page /Parent 2 0 R /Annots [4 0 R] >>",
|
||||
"<< /Type /Annot /Subtype /Widget /FT /Tx /T (name) /Rect [60 60 200 80] /P 3 0 R >>",
|
||||
"<< /FT /Tx /T (total) /V (3) >>",
|
||||
];
|
||||
let mut bytes = b"%PDF-1.7\n".to_vec();
|
||||
let mut offsets = Vec::new();
|
||||
for (i, o) in objs.iter().enumerate() {
|
||||
offsets.push(bytes.len());
|
||||
bytes.extend_from_slice(format!("{} 0 obj\n{o}\nendobj\n", i + 1).as_bytes());
|
||||
}
|
||||
let xref = bytes.len();
|
||||
bytes.extend_from_slice(format!("xref\n0 {}\n0000000000 65535 f \n", objs.len() + 1).as_bytes());
|
||||
for o in offsets {
|
||||
bytes.extend_from_slice(format!("{o:010} 00000 n \n").as_bytes());
|
||||
}
|
||||
bytes.extend_from_slice(format!("trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n", objs.len() + 1).as_bytes());
|
||||
std::fs::write(dir.join("total.pdf"), bytes).unwrap();
|
||||
let mut a = auto(&dir);
|
||||
let doc = ok(&mut a, "doc_open", json!({ "path": "total.pdf" }))["doc"].as_u64().unwrap();
|
||||
let fields = ok(&mut a, "form_fields", json!({ "doc": doc }));
|
||||
assert!(fields["fields"].as_array().unwrap().iter().any(|f| f["name"] == "total"), "the field without a widget is listed: {fields}");
|
||||
for args in [json!({ "doc": doc, "field": "total", "event": "enter" }), json!({ "doc": doc, "field": "total", "event": "enter", "widget": 1 })] {
|
||||
let e = err(&mut a, "form_focus", args.clone()).to_string();
|
||||
assert!(e.contains("\"total\" has no widget to focus"), "{args}: {e}");
|
||||
}
|
||||
assert_eq!(
|
||||
ok(&mut a, "form_focus", json!({ "doc": doc, "field": "name", "event": "enter" }))["changed"],
|
||||
false,
|
||||
"a field with a widget still takes it"
|
||||
);
|
||||
}
|
||||
|
||||
/// XFA scripts run through the tools: initialize and calculate on opening, calculations after
|
||||
/// form_fill, and a button's click script through js_run, adding a row that undo takes away.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user