LaravelでPDFを出力してみた

「LaravelでPDFを出したい」ってとき、mpdfとかdompdfがよく紹介されてるんだけど、CSSの細かいところが反映されなかったり、日本語フォントが変だったりで、ちょっと困ることありませんか?
今回、「もういっそ、HTML + CSS をそのままブラウザみたいにレンダリングしてPDF化したい!」と思って、Browsershotっていうライブラリを使ってみました。
結果的に、思ってたより簡単だし、かなり綺麗に出力できたので、その記録です。
目次
なぜBrowsershotを使ったのか?
もともと mpdf
を試してたんですが…
- CSSグリッドやflexがうまく効かない
- Google Fontsが使えない
- レイアウト崩れが出る
という感じで「HTMLをPDFにできる」とはいえ、再現度がかなり低かったです。
それで調べてたら、「Browsershotならpuppeteer + headless ChromeでPDF化できる」っていう情報を発見。要するにChromeでレンダリング→そのままPDF出力というイメージらしく、期待が高まる。
この方法が使える環境・技術スタック
今回紹介するBrowsershotを使ったPDF出力は、以下のような技術スタック・環境で動作確認しました
- MacOS 13.7
- Laravel 11
- Docker環境(arm64)
- Node.js & npm(puppeteer動作用)
- Chromium(headless Chrome)
- Linuxベースの環境(Debian/Ubuntu系)
docker-composeを使ってローカル環境での導入をやってみました。
そのため、導入には Dockerfileの編集やビルド作業が発生します。
また、puppeteerでChromeを動かすために一定のLinux知識やフォントの導入が必要です。
※Xserverなどの共用レンタルサーバーや、npmが使えない環境ではこの方法は使えません。
ローカルやVPS、クラウド(EC2など)向けの内容です。
環境設定
1. ディレクトリ構成
まずは空のディレクトリで始めます
mkdir laravel-browsershot-pdf
cd laravel-browsershot-pdf
2. docker-compose
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-app
volumes:
- ./:/var/www
ports:
- "8080:80"
working_dir: /var/www
3. Dockerfile
FROM php:8.3-fpm
# 基本パッケージ
RUN apt-get update && apt-get install -y \
git unzip zip curl libzip-dev \
libpng-dev libonig-dev libxml2-dev \
libnss3 libatk1.0-0 libatk-bridge2.0-0 \
libxss1 libasound2 libgbm-dev libxshmfence-dev libxrandr2 \
fonts-ipafont-gothic \
nodejs npm
# PHP拡張
RUN docker-php-ext-install zip pdo pdo_mysql
# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# Puppeteer用Chromium
RUN npm install -g npm && \
npm install puppeteer@21 && \
node -e "require('puppeteer').createBrowserFetcher().download('138.0.0.0')"
Laravel設定
1. Laravelプロジェクト作成と起動
docker-compose run --rm app composer create-project laravel/laravel .
docker-compose up -d
2. Browsershotのインストール
docker-compose exec app composer require spatie/browsershot
3. コントローラー追加
docker-compose exec app php artisan make:controller PdfController
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\View;
use Spatie\Browsershot\Browsershot;
class PdfController extends Controller
{
public function generate()
{
$html = View::make('pdf.sample', ['message' => 'こんにちは、Browsershot!'])->render();
$pdfPath = storage_path('app/public/sample.pdf');
Browsershot::html($html)
->format('A4')
->waitUntilNetworkIdle()
->savePdf($pdfPath);
return response()->download($pdfPath);
}
}
4. Bladeテンプレートを追加
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>サンプルPDF</title>
<style>
body { font-family: "IPA Pゴシック", sans-serif; padding: 40px; }
h1 { color: #336699; }
</style>
</head>
<body>
<h1>{{ $message }}</h1>
<p>これはBrowsershotで作成されたPDFです。</p>
</body>
</html>
5. ルートを設定
use App\Http\Controllers\PdfController;
Route::get('/pdf', [PdfController::class, 'generate']);
PDF出力
1. アクセスしてPDFを確認
ブラウザで以下にアクセスするとPDFのダウンロードが始まります。
http://localhost:8080/pdf
これで完成です!
というわけで、今回はBrowsershotを使ったPDF出力の方法をまとめてみました。
自分と同じように「どうすればいいんだろ?」と悩んでいた誰かの参考になればうれしいです。
今回は以上です!