laravel-sdk maintained by lesspdf
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 |
RateLimitException — retryAfter(): int |
browser_unavailable |
BrowserUnavailableException |
ssrf_blocked / timeout / render_failed / invalid_source |
RenderException — errorCode(): 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.