From 86bb0d4e1c60ef200d3c825fbe647e7e217ef10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 9 Oct 2026 23:31:19 -0400 Subject: [PATCH] ElyPDF: a refused OCR text layer reports no success (P-09) --- apps/ely-pdf/src/ocr.rs | 6 ++++-- apps/ely-pdf/src/tests/structure.rs | 30 +++++++++++++++++++++++++++++ apps/ely-pdf/src/writes.rs | 10 ++++++---- tasks/BUGS.md | 2 +- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/apps/ely-pdf/src/ocr.rs b/apps/ely-pdf/src/ocr.rs index d9050de..79af960 100644 --- a/apps/ely-pdf/src/ocr.rs +++ b/apps/ely-pdf/src/ocr.rs @@ -165,10 +165,12 @@ impl Viewer { let Some(view) = self.tab(tab).map(|tab| tab.view.clone()).filter(|_| !pages.is_empty()) else { return self.toast(summary, Severity::Info, cx); }; - self.on_same(&view, run.version, cx, |view, cx| { + let laid = self.on_same(&view, run.version, cx, |view, cx| { view.edit("adding recognised text", move |worker, doc| worker.text_layer(doc, &pages, replace), cx) }); - self.toast(summary, Severity::Success, cx); + if laid { + self.toast(summary, Severity::Success, cx); + } } /// The OCR dialog: the ask, then the progress. diff --git a/apps/ely-pdf/src/tests/structure.rs b/apps/ely-pdf/src/tests/structure.rs index b35872f..4a75498 100644 --- a/apps/ely-pdf/src/tests/structure.rs +++ b/apps/ely-pdf/src/tests/structure.rs @@ -234,3 +234,33 @@ fn a_cancelled_run_cannot_touch_the_next() { assert!(cx.update(|_, app| viewer.read(app).ocr.is_some()), "the second run goes on"); settle(cx, |app| viewer.read(app).ocr.is_none() && view.read(app).edited == 1); } + +#[test] +fn a_refused_text_layer_reports_no_success() { + use ely_gpui_component::primitives::Severity; + let dir = tempfile::tempdir().expect("a temp dir"); + let mut cx = TestAppContext::single(); + let path = scanned(dir.path()); + let (viewer, cx) = windowed(&mut cx, dir.path(), &[path], true); + let view = shown(cx, &viewer); + // The window's toasts live on its root. + let root = cx.update(|window, app| { + let root = ely_ui::document_root(ely_platform::AppId::Pdf, viewer.clone().into(), &ely_ui::commands::FILE, Vec::new(), window, app); + viewer.update(app, |viewer, _| viewer.core.root = root.downgrade()); + root + }); + let tab = cx.update(|_, app| viewer.read(app).core.tabs[0].id); + let run = cx.update(|window, app| viewer.update(app, |viewer, cx| { + viewer.ask_ocr(window, cx); + viewer.start_ocr(cx); + viewer.ocr_runs - 1 + })); + // An edit still runs when the text is laid. + cx.update(|_, app| view.update(app, |view, _| view.editing = true)); + let line = ely_pdf_engine::OcrLine { rect: [72.0, 700.0, 300.0, 720.0], text: "Hello".into() }; + cx.update(|_, app| viewer.update(app, |viewer, cx| viewer.ocr_read((tab, run, 0), Ok(Some(vec![line])), cx))); + settle(cx, |app| viewer.read(app).ocr.is_none()); + let toasts = cx.update(|_, app| root.read(app).toasts(app)); + assert!(toasts.iter().any(|(_, severity)| *severity == Some(Severity::Warning)), "{toasts:?}"); + assert!(!toasts.iter().any(|(_, severity)| *severity == Some(Severity::Success)), "{toasts:?}"); +} diff --git a/apps/ely-pdf/src/writes.rs b/apps/ely-pdf/src/writes.rs index c2b7ce5..d09674c 100644 --- a/apps/ely-pdf/src/writes.rs +++ b/apps/ely-pdf/src/writes.rs @@ -176,25 +176,27 @@ impl Viewer { self.pick(false, cx, move |viewer, path: PathBuf, cx| { viewer.on_same(&view, version, cx, |view, cx| { view.edit("replacing an image", move |worker, doc| worker.replace_image(doc, page, index, &path), cx) - }) + }); }); } - /// Runs `step` on `view` if no edit came since `version`. + /// Whether `step` ran: no edit came since `version`. pub(crate) fn on_same( &self, view: &gpui::Entity, version: u64, cx: &mut Context, step: impl FnOnce(&mut crate::view::PdfView, &mut Context), - ) { + ) -> bool { // Landed or queued, an edit since may move the target. let open = self.core.tabs.iter().any(|tab| tab.view == *view); if !open || view.read(cx).version != version || !view.read(cx).settled() { let text = Self::text(cx, "pdf.object.changed", &[]); - return self.toast(text, Severity::Warning, cx); + self.toast(text, Severity::Warning, cx); + return false; } view.update(cx, step); + true } /// Asks for a picture; it lands mid-page. diff --git a/tasks/BUGS.md b/tasks/BUGS.md index 15a6bfd..b08489a 100644 --- a/tasks/BUGS.md +++ b/tasks/BUGS.md @@ -41,7 +41,7 @@ - [x] P-06 默认字体“原字体”在 Type 1/Type 3 字体的文件上失败,只给引擎原话 — 原字体写不了新字(非 CID 编码、页面内直接字体、不在本页)时面板提示换字体;R2 PASS - [x] P-07 含连字的文字块永远不可编辑 — 字形按 stext 的写法展开(多字字形、连字码点),整页、单块与删除同一判法;R3 PASS - [x] P-08 页面读取失败后每帧重试、每帧弹提示 — 失败的读取按页或种类记下,编辑后重读;签名读取以保存计数判过期 -- [ ] P-09 识别文字的写入被拒后仍弹“成功” +- [x] P-09 识别文字的写入被拒后仍弹“成功” — on_same 回报是否执行;被拒时只有警告 - [ ] P-10 预览回复被丢弃时“应用”静默无事 - [ ] P-11 撤销或重做在 MuPDF 侧失败后 worker 的步数不再同步 - [ ] P-12 `unhook` 中途抛出时泄漏保留的对象