Looking to hire Laravel developers? Try LaraJobs

laravel-anydoc maintained by hosmelq

Description
Laravel integration for converting documents to GitHub-Flavored Markdown with Anydoc.
Last update
2026/08/11 00:16 (dev-main)
License
Links
Downloads
0

Comments
comments powered by Disqus

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 .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:

  • EncryptedException
  • IoException
  • MalformedException
  • MissingPartException
  • ResourceLimitException
  • 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.

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.