From 07a5f3f3a133a46fe0792783a6233b67471decd4 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:08:47 +0800 Subject: [PATCH 01/10] M5: the first save after placing a signature asks to lock it into the page (#2149) A Fill & Sign signature stays a removable comment unless Flatten Fill & Sign when saving is on, which sat only in Preferences. The first save after a signature or initials is placed now asks: Lock and save flattens the document's Fill & Sign marks, Keep editable saves as before, Cancel saves nothing, and Don't ask again turns the answer into the preference. The Sign menu carries the same switch. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/app.rs | 4 ++ crates/ui-egui/src/app/prefs.rs | 4 ++ crates/ui-egui/src/dialogs.rs | 39 +++++++++++++ crates/ui-egui/src/editing.rs | 7 +++ crates/ui-egui/src/editing/save.rs | 47 +++++++++++++++ crates/ui-egui/src/i18n/ar.tsv | 7 +++ crates/ui-egui/src/i18n/bg.tsv | 7 +++ crates/ui-egui/src/i18n/cs.tsv | 7 +++ crates/ui-egui/src/i18n/de.tsv | 7 +++ crates/ui-egui/src/i18n/el.tsv | 7 +++ crates/ui-egui/src/i18n/es.tsv | 7 +++ crates/ui-egui/src/i18n/fr.tsv | 7 +++ crates/ui-egui/src/i18n/hu.tsv | 7 +++ crates/ui-egui/src/i18n/it.tsv | 7 +++ crates/ui-egui/src/i18n/ja.tsv | 7 +++ crates/ui-egui/src/i18n/kk.tsv | 7 +++ crates/ui-egui/src/i18n/lt.tsv | 7 +++ crates/ui-egui/src/i18n/pl.tsv | 7 +++ crates/ui-egui/src/i18n/pt-br.tsv | 7 +++ crates/ui-egui/src/i18n/ru.tsv | 7 +++ crates/ui-egui/src/i18n/sl.tsv | 7 +++ crates/ui-egui/src/i18n/te.tsv | 8 +++ crates/ui-egui/src/i18n/tr.tsv | 7 +++ crates/ui-egui/src/i18n/uk.tsv | 7 +++ crates/ui-egui/src/i18n/zh-hans.tsv | 7 +++ crates/ui-egui/src/i18n/zh-hant.tsv | 8 +++ crates/ui-egui/src/lib.rs | 9 +++ crates/ui-egui/src/state.rs | 3 + crates/ui-egui/src/tools/fill_sign.rs | 2 + crates/ui-egui/src/workflows/js_ui.rs | 4 ++ crates/ui-egui/tests/it/fill_sign.rs | 84 ++++++++++++++++++++++++++- docs/target-app-parity.md | 2 +- parity/acrobat-features.toml | 4 +- 33 files changed, 354 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/app.rs b/crates/ui-egui/src/app.rs index 43d0066e63..29801c4b76 100644 --- a/crates/ui-egui/src/app.rs +++ b/crates/ui-egui/src/app.rs @@ -106,6 +106,10 @@ impl PdfCraftApp { system_text_scale: 1.0, ui_scale_applied: None, flatten_fill_sign_on_save: false, + ask_to_lock_signatures: true, + placed_signatures: Default::default(), + pending_lock: None, + lock_remember: false, fill_text_size: fill_sign::TEXT_SIZE, fill_text_color: [0.0; 3], dialog: None, diff --git a/crates/ui-egui/src/app/prefs.rs b/crates/ui-egui/src/app/prefs.rs index 7bd8943774..5e35547035 100644 --- a/crates/ui-egui/src/app/prefs.rs +++ b/crates/ui-egui/src/app/prefs.rs @@ -101,6 +101,7 @@ impl PdfCraftApp { // Null follows the system's text scaling. "ui_scale": self.ui_scale, "flatten_fill_sign": self.flatten_fill_sign_on_save, + "ask_to_lock_signatures": self.ask_to_lock_signatures, "fill_text_size": self.fill_text_size, "date_format": self.session.date_format(), // Null follows the interface language. @@ -284,6 +285,9 @@ impl PdfCraftApp { if let Some(on) = v["flatten_fill_sign"].as_bool() { self.flatten_fill_sign_on_save = on; } + if let Some(on) = v["ask_to_lock_signatures"].as_bool() { + self.ask_to_lock_signatures = on; + } if let Some(size) = v["fill_text_size"].as_f64().and_then(fill_sign::valid_text_size) { self.fill_text_size = size; } diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 42bed83657..30985fab28 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -50,6 +50,7 @@ struct Pending { revert_now: bool, overwrite_now: bool, overwrite_elsewhere: bool, + lock_answer: Option, summarize_now: bool, optimize_now: bool, duplicate_now: Option, @@ -275,6 +276,11 @@ fn dialog_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, dialog: Dialog, req: &m return false; } } + Dialog::LockSignatures => { + if !dialog_lock_signatures(ui, app, req) { + return false; + } + } Dialog::Revert => { if !dialog_revert(ui, app, req) { return false; @@ -1246,6 +1252,31 @@ fn dialog_summarize_comments(ui: &mut egui::Ui, app: &mut PdfCraftApp, req: &mut false } +/// `Dialog::LockSignatures` content (#2149). Returns `false` when it has its own buttons and no +/// footer is wanted. +fn dialog_lock_signatures(ui: &mut egui::Ui, app: &mut PdfCraftApp, req: &mut Pending) -> bool { + ui.label(egui::RichText::new(tl!("Lock your signature into the page?")).font(theme::semibold(18.0))); + ui.add_space(8.0); + ui.label(tl!("A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page.")); + ui.add_space(8.0); + ui.add(widgets::checkbox(&mut app.lock_remember, tl!("Don't ask again"))); + ui.add_space(12.0); + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + if widgets::pill_button(ui, tl!("Lock and save"), true).clicked() { + req.lock_answer = Some(true); + req.close = true; + } + if widgets::pill_button(ui, tl!("Keep editable"), false).clicked() { + req.lock_answer = Some(false); + req.close = true; + } + if widgets::pill_button(ui, tl!("Cancel"), false).clicked() { + req.close = true; + } + }); + false +} + /// `Dialog::Revert` content. Returns `false` when it has its own buttons and no footer is wanted. fn dialog_revert(ui: &mut egui::Ui, app: &mut PdfCraftApp, req: &mut Pending) -> bool { let name = app.active_ids().and_then(|(_, id)| app.session.get(id)).map(|d| d.name.clone()).unwrap_or_default(); @@ -1762,6 +1793,14 @@ fn finish_requests(app: &mut PdfCraftApp, dialog: Dialog, modal_closed: bool, re if dialog == Dialog::OverwriteChanged && (modal_closed || req.close) { app.pending_overwrite = None; } + if let Some(lock) = req.lock_answer { + let remember = std::mem::take(&mut app.lock_remember); + app.answer_lock_signatures(lock, remember); + } else if dialog == Dialog::LockSignatures && (modal_closed || req.close) { + // Cancelled or dismissed: nothing is saved. + app.pending_lock = None; + app.lock_remember = false; + } } /// Runs the document actions the dialog asked for. diff --git a/crates/ui-egui/src/editing.rs b/crates/ui-egui/src/editing.rs index b810e2abe5..f753335b72 100644 --- a/crates/ui-egui/src/editing.rs +++ b/crates/ui-egui/src/editing.rs @@ -10,6 +10,7 @@ mod close; mod save; pub use close::CloseRequest; +pub use save::PendingLock; #[cfg(not(target_arch = "wasm32"))] pub use save::PendingOverwrite; #[cfg(target_arch = "wasm32")] @@ -669,12 +670,18 @@ impl PdfCraftApp { fn apply_queued_edit(&mut self, i: usize) -> bool { let Some(edit) = self.views.get_mut(i).and_then(|v| v.pending_edit.take()) else { return true }; let signature_page = self.views.get_mut(i).and_then(|v| v.fill_signature_page.take()); + let doc_id = self.views.get(i).map(|v| v.id); let committed = self.views.get_mut(i).and_then(|v| v.forms.committed.take()); let typed = match (&edit, &committed) { (Edit::SetFieldValue { name, .. }, Some(draft)) => *name == draft.name, _ => false, }; if self.apply_edit(edit) { + if signature_page.is_some() + && let Some(id) = doc_id + { + self.placed_signatures.insert(id); + } // Tab can wrap to the same field (including another widget of that field). // Its new editor was opened before this edit ran, so it still holds the old // value. Refresh from the accepted form before Save can commit that stale draft. diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index f685247c9d..446aba1fc5 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -11,6 +11,14 @@ pub struct PendingOverwrite { after: Box, } +/// A save held back by [`Dialog::LockSignatures`](crate::Dialog::LockSignatures) (#2149). +pub struct PendingLock { + pub id: pdfcraft_engine::DocId, + target: SaveTarget, + /// What was to run once the document is written (close its tab, quit). + after: Box, +} + /// Where Save writes. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SaveTarget { @@ -46,6 +54,13 @@ impl PdfCraftApp { return false; } let Some(id) = self.views.get(index).map(|v| v.id) else { return false }; + // A signature just placed with Fill & Sign is a removable comment unless it is flattened: + // ask once whether to lock it into the page (#2149). + if self.ask_to_lock_signatures && !self.flatten_fill_sign_on_save && self.placed_signatures.contains(&id) { + self.pending_lock = Some(PendingLock { id, target, after: Box::new(after) }); + self.dialog = Some(crate::Dialog::LockSignatures); + return false; + } let Some(doc) = self.session.get(id) else { return false }; let (name, path) = (doc.name.clone(), doc.path.clone()); #[cfg(not(target_arch = "wasm32"))] @@ -154,6 +169,37 @@ impl PdfCraftApp { } } + /// The Lock signatures dialog answered (#2149): lock the document's Fill & Sign marks into + /// its pages first when `lock`, then save as was asked. With `remember`, the answer becomes + /// the preference (flatten on every save, or never ask again). + pub(crate) fn answer_lock_signatures(&mut self, lock: bool, remember: bool) { + let Some(p) = self.pending_lock.take() else { return }; + self.placed_signatures.remove(&p.id); + if remember { + if lock { + self.flatten_fill_sign_on_save = true; + } else { + self.ask_to_lock_signatures = false; + } + } + if lock && !self.flatten_fill_sign_on_save { + let before = self.session.get(p.id).map(|d| d.edit_generation()); + if let Err(e) = self.session.apply(p.id, pdfcraft_engine::Edit::FlattenFillSign) { + self.notify_error(e); + return; + } + if before != self.session.get(p.id).map(|d| d.edit_generation()) + && let Some(doc) = self.session.get(p.id) + && let Some(view) = self.views.iter_mut().find(|v| v.id == p.id) + { + view.document_changed(&doc.info); + } + } + if let Some(index) = self.views.iter().position(|v| v.id == p.id) { + self.save_then(index, p.target, p.after); + } + } + /// Write document `id` to `dest` and make it the document's file. Returns `true` if written. #[cfg(not(target_arch = "wasm32"))] fn save_doc_to(&mut self, id: pdfcraft_engine::DocId, dest: &str, target: SaveTarget) -> bool { @@ -192,6 +238,7 @@ impl PdfCraftApp { match self.session.mark_saved(id, bytes, Some(dest.to_string())) { Ok(()) => { self.forget_recovery(id); + self.placed_signatures.remove(&id); if let Some(doc) = self.session.get(id) && let Some(view) = self.views.iter_mut().find(|v| v.id == id) { diff --git a/crates/ui-egui/src/i18n/ar.tsv b/crates/ui-egui/src/i18n/ar.tsv index 1b08929307..50d633bd33 100644 --- a/crates/ui-egui/src/i18n/ar.tsv +++ b/crates/ui-egui/src/i18n/ar.tsv @@ -2278,6 +2278,13 @@ calendar date December ديسمبر Smaller pages صفحات أصغر Reset page size إعادة ضبط حجم الصفحة Flatten Fill & Sign when saving تسطيح التعبئة والتوقيع عند الحفظ + Lock your signature into the page? هل تريد تثبيت توقيعك في الصفحة؟ + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. التوقيع الموضوع تعليق إلى أن يُثبَّت: يستطيع أي شخص لديه محرر PDF نقله أو إزالته. التثبيت يجعل نصوص التعبئة والتوقيع وعلاماتها وتواقيعها جزءًا من الصفحة. + Don't ask again عدم السؤال مرة أخرى + Lock and save التثبيت والحفظ + Keep editable إبقاؤه قابلًا للتحرير + Lock into the page when saving التثبيت في الصفحة عند الحفظ + Otherwise, ask on the first save after a signature is placed وإلا فاسأل عند أول حفظ بعد وضع توقيع Text, marks and signatures become part of the page. Other comments stay editable. النص والعلامات والتوقيعات تصبح جزءًا من الصفحة. التعليقات الأخرى تبقى قابلة للتحرير. Date format تنسيق التاريخ Fill & Sign dates تواريخ التعبئة والتوقيع diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index e269febf18..56cd2f3fd1 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -138,6 +138,13 @@ Side panels Странични панели Enable Acrobat JavaScript Включи Acrobat JavaScript Flatten Fill & Sign when saving Сплескай „Попълни и подпиши“ при запазване + Lock your signature into the page? Да се заключи ли подписът ви в страницата? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Поставеният подпис е коментар, докато не бъде заключен: всеки с PDF редактор може да го премести или премахне. Заключването прави текста, отметките и подписите от „Попълване и подписване“ част от страницата. + Don't ask again Не питай отново + Lock and save Заключване и запис + Keep editable Да остане редактируем + Lock into the page when saving Заключване в страницата при запис + Otherwise, ask on the first save after a signature is placed Иначе питай при първия запис след поставяне на подпис Text, marks and signatures become part of the page. Other comments stay editable. Текстът, маркировките и подписите стават част от страницата. Другите коментари остават редактируеми. OK OK Cancel Отказ diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 22f6e3e056..89da6a9309 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -2299,6 +2299,13 @@ calendar date December prosince Smaller pages Menší stránky Reset page size Obnovit velikost stránek Flatten Fill & Sign when saving Při ukládání sloučit obsah Vyplnit a podepsat se stránkou + Lock your signature into the page? Zamknout podpis do stránky? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Umístěný podpis je komentář, dokud není zamčen: kdokoli s editorem PDF ho může přesunout nebo odstranit. Zamčení udělá z textu, značek a podpisů Vyplnit a podepsat součást stránky. + Don't ask again Už se neptat + Lock and save Zamknout a uložit + Keep editable Ponechat upravitelné + Lock into the page when saving Při ukládání zamknout do stránky + Otherwise, ask on the first save after a signature is placed Jinak se zeptat při prvním uložení po umístění podpisu Text, marks and signatures become part of the page. Other comments stay editable. Text, značky a podpisy se stanou součástí stránky. Ostatní komentáře zůstanou upravitelné. Date format Formát data Fill & Sign dates Datum pro Vyplnit a podepsat diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 2c9ce7ae36..a80eb7b98d 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -71,6 +71,13 @@ Side panels Seitenleisten Enable Acrobat JavaScript Acrobat-JavaScript aktivieren Flatten Fill & Sign when saving Ausfüllen und unterschreiben beim Speichern reduzieren + Lock your signature into the page? Unterschrift fest in die Seite übernehmen? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Eine platzierte Unterschrift ist ein Kommentar, bis sie fest übernommen wird: Jeder mit einem PDF-Editor kann sie verschieben oder entfernen. Beim Übernehmen werden Text, Markierungen und Unterschriften aus „Ausfüllen und unterschreiben“ Teil der Seite. + Don't ask again Nicht mehr fragen + Lock and save Übernehmen und speichern + Keep editable Bearbeitbar lassen + Lock into the page when saving Beim Speichern fest in die Seite übernehmen + Otherwise, ask on the first save after a signature is placed Andernfalls beim ersten Speichern nach dem Platzieren einer Unterschrift fragen Text, marks and signatures become part of the page. Other comments stay editable. Text, Markierungen und Unterschriften werden Teil der Seite. Andere Kommentare bleiben bearbeitbar. OK OK Join our Discord Unserem Discord beitreten diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index d83bf4aef7..9952012e7a 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -286,6 +286,13 @@ Failed Αποτυχία Page {p} of {n} Σελίδα {p} από {n} Flatten Fill & Sign when saving Ενσωμάτωση συμπληρώσεων και υπογραφών κατά την αποθήκευση + Lock your signature into the page? Κλείδωμα της υπογραφής σας στη σελίδα; + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Μια τοποθετημένη υπογραφή είναι σχόλιο μέχρι να κλειδωθεί: οποιοσδήποτε με επεξεργαστή PDF μπορεί να τη μετακινήσει ή να την αφαιρέσει. Το κλείδωμα κάνει το κείμενο, τα σημάδια και τις υπογραφές της Συμπλήρωσης και υπογραφής μέρος της σελίδας. + Don't ask again Να μην ερωτηθώ ξανά + Lock and save Κλείδωμα και αποθήκευση + Keep editable Να παραμείνει επεξεργάσιμη + Lock into the page when saving Κλείδωμα στη σελίδα κατά την αποθήκευση + Otherwise, ask on the first save after a signature is placed Διαφορετικά, ερώτηση στην πρώτη αποθήκευση μετά την τοποθέτηση υπογραφής Text, marks and signatures become part of the page. Other comments stay editable. Το κείμενο, τα σημάδια και οι υπογραφές γίνονται μέρος της σελίδας. Τα υπόλοιπα σχόλια παραμένουν επεξεργάσιμα. Current page — type a page number or label (such as iv) and press Enter Τρέχουσα σελίδα — πληκτρολογήστε αριθμό ή ετικέτα σελίδας (π.χ. iv) και πατήστε Enter Zoom — type a percentage and press Enter Ζουμ — πληκτρολογήστε ένα ποσοστό και πατήστε Enter diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 131d2dacaf..cb6b90f278 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -94,6 +94,13 @@ Side panels Paneles laterales Enable Acrobat JavaScript Activar Acrobat JavaScript Flatten Fill & Sign when saving Acoplar Rellenar y firmar al guardar + Lock your signature into the page? ¿Fijar su firma en la página? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Una firma colocada es un comentario hasta que se fija: cualquiera con un editor de PDF puede moverla o quitarla. Al fijarla, el texto, las marcas y las firmas de Rellenar y firmar pasan a formar parte de la página. + Don't ask again No volver a preguntar + Lock and save Fijar y guardar + Keep editable Mantener editable + Lock into the page when saving Fijar en la página al guardar + Otherwise, ask on the first save after a signature is placed Si no, preguntar al guardar por primera vez tras colocar una firma Text, marks and signatures become part of the page. Other comments stay editable. El texto, las marcas y las firmas pasan a formar parte de la página. Los demás comentarios siguen siendo editables. OK Aceptar Join our Discord Únete a nuestro Discord diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 1c22cf9d81..2cb821d804 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -71,6 +71,13 @@ Side panels Panneaux latéraux Enable Acrobat JavaScript Activer Acrobat JavaScript Flatten Fill & Sign when saving Aplatir Remplir et signer à l'enregistrement + Lock your signature into the page? Intégrer votre signature à la page ? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Une signature placée reste un commentaire tant qu’elle n’est pas intégrée : n’importe qui avec un éditeur PDF peut la déplacer ou la supprimer. L’intégration rend le texte, les marques et les signatures de Remplir et signer partie de la page. + Don't ask again Ne plus demander + Lock and save Intégrer et enregistrer + Keep editable Garder modifiable + Lock into the page when saving Intégrer à la page à l’enregistrement + Otherwise, ask on the first save after a signature is placed Sinon, demander au premier enregistrement après avoir placé une signature Text, marks and signatures become part of the page. Other comments stay editable. Le texte, les marques et les signatures deviennent partie de la page. Les autres commentaires restent modifiables. OK OK Join our Discord Rejoindre notre Discord diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 47d9c24b40..1b9a0c5d78 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -2255,6 +2255,13 @@ action wizard Start Indítás Smaller pages Kisebb oldalak Reset page size Oldalméret visszaállítása Flatten Fill & Sign when saving Kitöltés és aláírás beégetése mentéskor + Lock your signature into the page? Rögzíti az aláírását az oldalba? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Az elhelyezett aláírás megjegyzés, amíg nincs rögzítve: bárki egy PDF-szerkesztővel áthelyezheti vagy eltávolíthatja. A rögzítés a Kitöltés és aláírás szövegét, jeleit és aláírásait az oldal részévé teszi. + Don't ask again Ne kérdezzen többet + Lock and save Rögzítés és mentés + Keep editable Szerkeszthető marad + Lock into the page when saving Mentéskor rögzítés az oldalba + Otherwise, ask on the first save after a signature is placed Különben kérdezzen rá az aláírás elhelyezése utáni első mentéskor Text, marks and signatures become part of the page. Other comments stay editable. A szöveg, a jelek és az aláírások az oldal részévé válnak. A többi megjegyzés szerkeszthető marad. Date format Dátumformátum Fill & Sign dates Kitöltés és aláírás dátumai diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 0cae8f3e98..eb713baefd 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -2215,6 +2215,13 @@ Files are combined in this order. Leave Pages empty to take every page. I file vengono combinati in quest'ordine. Lascia vuoto Pagine per prendere tutte le pagine. Fill & Sign dates Date di Compila e firma Flatten Fill & Sign when saving Appiattisci Compila e firma al salvataggio + Lock your signature into the page? Bloccare la firma nella pagina? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Una firma inserita è un commento finché non viene bloccata: chiunque abbia un editor PDF può spostarla o rimuoverla. Il blocco rende testo, segni e firme di Compila e firma parte della pagina. + Don't ask again Non chiedere più + Lock and save Blocca e salva + Keep editable Mantieni modificabile + Lock into the page when saving Blocca nella pagina al salvataggio + Otherwise, ask on the first save after a signature is placed Altrimenti, chiedi al primo salvataggio dopo aver inserito una firma H / Space (hold) H / Spazio (tieni premuto) Home / End Home / Fine Inserted text Testo inserito diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index abc616b817..9e151bbc62 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -2270,6 +2270,13 @@ @plural {n} page|{n} pages {n} ページ signature pad Type 入力 Flatten Fill & Sign when saving 保存時に入力と署名を統合する + Lock your signature into the page? 署名をページに固定しますか? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 配置した署名は固定するまで注釈です。PDF エディターを持つ人なら誰でも移動や削除ができます。固定すると、入力と署名のテキスト、マーク、署名がページの一部になります。 + Don't ask again 今後は確認しない + Lock and save 固定して保存 + Keep editable 編集可能のままにする + Lock into the page when saving 保存時にページに固定 + Otherwise, ask on the first save after a signature is placed それ以外は、署名を配置した後の最初の保存時に確認する Text, marks and signatures become part of the page. Other comments stay editable. テキスト、マーク、署名がページの一部になります。その他のコメントは引き続き編集できます。 Scroll tabs left タブを左にスクロール Scroll tabs right タブを右にスクロール diff --git a/crates/ui-egui/src/i18n/kk.tsv b/crates/ui-egui/src/i18n/kk.tsv index fc6a2d783a..87b0496d70 100644 --- a/crates/ui-egui/src/i18n/kk.tsv +++ b/crates/ui-egui/src/i18n/kk.tsv @@ -2187,6 +2187,13 @@ calendar date December Желтоқсан Smaller pages Кішірек беттер Reset page size Бет өлшемін қалпына келтіру Flatten Fill & Sign when saving Сақтау кезінде «Толтыру және қол қою» мазмұнын жазықтау + Lock your signature into the page? Қолтаңбаңызды бетке бекіту керек пе? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Қойылған қолтаңба бекітілгенге дейін түсініктеме болып қалады: PDF өңдегіші бар кез келген адам оны жылжытып не жоя алады. Бекіту «Толтыру және қол қою» мәтінін, белгілерін және қолтаңбаларын беттің бөлігіне айналдырады. + Don't ask again Қайта сұрамау + Lock and save Бекіту және сақтау + Keep editable Өңделетін күйде қалдыру + Lock into the page when saving Сақтағанда бетке бекіту + Otherwise, ask on the first save after a signature is placed Әйтпесе, қолтаңба қойылғаннан кейінгі алғашқы сақтауда сұрау Text, marks and signatures become part of the page. Other comments stay editable. Мәтін, белгілер мен қолтаңбалар беттің бөлігіне айналады. Басқа түсініктемелер өңделетін күйде қалады. Date format Күн пішімі Fill & Sign dates «Толтыру және қол қою» күндері diff --git a/crates/ui-egui/src/i18n/lt.tsv b/crates/ui-egui/src/i18n/lt.tsv index fd7f431154..725d8ad22e 100644 --- a/crates/ui-egui/src/i18n/lt.tsv +++ b/crates/ui-egui/src/i18n/lt.tsv @@ -590,6 +590,13 @@ Fixed what could be fixed; 1 problem remains Ištaisyta tai, ką buvo galima; liko 1 problema Fixed what could be fixed; {n} problems remain Ištaisyta tai, ką buvo galima; liko problemų: {n} Flatten Fill & Sign when saving Įrašant suplokštinti pildymo ir pasirašymo turinį + Lock your signature into the page? Įtvirtinti parašą puslapyje? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Padėtas parašas yra komentaras, kol neįtvirtintas: bet kas su PDF redaktoriumi gali jį perkelti ar pašalinti. Įtvirtinus užpildymo ir pasirašymo tekstas, žymės ir parašai tampa puslapio dalimi. + Don't ask again Daugiau neklausti + Lock and save Įtvirtinti ir įrašyti + Keep editable Palikti redaguojamą + Lock into the page when saving Įrašant įtvirtinti puslapyje + Otherwise, ask on the first save after a signature is placed Kitaip klausti pirmą kartą įrašant po parašo padėjimo Flip Horizontal Apversti horizontaliai Flip Vertical Apversti vertikaliai Flip horizontal Apversti horizontaliai diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 208dd1c61e..ee94b2085a 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -2249,6 +2249,13 @@ @plural {n} page|{n} pages {n} strona|{n} strony|{n} stron signature pad Type Wpisz Flatten Fill & Sign when saving Spłaszczaj elementy Wypełnij i podpisz przy zapisie + Lock your signature into the page? Utrwalić podpis na stronie? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Umieszczony podpis jest komentarzem, dopóki nie zostanie utrwalony: każdy z edytorem PDF może go przenieść lub usunąć. Utrwalenie sprawia, że tekst, znaki i podpisy z Wypełnij i podpisz stają się częścią strony. + Don't ask again Nie pytaj ponownie + Lock and save Utrwal i zapisz + Keep editable Zachowaj edytowalny + Lock into the page when saving Utrwalaj na stronie przy zapisie + Otherwise, ask on the first save after a signature is placed W przeciwnym razie pytaj przy pierwszym zapisie po umieszczeniu podpisu Text, marks and signatures become part of the page. Other comments stay editable. Tekst, oznaczenia i podpisy stają się częścią strony. Pozostałe komentarze pozostają edytowalne. Scroll tabs left Przewiń karty w lewo Scroll tabs right Przewiń karty w prawo diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index a172a03879..d98d77904e 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -2208,6 +2208,13 @@ action wizard Start Iniciar First Primeiro First commit Primeiro commit Flatten Fill & Sign when saving Nivelar Preencher e assinar ao salvar + Lock your signature into the page? Fixar sua assinatura na página? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Uma assinatura colocada é um comentário até ser fixada: qualquer pessoa com um editor de PDF pode movê-la ou removê-la. Fixar torna o texto, as marcas e as assinaturas de Preencher e assinar parte da página. + Don't ask again Não perguntar novamente + Lock and save Fixar e salvar + Keep editable Manter editável + Lock into the page when saving Fixar na página ao salvar + Otherwise, ask on the first save after a signature is placed Caso contrário, perguntar ao salvar pela primeira vez após colocar uma assinatura H / Space (hold) H / Espaço (manter pressionado) Home / End Home / End Inserted text Texto inserido diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index b45d73ecab..bae4c18a5c 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -91,6 +91,13 @@ Side panels Боковые панели Enable Acrobat JavaScript Включить Acrobat JavaScript Flatten Fill & Sign when saving Расплющить «Заполнить и подписать» при сохранении + Lock your signature into the page? Закрепить подпись на странице? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Размещённая подпись остаётся комментарием, пока не закреплена: любой с редактором PDF может её переместить или удалить. Закрепление делает текст, отметки и подписи из «Заполнить и подписать» частью страницы. + Don't ask again Больше не спрашивать + Lock and save Закрепить и сохранить + Keep editable Оставить редактируемой + Lock into the page when saving Закреплять на странице при сохранении + Otherwise, ask on the first save after a signature is placed Иначе спрашивать при первом сохранении после размещения подписи Text, marks and signatures become part of the page. Other comments stay editable. Текст, пометки и подписи становятся частью страницы. Остальные комментарии можно по-прежнему править. OK OK Join our Discord Присоединяйтесь к нашему Discord diff --git a/crates/ui-egui/src/i18n/sl.tsv b/crates/ui-egui/src/i18n/sl.tsv index c95f6d6bd6..cd08378b90 100644 --- a/crates/ui-egui/src/i18n/sl.tsv +++ b/crates/ui-egui/src/i18n/sl.tsv @@ -2203,6 +2203,13 @@ signature pad Type Vtipkaj Document properties ({key}) Lastnosti dokumenta ({key}) Fill & Sign dates Datumi pri Izpolni in podpiši Flatten Fill & Sign when saving Splošči Izpolni in podpiši ob shranjevanju + Lock your signature into the page? Zakleniti podpis v stran? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Postavljen podpis je komentar, dokler ni zaklenjen: vsak z urejevalnikom PDF ga lahko premakne ali odstrani. Zaklepanje naredi besedilo, oznake in podpise iz Izpolni in podpiši del strani. + Don't ask again Ne sprašuj več + Lock and save Zakleni in shrani + Keep editable Ohrani urejljivo + Lock into the page when saving Ob shranjevanju zakleni v stran + Otherwise, ask on the first save after a signature is placed Sicer vprašaj ob prvem shranjevanju po postavitvi podpisa Font size 0 uses automatic sizing. Velikost pisave 0 pomeni samodejno velikost. Import list… Uvozi seznam… Larger pages Večje strani diff --git a/crates/ui-egui/src/i18n/te.tsv b/crates/ui-egui/src/i18n/te.tsv index 9b81ffb8b9..3c7e4aa7b6 100644 --- a/crates/ui-egui/src/i18n/te.tsv +++ b/crates/ui-egui/src/i18n/te.tsv @@ -2503,6 +2503,14 @@ animation effects Off ఆఫ్ Send backward వెనుకకు పంపు Send to back అందరికంటే వెనుకకు పంపు + Lock your signature into the page? మీ సంతకాన్ని పేజీలో లాక్ చేయాలా? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. ఉంచిన సంతకం లాక్ చేసే వరకు ఒక వ్యాఖ్య మాత్రమే: PDF ఎడిటర్ ఉన్న ఎవరైనా దాన్ని తరలించవచ్చు లేదా తొలగించవచ్చు. లాక్ చేయడం వల్ల నింపి సంతకం చేయి వచనం, గుర్తులు, సంతకాలు పేజీలో భాగమవుతాయి. + Don't ask again మళ్లీ అడగవద్దు + Lock and save లాక్ చేసి సేవ్ చేయి + Keep editable సవరించగలిగేలా ఉంచు + Lock into the page when saving సేవ్ చేసేటప్పుడు పేజీలో లాక్ చేయి + Otherwise, ask on the first save after a signature is placed లేకపోతే, సంతకం ఉంచిన తర్వాత మొదటి సేవ్‌లో అడుగు + # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today ఈరోజు @plural {n} reply|{n} replies {n} ప్రత్యుత్తరం|{n} ప్రత్యుత్తరాలు diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 6921a6c33e..e62d87e160 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -62,6 +62,13 @@ Side panels Yan paneller Enable Acrobat JavaScript Acrobat JavaScript'i etkinleştir Flatten Fill & Sign when saving Kaydederken Doldur ve İmzala'yı düzleştir + Lock your signature into the page? İmzanız sayfaya sabitlensin mi? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Yerleştirilen imza, sabitlenene kadar bir yorumdur: PDF düzenleyicisi olan herkes onu taşıyabilir veya kaldırabilir. Sabitlemek, Doldur ve İmzala metnini, işaretlerini ve imzalarını sayfanın parçası yapar. + Don't ask again Bir daha sorma + Lock and save Sabitle ve kaydet + Keep editable Düzenlenebilir bırak + Lock into the page when saving Kaydederken sayfaya sabitle + Otherwise, ask on the first save after a signature is placed Aksi halde, imza yerleştirildikten sonraki ilk kayıtta sor Text, marks and signatures become part of the page. Other comments stay editable. Metin, işaretler ve imzalar sayfanın bir parçası olur. Diğer yorumlar düzenlenebilir kalır. OK Tamam Join our Discord Discord'umuza katılın diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index a824769600..863d1a69c1 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -2278,6 +2278,13 @@ calendar date December грудня Smaller pages Менші сторінки Reset page size Скинути розмір сторінок Flatten Fill & Sign when saving Звести «Заповнення й підпис» під час збереження + Lock your signature into the page? Закріпити підпис на сторінці? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Розміщений підпис є коментарем, доки його не закріплено: будь-хто з редактором PDF може його перемістити або видалити. Закріплення робить текст, позначки й підписи з «Заповнити й підписати» частиною сторінки. + Don't ask again Більше не питати + Lock and save Закріпити й зберегти + Keep editable Залишити редагованим + Lock into the page when saving Закріплювати на сторінці під час збереження + Otherwise, ask on the first save after a signature is placed Інакше питати під час першого збереження після розміщення підпису Text, marks and signatures become part of the page. Other comments stay editable. Текст, позначки й підписи стають частиною сторінки. Інші коментарі залишаються редагованими. Date format Формат дати Fill & Sign dates Дати заповнення й підпису diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 96e9c3bb75..a3eee785a9 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -144,6 +144,13 @@ Side panels 侧边面板 Enable Acrobat JavaScript 启用 Acrobat JavaScript Flatten Fill & Sign when saving 保存时拼合填写与签名 + Lock your signature into the page? 将签名锁定到页面中? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 放置的签名在锁定前只是注释:任何有 PDF 编辑器的人都能移动或删除它。锁定后,“填写和签名”的文字、标记和签名会成为页面的一部分。 + Don't ask again 不再询问 + Lock and save 锁定并保存 + Keep editable 保持可编辑 + Lock into the page when saving 保存时锁定到页面 + Otherwise, ask on the first save after a signature is placed 否则,在放置签名后首次保存时询问 Text, marks and signatures become part of the page. Other comments stay editable. 文字、标记和签名会成为页面内容。其他注释仍可编辑。 OK 确定 Cancel 取消 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index a1b933b830..3695a90572 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2544,6 +2544,14 @@ animation effects Off 關 Send backward 下移一層 Send to back 移到最下層 + Lock your signature into the page? 將簽名鎖定到頁面中? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 放置的簽名在鎖定前只是註解:任何有 PDF 編輯器的人都能移動或移除它。鎖定後,「填寫與簽署」的文字、標記與簽名會成為頁面的一部分。 + Don't ask again 不再詢問 + Lock and save 鎖定並儲存 + Keep editable 保持可編輯 + Lock into the page when saving 儲存時鎖定到頁面 + Otherwise, ask on the first save after a signature is placed 否則,在放置簽名後第一次儲存時詢問 + # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today 今天 @plural {n} reply|{n} replies {n} 則回覆 diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index fe0138f27f..c847b01337 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -326,6 +326,15 @@ pub struct PdfCraftApp { ui_scale_applied: Option, /// Preferences: bake Fill & Sign marks into the page when saving. Off, so a normal save stays editable. pub flatten_fill_sign_on_save: bool, + /// Ask on the first save after Fill & Sign places a signature or initials whether to lock + /// them into the page (#2149). Off once the user says not to ask again. + pub ask_to_lock_signatures: bool, + /// Documents with a signature or initials placed by Fill & Sign since they were last saved + /// (or asked about). + pub(crate) placed_signatures: std::collections::HashSet, + /// A save waiting behind [`Dialog::LockSignatures`], and its Don't ask again box. + pub(crate) pending_lock: Option, + pub(crate) lock_remember: bool, /// Point size for new Fill & Sign text and dates, kept across launches. pub fill_text_size: f64, /// Colour for new Fill & Sign text and dates, kept across launches. diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index e3a9c540a6..c034c51a8d 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -142,6 +142,9 @@ pub enum Dialog { Revert, /// Save over a file that changed on disk since the document came from it (#2166). OverwriteChanged, + /// Saving a document with a signature just placed by Fill & Sign: lock it into the page + /// first? (#2149) + LockSignatures, /// Link Properties. LinkProps, /// Organize ▸ Extract (options), Rotate Pages. diff --git a/crates/ui-egui/src/tools/fill_sign.rs b/crates/ui-egui/src/tools/fill_sign.rs index 045bb64974..225faca601 100644 --- a/crates/ui-egui/src/tools/fill_sign.rs +++ b/crates/ui-egui/src/tools/fill_sign.rs @@ -398,6 +398,8 @@ pub(crate) fn signature_entries(ui: &mut egui::Ui, app: &mut crate::PdfCraftApp, }); ui.add_space(4.0); } + // Where the signing happens, not only in Preferences (#2149). + ui.add(crate::widgets::checkbox(&mut app.flatten_fill_sign_on_save, tl!("Lock into the page when saving"))); command } diff --git a/crates/ui-egui/src/workflows/js_ui.rs b/crates/ui-egui/src/workflows/js_ui.rs index 70c3f775e9..dce319d637 100644 --- a/crates/ui-egui/src/workflows/js_ui.rs +++ b/crates/ui-egui/src/workflows/js_ui.rs @@ -560,6 +560,10 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok ui.add_space(8.0); ui.label(egui::RichText::new(tl!("Fill & Sign")).font(theme::semibold(13.0))); ui.add(widgets::checkbox(&mut app.flatten_fill_sign_on_save, tl!("Flatten Fill & Sign when saving"))); + ui.add_enabled( + !app.flatten_fill_sign_on_save, + widgets::checkbox(&mut app.ask_to_lock_signatures, tl!("Otherwise, ask on the first save after a signature is placed")), + ); ui.label( egui::RichText::new(tl!("Text, marks and signatures become part of the page. Other comments stay editable.")).small().color(t.text_muted), ); diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index ce21bd9f74..1bd6f53ff9 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -4,7 +4,7 @@ use egui::{Pos2, pos2}; use egui_kittest::Harness; use egui_kittest::kittest::Queryable; use pdfcraft_ui_egui::fill_sign::{FillTool, SavedSig}; -use pdfcraft_ui_egui::{Dialog, PdfCraftApp, QuickTool}; +use pdfcraft_ui_egui::{Dialog, PdfCraftApp, QuickTool, SaveTarget}; const FIXTURE: &[u8] = b"%PDF-1.7 1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj @@ -1752,6 +1752,88 @@ fn photos_and_images_dropped_on_the_signature_pad_are_imported_not_opened() { std::fs::remove_file(photo).unwrap(); } +/// #2149: the first save after Fill & Sign places a signature asks whether to lock it into the +/// page. Lock and save flattens it; Keep editable keeps it a comment; Don't ask again becomes +/// the preference; the Sign menu has the switch too. +#[test] +fn the_first_save_after_placing_a_signature_asks_to_lock_it() { + let out = std::env::temp_dir().join(format!("pdfcraft-lock-signature-{}.pdf", std::process::id())); + let mut h = harness(); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 300.0); + let signed = |h: &Harness<'static, PdfCraftApp>| { + h.state().session.get(h.state().views[0].id).unwrap().info.annotations.iter().filter(|a| a.fill_sign).count() + }; + assert_eq!(signed(&h), 1); + // Keep editable: saved, still a comment, and not asked again for this placement. + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks first"); + h.run_steps(2); + h.get_by_label("Lock your signature into the page?"); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + assert!(!h.state().session.get(h.state().views[0].id).unwrap().dirty, "saved"); + assert_eq!(signed(&h), 1, "still a removable comment"); + assert!(h.state_mut().save_active(SaveTarget::InPlace), "no new signature: no question"); + // A new signature asks again; Lock and save with Don't ask again flattens and remembers. + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 200.0); + assert!(!h.state_mut().save_active(SaveTarget::InPlace)); + h.run_steps(2); + h.get_by_label("Don't ask again").click(); + h.run_steps(1); + h.get_by_label("Lock and save").click(); + h.run_steps(3); + assert_eq!(signed(&h), 0, "locked into the page"); + assert!(!h.state().session.get(h.state().views[0].id).unwrap().dirty); + assert!(h.state().flatten_fill_sign_on_save, "remembered as the preference"); + // The Sign menu shows the same switch. + h.state_mut().flatten_fill_sign_on_save = false; + h.get_by_label("Fill & Sign").click(); + h.run_steps(2); + h.get_by_label("Fill & Sign").click(); + h.run_steps(2); + h.get_by_label("Lock into the page when saving").click(); + h.run_steps(2); + assert!(h.state().flatten_fill_sign_on_save); + let _ = std::fs::remove_file(&out); +} + +/// #2149: Cancel saves nothing, and Keep editable with Don't ask again turns the question off. +#[test] +fn cancelling_the_lock_question_saves_nothing() { + let out = std::env::temp_dir().join(format!("pdfcraft-lock-cancel-{}.pdf", std::process::id())); + let _ = std::fs::remove_file(&out); + let mut h = harness(); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 300.0); + assert!(!h.state_mut().save_active(SaveTarget::InPlace)); + h.run_steps(2); + h.get_by_label("Cancel").click(); + h.run_steps(3); + assert!(!out.exists() && h.state().session.get(h.state().views[0].id).unwrap().dirty, "nothing saved"); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asked again"); + h.run_steps(2); + h.get_by_label("Don't ask again").click(); + h.run_steps(1); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + assert!(out.exists() && !h.state().ask_to_lock_signatures); + let restored = { + let mut a = PdfCraftApp::new(); + a.restore(&h.state().persist()); + a.ask_to_lock_signatures + }; + assert!(!restored, "kept with the settings"); + let _ = std::fs::remove_file(&out); +} + /// Where a mode bar tab is, by its label. fn mode_tab(h: &Harness<'static, PdfCraftApp>, label: &str) -> egui::Rect { h.get_all_by_role_and_label(egui::accesskit::Role::Button, label).map(|n| n.rect()).find(|r| r.bottom() < 120.0).expect("a mode tab") diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index d03c27c95c..03381a30fa 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -136,7 +136,7 @@ workflows, exchange edge cases and everything outside the mainstream slice are e | Print | 8% | 55% | Good on macOS and Linux; Windows prints since #847 (#756), not yet re-scored | | Highlight, sticky notes, text comments | 12% | 85% | All markup types, replies, properties | | Fill in forms | 10% | 85% | Every AcroForm field type, Acrobat's formatting and calculations | -| Fill & Sign: typed, drawn or image signature | 10% | 85% | Fill & Sign tools; flatten on save | +| Fill & Sign: typed, drawn or image signature | 10% | 85% | Fill & Sign tools; flatten on save (opt-in, and asked on the first save after signing) | | Rotate, delete, reorder and extract pages | 8% | 85% | Organize grid, drag reorder, thumbnails | | Combine files | 6% | 85% | Combine tab with bookmarks per file | | Fix a typo in existing text | 5% | 40% | Latin paragraphs only; font may be substituted | diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index cabfc7823a..66f21615d8 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -5441,8 +5441,8 @@ milestone = "M5" status = "shipped" commands = ["comment.flatten"] tools = ["doc_save", "doc_flatten"] -evidence = ["crates/edit/src/tests.rs::fill_sign_flatten_bakes_marks_keeps_other_comments_and_does_not_reuse_pcfl0", "crates/engine/src/tests.rs::flattening_fill_and_sign_keeps_other_comments_fields_and_undoes", "crates/engine/src/tests.rs::flattening_fill_and_sign_is_refused_without_permission_to_change", "crates/automation/tests/it/automation.rs::saving_with_flatten_fill_sign_bakes_marks_and_leaves_other_comments", "crates/ui-egui/tests/it/fill_sign.rs::saving_flattens_fill_and_sign_only_when_the_preference_is_on"] -notes = "Preferences ▸ Fill & Sign, and doc_save flatten_fill_sign, bake text, marks and signatures into the page. The option defaults off, so a normal save leaves them editable. Other comments and form fields stay. Flatten comments still flattens every comment." +evidence = ["crates/edit/src/tests.rs::fill_sign_flatten_bakes_marks_keeps_other_comments_and_does_not_reuse_pcfl0", "crates/engine/src/tests.rs::flattening_fill_and_sign_keeps_other_comments_fields_and_undoes", "crates/engine/src/tests.rs::flattening_fill_and_sign_is_refused_without_permission_to_change", "crates/automation/tests/it/automation.rs::saving_with_flatten_fill_sign_bakes_marks_and_leaves_other_comments", "crates/ui-egui/tests/it/fill_sign.rs::saving_flattens_fill_and_sign_only_when_the_preference_is_on", "crates/ui-egui/tests/it/fill_sign.rs::the_first_save_after_placing_a_signature_asks_to_lock_it", "crates/ui-egui/tests/it/fill_sign.rs::cancelling_the_lock_question_saves_nothing"] +notes = "Preferences ▸ Fill & Sign, and doc_save flatten_fill_sign, bake text, marks and signatures into the page. The option defaults off, so a normal save leaves them editable; the first save after a signature or initials is placed asks whether to lock it into the page (Lock and save / Keep editable, with Don't ask again), and the Sign menu carries the same switch (#2149). Other comments and form fields stay. Flatten comments still flattens every comment." [[feature]] id = "form.autofill-profile" From 97f514d632fa0a8376dd31618a23081dc743b169 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Sat, 17 Oct 2026 13:08:47 +0800 Subject: [PATCH 02/10] M5: Vietnamese for the lock-signatures prompt (#2149) Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/vi.tsv | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index e79a8aacc4..2275b4b78a 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -2885,3 +2885,12 @@ key Space Space No image on clipboard to paste Không có hình ảnh trong bảng nhớ tạm để dán Pinned {tool} to Home Đã ghim {tool} vào Trang chủ Unpinned {tool} from Home Đã bỏ ghim {tool} khỏi Trang chủ + +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Chữ ký đã đặt chỉ là một bình luận cho đến khi được khóa: bất kỳ ai có trình chỉnh sửa PDF đều có thể di chuyển hoặc xóa nó. Khóa sẽ biến văn bản, dấu và chữ ký Điền & Ký của bạn thành một phần của trang. + Don't ask again Không hỏi lại + Keep editable Giữ để chỉnh sửa được + Lock and save Khóa và lưu + Lock into the page when saving Khóa vào trang khi lưu + Lock your signature into the page? Khóa chữ ký của bạn vào trang? + Otherwise, ask on the first save after a signature is placed Nếu không, hỏi ở lần lưu đầu tiên sau khi đặt chữ ký From c2b1de2b8bacb02350778c0aa12413740c1a9f7d Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:14:45 +0800 Subject: [PATCH 03/10] M7: a signature lock that fails keeps the question and the preference (#2149) Review follow-up on #2348: the document stays tracked as holding a placed signature and "Don't ask again" is remembered only once locking succeeded. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/editing/save.rs | 20 ++++++++------ crates/ui-egui/tests/it/fill_sign.rs | 41 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index 446aba1fc5..a2830d680d 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -171,17 +171,11 @@ impl PdfCraftApp { /// The Lock signatures dialog answered (#2149): lock the document's Fill & Sign marks into /// its pages first when `lock`, then save as was asked. With `remember`, the answer becomes - /// the preference (flatten on every save, or never ask again). + /// the preference (flatten on every save, or never ask again). Nothing changes when locking + /// fails: the document still counts as holding a placed signature, so the next save asks + /// again, and the answer isn't remembered. pub(crate) fn answer_lock_signatures(&mut self, lock: bool, remember: bool) { let Some(p) = self.pending_lock.take() else { return }; - self.placed_signatures.remove(&p.id); - if remember { - if lock { - self.flatten_fill_sign_on_save = true; - } else { - self.ask_to_lock_signatures = false; - } - } if lock && !self.flatten_fill_sign_on_save { let before = self.session.get(p.id).map(|d| d.edit_generation()); if let Err(e) = self.session.apply(p.id, pdfcraft_engine::Edit::FlattenFillSign) { @@ -195,6 +189,14 @@ impl PdfCraftApp { view.document_changed(&doc.info); } } + self.placed_signatures.remove(&p.id); + if remember { + if lock { + self.flatten_fill_sign_on_save = true; + } else { + self.ask_to_lock_signatures = false; + } + } if let Some(index) = self.views.iter().position(|v| v.id == p.id) { self.save_then(index, p.target, p.after); } diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index 1bd6f53ff9..54af2288f0 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -1802,6 +1802,47 @@ fn the_first_save_after_placing_a_signature_asks_to_lock_it() { let _ = std::fs::remove_file(&out); } +/// #2348 review: when locking the signature fails (here the document's security allows comments +/// but not changes), nothing is lost: the next save asks again, and "Don't ask again" isn't +/// remembered. +#[test] +fn a_lock_that_fails_keeps_the_question() { + let out = std::env::temp_dir().join(format!("pdfcraft-lock-fails-{}.pdf", std::process::id())); + let mut doc = pdfcraft_cos::Document::open(std::sync::Arc::new(FIXTURE.to_vec())).unwrap(); + doc.set_encryption(&pdfcraft_cos::NewEncryption { + algorithm: pdfcraft_cos::Algorithm::Aes256, + user_password: "", + owner_password: "owner", + // Everything but bit 4, modifying the document. + permissions: -1 ^ 8, + encrypt_metadata: true, + seed: [4; 32], + }) + .unwrap(); + let mut h = harness_bytes(&pdfcraft_cos::write_full(&doc, &Default::default()).unwrap()); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 300.0); + let signed = |h: &Harness<'static, PdfCraftApp>| { + h.state().session.get(h.state().views[0].id).unwrap().info.annotations.iter().filter(|a| a.fill_sign).count() + }; + assert_eq!(signed(&h), 1, "comments are allowed"); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks first"); + h.run_steps(2); + h.get_by_label("Don't ask again").click(); + h.run_steps(1); + h.get_by_label("Lock and save").click(); + h.run_steps(3); + assert_eq!(signed(&h), 1, "changes aren't allowed: still a comment"); + assert!(!h.state().flatten_fill_sign_on_save, "the failed answer isn't remembered"); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "the next save asks again"); + h.run_steps(2); + h.get_by_label("Lock your signature into the page?"); + let _ = std::fs::remove_file(&out); +} + /// #2149: Cancel saves nothing, and Keep editable with Don't ask again turns the question off. #[test] fn cancelling_the_lock_question_saves_nothing() { From 30aa8793f69d2a357108b43f4114b79bf5cff808 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 19 Oct 2026 07:10:23 +0800 Subject: [PATCH 04/10] M7: the lock question keeps the changed-on-disk question, skips signed documents and undone marks (#2149) Review follow-ups on #2348 (benandez): a dialog an answer opened this frame isn't closed with it; documents with digital signatures aren't asked, since locking changes them after signing; a placement that was undone asks nothing; the mark is forgotten when the tab closes; the other catalogues carry the AI-assisted header; parity evidence lists the new tests. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/app/files.rs | 1 + crates/ui-egui/src/dialogs.rs | 6 ++- crates/ui-egui/src/editing/save.rs | 8 +++- crates/ui-egui/src/i18n/ar.tsv | 1 + crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/kk.tsv | 1 + crates/ui-egui/src/i18n/lt.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sl.tsv | 1 + crates/ui-egui/src/i18n/te.tsv | 1 + crates/ui-egui/src/i18n/tr.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + crates/ui-egui/src/lib.rs | 4 +- crates/ui-egui/tests/it/fill_sign.rs | 65 ++++++++++++++++++++++++++++ parity/acrobat-features.toml | 2 +- 27 files changed, 101 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/app/files.rs b/crates/ui-egui/src/app/files.rs index 29e6978461..28e82758a4 100644 --- a/crates/ui-egui/src/app/files.rs +++ b/crates/ui-egui/src/app/files.rs @@ -746,6 +746,7 @@ impl PdfCraftApp { } self.remember_view(index); let id = self.views.remove(index).id; + self.placed_signatures.remove(&id); #[cfg(not(target_arch = "wasm32"))] if let Some(path) = self.session.get(id).and_then(|d| d.path.clone()) { if self.closed_tabs.len() == 10 { diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 30985fab28..58a7b560bb 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -1983,7 +1983,11 @@ fn finish_close(app: &mut PdfCraftApp, dialog: Dialog, modal_closed: bool, req: if modal_closed || req.close || replaces { // Only now: the number in Preferences can pass through 0 on its way to the one wanted. app.recent.truncate(app.recent_limit); - app.dialog = None; + // An answer may have opened another dialog this frame (saving a file that changed on + // disk asks about that): it stays. + if app.dialog == Some(dialog) { + app.dialog = None; + } app.props_draft = None; app.view_draft = None; app.custom_draft = None; diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index a2830d680d..520ce47477 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -55,8 +55,12 @@ impl PdfCraftApp { } let Some(id) = self.views.get(index).map(|v| v.id) else { return false }; // A signature just placed with Fill & Sign is a removable comment unless it is flattened: - // ask once whether to lock it into the page (#2149). - if self.ask_to_lock_signatures && !self.flatten_fill_sign_on_save && self.placed_signatures.contains(&id) { + // ask once whether to lock it into the page (#2149). Only while a Fill & Sign mark is still + // there (undo may have taken it away), and never on a digitally signed document: locking + // changes page content in an update, which validators report as a change after signing. + let still_marked = self.session.get(id).is_some_and(|d| d.info.annotations.iter().any(|a| a.fill_sign)); + let signed = self.session.get(id).is_some_and(|d| d.is_signed()); + if self.ask_to_lock_signatures && !self.flatten_fill_sign_on_save && self.placed_signatures.contains(&id) && still_marked && !signed { self.pending_lock = Some(PendingLock { id, target, after: Box::new(after) }); self.dialog = Some(crate::Dialog::LockSignatures); return false; diff --git a/crates/ui-egui/src/i18n/ar.tsv b/crates/ui-egui/src/i18n/ar.tsv index 50d633bd33..746e20477d 100644 --- a/crates/ui-egui/src/i18n/ar.tsv +++ b/crates/ui-egui/src/i18n/ar.tsv @@ -2278,6 +2278,7 @@ calendar date December ديسمبر Smaller pages صفحات أصغر Reset page size إعادة ضبط حجم الصفحة Flatten Fill & Sign when saving تسطيح التعبئة والتوقيع عند الحفظ +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? هل تريد تثبيت توقيعك في الصفحة؟ A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. التوقيع الموضوع تعليق إلى أن يُثبَّت: يستطيع أي شخص لديه محرر PDF نقله أو إزالته. التثبيت يجعل نصوص التعبئة والتوقيع وعلاماتها وتواقيعها جزءًا من الصفحة. Don't ask again عدم السؤال مرة أخرى diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 56cd2f3fd1..516fbb62c9 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -138,6 +138,7 @@ Side panels Странични панели Enable Acrobat JavaScript Включи Acrobat JavaScript Flatten Fill & Sign when saving Сплескай „Попълни и подпиши“ при запазване +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Да се заключи ли подписът ви в страницата? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Поставеният подпис е коментар, докато не бъде заключен: всеки с PDF редактор може да го премести или премахне. Заключването прави текста, отметките и подписите от „Попълване и подписване“ част от страницата. Don't ask again Не питай отново diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 89da6a9309..805b9c1885 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -2299,6 +2299,7 @@ calendar date December prosince Smaller pages Menší stránky Reset page size Obnovit velikost stránek Flatten Fill & Sign when saving Při ukládání sloučit obsah Vyplnit a podepsat se stránkou +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Zamknout podpis do stránky? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Umístěný podpis je komentář, dokud není zamčen: kdokoli s editorem PDF ho může přesunout nebo odstranit. Zamčení udělá z textu, značek a podpisů Vyplnit a podepsat součást stránky. Don't ask again Už se neptat diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index a80eb7b98d..2a4a5b4e0e 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -71,6 +71,7 @@ Side panels Seitenleisten Enable Acrobat JavaScript Acrobat-JavaScript aktivieren Flatten Fill & Sign when saving Ausfüllen und unterschreiben beim Speichern reduzieren +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Unterschrift fest in die Seite übernehmen? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Eine platzierte Unterschrift ist ein Kommentar, bis sie fest übernommen wird: Jeder mit einem PDF-Editor kann sie verschieben oder entfernen. Beim Übernehmen werden Text, Markierungen und Unterschriften aus „Ausfüllen und unterschreiben“ Teil der Seite. Don't ask again Nicht mehr fragen diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 9952012e7a..6e031b4e4f 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -286,6 +286,7 @@ Failed Αποτυχία Page {p} of {n} Σελίδα {p} από {n} Flatten Fill & Sign when saving Ενσωμάτωση συμπληρώσεων και υπογραφών κατά την αποθήκευση +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Κλείδωμα της υπογραφής σας στη σελίδα; A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Μια τοποθετημένη υπογραφή είναι σχόλιο μέχρι να κλειδωθεί: οποιοσδήποτε με επεξεργαστή PDF μπορεί να τη μετακινήσει ή να την αφαιρέσει. Το κλείδωμα κάνει το κείμενο, τα σημάδια και τις υπογραφές της Συμπλήρωσης και υπογραφής μέρος της σελίδας. Don't ask again Να μην ερωτηθώ ξανά diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index cb6b90f278..10897567ac 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -94,6 +94,7 @@ Side panels Paneles laterales Enable Acrobat JavaScript Activar Acrobat JavaScript Flatten Fill & Sign when saving Acoplar Rellenar y firmar al guardar +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? ¿Fijar su firma en la página? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Una firma colocada es un comentario hasta que se fija: cualquiera con un editor de PDF puede moverla o quitarla. Al fijarla, el texto, las marcas y las firmas de Rellenar y firmar pasan a formar parte de la página. Don't ask again No volver a preguntar diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 2cb821d804..0c4572fe22 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -71,6 +71,7 @@ Side panels Panneaux latéraux Enable Acrobat JavaScript Activer Acrobat JavaScript Flatten Fill & Sign when saving Aplatir Remplir et signer à l'enregistrement +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Intégrer votre signature à la page ? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Une signature placée reste un commentaire tant qu’elle n’est pas intégrée : n’importe qui avec un éditeur PDF peut la déplacer ou la supprimer. L’intégration rend le texte, les marques et les signatures de Remplir et signer partie de la page. Don't ask again Ne plus demander diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 1b9a0c5d78..7a066c9ba3 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -2255,6 +2255,7 @@ action wizard Start Indítás Smaller pages Kisebb oldalak Reset page size Oldalméret visszaállítása Flatten Fill & Sign when saving Kitöltés és aláírás beégetése mentéskor +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Rögzíti az aláírását az oldalba? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Az elhelyezett aláírás megjegyzés, amíg nincs rögzítve: bárki egy PDF-szerkesztővel áthelyezheti vagy eltávolíthatja. A rögzítés a Kitöltés és aláírás szövegét, jeleit és aláírásait az oldal részévé teszi. Don't ask again Ne kérdezzen többet diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index eb713baefd..ae3ebd6693 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -2215,6 +2215,7 @@ Files are combined in this order. Leave Pages empty to take every page. I file vengono combinati in quest'ordine. Lascia vuoto Pagine per prendere tutte le pagine. Fill & Sign dates Date di Compila e firma Flatten Fill & Sign when saving Appiattisci Compila e firma al salvataggio +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Bloccare la firma nella pagina? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Una firma inserita è un commento finché non viene bloccata: chiunque abbia un editor PDF può spostarla o rimuoverla. Il blocco rende testo, segni e firme di Compila e firma parte della pagina. Don't ask again Non chiedere più diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 9e151bbc62..ddeb261438 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -2270,6 +2270,7 @@ @plural {n} page|{n} pages {n} ページ signature pad Type 入力 Flatten Fill & Sign when saving 保存時に入力と署名を統合する +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? 署名をページに固定しますか? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 配置した署名は固定するまで注釈です。PDF エディターを持つ人なら誰でも移動や削除ができます。固定すると、入力と署名のテキスト、マーク、署名がページの一部になります。 Don't ask again 今後は確認しない diff --git a/crates/ui-egui/src/i18n/kk.tsv b/crates/ui-egui/src/i18n/kk.tsv index 87b0496d70..8171e1cac4 100644 --- a/crates/ui-egui/src/i18n/kk.tsv +++ b/crates/ui-egui/src/i18n/kk.tsv @@ -2187,6 +2187,7 @@ calendar date December Желтоқсан Smaller pages Кішірек беттер Reset page size Бет өлшемін қалпына келтіру Flatten Fill & Sign when saving Сақтау кезінде «Толтыру және қол қою» мазмұнын жазықтау +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Қолтаңбаңызды бетке бекіту керек пе? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Қойылған қолтаңба бекітілгенге дейін түсініктеме болып қалады: PDF өңдегіші бар кез келген адам оны жылжытып не жоя алады. Бекіту «Толтыру және қол қою» мәтінін, белгілерін және қолтаңбаларын беттің бөлігіне айналдырады. Don't ask again Қайта сұрамау diff --git a/crates/ui-egui/src/i18n/lt.tsv b/crates/ui-egui/src/i18n/lt.tsv index 725d8ad22e..d93109274e 100644 --- a/crates/ui-egui/src/i18n/lt.tsv +++ b/crates/ui-egui/src/i18n/lt.tsv @@ -590,6 +590,7 @@ Fixed what could be fixed; 1 problem remains Ištaisyta tai, ką buvo galima; liko 1 problema Fixed what could be fixed; {n} problems remain Ištaisyta tai, ką buvo galima; liko problemų: {n} Flatten Fill & Sign when saving Įrašant suplokštinti pildymo ir pasirašymo turinį +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Įtvirtinti parašą puslapyje? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Padėtas parašas yra komentaras, kol neįtvirtintas: bet kas su PDF redaktoriumi gali jį perkelti ar pašalinti. Įtvirtinus užpildymo ir pasirašymo tekstas, žymės ir parašai tampa puslapio dalimi. Don't ask again Daugiau neklausti diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index ee94b2085a..f071c3cda3 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -2249,6 +2249,7 @@ @plural {n} page|{n} pages {n} strona|{n} strony|{n} stron signature pad Type Wpisz Flatten Fill & Sign when saving Spłaszczaj elementy Wypełnij i podpisz przy zapisie +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Utrwalić podpis na stronie? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Umieszczony podpis jest komentarzem, dopóki nie zostanie utrwalony: każdy z edytorem PDF może go przenieść lub usunąć. Utrwalenie sprawia, że tekst, znaki i podpisy z Wypełnij i podpisz stają się częścią strony. Don't ask again Nie pytaj ponownie diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index d98d77904e..eae76f0a35 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -2208,6 +2208,7 @@ action wizard Start Iniciar First Primeiro First commit Primeiro commit Flatten Fill & Sign when saving Nivelar Preencher e assinar ao salvar +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Fixar sua assinatura na página? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Uma assinatura colocada é um comentário até ser fixada: qualquer pessoa com um editor de PDF pode movê-la ou removê-la. Fixar torna o texto, as marcas e as assinaturas de Preencher e assinar parte da página. Don't ask again Não perguntar novamente diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index bae4c18a5c..0b5274603c 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -91,6 +91,7 @@ Side panels Боковые панели Enable Acrobat JavaScript Включить Acrobat JavaScript Flatten Fill & Sign when saving Расплющить «Заполнить и подписать» при сохранении +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Закрепить подпись на странице? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Размещённая подпись остаётся комментарием, пока не закреплена: любой с редактором PDF может её переместить или удалить. Закрепление делает текст, отметки и подписи из «Заполнить и подписать» частью страницы. Don't ask again Больше не спрашивать diff --git a/crates/ui-egui/src/i18n/sl.tsv b/crates/ui-egui/src/i18n/sl.tsv index cd08378b90..41286d281a 100644 --- a/crates/ui-egui/src/i18n/sl.tsv +++ b/crates/ui-egui/src/i18n/sl.tsv @@ -2203,6 +2203,7 @@ signature pad Type Vtipkaj Document properties ({key}) Lastnosti dokumenta ({key}) Fill & Sign dates Datumi pri Izpolni in podpiši Flatten Fill & Sign when saving Splošči Izpolni in podpiši ob shranjevanju +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Zakleniti podpis v stran? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Postavljen podpis je komentar, dokler ni zaklenjen: vsak z urejevalnikom PDF ga lahko premakne ali odstrani. Zaklepanje naredi besedilo, oznake in podpise iz Izpolni in podpiši del strani. Don't ask again Ne sprašuj več diff --git a/crates/ui-egui/src/i18n/te.tsv b/crates/ui-egui/src/i18n/te.tsv index 3c7e4aa7b6..0082655ae8 100644 --- a/crates/ui-egui/src/i18n/te.tsv +++ b/crates/ui-egui/src/i18n/te.tsv @@ -2503,6 +2503,7 @@ animation effects Off ఆఫ్ Send backward వెనుకకు పంపు Send to back అందరికంటే వెనుకకు పంపు +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? మీ సంతకాన్ని పేజీలో లాక్ చేయాలా? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. ఉంచిన సంతకం లాక్ చేసే వరకు ఒక వ్యాఖ్య మాత్రమే: PDF ఎడిటర్ ఉన్న ఎవరైనా దాన్ని తరలించవచ్చు లేదా తొలగించవచ్చు. లాక్ చేయడం వల్ల నింపి సంతకం చేయి వచనం, గుర్తులు, సంతకాలు పేజీలో భాగమవుతాయి. Don't ask again మళ్లీ అడగవద్దు diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index e62d87e160..d40032efa8 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -62,6 +62,7 @@ Side panels Yan paneller Enable Acrobat JavaScript Acrobat JavaScript'i etkinleştir Flatten Fill & Sign when saving Kaydederken Doldur ve İmzala'yı düzleştir +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? İmzanız sayfaya sabitlensin mi? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Yerleştirilen imza, sabitlenene kadar bir yorumdur: PDF düzenleyicisi olan herkes onu taşıyabilir veya kaldırabilir. Sabitlemek, Doldur ve İmzala metnini, işaretlerini ve imzalarını sayfanın parçası yapar. Don't ask again Bir daha sorma diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 863d1a69c1..30c8000fc8 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -2278,6 +2278,7 @@ calendar date December грудня Smaller pages Менші сторінки Reset page size Скинути розмір сторінок Flatten Fill & Sign when saving Звести «Заповнення й підпис» під час збереження +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? Закріпити підпис на сторінці? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Розміщений підпис є коментарем, доки його не закріплено: будь-хто з редактором PDF може його перемістити або видалити. Закріплення робить текст, позначки й підписи з «Заповнити й підписати» частиною сторінки. Don't ask again Більше не питати diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index a3eee785a9..c5c7918373 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -144,6 +144,7 @@ Side panels 侧边面板 Enable Acrobat JavaScript 启用 Acrobat JavaScript Flatten Fill & Sign when saving 保存时拼合填写与签名 +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? 将签名锁定到页面中? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 放置的签名在锁定前只是注释:任何有 PDF 编辑器的人都能移动或删除它。锁定后,“填写和签名”的文字、标记和签名会成为页面的一部分。 Don't ask again 不再询问 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 3695a90572..9ef514a3b5 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2544,6 +2544,7 @@ animation effects Off 關 Send backward 下移一層 Send to back 移到最下層 +# AI-assisted additions for the lock-signatures prompt (#2149) (2026-10-16); not reviewed by a native speaker. Lock your signature into the page? 將簽名鎖定到頁面中? A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 放置的簽名在鎖定前只是註解:任何有 PDF 編輯器的人都能移動或移除它。鎖定後,「填寫與簽署」的文字、標記與簽名會成為頁面的一部分。 Don't ask again 不再詢問 diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index c847b01337..21c487d361 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -329,8 +329,8 @@ pub struct PdfCraftApp { /// Ask on the first save after Fill & Sign places a signature or initials whether to lock /// them into the page (#2149). Off once the user says not to ask again. pub ask_to_lock_signatures: bool, - /// Documents with a signature or initials placed by Fill & Sign since they were last saved - /// (or asked about). + /// Documents with a Fill & Sign signature placed since their last save, asked about on + /// saving (#2149). Not part of the recovery data: a recovered document isn't asked. pub(crate) placed_signatures: std::collections::HashSet, /// A save waiting behind [`Dialog::LockSignatures`], and its Don't ask again box. pub(crate) pending_lock: Option, diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index 54af2288f0..3c7eb8a0d8 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -1843,6 +1843,71 @@ fn a_lock_that_fails_keeps_the_question() { let _ = std::fs::remove_file(&out); } +/// #2348 review: the lock question stays out of the way where it must. Answering it on a file +/// that changed on disk leaves the changed-on-disk question showing (it was closed with the lock +/// question, so the save silently didn't happen); a signature placed and then undone asks +/// nothing; and a digitally signed document isn't asked, since locking would change it after +/// signing. +#[test] +fn the_lock_question_is_skipped_or_kept_where_it_must_be() { + let place = |h: &mut Harness<'static, PdfCraftApp>| { + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(h, 40.0, 300.0); + }; + // A file that changed on disk. + let path = std::env::temp_dir().join(format!("pdfcraft-lock-changed-{}.pdf", std::process::id())); + std::fs::write(&path, FIXTURE).unwrap(); + let mut h = harness_bytes(FIXTURE); + h.state_mut().close_tab(0); + h.state_mut().open_path(&path.to_string_lossy()); + for _ in 0..100 { + if !h.state().views.is_empty() { + break; + } + h.run_steps(1); + std::thread::sleep(std::time::Duration::from_millis(5)); + } + h.run_steps(3); + place(&mut h); + std::fs::write(&path, [FIXTURE, b"\n% rewritten elsewhere\n".as_slice()].concat()).unwrap(); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks first"); + h.run_steps(2); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + assert_eq!(h.state().dialog, Some(Dialog::OverwriteChanged), "the changed-on-disk question shows"); + let _ = std::fs::remove_file(&path); + + // Placed, then undone: nothing to lock, so the save just saves. + let out = std::env::temp_dir().join(format!("pdfcraft-lock-undone-{}.pdf", std::process::id())); + let mut h = harness(); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + place(&mut h); + assert!(h.state_mut().execute("edit.undo")); + h.run_steps(2); + assert!(h.state_mut().save_active(SaveTarget::InPlace), "no question after the signature was undone"); + let _ = std::fs::remove_file(&out); + + // Digitally signed: never asked. + let mut doc = { + let mut h = harness(); + let app = h.state_mut(); + let id = app.views[0].id; + let p12 = std::fs::read(concat!(env!("CARGO_MANIFEST_DIR"), "/../sign/tests/data/ec-p256.p12")).unwrap(); + let digital_id = pdfcraft_engine::sign::pkcs12::open(&p12, "test").unwrap(); + let opts = pdfcraft_engine::SignOptions { page: 0, rect: Some([20.0, 20.0, 160.0, 50.0]), ..Default::default() }; + app.session.sign(id, &digital_id, opts).expect("signs") + }; + let mut h = harness_bytes(&std::mem::take(&mut doc)); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + place(&mut h); + let id = h.state().views[0].id; + assert!(h.state().session.get(id).unwrap().is_signed()); + assert!(h.state_mut().save_active(SaveTarget::InPlace), "a signed document isn't asked"); + let _ = std::fs::remove_file(&out); +} + /// #2149: Cancel saves nothing, and Keep editable with Don't ask again turns the question off. #[test] fn cancelling_the_lock_question_saves_nothing() { diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 66f21615d8..cd0c858371 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -5441,7 +5441,7 @@ milestone = "M5" status = "shipped" commands = ["comment.flatten"] tools = ["doc_save", "doc_flatten"] -evidence = ["crates/edit/src/tests.rs::fill_sign_flatten_bakes_marks_keeps_other_comments_and_does_not_reuse_pcfl0", "crates/engine/src/tests.rs::flattening_fill_and_sign_keeps_other_comments_fields_and_undoes", "crates/engine/src/tests.rs::flattening_fill_and_sign_is_refused_without_permission_to_change", "crates/automation/tests/it/automation.rs::saving_with_flatten_fill_sign_bakes_marks_and_leaves_other_comments", "crates/ui-egui/tests/it/fill_sign.rs::saving_flattens_fill_and_sign_only_when_the_preference_is_on", "crates/ui-egui/tests/it/fill_sign.rs::the_first_save_after_placing_a_signature_asks_to_lock_it", "crates/ui-egui/tests/it/fill_sign.rs::cancelling_the_lock_question_saves_nothing"] +evidence = ["crates/edit/src/tests.rs::fill_sign_flatten_bakes_marks_keeps_other_comments_and_does_not_reuse_pcfl0", "crates/engine/src/tests.rs::flattening_fill_and_sign_keeps_other_comments_fields_and_undoes", "crates/engine/src/tests.rs::flattening_fill_and_sign_is_refused_without_permission_to_change", "crates/automation/tests/it/automation.rs::saving_with_flatten_fill_sign_bakes_marks_and_leaves_other_comments", "crates/ui-egui/tests/it/fill_sign.rs::saving_flattens_fill_and_sign_only_when_the_preference_is_on", "crates/ui-egui/tests/it/fill_sign.rs::the_first_save_after_placing_a_signature_asks_to_lock_it", "crates/ui-egui/tests/it/fill_sign.rs::cancelling_the_lock_question_saves_nothing", "crates/ui-egui/tests/it/fill_sign.rs::a_lock_that_fails_keeps_the_question", "crates/ui-egui/tests/it/fill_sign.rs::the_lock_question_is_skipped_or_kept_where_it_must_be"] notes = "Preferences ▸ Fill & Sign, and doc_save flatten_fill_sign, bake text, marks and signatures into the page. The option defaults off, so a normal save leaves them editable; the first save after a signature or initials is placed asks whether to lock it into the page (Lock and save / Keep editable, with Don't ask again), and the Sign menu carries the same switch (#2149). Other comments and form fields stay. Flatten comments still flattens every comment." [[feature]] From 58e28e19508d42c334c2c1b06e7cbc1103b090f4 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 19 Oct 2026 07:33:08 +0800 Subject: [PATCH 05/10] M7: a web save forgets the placed signature too (#2149) Review follow-up on #2348: as the native save does, so a later save with the question turned back on doesn't ask about what was already saved. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/editing/save.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index 520ce47477..0ab9bd5b5d 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -110,7 +110,10 @@ impl PdfCraftApp { }; match delivered { Ok(()) => { - let _ = self.session.mark_saved(id, bytes, None); + // As the native save does: what was saved isn't asked about again (#2149). + if self.session.mark_saved(id, bytes, None).is_ok() { + self.placed_signatures.remove(&id); + } if let Some(doc) = self.session.get(id) && let Some(view) = self.views.get_mut(index) { From b92d937c9199f021362337ff8045c1d0d10e4909 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 19 Oct 2026 14:56:12 +0800 Subject: [PATCH 06/10] M7: offer to lock a signature right after it's placed, and keep the switch in the panel (#2149) benandez's review suggestion on #2348. Right after Fill & Sign places a signature or initials, a notice says "Signature added. Others can move it until it's locked." with Lock now, which flattens the marks into the page (undoable) without saving; the save that follows then has nothing to ask about. The placed-signature mark is kept, so undoing the lock brings back the notice and the save-time question, which stays as the fallback. Dismiss hides the notice until the next signature. No notice on a digitally signed document or when saves lock anyway. The "Lock into the page when saving" switch is now once, in the panel's Sign yourself section, no longer in the Sign menu over the page. The three new strings are in all 22 catalogues (AI-assisted, not reviewed by native speakers). Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/canvas.rs | 20 +++++- crates/ui-egui/src/canvas/bars.rs | 24 ++++++- crates/ui-egui/src/editing.rs | 9 +++ crates/ui-egui/src/editing/save.rs | 45 +++++++++---- crates/ui-egui/src/i18n/ar.tsv | 3 + crates/ui-egui/src/i18n/bg.tsv | 3 + crates/ui-egui/src/i18n/cs.tsv | 3 + crates/ui-egui/src/i18n/de.tsv | 3 + crates/ui-egui/src/i18n/el.tsv | 3 + crates/ui-egui/src/i18n/es.tsv | 3 + crates/ui-egui/src/i18n/fr.tsv | 3 + crates/ui-egui/src/i18n/hu.tsv | 3 + crates/ui-egui/src/i18n/it.tsv | 3 + crates/ui-egui/src/i18n/ja.tsv | 3 + crates/ui-egui/src/i18n/kk.tsv | 3 + crates/ui-egui/src/i18n/lt.tsv | 3 + crates/ui-egui/src/i18n/pl.tsv | 3 + crates/ui-egui/src/i18n/pt-br.tsv | 3 + crates/ui-egui/src/i18n/ru.tsv | 3 + crates/ui-egui/src/i18n/sl.tsv | 3 + crates/ui-egui/src/i18n/te.tsv | 3 + crates/ui-egui/src/i18n/tr.tsv | 3 + crates/ui-egui/src/i18n/uk.tsv | 3 + crates/ui-egui/src/i18n/vi.tsv | 3 + crates/ui-egui/src/i18n/zh-hans.tsv | 3 + crates/ui-egui/src/i18n/zh-hant.tsv | 3 + crates/ui-egui/src/panels/tools.rs | 3 + crates/ui-egui/src/tools/fill_sign.rs | 2 - crates/ui-egui/tests/it/fill_sign.rs | 97 ++++++++++++++++++++++++++- parity/acrobat-features.toml | 4 +- 30 files changed, 249 insertions(+), 21 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index a220644421..ac10aec000 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -642,6 +642,9 @@ pub struct DocView { pub fill_text: Option, /// A queued Fill & Sign signature: select it after its edit succeeds, then leave placement. pub(crate) fill_signature_page: Option, + /// The "Signature added … Lock now" notice was dismissed; placing another signature shows it + /// again (#2149). + pub(crate) lock_offer_dismissed: bool, pub(crate) signature_drag: crate::signature_drag::SignatureDrag, /// What the page modules claim for the cursor this frame; `document_area` sets it once. pub(crate) cursor: crate::page_cursor::PageCursor, @@ -736,6 +739,8 @@ pub enum ViewAction { CameraLevel, /// After Enhance page: leave the tool for this command (Crop pages, Organize pages). CameraNext(&'static str), + /// Lock now, on the notice shown after a signature is placed (#2149). + LockSignatures, } /// Default page right-click menu actions. @@ -1001,6 +1006,7 @@ impl DocView { marquee_done: None, fill_text: None, fill_signature_page: None, + lock_offer_dismissed: false, signature_drag: Default::default(), cursor: Default::default(), router: Default::default(), @@ -3030,6 +3036,12 @@ fn apply_notice(app: &mut PdfCraftApp, index: usize, notice: Option) { Some(Notice::Console) => app.dialog = Some(crate::Dialog::JsConsole), // Back on in Preferences ▸ General. Some(Notice::HideSecured) => app.show_secured_notice = false, + // Handled by the app with the other view actions (`ViewAction`). + Some(Notice::LockSignatures) => { + if let Some(view) = app.views.get_mut(index) { + view.pending_action = Some(ViewAction::LockSignatures); + } + } None => {} } } @@ -3499,13 +3511,19 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { (app.left_open && app.left == crate::LeftPanel::Tool("edit")) || matches!(app.quick_tool, QuickTool::AddText | QuickTool::EditText); let text_style = app.text_style.clone(); let show_secured_notice = app.show_secured_notice; + // Offer to lock a just-placed signature (#2149) on the same terms the save-time question + // asks: one is still there, saving doesn't lock it anyway, and the document isn't signed. + let lock_offer = !app.flatten_fill_sign_on_save + && app.placed_signatures.contains(&app.views[index].id) + && doc.info.annotations.iter().any(|a| a.fill_sign) + && !doc.is_signed(); let view = &mut app.views[index]; // Opened without the owner password and something is restricted. The notice says so unless // the user turned it off (#4035): the restrictions themselves, and Document Properties ▸ // Security, stay as they are. let secured = show_secured_notice && doc.security_summary().is_some_and(|s| !(s.owner || (s.permissions.modify() && s.permissions.assemble()))); let repaired = !doc.repair_log().is_empty(); - let notice = notices(view, doc, secured, repaired, crate::sign_ui::banner(&doc.signatures), ui, &t); + let notice = notices(view, doc, secured, repaired, lock_offer, crate::sign_ui::banner(&doc.signatures), ui, &t); apply_notice(app, index, notice); let Some(doc) = app.session.get(app.views[index].id) else { return }; let info = &doc.info; diff --git a/crates/ui-egui/src/canvas/bars.rs b/crates/ui-egui/src/canvas/bars.rs index 4ad2bf05db..9d488721f5 100644 --- a/crates/ui-egui/src/canvas/bars.rs +++ b/crates/ui-egui/src/canvas/bars.rs @@ -59,16 +59,20 @@ pub(super) enum Notice { Close, /// Stop showing the secured-document notice, in every document (#4035). HideSecured, + /// Lock the placed signatures into the page (#2149). + LockSignatures, } /// The notice bar above the pages: a file changed on disk first, then what a historical -/// snapshot is, then the signature status bar, a form's scripts having been turned -/// off, security, forms and warnings. +/// snapshot is, then the offer to lock a signature just placed, then the signature status bar, +/// a form's scripts having been turned off, security, forms and warnings. +#[allow(clippy::too_many_arguments)] pub(super) fn notices( view: &mut DocView, doc: &pdfcraft_engine::Document, secured: bool, repaired: bool, + lock_offer: bool, signed: Option<(&str, Color32, &str, String)>, ui: &mut egui::Ui, t: &Tokens, @@ -122,6 +126,22 @@ pub(super) fn notices( return Some(Notice::Close); } } + // A signature just placed with Fill & Sign is a comment others can move (#2149): offer to + // lock it into the page now. `lock_offer` is false for a digitally signed document. + if lock_offer && !view.lock_offer_dismissed { + let mut lock = false; + notice_bar(ui, t, "lock", t.accent_text, tl!("Signature added. Others can move it until it's locked.").to_string(), |ui| { + if icons::button(ui, "x", 22.0, false, tl!("Dismiss")).clicked() { + view.lock_offer_dismissed = true; + } + if crate::widgets::pill_button(ui, tl!("Lock now"), true).clicked() { + lock = true; + } + }); + if lock { + return Some(Notice::LockSignatures); + } + } if let Some((icon, color, template, arg)) = signed { let mut open = false; notice_bar(ui, t, icon, color, crate::i18n::fmt(tl!(template), &[("by", &arg)]), |ui| { diff --git a/crates/ui-egui/src/editing.rs b/crates/ui-egui/src/editing.rs index f753335b72..c97cf934d4 100644 --- a/crates/ui-egui/src/editing.rs +++ b/crates/ui-egui/src/editing.rs @@ -478,6 +478,11 @@ impl PdfCraftApp { } } Some(crate::canvas::ViewAction::CameraLevel) => self.camera_level(i), + Some(crate::canvas::ViewAction::LockSignatures) => { + if let Some(id) = self.views.get(i).map(|v| v.id) { + self.lock_signatures_now(id); + } + } Some(crate::canvas::ViewAction::CameraNext(command)) => { self.leave_camera(i); self.execute(command); @@ -681,6 +686,10 @@ impl PdfCraftApp { && let Some(id) = doc_id { self.placed_signatures.insert(id); + // A new signature shows the Lock now notice again, even if the last was dismissed. + if let Some(view) = self.views.get_mut(i) { + view.lock_offer_dismissed = false; + } } // Tab can wrap to the same field (including another widget of that field). // Its new editor was opened before this edit ran, so it still holds the old diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index 0ab9bd5b5d..131cb815e1 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -183,18 +183,8 @@ impl PdfCraftApp { /// again, and the answer isn't remembered. pub(crate) fn answer_lock_signatures(&mut self, lock: bool, remember: bool) { let Some(p) = self.pending_lock.take() else { return }; - if lock && !self.flatten_fill_sign_on_save { - let before = self.session.get(p.id).map(|d| d.edit_generation()); - if let Err(e) = self.session.apply(p.id, pdfcraft_engine::Edit::FlattenFillSign) { - self.notify_error(e); - return; - } - if before != self.session.get(p.id).map(|d| d.edit_generation()) - && let Some(doc) = self.session.get(p.id) - && let Some(view) = self.views.iter_mut().find(|v| v.id == p.id) - { - view.document_changed(&doc.info); - } + if lock && !self.flatten_fill_sign_on_save && !self.flatten_fill_sign_in(p.id) { + return; } self.placed_signatures.remove(&p.id); if remember { @@ -209,6 +199,37 @@ impl PdfCraftApp { } } + /// Lock now, on the notice shown after a signature is placed (#2149): lock document `id`'s + /// Fill & Sign marks into its pages, as the save-time question's Lock does but without + /// saving. Undoable. The next save doesn't ask, as no Fill & Sign mark is left to lock; the + /// placed-signature mark itself stays, so undoing the lock brings back both the notice and the + /// save-time question. Never on a digitally signed document. + pub(crate) fn lock_signatures_now(&mut self, id: pdfcraft_engine::DocId) { + if self.session.get(id).is_none_or(|d| d.is_signed()) { + return; + } + if self.flatten_fill_sign_in(id) { + self.notify_tr("Signature locked into the page."); + } + } + + /// Lock document `id`'s Fill & Sign marks into its pages (an undoable edit). Returns false, + /// having told the user why, when that fails. + fn flatten_fill_sign_in(&mut self, id: pdfcraft_engine::DocId) -> bool { + let before = self.session.get(id).map(|d| d.edit_generation()); + if let Err(e) = self.session.apply(id, pdfcraft_engine::Edit::FlattenFillSign) { + self.notify_error(e); + return false; + } + if before != self.session.get(id).map(|d| d.edit_generation()) + && let Some(doc) = self.session.get(id) + && let Some(view) = self.views.iter_mut().find(|v| v.id == id) + { + view.document_changed(&doc.info); + } + true + } + /// Write document `id` to `dest` and make it the document's file. Returns `true` if written. #[cfg(not(target_arch = "wasm32"))] fn save_doc_to(&mut self, id: pdfcraft_engine::DocId, dest: &str, target: SaveTarget) -> bool { diff --git a/crates/ui-egui/src/i18n/ar.tsv b/crates/ui-egui/src/i18n/ar.tsv index 746e20477d..bec6e3aad6 100644 --- a/crates/ui-egui/src/i18n/ar.tsv +++ b/crates/ui-egui/src/i18n/ar.tsv @@ -2286,6 +2286,9 @@ calendar date December ديسمبر Keep editable إبقاؤه قابلًا للتحرير Lock into the page when saving التثبيت في الصفحة عند الحفظ Otherwise, ask on the first save after a signature is placed وإلا فاسأل عند أول حفظ بعد وضع توقيع + Signature added. Others can move it until it's locked. تمت إضافة التوقيع. يمكن للآخرين نقله إلى أن يتم تثبيته. + Lock now التثبيت الآن + Signature locked into the page. تم تثبيت التوقيع في الصفحة. Text, marks and signatures become part of the page. Other comments stay editable. النص والعلامات والتوقيعات تصبح جزءًا من الصفحة. التعليقات الأخرى تبقى قابلة للتحرير. Date format تنسيق التاريخ Fill & Sign dates تواريخ التعبئة والتوقيع diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 516fbb62c9..47abedb819 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -146,6 +146,9 @@ Keep editable Да остане редактируем Lock into the page when saving Заключване в страницата при запис Otherwise, ask on the first save after a signature is placed Иначе питай при първия запис след поставяне на подпис + Signature added. Others can move it until it's locked. Подписът е добавен. Други могат да го преместват, докато не бъде заключен. + Lock now Заключи сега + Signature locked into the page. Подписът е заключен в страницата. Text, marks and signatures become part of the page. Other comments stay editable. Текстът, маркировките и подписите стават част от страницата. Другите коментари остават редактируеми. OK OK Cancel Отказ diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 805b9c1885..61bba6af53 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -2307,6 +2307,9 @@ calendar date December prosince Keep editable Ponechat upravitelné Lock into the page when saving Při ukládání zamknout do stránky Otherwise, ask on the first save after a signature is placed Jinak se zeptat při prvním uložení po umístění podpisu + Signature added. Others can move it until it's locked. Podpis byl přidán. Ostatní ho mohou přesouvat, dokud nebude zamčen. + Lock now Zamknout nyní + Signature locked into the page. Podpis byl zamčen do stránky. Text, marks and signatures become part of the page. Other comments stay editable. Text, značky a podpisy se stanou součástí stránky. Ostatní komentáře zůstanou upravitelné. Date format Formát data Fill & Sign dates Datum pro Vyplnit a podepsat diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 2a4a5b4e0e..c0fcca266e 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -79,6 +79,9 @@ Keep editable Bearbeitbar lassen Lock into the page when saving Beim Speichern fest in die Seite übernehmen Otherwise, ask on the first save after a signature is placed Andernfalls beim ersten Speichern nach dem Platzieren einer Unterschrift fragen + Signature added. Others can move it until it's locked. Unterschrift hinzugefügt. Andere können sie verschieben, bis sie fest übernommen ist. + Lock now Jetzt übernehmen + Signature locked into the page. Unterschrift fest in die Seite übernommen. Text, marks and signatures become part of the page. Other comments stay editable. Text, Markierungen und Unterschriften werden Teil der Seite. Andere Kommentare bleiben bearbeitbar. OK OK Join our Discord Unserem Discord beitreten diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 6e031b4e4f..83bd80433d 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -294,6 +294,9 @@ Keep editable Να παραμείνει επεξεργάσιμη Lock into the page when saving Κλείδωμα στη σελίδα κατά την αποθήκευση Otherwise, ask on the first save after a signature is placed Διαφορετικά, ερώτηση στην πρώτη αποθήκευση μετά την τοποθέτηση υπογραφής + Signature added. Others can move it until it's locked. Η υπογραφή προστέθηκε. Άλλοι μπορούν να τη μετακινήσουν μέχρι να κλειδωθεί. + Lock now Κλείδωμα τώρα + Signature locked into the page. Η υπογραφή κλειδώθηκε στη σελίδα. Text, marks and signatures become part of the page. Other comments stay editable. Το κείμενο, τα σημάδια και οι υπογραφές γίνονται μέρος της σελίδας. Τα υπόλοιπα σχόλια παραμένουν επεξεργάσιμα. Current page — type a page number or label (such as iv) and press Enter Τρέχουσα σελίδα — πληκτρολογήστε αριθμό ή ετικέτα σελίδας (π.χ. iv) και πατήστε Enter Zoom — type a percentage and press Enter Ζουμ — πληκτρολογήστε ένα ποσοστό και πατήστε Enter diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 10897567ac..90670ed8c3 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -102,6 +102,9 @@ Keep editable Mantener editable Lock into the page when saving Fijar en la página al guardar Otherwise, ask on the first save after a signature is placed Si no, preguntar al guardar por primera vez tras colocar una firma + Signature added. Others can move it until it's locked. Firma añadida. Otros pueden moverla hasta que se fije. + Lock now Fijar ahora + Signature locked into the page. Firma fijada en la página. Text, marks and signatures become part of the page. Other comments stay editable. El texto, las marcas y las firmas pasan a formar parte de la página. Los demás comentarios siguen siendo editables. OK Aceptar Join our Discord Únete a nuestro Discord diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 0c4572fe22..ec03bf9065 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -79,6 +79,9 @@ Keep editable Garder modifiable Lock into the page when saving Intégrer à la page à l’enregistrement Otherwise, ask on the first save after a signature is placed Sinon, demander au premier enregistrement après avoir placé une signature + Signature added. Others can move it until it's locked. Signature ajoutée. D’autres peuvent la déplacer tant qu’elle n’est pas intégrée. + Lock now Intégrer maintenant + Signature locked into the page. Signature intégrée à la page. Text, marks and signatures become part of the page. Other comments stay editable. Le texte, les marques et les signatures deviennent partie de la page. Les autres commentaires restent modifiables. OK OK Join our Discord Rejoindre notre Discord diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 7a066c9ba3..62bdec8418 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -2263,6 +2263,9 @@ action wizard Start Indítás Keep editable Szerkeszthető marad Lock into the page when saving Mentéskor rögzítés az oldalba Otherwise, ask on the first save after a signature is placed Különben kérdezzen rá az aláírás elhelyezése utáni első mentéskor + Signature added. Others can move it until it's locked. Aláírás hozzáadva. Mások áthelyezhetik, amíg nincs rögzítve. + Lock now Rögzítés most + Signature locked into the page. Az aláírás rögzítve az oldalba. Text, marks and signatures become part of the page. Other comments stay editable. A szöveg, a jelek és az aláírások az oldal részévé válnak. A többi megjegyzés szerkeszthető marad. Date format Dátumformátum Fill & Sign dates Kitöltés és aláírás dátumai diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index ae3ebd6693..cb4af09c53 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -2223,6 +2223,9 @@ Keep editable Mantieni modificabile Lock into the page when saving Blocca nella pagina al salvataggio Otherwise, ask on the first save after a signature is placed Altrimenti, chiedi al primo salvataggio dopo aver inserito una firma + Signature added. Others can move it until it's locked. Firma aggiunta. Altri possono spostarla finché non viene bloccata. + Lock now Blocca ora + Signature locked into the page. Firma bloccata nella pagina. H / Space (hold) H / Spazio (tieni premuto) Home / End Home / Fine Inserted text Testo inserito diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index ddeb261438..bb485aa589 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -2278,6 +2278,9 @@ signature pad Type 入力 Keep editable 編集可能のままにする Lock into the page when saving 保存時にページに固定 Otherwise, ask on the first save after a signature is placed それ以外は、署名を配置した後の最初の保存時に確認する + Signature added. Others can move it until it's locked. 署名を追加しました。固定するまでは他の人が移動できます。 + Lock now 今すぐ固定 + Signature locked into the page. 署名をページに固定しました。 Text, marks and signatures become part of the page. Other comments stay editable. テキスト、マーク、署名がページの一部になります。その他のコメントは引き続き編集できます。 Scroll tabs left タブを左にスクロール Scroll tabs right タブを右にスクロール diff --git a/crates/ui-egui/src/i18n/kk.tsv b/crates/ui-egui/src/i18n/kk.tsv index 8171e1cac4..01cd4375f2 100644 --- a/crates/ui-egui/src/i18n/kk.tsv +++ b/crates/ui-egui/src/i18n/kk.tsv @@ -2195,6 +2195,9 @@ calendar date December Желтоқсан Keep editable Өңделетін күйде қалдыру Lock into the page when saving Сақтағанда бетке бекіту Otherwise, ask on the first save after a signature is placed Әйтпесе, қолтаңба қойылғаннан кейінгі алғашқы сақтауда сұрау + Signature added. Others can move it until it's locked. Қолтаңба қосылды. Ол бекітілгенше басқалар оны жылжыта алады. + Lock now Қазір бекіту + Signature locked into the page. Қолтаңба бетке бекітілді. Text, marks and signatures become part of the page. Other comments stay editable. Мәтін, белгілер мен қолтаңбалар беттің бөлігіне айналады. Басқа түсініктемелер өңделетін күйде қалады. Date format Күн пішімі Fill & Sign dates «Толтыру және қол қою» күндері diff --git a/crates/ui-egui/src/i18n/lt.tsv b/crates/ui-egui/src/i18n/lt.tsv index d93109274e..20fa9230c7 100644 --- a/crates/ui-egui/src/i18n/lt.tsv +++ b/crates/ui-egui/src/i18n/lt.tsv @@ -598,6 +598,9 @@ Keep editable Palikti redaguojamą Lock into the page when saving Įrašant įtvirtinti puslapyje Otherwise, ask on the first save after a signature is placed Kitaip klausti pirmą kartą įrašant po parašo padėjimo + Signature added. Others can move it until it's locked. Parašas pridėtas. Kiti gali jį perkelti, kol jis neįtvirtintas. + Lock now Įtvirtinti dabar + Signature locked into the page. Parašas įtvirtintas puslapyje. Flip Horizontal Apversti horizontaliai Flip Vertical Apversti vertikaliai Flip horizontal Apversti horizontaliai diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index f071c3cda3..9a7a5f0089 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -2257,6 +2257,9 @@ signature pad Type Wpisz Keep editable Zachowaj edytowalny Lock into the page when saving Utrwalaj na stronie przy zapisie Otherwise, ask on the first save after a signature is placed W przeciwnym razie pytaj przy pierwszym zapisie po umieszczeniu podpisu + Signature added. Others can move it until it's locked. Dodano podpis. Inni mogą go przesunąć, dopóki nie zostanie utrwalony. + Lock now Utrwal teraz + Signature locked into the page. Podpis utrwalono na stronie. Text, marks and signatures become part of the page. Other comments stay editable. Tekst, oznaczenia i podpisy stają się częścią strony. Pozostałe komentarze pozostają edytowalne. Scroll tabs left Przewiń karty w lewo Scroll tabs right Przewiń karty w prawo diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index eae76f0a35..5f143e88b7 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -2216,6 +2216,9 @@ action wizard Start Iniciar Keep editable Manter editável Lock into the page when saving Fixar na página ao salvar Otherwise, ask on the first save after a signature is placed Caso contrário, perguntar ao salvar pela primeira vez após colocar uma assinatura + Signature added. Others can move it until it's locked. Assinatura adicionada. Outras pessoas podem movê-la até que seja fixada. + Lock now Fixar agora + Signature locked into the page. Assinatura fixada na página. H / Space (hold) H / Espaço (manter pressionado) Home / End Home / End Inserted text Texto inserido diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 0b5274603c..0545896c55 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -99,6 +99,9 @@ Keep editable Оставить редактируемой Lock into the page when saving Закреплять на странице при сохранении Otherwise, ask on the first save after a signature is placed Иначе спрашивать при первом сохранении после размещения подписи + Signature added. Others can move it until it's locked. Подпись добавлена. Другие могут перемещать её, пока она не закреплена. + Lock now Закрепить сейчас + Signature locked into the page. Подпись закреплена на странице. Text, marks and signatures become part of the page. Other comments stay editable. Текст, пометки и подписи становятся частью страницы. Остальные комментарии можно по-прежнему править. OK OK Join our Discord Присоединяйтесь к нашему Discord diff --git a/crates/ui-egui/src/i18n/sl.tsv b/crates/ui-egui/src/i18n/sl.tsv index 41286d281a..c938b17fbc 100644 --- a/crates/ui-egui/src/i18n/sl.tsv +++ b/crates/ui-egui/src/i18n/sl.tsv @@ -2211,6 +2211,9 @@ signature pad Type Vtipkaj Keep editable Ohrani urejljivo Lock into the page when saving Ob shranjevanju zakleni v stran Otherwise, ask on the first save after a signature is placed Sicer vprašaj ob prvem shranjevanju po postavitvi podpisa + Signature added. Others can move it until it's locked. Podpis je dodan. Drugi ga lahko premaknejo, dokler ni zaklenjen. + Lock now Zakleni zdaj + Signature locked into the page. Podpis je zaklenjen v stran. Font size 0 uses automatic sizing. Velikost pisave 0 pomeni samodejno velikost. Import list… Uvozi seznam… Larger pages Večje strani diff --git a/crates/ui-egui/src/i18n/te.tsv b/crates/ui-egui/src/i18n/te.tsv index 0082655ae8..4d08bfa192 100644 --- a/crates/ui-egui/src/i18n/te.tsv +++ b/crates/ui-egui/src/i18n/te.tsv @@ -2511,6 +2511,9 @@ animation effects Off ఆఫ్ Keep editable సవరించగలిగేలా ఉంచు Lock into the page when saving సేవ్ చేసేటప్పుడు పేజీలో లాక్ చేయి Otherwise, ask on the first save after a signature is placed లేకపోతే, సంతకం ఉంచిన తర్వాత మొదటి సేవ్‌లో అడుగు + Signature added. Others can move it until it's locked. సంతకం జోడించబడింది. లాక్ చేసే వరకు ఇతరులు దానిని తరలించగలరు. + Lock now ఇప్పుడే లాక్ చేయి + Signature locked into the page. సంతకం పేజీలో లాక్ చేయబడింది. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today ఈరోజు diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index d40032efa8..20ab89ee2b 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -70,6 +70,9 @@ Keep editable Düzenlenebilir bırak Lock into the page when saving Kaydederken sayfaya sabitle Otherwise, ask on the first save after a signature is placed Aksi halde, imza yerleştirildikten sonraki ilk kayıtta sor + Signature added. Others can move it until it's locked. İmza eklendi. Sabitlenene kadar başkaları onu taşıyabilir. + Lock now Şimdi sabitle + Signature locked into the page. İmza sayfaya sabitlendi. Text, marks and signatures become part of the page. Other comments stay editable. Metin, işaretler ve imzalar sayfanın bir parçası olur. Diğer yorumlar düzenlenebilir kalır. OK Tamam Join our Discord Discord'umuza katılın diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 30c8000fc8..3c181c5b2b 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -2286,6 +2286,9 @@ calendar date December грудня Keep editable Залишити редагованим Lock into the page when saving Закріплювати на сторінці під час збереження Otherwise, ask on the first save after a signature is placed Інакше питати під час першого збереження після розміщення підпису + Signature added. Others can move it until it's locked. Підпис додано. Інші можуть переміщувати його, доки його не закріплено. + Lock now Закріпити зараз + Signature locked into the page. Підпис закріплено на сторінці. Text, marks and signatures become part of the page. Other comments stay editable. Текст, позначки й підписи стають частиною сторінки. Інші коментарі залишаються редагованими. Date format Формат дати Fill & Sign dates Дати заповнення й підпису diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 2275b4b78a..095c6e8832 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -2894,3 +2894,6 @@ key Space Space Lock into the page when saving Khóa vào trang khi lưu Lock your signature into the page? Khóa chữ ký của bạn vào trang? Otherwise, ask on the first save after a signature is placed Nếu không, hỏi ở lần lưu đầu tiên sau khi đặt chữ ký + Signature added. Others can move it until it's locked. Đã thêm chữ ký. Người khác có thể di chuyển nó cho đến khi được khóa. + Lock now Khóa ngay + Signature locked into the page. Đã khóa chữ ký vào trang. diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index c5c7918373..9959eb8a7e 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -152,6 +152,9 @@ Keep editable 保持可编辑 Lock into the page when saving 保存时锁定到页面 Otherwise, ask on the first save after a signature is placed 否则,在放置签名后首次保存时询问 + Signature added. Others can move it until it's locked. 已添加签名。锁定之前,其他人可以移动它。 + Lock now 立即锁定 + Signature locked into the page. 签名已锁定到页面。 Text, marks and signatures become part of the page. Other comments stay editable. 文字、标记和签名会成为页面内容。其他注释仍可编辑。 OK 确定 Cancel 取消 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 9ef514a3b5..4f5a08dcbd 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2552,6 +2552,9 @@ animation effects Off 關 Keep editable 保持可編輯 Lock into the page when saving 儲存時鎖定到頁面 Otherwise, ask on the first save after a signature is placed 否則,在放置簽名後第一次儲存時詢問 + Signature added. Others can move it until it's locked. 已加入簽名。鎖定之前,其他人可以移動它。 + Lock now 立即鎖定 + Signature locked into the page. 簽名已鎖定到頁面。 # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today 今天 diff --git a/crates/ui-egui/src/panels/tools.rs b/crates/ui-egui/src/panels/tools.rs index 73326fe571..6ab469e123 100644 --- a/crates/ui-egui/src/panels/tools.rs +++ b/crates/ui-egui/src/panels/tools.rs @@ -291,6 +291,9 @@ fn tool_detail(app: &mut PdfCraftApp, ui: &mut egui::Ui, t: &Tokens, g: &'static if let Some(cmd) = crate::fill_sign::signature_entries(ui, app, t) { run = Some(cmd); } + // Where the signing happens, not only in Preferences, and once: not also in the Sign + // menu over the page, where it was easy to miss (#2149). + ui.add(widgets::checkbox(&mut app.flatten_fill_sign_on_save, tl!("Lock into the page when saving"))); } }); if footer { diff --git a/crates/ui-egui/src/tools/fill_sign.rs b/crates/ui-egui/src/tools/fill_sign.rs index 225faca601..045bb64974 100644 --- a/crates/ui-egui/src/tools/fill_sign.rs +++ b/crates/ui-egui/src/tools/fill_sign.rs @@ -398,8 +398,6 @@ pub(crate) fn signature_entries(ui: &mut egui::Ui, app: &mut crate::PdfCraftApp, }); ui.add_space(4.0); } - // Where the signing happens, not only in Preferences (#2149). - ui.add(crate::widgets::checkbox(&mut app.flatten_fill_sign_on_save, tl!("Lock into the page when saving"))); command } diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index 3c7eb8a0d8..733fed5bde 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -1754,7 +1754,8 @@ fn photos_and_images_dropped_on_the_signature_pad_are_imported_not_opened() { /// #2149: the first save after Fill & Sign places a signature asks whether to lock it into the /// page. Lock and save flattens it; Keep editable keeps it a comment; Don't ask again becomes -/// the preference; the Sign menu has the switch too. +/// the preference; the Fill & Sign panel has the switch too, once, and the Sign menu no longer +/// does (review on #2348). #[test] fn the_first_save_after_placing_a_signature_asks_to_lock_it() { let out = std::env::temp_dir().join(format!("pdfcraft-lock-signature-{}.pdf", std::process::id())); @@ -1790,18 +1791,110 @@ fn the_first_save_after_placing_a_signature_asks_to_lock_it() { assert_eq!(signed(&h), 0, "locked into the page"); assert!(!h.state().session.get(h.state().views[0].id).unwrap().dirty); assert!(h.state().flatten_fill_sign_on_save, "remembered as the preference"); - // The Sign menu shows the same switch. + // The switch is in the panel's Sign yourself section, once; the Sign menu over the page, + // where it was easy to miss, no longer has it. h.state_mut().flatten_fill_sign_on_save = false; h.get_by_label("Fill & Sign").click(); h.run_steps(2); h.get_by_label("Fill & Sign").click(); h.run_steps(2); + h.get_by_label("Use signature"); + assert!(h.query_by_label("Lock into the page when saving").is_none(), "not in the Sign menu"); + h.key_press(egui::Key::Escape); + h.run_steps(2); + assert!(h.query_by_label("Lock into the page when saving").is_none(), "the panel is closed"); + h.state_mut().left = pdfcraft_ui_egui::LeftPanel::Tool("fill_sign"); + h.state_mut().left_open = true; + h.run_steps(3); + h.get_by_label("Use signature"); + assert_eq!(h.query_all_by_label("Lock into the page when saving").count(), 1, "once, in the panel"); h.get_by_label("Lock into the page when saving").click(); h.run_steps(2); assert!(h.state().flatten_fill_sign_on_save); let _ = std::fs::remove_file(&out); } +/// #2348 review (benandez): right after a signature is placed, a notice offers to lock it into +/// the page now. Lock now flattens it (undoable) without saving, so the save that follows doesn't +/// ask; undoing the lock brings back both the notice and the question. Dismiss hides the notice +/// until the next signature. A digitally signed document, or one whose saves lock anyway, gets no +/// notice. +#[test] +fn placing_a_signature_offers_to_lock_it_now() { + const NOTICE: &str = "Signature added. Others can move it until it's locked."; + let out = std::env::temp_dir().join(format!("pdfcraft-lock-now-{}.pdf", std::process::id())); + let place = |h: &mut Harness<'static, PdfCraftApp>, y: f32| { + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(h, 40.0, y); + h.run_steps(2); + }; + let marks = |h: &Harness<'static, PdfCraftApp>| { + h.state().session.get(h.state().views[0].id).unwrap().info.annotations.iter().filter(|a| a.fill_sign).count() + }; + let mut h = harness(); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + assert!(h.query_by_label(NOTICE).is_none(), "nothing placed yet"); + place(&mut h, 300.0); + h.get_by_label(NOTICE); + h.get_by_label("Lock now").click(); + h.run_steps(3); + assert_eq!(marks(&h), 0, "locked into the page"); + assert!(h.query_by_label(NOTICE).is_none(), "nothing left to lock"); + assert!(h.state().session.get(h.state().views[0].id).unwrap().dirty, "locked, not saved"); + // Undo brings the movable signature back, with the notice and the save-time question. + assert!(h.state_mut().execute("edit.undo")); + h.run_steps(3); + assert_eq!(marks(&h), 1); + h.get_by_label(NOTICE); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks again after undoing the lock"); + h.run_steps(2); + h.get_by_label("Cancel").click(); + h.run_steps(2); + // Locked again, the save doesn't ask. + h.get_by_label("Lock now").click(); + h.run_steps(3); + assert!(h.state_mut().save_active(SaveTarget::InPlace), "already locked: no question"); + // Dismiss hides it until the next signature. + place(&mut h, 200.0); + h.get_by_label(NOTICE); + h.get_by_label("Dismiss").click(); + h.run_steps(2); + assert!(h.query_by_label(NOTICE).is_none(), "dismissed"); + place(&mut h, 100.0); + h.get_by_label(NOTICE); + // Once saved, a signature kept as a comment is no longer just placed: no notice. + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks first"); + h.run_steps(2); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + assert!(marks(&h) > 0, "still a comment"); + assert!(h.query_by_label(NOTICE).is_none(), "saved: nothing just placed"); + place(&mut h, 50.0); + h.get_by_label(NOTICE); + // Saves that lock anyway need no offer. + h.state_mut().flatten_fill_sign_on_save = true; + h.run_steps(2); + assert!(h.query_by_label(NOTICE).is_none(), "saving locks it anyway"); + let _ = std::fs::remove_file(&out); + + // Digitally signed: no notice, as locking would change the document after signing. + let mut doc = { + let mut h = harness(); + let app = h.state_mut(); + let id = app.views[0].id; + let p12 = std::fs::read(concat!(env!("CARGO_MANIFEST_DIR"), "/../sign/tests/data/ec-p256.p12")).unwrap(); + let digital_id = pdfcraft_engine::sign::pkcs12::open(&p12, "test").unwrap(); + let opts = pdfcraft_engine::SignOptions { page: 0, rect: Some([20.0, 20.0, 160.0, 50.0]), ..Default::default() }; + app.session.sign(id, &digital_id, opts).expect("signs") + }; + let mut h = harness_bytes(&std::mem::take(&mut doc)); + place(&mut h, 300.0); + assert_eq!(marks(&h), 1); + assert!(h.query_by_label(NOTICE).is_none(), "a signed document isn't offered the lock"); +} + /// #2348 review: when locking the signature fails (here the document's security allows comments /// but not changes), nothing is lost: the next save asks again, and "Don't ask again" isn't /// remembered. diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index cd0c858371..bec5d3568f 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -5441,8 +5441,8 @@ milestone = "M5" status = "shipped" commands = ["comment.flatten"] tools = ["doc_save", "doc_flatten"] -evidence = ["crates/edit/src/tests.rs::fill_sign_flatten_bakes_marks_keeps_other_comments_and_does_not_reuse_pcfl0", "crates/engine/src/tests.rs::flattening_fill_and_sign_keeps_other_comments_fields_and_undoes", "crates/engine/src/tests.rs::flattening_fill_and_sign_is_refused_without_permission_to_change", "crates/automation/tests/it/automation.rs::saving_with_flatten_fill_sign_bakes_marks_and_leaves_other_comments", "crates/ui-egui/tests/it/fill_sign.rs::saving_flattens_fill_and_sign_only_when_the_preference_is_on", "crates/ui-egui/tests/it/fill_sign.rs::the_first_save_after_placing_a_signature_asks_to_lock_it", "crates/ui-egui/tests/it/fill_sign.rs::cancelling_the_lock_question_saves_nothing", "crates/ui-egui/tests/it/fill_sign.rs::a_lock_that_fails_keeps_the_question", "crates/ui-egui/tests/it/fill_sign.rs::the_lock_question_is_skipped_or_kept_where_it_must_be"] -notes = "Preferences ▸ Fill & Sign, and doc_save flatten_fill_sign, bake text, marks and signatures into the page. The option defaults off, so a normal save leaves them editable; the first save after a signature or initials is placed asks whether to lock it into the page (Lock and save / Keep editable, with Don't ask again), and the Sign menu carries the same switch (#2149). Other comments and form fields stay. Flatten comments still flattens every comment." +evidence = ["crates/edit/src/tests.rs::fill_sign_flatten_bakes_marks_keeps_other_comments_and_does_not_reuse_pcfl0", "crates/engine/src/tests.rs::flattening_fill_and_sign_keeps_other_comments_fields_and_undoes", "crates/engine/src/tests.rs::flattening_fill_and_sign_is_refused_without_permission_to_change", "crates/automation/tests/it/automation.rs::saving_with_flatten_fill_sign_bakes_marks_and_leaves_other_comments", "crates/ui-egui/tests/it/fill_sign.rs::saving_flattens_fill_and_sign_only_when_the_preference_is_on", "crates/ui-egui/tests/it/fill_sign.rs::placing_a_signature_offers_to_lock_it_now", "crates/ui-egui/tests/it/fill_sign.rs::the_first_save_after_placing_a_signature_asks_to_lock_it", "crates/ui-egui/tests/it/fill_sign.rs::cancelling_the_lock_question_saves_nothing", "crates/ui-egui/tests/it/fill_sign.rs::a_lock_that_fails_keeps_the_question", "crates/ui-egui/tests/it/fill_sign.rs::the_lock_question_is_skipped_or_kept_where_it_must_be"] +notes = "Preferences ▸ Fill & Sign, and doc_save flatten_fill_sign, bake text, marks and signatures into the page. The option defaults off, so a normal save leaves them editable; right after a signature or initials is placed, a notice offers Lock now (undoable, no save), and the first save after one is placed asks whether to lock it into the page (Lock and save / Keep editable, with Don't ask again); the Fill & Sign panel's Sign yourself section carries the same switch (#2149). Not on a digitally signed document. Other comments and form fields stay. Flatten comments still flattens every comment." [[feature]] id = "form.autofill-profile" From 78e15fc4cf32e4d08b138df51d5f228283822195 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Thu, 22 Oct 2026 19:28:12 +0800 Subject: [PATCH 07/10] i18n: translate the signature-lock prompts into the newer catalogues (#2149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Catalan, Sorani, Finnish, Hindi, Indonesian, Korean, Lao, Norwegian Bokmål, Romanian, Albanian and Thai joined main after this branch was written; their coverage tests ask for these strings too. AI-assisted, as noted in each catalogue. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/i18n/ca.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/ckb.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/fi.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/hi.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/id.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/ko.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/lo.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/nb.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/ro.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/sq.tsv | 10 ++++++++++ crates/ui-egui/src/i18n/th.tsv | 10 ++++++++++ 11 files changed, 110 insertions(+) diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 7ff0f83fee..aff4f22037 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -2846,6 +2846,16 @@ theme Appearance Aspecte Unpin tool Desenganxa l'eina Arrange image Organitza la imatge Crop Escapça + Lock your signature into the page? Voleu fixar la signatura a la pàgina? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Una signatura col·locada és un comentari fins que es fixa: qualsevol persona amb un editor de PDF la pot moure o eliminar. En fixar-la, el text, les marques i les signatures d'Omple i signa passen a formar part de la pàgina. + Lock and save Fixa i desa + Keep editable Mantén editable + Don't ask again No ho tornis a preguntar + Lock into the page when saving Fixa a la pàgina en desar + Otherwise, ask on the first save after a signature is placed Si no, pregunta en el primer desament després de col·locar una signatura + Signature added. Others can move it until it's locked. S'ha afegit la signatura. Altres persones la poden moure fins que es fixi. + Lock now Fixa ara + Signature locked into the page. Signatura fixada a la pàgina. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today Avui diff --git a/crates/ui-egui/src/i18n/ckb.tsv b/crates/ui-egui/src/i18n/ckb.tsv index 3b6a7671ed..28c37782a5 100644 --- a/crates/ui-egui/src/i18n/ckb.tsv +++ b/crates/ui-egui/src/i18n/ckb.tsv @@ -2754,6 +2754,16 @@ certificate Version وەشان No tools match your search. هیچ ئامرازێک لەگەڵ گەڕانەکەت ناگونجێت. Expand all tools فراوانکردنی هەموو ئامرازەکان Collapse all tools کۆکردنەوەی هەموو ئامرازەکان + Lock your signature into the page? واژووەکەت لە لاپەڕەکەدا جێگیر بکرێت؟ + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. واژوویەکی دانراو تا جێگیر نەکرێت لێدوانێکە: هەر کەسێک دەستکاریکەرێکی PDFی هەبێت دەتوانێت بیجووڵێنێت یان لایبەرێت. جێگیرکردن دەقەکان، نیشانەکان و واژووەکانی پڕکردنەوە و واژووکردن دەکاتە بەشێک لە لاپەڕەکە. + Lock and save جێگیرکردن و پاشەکەوتکردن + Keep editable با دەستکاریکراو بمێنێتەوە + Don't ask again دووبارە مەپرسە + Lock into the page when saving لە کاتی پاشەکەوتکردندا لە لاپەڕەکەدا جێگیر بکە + Otherwise, ask on the first save after a signature is placed ئەگەر نا، لە یەکەم پاشەکەوتکردن دوای دانانی واژوویەک بپرسە + Signature added. Others can move it until it's locked. واژوو زیادکرا. کەسانی تر دەتوانن بیجووڵێنن تا جێگیر دەکرێت. + Lock now ئێستا جێگیر بکە + Signature locked into the page. واژوو لە لاپەڕەکەدا جێگیرکرا. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today ئەمڕۆ diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 8c1a9baab9..cfd2add95e 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -2856,6 +2856,16 @@ theme Appearance Teema Unpin tool Irrota työkalu Arrange image Järjestä kuva Crop Rajaa + Lock your signature into the page? Lukitaanko allekirjoitus sivuun? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Sijoitettu allekirjoitus on kommentti, kunnes se lukitaan: kuka tahansa PDF-muokkaimen käyttäjä voi siirtää tai poistaa sen. Lukitseminen tekee Täytä ja allekirjoita -tekstistä, -merkinnöistä ja -allekirjoituksista osan sivua. + Lock and save Lukitse ja tallenna + Keep editable Pidä muokattavana + Don't ask again Älä kysy uudelleen + Lock into the page when saving Lukitse sivuun tallennettaessa + Otherwise, ask on the first save after a signature is placed Muuten kysy ensimmäisellä tallennuskerralla allekirjoituksen sijoittamisen jälkeen + Signature added. Others can move it until it's locked. Allekirjoitus lisätty. Muut voivat siirtää sitä, kunnes se lukitaan. + Lock now Lukitse nyt + Signature locked into the page. Allekirjoitus lukittu sivuun. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today Tänään diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 52c5450bb3..a3f4765491 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -2816,6 +2816,16 @@ theme Appearance स्वरूप Remove crop क्रॉप हटाएँ Send backward पीछे भेजें Send to back सबसे पीछे भेजें + Lock your signature into the page? अपने हस्ताक्षर को पृष्ठ में लॉक करें? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. रखा गया हस्ताक्षर तब तक एक टिप्पणी है जब तक उसे लॉक न किया जाए: PDF संपादक वाला कोई भी व्यक्ति उसे हटा या खिसका सकता है। लॉक करने से आपके भरें व हस्ताक्षर करें के टेक्स्ट, चिह्न और हस्ताक्षर पृष्ठ का हिस्सा बन जाते हैं। + Lock and save लॉक करें और सहेजें + Keep editable संपादन योग्य रखें + Don't ask again फिर से न पूछें + Lock into the page when saving सहेजते समय पृष्ठ में लॉक करें + Otherwise, ask on the first save after a signature is placed अन्यथा, हस्ताक्षर रखने के बाद पहली बार सहेजने पर पूछें + Signature added. Others can move it until it's locked. हस्ताक्षर जोड़ा गया। लॉक होने तक दूसरे इसे खिसका सकते हैं। + Lock now अभी लॉक करें + Signature locked into the page. हस्ताक्षर पृष्ठ में लॉक कर दिया गया। # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today आज diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 89152f62a4..d9d5395ae9 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -2817,6 +2817,16 @@ theme Appearance Tampilan Remove crop Hapus pangkasan Send backward Mundur Selangkah Send to back Kirim ke Belakang + Lock your signature into the page? Kunci tanda tangan Anda ke halaman? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Tanda tangan yang ditempatkan adalah komentar sampai dikunci: siapa pun yang memiliki editor PDF dapat memindahkan atau menghapusnya. Mengunci menjadikan teks, tanda, dan tanda tangan Isi & Tanda Tangani Anda bagian dari halaman. + Lock and save Kunci dan simpan + Keep editable Biarkan dapat diedit + Don't ask again Jangan tanya lagi + Lock into the page when saving Kunci ke halaman saat menyimpan + Otherwise, ask on the first save after a signature is placed Jika tidak, tanyakan saat penyimpanan pertama setelah tanda tangan ditempatkan + Signature added. Others can move it until it's locked. Tanda tangan ditambahkan. Orang lain dapat memindahkannya sampai dikunci. + Lock now Kunci sekarang + Signature locked into the page. Tanda tangan dikunci ke halaman. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today Hari ini diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 20315b5fcf..de83b8b64b 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -2816,6 +2816,16 @@ theme Appearance 모양 Remove crop 자르기 제거 Send backward 뒤로 보내기 Send to back 맨 뒤로 보내기 + Lock your signature into the page? 서명을 페이지에 고정하시겠습니까? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. 배치한 서명은 고정하기 전까지 주석입니다. PDF 편집기가 있는 사람은 누구나 이를 이동하거나 제거할 수 있습니다. 고정하면 채우기 및 서명의 텍스트, 표시 및 서명이 페이지의 일부가 됩니다. + Lock and save 고정 및 저장 + Keep editable 편집 가능하게 유지 + Don't ask again 다시 묻지 않음 + Lock into the page when saving 저장할 때 페이지에 고정 + Otherwise, ask on the first save after a signature is placed 그렇지 않으면 서명을 배치한 후 처음 저장할 때 묻기 + Signature added. Others can move it until it's locked. 서명이 추가되었습니다. 고정하기 전까지 다른 사람이 이동할 수 있습니다. + Lock now 지금 고정 + Signature locked into the page. 서명이 페이지에 고정되었습니다. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today 오늘 diff --git a/crates/ui-egui/src/i18n/lo.tsv b/crates/ui-egui/src/i18n/lo.tsv index ea684f6d59..05e33773ee 100644 --- a/crates/ui-egui/src/i18n/lo.tsv +++ b/crates/ui-egui/src/i18n/lo.tsv @@ -2470,6 +2470,16 @@ animation effects Off ປິດ No tools match your search. ບໍ່ມີເຄື່ອງມືທີ່ກົງກັບການຄົ້ນຫາຂອງທ່ານ. Expand all tools ຂະຫຍາຍເຄື່ອງມືທັງໝົດ Collapse all tools ຫຍໍ້ເຄື່ອງມືທັງໝົດເຂົ້າ + Lock your signature into the page? ລັອກລາຍເຊັນຂອງທ່ານເຂົ້າໃນໜ້າບໍ່? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. ລາຍເຊັນທີ່ວາງໄວ້ເປັນຄຳເຫັນຈົນກວ່າຈະຖືກລັອກ: ທຸກຄົນທີ່ມີໂປຣແກຣມແກ້ໄຂ PDF ສາມາດຍ້າຍ ຫຼື ລຶບມັນອອກໄດ້. ການລັອກຈະເຮັດໃຫ້ຂໍ້ຄວາມ, ເຄື່ອງໝາຍ ແລະ ລາຍເຊັນຂອງ ຕື່ມຂໍ້ມູນ & ເຊັນ ກາຍເປັນສ່ວນໜຶ່ງຂອງໜ້າ. + Lock and save ລັອກ ແລະ ບັນທຶກ + Keep editable ໃຫ້ແກ້ໄຂໄດ້ຢູ່ + Don't ask again ບໍ່ຕ້ອງຖາມອີກ + Lock into the page when saving ລັອກເຂົ້າໃນໜ້າເມື່ອບັນທຶກ + Otherwise, ask on the first save after a signature is placed ຖ້າບໍ່ດັ່ງນັ້ນ, ຖາມເມື່ອບັນທຶກຄັ້ງທຳອິດຫຼັງຈາກວາງລາຍເຊັນ + Signature added. Others can move it until it's locked. ເພີ່ມລາຍເຊັນແລ້ວ. ຄົນອື່ນສາມາດຍ້າຍມັນໄດ້ຈົນກວ່າຈະຖືກລັອກ. + Lock now ລັອກດຽວນີ້ + Signature locked into the page. ລັອກລາຍເຊັນເຂົ້າໃນໜ້າແລ້ວ. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today ມື້ນີ້ diff --git a/crates/ui-egui/src/i18n/nb.tsv b/crates/ui-egui/src/i18n/nb.tsv index b54b3e3526..73b79fc4aa 100644 --- a/crates/ui-egui/src/i18n/nb.tsv +++ b/crates/ui-egui/src/i18n/nb.tsv @@ -2735,6 +2735,16 @@ key Space Mellomrom Remove crop Fjern beskjæring Send backward Flytt bakover Send to back Plasser lengst bak + Lock your signature into the page? Vil du låse signaturen inn i siden? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. En plassert signatur er en kommentar til den låses: alle med et PDF-redigeringsprogram kan flytte eller fjerne den. Låsing gjør teksten, merkene og signaturene fra Fyll ut og signer til en del av siden. + Lock and save Lås og lagre + Keep editable Behold redigerbar + Don't ask again Ikke spør igjen + Lock into the page when saving Lås inn i siden ved lagring + Otherwise, ask on the first save after a signature is placed Ellers spør ved første lagring etter at en signatur er plassert + Signature added. Others can move it until it's locked. Signatur lagt til. Andre kan flytte den til den er låst. + Lock now Lås nå + Signature locked into the page. Signaturen er låst inn i siden. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today I dag diff --git a/crates/ui-egui/src/i18n/ro.tsv b/crates/ui-egui/src/i18n/ro.tsv index 0d0fbf16b4..8740b22401 100644 --- a/crates/ui-egui/src/i18n/ro.tsv +++ b/crates/ui-egui/src/i18n/ro.tsv @@ -2846,6 +2846,16 @@ theme Appearance Aspect Unpin tool Anulează fixarea instrumentului Arrange image Aranjează imaginea Crop Decupare + Lock your signature into the page? Blocați semnătura în pagină? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. O semnătură plasată este un comentariu până când este blocată: oricine are un editor PDF o poate muta sau elimina. Blocarea face ca textul, marcajele și semnăturile din Completare și semnare să devină parte a paginii. + Lock and save Blocare și salvare + Keep editable Păstrare editabilă + Don't ask again Nu mai întreba + Lock into the page when saving Blocare în pagină la salvare + Otherwise, ask on the first save after a signature is placed Altfel, întreabă la prima salvare după plasarea unei semnături + Signature added. Others can move it until it's locked. Semnătură adăugată. Alții o pot muta până când este blocată. + Lock now Blocare acum + Signature locked into the page. Semnătura a fost blocată în pagină. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today Astăzi diff --git a/crates/ui-egui/src/i18n/sq.tsv b/crates/ui-egui/src/i18n/sq.tsv index eb9eed3a0e..6936db86d3 100644 --- a/crates/ui-egui/src/i18n/sq.tsv +++ b/crates/ui-egui/src/i18n/sq.tsv @@ -2571,6 +2571,16 @@ animation effects Off Joaktiv Remove crop Hiq prerjen Send backward Kalo prapa Send to back Dërgo mbrapa + Lock your signature into the page? Të kyçet nënshkrimi në faqe? + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. Një nënshkrim i vendosur është koment derisa të kyçet: kushdo me një redaktues PDF mund ta zhvendosë ose ta heqë. Kyçja i bën tekstin, shenjat dhe nënshkrimet e Plotëso dhe nënshkruaj pjesë të faqes. + Lock and save Kyç dhe ruaj + Keep editable Mbaje të redaktueshëm + Don't ask again Mos pyet më + Lock into the page when saving Kyç në faqe gjatë ruajtjes + Otherwise, ask on the first save after a signature is placed Përndryshe, pyet në ruajtjen e parë pasi të vendoset një nënshkrim + Signature added. Others can move it until it's locked. Nënshkrimi u shtua. Të tjerët mund ta zhvendosin derisa të kyçet. + Lock now Kyç tani + Signature locked into the page. Nënshkrimi u kyç në faqe. # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today Sot diff --git a/crates/ui-egui/src/i18n/th.tsv b/crates/ui-egui/src/i18n/th.tsv index f7f8a65c5c..1bf9d79c3a 100644 --- a/crates/ui-egui/src/i18n/th.tsv +++ b/crates/ui-egui/src/i18n/th.tsv @@ -2722,6 +2722,16 @@ theme Appearance ลักษณะที่ปรากฏ Remove crop เอาการครอบตัดออก Send backward ส่งไปข้างหลัง Send to back ส่งไปไว้ข้างหลังสุด + Lock your signature into the page? ล็อกลายเซ็นของคุณเข้ากับหน้าหรือไม่ + A placed signature is a comment until it is locked: anyone with a PDF editor can move or remove it. Locking makes your Fill & Sign text, marks and signatures part of the page. ลายเซ็นที่วางไว้เป็นความคิดเห็นจนกว่าจะล็อก: ใครก็ตามที่มีโปรแกรมแก้ไข PDF สามารถย้ายหรือเอาออกได้ การล็อกจะทำให้ข้อความ เครื่องหมาย และลายเซ็นจากกรอกและเซ็นกลายเป็นส่วนหนึ่งของหน้า + Lock and save ล็อกและบันทึก + Keep editable คงไว้ให้แก้ไขได้ + Don't ask again ไม่ต้องถามอีก + Lock into the page when saving ล็อกเข้ากับหน้าเมื่อบันทึก + Otherwise, ask on the first save after a signature is placed หรือถามเมื่อบันทึกครั้งแรกหลังจากวางลายเซ็น + Signature added. Others can move it until it's locked. เพิ่มลายเซ็นแล้ว ผู้อื่นสามารถย้ายได้จนกว่าจะล็อก + Lock now ล็อกเดี๋ยวนี้ + Signature locked into the page. ล็อกลายเซ็นเข้ากับหน้าแล้ว # Date picker: Today button (#4274) (AI-assisted, not separately native-reviewed) Today วันนี้ From a06ff3769f80c22b8102d23b09850908fca87034 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Sat, 31 Oct 2026 08:00:03 +0800 Subject: [PATCH 08/10] M7: locking a signature clears the comment selection (#2149) Review follow-up on #2348: flatten_fill_sign_in refreshed the view after Edit::FlattenFillSign but kept view.comments.selected. The placed signature is usually the selected comment, and once it is part of the page its (page, index) names nothing, or another comment after the indices shift. Both Lock now and the save-time Lock and save go through flatten_fill_sign_in, so both clear it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv --- crates/ui-egui/src/editing/save.rs | 3 +++ crates/ui-egui/tests/it/fill_sign.rs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index 131cb815e1..adf0c96f35 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -226,6 +226,9 @@ impl PdfCraftApp { && let Some(view) = self.views.iter_mut().find(|v| v.id == id) { view.document_changed(&doc.info); + // The marks left the comments: a selected one is gone, and its (page, index) could + // now name another comment. + view.comments.selected = None; } true } diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index 733fed5bde..4498f7cd16 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -1895,6 +1895,22 @@ fn placing_a_signature_offers_to_lock_it_now() { assert!(h.query_by_label(NOTICE).is_none(), "a signed document isn't offered the lock"); } +/// #2348 review: locking takes the placed signature out of the comments, so its selection goes +/// too; a stale (page, index) would point at nothing, or at another comment. +#[test] +fn locking_a_signature_clears_the_comment_selection() { + let mut h = harness(); + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 300.0); + h.run_steps(2); + assert!(h.state().views[0].comments.selected.is_some(), "the placed signature is selected"); + h.get_by_label("Lock now").click(); + h.run_steps(3); + assert!(h.state().views[0].comments.selected.is_none(), "nothing left to select"); +} + /// #2348 review: when locking the signature fails (here the document's security allows comments /// but not changes), nothing is lost: the next save asks again, and "Don't ask again" isn't /// remembered. From c7404f44bc6b5db832ae656db917be130841c4e2 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 2 Nov 2026 08:41:35 +0800 Subject: [PATCH 09/10] M7: reverting forgets a signature placed since the last save (#2149) File > Revert discards a signature placed since the last save, but its marker stayed, so the next save asked to lock a signature that was gone whenever the saved file held an earlier Fill & Sign mark. A successful revert now clears the marker. Review on #2348 (CodeRabbit). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv --- crates/ui-egui/src/editing/close.rs | 3 +++ crates/ui-egui/tests/it/fill_sign.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/crates/ui-egui/src/editing/close.rs b/crates/ui-egui/src/editing/close.rs index 55d884cd9a..53f5b04eec 100644 --- a/crates/ui-egui/src/editing/close.rs +++ b/crates/ui-egui/src/editing/close.rs @@ -55,6 +55,9 @@ impl PdfCraftApp { view.forms.unbaked.clear(); view.pending_edit = None; } + // A signature placed since the last save is gone with the rest: the next save + // doesn't ask to lock it (#2149). + self.placed_signatures.remove(&id); self.notify_tr("Reverted to the last saved version"); } Err(e) => self.notify_error(e), diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index 4498f7cd16..ff1bcbcdb4 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -1814,6 +1814,34 @@ fn the_first_save_after_placing_a_signature_asks_to_lock_it() { let _ = std::fs::remove_file(&out); } +/// #2348 review (CodeRabbit): File ▸ Revert discards a signature just placed, so the save after it +/// doesn't ask to lock one, even when the saved file holds an earlier Fill & Sign signature. +#[test] +fn reverting_forgets_a_signature_just_placed() { + let out = std::env::temp_dir().join(format!("pdfcraft-lock-revert-{}.pdf", std::process::id())); + let mut h = harness(); + h.state_mut().save_override = Some(out.to_string_lossy().into_owned()); + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + // An earlier signature, saved as a comment: the file keeps a Fill & Sign mark. + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 300.0); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks first"); + h.run_steps(2); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + // A new signature, then Revert: the new one is gone, the saved one stays. + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 200.0); + h.state_mut().revert_active(); + h.run_steps(3); + let marks = h.state().session.get(h.state().views[0].id).unwrap().info.annotations.iter().filter(|a| a.fill_sign).count(); + assert_eq!(marks, 1, "the saved signature is back, the new one is gone"); + assert!(h.state_mut().save_active(SaveTarget::InPlace), "no signature was placed since the last save: no question"); + let _ = std::fs::remove_file(&out); +} + /// #2348 review (benandez): right after a signature is placed, a notice offers to lock it into /// the page now. Lock now flattens it (undoable) without saving, so the save that follows doesn't /// ask; undoing the lock brings back both the notice and the question. Dismiss hides the notice From 40260c4a6df6592305582b428676ae3ae9e685a7 Mon Sep 17 00:00:00 2001 From: mtlcl410 <2368091+mtlcl410@users.noreply.github.com> Date: Mon, 2 Nov 2026 09:42:26 +0800 Subject: [PATCH 10/10] M7: the signature mark stays until the save succeeds, and a reload forgets it (#2149) Answering the lock question removed the placed-signature mark before the save ran, so a save that then failed or was cancelled didn't ask again. The answer now only lets the save that follows go ahead without asking; the mark is removed when a save succeeds, as before. A reload from disk replaces the document, so it forgets the mark too (a failed reload keeps it). Review on #2348 (CodeRabbit). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Pq7HyCfwzyRXj4VfQAVVdv --- crates/ui-egui/src/app.rs | 1 + crates/ui-egui/src/editing/save.rs | 14 ++++++++--- crates/ui-egui/src/lib.rs | 4 ++++ crates/ui-egui/src/platform/file_watch.rs | 20 +++++++++++++++- crates/ui-egui/tests/it/fill_sign.rs | 29 +++++++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/app.rs b/crates/ui-egui/src/app.rs index 29801c4b76..fd3bc035d8 100644 --- a/crates/ui-egui/src/app.rs +++ b/crates/ui-egui/src/app.rs @@ -109,6 +109,7 @@ impl PdfCraftApp { ask_to_lock_signatures: true, placed_signatures: Default::default(), pending_lock: None, + lock_answered: None, lock_remember: false, fill_text_size: fill_sign::TEXT_SIZE, fill_text_color: [0.0; 3], diff --git a/crates/ui-egui/src/editing/save.rs b/crates/ui-egui/src/editing/save.rs index adf0c96f35..638bd13f1c 100644 --- a/crates/ui-egui/src/editing/save.rs +++ b/crates/ui-egui/src/editing/save.rs @@ -60,7 +60,14 @@ impl PdfCraftApp { // changes page content in an update, which validators report as a change after signing. let still_marked = self.session.get(id).is_some_and(|d| d.info.annotations.iter().any(|a| a.fill_sign)); let signed = self.session.get(id).is_some_and(|d| d.is_signed()); - if self.ask_to_lock_signatures && !self.flatten_fill_sign_on_save && self.placed_signatures.contains(&id) && still_marked && !signed { + let answered = self.lock_answered.take() == Some(id); + if self.ask_to_lock_signatures + && !self.flatten_fill_sign_on_save + && self.placed_signatures.contains(&id) + && still_marked + && !signed + && !answered + { self.pending_lock = Some(PendingLock { id, target, after: Box::new(after) }); self.dialog = Some(crate::Dialog::LockSignatures); return false; @@ -180,13 +187,14 @@ impl PdfCraftApp { /// its pages first when `lock`, then save as was asked. With `remember`, the answer becomes /// the preference (flatten on every save, or never ask again). Nothing changes when locking /// fails: the document still counts as holding a placed signature, so the next save asks - /// again, and the answer isn't remembered. + /// again, and the answer isn't remembered. The placed-signature mark stays until the save + /// succeeds, so a save that fails or is cancelled asks again too. pub(crate) fn answer_lock_signatures(&mut self, lock: bool, remember: bool) { let Some(p) = self.pending_lock.take() else { return }; if lock && !self.flatten_fill_sign_on_save && !self.flatten_fill_sign_in(p.id) { return; } - self.placed_signatures.remove(&p.id); + self.lock_answered = Some(p.id); if remember { if lock { self.flatten_fill_sign_on_save = true; diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 21c487d361..6aa5e9ddeb 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -334,6 +334,10 @@ pub struct PdfCraftApp { pub(crate) placed_signatures: std::collections::HashSet, /// A save waiting behind [`Dialog::LockSignatures`], and its Don't ask again box. pub(crate) pending_lock: Option, + /// The document whose lock question was just answered: the save that follows the answer + /// doesn't ask again. The placed-signature mark itself stays until that save succeeds, so a + /// save that fails or is cancelled asks again next time. + pub(crate) lock_answered: Option, pub(crate) lock_remember: bool, /// Point size for new Fill & Sign text and dates, kept across launches. pub fill_text_size: f64, diff --git a/crates/ui-egui/src/platform/file_watch.rs b/crates/ui-egui/src/platform/file_watch.rs index 57de3428a3..a532b1d1ed 100644 --- a/crates/ui-egui/src/platform/file_watch.rs +++ b/crates/ui-egui/src/platform/file_watch.rs @@ -451,8 +451,10 @@ impl PdfCraftApp { let Some(id) = self.views.get(i).map(|v| v.id) else { return }; match self.session.reload(id, bytes) { Ok(()) => { - // Whatever was unsaved is gone, so there is nothing left to recover. + // Whatever was unsaved is gone, so there is nothing left to recover, and no + // signature placed since the last save is left to ask about locking (#2149). self.forget_recovery(id); + self.placed_signatures.remove(&id); if let (Some(view), Some(doc)) = (self.views.get_mut(i), self.session.get(id)) { view.document_replaced(&doc.info); } @@ -556,6 +558,22 @@ mod tests { app.session.get(app.views[0].id).unwrap() } + /// #2348 review (CodeRabbit): a reload from disk replaces the document, so a signature + /// placed since the last save is gone and the next save doesn't ask to lock it. A reload + /// that fails keeps the document, and the mark with it. + #[test] + fn reloading_from_disk_forgets_a_signature_just_placed() { + let (_dir, path) = TempDir::new("lock-reload", &fixture(1)); + let mut h = harness(&path); + h.run_steps(3); + let id = h.state().views[0].id; + h.state_mut().placed_signatures.insert(id); + h.state_mut().reload_view(0, Arc::new(b"not a pdf".to_vec())); + assert!(h.state().placed_signatures.contains(&id), "a failed reload keeps the mark"); + h.state_mut().reload_view(0, Arc::new(fixture(2))); + assert!(!h.state().placed_signatures.contains(&id), "the reloaded document has no placed signature"); + } + /// Association recovery changes ctime without replacing PDF contents. It must neither /// discard undo history nor make the next Save ask to overwrite our own metadata change. #[cfg(target_os = "macos")] diff --git a/crates/ui-egui/tests/it/fill_sign.rs b/crates/ui-egui/tests/it/fill_sign.rs index ff1bcbcdb4..5f821e4ea0 100644 --- a/crates/ui-egui/tests/it/fill_sign.rs +++ b/crates/ui-egui/tests/it/fill_sign.rs @@ -1814,6 +1814,35 @@ fn the_first_save_after_placing_a_signature_asks_to_lock_it() { let _ = std::fs::remove_file(&out); } +/// #2348 review (CodeRabbit): answering the lock question doesn't forget the signature; only a +/// save that succeeds does. When the save after the answer fails, the next save asks again. +#[test] +fn a_save_that_fails_after_the_lock_question_asks_again() { + let bad = std::env::temp_dir().join(format!("pdfcraft-no-such-dir-{}", std::process::id())).join("out.pdf"); + let good = std::env::temp_dir().join(format!("pdfcraft-lock-retry-{}.pdf", std::process::id())); + let mut h = harness(); + h.state_mut().save_override = Some(bad.to_string_lossy().into_owned()); + h.state_mut().signature = Some(SavedSig::Typed("Ada Lovelace".into())); + h.state_mut().execute("sign.fill.signature"); + h.run_steps(2); + click(&mut h, 40.0, 300.0); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks first"); + h.run_steps(2); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + assert!(h.state().session.get(h.state().views[0].id).unwrap().dirty, "the save failed"); + // The save after the answer doesn't ask twice in a row, but the failed one left the signature + // unsaved: the next save asks again. + h.state_mut().save_override = Some(good.to_string_lossy().into_owned()); + assert!(!h.state_mut().save_active(SaveTarget::InPlace), "asks again"); + h.run_steps(2); + h.get_by_label("Keep editable").click(); + h.run_steps(3); + assert!(!h.state().session.get(h.state().views[0].id).unwrap().dirty, "saved this time"); + assert!(h.state_mut().save_active(SaveTarget::InPlace), "saved: no question"); + let _ = std::fs::remove_file(&good); +} + /// #2348 review (CodeRabbit): File ▸ Revert discards a signature just placed, so the save after it /// doesn't ask to lock one, even when the saved file holds an earlier Fill & Sign signature. #[test]