mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
M12: say when a form's scripts were turned off (#1251)
* M12: say when a form's scripts were turned off When an XFA script runs past its time limit it is abandoned and the form's scripts are turned off for the document. The only trace was an error line in the JavaScript console, so calculations and buttons just stopped working. - Engine: Document::xfa_scripts_off() reports the state (set on open and on an edit), so a viewer shows it without reading error text. doc_info reports it to agents as xfa_scripts_off. - App: when it happens (at open or after a click) a toast says so once, and the notice bar says so from then on, ahead of its other notices, with a JavaScript Console button. A notice bar the user had closed comes back for it; closed again, it stays closed. - The sentence is translated in the 17 catalogs that are complete (hashes in ATTRIBUTION.toml updated). Tests: two kittest UI tests (runaway at open; runaway click after the bar was closed), which fail without the app change; engine assertions for both states; a doc_info tool test. Co-Authored-By: Claude Opus 5.5 <[email protected]> * Catalogs: the scripts-off notice in Greek, Polish and Telugu; refresh hashes --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: Brandon Thomas <[email protected]>
This commit is contained in:
30 files changed
+184
-32
No files matched your search
+20
-20
@@ -7,7 +7,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Greek UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "74d52e8de164f17e2f54905dbb53e547b41ecd892c5402f89088a42c205892f7"
|
||||
sha256 = "e04ab5246125efae2bad439e62c150fc10649c4b6796b30565615f5bd7509ec6"
|
||||
|
||||
# ATTRIBUTION.toml: one entry per asset (AGENTS.md §1). The source of truth for provenance and licences.
|
||||
# ATTRIBUTION.md is generated from this file: `cargo xtask assets --write`. Checked by `cargo xtask assets` (part of `xtask ci`).
|
||||
@@ -30,7 +30,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Simplified Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "a0c2b7210671794d9c4b9f1eed1990ce6e34f91fac54f330b55a5d163487c417"
|
||||
sha256 = "dd5da0d13f9e75b2d285a2337e2ebda61e99ffae28a2e807c719e094a620c71f"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/zh-hant.tsv"
|
||||
@@ -41,7 +41,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Traditional Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "c155ca42d4fe98076aa69bcb45571b034e5c685323024e3652a7056990dec60d"
|
||||
sha256 = "d3729a1cd0d616e434d2852e918e16542712a682432484b964d4c9530e0001fa"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/cs.tsv"
|
||||
@@ -52,7 +52,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Czech UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "8d8e221cec2447eeaf2de1e4c22a0ad3bbb2056df445c9a4aab9cc641408abb6"
|
||||
sha256 = "c1bf8de7fd14d2814ae552900eced600d5c13ebe951bba62e5fee265c072dec7"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pt-br.tsv"
|
||||
@@ -63,7 +63,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Brazilian Portuguese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "851e4c262d5cc48c0556845403fdea3c8909c86d3c83175ac56e66da6c3df2c2"
|
||||
sha256 = "6b7e7f11ef0bd19e4862f2b3e4f3fa4e7c77857c264dc36620875cb62b4d71ab"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/es.tsv"
|
||||
@@ -74,7 +74,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Spanish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "47be4a80633789d29ed0e5d70f8b4f7fc5e2c5af2181076c4773f7808620e598"
|
||||
sha256 = "4d915ac6ec905b24af4b9659c8457ac6fd8de93b2f989403c90fb7c2617c6fc3"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/fr.tsv"
|
||||
@@ -85,7 +85,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "French UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "c9792fd691ab7b8d1d575b31787813d45aacb6ac53af252dffeaab2d0b5f867b"
|
||||
sha256 = "273d16de3d4044aa70eb1c1861610cfe306908df53a03ffd96242d4434be9882"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/de.tsv"
|
||||
@@ -96,7 +96,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "German UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "97faaaf19582d9c3d34325a14e0db1b1b5bdf6b49151e8dda5d98e7a80049f12"
|
||||
sha256 = "bd02e51b45e3f2a4f647af1602a31e15401e3715dc88a964afd34a4c29aa5392"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/te.tsv"
|
||||
@@ -107,7 +107,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Telugu UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "85e2ba7e083a629c493dd88911614e81ecb26be5166f2859a6d840a410a0399e"
|
||||
sha256 = "c597d66f587ff89fe88ee1893f418665a1f7f81ba786378a65e69bd5bf65a952"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/it.tsv"
|
||||
@@ -118,7 +118,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Italian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "4a40714c86b012d5e4810b6279824a6a576d7f5a8202a5932dd2a7de7b23bcdd"
|
||||
sha256 = "91d826a767d02eeaa6c0d0084fcb78c29c5d65b57328c5300d471e67bb256350"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/pl.tsv"
|
||||
@@ -129,7 +129,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Polish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "fc907f19f2273c5a3fd69544207c757d1e43c97f371e0bebefea409a266af640"
|
||||
sha256 = "6e976b1deaee1ed25f30626c833ea7dca8c0daf806fc6d9c97e096a90b1acb36"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ru.tsv"
|
||||
@@ -140,7 +140,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Russian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "dc674b7b60359e26fe994594ee57160772f35997550888fc8b41065d9c248023"
|
||||
sha256 = "c9467546f1659c734ee4ebcf23eec357bae84230c2e5a3f68baccec7c2cab86c"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/hu.tsv"
|
||||
@@ -151,7 +151,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Hungarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "f081f92d60e4b3ed75ecef178698170346965b0f36d49e2c46ec6bbd0de37f7e"
|
||||
sha256 = "f643927cf4de4038f4c80f4deede8f440d7f5e4adf57fb92c6f0afbef86258d3"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/bg.tsv"
|
||||
@@ -162,7 +162,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Bulgarian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "ec0a0349c33e5f22a4c27367d55454a4946f1c7288f65f7a8b1e4fc417191ebe"
|
||||
sha256 = "30cf24faab89cf2fe9c97871ba9d5daf38cbf8adb3bbcaf0ff9bcb3fb2ff7bc0"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/kk.tsv"
|
||||
@@ -173,7 +173,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Kazakh UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "b8e71686440249c9247bf99089d828a172e08cc5e4a85939af05ddb60589518b"
|
||||
sha256 = "4d3b525a99206de8b583de2ef3be3de2343051a5068ddad723e4e684472a11ec"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ja.tsv"
|
||||
@@ -184,7 +184,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Japanese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "0ea5c0904da3166e252746803c96f9f72a69242e151328f7b8b454052db12dd7"
|
||||
sha256 = "6d37d306022c6f6860beeef4aa423b1a43b5ce0e941268624642aaed5d0a790c"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/uk.tsv"
|
||||
@@ -195,7 +195,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Ukrainian UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "380e545b11a8e3325362acb6cb99f57203262baa2487665d57a2edbd504aeafb"
|
||||
sha256 = "3daf5f42f0ce558add463370da25d823c9371eec0d95c11d6d2f2fd0cb1da0c8"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/ar.tsv"
|
||||
@@ -206,7 +206,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Arabic UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "369fc4f6c0fe1501cfb4cb310e0007a054287ee5e3b813617b783782c6759bfd"
|
||||
sha256 = "b401a44ff5a5d07bf1cb6786139f9886f0f354137fe4dcc41c46115d09235730"
|
||||
|
||||
[[asset]]
|
||||
path = "crates/ui-egui/src/i18n/tr.tsv"
|
||||
@@ -217,7 +217,7 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Turkish UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "cd346d40188a768468adc7631150e93527a52c1693669425c8569bd9ba548b60"
|
||||
sha256 = "f1ded17cdf0c3fe53f4af77b91319913122ea4e3df2f7be0123def1e753de49b"
|
||||
|
||||
[[asset]]
|
||||
path = "assets/icons/accessibility.svg"
|
||||
@@ -3328,4 +3328,4 @@ licence = "MIT OR Apache-2.0"
|
||||
licence_file = "LICENSE-MIT"
|
||||
kind = "translation"
|
||||
usage = "Slovene UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
|
||||
sha256 = "72bb3bac963553ad79268dc3b7ef02d25f03d71fb79043a0c6df3bd38d984b32"
|
||||
sha256 = "14301dcc1f33e13c20543a95fc9434049ff21affe0c49f54147fb037e2a10b79"
|
||||
@@ -140,6 +140,7 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md
|
||||
|
||||
## Progress log
|
||||
|
||||
- **2026-10-12 (M12, XFA scripts turned off):** When a runaway script turns an XFA form's scripts off, the app now says so: a toast as it happens (at open or after a click), and the notice bar from then on, ahead of its other notices, with a JavaScript Console button. Before, the error was only in the console, so calculations and buttons just stopped. A notice bar the user had closed comes back for it; closed again, it stays closed. The engine reports the state (`Document::xfa_scripts_off`, and `xfa_scripts_off` in `doc_info`) instead of the UI reading error text. Two kittest UI tests (a runaway at open; a runaway click after the bar was closed), engine assertions and a `doc_info` tool test. Overall unchanged.
|
||||
- **2026-10-12 (M14, Windows low-DPI font-hinting candidate):** The Windows policy disables symmetric rendering for embedded JetBrains Mono only. On the locked-font macOS probe, horizontal alpha-edge energy per ink improves 2.78%, 3.87% and 1.59% at 100%, 125% and 150% scaling, and is down 0.79% at 200%; Inter is pixel-identical. Logical layout hashes and 8 MiB atlas size match, repeated fractional offsets add no atlas dimensions, and baseline/candidate time and RSS ranges overlap. Windows runtime and hardware remain unverified; estimates unchanged.
|
||||
- **2026-10-12 (M7, #1200 candidate):** Bounded local GoToR file specifications open in desktop tabs; explicit numeric page views apply, while named destinations open at the default view with a warning. The automation tool resolves relative to the source and uses existing `--root` confinement. Appearance-only Link Properties edits preserve the existing action. URL/network paths remain refused; `/NewWindow` is a tab hint. Estimates unchanged.
|
||||
- **2026-10-12 (M7, private image crops):** Exclusive image crops resize with their pixels across nested states, intervening operators and content streams; shared clips remain fixed. Synthetic engine/tool regressions and private image save/reopen checks; estimates unchanged.
|
||||
|
||||
@@ -2097,6 +2097,9 @@ fn info(d: &Document) -> Value {
|
||||
// (laid out from the template by PdfCraft, see xfa_layout; placeholder pages when that failed).
|
||||
"xfa": i.xfa.map(|x| match x { pdfcraft_render::Xfa::Static => "static", pdfcraft_render::Xfa::Dynamic => "dynamic" }),
|
||||
"xfa_layout": d.xfa.as_ref().map(|x| json!({ "pages": x.pages, "fields": x.fields, "warnings": x.warnings })),
|
||||
// A script of the form ran too long and was abandoned: its scripts no longer run in this
|
||||
// document (calculations, buttons) until it is opened again.
|
||||
"xfa_scripts_off": d.xfa_scripts_off(),
|
||||
// What was rewritten from, or could not be written to, the XFA data.
|
||||
"xfa_warnings": d.xfa_warnings,
|
||||
"security": security,
|
||||
|
||||
@@ -4412,6 +4412,7 @@ fn xfa_scripts_run_for_buttons_and_field_changes_through_tools() {
|
||||
// Filling recalculates; a bad value shows its message (the tool reports alerts in js output? no: it is applied, the value stays).
|
||||
ok(&mut a, "form_fill", json!({ "doc": doc, "values": { "qty": "4" } }));
|
||||
assert_eq!(field(&mut a, "total").unwrap()["value"], "20");
|
||||
assert_eq!(ok(&mut a, "doc_info", json!({ "doc": doc }))["xfa_scripts_off"], false);
|
||||
// A button's XFA click script runs through js_run, like any button.
|
||||
let before = ok(&mut a, "doc_info", json!({ "doc": doc }))["xfa_layout"]["fields"].as_u64().unwrap();
|
||||
let r = ok(&mut a, "js_run", json!({ "doc": doc, "script": "", "field": "addRow" }));
|
||||
@@ -4425,6 +4426,21 @@ fn xfa_scripts_run_for_buttons_and_field_changes_through_tools() {
|
||||
assert_eq!(r["alerts"], json!(["Hello 4"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doc_info_says_when_a_runaway_script_turned_the_forms_scripts_off() {
|
||||
let dir = workdir("xfa-scripts-off");
|
||||
// An initialize script whose loops are in nested calls runs until it is abandoned.
|
||||
let tpl = pdfcraft_xfa::fixtures::scripted_template().replace(
|
||||
"if (qty.rawValue === null) qty.rawValue = 2;",
|
||||
"function f() { for (var j = 0; j < 99999; j++) {} } for (var k = 0; k < 5000; k++) f();",
|
||||
);
|
||||
std::fs::write(dir.join("runaway.pdf"), pdfcraft_xfa::fixtures::shell(&tpl)).unwrap();
|
||||
let mut a = auto(&dir);
|
||||
let doc = ok(&mut a, "doc_open", json!({ "path": "runaway.pdf" }))["doc"].as_u64().unwrap();
|
||||
assert_eq!(ok(&mut a, "doc_info", json!({ "doc": doc }))["xfa_scripts_off"], true);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// doc_info describes a link's set-layer-visibility action by layer name.
|
||||
#[test]
|
||||
fn doc_info_describes_set_layer_links() {
|
||||
|
||||
@@ -291,6 +291,8 @@ pub struct Document {
|
||||
pub xfa: Option<XfaLayout>,
|
||||
/// The parsed template of a laid-out XFA form, for its scripts.
|
||||
xfa_template: Option<Arc<pdfcraft_xfa::model::Template>>,
|
||||
/// A script of the XFA form ran away, so its scripts are off (see [`Document::xfa_scripts_off`]).
|
||||
xfa_scripts_off: bool,
|
||||
/// A content-only refresh keeps `info` from before the edit; an edit may have added or
|
||||
/// embedded fonts, so the font list is read again on demand ([`Session::refresh_fonts`]).
|
||||
fonts_stale: bool,
|
||||
@@ -382,6 +384,13 @@ impl Document {
|
||||
self.editor.as_ref().map(|e| a11y::check(&e.cos, options))
|
||||
}
|
||||
|
||||
/// A script of this XFA form ran past its time limit and was abandoned, so the form's
|
||||
/// scripts no longer run in this document (reopening it runs them again). The script's
|
||||
/// error is in the JavaScript output; this is the state, for a viewer to show.
|
||||
pub fn xfa_scripts_off(&self) -> bool {
|
||||
self.xfa_scripts_off
|
||||
}
|
||||
|
||||
/// PDF Optimizer ▸ Audit space usage.
|
||||
pub fn audit_space(&self) -> Vec<optimize::SpaceUse> {
|
||||
self.editor.as_ref().map(|e| optimize::audit_space(&e.cos, self.bytes.len() as u64)).unwrap_or_default()
|
||||
@@ -2984,6 +2993,7 @@ impl Session {
|
||||
d.xfa_template = if ran_away { None } else { xfa_template };
|
||||
d.js_output.append(script_output);
|
||||
if ran_away {
|
||||
d.xfa_scripts_off = true;
|
||||
d.js_output.errors.push(XFA_SCRIPTS_OFF.into());
|
||||
}
|
||||
note_warnings(&mut d.xfa_warnings, &xfa_warnings);
|
||||
@@ -3071,6 +3081,7 @@ impl Session {
|
||||
js_output: Default::default(),
|
||||
xfa,
|
||||
xfa_template: None,
|
||||
xfa_scripts_off: false,
|
||||
fonts_stale: false,
|
||||
xfa_warnings: Vec::new(),
|
||||
edit_notices: Vec::new(),
|
||||
@@ -3237,6 +3248,7 @@ impl Session {
|
||||
doc.js_output.append(cx.xfa_out);
|
||||
if cx.xfa_ran_away {
|
||||
doc.xfa_template = None;
|
||||
doc.xfa_scripts_off = true;
|
||||
doc.js_output.errors.push(XFA_SCRIPTS_OFF.into());
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -3097,6 +3097,7 @@ fn xfa_scripts_initialize_calculate_validate_toggle_and_add_rows() {
|
||||
assert_eq!(value(&s, "qty"), vec!["2".to_string()]);
|
||||
assert_eq!(value(&s, "total"), vec!["10".to_string()], "{:?}", s.take_js_output(id));
|
||||
assert_eq!(value(&s, "grand"), vec!["0".to_string()]);
|
||||
assert!(!s.get(id).unwrap().xfa_scripts_off());
|
||||
assert!(!s.get(id).unwrap().dirty);
|
||||
assert!(!names(&s).iter().any(|n| n == "note"), "details is hidden");
|
||||
// A change recalculates.
|
||||
@@ -3282,6 +3283,7 @@ fn xfa_scripts_stay_off_with_javascript_off() {
|
||||
let qty = s.get(id).unwrap().form.iter().find(|f| f.name == "qty").unwrap().value.clone();
|
||||
assert!(qty.is_empty(), "no initialize script ran");
|
||||
assert!(s.run_javascript(id, "", Some("addRow")).is_err());
|
||||
assert!(!s.get(id).unwrap().xfa_scripts_off(), "the preference, not a script that ran away");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3403,11 +3405,13 @@ fn runaway_xfa_calculations_at_open_end_quickly_with_a_report() {
|
||||
assert!(out.errors.iter().any(|e| e.contains("total") && e.contains("maximum number of iteration")), "{:?}", out.errors);
|
||||
assert!(out.errors.iter().any(|e| e.contains("abandoned")), "{:?}", out.errors);
|
||||
assert!(out.errors.iter().any(|e| e.contains("scripts are off")), "{:?}", out.errors);
|
||||
assert!(s.get(id).unwrap().xfa_scripts_off());
|
||||
// The scripts stay off: a change runs nothing more.
|
||||
let started = std::time::Instant::now();
|
||||
s.apply(id, Edit::SetFieldValue { name: "qty".into(), value: FieldValue::Text("3".into()) }).unwrap();
|
||||
assert!(started.elapsed() < std::time::Duration::from_secs(2));
|
||||
assert!(s.take_js_output(id).errors.is_empty());
|
||||
assert!(s.get(id).unwrap().xfa_scripts_off());
|
||||
}
|
||||
|
||||
/// The page view's raster of `page` at 1 px/pt (the document's own render pool).
|
||||
|
||||
@@ -300,6 +300,8 @@ pub struct DocView {
|
||||
pub highlight_fields: bool,
|
||||
pub page_input: String,
|
||||
pub notice_dismissed: bool,
|
||||
/// The user has been told that the form's scripts were turned off (it is said once).
|
||||
pub scripts_off_noted: bool,
|
||||
/// The file changed on disk while this document had unsaved changes: the notice bar asks
|
||||
/// before replacing it (#431).
|
||||
pub disk_changed: bool,
|
||||
@@ -581,6 +583,7 @@ impl DocView {
|
||||
highlight_fields: defaults.highlight_fields,
|
||||
page_input: "1".into(),
|
||||
notice_dismissed: false,
|
||||
scripts_off_noted: false,
|
||||
disk_changed: false,
|
||||
cover: false,
|
||||
back: Vec::new(),
|
||||
@@ -2046,6 +2049,7 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) {
|
||||
Some(Notice::Unlock) => app.unlock_prompt = Some(crate::UnlockPrompt::new(doc, None)),
|
||||
Some(Notice::Signatures) => app.right = Some(RightPanel::Signatures),
|
||||
Some(Notice::FieldHighlights(on)) => app.view_defaults.highlight_fields = on,
|
||||
Some(Notice::Console) => app.dialog = Some(crate::Dialog::JsConsole),
|
||||
None => {}
|
||||
}
|
||||
// No dialog, close prompt or palette over the page: only then does page input count.
|
||||
@@ -3606,12 +3610,14 @@ enum Notice {
|
||||
Repairs,
|
||||
/// Highlight fields was turned on or off.
|
||||
FieldHighlights(bool),
|
||||
/// Open the JavaScript console (the form's scripts were turned off; the error is there).
|
||||
Console,
|
||||
/// Unlock… (enter the permissions password).
|
||||
Unlock,
|
||||
}
|
||||
|
||||
/// The notice bar above the pages: the signature status first (Acrobat's signature bar), then
|
||||
/// security, forms and warnings.
|
||||
/// a form's scripts having been turned off, security, forms and warnings.
|
||||
fn notices(
|
||||
view: &mut DocView,
|
||||
doc: &pdfcraft_engine::Document,
|
||||
@@ -3659,8 +3665,14 @@ fn notices(
|
||||
let mut open_security = false;
|
||||
let mut unlock = false;
|
||||
let mut open_repairs = false;
|
||||
let mut open_console = false;
|
||||
let mut toggled = None;
|
||||
let msg = if secured {
|
||||
// A form whose scripts were turned off no longer calculates or answers its buttons: that
|
||||
// comes before what else there is to say about the document.
|
||||
let scripts_off = doc.xfa_scripts_off();
|
||||
let msg = if scripts_off {
|
||||
Some(("triangle-alert", crate::js_ui::scripts_off_notice(), true))
|
||||
} else if secured {
|
||||
Some(("lock", tl!("This document is secured. Some changes are restricted by its security settings.").to_string(), false))
|
||||
} else if let Some(x) = xfa {
|
||||
let lang = crate::i18n::current();
|
||||
@@ -3712,6 +3724,9 @@ fn notices(
|
||||
toggled = Some(Notice::FieldHighlights(view.highlight_fields));
|
||||
}
|
||||
}
|
||||
if scripts_off && crate::widgets::pill_button(ui, tl!("JavaScript Console"), false).clicked() {
|
||||
open_console = true;
|
||||
}
|
||||
if secured && crate::widgets::pill_button(ui, tl!("Security settings"), false).clicked() {
|
||||
open_security = true;
|
||||
}
|
||||
@@ -3725,6 +3740,9 @@ fn notices(
|
||||
if open_repairs {
|
||||
return Some(Notice::Repairs);
|
||||
}
|
||||
if open_console {
|
||||
return Some(Notice::Console);
|
||||
}
|
||||
if unlock {
|
||||
return Some(Notice::Unlock);
|
||||
}
|
||||
|
||||
@@ -2031,6 +2031,7 @@
|
||||
DPI sets the printed page size without resampling the image. قيمة DPI تحدد حجم الصفحة المطبوعة دون إعادة تشكيل الصورة.
|
||||
Create إنشاء
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. نموذج XFA ديناميكي رُتِّب من قالبه: {pages}، {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. سكربتات هذا النموذج متوقفة لأن أحدها استغرق وقتًا طويلًا جدًا. أعد فتح المستند لتشغيلها من جديد.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. نموذج XFA: حقوله وبيانات XFA فيه تبقى متطابقة، فتعرض العارضات الأخرى ما تعبئه.
|
||||
Switch to two-page view first to show the cover page انتقل أولًا إلى عرض الصفحتين لإظهار صفحة الغلاف
|
||||
Page display: {layout} عرض الصفحات: {layout}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2086,6 +2086,7 @@
|
||||
DPI sets the printed page size without resampling the image. DPI определя размера на отпечатаната страница без преизчисляване на изображението.
|
||||
Create Създай
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Динамичен XFA формуляр, оформен по своя шаблон: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Скриптовете на този формуляр са изключени, защото един от тях се изпълняваше твърде дълго. Отворете документа отново, за да се изпълнят пак.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA формуляр: полетата и XFA данните му се поддържат синхронизирани, така че другите програми за преглед показват попълненото от вас.
|
||||
@plural {n} page|{n} pages {n} страница|{n} страници
|
||||
@plural {n} field|{n} fields {n} поле|{n} полета
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 8 and column 16.
|
@@ -2080,6 +2080,7 @@ key Space Mezerník
|
||||
DPI sets the printed page size without resampling the image. DPI určuje velikost tištěné stránky bez převzorkování obrázku.
|
||||
Create Vytvořit
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Dynamický formulář XFA rozvržený podle šablony: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Skripty tohoto formuláře jsou vypnuté, protože jeden z nich běžel příliš dlouho. Chcete-li je znovu spustit, otevřete dokument znovu.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Formulář XFA: jeho pole a data XFA se udržují ve shodě, takže ostatní prohlížeče zobrazí, co vyplníte.
|
||||
Switch to two-page view first to show the cover page Titulní stránku zobrazíte, až přepnete na dvoustránkové zobrazení
|
||||
@plural {n} page|{n} pages {n} stránka|{n} stránky|{n} stránek
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1967,6 +1967,7 @@
|
||||
DPI sets the printed page size without resampling the image. Die DPI bestimmt die gedruckte Seitengröße, ohne das Bild neu zu berechnen.
|
||||
Create Erstellen
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Dynamisches XFA-Formular, aus seiner Vorlage aufgebaut: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Die Skripte dieses Formulars sind ausgeschaltet, weil eines zu lange lief. Öffne das Dokument erneut, um sie wieder auszuführen.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA-Formular: Seine Felder und XFA-Daten werden synchron gehalten, damit andere Viewer anzeigen, was du ausfüllst.
|
||||
@plural {n} page|{n} pages {n} Seite|{n} Seiten
|
||||
@plural {n} field|{n} fields {n} Feld|{n} Felder
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 51.
|
@@ -2347,3 +2347,4 @@ animation effects Off Ανενεργά
|
||||
The linked PDF opened, but its page destination was out of range. Το συνδεδεμένο PDF άνοιξε, αλλά ο προορισμός σελίδας του είναι εκτός εύρους.
|
||||
Local cross-PDF links require the desktop app Οι τοπικοί σύνδεσμοι μεταξύ PDF απαιτούν την εφαρμογή υπολογιστή
|
||||
The existing action will be preserved. Η υπάρχουσα ενέργεια θα διατηρηθεί.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Τα σενάρια αυτής της φόρμας είναι απενεργοποιημένα, επειδή ένα από αυτά εκτελούνταν για πολύ ώρα. Ανοίξτε ξανά το έγγραφο για να εκτελεστούν ξανά.
|
||||
|
Can't render this file because it contains an unexpected character in line 479 and column 12.
|
@@ -2046,6 +2046,7 @@ action wizard Start Iniciar
|
||||
DPI sets the printed page size without resampling the image. Los PPP determinan el tamaño de la página impresa sin remuestrear la imagen.
|
||||
Create Crear
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Formulario XFA dinámico maquetado a partir de su plantilla: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Los scripts de este formulario están desactivados porque uno tardó demasiado en ejecutarse. Vuelve a abrir el documento para ejecutarlos de nuevo.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Formulario XFA: sus campos y sus datos XFA se mantienen sincronizados, para que otros visores muestren lo que rellenes.
|
||||
@plural {n} page|{n} pages {n} página|{n} páginas
|
||||
@plural {n} field|{n} fields {n} campo|{n} campos
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 60.
|
@@ -1962,6 +1962,7 @@
|
||||
DPI sets the printed page size without resampling the image. Les PPP déterminent la taille de la page imprimée sans rééchantillonner l'image.
|
||||
Create Créer
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Formulaire XFA dynamique mis en page à partir de son modèle : {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Les scripts de ce formulaire sont désactivés, car l'un d'eux s'est exécuté trop longtemps. Rouvrez le document pour les exécuter à nouveau.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Formulaire XFA : ses champs et ses données XFA restent synchronisés, afin que les autres visionneuses affichent ce que vous remplissez.
|
||||
@plural {n} page|{n} pages {n} page|{n} pages
|
||||
@plural {n} field|{n} fields {n} champ|{n} champs
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 39.
|
@@ -1952,6 +1952,7 @@
|
||||
DPI sets the printed page size without resampling the image. A DPI a nyomtatott oldalméretet állítja be, a kép újramintázása nélkül.
|
||||
Create Létrehozás
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Dinamikus XFA-űrlap, a sablonjából felépítve: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Az űrlap szkriptjei ki vannak kapcsolva, mert az egyik túl sokáig futott. Nyisd meg újra a dokumentumot, hogy ismét fussanak.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA-űrlap: a mezői és az XFA-adatai együtt maradnak, így más megjelenítők azt mutatják, amit beírsz.
|
||||
@plural {n} page|{n} pages {n} oldal|{n} oldal
|
||||
@plural {n} field|{n} fields {n} mező|{n} mező
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 44.
|
@@ -2055,6 +2055,7 @@
|
||||
DPI sets the printed page size without resampling the image. I DPI determinano la dimensione della pagina stampata senza ricampionare l'immagine.
|
||||
Create Crea
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Modulo XFA dinamico impaginato dal suo modello: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Gli script di questo modulo sono disattivati perché uno è rimasto in esecuzione troppo a lungo. Riapri il documento per eseguirli di nuovo.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Modulo XFA: i suoi campi e i dati XFA restano allineati, così gli altri visualizzatori mostrano ciò che compili.
|
||||
Default page display Visualizzazione pagina predefinita
|
||||
Default zoom Ingrandimento predefinito
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1781,6 +1781,7 @@
|
||||
At least one signature has problems. 問題のある署名があります。
|
||||
This document is secured. Some changes are restricted by its security settings. この文書は保護されています。セキュリティ設定により一部の変更が制限されています。
|
||||
This is a dynamic XFA form, which PdfCraft can't display yet. What you see is the file's placeholder page. この文書は動的 XFA フォームで、PdfCraft はまだ表示できません。現在はファイルの代替ページを表示しています。
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. このフォームのスクリプトは、実行に時間がかかりすぎたものがあったため無効になっています。もう一度実行するには、文書を再度開いてください。
|
||||
This form also contains XFA data, which PdfCraft doesn't read yet. You can fill its fields, but Acrobat may show the XFA values instead. このフォームには PdfCraft がまだ読み取れない XFA データも含まれます。フィールドには入力できますが、Acrobat は代わりに XFA の値を表示する場合があります。
|
||||
This document contains {n} interactive form fields. この文書には対話型フォームフィールドが {n} 個あります。
|
||||
This file was damaged and has been repaired. Saving keeps the repaired version. このファイルは破損していたため修復しました。保存すると修復済みの状態が保持されます。
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1957,6 +1957,7 @@
|
||||
DPI sets the printed page size without resampling the image. DPI пиксельдер санын өзгертпей, басып шығарылатын беттің өлшемін орнатады.
|
||||
Create Жасау
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Ұлгісі бойынша құрылған динамикалық XFA формасы: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Бұл форманың скрипттері өшірілді, себебі олардың бірі тым ұзақ орындалды. Оларды қайта іске қосу үшін құжатты қайта ашыңыз.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA формасы: өрістері мен XFA деректері өзара синхрондалып тұрады, сондықтан басқа қарау құралдары да сіз енгізген мәндерді көрсетеді.
|
||||
Switch to two-page view first to show the cover page Мұқабаны көрсету үшін алдымен екі беттік көрініске ауысыңыз
|
||||
Page display: {layout} Беттерді көрсету: {layout}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 823 and column 12.
|
@@ -2311,3 +2311,4 @@ animation effects Off Wyłączone
|
||||
The linked PDF opened, but its page destination was out of range. Połączony plik PDF został otwarty, ale jego strona docelowa jest poza zakresem.
|
||||
Local cross-PDF links require the desktop app Lokalne łącza między plikami PDF wymagają aplikacji komputerowej
|
||||
The existing action will be preserved. Istniejąca akcja zostanie zachowana.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Skrypty tego formularza są wyłączone, ponieważ jeden z nich działał zbyt długo. Otwórz dokument ponownie, aby znów je uruchomić.
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1948,6 +1948,7 @@
|
||||
DPI sets the printed page size without resampling the image. O DPI define o tamanho da página impressa sem reamostrar a imagem.
|
||||
Create Criar
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Formulário XFA dinâmico formatado a partir de seu modelo: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Os scripts deste formulário estão desativados porque um deles demorou demais para ser executado. Reabra o documento para executá-los novamente.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Formulário XFA: seus campos e seus dados XFA são mantidos sincronizados, para que outros leitores exibam o que você preencher.
|
||||
@plural {n} page|{n} pages {n} página|{n} páginas
|
||||
@plural {n} field|{n} fields {n} campo|{n} campos
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -2032,6 +2032,7 @@
|
||||
DPI sets the printed page size without resampling the image. DPI задаёт размер печатной страницы без передискретизации изображения.
|
||||
Create Создать
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Динамическая форма XFA, сверстанная по шаблону: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Скрипты этой формы выключены: один из них выполнялся слишком долго. Чтобы запустить их снова, откройте документ заново.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Форма XFA: её поля и данные XFA синхронизированы, поэтому другие просмотрщики показывают то, что вы заполняете.
|
||||
Switch to two-page view first to show the cover page Сначала переключитесь в двухстраничный вид, чтобы показать титульную страницу
|
||||
Page display: {layout} Отображение страниц: {layout}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -654,6 +654,7 @@
|
||||
Duplicate… Podvoji…
|
||||
Dynamic Dinamično
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Dinamični obrazec XFA, razporejen iz predloge: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Skripti tega obrazca so izklopljeni, ker se je eden izvajal predolgo. Če jih želite znova zagnati, znova odprite dokument.
|
||||
E-Sign E-podpis
|
||||
Each field needs a tooltip that says what to enter. Add one in Field Properties > General > Tooltip. Vsako polje potrebuje namig, ki pove, kaj vnesti. Dodajte ga v Lastnosti polja > Splošno > Namig.
|
||||
Each file holds as many of the {n} pages as fit. Vsaka datoteka vsebuje toliko od {n} strani, kot jih gre vanjo.
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -2244,3 +2244,4 @@ animation effects Off ఆఫ్
|
||||
The linked PDF opened, but its page destination was out of range. లింక్ చేసిన PDF తెరుచుకుంది, కానీ దాని పేజీ గమ్యం పరిధికి వెలుపల ఉంది.
|
||||
Local cross-PDF links require the desktop app PDFల మధ్య స్థానిక లింక్లకు డెస్క్టాప్ యాప్ అవసరం
|
||||
The existing action will be preserved. ఉన్న చర్య అలాగే ఉంచబడుతుంది.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. ఒక స్క్రిప్ట్ చాలా సేపు నడిచినందున ఈ ఫారమ్ స్క్రిప్ట్లు ఆపివేయబడ్డాయి. వాటిని మళ్లీ నడపడానికి పత్రాన్ని మళ్లీ తెరవండి.
|
||||
|
Can't render this file because it contains an unexpected character in line 13 and column 16.
|
@@ -1929,6 +1929,7 @@
|
||||
DPI sets the printed page size without resampling the image. DPI, resmi yeniden örneklemeden yazdırılan sayfa boyutunu ayarlar.
|
||||
Create Oluştur
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Şablonundan oluşturulmuş dinamik XFA formu: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Bu formun komut dosyaları kapatıldı, çünkü biri çok uzun süre çalıştı. Yeniden çalıştırmak için belgeyi yeniden açın.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA formu: Diğer görüntüleyicilerin doldurduklarınızı göstermesi için alanları ve XFA verileri eşitlenir.
|
||||
@plural {n} page|{n} pages {n} sayfa
|
||||
@plural {n} field|{n} fields {n} alan
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 822 and column 12.
|
@@ -2029,6 +2029,7 @@
|
||||
DPI sets the printed page size without resampling the image. DPI задає розмір друкованої сторінки без зміни кількості пікселів зображення.
|
||||
Create Створити
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. Динамічну форму XFA скомпоновано за її шаблоном: {pages}, {fields}.
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. Сценарії цієї форми вимкнено, бо один із них виконувався надто довго. Щоб запустити їх знову, відкрийте документ повторно.
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. Форма XFA: її поля й дані XFA синхронізуються, тому інші засоби перегляду показують введені вами дані.
|
||||
Switch to two-page view first to show the cover page Спочатку перемкніть на двосторінковий вигляд, щоб показати обкладинку
|
||||
Page display: {layout} Відображення сторінок: {layout}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2103,6 +2103,7 @@
|
||||
DPI sets the printed page size without resampling the image. DPI 用于设置打印页面尺寸,不会对图像重新采样。
|
||||
Create 创建
|
||||
Dynamic XFA form laid out from its template: {pages}, {fields}. 动态 XFA 表单已按模板排版:{pages},{fields}。
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. 此表单的脚本已关闭,因为其中一个脚本运行时间过长。重新打开文档即可再次运行。
|
||||
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA 表单:字段与 XFA 数据保持同步,以便其他阅读器显示您填写的内容。
|
||||
@plural {n} page|{n} pages {n} 页
|
||||
@plural {n} field|{n} fields {n} 个字段
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -1682,6 +1682,7 @@
|
||||
At least one signature has problems. 至少一個簽名存在問題。
|
||||
This document is secured. Some changes are restricted by its security settings. 本文件已加密。其安全設定限制了部分更改。
|
||||
This is a dynamic XFA form, which PdfCraft can't display yet. What you see is the file's placeholder page. 這是動態 XFA 表單,PdfCraft 暫無法顯示。您看到的是檔案的佔位頁面。
|
||||
This form's scripts are turned off because one ran too long. Reopen the document to run them again. 此表單的指令碼已關閉,因為其中一個指令碼執行時間過長。重新開啟文件即可再次執行。
|
||||
This form also contains XFA data, which PdfCraft doesn't read yet. You can fill its fields, but Acrobat may show the XFA values instead. 此表單還包含 XFA 資料,PdfCraft 暫不讀取。您可以填寫欄位,但 Acrobat 可能顯示 XFA 值。
|
||||
This document contains {n} interactive form fields. 本文件包含 {n} 個互動式表單欄位。
|
||||
This file was damaged and has been repaired. Saving keeps the repaired version. 此檔案已損壞並已修復。儲存將保留修復後的版本。
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 9 and column 16.
|
@@ -22,11 +22,30 @@ pub struct DocJsDraft {
|
||||
pub script: String,
|
||||
}
|
||||
|
||||
/// What the user is told when a form's scripts were turned off: in a toast as it happens, then
|
||||
/// on the notice bar for as long as the document is open.
|
||||
pub(crate) fn scripts_off_notice() -> String {
|
||||
tl!("This form's scripts are turned off because one ran too long. Reopen the document to run them again.").to_string()
|
||||
}
|
||||
|
||||
impl PdfCraftApp {
|
||||
/// Act on what scripts produced in document `id`: alerts are shown, console output goes to
|
||||
/// the console, print and page requests are carried out, links wait for the user's permission
|
||||
/// (form submissions are reported, never sent).
|
||||
pub fn handle_js(&mut self, id: DocId, out: JsOutput) {
|
||||
let view = self.views.iter().position(|v| v.id == id);
|
||||
// A script that ran away turned the form's scripts off: calculations and buttons stop
|
||||
// working, and the error is only in the console. Say so once, and bring the notice bar
|
||||
// (which keeps saying so) back if it was closed. A message of the form's own, below,
|
||||
// takes the toast's place.
|
||||
if let Some(v) = view.and_then(|i| self.views.get_mut(i))
|
||||
&& !v.scripts_off_noted
|
||||
&& self.session.get(id).is_some_and(|d| d.xfa_scripts_off())
|
||||
{
|
||||
v.scripts_off_noted = true;
|
||||
v.notice_dismissed = false;
|
||||
self.notify(scripts_off_notice());
|
||||
}
|
||||
if out.is_empty() {
|
||||
return;
|
||||
}
|
||||
@@ -34,7 +53,6 @@ impl PdfCraftApp {
|
||||
for e in &out.errors {
|
||||
self.js_console.log.push(format!("Error: {e}"));
|
||||
}
|
||||
let view = self.views.iter().position(|v| v.id == id);
|
||||
for r in out.requests {
|
||||
match r {
|
||||
Request::Print => self.open_print(),
|
||||
|
||||
@@ -103,6 +103,17 @@ fn ordinary_forms_and_documents_have_no_xfa_notice() {
|
||||
assert_eq!(xfa(&h), Some(pdfcraft_render::Xfa::Dynamic));
|
||||
}
|
||||
|
||||
/// Run frames until the pages are drawn (fields can be clicked then).
|
||||
fn wait_for_pages(h: &mut Harness<'static, PdfCraftApp>) {
|
||||
for _ in 0..40 {
|
||||
h.run_steps(2);
|
||||
if !h.state().render_pending() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
|
||||
/// Click the centre of a field's widget.
|
||||
fn click_field(h: &mut Harness<'static, PdfCraftApp>, name: &str) {
|
||||
let p = {
|
||||
@@ -122,13 +133,7 @@ fn click_field(h: &mut Harness<'static, PdfCraftApp>, name: &str) {
|
||||
#[test]
|
||||
fn xfa_buttons_run_their_scripts_in_the_app() {
|
||||
let mut h = open(pdfcraft_xfa::fixtures::shell(&pdfcraft_xfa::fixtures::scripted_template()));
|
||||
for _ in 0..40 {
|
||||
h.run_steps(2);
|
||||
if !h.state().render_pending() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
}
|
||||
wait_for_pages(&mut h);
|
||||
let names = |h: &Harness<'static, PdfCraftApp>| {
|
||||
let s = h.state();
|
||||
s.session.get(s.views[0].id).unwrap().form.iter().map(|f| f.name.clone()).collect::<Vec<_>>()
|
||||
@@ -152,3 +157,58 @@ fn messages_from_scripts_run_on_open_show_right_away() {
|
||||
let id = h.state().views[0].id;
|
||||
assert!(h.state_mut().session.take_js_output(id).is_empty(), "nothing waits for the next edit");
|
||||
}
|
||||
|
||||
/// A script the engine's own limits don't stop (its loops are in nested calls): it runs until
|
||||
/// it is abandoned, which turns the form's scripts off.
|
||||
const RUNAWAY: &str = "function f() { for (var j = 0; j < 99999; j++) {} } for (var k = 0; k < 5000; k++) f();";
|
||||
const SCRIPTS_OFF: &str = "This form's scripts are turned off because one ran too long";
|
||||
|
||||
fn says_scripts_are_off(toast: &Option<(String, f64)>) -> bool {
|
||||
toast.as_ref().is_some_and(|t| t.0.contains(SCRIPTS_OFF))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_form_whose_scripts_were_turned_off_at_open_says_so() {
|
||||
let tpl = pdfcraft_xfa::fixtures::scripted_template().replace("if (qty.rawValue === null) qty.rawValue = 2;", RUNAWAY);
|
||||
let mut h = open(pdfcraft_xfa::fixtures::shell(&tpl));
|
||||
let id = h.state().views[0].id;
|
||||
assert!(h.state().session.get(id).unwrap().xfa_scripts_off(), "script console: {:?}", h.state().js_console.log);
|
||||
// A toast says so as it happens; the notice bar keeps saying so after the toast is gone.
|
||||
assert!(says_scripts_are_off(&h.state().toast), "{:?}", h.state().toast);
|
||||
h.state_mut().toast = None;
|
||||
h.run_steps(2);
|
||||
assert!(h.query_by_label_contains(SCRIPTS_OFF).is_some());
|
||||
// The bar leads to the console, where the script's error is.
|
||||
h.get_by_label("JavaScript Console").click();
|
||||
h.run_steps(2);
|
||||
assert_eq!(h.state().dialog, Some(pdfcraft_ui_egui::Dialog::JsConsole));
|
||||
assert!(h.state().js_console.log.iter().any(|l| l.contains("abandoned")), "{:?}", h.state().js_console.log);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scripts_turned_off_by_a_click_bring_a_closed_notice_bar_back() {
|
||||
let tpl = pdfcraft_xfa::fixtures::scripted_template().replace(r#"xfa.host.messageBox("Hello " + qty.rawValue);"#, RUNAWAY);
|
||||
let mut h = open(pdfcraft_xfa::fixtures::shell(&tpl));
|
||||
wait_for_pages(&mut h);
|
||||
// The form opens with its scripts on, and the user closes the notice bar.
|
||||
let id = h.state().views[0].id;
|
||||
assert!(!h.state().session.get(id).unwrap().xfa_scripts_off());
|
||||
assert!(h.query_by_label_contains(SCRIPTS_OFF).is_none() && !says_scripts_are_off(&h.state().toast));
|
||||
h.get_by_label("Dismiss").click();
|
||||
h.run_steps(2);
|
||||
assert!(h.state().views[0].notice_dismissed);
|
||||
// The button's script runs away: a toast says the scripts are off and the bar is back.
|
||||
click_field(&mut h, "hello");
|
||||
assert!(h.state().session.get(id).unwrap().xfa_scripts_off(), "script console: {:?}", h.state().js_console.log);
|
||||
assert!(says_scripts_are_off(&h.state().toast), "{:?}", h.state().toast);
|
||||
assert!(!h.state().views[0].notice_dismissed);
|
||||
h.state_mut().toast = None;
|
||||
h.run_steps(2);
|
||||
assert!(h.query_by_label_contains(SCRIPTS_OFF).is_some());
|
||||
// It is said once: closed again, the bar stays closed through what the user does next.
|
||||
h.get_by_label("Dismiss").click();
|
||||
h.run_steps(2);
|
||||
click_field(&mut h, "addRow");
|
||||
assert!(h.state().views[0].notice_dismissed);
|
||||
assert!(!says_scripts_are_off(&h.state().toast), "{:?}", h.state().toast);
|
||||
}
|
||||
@@ -5184,8 +5184,8 @@ milestone = "M12"
|
||||
status = "partial"
|
||||
commands = ["file.open"]
|
||||
tools = ["doc_open", "doc_info", "form_fields", "form_fill", "js_run"]
|
||||
evidence = ["crates/xfa/src/tests.rs::layout_places_captions_fields_and_sizes_containers_to_their_content", "crates/xfa/src/tests.rs::tables_break_across_pages_and_repeat_their_header_row", "crates/xfa/src/tests.rs::rendering_into_the_pdf_replaces_the_placeholder_page_and_adds_fields", "crates/engine/src/tests.rs::dynamic_xfa_forms_are_laid_out_on_open_filled_and_saved_incrementally", "crates/automation/tests/automation.rs::dynamic_xfa_forms_open_render_fill_and_save_through_tools", "crates/ui-egui/tests/ui_group/xfa.rs::a_dynamic_xfa_form_is_laid_out_and_filled", "crates/xfa/src/tests.rs::nested_width_less_subforms_lay_out_in_polynomial_time", "crates/js/src/tests.rs::instance_managers_add_remove_and_count", "crates/xfa/src/tests.rs::the_form_tree_carries_values_presence_instances_and_the_events", "crates/engine/src/tests.rs::xfa_scripts_initialize_calculate_validate_toggle_and_add_rows", "crates/automation/tests/automation.rs::xfa_scripts_run_for_buttons_and_field_changes_through_tools", "crates/ui-egui/tests/ui_group/xfa.rs::xfa_buttons_run_their_scripts_in_the_app", "crates/engine/src/tests.rs::hostile_xfa_open_scripts_are_merged_capped_kept_from_printing_and_noted", "crates/engine/src/tests.rs::runaway_xfa_calculations_at_open_end_quickly_with_a_report", "crates/xfa/src/tests.rs::the_live_form_of_nested_repeating_subforms_stops_at_its_node_budget", "crates/xfa/src/tests.rs::data_ops_of_one_event_are_one_datasets_rewrite", "crates/xfa/src/tests.rs::choice_lists_signatures_passwords_and_pictures_become_fields_and_images", "crates/xfa/src/tests.rs::choice_values_come_from_the_data_as_saved_values_and_several_for_lists", "crates/xfa/src/tests.rs::pictures_are_sized_from_their_headers_and_hostile_ones_are_refused_unallocated", "crates/engine/src/tests.rs::xfa_choice_lists_signature_and_picture_fields_open_fill_script_and_save", "crates/xfa/src/tests.rs::repeating_subforms_inside_positioned_content_get_their_instances", "crates/engine/src/tests.rs::xfa_rows_in_areas_are_laid_out_read_calculated_and_written_where_adobe_binds_them"]
|
||||
notes = "laid out from the template on open by the xfa crate (positioned, tb, lr-tb, table/row, page masters, breaks, repeated header rows, captions, rich text, JPEG/PNG/GIF images) into real pages and AcroForm fields (text, password, date with AFDate formats, check boxes, radio groups, drop-downs and list boxes with shown/saved item pairs and multi-select, signature fields, image fields showing their picture (pictures decoded once per document, at most 20 million pixels, and reused across re-layouts), buttons with reset/print/save/URL), filled from the datasets packet (including rows another viewer added), saved as an incremental revision with the values merged into the datasets so Adobe's viewers show them; doc_info reports xfa_layout. XFA JavaScript and FormCalc (form.xfa-formcalc) run in the js crate's XFA object model (xfa.form, SOM resolution, rawValue, presence, access, instance managers, xfa.host, xfa.layout, xfa.event): initialize and calculate on open, exit, validate and calculate after a field changes, click for buttons (through js_run and the app), with rows added and removed, subforms shown and hidden, and the form laid out again, all as one undo step and kept in the file. Hostile scripts are bounded per event (merged and capped effects, messages and console lines, lower loop limits and a time budget on open and calculate, one datasets rewrite per event, print/save/links/focus from clicks only, a runaway script abandoned and the form's scripts turned off) and what open-time scripts changed is noted. Missing: loops inside nested calls are only stopped by abandoning their thread, and script memory is not capped; the change event per keystroke; xfa.template, connections and data descriptions; a hidden instance of a repeating subform hides them all; choosing a new picture for an image field; barcodes are drawn as their text, not bars; keep rules; metrics are approximate and Acrobat fidelity is eyeballed on real forms, not measured (#60)"
|
||||
evidence = ["crates/xfa/src/tests.rs::layout_places_captions_fields_and_sizes_containers_to_their_content", "crates/xfa/src/tests.rs::tables_break_across_pages_and_repeat_their_header_row", "crates/xfa/src/tests.rs::rendering_into_the_pdf_replaces_the_placeholder_page_and_adds_fields", "crates/engine/src/tests.rs::dynamic_xfa_forms_are_laid_out_on_open_filled_and_saved_incrementally", "crates/automation/tests/automation.rs::dynamic_xfa_forms_open_render_fill_and_save_through_tools", "crates/ui-egui/tests/ui_group/xfa.rs::a_dynamic_xfa_form_is_laid_out_and_filled", "crates/xfa/src/tests.rs::nested_width_less_subforms_lay_out_in_polynomial_time", "crates/js/src/tests.rs::instance_managers_add_remove_and_count", "crates/xfa/src/tests.rs::the_form_tree_carries_values_presence_instances_and_the_events", "crates/engine/src/tests.rs::xfa_scripts_initialize_calculate_validate_toggle_and_add_rows", "crates/automation/tests/automation.rs::xfa_scripts_run_for_buttons_and_field_changes_through_tools", "crates/ui-egui/tests/ui_group/xfa.rs::xfa_buttons_run_their_scripts_in_the_app", "crates/engine/src/tests.rs::hostile_xfa_open_scripts_are_merged_capped_kept_from_printing_and_noted", "crates/engine/src/tests.rs::runaway_xfa_calculations_at_open_end_quickly_with_a_report", "crates/ui-egui/tests/ui_group/xfa.rs::a_form_whose_scripts_were_turned_off_at_open_says_so", "crates/ui-egui/tests/ui_group/xfa.rs::scripts_turned_off_by_a_click_bring_a_closed_notice_bar_back", "crates/automation/tests/automation.rs::doc_info_says_when_a_runaway_script_turned_the_forms_scripts_off", "crates/xfa/src/tests.rs::the_live_form_of_nested_repeating_subforms_stops_at_its_node_budget", "crates/xfa/src/tests.rs::data_ops_of_one_event_are_one_datasets_rewrite", "crates/xfa/src/tests.rs::choice_lists_signatures_passwords_and_pictures_become_fields_and_images", "crates/xfa/src/tests.rs::choice_values_come_from_the_data_as_saved_values_and_several_for_lists", "crates/xfa/src/tests.rs::pictures_are_sized_from_their_headers_and_hostile_ones_are_refused_unallocated", "crates/engine/src/tests.rs::xfa_choice_lists_signature_and_picture_fields_open_fill_script_and_save", "crates/xfa/src/tests.rs::repeating_subforms_inside_positioned_content_get_their_instances", "crates/engine/src/tests.rs::xfa_rows_in_areas_are_laid_out_read_calculated_and_written_where_adobe_binds_them"]
|
||||
notes = "laid out from the template on open by the xfa crate (positioned, tb, lr-tb, table/row, page masters, breaks, repeated header rows, captions, rich text, JPEG/PNG/GIF images) into real pages and AcroForm fields (text, password, date with AFDate formats, check boxes, radio groups, drop-downs and list boxes with shown/saved item pairs and multi-select, signature fields, image fields showing their picture (pictures decoded once per document, at most 20 million pixels, and reused across re-layouts), buttons with reset/print/save/URL), filled from the datasets packet (including rows another viewer added), saved as an incremental revision with the values merged into the datasets so Adobe's viewers show them; doc_info reports xfa_layout. XFA JavaScript and FormCalc (form.xfa-formcalc) run in the js crate's XFA object model (xfa.form, SOM resolution, rawValue, presence, access, instance managers, xfa.host, xfa.layout, xfa.event): initialize and calculate on open, exit, validate and calculate after a field changes, click for buttons (through js_run and the app), with rows added and removed, subforms shown and hidden, and the form laid out again, all as one undo step and kept in the file. Hostile scripts are bounded per event (merged and capped effects, messages and console lines, lower loop limits and a time budget on open and calculate, one datasets rewrite per event, print/save/links/focus from clicks only, a runaway script abandoned and the form's scripts turned off, which the app says in a toast and on the notice bar, with a button to the JavaScript console, and doc_info reports as xfa_scripts_off) and what open-time scripts changed is noted. Missing: loops inside nested calls are only stopped by abandoning their thread, and script memory is not capped; the change event per keystroke; xfa.template, connections and data descriptions; a hidden instance of a repeating subform hides them all; choosing a new picture for an image field; barcodes are drawn as their text, not bars; keep rules; metrics are approximate and Acrobat fidelity is eyeballed on real forms, not measured (#60)"
|
||||
|
||||
[[feature]]
|
||||
id = "form.xfa-formcalc"
|
||||
|
||||
Reference in new issue
Block a user