laravel-anydoc maintained by hosmelq
Laravel Anydoc
Laravel Anydoc provides a fluent Laravel API for converting Word, PowerPoint, Excel, OpenDocument, RTF, EPUB, CSV, and PDF files to GitHub-Flavored Markdown. It also provides access to Anydoc's structured document model.
Requirements
- PHP 8.4+
- Laravel 12.x or 13.x
- ext-anydoc 0.1.x
Installation
Install the native extension with PIE:
pie install hosmelq/ext-anydoc
Then install the Laravel package via Composer:
composer require hosmelq/laravel-anydoc
Laravel discovers the package automatically. There are no configuration files, migrations, or other assets to publish.
Basic Usage
Convert a local file to Markdown:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc;
$markdown = Anydoc::file('report.docx')->markdown();
Uploaded files are also supported:
$markdown = Anydoc::file($uploadedFile)->markdown();
Convert directly from bytes:
$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();
Laravel Filesystem Disks
Use disk to convert a file stored on a Laravel filesystem disk:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc;
$markdown = Anydoc::disk('s3')
->file('documents/report.docx')
->markdown();
Omit the disk name to use Laravel's default filesystem disk:
$markdown = Anydoc::disk()
->file('documents/report.docx')
->markdown();
Disk and uploaded-file inputs are read into memory before conversion. All conversions are synchronous.
Structured Documents
Call document instead of markdown 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 |
Use the Format enum when you need to specify a format explicitly:
use HosmelQ\Anydoc\Laravel\Enums\Format;
use HosmelQ\Anydoc\Laravel\Facades\Anydoc;
$markdown = Anydoc::bytes($bytes, Format::Docx)->markdown();
The enum exposes these cases:
Format::Csv;
Format::Doc;
Format::Docx;
Format::Epub;
Format::Odp;
Format::Ods;
Format::Odt;
Format::Pdf;
Format::Ppt;
Format::Pptx;
Format::Rtf;
Format::Xlsx;
Macro-enabled and legacy extensions are normalized to their corresponding
formats. For example, .docm resolves to Format::Docx, .pptm resolves to
Format::Pptx, and .xls resolves to Format::Xlsx.
Format Detection
Formats may be detected from bytes, extensions, or paths:
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 when the format is not
recognized. Extension detection is case-insensitive and accepts an optional
leading dot.
Uploaded files are detected from their contents, with the original filename used as a fallback. Filesystem objects are detected from their contents, with their path used as a fallback.
Dependency Injection
The Anydoc contract may be injected instead of using 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();
}
}
The package registers the contract as a singleton backed by the Anydoc manager.
Error Handling
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);
}
Concrete conversion exceptions include:
EncryptedExceptionIoExceptionMalformedExceptionMissingPartExceptionResourceLimitExceptionUnsupportedException
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.
Testing
Use fake to test application code without reading files or running a conversion:
use HosmelQ\Anydoc\Laravel\Facades\Anydoc;
Anydoc::fake([
'markdown' => '# Converted document',
]);
$markdown = Anydoc::file('report.docx')->markdown();
Anydoc::assertConvertedToMarkdown(
fn ($conversion): bool => $conversion->input === 'report.docx',
);
Fake responses may also be closures:
Anydoc::fake([
'markdown' => fn ($conversion): string => "# {$conversion->input}",
]);
The fake provides the following assertions and inspection methods:
Anydoc::assertConverted();
Anydoc::assertConvertedTimes(2);
Anydoc::assertConvertedToDocument();
Anydoc::assertConvertedToMarkdown();
Anydoc::assertNotConverted(fn ($conversion): bool => false);
Anydoc::assertNothingConverted();
$conversions = Anydoc::conversions();
Assertion and filtering callbacks receive a recorded conversion containing its
disk, format, input, output type, and source type. A callback matches when it
returns true.
A conversion is recorded when markdown or document is called, not when the
pending conversion is created. The default fake Markdown response is an empty
string. Document conversions require a configured Anydoc\Document response.
Running the Test Suite
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.