From 3dae8491750f77db601a6db296f78deb360460bd Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 30 Oct 2026 22:10:53 -0700 Subject: [PATCH] Redact: Save As after Undo past saved redactions proposes the name without _Redacted After Undo past a saved Apply redactions, Save asks where but proposed the redacted copy's own name, so the dialog's default was the redacted file. It now proposes the name without _Redacted. --- crates/ui-egui/src/editing.rs | 2 +- crates/ui-egui/src/editing/save.rs | 17 +++++++++++++++++ crates/ui-egui/src/editing/tests.rs | 13 ++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/editing.rs b/crates/ui-egui/src/editing.rs index 2e7c11b935..600f5e2c47 100644 --- a/crates/ui-egui/src/editing.rs +++ b/crates/ui-egui/src/editing.rs @@ -20,7 +20,7 @@ pub use save::{SaveTarget, write_atomically}; #[cfg(test)] pub(crate) use save::{is_mail_temp_copy, save_dialog_folder}; #[cfg(test)] -use save::{write_atomically_syncing, write_atomically_with}; +use save::{unredacted_file_name, write_atomically_syncing, write_atomically_with}; /// Text typed into a new text box that isn't added to the page yet. fn new_text_typed(v: &crate::DocView) -> bool { diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index 2e9e8e72ab..41f647ff92 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -62,6 +62,9 @@ impl PdfCraftApp { .views .get(index) .is_some_and(|v| v.redactions_undone_after_save > 0 || v.saved_redactions_cut_off || v.redactions_applied < v.redactions_downloaded); + // The restored content is not a redacted copy: propose the name without `_Redacted`, so + // the dialog's default is not the redacted file. + let name = if unredacted && !redacted { unredacted_file_name(&name) } else { name }; let target = if not_delivered || unredacted { SaveTarget::As } else { target }; #[cfg(not(target_arch = "wasm32"))] { @@ -257,6 +260,20 @@ pub(crate) fn is_mail_temp_copy(path: &str) -> bool { || p.contains("/library/containers/com.apple.mail/data/library/mail downloads/") } +/// `name` without the `_Redacted` that [`pdfcraft_engine::redacted_file_name`] adds before the +/// extension ("contract_Redacted.pdf" gives "contract.pdf"). Any other name is returned as it is. +pub(crate) fn unredacted_file_name(name: &str) -> String { + let cut = name.len().saturating_sub(4); + let (stem, ext) = match (name.get(..cut), name.get(cut..)) { + (Some(stem), Some(ext)) if ext.eq_ignore_ascii_case(".pdf") => (stem, ext), + _ => (name, ""), + }; + match stem.strip_suffix("_Redacted") { + Some(base) if !base.is_empty() => format!("{base}{ext}"), + _ => name.to_string(), + } +} + /// The folder a Save As style dialog should open in for a document stored at `path`: the /// document's own folder, as Acrobat and Foxit do, instead of whichever folder the system picks. /// `None` (keep the system's choice) for a new document, a path with no parent, or an email diff --git a/crates/ui-egui/src/editing/tests.rs b/crates/ui-egui/src/editing/tests.rs index 76886ff54f..d848e841c5 100644 --- a/crates/ui-egui/src/editing/tests.rs +++ b/crates/ui-egui/src/editing/tests.rs @@ -1,4 +1,15 @@ -use super::{SaveTarget, is_mail_temp_copy, save_dialog_folder, write_atomically, write_atomically_syncing, write_atomically_with}; +use super::{ + SaveTarget, is_mail_temp_copy, save_dialog_folder, unredacted_file_name, write_atomically, write_atomically_syncing, write_atomically_with, +}; + +#[test] +fn restored_content_is_not_proposed_under_the_redacted_name() { + assert_eq!(unredacted_file_name("contract_Redacted.pdf"), "contract.pdf"); + assert_eq!(unredacted_file_name("SCAN_Redacted.PDF"), "SCAN.PDF"); + assert_eq!(unredacted_file_name("Untitled_Redacted"), "Untitled"); + assert_eq!(unredacted_file_name("contract.pdf"), "contract.pdf"); + assert_eq!(unredacted_file_name("_Redacted.pdf"), "_Redacted.pdf"); +} #[test] fn save_dialogs_open_in_the_documents_folder() {