pontedilana/php-weasyprint
PHP 8.3+ wrapper around WeasyPrint (v60+) to generate PDFs from URLs or HTML. Snappy-inspired, drop-in style API with strict typing. Set the weasyprint binary, pass CLI options, stream to browser or write files.
## Getting Started
### Minimal Setup
1. **Install the package**:
```bash
composer require pontedilana/php-weasyprint
Install WeasyPrint (version 60+):
sudo apt-get install weasyprint
brew install weasyprint
Verify binary path (e.g., /usr/local/bin/weasyprint or C:\Program Files\WeasyPrint\weasyprint.exe).
use Pontedilana\PhpWeasyPrint\Pdf;
$pdf = new Pdf('/usr/local/bin/weasyprint');
header('Content-Type: application/pdf');
echo $pdf->getOutput('<h1>Hello, PDF!</h1>');
$pdf = new Pdf('/usr/local/bin/weasyprint');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="report.pdf"');
echo $pdf->getOutput('https://example.com');
Enum/ directory: For predefined options (e.g., MediaType, PdfVersion).Pdf class: Core functionality (methods like getOutput(), generateFromHtml()).use Pontedilana\PhpWeasyPrint\Pdf;
public function generateInvoice(Request $request)
{
$html = view('invoices.pdf', ['invoice' => $request->invoice])->render();
$pdf = new Pdf('/usr/local/bin/weasyprint');
$pdf->setOption('media-type', 'print');
$pdf->setOption('stylesheet', [public_path('css/invoice.css')]);
return response($pdf->getOutput($html))
->header('Content-Type', 'application/pdf');
}
use Pontedilana\PhpWeasyPrint\Pdf;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class GeneratePdfJob implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
$pdf = new Pdf('/usr/local/bin/weasyprint');
$pdf->disableTimeout(); // Let Laravel Queue handle timeouts
$pdf->generateFromHtml('<h1>Queued PDF</h1>', storage_path('app/invoice.pdf'));
}
}
namespace App\Services;
use Pontedilana\PhpWeasyPrint\Pdf;
class PdfService
{
protected Pdf $pdf;
public function __construct()
{
$this->pdf = new Pdf(config('weasyprint.binary'));
}
public function generateFromView(string $view, array $data, string $filename)
{
$html = view($view, $data)->render();
$this->pdf->generateFromHtml($html, $filename);
}
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
$this->pdf->setOption($key, $value);
}
}
}
$pdf = new Pdf('/usr/local/bin/weasyprint');
$pdf->setOption('media-type', \Pontedilana\PhpWeasyPrint\Enum\MediaType::Print);
$pdf->setOption('pdf-version', \Pontedilana\PhpWeasyPrint\Enum\PdfVersion::Pdf17);
$pdf->setOption('timeout', 60); // Override default 10s timeout
$pdf->setOption('attachment', [
'https://example.com/logo.png',
storage_path('app/assets/header.jpg')
]);
try {
$pdf = new Pdf('/usr/local/bin/weasyprint');
$output = $pdf->getOutput('https://example.com');
} catch (\Pontedilana\PhpWeasyPrint\Exception\RuntimeException $e) {
Log::error('PDF generation failed: ' . $e->getMessage());
return response()->view('errors.pdf_failed');
}
Service Provider Binding:
// config/app.php
'providers' => [
// ...
App\Providers\PdfServiceProvider::class,
];
// app/Providers/PdfServiceProvider.php
public function register()
{
$this->app->singleton(PdfService::class, function ($app) {
return new PdfService(new Pdf(config('weasyprint.binary')));
});
}
Config File:
// config/weasyprint.php
return [
'binary' => env('WEASYPRINT_BINARY', '/usr/local/bin/weasyprint'),
'timeout' => env('WEASYPRINT_TIMEOUT', 10),
'allowed_schemes' => ['http', 'https'],
];
Middleware for PDF Generation:
public function handle(Request $request, Closure $next)
{
if ($request->is('pdf/*')) {
$pdf = new Pdf(config('weasyprint.binary'));
$html = $next($request)->getContent();
return response($pdf->getOutput($html))
->header('Content-Type', 'application/pdf');
}
return $next($request);
}
use Pontedilana\PhpWeasyPrint\Pdf;
use Illuminate\Support\Facades\Storage;
public function testPdfGeneration()
{
$pdf = new Pdf('/usr/local/bin/weasyprint');
$html = '<h1>Test PDF</h1>';
$filename = 'test.pdf';
// Generate PDF
$pdf->generateFromHtml($html, storage_path("app/{$filename}"));
// Assert file exists and is a valid PDF
$this->assertTrue(Storage::exists("app/{$filename}"));
$this->assertTrue(file_exists(storage_path("app/{$filename}")));
}
$pdf->disableTimeout(); // Let Laravel Queue handle timeouts
$cachedHtml = Cache::remember('invoice_html', 3600, function () {
return view('invoices.pdf', ['data' => $data])->render();
});
$pdf->getOutput($cachedHtml);
generateFromHtml for Local Files:
$pdf->generateFromHtml($html, 'path/to/output.pdf');
// Faster than streaming output for large PDFs
Binary Path Issues:
/usr/local/bin/weasyprint may fail on Windows or CI environments.config('weasyprint.binary') or env('WEASYPRINT_BINARY').checkBinary() to validate the path:
$pdf = new Pdf('/path/to/weasyprint');
if (!$pdf->checkBinary()) {
throw new \RuntimeException('WeasyPrint binary not found or not executable.');
}
Timeout Conflicts:
$pdf->setTimeout(30); // 30 seconds
// OR
$pdf->disableTimeout(); // For queue workers
SSRF Risks:
attachment, stylesheet) are fetched by default only for http/https.$pdf = new Pdf('/usr/local/bin/weasyprint', [], null, ['http', 'https', 'ftp']);
setOption().Memory Limits:
memory_limit.ini_set('memory_limit', '512M');
generateFromHtml() for localHow can I help you explore Laravel packages today?