Files
pdfcraft/crates/automation/tests/it/link_javascript.rs
T
FredAppsandLMatt08 e47e9fbc5e automation: link_run_javascript, open_doc requests, links' JavaScript in doc_info
Ported from #2081 on main's ScriptSession: link_run_javascript runs a link's action with
ordered answers (as js_run takes them) and reports its requests without carrying them out.

Co-authored-by: LMatt08 <245691047+LMatt08@users.noreply.github.com>
2026-11-11 12:55:47 +01:00

66 lines
3.6 KiB
Rust

//! A link's JavaScript action through the tool table (#2081, ported): link_list shows the
//! script, link_run_javascript runs it with ordered answers and reports requests, never
//! carrying them out.
use pdfcraft_automation::{Automation, Content};
use serde_json::{Value, json};
fn tool(a: &mut Automation, name: &str, args: Value) -> Value {
match a.call(name, &args).unwrap().remove(0) {
Content::Json(value) => value,
other => panic!("expected JSON: {other:?}"),
}
}
fn pdf_of(objs: &[String]) -> Vec<u8> {
let mut out = b"%PDF-1.7\n".to_vec();
let mut offsets = Vec::new();
for (i, o) in objs.iter().enumerate() {
offsets.push(out.len());
out.extend_from_slice(format!("{} 0 obj\n{o}\nendobj\n", i + 1).as_bytes());
}
let xref = out.len();
out.extend_from_slice(format!("xref\n0 {}\n0000000000 65535 f \n", objs.len() + 1).as_bytes());
for o in offsets {
out.extend_from_slice(format!("{o:010} 00000 n \n").as_bytes());
}
out.extend_from_slice(format!("trailer\n<< /Size {} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n", objs.len() + 1).as_bytes());
out
}
#[test]
fn link_run_javascript_answers_menus_and_reports_requests() {
let script = "var c = app.popUpMenuEx\\({cName:'Red', cReturn:'r'}, {cName:'Blue', cReturn:'b'}\\); getField\\('log'\\).value = c + ':' + event.type; app.openDoc\\('other.pdf'\\);";
let pdf = pdf_of(&[
"<< /Type /Catalog /Pages 2 0 R /AcroForm << /Fields [4 0 R] /DA (/Helv 0 Tf 0 g) /DR << /Font << /Helv 5 0 R >> >> >> >>".into(),
"<< /Type /Pages /Kids [3 0 R] /Count 1 /MediaBox [0 0 200 300] >>".into(),
"<< /Type /Page /Parent 2 0 R /Annots [4 0 R 6 0 R 7 0 R] >>".into(),
"<< /Type /Annot /Subtype /Widget /FT /Tx /T (log) /V () /Rect [20 250 180 270] /P 3 0 R /F 4 >>".into(),
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /WinAnsiEncoding >>".into(),
format!("<< /Type /Annot /Subtype /Link /Rect [20 20 180 60] /Border [0 0 0] /A << /S /JavaScript /JS ({script}) >> >>"),
"<< /Type /Annot /Subtype /Link /Rect [20 80 180 120] /Border [0 0 0] /A << /S /JavaScript /JS (missing\\(\\);) >> >>".into(),
]);
let dir = std::env::temp_dir().join(format!("pdfcraft-link-js-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("link.pdf"), pdf).unwrap();
let mut a = Automation::new().with_root(&dir).unwrap();
let doc = tool(&mut a, "doc_open", json!({ "path": "link.pdf" }))["doc"].as_u64().unwrap();
let info = tool(&mut a, "doc_info", json!({ "doc": doc }));
assert!(info.to_string().contains("\"javascript\""), "links show their script: {info}");
let log = |a: &mut Automation| {
let f = tool(a, "form_fields", json!({ "doc": doc }));
f["fields"].as_array().unwrap().iter().find(|f| f["name"] == "log").map(|f| f["value"].clone()).unwrap()
};
let out = tool(&mut a, "link_run_javascript", json!({ "doc": doc, "page": 1, "index": 2, "answers": ["b"] }));
assert_eq!(out["error"], Value::Null, "{out}");
assert_eq!(out["requests"], json!([{ "open_doc": "other.pdf" }]), "{out}");
assert_eq!(log(&mut a), "b:Link");
let failed = tool(&mut a, "link_run_javascript", json!({ "doc": doc, "page": 1, "index": 3 }));
assert!(failed["error"].is_string(), "{failed}");
assert!(a.call("link_run_javascript", &json!({ "doc": doc, "page": 1, "index": 1 })).is_err(), "a widget is no JavaScript link");
assert!(a.call("link_run_javascript", &json!({ "doc": doc, "page": 0, "index": 2 })).is_err());
let _ = std::fs::remove_dir_all(&dir);
}