diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 16ae63490..eb9244a28 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -4502,11 +4502,8 @@ fn notices( // 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 { + // What the document is as a form, if it is one: what the bar says first, secured or not. + let form = if let Some(x) = xfa { let lang = crate::i18n::current(); let pages = crate::i18n::trn(lang, x.pages as u64, "{n} page", "{n} pages"); let fields = crate::i18n::trn(lang, x.fields as u64, "{n} field", "{n} fields"); @@ -4537,6 +4534,21 @@ fn notices( crate::i18n::fmt(tl!("This document contains {n} interactive form fields."), &[("n", &info.fields.len().to_string())]), true, )) + } else { + None + }; + let msg = if scripts_off { + Some(("triangle-alert", crate::js_ui::scripts_off_notice(), true)) + } else if secured { + // A secured form still says what it is, and keeps Highlight fields: the security + // sentence follows the form's. + let security = tl!("This document is secured. Some changes are restricted by its security settings."); + Some(match form { + Some((icon, text, fields)) => (icon, format!("{text} {security}"), fields), + None => ("lock", security.to_string(), false), + }) + } else if form.is_some() { + form } else if repaired { Some(("bandage", tl!("This file was damaged and has been repaired. Saving keeps the repaired version.").to_string(), false)) } else if !info.warnings.is_empty() { diff --git a/crates/ui-egui/tests/editing.rs b/crates/ui-egui/tests/editing.rs index 2ef52a440..d41041e52 100644 --- a/crates/ui-egui/tests/editing.rs +++ b/crates/ui-egui/tests/editing.rs @@ -2024,7 +2024,12 @@ fn protected(user: &str, owner: &str, permissions: i32) -> Vec { } fn protected_with(algorithm: pdfcraft_cos::Algorithm, user: &str, owner: &str, permissions: i32) -> Vec { - let mut doc = pdfcraft_cos::Document::open(std::sync::Arc::new(fixture(2))).unwrap(); + encrypted(fixture(2), algorithm, user, owner, permissions) +} + +/// `bytes` rewritten encrypted. +fn encrypted(bytes: Vec, algorithm: pdfcraft_cos::Algorithm, user: &str, owner: &str, permissions: i32) -> Vec { + let mut doc = pdfcraft_cos::Document::open(std::sync::Arc::new(bytes)).unwrap(); doc.set_encryption(&pdfcraft_cos::NewEncryption { algorithm, user_password: user, @@ -2146,6 +2151,25 @@ fn remove_security_asks_for_the_permissions_password_first() { assert!(doc.dirty && doc.security_summary().is_none(), "security is removed (written by the next save)"); } +/// A secured form still says it is one and offers Highlight fields: the security notice joins +/// the form's instead of replacing it. +#[test] +fn a_secured_form_shows_its_form_notice_and_its_security() { + // Changing and assembling withheld, filling allowed (as government forms are often set). + let bytes = encrypted(include_bytes!("data/form.pdf").to_vec(), pdfcraft_cos::Algorithm::Aes256, "", "owner", -1036); + let mut h = Harness::builder().with_size(egui::vec2(1600.0, 900.0)).build_eframe(move |_cc| { + let mut app = PdfCraftApp::new(); + app.set_option("language", "en").unwrap(); + app.open_bytes("form.pdf", None, bytes).unwrap(); + app + }); + h.run_steps(4); + h.get_by_label_contains("interactive form fields. This document is secured. Some changes are restricted by its security settings."); + h.get_by_label("Highlight fields"); + h.get_by_label("Security settings"); + h.get_by_label("Unlock…"); +} + #[test] fn restricted_documents_show_a_notice_and_block_page_changes() { let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| { diff --git a/crates/ui-egui/tests/ui_group/xfa.rs b/crates/ui-egui/tests/ui_group/xfa.rs index 2285f36e0..6565a9816 100644 --- a/crates/ui-egui/tests/ui_group/xfa.rs +++ b/crates/ui-egui/tests/ui_group/xfa.rs @@ -212,3 +212,22 @@ fn scripts_turned_off_by_a_click_bring_a_closed_notice_bar_back() { assert!(h.state().views[0].notice_dismissed); assert!(!says_scripts_are_off(&h.state().toast), "{:?}", h.state().toast); } + +#[test] +fn a_secured_dynamic_xfa_form_shows_its_layout_and_its_security() { + // Changing and assembling withheld, filling allowed. + let mut doc = pdfcraft_cos::Document::open(std::sync::Arc::new(pdfcraft_xfa::fixtures::shell(&pdfcraft_xfa::fixtures::template(2)))).unwrap(); + doc.set_encryption(&pdfcraft_cos::NewEncryption { + algorithm: pdfcraft_cos::Algorithm::Aes256, + user_password: "", + owner_password: "owner", + permissions: -1036, + encrypt_metadata: true, + seed: [4; 32], + }) + .unwrap(); + let h = open(pdfcraft_cos::write_full(&doc, &Default::default()).unwrap()); + h.get_by_label_contains("laid out from its template: 2 pages, 11 fields. This document is secured."); + h.get_by_label("Highlight fields"); + h.get_by_label("Security settings"); +} diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index e19efb0a0..58cc527ea 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -1802,8 +1802,8 @@ milestone = "M3" status = "shipped" commands = [] tools = [] -evidence = ["crates/ui-egui/tests/editing.rs::restricted_documents_show_a_notice_and_block_page_changes"] -notes = "" +evidence = ["crates/ui-egui/tests/editing.rs::restricted_documents_show_a_notice_and_block_page_changes", "crates/ui-egui/tests/editing.rs::a_secured_form_shows_its_form_notice_and_its_security", "crates/ui-egui/tests/ui_group/xfa.rs::a_secured_dynamic_xfa_form_shows_its_layout_and_its_security"] +notes = "On a secured form (fields or XFA), the security sentence follows the form's notice, which keeps Highlight fields and the XFA layout notes." [[feature]] id = "view.pdfa-view-mode" @@ -4392,8 +4392,8 @@ milestone = "M3" status = "shipped" commands = [] tools = [] -evidence = ["crates/ui-egui/tests/view.rs::highlight_fields_tints_the_field_area", "crates/ui-egui/tests/view.rs::required_radio_buttons_get_a_round_red_border", "crates/ui-egui/tests/view.rs::field_highlighting_is_remembered"] -notes = "remembered for the next document and session; required fields get a red border (round for radio buttons); the tint is drawn over the field, where Acrobat paints the highlight colour behind it" +evidence = ["crates/ui-egui/tests/view.rs::highlight_fields_tints_the_field_area", "crates/ui-egui/tests/view.rs::required_radio_buttons_get_a_round_red_border", "crates/ui-egui/tests/view.rs::field_highlighting_is_remembered", "crates/ui-egui/tests/editing.rs::a_secured_form_shows_its_form_notice_and_its_security"] +notes = "offered on secured forms too; remembered for the next document and session; required fields get a red border (round for radio buttons); the tint is drawn over the field, where Acrobat paints the highlight colour behind it" [[feature]] id = "form.field-appearance-display"