protendai/nativephp-mobile-ocr
android.min_version 21 and ios.min_version 13.0.recognize(), capture(), scan(), events, and layout helpers.src/Events/TextRecognized.php and src/RecognitionFailed.php for the result contract.src/Layout.php for rows() and valueBeside().resources/js/index.js and its TypeScript types for Inertia/Vue/React usage.TextRecognized, pass the recognized lines through Layout::rows(), and parse the resulting rows into structured line items. This avoids depending on raw box coordinates and works across Android and iOS.use App\Events\DocumentScanned;
use NativePHP\TextRecognition\Facades\TextRecognizer; // use the facade name from the README
TextRecognizer::scan(); // opens the native document scanner
Then handle the result in a listener/controller:
use NativePHP\TextRecognition\Events\TextRecognized;
use NativePHP\TextRecognition\Layout;
public function handle(TextRecognized $event): void
{
$rows = Layout::rows($event->lines);
// $rows is unit-agnostic; no need to normalize iOS fractions manually.
$invoiceNumber = Layout::valueBeside($event->lines, 'Invoice');
}
recognize($path) for images already on disk.capture() when you want a single photo from the camera.scan() when you want the native document scanner with edge detection/cropping.capture()/scan() request has its own id, and failures arrive for that id. Keep the id in component state so a RecognitionFailed does not clear the wrong UI.use NativePHP\TextRecognition\RecognitionFailed;
public function failed(RecognitionFailed $event): void
{
if ($event->isPermissionDenied()) {
$this->showPermissionHelp = true;
return;
}
if ($event->isBusy()) {
$this->status = 'Another capture or scan is already open.';
return;
}
$this->status = $event->message;
}
Layout::rows() and Layout::valueBeside() are unit-agnostic, so they are the safest way to build forms, tables, and key/value extraction. Use width/height only when you need to normalize or map boxes yourself.$x = $box->left / $event->width;
$y = $box->top / $event->height;
Guard against null dimensions from older native code before dividing.
storage/app/... immediately.recognize, capture, scan, event listeners, and rows() from resources/js/index.js (or the published package entry point). This is the shortest path for SPA-style screens that should not round-trip through a full page reload.width/height. Code using Layout::rows() or Layout::valueBeside() is unaffected.width and height are optional. Older native code can send null. Do not assume they are always present; fall back gracefully or skip coordinate normalization.width/height versus the original dimensions.capture() or scan() can be open at a time. A second call receives RecognitionFailed::BUSY for its own id instead of silently orphaning the first request. Disable duplicate actions in the UI.capture() no longer inserts into MediaStore; photos go to the app cache through the NativePHP core FileProvider. This fixes crashes and empty gallery files on Android 8/9, but it also means users will not find captures in their photo gallery.$path if you need persistence. Temp files are also deleted when the user cancels or the capture fails.RecognitionFailed('Camera permission denied'), the same as Android, instead of ScanCancelled. Do not use cancellation as a proxy for permission denial.NSCameraUsageDescription is in the manifest, so capture() and scan() work without app-side Info.plist edits. If you customize permission text, verify the manifest merge.width/height, and first few box coordinates; verify whether the path still exists; confirm only one capture/scan is active; test on Android 8/9 and with a very high-resolution photo; and use FakeBridge to isolate PHP-side handling from native behavior.Layout helpers for text reconstruction, and the JS event listeners for custom Inertia/Vue/React UIs. If you need custom native behavior, follow the moved native source layout under resources/android/src/ and resources/ios/Sources/.How can I help you explore Laravel packages today?