mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 19:48:14 -06:00
M3: match Greek final sigma in case-insensitive search (#1349)
* M3: use the macOS Finder label in the tab-menu regression The UI already uses Reveal in Finder on macOS, but the regression test queried Reveal in File Explorer on every platform. Select the platform's label while preserving the disabled-action and inactive-tab assertions. Co-Authored-By: Codex <noreply@openai.com> * M3: match Greek final sigma in case-insensitive search Fold capital, normal and final sigma to the same character so Greek words match in either case. Cover exact search, whole words, glyph ranges and bounded matches without changing the existing Turkish-I behavior. Record the search fix and the macOS test portability follow-up in the roadmap. Workspace tests, all-target Clippy, formatting, wasm, layering and a synthetic CLI before/after comparison pass on macOS. Co-Authored-By: Codex <noreply@openai.com> --------- Co-authored-by: nevicia2010-hub <259836477+nevicia2010-hub@users.noreply.github.com> Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
co-authored by
Codex
nevicia2010-hub
parent
9d6552291b
commit
51982db121
@@ -140,6 +140,7 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md
|
||||
|
||||
## Progress log
|
||||
|
||||
- **2026-10-12 (community, M3, Greek search):** Case-insensitive Find treats normal, final and capital sigma (`σ`, `ς`, `Σ`) as the same letter, so `κόσμος` and `ΚΌΣΜΟΣ` find each other. Case-sensitive and whole-word options, glyph ranges and match limits retain their behavior. Synthetic text-search regressions cover both directions. The tab-menu regression selects the platform's Reveal label (Finder on macOS), restoring the macOS test without changing the menu. Estimates unchanged.
|
||||
- **2026-10-12 (M12, XFA scripts turned off):** When a runaway script turns an XFA form's scripts off, the app now says so: a toast as it happens (at open or after a click), and the notice bar from then on, ahead of its other notices, with a JavaScript Console button. Before, the error was only in the console, so calculations and buttons just stopped. A notice bar the user had closed comes back for it; closed again, it stays closed. The engine reports the state (`Document::xfa_scripts_off`, and `xfa_scripts_off` in `doc_info`) instead of the UI reading error text. Two kittest UI tests (a runaway at open; a runaway click after the bar was closed), engine assertions and a `doc_info` tool test. Overall unchanged.
|
||||
- **2026-10-12 (M14, Windows low-DPI font-hinting candidate):** The Windows policy disables symmetric rendering for embedded JetBrains Mono only. On the locked-font macOS probe, horizontal alpha-edge energy per ink improves 2.78%, 3.87% and 1.59% at 100%, 125% and 150% scaling, and is down 0.79% at 200%; Inter is pixel-identical. Logical layout hashes and 8 MiB atlas size match, repeated fractional offsets add no atlas dimensions, and baseline/candidate time and RSS ranges overlap. Windows runtime and hardware remain unverified; estimates unchanged.
|
||||
- **2026-10-12 (M7, #1200 candidate):** Bounded local GoToR file specifications open in desktop tabs; explicit numeric page views apply, while named destinations open at the default view with a warning. The automation tool resolves relative to the source and uses existing `--root` confinement. Appearance-only Link Properties edits preserve the existing action. URL/network paths remain refused; `/NewWindow` is a tab hint. Estimates unchanged.
|
||||
|
||||
@@ -38,11 +38,13 @@ pub struct TextGlyph {
|
||||
/// combining dot (two characters) and leaves `ı` alone, so neither `İSTANBUL` nor `IRMAK` could
|
||||
/// be found by typing `istanbul` or `ırmak`. Search can't know the document's language, so the
|
||||
/// four are treated as one letter; this only adds matches where an `ı` or `İ` is involved.
|
||||
/// Greek sigma's normal and word-final forms (`σ`/`ς`) both fold to `σ`, as does `Σ`.
|
||||
pub fn fold_case(s: &str) -> String {
|
||||
let mut out = String::with_capacity(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'I' | 'i' | '\u{130}' | '\u{131}' => out.push('i'),
|
||||
'Σ' | 'σ' | 'ς' => out.push('σ'),
|
||||
_ => out.extend(c.to_lowercase()),
|
||||
}
|
||||
}
|
||||
@@ -1791,14 +1793,38 @@ mod tests {
|
||||
assert_eq!(lower.find("KIŞ"), vec![13..16]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn greek_sigma_forms_match_without_changing_exact_search_or_glyph_ranges() {
|
||||
let mut v = Vec::new();
|
||||
word(&mut v, "κόσμος", 10.0, 10.0, 7.0);
|
||||
word(&mut v, "ΚΌΣΜΟΣ", 80.0, 10.0, 7.0);
|
||||
word(&mut v, "σςΣ", 150.0, 10.0, 7.0);
|
||||
let text = layout(v);
|
||||
assert_eq!(text.plain_text(), "κόσμος ΚΌΣΜΟΣ σςΣ");
|
||||
for query in ["κόσμος", "ΚΌΣΜΟΣ"] {
|
||||
assert_eq!(text.find(query), vec![0..6, 6..12]);
|
||||
assert_eq!(text.find_opts(query, false, true), vec![0..6, 6..12]);
|
||||
}
|
||||
for query in ["σ", "ς", "Σ"] {
|
||||
assert_eq!(text.find(query), vec![2..3, 5..6, 8..9, 11..12, 12..13, 13..14, 14..15]);
|
||||
assert!(text.find_opts(query, false, true).is_empty());
|
||||
}
|
||||
assert_eq!(text.find_opts("κόσμος", true, false), vec![0..6]);
|
||||
assert_eq!(text.find_opts("ΚΌΣΜΟΣ", true, false), vec![6..12]);
|
||||
assert_eq!(text.find_opts("ς", true, false), vec![5..6, 13..14]);
|
||||
assert_eq!(text.find_opts_limited("ς", false, false, 2), vec![2..3, 5..6]);
|
||||
assert_eq!(text.line_rects(text.find("κόσμος")[1].clone()), text.line_rects(6..12));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_folding_keeps_one_character_per_character() {
|
||||
for s in ["İSTANBUL", "ırmak", "Hello WORLD", "ÇAĞLAR", "Straße", "ΣΟΦΙΑ", "日本語"] {
|
||||
for s in ["İSTANBUL", "ırmak", "Hello WORLD", "ÇAĞLAR", "Straße", "ΣΟΦΙΑ", "Σσς", "日本語"] {
|
||||
assert_eq!(fold_case(s).chars().count(), s.chars().count(), "{s}");
|
||||
}
|
||||
assert_eq!(fold_case("Hello WORLD"), "hello world");
|
||||
assert_eq!(fold_case("ÇAĞLAR Şişli"), "çağlar şişli");
|
||||
assert_eq!(fold_case("İIıi"), "iiii");
|
||||
assert_eq!(fold_case("Σσς"), "σσσ");
|
||||
}
|
||||
|
||||
fn g(t: &str, x0: f32, y0: f32, x1: f32) -> TextGlyph {
|
||||
|
||||
@@ -265,7 +265,8 @@ mod ui_tests {
|
||||
h.get_by_role_and_label(egui::accesskit::Role::Tab, "first.pdf").click_secondary();
|
||||
h.run_steps(2);
|
||||
assert!(h.get_by_label("Reopen Closed Tab").accesskit_node().is_disabled());
|
||||
assert!(h.get_by_label("Reveal in File Explorer").accesskit_node().is_disabled());
|
||||
let reveal_label = if cfg!(target_os = "macos") { "Reveal in Finder" } else { "Reveal in File Explorer" };
|
||||
assert!(h.get_by_label(reveal_label).accesskit_node().is_disabled());
|
||||
assert_eq!(h.state().active, Some(1));
|
||||
h.get_by_label("Close Tab").click();
|
||||
h.run_steps(3);
|
||||
|
||||
@@ -143,6 +143,7 @@ M2 (renderer), M7 (content editing), M10 (OCR and Office), M11 (Preflight and st
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-12 | minor | M3: case-insensitive Find matches normal, final and capital Greek sigma; exact search, word boundaries, glyph ranges and match limits have regression coverage. The tab-menu test uses Finder on macOS. Estimates unchanged. |
|
||||
| 2026-10-12 | minor | M14: Windows low-DPI JetBrains Mono hinting candidate; raster and layout evidence recorded without a Windows runtime claim. Estimates unchanged. |
|
||||
| 2026-10-12 | minor | M7/#1200 candidate: bounded local GoToR opening, root-confined automation, and action-preserving appearance edits; named destinations and window behavior remain partial; estimates unchanged. |
|
||||
| 2026-10-12 | minor | Exclusive image crops resize with their pixels across nested states, intervening operators and content streams; shared clips remain fixed. Synthetic engine/tool regressions and private image save/reopen checks; estimates unchanged. |
|
||||
|
||||
Reference in New Issue
Block a user