Files
pdfcraft/crates/ui-egui/tests/camera.rs
513a1ecc3a i18n: let the layers below hand their sentences over, so they can be translated (#478)
* i18n: let the layers below hand their sentences over, so they can be translated

The accessibility findings, the PDF/A problems, the signature details, the
page-range mistakes and the edit labels were English in all ten languages,
because matching their wording from the UI with a tl! literal would break
silently whenever the engine changed it.

So each layer now hands the wording over instead, apart from the document's
own words: a WORDINGS list of every sentence it can say, with holes in it, and
said(&words), which looks the wording up through the closure it is given and
fills the holes in a single pass. in_english changes nothing, for the CLI and
the tests; the UI passes &i18n::words, a catalog lookup. A wording with no
entry comes back as it stands, which is the English, so the nine catalogs this
does not touch read exactly as before.

a11y::report_html takes a language tag and a words closure, so a saved report
reads the way the panel read, down to its lang attribute. print::PrintError
gained a variant per thing a person can be wrong about, with the numbers apart
from the words; faults rather than mistakes keep their own text.

Spanish translates all of it (107 entries). spanish_covers_what_the_engine_says
requires an entry per wording in the eight lists and
spanish_keeps_the_holes_in_what_the_engine_says requires the holes to survive
the translation, so the catalog cannot fall behind the engine unnoticed.

No new tl! literal, so the Chinese, French and Russian UI-literal gates are
unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Sign tests from #1099: compare against the &'static str kinds

* Camera tests: undo returns a Label now

---------

Co-authored-by: Fernando Areán <soporte@codebit.es>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 15:45:51 -04:00

217 lines
8.7 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Scan & OCR ▸ Enhance ▸ Camera image in the real shell (egui_kittest): drag a corner, then
//! Enhance page; Cancel and Esc leave the page as it was.
use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use pdfcraft_ui_egui::{PdfCraftApp, QuickTool};
const FIXTURE: &[u8] = b"%PDF-1.7
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
2 0 obj << /Type /Pages /Kids [3 0 R 4 0 R] /Count 2 /MediaBox [0 0 300 400] >> endobj
3 0 obj << /Type /Page /Parent 2 0 R >> endobj
4 0 obj << /Type /Page /Parent 2 0 R >> endobj
trailer << /Root 1 0 R >>
%%EOF";
fn harness() -> Harness<'static, PdfCraftApp> {
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| {
let mut app = PdfCraftApp::new();
app.set_option("language", "en").unwrap();
app.open_bytes("doc.pdf", None, FIXTURE.to_vec()).unwrap();
app.set_option("left", "closed").unwrap();
app.set_option("zoom", "100").unwrap();
app
});
h.run_steps(6);
h
}
fn size(h: &Harness<'static, PdfCraftApp>, page: usize) -> (f32, f32) {
let s = h.state();
let p = &s.session.get(s.views[0].id).unwrap().info.pages[page];
(p.width, p.height)
}
fn undo_label(h: &Harness<'static, PdfCraftApp>) -> Option<String> {
let s = h.state();
s.session.get(s.views[0].id).unwrap().can_undo().map(str::to_owned)
}
#[test]
fn dragging_a_corner_then_enhance_page_straightens_the_page() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
assert_eq!(h.state().quick_tool, QuickTool::EnhanceCamera);
h.run_steps(3);
let quad = h.state().views[0].camera.expect("the corners start on the current page");
assert_eq!(quad.page, 0);
assert_eq!(quad.corners, [[15.0, 20.0], [285.0, 20.0], [285.0, 380.0], [15.0, 380.0]], "inset by 5%");
// Drag the top-left corner to (60, 80) on the page.
let r = h.state().views[0].page_screen_rect(0).unwrap();
let (a, b) = (r.min + r.size() * 0.05, r.min + r.size() * 0.2);
h.hover_at(a);
h.run_steps(1);
h.drag_at(a);
h.run_steps(1);
for k in 1..=4 {
h.hover_at(a + (b - a) * (k as f32 / 4.0));
h.run_steps(1);
}
h.drop_at(b);
h.run_steps(2);
let c = h.state().views[0].camera.unwrap().corners[0];
assert!((c[0] - 60.0).abs() < 1.0 && (c[1] - 80.0).abs() < 1.0, "{c:?}");
assert_eq!(undo_label(&h), None, "dragging changes nothing yet");
h.get_by_label("Enhance page").click();
h.run_steps(4);
// The longer of each pair of opposite sides: 270 × 360 pt.
let (w, hh) = size(&h, 0);
assert!((w - 270.0).abs() < 2.0 && (hh - 360.0).abs() < 2.0, "{w}×{hh}");
assert_eq!(size(&h, 1), (300.0, 400.0), "only the page with the corners changes");
assert_eq!(undo_label(&h).as_deref(), Some("Enhance camera image"));
// The tool stays open for the enhanced page: turn it, then Done.
assert_eq!(h.state().quick_tool, QuickTool::EnhanceCamera);
assert!(h.state().views[0].camera.is_none());
assert_eq!(h.state().views[0].camera_done.map(|d| d.page), Some(0));
h.get_by_label("Rotate clockwise").click();
h.run_steps(4);
let (w, hh) = size(&h, 0);
assert!((w - 360.0).abs() < 2.0 && (hh - 270.0).abs() < 2.0, "{w}×{hh}");
assert_eq!(undo_label(&h).as_deref(), Some("Rotate page"));
h.get_by_label("Done").click();
h.run_steps(3);
assert_eq!(h.state().quick_tool, QuickTool::Select);
assert_eq!(h.state().views[0].camera_done, None);
}
#[test]
fn the_clean_up_level_enhances_again_from_the_photo_and_keeps_the_turn() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.look, done.level, done.steps), (pdfcraft_engine::camera_clean::Look::Colour, 50, 1), "enhanced in colour at the default level");
h.get_by_label("Rotate clockwise").click();
h.run_steps(4);
let turned = size(&h, 0);
assert!(turned.0 > turned.1, "{turned:?}");
// Let go of the slider at 100.
let view = &mut h.state_mut().views[0];
if let Some(d) = view.camera_done.as_mut() {
d.level = 100;
}
view.pending_action = Some(pdfcraft_ui_egui::canvas::ViewAction::CameraLevel);
h.run_steps(4);
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.applied.1, done.steps, done.turns), (100, 2, 90));
assert_eq!(size(&h, 0), turned, "still turned");
// The history holds one enhancement and one turn, not a pile of them.
let s = h.state_mut();
let id = s.views[0].id;
assert_eq!(s.session.undo(id).unwrap().as_str(), "Rotate page");
assert_eq!(s.session.undo(id).unwrap().as_str(), "Enhance camera image");
assert_eq!(s.session.get(id).unwrap().can_undo(), None);
let p = &s.session.get(id).unwrap().info.pages[0];
assert_eq!((p.width, p.height), (300.0, 400.0), "back to the photo");
}
#[test]
fn choosing_a_look_enhances_again() {
use pdfcraft_engine::camera_clean::Look;
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
for (label, look) in [("Black and white", Look::BlackWhite), ("Greyscale", Look::Greyscale), ("Original", Look::Original)] {
h.get_by_label(label).click();
h.run_steps(4);
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.applied.0, done.steps), (look, 1), "{label}");
let s = h.state();
assert_eq!(s.session.get(s.views[0].id).unwrap().can_undo(), Some("Enhance camera image"), "{label}");
}
}
#[test]
fn after_enhance_page_crop_and_organize_take_over() {
for (button, then) in [("Crop pages", QuickTool::Crop), ("Organize pages", QuickTool::Select)] {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
assert_eq!(h.state().views[0].camera_done.map(|d| d.page), Some(0));
h.get_by_label(button).click();
h.run_steps(3);
assert_eq!(h.state().quick_tool, then, "{button}");
assert_eq!(h.state().views[0].camera_done, None, "{button}");
assert_eq!(h.state().views[0].organize, button == "Organize pages");
}
}
#[test]
fn cancel_and_escape_leave_the_page_alone() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Cancel").click();
h.run_steps(3);
assert_eq!(h.state().quick_tool, QuickTool::Select);
assert!(h.state().views[0].camera.is_none());
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
assert!(h.state().views[0].camera.is_some());
h.key_press(egui::Key::Escape);
h.run_steps(3);
assert_eq!(h.state().quick_tool, QuickTool::Select);
assert!(h.state().views[0].camera.is_none());
assert_eq!(size(&h, 0), (300.0, 400.0));
assert_eq!(undo_label(&h), None);
}
/// A turn made outside the tool after the enhancement breaks its run in the history: changing the
/// level then leaves the page and the history as they are instead of enhancing the straightened
/// page a second time.
#[test]
fn the_level_leaves_the_page_alone_when_another_edit_came_in_between() {
let mut h = harness();
assert!(h.state_mut().execute("ocr.enhance_camera"));
h.run_steps(3);
h.get_by_label("Enhance page").click();
h.run_steps(4);
h.get_by_label("Rotate clockwise").click();
h.run_steps(4);
// Another "Rotate page" from outside the tool, with the same history label.
{
let s = h.state_mut();
let id = s.views[0].id;
s.session.apply(id, pdfcraft_engine::Edit::RotatePages { pages: vec![0], degrees: 90 }).unwrap();
}
h.run_steps(2);
let before = size(&h, 0);
let applied = h.state().views[0].camera_done.unwrap().applied;
let view = &mut h.state_mut().views[0];
if let Some(d) = view.camera_done.as_mut() {
d.level = 100;
}
view.pending_action = Some(pdfcraft_ui_egui::canvas::ViewAction::CameraLevel);
h.run_steps(4);
assert_eq!(size(&h, 0), before, "the page is not warped again");
let done = h.state().views[0].camera_done.unwrap();
assert_eq!((done.level, done.applied), (applied.1, applied), "the slider goes back");
let s = h.state_mut();
let id = s.views[0].id;
assert_eq!(s.session.undo(id).unwrap().as_str(), "Rotate page");
assert_eq!(s.session.undo(id).unwrap().as_str(), "Rotate page");
assert_eq!(s.session.undo(id).unwrap().as_str(), "Enhance camera image");
assert_eq!(s.session.get(id).unwrap().can_undo(), None);
}