Looking to hire Laravel developers? Try LaraJobs

laravel-sdk maintained by lesspdf

Description
Laravel SDK for the lesspdf-engine HTML/Typst to PDF API
Author
Last update
2026/05/27 00:25 (dev-master)
License
Downloads
3

Comments
comments powered by Disqus

LessPDF Laravel SDK

Laravel SDK for lesspdf-engine — a self-hosted Rust service that converts HTML (via Chrome) or Typst markup to PDF.

Installation

composer require lesspdf/laravel-sdk

Add the following to your .env:

LESSPDF_URL=http://localhost:8080
LESSPDF_API_KEY=your-api-key

Optionally publish the config:

php artisan vendor:publish --tag="lesspdf-config"

Quick start

use LessPdf\Facades\LessPdf;

// Render HTML → PDF bytes (stream or store as you like)
$pdf = LessPdf::renderHtml('<h1>Hello world</h1>');
return response($pdf, 200)->header('Content-Type', 'application/pdf');

// Store on server and get a URL back
$result = LessPdf::storeHtml('<h1>Invoice</h1>');
// $result->url, $result->id, $result->expiresAt

Render options

All options are fluent and optional. format defaults to A4 — override only when needed:

use LessPdf\DTOs\RenderOptions;
use LessPdf\Enums\PaperFormat;

$options = RenderOptions::make()
    // format defaults to A4; override if needed:
    ->format(PaperFormat::Letter)
    ->landscape()
    ->scale(0.9)                        // 0.1 – 2.0
    ->printBackground()
    ->pageRanges('1-3,5')
    ->margins('20mm', '20mm', '15mm', '15mm')   // top, bottom, left, right
    ->metadata(title: 'My PDF', author: 'Acme', keywords: ['pdf'])
    ->waitForNetworkIdle()             // or ->waitForDomContentLoaded(settle_ms: 150)
    ->timeoutMs(15000);

$pdf = LessPdf::renderHtml($html, $options);
Method Default Notes
format(PaperFormat) A4 A4, A3, A5, Letter, Legal, Tabloid
landscape() false
scale(float) 1.0 0.1 – 2.0
printBackground() false
pageRanges(string) e.g. "1-3,5"
margins(top, bottom, left, right) units: mm, cm, in, px, pt
metadata(title, author, subject, keywords) PDF metadata
waitForNetworkIdle() Chrome wait strategy
waitForDomContentLoaded(settle_ms) Optionally add settle delay
waitForLoad()
timeoutMs(int) server default Per-request timeout override

All render methods

// HTML → inline PDF bytes
$pdf = LessPdf::renderHtml(string $html, ?RenderOptions $options): string

// HTML → store on server
$result = LessPdf::storeHtml(string $html, ?RenderOptions $options): StoredResult

// URL → inline PDF bytes (Chrome only; private IPs are SSRF-blocked)
$pdf = LessPdf::renderUrl(string $url, ?RenderOptions $options): string

// Typst markup → inline PDF bytes
$pdf = LessPdf::renderTypst(string $source, array $inputs = [], ?RenderOptions $options): string

// Full control
$pdf = LessPdf::render(Renderer $renderer, array|string $source, Delivery $delivery, ?RenderOptions $options): string|StoredResult

Batch rendering & polling

use LessPdf\Facades\LessPdf;

$batch = LessPdf::batch([
    ['renderer' => 'chrome', 'source' => ['html' => '<h1>Doc A</h1>'], 'delivery' => 'store'],
    ['renderer' => 'chrome', 'source' => ['html' => '<h1>Doc B</h1>'], 'delivery' => 'store'],
], webhook: [
    'url'    => route('webhooks.lesspdf'),
    'secret' => config('lesspdf.webhook_secret'),
    'events' => ['batch.completed'],
]);

// $batch->id, $batch->status, $batch->total

// Poll until done:
do {
    sleep(2);
    $batch = LessPdf::getBatchStatus($batch->id);
} while ($batch->status !== 'completed' && $batch->status !== 'failed');

Webhook setup

routes/web.php

Route::post('/webhooks/lesspdf', [LessPdfWebhookController::class, 'handle'])
    ->name('webhooks.lesspdf');

app/Http/Controllers/LessPdfWebhookController.php

use Illuminate\Http\Request;
use LessPdf\Webhooks\LessPdfWebhook;
use LessPdf\Exceptions\SignatureException;

public function handle(Request $request): \Illuminate\Http\Response
{
    try {
        $event = LessPdfWebhook::fromRequest($request);
    } catch (SignatureException $e) {
        return response('Forbidden', 403);
    }

    if ($event->is('batch.completed')) {
        $data = $event->data; // ['batch_id', 'total', 'completed', 'failed']
        // dispatch your job...
    }

    return response('', 200);
}

Set LESSPDF_WEBHOOK_SECRET to the same secret you pass when submitting a batch.

Exception handling

use LessPdf\Exceptions\AuthenticationException;
use LessPdf\Exceptions\BrowserUnavailableException;
use LessPdf\Exceptions\LessPdfException;
use LessPdf\Exceptions\RateLimitException;
use LessPdf\Exceptions\RenderException;

try {
    $pdf = LessPdf::renderHtml($html);
} catch (AuthenticationException $e) {
    // 401 — bad API key
} catch (RateLimitException $e) {
    // 429 — retry after $e->retryAfter() seconds (SDK retries automatically by default)
} catch (BrowserUnavailableException $e) {
    // Chrome pool exhausted
} catch (RenderException $e) {
    // $e->errorCode() → 'timeout' | 'render_failed' | 'ssrf_blocked' | 'invalid_source'
} catch (LessPdfException $e) {
    // Base exception for all other errors
}
Status / error.code Exception
401 AuthenticationException
429 RateLimitExceptionretryAfter(): int
browser_unavailable BrowserUnavailableException
ssrf_blocked / timeout / render_failed / invalid_source RenderExceptionerrorCode(): string
404 NotFoundException
Other non-2xx LessPdfException

Configuration

// config/lesspdf.php
return [
    'url'                       => env('LESSPDF_URL', 'http://localhost:8080'),
    'api_key'                   => env('LESSPDF_API_KEY'),
    'timeout'                   => env('LESSPDF_TIMEOUT', 60),          // HTTP timeout (seconds)
    'retry_attempts'            => env('LESSPDF_RETRY_ATTEMPTS', 2),    // auto-retry on 429
    'retry_delay_ms'            => env('LESSPDF_RETRY_DELAY_MS', 5000), // fallback delay
    'webhook_secret'            => env('LESSPDF_WEBHOOK_SECRET'),
    'webhook_tolerance_seconds' => env('LESSPDF_WEBHOOK_TOLERANCE', 300),
];

Testing

composer test

License

The MIT License (MIT). Please see License File for more information.