hosmelq / laravel-anydoc
Laravel integration for converting documents to GitHub-Flavored Markdown with Firecrawl anydoc.
Requires
- php: ^8.4
- ext-anydoc: ^0.1.3
- 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
README
Convert Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, CSV, and PDF files to GitHub-Flavored Markdown in Laravel applications. You may also access anydoc's structured document model.
Requirements
- PHP 8.4 or later
- Laravel 12 or 13
- ext-anydoc ^0.1.3
Installation
Install the native extension with PIE:
pie install hosmelq/ext-anydoc
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();
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. All conversions run synchronously.
Read structured documents
Call document() to access anydoc's readonly document model:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc; $document = Anydoc::file('presentation.pptx')->document(); $blocks = $document->blocks; $notes = $document->notes; $assets = $document->assets;
The document model includes blocks, inline content, lists, tables, notes, and embedded assets.
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, ResourceLimitException, and UnsupportedException. 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.
Running tests
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.