ikromjon / nativephp-mobile-document-scanner
Document scanner plugin for NativePHP Mobile - scan documents with edge detection, cropping, and perspective correction using native camera APIs
Package info
github.com/Ikromjon1998/nativephp-mobile-document-scanner
Type:nativephp-plugin
pkg:composer/ikromjon/nativephp-mobile-document-scanner
Requires
- php: ^8.3
- nativephp/mobile: ^3.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.28
- orchestra/testbench: ^9.0
- pestphp/pest: ^3.0
- phpstan/phpstan: ^2.0
- rector/rector: ^2.3
This package is auto-updated.
Last update: 2026-05-11 17:14:02 UTC
README
Scan documents with automatic edge detection, perspective correction, and cropping in your NativePHP Mobile app — powered by native platform APIs.
Quick Start
composer require ikromjon/nativephp-mobile-document-scanner
php artisan native:plugin:register ikromjon/nativephp-mobile-document-scanner
php artisan native:run android # or ios
use Ikromjon\DocumentScanner\Facades\DocumentScanner; use Ikromjon\DocumentScanner\Events\DocumentScanned; use Native\Mobile\Attributes\OnNative; // In your Livewire component: DocumentScanner::scan(); // Handle the result (Livewire method) #[OnNative(DocumentScanned::class)] public function onScanned($data) { $paths = $data['paths']; // ['/path/scan_0.jpg', ...] $pageCount = $data['pageCount']; // 2 }
That's it. The scanner opens, the user scans, and you get the file paths back via events. See below for full options and JavaScript usage.
How It Works
| Platform | Native API | Features |
|---|---|---|
| iOS | VisionKit (VNDocumentCameraViewController) |
Auto edge detection, perspective correction, shadow removal, multi-page |
| Android | Google ML Kit Document Scanner | Auto edge detection, cropping, rotation, multi-page, gallery import |
No external API keys or internet required. Camera permission is handled automatically.
Installation
composer require ikromjon/nativephp-mobile-document-scanner php artisan native:plugin:register ikromjon/nativephp-mobile-document-scanner
Build your app (plugin requires a native build):
php artisan native:run android
# or
php artisan native:run ios
Note: The scanner requires a native build on a real device — it won't work with
php artisan serve. If you callscan()without a native build, you'll see a warning in your Laravel log.
Configuration
Optionally publish the config file:
php artisan vendor:publish --tag=document-scanner-config
| Key | Default | Description |
|---|---|---|
default_max_pages |
0 |
Default max pages per scan (0 = unlimited) |
max_pages_limit |
100 |
Absolute cap on pages per scan |
default_output_format |
jpeg |
Default output format (jpeg or pdf) |
default_jpeg_quality |
90 |
Default JPEG compression quality (1-100) |
storage_directory |
scanned-documents |
Subdirectory for scanned files |
default_gallery_import |
false |
Allow gallery import (Android only) |
default_scanner_mode |
full |
Scanner mode: base, filter, full (Android only) |
Usage (PHP)
Scan a Document
use Ikromjon\DocumentScanner\Facades\DocumentScanner; // Scan with defaults DocumentScanner::scan(); // Scan with options DocumentScanner::scan([ 'maxPages' => 3, 'outputFormat' => 'jpeg', 'jpegQuality' => 85, ]); // Scan a single page (e.g. ID card) DocumentScanner::scan(['maxPages' => 1]); // Scan to PDF DocumentScanner::scan(['outputFormat' => 'pdf']);
The scan() method opens the native scanner UI and returns immediately. Results are delivered asynchronously via events.
Type-Safe DTO
use Ikromjon\DocumentScanner\Data\ScanOptions; use Ikromjon\DocumentScanner\Enums\OutputFormat; DocumentScanner::scan(new ScanOptions( maxPages: 5, outputFormat: OutputFormat::Pdf, ));
Scan Parameters
| Parameter | Type | Platform | Description |
|---|---|---|---|
maxPages |
int | Both | Max pages to scan (0 = unlimited) |
outputFormat |
OutputFormat|string | Both | jpeg or pdf |
jpegQuality |
int | Both | JPEG quality 1-100 (only for jpeg output) |
galleryImport |
bool | Android only | Allow importing from device gallery |
scannerMode |
ScannerMode|string | Android only | base, filter, or full |
Full Livewire Example
A complete component that scans documents and displays the results:
use Livewire\Component; use Native\Mobile\Attributes\OnNative; use Ikromjon\DocumentScanner\Facades\DocumentScanner; use Ikromjon\DocumentScanner\Events\DocumentScanned; use Ikromjon\DocumentScanner\Events\ScanCancelled; use Ikromjon\DocumentScanner\Events\ScanFailed; class DocumentScannerComponent extends Component { public array $scannedFiles = []; public string $error = ''; public function scan() { DocumentScanner::scan(['maxPages' => 5]); } #[OnNative(DocumentScanned::class)] public function onScanned($data) { $this->scannedFiles = $data['paths']; } #[OnNative(ScanCancelled::class)] public function onCancelled() { // User dismissed the scanner } #[OnNative(ScanFailed::class)] public function onFailed($data) { $this->error = $data['error']; } }
Important: Use
#[OnNative(...)](not#[On(...)]) for NativePHP events.
Listening to Events (Laravel)
use Ikromjon\DocumentScanner\Events\DocumentScanned; class HandleDocumentScanned { public function handle(DocumentScanned $event): void { // $event->paths — array of file paths // $event->pageCount — number of pages scanned // $event->outputFormat — 'jpeg' or 'pdf' } }
Usage (JavaScript)
import { scan, imagesToPdf, pdfToImages, Events, } from "../../vendor/ikromjon/nativephp-mobile-document-scanner/resources/js/index.js"; import { On } from "#nativephp"; // Open scanner await scan({ maxPages: 3, outputFormat: "jpeg", jpegQuality: 90 }); // Listen for results On(Events.DocumentScanned, (payload) => { console.log("Scanned:", payload.paths, payload.pageCount); }); On(Events.ScanCancelled, () => { console.log("Cancelled"); }); On(Events.ScanFailed, (payload) => { console.error("Failed:", payload.error); }); // Combine images into PDF const result = await imagesToPdf(["/path/scan_0.jpg", "/path/scan_1.jpg"]); console.log("PDF:", result.path); // Extract thumbnails from PDF const thumbs = await pdfToImages("/path/scan.pdf", 80); console.log("Pages:", thumbs.paths);
Events
| Event | Payload | When |
|---|---|---|
DocumentScanned |
paths, pageCount, outputFormat |
Scanning completed successfully |
ScanCancelled |
— | User cancelled the scanner |
ScanFailed |
error |
An error occurred |
PdfCreated |
path |
imagesToPdf() completed |
Scanned Files
Scanned files are saved to the app's internal storage under the configured storage_directory (default: scanned-documents). File paths returned in the DocumentScanned event are absolute paths on the device.
- JPEG output: one file per page (e.g.
scan_0.jpg,scan_1.jpg) - PDF output: a single multi-page PDF file
- Files persist until the app is uninstalled or you delete them manually
Which format to use?
JPEG (default) is the better choice for most apps. You get individual files per page, which means you can show page previews, let users view/delete specific pages, and convert to PDF later on your own terms (e.g. with fpdf). You also control file size via jpegQuality.
PDF is useful when you need a single file immediately and don't need to display or manipulate individual pages. Note that the native scanner produces the PDF directly — you can't extract page previews from it without a separate library.
See Smart Docs for an example that scans as JPEG by default and converts to PDF on demand.
Required Permissions
Android: CAMERA — declared automatically via nativephp.json. ML Kit handles the scanner UI internally.
iOS: VisionKit requests camera access at runtime. Your app's Info.plist must include an NSCameraUsageDescription string — NativePHP sets a default, but you should customize it for your app store submission (e.g. "This app uses the camera to scan documents").
No API keys or internet required.
Demo App
See Smart Docs — a full NativePHP mobile app that uses this plugin for document scanning.
Documentation
- Installation — requirements, setup steps, verification
- Configuration — all config options explained
- Usage with Livewire — Livewire components and event handling
- Usage with JavaScript — Inertia Vue/React integration
- API Reference — events, DTOs, enums, validation, contracts
JPEG-to-PDF Conversion
Combine scanned JPEG pages into a single PDF on-device — no extra PHP library needed:
use Ikromjon\DocumentScanner\Facades\DocumentScanner; use Ikromjon\DocumentScanner\Events\PdfCreated; use Native\Mobile\Attributes\OnNative; // Combine images into a PDF $result = DocumentScanner::imagesToPdf([ '/path/scan_0.jpg', '/path/scan_1.jpg', ]); $pdfPath = $result['path']; // '/path/combined_1712345678.pdf' // Listen for completion #[OnNative(PdfCreated::class)] public function onPdfCreated($data) { $pdfPath = $data['path']; }
Works with any JPEG files, not just scanned documents.
PDF Page Thumbnails
Extract page previews from a PDF — useful when you scan as PDF but need page-level previews:
$result = DocumentScanner::pdfToImages('/path/scan.pdf', quality: 80); $pagePaths = $result['paths']; // ['/path/page_0.jpg', '/path/page_1.jpg']
Planned Features
- File management — list, delete, and clean up scanned files via the plugin API
- Image post-processing — grayscale, contrast, rotation on scanned images
See ROADMAP.md for full details and status.
Testing
composer test
composer analyse
Requirements
- PHP 8.3+
- NativePHP Mobile v3+
- iOS 13+ / Android API 21+
License
MIT