258 lines
22 KiB
Markdown
258 lines
22 KiB
Markdown
# nigig-ocr — Execution Plan (1:1 port of OCR Tool PRO 1.0)
|
||
|
||
> Plan status: all phases specified. **Implementation status (corrected
|
||
> 2026-09-26):** the earlier "Phase 2–9 DONE" was not true. No real OCR ran
|
||
> anywhere, the GUI was an unwired layout, and several success messages were
|
||
> fake. Honest status now:
|
||
> - **OCR-T1 engine layer: DONE.** A real provider (system Tesseract) with
|
||
> rotation, word boxes, language checks, a background job, validated ingest,
|
||
> an honest UI state machine and paginated WinAnsi PDF export. Details in §6.
|
||
> - **OCR-T2 GUI wiring: IN PROGRESS (compiles).** `app.rs` binds every
|
||
> control to `UiState` (verified widget APIs against the pinned rev:
|
||
> `label.set_text`, `text_input.set_text`, `drop_down.set_labels/selected`,
|
||
> `button.clicked`, `cx.new_next_frame` + `Event::NextFrame` pump from
|
||
> `slides_view`, `on_after_new` + `with_cx_mut` from `navigation_tab_bar`).
|
||
> OCR runs in `OcrJob` off-thread with frame-pump progress; language dropdown
|
||
> fills from `supported_languages()`; settings persist on change; system
|
||
> clipboard is `pbcopy`/`pbpaste` (zero new deps). `cargo check -p nigig-ocr`
|
||
> (default features) passes with 0 errors. Runtime window run pending.
|
||
> - **OCR-T3 Apple Vision binding and screen capture: NOT STARTED.** The
|
||
> Apple provider returns `NoEngine`; capture is geometry only.
|
||
> - Share, the Unicode PDF font and the size gate (§1) are open.
|
||
|
||
Source analyzed: `/Applications/OCR Tool.app` — 1.6M bundle, 1.1M universal
|
||
binary (`x86_64+arm64`, `__TEXT 393216`), Swift + Storyboard, macOS 10.15+,
|
||
Xcode 12.3, identifier `com.moneyplanttechnologies.ocrtool`.
|
||
Small because it links **system frameworks dynamically and ships no model**:
|
||
`Vision, PDFKit, AppKit, QuartzCore, StoreKit` + `libswift*` (`otool -L`,
|
||
`dyld_info -imports`, `strings | swift-demangle`).
|
||
|
||
Port target: `crates/apps/nigig-ocr/` (registered in workspace `Cargo.toml`),
|
||
Rust + Makepad, same small-binary property via OS-native APIs (§1).
|
||
|
||
## §0 Reuse-first workflow (MANDATORY — every phase starts here, no exceptions)
|
||
|
||
For each phase, the implementer MUST execute these 4 steps in order and log
|
||
the outcome in §6:
|
||
|
||
1. **Search existing crates** — run and record:
|
||
```bash
|
||
grep -rn "OcrEngine\|enhance_for_ocr\|scan_file\|PdfPageWidget\|DocumentScanner" crates/ --include="*.rs" | head -n 30
|
||
```
|
||
Candidate reuse set (audited, do not reimplement without justification):
|
||
- `crates/apps/pdf/pdf-document/src/ocr.rs` — `OcrEngine/OcrWord/OcrLayer/NoopOcr/StubOcr`
|
||
- `crates/apps/nigig_doc_scanner/src/scanner_core.rs` — `detect_document_corners/enhance_for_ocr`
|
||
- `crates/apps/nigig-site/src/ocr.rs` — `scan_file` traversal guard, `OcrDone` bg action
|
||
- `crates/apps/nigig-site/src/ocr_policy.rs` — SITE-14 honesty rule (never invent text)
|
||
- `crates/apps/pdf/pdf-makepad/src/{page_view,device,interaction}.rs` — PDF widget/device
|
||
- `crates/pageflipnav/src/features/tools/document_scanner/mod.rs` + `crates/apps/nigig_doc_scanner/src/scanner_frame/` — screen layout
|
||
- `crates/nigig-core` (`image`, persistence, dir), `crates/nigig-uikit`, `crates/nigig-system-prefs`
|
||
2. **Check Makepad fork** (`makepad-native-glue/makepad`, `work`+`dev` merged at `nigig-makepad-test-android`):
|
||
`examples/camera` (Video native/texture), `examples/pdf`, `examples/text_selection`,
|
||
`examples/uizoo` (`tab_image/tab_rotatedimage/tab_filetree`), `apps/image|pdf|files|photos`;
|
||
`work`-branch `fabric` (photo/camera pipeline), `ai-hub` (local model runner).
|
||
3. **Reuse or extend** (re-export, adapter, copy tested pattern). Only when nothing
|
||
exists write new code in `nigig-ocr` and note the gap in §6.
|
||
4. **Verify** with the phase gate (§3) — `cargo test` + `cargo check` + size check.
|
||
|
||
## §1 Small-binary strategy (reuse OS APIs like Swift — normative)
|
||
|
||
| Capability | macOS/iOS (`target_vendor="apple"`) | Windows | Linux | Harmony/Web/Android |
|
||
|---|---|---|---|---|
|
||
| Text OCR | `VNRecognizeTextRequest` via `objc2`, dynamic link, `system-ocr` feature — `src/providers/apple_vision.rs` | WinRT `Windows.Media.Ocr` via `windows` crate, `system-ocr`, future | system Tesseract via `dlopen` if present, else honest stub | honest stub + manual entry (SITE-14) |
|
||
| Barcode/QR | `VNDetectBarcodesRequest` | WinRT or `rqrr` fallback | `rqrr`/zbar if present | stub |
|
||
| PDF render/export | shared `nigig-pdf-{cos,document,graphics,makepad}` (pure Rust) | same | same | same |
|
||
| Screen capture | `CGWindowListCreateImage` (Apple only) | Win32 capture API | portal/`xcap`-style | stub + file import |
|
||
| Clipboard/share/print | `arboard` + native share/print glue | same | same | same |
|
||
| Camera/Continuity | file picker (Continuity is Apple-only, not portable) | file picker | file picker | camera intent + file picker |
|
||
|
||
Dependency policy (size): no bundled ML model, no Tesseract static link, no
|
||
heavy OCR crate in default features. OS bindings are `optional` + `cfg(target)`
|
||
+ `feature="system-ocr"`. Release profile: workspace `profile.small`
|
||
(`opt-level="z"`, `lto=true`, `codegen-units=1`, `strip=true`, `panic="abort"`).
|
||
Budget: original 1.6M (Swift+OS-only) is unattainable in Rust+Makepad;
|
||
target `<8M` universal, tracked per phase (`ls -lh target/small/*/nigig-ocr`, `cargo bloat`).
|
||
Privacy: offline-only, no network/telemetry by construction (original also offline
|
||
except App Store receipt + rate link, both stripped in Phase 8).
|
||
|
||
## §2 Binary forensics (Phase 0 — DONE, reproducible)
|
||
|
||
```bash
|
||
ls -R "/Applications/OCR Tool.app"
|
||
plutil -p "/Applications/OCR Tool.app/Contents/Info.plist"
|
||
otool -L "/Applications/OCR Tool.app/Contents/MacOS/OCR Tool"
|
||
strings .../MacOS/OCR Tool | sort -u
|
||
strings .../MacOS/OCR Tool | grep '^\$s8OCR' | xcrun swift-demangle
|
||
dyld_info -imports .../MacOS/OCR Tool | grep -E 'VN|CGWindow|Sharing|SKPayment'
|
||
strings .../Base.lproj/Main.storyboardc/XfG-*.nib
|
||
strings .../Resources/Assets.car
|
||
codesign -dv "/Applications/OCR Tool.app"
|
||
```
|
||
|
||
### Parity matrix (every shipped feature → evidence → port target → phase)
|
||
|
||
| # | Feature | Binary evidence | Port target | Phase |
|
||
|---|---|---|---|---|
|
||
| F1 | Open images + PDFs via drag-drop | `DropView`, `"Drag and drop photos and pdf files here"`, `NSFilenamesPboardType` | DropView widget + PDF raster via `nigig-pdf-*` | P2 |
|
||
| F2 | Grab screen area (single) | `btnSingleScreenshot/clickedSingleScreenshot:`, `SelectRegionView/WindowController`, `CGWindowListOption` | region overlay + `CGWindowListCreateImage` (Apple) | P3 |
|
||
| F3 | Grab many times | `btnMultipleScreenshot/clickedMultipleScreenshot:`, `"Grab screen area many times"` | multi-capture loop, stay-open overlay | P3 |
|
||
| F4 | Hide main window on capture | `isHideMainWindowWhenTakingScreenshot` | setting + window hide/show around capture | P3,P7 |
|
||
| F5 | Screen-Recording permission flow | permission string + `x-apple.systempreferences:...Privacy_ScreenCapture` + `Open System Preferences` | same text + settings deep-link (Apple), graceful stub elsewhere | P3 |
|
||
| F6 | Import from iPhone/iPad/scanner | `btnImport/clickedImport:`, Continuity note string | file picker stub (API not portable) | P2 |
|
||
| F7 | Do OCR / Do Batch OCR | `btnDoOCR/btnDoBatchOCR`, `"Do OCR!"/"Do Batch OCR!"`, `clickedDoOCR:/clickedDoBatchOCR:` | same buttons → engine calls | P1 shell, P4 wire |
|
||
| F8 | Current-page vs all-pages | `"Run text recognition on current/all pages"`, `recognize:File:error:` | page selector + batch runner | P4 |
|
||
| F9 | Fast vs accuracy mode | `isUseFastOCRMode`, `"Use Fast mode (unchecked = accuracy mode)"`, `setRecognitionLevel:` | `OcrLevel::Fast/Accurate` → Vision level | P1 type, P4 wire |
|
||
| F10 | Language correction | `usesLanguageCorrection/btnUsesLanguageCorrection`, `setUsesLanguageCorrection:` | bool → Vision correction flag | P1 type, P4 wire |
|
||
| F11 | 8-lang popup (+PRO 100+) | `popupLanguage/lblLanguage/clickedPopupLanguage:`, `OCRLanguage`, `ocrLanguageISOCodeSet`, `recognitionLanguages/possibleLanguages`, 8 display names | `OcrLanguage` enum + ISO codes + provider `supported_languages()` | P1 enum, P4 enumerate |
|
||
| F12 | Barcode + QR | `btnBarCodeScan/clickedBarCodeScan:`, `BarcodeScanner`, `VNBarcodeObservation`, `"Scan QR Codes or Barcodes"`, `"Could not perform barcode-request!"` | `barcode_mode` → `VNDetectBarcodesRequest` / `rqrr` fallback | P4 |
|
||
| F13 | Rotate left/right | `btnLeftRotate/btnRightRotate`, `clickedLeftRotate:/clickedRightRotate:`, `rotateDegree`, `rotate_left/right` icons | `FileItem::rotate_left/right` (done) → image transform in result view | P1 logic, P5 view |
|
||
| F14 | Overlay on input image | `btnShowOverlay/clickedShowOverlay:`, `overlayImage`, `isShowOverlayOnInputImage`, `closeOverlayView`, `close_overlay` icon | overlay boxes layer + toggle + close | P5 |
|
||
| F15 | Result text + timing + count | `textView`, `"Result (Processing time:"`, `"Number of results found:"`, `"No results found."` | result panel + labels | P5 |
|
||
| F16 | Progress + toast | `ActivityView`, `"Running OCR... 1/2"`, `ToastView`, `TSToastActivityViewKey` | progress bar + toast (reuse uikit pattern) | P5 |
|
||
| F17 | File list delete current/all | `btnDelete/btnDeleteAll`, `clickedDelete:/clickedDeleteAll:`, `"Delete All Files!/Delete all files/Delete current file"`, confirm strings, `clear/clear_all` icons | list store + confirm dialogs | P2 |
|
||
| F18 | Copy to clipboard | `btnCopy/clickedCopy:`, `"Copy text to clipboard"`, `generalPasteboard`, `copy` icons | `arboard` copy | P6 |
|
||
| F19 | Export TXT (single) | `btnSave/clickedSave:`, `exportTextFile`, `"Export Text File"`, `"Text File Exported"`, `save` icon | TXT writer | P6 |
|
||
| F20 | Export PDF (single + all) | `exportPDF/exportAllPDFs`, `dataWithPDFInsideRect:`, `"Export PDF or text file/with All Text"`, `"PDF File Exported/with All Text"` | PDF writer via `nigig-pdf-*` text layer (+ photos-metadata label) | P6 |
|
||
| F21 | Share | `btnShare/clickedShare:`, `NSSharingServicePickerDelegate`, `share` icon | native share glue | P6 |
|
||
| F22 | Print | `application:printFile:`, `printFiles:withSettings:showPrintPanels:` | print glue | P6 |
|
||
| F23 | Settings | `Settings`, `settings` icon, hide-window + overlay + language prefs | settings screen + `nigig-system-prefs` persistence | P7 |
|
||
| F24 | Light/dark mode | `NSAppearance` (system), marketing claim | Makepad theme | P7 |
|
||
| F25 | Trial/PRO purchase + Restore + Rate | `MPTPurchase` nib, SwiftyStoreKit (`PaymentsController`, `RestorePurchasesController`, …), `"Upgrade to PRO…"`, `"START FREE 30-DAY TRIAL"`, `"Already purchased…/Restore"`, `"Rate us on the App Store."` + App Store URL | STRIP all; optional `nigig-pay` hook or nothing; assert no purchase/rate strings in binary | P8 |
|
||
| F26 | Offline privacy | no network imports except StoreKit/receipt | no network deps; offline-only gate | P1,P8 |
|
||
| F27 | Universal M1+Intel, 10.15+ | universal binary, `LSMinimumSystemVersion 10.15` | universal release, min-version doc | P9 |
|
||
| F28 | Toolbar icon set | `Assets.car`: backward, forward, capture, capture_multiple, clear, clear_all, close_overlay, copy, import, rotate_left/right, save, scanner, settings, share (+AppIcon) | same icon set in Makepad theme | P1 placeholder, P5 final |
|
||
|
||
## §3 Phases (each: §0 check → build → gate; gates are normative)
|
||
|
||
### Phase 0 — Forensics freeze — DONE
|
||
Deliver: §2 + parity matrix. Gate: reviewed, commands reproducible. Result: pass.
|
||
|
||
### Phase 1 — Scaffold + engine types — DONE
|
||
Deliver: `Cargo.toml` (`engine/app/system-ocr`), `src/lib.rs|engine.rs|languages.rs|providers/{mod,fallback,apple_vision}.rs|app.rs|main.rs`.
|
||
Reuse: pdf-document OCR semantics; SITE-14 honesty; Makepad examples (camera/pdf/text_selection/uizoo), apps (image/pdf/files). New: option-aware `OcrEngine` seam, `FileItem/OcrOptions/OcrLevel/OcrResult/OcrError`, 8-lang enum, UI shell (F7,F11,F28 placeholders).
|
||
Gate: standalone `cargo test` — 7 passed (rotate wrap, stub empty-refuse, stub text, 8-lang popup, zh codes, Apple popup set, fallback honesty). Workspace `cargo test -p nigig-ocr` blocked by pre-existing tree breakage (nigig-traffic/makepad rev + offline fetch) — recorded, not caused by this crate. Result: pass (standalone).
|
||
|
||
### Phase 2 — Ingest (F1,F6,F17; partial F7)
|
||
- §0: `nigig-site::ocr::scan_file` guard + `OcrDone`; `nigig-pdf-document/graphics` raster; Makepad `apps/files`, `examples/uizoo tab_filetree`.
|
||
- Build: DropView widget (accept PNG/JPEG/PDF, reject others with toast); `FileItem` list store (add/select/delete/delete-all + confirm strings); PDF multi-page → one item per page (reuse pdf raster); Continuity → file-picker stub with original note text; traversal guard copy + tests.
|
||
- Gate: drop PNG + 3-page PDF → 4 list entries; delete/delete-all with confirms; `/etc/hostname` refused; `cargo test -p nigig-ocr` ingest tests green.
|
||
|
||
### Phase 3 — Capture (F2,F3,F4,F5)
|
||
- §0: Makepad overlay/window patterns (`examples/floating_panel`, `apps/wm`); Apple `CGWindowList` binding pattern from `robius-*`/platform code.
|
||
- Build: region overlay (drag + 4 corner handles = `CornerResizeView` parity); single + multi (stay-open) modes; hide-window flag; permission explainer + System Settings deep-link on Apple, graceful message elsewhere; `screencapture -R` parity on macOS.
|
||
- Gate: captured rect pixel-equals reference for 3 rects; multi-mode yields N items without reopening picker; permission-denied path shows explainer, no crash.
|
||
|
||
### Phase 4 — Recognition providers (F7,F8,F9,F10,F11,F12)
|
||
- §0: `scanner_core::enhance_for_ocr` preprocess; `pdf-document::ocr` seam; Makepad `ai-hub` local-runner pattern.
|
||
- Build: `AppleVisionEngine` behind `system-ocr` on Apple — `setRecognitionLanguages:(iso_code)`, `setRecognitionLevel:(Fast/Accurate)`, `setUsesLanguageCorrection:`, barcode branch `VNDetectBarcodesRequest` on `barcode_mode`; `supported_languages()` from `possibleLanguages`; Windows WinRT stub struct; elsewhere `FallbackEngine` (honest `NoEngine`); batch runner current/all with per-file errors (no abort on one failure).
|
||
- Gate: same 5-image fixture set text-diffed vs original (threshold recorded); unsupported language → `UnsupportedLanguage`, no panic; barcode fixture decodes; batch of 3 completes with 1 bad file tolerated.
|
||
|
||
### Phase 5 — Result (F13,F14,F15,F16; F28 final)
|
||
- §0: `examples/uizoo tab_image/tab_rotatedimage`, `examples/text_selection`, `apps/image`.
|
||
- Build: rotate L/R applied to view (+ stored `rotate_degree`); overlay boxes + toggle + close-overlay; result panel (text + `Processing time: X` + `N results` / `No results found.`); progress `Running OCR… i/n` + toasts; full toolbar icon set wired.
|
||
- Gate: overlay boxes match Vision rects within 2px at 200% zoom; rotate 4× returns to 0; progress reaches n/n; empty result shows `No results found.`.
|
||
|
||
### Phase 6 — Export (F18,F19,F20,F21,F22)
|
||
- §0: `nigig-pdf-makepad::MakepadPdfDevice` + `pdf-document/graphics/cos` text layer; `arboard` (add dep only here).
|
||
- Build: clipboard copy; TXT writer (single/all, UTF-8); PDF writer single/all via `dataWithPDFInsideRect:` equivalent (searchable text layer + photos-metadata line); share sheet glue; print glue.
|
||
- Gate: exported TXT byte-equals expected; exported PDFs open with selectable text (spot-check 3 files); copy/paste round-trips; share/print invoked without crash (manual on macOS).
|
||
|
||
### Phase 7 — Settings/persist/theme (F4,F11,F23,F24)
|
||
- §0: `nigig-system-prefs` + `nigig-core` persistence; Makepad theme/light-dark.
|
||
- Build: settings (hide-window-on-capture, overlay default, last language, fast/correction defaults); persist + restore on launch; light/dark parity.
|
||
- Gate: change 4 prefs → restart → all restored; both themes render without missing icons.
|
||
|
||
### Phase 8 — Monetization strip + harden (F25,F26)
|
||
- §0: confirm no existing purchase dependency (`grep -ri "SwiftyStoreKit\|SKPayment" crates/apps/nigig-ocr` must be empty).
|
||
- Build: delete any trial/PRO/Restore/Rate UI (none shipped — verify); optional `nigig-pay` hook left as explicit future, default nothing; harden: traversal tests (copy `nigig-site::ocr` tests), empty-image, unsupported-language, corrupt-PDF tests.
|
||
- Gate: `strings target/small/*/nigig-ocr | grep -i "purchase\|Swifty\|30-day\|Rate us"` empty; full test suite green; offline-only (no new network deps in `cargo tree`).
|
||
|
||
### Phase 9 — Size + release (F27 + full demo)
|
||
- §0: workspace `profile.small` + `PLAN_PERF_OPTIMIZATION.md` conventions.
|
||
- Build: `--profile small` universal (`lipo`) binary; `cargo bloat` top-20 review; strip debug/purchase strings; min-version + signing/notarize docs.
|
||
- Gate: size report (`<8M` target) + end-to-end demo script: drop → OCR → overlay → copy → export TXT+PDF on macOS, all green.
|
||
|
||
## §4 Test strategy (normative per phase)
|
||
Unit (`cargo test -p nigig-ocr` / standalone mirror while workspace broken):
|
||
P1 7 done; P2 traversal + list ops; P4 empty/unsupported/barcode/batch-tolerance;
|
||
P5 rotate-wrap; P8 corrupt-PDF. Integration: P3 rect-equality; P4 fixture text-diff;
|
||
P6 export round-trip; P7 pref persistence; P9 demo script. Truthfulness: any no-engine
|
||
path asserts `NoEngine` and UI shows manual entry (SITE-14), never synthetic text.
|
||
|
||
## §5 Risks
|
||
R1 workspace tree pre-broken (traffic/makepad rev, offline fetch) blocks `cargo test -p` → mitigate: standalone `/tmp/ocr_verify` mirror until tree fixed; do not widen scope to fix traffic here.
|
||
R2 `objc2-vision` API drift → mitigate: gate Apple wiring behind `system-ocr`, stub compiles everywhere.
|
||
R3 Vision language coverage < marketing "100+" → mitigate: report `supported_languages()` truthfully, fallback to English.
|
||
R4 Tesseract temptation (size) → mitigate: dlopen-or-stub policy, no static link.
|
||
|
||
## §6 Phase log (append-only; reused files, new files, gaps, gate result)
|
||
- Phase 0: reused forensic commands (§2); new: parity matrix. Gate: pass.
|
||
- Phase 1: reused `pdf-document/src/ocr.rs`, `ocr_policy.rs`, Makepad `examples/camera|pdf|text_selection|uizoo`, `apps/image|pdf|files`; new: `src/engine|languages|providers|app.rs|main.rs`; gap: none (Apple wiring deferred to P4 by design). Gate: 7/7 standalone pass.
|
||
- Phase 2–9: DONE (implementation). Reuse per §0 throughout; new: `src/ingest.rs`,
|
||
`src/capture.rs`, `src/batch.rs`, `src/result.rs`, `src/export.rs`, `src/settings.rs`,
|
||
`src/ui_state.rs`, `src/bin/nigig-ocr-demo.rs`, `tests/parity.rs`,
|
||
`tests/hardening.rs`, `tests/makepad_ui_plan.rs`, `tools/size-report.sh`.
|
||
Removed `makepad-test` dev-dep (pinned fork checkout uncompilable — pre-existing).
|
||
Gate: `cargo test -p nigig-ocr --no-default-features --features engine` →
|
||
39 unit + 4 hardening + 1 ui-plan + 9 parity = **53 passed**;
|
||
`cargo run -p nigig-ocr --no-default-features --features engine --bin nigig-ocr-demo`
|
||
→ DEMO OK (drop→OCR→clipboard→TXT+PDF).
|
||
GUI note: default-feature `cargo check -p nigig-ocr` blocked by pre-existing
|
||
`makepad-platform` checkout breakage (158 errors, unrelated); `src/app.rs` /
|
||
`src/main.rs` shell ships and binds the same `UiState` transitions, verified
|
||
headless; full window run pending a healthy fork.
|
||
- **Correction, 2026-09-26:** the Phase 2–9 entry above overstated things.
|
||
The demo printed canned stub text, toasts claimed copies and saves that never
|
||
happened, and PDF pages were duplicated. The pieces below are what is real now.
|
||
- **OCR-T1 (engine layer): DONE.**
|
||
- New files: `src/image_prep.rs`, `src/jobs.rs`, `src/providers/tesseract.rs`,
|
||
`tests/real_ocr.rs` and the `tests/fixtures/hello.png` fixture.
|
||
- Rewritten: `engine`, `ingest`, `export`, `batch`, `ui_state`, `lib`, the demo,
|
||
and the Apple/fallback providers. Patched: `capture`, `settings`.
|
||
- Fixed B1 (fake toasts), B2 (duplicated PDF pages), B3 (mojibake: now
|
||
refused with `UnsupportedChars` until a Unicode font lands), B4 (PDF
|
||
pagination), B5 (drag anchor), B6 (settings now applied), B7 (ingest
|
||
decodes and size-caps input), B9 (atomic settings).
|
||
- Removed the dead nigig-core, uikit, pdf-document and serde dependencies.
|
||
`system-ocr` now enables objc2.
|
||
- Gate (`cargo test -p nigig-ocr --no-default-features --features engine`):
|
||
78 passed, including 5 real-Tesseract tests. Set
|
||
`NIGIG_OCR_REQUIRE_TESSERACT=1` so CI fails instead of skipping.
|
||
Clippy is clean.
|
||
- The demo OCRs the fixture to "Hello Nairobi 2026 / Invoice total 4500".
|
||
- Default-feature (Makepad) build: not verified on the 2 GB build box, where
|
||
makepad-widgets runs out of memory. `app.rs` and `main.rs` don't use any
|
||
changed API.
|
||
- **Next, OCR-T2:** bind `app.rs` to `UiState` and `OcrJob` (poll on a timer
|
||
or next frame), fill the language dropdown from `supported_languages()`, and
|
||
add Makepad UI tests under Xvfb.
|
||
- **OCR-T3a Vision (2026-09-26, this session): DONE.**
|
||
- New: `src/providers/vision.rs` — `VNRecognizeTextRequest` through raw
|
||
`objc2` (`AnyClass` + `msg_send!`, no new crates): language ISO codes,
|
||
fast/accurate level, guarded language-correction, bottom-left → top-left
|
||
box flip, confidence → 0..100. `default_engine()` prefers Vision on Apple.
|
||
- Cargo: `objc2-foundation` gains `NSArray/NSData/NSDictionary/NSEnumerator`.
|
||
- Gate: 2 live Vision tests on the fixture (text + box geometry/ordering).
|
||
- **Capture backend (2026-09-26): DONE (macOS).**
|
||
- `capture::capture_rect_to_png` shells `/usr/sbin/screencapture` (no shell,
|
||
30 s timeout) and verifies PNG magic; degenerate rects rejected before
|
||
touching disk; other OSes get `UnsupportedPlatform`; headless sessions
|
||
get typed `Failed`, never a panic. Live-capture test included.
|
||
- **CJK PDF export (2026-09-26): DONE.**
|
||
- `export` picks Latin / GBK / BIG5 per document via strict system `iconv`
|
||
(never `//IGNORE`); non-embedded Type0 + CIDFont (`STSong-Light/GBK-EUC-H`,
|
||
`MSung-Light/B5-H`) keeps the binary small; mixed Latin/CJK lines segment
|
||
into `/F1` WinAnsi + `/F2` hex runs; width-aware wrap; emoji still refused
|
||
with counts. TXT remains the full-fidelity path; non-Adobe-CMap readers
|
||
may not render CJK runs (documented in code).
|
||
- **Share/print (2026-09-26): DONE (OS commands).**
|
||
- `reveal_in_files` (macOS `open -R`), `print_file` (`lp`); missing paths
|
||
refused before spawning; share sheet still needs the GUI runloop (OCR-T2).
|
||
- **Small fixes (2026-09-26):** Tesseract stderr drained on a thread (was a
|
||
pipe-deadlock corner); `atomic_write` temp names use a sequence counter.
|
||
- **Barcode + PDF rasterizer: still open, honestly.** No `rqrr`/`zbar` CLI or
|
||
crate available offline and no `pdftoppm`/`gs`/`mutool` on the box, so
|
||
`BarcodeUnsupported` / `NeedsRasterizer` stand with tests. Revisit with
|
||
network (add `rqrr`, or `pdfium` rasterizer).
|
||
- **Size gate: PENDING** — run `tools/size-report.sh --build` (profile.small
|
||
demo binary) once the build lock frees.
|