From 21df6fc30ee3b859ca06c8ccefd5bb48d9505766 Mon Sep 17 00:00:00 2001 From: Philip Date: Mon, 12 Oct 2026 23:30:34 +0100 Subject: [PATCH] M10: Edit recognizes a scan that also carries some text of its own Pages with a comment or text box over a scanned picture count as having text, so the recognition run skipped them ("the page already contains text") and Edit did nothing for them. The run started by Edit now also reads the picture of such a page, as Acrobat does, and the words the page's text already has are not added twice. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/ocr_ui.rs | 5 ++++- crates/ui-egui/tests/ocr.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/ocr_ui.rs b/crates/ui-egui/src/ocr_ui.rs index 27778957c9..3549753f74 100644 --- a/crates/ui-egui/src/ocr_ui.rs +++ b/crates/ui-egui/src/ocr_ui.rs @@ -201,7 +201,10 @@ impl PdfCraftApp { if !covered || !pdfcraft_engine::ocr::available() { return; } - let settings = self.ocr_draft.settings(); + // A picture on a page that has some text of its own (a comment, a text box) is still a + // scan, as in Acrobat; the words the page's text already has are not added twice. + let mut settings = self.ocr_draft.settings(); + settings.skip_text_pages = false; self.start_ocr_pages(id, &[page], settings); } diff --git a/crates/ui-egui/tests/ocr.rs b/crates/ui-egui/tests/ocr.rs index 415b3eea27..b53623b6b6 100644 --- a/crates/ui-egui/tests/ocr.rs +++ b/crates/ui-egui/tests/ocr.rs @@ -222,6 +222,38 @@ fn the_edit_panel_recognizes_a_scanned_page_too() { assert_eq!(h.state().session.get(id).unwrap().can_undo(), Some("Recognize text")); } +/// A scanned page that also carries some text of its own (a comment or text box over the map) is +/// still a scan: Edit reads the picture, and the page's own words are not added a second time. +#[test] +fn editing_a_scan_that_carries_some_text_still_recognizes_the_picture_once() { + if !pdfcraft_engine::ocr::available() { + eprintln!("skipped: OCR models not installed"); + return; + } + let hybrid = { + let mut s = Session::new(); + let id = s.open("scan.pdf", None, std::sync::Arc::new(scan()), None).unwrap(); + let (w, h) = { + let p = &s.get(id).unwrap().info.pages[0]; + (p.width as f64, p.height as f64) + }; + let note = + AddedText { rect: [w * 0.1, h * 0.75, w * 0.9, h * 0.85], text: "Invoice reference 4711".into(), size: 18.0, ..Default::default() }; + s.apply(id, Edit::AddText { page: 0, text: note }).unwrap(); + s.save_bytes(id).unwrap().to_vec() + }; + let mut h = edit_harness(hybrid); + let id = h.state().active_ids().unwrap().1; + assert!(h.state_mut().execute("edit.edit_text")); + h.run_steps(4); + assert_eq!(h.state().session.get(id).unwrap().can_undo(), Some("Recognize text")); + let doc = h.state().session.get(id).unwrap(); + let mut r = pdfcraft_render::PageRenderer::new(doc.bytes.clone(), Default::default()); + let text = r.render(pdfcraft_render::RenderRequest { page: 0, kind: pdfcraft_render::RequestKind::Text, scale: 1.0, ..Default::default() }); + let text = text.text.unwrap().plain_text().to_lowercase(); + assert_eq!((text.matches("invoice").count(), text.matches("scanned").count()), (1, 1), "{text}"); +} + #[test] fn editing_a_page_that_has_text_does_not_recognize_anything() { let mut h = edit_harness(text_page());