hosmelq / laravel-anydoc
Laravel integration for converting documents to GitHub-Flavored Markdown with Firecrawl anydoc.
Requires
- php: ^8.4
- ext-anydoc: ^0.2.4
- illuminate/filesystem: ^12.0 || ^13.0
- illuminate/http: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
- spatie/laravel-package-tools: ^1.93
- thecodingmachine/safe: ^3.4
Requires (Dev)
- ergebnis/composer-normalize: ^2.52
- larastan/larastan: ^3.10
- laravel/pint: ^1.30
- orchestra/pest-plugin-testbench: ^4.1
- orchestra/testbench: ^10.0 || ^11.1
- pestphp/pest: ^4.7
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- rector/rector: ^2.6
- shipmonk/composer-dependency-analyser: ^1.8
- spaze/phpstan-disallowed-calls: ^4.14
- thecodingmachine/phpstan-safe-rule: ^1.4
- ticketswap/phpstan-error-formatter: ^1.3
- tomasvotruba/type-coverage: ^2.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Convert documents to GitHub-Flavored Markdown in Laravel applications, with access to anydoc's structured document model when available.
Requirements
- ext-anydoc ^0.2.4
- Laravel 12+
- PHP 8.4+
Installation
Install the native extension with PIE:
pie install hosmelq/ext-anydoc:^0.2.4
Install Laravel anydoc with Composer:
composer require hosmelq/laravel-anydoc
Convert documents
Convert a local file to Markdown with the Anydoc facade:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $markdown = Anydoc::file('report.docx')->markdown();
file() also accepts an uploaded file:
$markdown = Anydoc::file($uploadedFile)->markdown();
Convert document bytes directly:
$bytes = file_get_contents('report.docx'); $markdown = Anydoc::bytes($bytes)->markdown();
CSV has no content signature, so CSV bytes require an explicit format:
use HosmelQ\Anydoc\Laravel\Enums\Format; use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $bytes = file_get_contents('data.csv'); $markdown = Anydoc::bytes($bytes, Format::Csv)->markdown();
All conversions run synchronously.
Convert files from disks
Convert a file stored on a Laravel filesystem disk:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $markdown = Anydoc::disk('s3') ->file('documents/report.docx') ->markdown();
Call Anydoc::disk() without a name to use Laravel's default disk.
Disk files and uploaded files are read into memory before conversion.
Read structured documents
Call document() to access anydoc's readonly document model:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $document = Anydoc::file('presentation.pptx')->document(); $assets = $document->assets; $blocks = $document->blocks; $notes = $document->notes;
The document model includes blocks, checkboxes, embedded assets, inline content, lists, math, notes, and tables.
PDF supports Markdown conversion only. Calling document() for a PDF throws an
Anydoc\Exception\UnsupportedException.
Supported formats
| Format | Extensions |
|---|---|
| CSV | .csv |
| EPUB | .epub |
| Excel | .xls, .xlsb, .xlsm, .xlsx |
| OpenDocument | .odp, .ods, .odt |
.pdf |
|
| PowerPoint | .pot, .pps, .ppsm, .ppsx, .ppt, .pptm, .pptx |
| Rich Text Format | .rtf |
| Word | .doc, .docm, .docx |
Pass a Format enum case when the format cannot be detected from the content:
use HosmelQ\Anydoc\Laravel\Enums\Format; use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $markdown = Anydoc::bytes($bytes, Format::Docx)->markdown();
Detect formats
Detect a format from bytes, an extension, or a path:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $fromBytes = Anydoc::formatFromBytes($bytes); $fromExtension = Anydoc::formatFromExtension('.DOCX'); $fromPath = Anydoc::formatFromPath('documents/report.docx');
Each method returns a Format enum case or null. Extension detection is
case-insensitive and accepts an optional leading dot.
Uploaded files use their contents first and their original name as a fallback. Filesystem files use their contents first and their path as a fallback.
Use dependency injection
Inject the Anydoc contract when a class should not depend on the facade:
use HosmelQ\Anydoc\Laravel\Contracts\Anydoc; final class ConvertDocument { public function __construct(private Anydoc $anydoc) {} public function handle(string $path): string { return $this->anydoc->file($path)->markdown(); } }
Handle errors
Native conversion errors extend Anydoc\Exception\ConvertException:
use Anydoc\Exception\ConvertException; use Anydoc\Exception\PanicException; use HosmelQ\Anydoc\Laravel\Facades\Anydoc; try { $markdown = Anydoc::file('report.docx')->markdown(); } catch (ConvertException $exception) { report($exception); } catch (PanicException $exception) { report($exception); }
Conversion exceptions include EncryptedException, IoException,
MalformedException, MissingPartException, NeedsOcrException,
ResourceLimitException, and UnsupportedException. NeedsOcrException
provides the 1-indexed PDF pages requiring OCR through $pages and the total
number of pages through $pageCount. Filesystem sources may also throw Laravel
filesystem exceptions when their contents cannot be read.
PanicException represents a panic from the native Rust library and does not
extend ConvertException.
Test conversions
Call Anydoc::fake() to test application behavior without reading files or
running conversions:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc; use HosmelQ\Anydoc\Laravel\RecordedConversion; Anydoc::fake([ 'markdown' => '# Converted document', ]); Anydoc::file('report.docx')->markdown(); Anydoc::assertConvertedToMarkdown( fn (RecordedConversion $conversion): bool => $conversion->input === 'report.docx', );
Use a closure when the response depends on the recorded conversion:
Anydoc::fake([ 'markdown' => fn (RecordedConversion $conversion): string => "# {$conversion->input}", ]); $markdown = Anydoc::file('report.docx')->markdown();
The fake provides assertions and access to recorded conversions:
Anydoc::assertConverted(); Anydoc::assertConvertedTimes(2); Anydoc::assertConvertedToDocument(); Anydoc::assertConvertedToMarkdown(); Anydoc::assertNotConverted($callback); Anydoc::assertNothingConverted(); $conversions = Anydoc::conversions();
Assertion and filtering callbacks receive a RecordedConversion containing
its disk, format, input, output type, and source type. A conversion is recorded
when markdown() or document() is called.
The default fake Markdown response is an empty string. Document conversions
require a configured Anydoc\Document response.
Development
Run the test suite with:
composer test
Changelog
See CHANGELOG.md for a list of changes.
Contributing
Pull requests are welcome. Please run the test suite before submitting changes.
License
The MIT License (MIT). Please see License File for more information.