Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tesseract Laravel Package

itul/tesseract

A Laravel package that integrates Tesseract OCR to extract text from images through a simple, configurable API. Supports multiple languages, image preprocessing, and straightforward setup for PHP and Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the system dependency first. This package is only a Laravel wrapper around the Tesseract OCR binary. On Ubuntu/Debian:

    sudo apt-get install tesseract-ocr
    # optional language packs
    sudo apt-get install tesseract-ocr-deu tesseract-ocr-fra
    

    On macOS: brew install tesseract. On Windows, install the UB Mannheim build and note the full tesseract.exe path.

  2. Install the package.

    composer require itul/tesseract
    

    Because the repository is on Bitbucket and has no Packagist/star signal, verify the version constraint and repository access before adding it to a shared project. If needed, add the VCS repository explicitly in composer.json.

  3. Publish and inspect configuration.

    php artisan vendor:publish --provider="Itul\\Tesseract\\TesseractServiceProvider"
    

    Look first at:

    • the package README and src/ directory for the actual facade/service API,
    • config/tesseract.php for binary path, language, and timeout options,
    • the service provider for bindings and facade aliases.
  4. Confirm the binary is reachable from PHP. From php artisan tinker or a route:

    exec('which tesseract', $out, $code);
    dump($out, $code);
    

    If this fails under PHP-FPM but works in your terminal, the web user’s PATH is the problem.

  5. First use case: extract text from a scanned invoice or receipt.

    use Itul\Tesseract\Facades\Tesseract;
    
    $text = Tesseract::recognize(storage_path('app/invoices/scan.png'));
    

    The exact method name depends on the package’s exposed API; check the facade and service class before wiring it into application code.

Implementation Patterns

Wrap OCR behind your own service. Do not call the package facade from controllers, jobs, and models directly. Create an application-level OcrService so you can swap the wrapper, add preprocessing, and mock OCR in tests.

namespace App\Services;

use Itul\Tesseract\Facades\Tesseract;

class OcrService
{
    public function extract(string $absolutePath, string $language = 'eng'): string
    {
        return Tesseract::recognize($absolutePath, $language);
    }
}

Preprocess images before OCR. Tesseract accuracy depends heavily on image quality. A common pattern is to use Intervention Image or Imagick first:

use Intervention\Image\ImageManager;

$image = ImageManager::imagick()->read($path)
    ->greyscale()
    ->resize(2000, null, function ($constraint) {
        $constraint->aspectRatio();
        $constraint->upsize();
    })
    ->contrast(15)
    ->sharpen(5);

$tempPath = storage_path('app/ocr/'.uniqid().'.png');
$image->save($tempPath);

$text = app(OcrService::class)->extract($tempPath);
unlink($tempPath);

Practical preprocessing tips:

  • Convert to greyscale.
  • Upscale small images to roughly 300 DPI equivalent.
  • Increase contrast for faded scans.
  • Binarize (threshold) only when the background is uneven.
  • Deskew rotated scans if the package or your image library supports it.

Run OCR in queued jobs. OCR is CPU-bound and slow. Treat it like a background operation:

class ExtractInvoiceText implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 120;
    public int $tries = 3;

    public function __construct(public int $invoiceId) {}

    public function handle(OcrService $ocr): void
    {
        $invoice = Invoice::findOrFail($this->invoiceId);

        $text = $ocr->extract(
            Storage::disk('local')->path($invoice->file_path),
            'eng'
        );

        $invoice->update([
            'ocr_text' => $text,
            'ocr_status' => 'completed',
        ]);
    }
}

Set $timeout higher than the package or binary timeout. If a worker is killed mid-OCR, you will get stuck jobs and temp files unless you clean up.

Store both raw and normalized text. Keep the raw Tesseract output for debugging and a cleaned version for search:

$raw = $ocr->extract($path);

$normalized = preg_replace('/[ \t]+/', ' ', $raw);
$normalized = preg_replace("/\r\n|\r/", "\n", $normalized);
$normalized = trim($normalized);

Then index the normalized text in a TEXT/LONGTEXT column or a search engine. Avoid running OCR repeatedly for the same document; store a checksum of the source file and skip unchanged files.

Use language and page-segmentation options deliberately. Most wrappers expose language and sometimes config variables. For mixed-language documents:

$text = Tesseract::recognize($path, 'eng+deu');

For structured forms or single columns, Tesseract’s page segmentation mode matters. If the package allows raw options, pass a PSM such as --psm 6 for a uniform block of text. If it does not, use the binary directly or wrap it yourself for those cases.

Batch processing pattern. For many files, queue one job per document and throttle concurrency. Running multiple Tesseract processes in parallel can exhaust memory and CPU:

Invoice::where('ocr_status', 'pending')
    ->chunkById(100, function ($invoices) {
        foreach ($invoices as $invoice) {
            ExtractInvoiceText::dispatch($invoice->id)
                ->onQueue('ocr');
        }
    });

Run a dedicated ocr queue worker with limited concurrency:

php artisan queue:work --queue=ocr --tries=3 --timeout=180

Testing pattern. Do not depend on the real OCR binary in fast tests. Bind a fake implementation of your own OcrService:

$this->mock(OcrService::class, function ($mock) {
    $mock->shouldReceive('extract')
        ->once()
        ->andReturn("Invoice #123\nTotal: 42.00");
});

Keep a small fixture image and a separate integration test that runs the real binary and asserts a few expected tokens, not the full exact string. OCR output can vary across Tesseract versions.

Gotchas and Tips

  • The package is not the OCR engine. Almost every failure is a missing binary, missing language data, wrong path, or poor image quality. Debug the tesseract command in the shell before debugging Laravel.

  • Binary path differences between CLI and PHP-FPM. Use an absolute path in configuration:

    'binary' => env('TESSERACT_BINARY', '/usr/bin/tesseract'),
    

    On Windows, quote paths with spaces and be careful with backslashes.

  • Language data may not be installed. tesseract --list-langs shows what is available. If deu is requested but not installed, Tesseract may fail or fall back unexpectedly.

  • Permissions and temp directories. The PHP user must be able to read uploaded files and write temporary images/output files. Common web-server setups break OCR because /tmp is not writable or open_basedir blocks the binary.

  • OCR is slow and memory-hungry. Large PDFs and high-resolution scans can take tens of seconds. Always use queues and timeouts. Avoid OCR during HTTP requests unless the image is tiny and the user expects a wait.

  • PDF handling is usually a trap. Tesseract works best on images. Multi-page PDFs often need to be rasterized first with Imagick/Ghostscript, then OCR’d page by page. If the package claims PDF support, inspect how it implements it and test a multi-page file.

  • Accuracy is not deterministic across environments. Different Tesseract versions, language packs, and image libraries produce different output. Pin the system dependency version in Docker and avoid brittle full-string assertions in tests.

  • Clean up temporary files. Wrappers often create temp images or output files. If a job fails or times out, these can accumulate. Use try/finally around preprocessing and OCR when you control the temp paths.

  • Escape shell arguments if you extend the wrapper. If you add your own command execution, never interpolate user-supplied filenames into a shell string. Use escapeshellarg() or Symfony Process with an argument array.

  • Configuration caching can surprise you. After changing config/tesseract.php or environment variables, run:

    php artisan config:clear
    

    In production, config:cache means env changes require a re-cache.

  • Check the package’s maintenance signal. With zero dependents and a Bitbucket-only repository, treat this as a thin internal dependency. Wrap it behind your own service, keep the integration covered by tests, and be prepared to call the Tesseract binary directly via Symfony Process if the wrapper becomes unmaintained.

  • Useful direct-binary fallback. If the package lacks an option you need, a small Symfony Process wrapper is often simpler:

    use Symfony\Component\Process\Process;
    
    $process = new Process([
        config('tesseract.binary', '/usr/bin/tesseract'),
        $imagePath,
        'stdout',
        '-l', 'eng',
        '--psm', '6',
    ]);
    
    $process->setTimeout(120);
    $process->run();
    
    if (! $process->isSuccessful()) {
        throw new \RuntimeException($process->getErrorOutput());
    }
    
    $text = $process->getOutput();
    

    This is also a good escape hatch for diagnosing whether a failure is in the package or in Tesseract itself.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings