Turn any image or PDF into structured, actionable data.
A powerful, developer-friendly Laravel package that reads text from images and PDFs, understands the content, fixes scanning errors with AI, and delivers clean, structured data directly to your application.
Why this package? Most OCR tools just give you a dump of raw text. This package gives you typed DTOs, structured fields, and confidence scores. It knows the difference between an Invoice Number and a Phone Number.
- ✨ Features
- 📐 Architecture
- 🚀 Installation
- ⚙️ Configuration
- 📖 Usage
- 🖥️ Artisan Commands
- 🎨 Blade Component
- 🗄️ Database Schema
- 🧩 OCR Driver Interface
- 🤖 AI Cleanup Details
- 🧪 Testing
- 📄 API Reference
- 📄 License
- 🧠 Multi-Driver OCR Engine: Seamlessly switch between Tesseract (offline/privacy-first), Google Cloud Vision, AWS Textract, or Azure Computer Vision drivers.
- 🤖 Optional AI Cleanup: Can use the
laravel/aiSDK with theCleanupAgentto fix OCR typos (e.g.,arnount→amount,nurnber→number) and normalize data formats. Supports OpenAI, Anthropic, Gemini, Ollama, DeepSeek, Groq, Mistral, and more when the AI stack is installed. - 📦 Typed DTOs: Returns an
OcrResultdata transfer object withtext,confidence,bounds, andmetadataproperties — not just raw arrays. - 📑 Advanced Invoice Extraction: Specialized algorithms to extract line items (quantity, description, unit price, total), subtotals, tax, shipping, and totals from complex invoice layouts.
- 🔍 Auto-Classification: Automatically detects document types (Invoice, Receipt, Contract, Purchase Order, Shipping, General) using keyword scoring.
- 📋 Reusable Templates: Define document templates with regex patterns and positional extraction to target specific fields. Supports template creation, import/export, and auto-matching.
- ⚡ Workflows: Define custom processing pipelines per document type in your config (e.g., "If Invoice → Extract Tables → Verify Required Fields").
- 🎨 Blade Component: Built-in
<x-laravel-ocr::document-preview>component to visualize results with bounding box overlays, inline editing, and data export. - 🖥️ Artisan Commands: CLI tools for environment diagnostics (
laravel-ocr:doctor), template creation (laravel-ocr:create-template), and document processing (laravel-ocr:process). - 💾 Database Persistence: Optionally save processed results to the
ocr_processed_documentstable with template associations, confidence scores, and processing times. - 🔒 Enterprise Security: Encrypted storage options, MIME-type validation, malware scanning, and full offline support for sensitive data.
src/
├── Agents/
│ └── CleanupAgent.php # Laravel AI SDK agent for OCR post-processing
├── Console/Commands/
│ ├── CreateTemplateCommand.php # artisan laravel-ocr:create-template
│ └── ProcessDocumentCommand.php # artisan laravel-ocr:process
├── Contracts/
│ └── OCRDriver.php # Interface: extract, extractTable, extractBarcode, extractQRCode, etc.
├── Drivers/
│ ├── TesseractDriver.php # Offline OCR via thiagoalessio/tesseract_ocr
│ ├── GoogleVisionDriver.php # Google Cloud Vision API
│ ├── AWSTextractDriver.php # AWS Textract API
│ └── AzureOCRDriver.php # Azure Computer Vision API
├── DTOs/
│ └── OcrResult.php # DTO: text, confidence, bounds, metadata
├── Enums/
│ ├── DocumentType.php # Invoice, Receipt, Contract, PurchaseOrder, Shipping, General
│ └── OcrDriver.php # Tesseract, GoogleVision, AWSTextract, Azure
├── Exceptions/
│ ├── AICleanupException.php
│ ├── DocumentParserException.php
│ └── OCRException.php
├── Facades/
│ └── LaravelOcr.php # Facade for OCRManager
├── Models/
│ ├── DocumentTemplate.php # Template definition with fields relationship
│ ├── ProcessedDocument.php # Stored OCR results with field accessors
│ └── TemplateField.php # Individual field definition with validation rules
├── Services/
│ ├── OCRManager.php # Driver manager (extends Illuminate\Support\Manager)
│ ├── AICleanupService.php # AI cleanup + basic rule-based typo correction
│ ├── DocumentParser.php # Main parsing engine with field/table/metadata extraction
│ └── TemplateManager.php # Template CRUD, import/export, auto-matching
└── LaravelOcrServiceProvider.php # Service provider: bindings, config, migrations, views, commands
Requires PHP 8.2+.
Core OCR features support Laravel 9 / 10 / 11 / 12 / 13.
AI cleanup is optional and depends on the laravel/ai package version you install.
| Capability | PHP | Laravel | Extra Package |
|---|---|---|---|
| Core OCR, templates, parsing, console commands | 8.2+ |
9+ |
None |
AI cleanup with provider=basic |
8.2+ |
9+ |
None |
AI cleanup with laravel/ai |
Depends on the laravel/ai version you install |
Depends on the laravel/ai version you install |
laravel/ai |
If you need the widest compatibility, keep AI cleanup on provider=basic.
Install the package, publish the config and migrations, verify the runtime, then process a document:
composer require mayaram/laravel-ocr
php artisan vendor:publish --tag=laravel-ocr-config
php artisan vendor:publish --tag=laravel-ocr-migrations
php artisan migrate
php artisan laravel-ocr:doctor
php artisan laravel-ocr:process storage/app/sample-invoice.pdf --type=invoiceFor AI cleanup, install laravel/ai separately and configure your provider credentials before using --ai-cleanup.
composer require mayaram/laravel-ocrcomposer require laravel/ailaravel/ai has its own PHP/Laravel constraints. Check the version you install if you need AI cleanup on top of the core OCR package.
php artisan vendor:publish --tag=laravel-ocr-config
php artisan vendor:publish --tag=laravel-ocr-migrations
php artisan migratephp artisan vendor:publish --tag=laravel-ocr-views# macOS
brew install tesseract
# Ubuntu/Debian
sudo apt-get install tesseract-ocr
# Verify installation
tesseract --versionSet your preferred driver and credentials in your .env file.
# Options: tesseract, google_vision, aws_textract, azure
LARAVEL_OCR_DRIVER=tesseractAll processing happens on your server. No data leaves your infrastructure.
LARAVEL_OCR_DRIVER=tesseract
TESSERACT_BINARY=/usr/bin/tesseract
TESSERACT_LANGUAGE=eng
TESSERACT_TIMEOUT=60LARAVEL_OCR_DRIVER=google_vision
GOOGLE_VISION_KEY_FILE=/path/to/service-account.json
GOOGLE_VISION_PROJECT_ID=your-project-idRequires:
composer require google/cloud-vision
LARAVEL_OCR_DRIVER=aws_textract
AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
AWS_DEFAULT_REGION=us-east-1Included:
aws/aws-sdk-phpis a core dependency.
LARAVEL_OCR_DRIVER=azure
AZURE_OCR_ENDPOINT=https://your-resource.cognitiveservices.azure.com
AZURE_OCR_KEY=your-subscription-key
AZURE_OCR_VERSION=3.2Enable AI-powered OCR post-processing to fix scanning errors and normalize data formats. This feature is optional and requires laravel/ai to be installed in the host app. The package uses a dedicated CleanupAgent that supports multiple LLM providers.
LARAVEL_OCR_AI_CLEANUP=true
LARAVEL_OCR_AI_PROVIDER=openai
LARAVEL_OCR_AI_TIMEOUT=60
# Optional: Set a default custom prompt for all AI cleanup calls
# LARAVEL_OCR_AI_CUSTOM_PROMPT="Extract all amounts in INR. Format names in Title Case."Set the API key for your chosen provider:
| Provider | Env Variable | Driver Key |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
openai |
| Anthropic | ANTHROPIC_API_KEY |
anthropic |
| Google Gemini | GEMINI_API_KEY |
gemini |
| Ollama (Local) | OLLAMA_API_KEY |
ollama |
| DeepSeek | DEEPSEEK_API_KEY |
deepseek |
| Groq | GROQ_API_KEY |
groq |
| Mistral | MISTRAL_API_KEY |
mistral |
| Azure OpenAI | AZURE_OPENAI_API_KEY |
azure |
| Cohere | COHERE_API_KEY |
cohere |
| OpenRouter | OPENROUTER_API_KEY |
openrouter |
| xAI | XAI_API_KEY |
xai |
| Jina | JINA_API_KEY |
jina |
| VoyageAI | VOYAGEAI_API_KEY |
voyageai |
| ElevenLabs | ELEVENLABS_API_KEY |
eleven |
Process documents asynchronously:
LARAVEL_OCR_QUEUE_ENABLED=false
LARAVEL_OCR_QUEUE_CONNECTION=default
LARAVEL_OCR_QUEUE_NAME=ocr-processingUse the doctor command to verify the package runtime setup before processing documents:
php artisan laravel-ocr:doctorIt checks:
- PHP version compatibility
- The configured default OCR driver
- Tesseract binary availability when Tesseract is the active driver
- Optional AI cleanup readiness
Use these checks before debugging extraction quality:
php artisan laravel-ocr:doctor
tesseract --versionCommon setup issues:
Tesseract binary not found: setTESSERACT_BINARYto the real binary path on your machine.- AI cleanup warning: install
laravel/ai, then configureLARAVEL_OCR_AI_PROVIDERand the matching API key. - PDF conversion issues: ensure the host has the extensions and system tools required by your PDF/image pipeline.
- Laravel install conflicts: check the PHP/Laravel constraints of
laravel/aiseparately from the core package.
LARAVEL_OCR_STORAGE_DISK=local
LARAVEL_OCR_ENCRYPT_DATA=false
LARAVEL_OCR_SCAN_MALWARE=falseThe LaravelOcr facade provides a simple entry point via the OCRManager.
use Mayaram\LaravelOcr\Facades\LaravelOcr;
// Extract from a local file path
$result = LaravelOcr::extract('/path/to/document.png');
echo $result['text'];
// "INVOICE #1001..."
// Extract from an UploadedFile
$result = LaravelOcr::extract(request()->file('document'));
// Extract a table
$tableResult = LaravelOcr::extractTable('/path/to/invoice.png');
foreach ($tableResult['table'] as $row) {
echo implode(' | ', $row) . "\n";
}If you only need OCR text and not structured parsing, this is the simplest integration point.
For powerful data extraction, use the DocumentParser. It returns a typed OcrResult DTO with text, confidence, bounds, and metadata properties.
use Mayaram\LaravelOcr\DTOs\OcrResult;
/** @var \Mayaram\LaravelOcr\Services\DocumentParser $parser */
$parser = app('laravel-ocr.parser');
$result = $parser->parse('storage/invoices/inv-2024.pdf', [
'document_type' => 'invoice',
'use_ai_cleanup' => true,
'save_to_database' => true,
]);
// Access the OcrResult DTO properties
echo $result->text; // Full extracted text
echo $result->confidence; // e.g., 0.98
echo $result->metadata['processing_time']; // e.g., 1.2
echo $result->metadata['document_type']; // "invoice"
echo $result->metadata['ai_cleanup_used']; // true
// Access structured fields
$fields = $result->metadata['fields'];
$invoiceNumber = $fields['invoice_number']['value'];
$totalAmount = $fields['totals']['total']['amount'];Use DocumentParser when you want document classification, template support, field extraction, metadata, or optional cleanup in one pipeline.
Process multiple documents at once:
$parser = app('laravel-ocr.parser');
$results = $parser->parseBatch([
'storage/invoices/inv-001.pdf',
'storage/invoices/inv-002.pdf',
'storage/invoices/inv-003.pdf',
], ['document_type' => 'invoice']);
foreach ($results as $result) {
echo $result->text . "\n---\n";
}The package includes an Advanced Invoice Extractor capable of parsing complex invoice tables into structured arrays.
$result = $parser->parse($invoicePath, [
'document_type' => 'invoice',
]);
// Line items are extracted automatically for invoices
$lineItems = $result->metadata['fields']['line_items'] ?? [];
foreach ($lineItems as $item) {
echo "{$item['description']}: {$item['quantity']} x \${$item['unit_price']} = \${$item['total']}\n";
}
// Output:
// Web Hosting: 12 x $10.00 = $120.00
// Domain Name: 1 x $15.00 = $15.00
// Invoice totals
$totals = $result->metadata['fields']['totals'] ?? [];
echo "Subtotal: " . ($totals['subtotal']['formatted'] ?? 'N/A');
echo "Tax: " . ($totals['tax']['formatted'] ?? 'N/A');
echo "Total: " . ($totals['total']['formatted'] ?? 'N/A');Define reusable templates to target specific fields using regex patterns. Templates are stored in the database and support import/export.
use Mayaram\LaravelOcr\Facades\LaravelOcr;
// 1. Create a Template
$templateManager = app('laravel-ocr.templates');
$template = $templateManager->create([
'name' => 'TechCorp Invoice',
'type' => 'invoice',
'description' => 'Template for TechCorp invoices',
'fields' => [
[
'key' => 'order_id',
'label' => 'Order ID',
'pattern' => '/Order\s*ID:\s*([A-F0-9]+)/i',
'type' => 'string',
'validators' => ['required' => true],
],
[
'key' => 'total_amount',
'label' => 'Total Amount',
'pattern' => '/Total:\s*\$?([\d,]+\.?\d*)/i',
'type' => 'currency',
],
],
]);
// 2. Apply a template during extraction
$result = LaravelOcr::extractWithTemplate($file, $template->id);
// 3. Auto-detect template based on content
$parser = app('laravel-ocr.parser');
$result = $parser->parse($file, ['auto_detect_template' => true]);
// 4. Export / Import templates
$json = $templateManager->exportTemplate($template->id);
$imported = $templateManager->importTemplate('/path/to/template.json');
// 5. Duplicate a template
$clone = $template->duplicate('TechCorp Invoice v2');Configure processing pipelines in config/laravel-ocr.php to standardize how different document types are handled.
// config/laravel-ocr.php
'workflows' => [
'invoice' => [
'options' => [
'use_ai_cleanup' => true,
'auto_detect_template' => true,
'extract_tables' => true,
],
'post_processors' => [
['class' => 'App\OCR\Processors\InvoiceProcessor'],
],
'validators' => [
['type' => 'required_fields', 'fields' => ['invoice_number', 'total']],
],
],
'receipt' => [
'options' => [
'use_ai_cleanup' => true,
'extract_line_items' => true,
],
'post_processors' => [
['class' => 'App\OCR\Processors\ReceiptProcessor'],
],
],
],// Usage
$parser = app('laravel-ocr.parser');
$result = $parser->parseWithWorkflow($file, 'invoice');use Mayaram\LaravelOcr\Facades\LaravelOcr;
// Use Tesseract (default)
$result = LaravelOcr::driver('tesseract')->extract($document);
// Switch to AWS Textract for this request
$result = LaravelOcr::driver('aws_textract')->extract($document);
// Switch to Google Vision
$result = LaravelOcr::driver('google_vision')->extract($document);$parser = app('laravel-ocr.parser');
$metadata = $parser->extractMetadata('/path/to/document.pdf');
// Returns:
// [
// 'file_name' => 'document.pdf',
// 'file_size' => 102400,
// 'mime_type' => 'application/pdf',
// 'created_at' => '2024-01-15 10:30:00',
// 'modified_at' => '2024-01-15 10:30:00',
// 'pdf_pages' => 3,
// 'pdf_author' => 'John Doe',
// 'pdf_title' => 'Q4 Invoice',
// 'pdf_creator' => 'Microsoft Word',
// ]use Mayaram\LaravelOcr\Models\ProcessedDocument;
// Query processed documents
$documents = ProcessedDocument::where('document_type', 'invoice')
->where('confidence_score', '>=', 0.7)
->latest()
->get();
foreach ($documents as $doc) {
// Get a specific field value
$invoiceNo = $doc->getFieldValue('invoice_number');
// Get all field values as a flat array
$allFields = $doc->getAllFieldValues();
// Check if the result is valid (status=completed & confidence >= 0.7)
if ($doc->isValid()) {
// Process the document
}
}# Basic
php artisan laravel-ocr:create-template "My Invoice" invoice
# Interactive (prompts for fields, patterns, validators)
php artisan laravel-ocr:create-template "My Invoice" invoice --interactive# Basic processing
php artisan laravel-ocr:process /path/to/document.pdf
# With options
php artisan laravel-ocr:process /path/to/invoice.pdf \
--type=invoice \
--template=1 \
--ai-cleanup \
--save \
--output=jsonFlags:
| Flag | Description |
|---|---|
--template=ID |
Use a specific template |
--type=TYPE |
Set document type (invoice, receipt, etc.) |
--ai-cleanup |
Enable AI-powered cleanup |
--save |
Save results to database |
--output=FORMAT |
Output format: json or table (default) |
php artisan laravel-ocr:doctorRun this first on a new machine, server, or CI environment.
Preview extracted documents and data directly in your UI with the built-in Alpine.js component.
<x-laravel-ocr::document-preview
:document="$processedDocument"
:show-overlay="true"
:show-actions="true"
:show-image="true"
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
document |
array |
required | Document data with url, documentId, and fields |
show-overlay |
bool |
false |
Show bounding box overlays on the document |
show-actions |
bool |
true |
Show Save/Export/Reprocess action buttons |
show-image |
bool |
true |
Use <img> tag (true) or <iframe> (false) |
Expected Document Structure:
$processedDocument = [
'url' => asset('storage/documents/invoice.pdf'),
'documentId' => $document->id,
'fields' => [
[
'key' => 'invoice_number',
'label' => 'Invoice Number',
'value' => 'INV-2024-001',
'confidence' => 0.95,
'bounds' => ['x' => 100, 'y' => 50, 'width' => 200, 'height' => 30],
],
// ...more fields
],
];Features:
- Click fields to highlight them on the document
- Inline editing of extracted values
- Confidence badges: 🟢 High (≥80%), 🟡 Medium (≥60%), 🔴 Low (<60%)
- Export extracted data as JSON
- Reprocess documents with one click
The package creates three tables:
| Column | Type | Description |
|---|---|---|
id |
bigint | Primary key |
name |
string | Template name |
description |
text | Optional description |
type |
string(50) | Document type (invoice, receipt, etc.) |
layout |
json | Layout configuration |
is_active |
boolean | Whether template is active |
version |
string(10) | Template version (default: 1.0) |
| Column | Type | Description |
|---|---|---|
id |
bigint | Primary key |
template_id |
foreignId | References ocr_templates |
key |
string(50) | Field identifier (snake_case) |
label |
string | Human-readable label |
type |
string(30) | Field type: string, numeric, date, currency, email, phone |
pattern |
text | Regex pattern for extraction |
position |
json | Positional extraction config (line, start, end) |
validators |
json | Validation rules (required, regex, length, type) |
default_value |
string | Fallback value |
order |
integer | Display order |
| Column | Type | Description |
|---|---|---|
id |
bigint | Primary key |
original_filename |
string | Original file name |
document_type |
string(50) | Detected or specified type |
extracted_data |
json | Full extraction result |
template_id |
foreignId | Template used (nullable) |
confidence_score |
decimal(3,2) | Overall confidence |
processing_time |
decimal(8,3) | Time in seconds |
user_id |
bigint | User who processed (nullable) |
status |
string(20) | completed, failed, etc. |
error_message |
text | Error details if failed |
All drivers implement the Mayaram\LaravelOcr\Contracts\OCRDriver interface:
interface OCRDriver
{
public function extract($document, array $options = []): array;
public function extractTable($document, array $options = []): array;
public function extractBarcode($document, array $options = []): array;
public function extractQRCode($document, array $options = []): array;
public function getSupportedLanguages(): array;
public function getSupportedFormats(): array;
}| Format | Tesseract | Google Vision | AWS Textract | Azure |
|---|---|---|---|---|
| JPG/JPEG | ✅ | ✅ | ✅ | ✅ |
| PNG | ✅ | ✅ | ✅ | ✅ |
| ✅ | ✅ | ✅ | ✅ | |
| TIFF | ✅ | ✅ | ❌ | ✅ |
| BMP | ✅ | ✅ | ❌ | ✅ |
| GIF | ❌ | ✅ | ❌ | ❌ |
| WebP | ❌ | ✅ | ❌ | ❌ |
use Mayaram\LaravelOcr\Contracts\OCRDriver;
class MyCustomDriver implements OCRDriver
{
public function extract($document, array $options = []): array
{
// Your implementation
return [
'text' => 'extracted text',
'confidence' => 0.95,
'bounds' => [],
'metadata' => ['engine' => 'custom'],
];
}
public function extractTable($document, array $options = []): array { /* ... */ }
public function extractBarcode($document, array $options = []): array { /* ... */ }
public function extractQRCode($document, array $options = []): array { /* ... */ }
public function getSupportedLanguages(): array { return ['en' => 'English']; }
public function getSupportedFormats(): array { return ['jpg', 'png', 'pdf']; }
}The AICleanupService provides two modes:
Set provider to basic to use built-in typo correction and field normalization without any AI provider:
$result = $parser->parse($document, [
'use_ai_cleanup' => true,
'provider' => 'basic',
]);Built-in corrections:
- Common OCR typos:
invOice→invoice,arnount→amount,nurnber→number,custorner→customer,payrnent→payment - OCR pattern fixes:
rn→m,Obefore numbers →0,lbefore numbers →1 - Field normalization by type: numeric, date (→
Y-m-d), currency, email, phone
Uses the CleanupAgent with your configured LLM provider for intelligent correction:
$result = $parser->parse($document, [
'use_ai_cleanup' => true,
// Uses LARAVEL_OCR_AI_PROVIDER from config
]);The agent is instructed to:
- Fix typos and OCR errors contextually
- Standardize formats (dates to
YYYY-MM-DD, currency to decimal) - Preserve values it can't confidently fix
- Return valid JSON matching the input structure
You can pass additional instructions to the AI cleanup agent to customize its behavior:
// Per-call custom prompt
$result = $parser->parse($document, [
'use_ai_cleanup' => true,
'custom_prompt' => 'Extract all amounts in INR. Normalize phone numbers to +91 format.',
]);Or set a default custom prompt in config/laravel-ocr.php:
'ai_cleanup' => [
'custom_prompt' => 'Always extract Hindi text. Format dates as DD/MM/YYYY.',
],The custom prompt is appended as "Additional Instructions" to the default cleanup prompt, so the base OCR correction behavior is always preserved.
The package uses Pest for testing with three test suites:
# Run all tests
composer test
# Or directly with Pest
./vendor/bin/pest
# Run specific suite
./vendor/bin/pest --testsuite=Unit
./vendor/bin/pest --testsuite=Feature
./vendor/bin/pest --testsuite=IntegrationTests use SQLite in-memory database automatically via phpunit.xml configuration.
Current coverage includes:
- parsing and template application
- console commands
- AI cleanup flow with mocked AI responses
- end-to-end package flow through integration tests
| Method | Returns | Description |
|---|---|---|
extract($document, $options) |
array |
Extract text from document |
extractWithTemplate($document, $templateId, $options) |
array |
Extract and apply template |
extractTable($document, $options) |
array |
Extract table data |
extractBarcode($document, $options) |
array |
Extract barcode |
extractQRCode($document, $options) |
array |
Extract QR code |
driver($name) |
OCRDriver |
Switch to a specific driver |
| Method | Returns | Description |
|---|---|---|
parse($document, $options) |
OcrResult |
Full parsing pipeline |
parseBatch($documents, $options) |
OcrResult[] |
Process multiple documents |
parseWithWorkflow($document, $workflow) |
OcrResult |
Parse using a named workflow |
extractMetadata($document) |
array |
Extract file & PDF metadata |
| Property | Type | Description |
|---|---|---|
text |
string |
Full extracted text |
confidence |
float |
Overall confidence score (0–1) |
bounds |
array |
Bounding box / layout data |
metadata |
array |
Processing time, document type, fields, template used, etc. |
| Method | Returns | Description |
|---|---|---|
create($data) |
DocumentTemplate |
Create a template with fields |
applyTemplate($extractedData, $templateId) |
array |
Apply template to extracted data |
findTemplateByContent($text) |
?DocumentTemplate |
Auto-detect matching template |
importTemplate($filePath) |
DocumentTemplate |
Import from JSON file |
exportTemplate($templateId) |
string |
Export as JSON string |
The MIT License (MIT). Please see License File for more information.
