2026/04/21

wkhtmltopdf から移行する完全ガイド|@page・CSS・日本語フォント対応

wkhtmltopdf移行PDF生成CSSチュートリアル

wkhtmltopdfを使っているプロジェクトで「@page { size: A4; margin: 0; } が効かない」「日本語が文字化けする」「CSS Gridが崩れる」といった問題に悩んでいる開発者は多いです。

原因はシンプルです。wkhtmltopdf は2012年に開発が始まったWebKitベースのツールで、現代のCSSを完全にはサポートしていません。そして現在、wkhtmltopdfの開発は事実上停止しています。

この記事では、wkhtmltopdfの主な限界とChromiumベースエンジンの違いを解説し、FUNBREW PDFへのスムーズな移行手順をコード付きで紹介します。

wkhtmltopdf の現状と EOL 問題

開発停止の経緯

wkhtmltopdf の最終安定版は 0.12.6(2020年リリース) です。GitHubのissueでもメンテナーが「アクティブメンテナンスは終了」と明言しており、以下の問題が未解決のまま残っています:

  • セキュリティパッチの未提供
  • Qt 5 への完全対応なし
  • モダンなCSSプロパティへの非対応
  • PDF/Aへの非対応

wkhtmltopdf を使い続けるリスク

リスク 影響
セキュリティパッチなし SSRF攻撃やXSS経由の情報漏洩リスク
CSS非対応の拡大 UI改修のたびにPDFが崩れる
OS依存のフォント問題 サーバー移行時に日本語が文字化け
採用難航 新メンバーが使いやすいドキュメントが少ない

wkhtmltopdf と Chromium の CSS 互換性比較

移行前に知っておくべき主要な差異をまとめます。

@page ルールの動作差異

最もよく問題になる @page の動作比較:

/* Chromiumでは完全動作 / wkhtmltopdfでは一部無視される */
@page {
  size: A4;
  margin: 0;           /* wkhtmltopdf: コマンドラインオプションが優先 */
  margin-top: 20mm;
  margin-bottom: 20mm;
}

/* Chromiumのみ対応 */
@page :first {
  margin-top: 60mm;    /* 表紙ページの余白設定 */
}

/* Chromiumのみ対応 */
@page {
  @bottom-center {
    content: counter(page) " / " counter(pages);  /* ページ番号 */
  }
}

wkhtmltopdf で @page { size: A4; margin: 0; } を使ってもデフォルト余白が残る場合、コマンドラインオプションで上書きが必要でした:

# wkhtmltopdf 時代の余白制御(コマンドラインオプション必須)
wkhtmltopdf \
  --margin-top 0 \
  --margin-right 0 \
  --margin-bottom 0 \
  --margin-left 0 \
  --page-size A4 \
  input.html output.pdf

Chromiumベースエンジンでは CSSの @page が仕様通りに動作します:

/* これだけで余白ゼロA4が実現する */
@page {
  size: A4;
  margin: 0;
}

CSS 機能の対応比較

CSS機能 wkhtmltopdf Chromium(FUNBREW PDF)
@page margin 部分対応(CLIオプション優先) 完全対応
@page margin-box(ヘッダー/フッター) 非対応 対応
break-inside: avoid 部分対応 完全対応
CSS Grid 非対応 対応
CSS Flexbox(モダン) 部分対応 完全対応
CSS カスタムプロパティ(変数) 非対応 対応
position: sticky 非対応 対応
SVGレンダリング 不安定 安定
日本語フォント OS依存(要手動設定) プリインストール済み

改ページ制御の違い

/* wkhtmltopdf: 古いプロパティのみ確実に動作 */
.keep-together {
  page-break-inside: avoid;  /* 旧プロパティ */
}

/* Chromium: 新旧両方対応 */
.keep-together {
  break-inside: avoid;          /* 新プロパティ(推奨) */
  page-break-inside: avoid;     /* 旧プロパティ(フォールバック) */
}

FUNBREW PDF API への移行コード

Step 1: wkhtmltopdf の既存コードを確認

典型的な wkhtmltopdf の使い方:

# コマンドライン
wkhtmltopdf \
  --page-size A4 \
  --margin-top 10mm \
  --margin-bottom 10mm \
  --margin-left 15mm \
  --margin-right 15mm \
  --encoding utf-8 \
  input.html output.pdf

Step 2: CSS を @page ルールに移行

wkhtmltopdf 時代のCLIオプションを CSS の @page に移す:

/* wkhtmltopdf CLIオプション → CSS @page への移行 */
@page {
  size: A4;
  margin: 10mm 15mm;  /* top/bottom: 10mm, left/right: 15mm */
}

Step 3: FUNBREW PDF API 呼び出しに変換

curl:

curl -X POST https://pdf.funbrew.cloud/api/pdf/generate \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<html>...</html>",
    "options": {
      "engine": "quality",
      "format": "A4"
    }
  }' \
  -o output.pdf

HTMLファイルを読み込んでAPIに送る場合:

HTML_CONTENT=$(cat input.html)

curl -X POST https://pdf.funbrew.cloud/api/pdf/generate \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d "{
    \"html\": $(echo "$HTML_CONTENT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'),
    \"options\": {
      \"engine\": \"quality\",
      \"format\": \"A4\"
    }
  }" \
  -o output.pdf

Python:

import requests

# wkhtmltopdf 時代のコード(移行前)
# subprocess.run([
#     'wkhtmltopdf',
#     '--page-size', 'A4',
#     '--margin-top', '10mm',
#     'input.html', 'output.pdf'
# ])

# FUNBREW PDF API への移行後
with open('input.html', 'r', encoding='utf-8') as f:
    html_content = f.read()

response = requests.post(
    'https://pdf.funbrew.cloud/api/pdf/generate',
    headers={
        'Authorization': 'Bearer sk-your-api-key',
        'Content-Type': 'application/json',
    },
    json={
        'html': html_content,
        'options': {
            'engine': 'quality',
            'format': 'A4',
        },
    },
)

data = response.json()

# PDFのダウンロード
pdf_response = requests.get(data['data']['download_url'])
with open('output.pdf', 'wb') as f:
    f.write(pdf_response.content)

print('PDF生成完了:', data['data']['download_url'])

Node.js:

const fs = require('fs');

// wkhtmltopdf 時代のコード(移行前)
// const { execSync } = require('child_process');
// execSync(`wkhtmltopdf --page-size A4 input.html output.pdf`);

// FUNBREW PDF API への移行後
const htmlContent = fs.readFileSync('./input.html', 'utf-8');

const response = await fetch('https://pdf.funbrew.cloud/api/pdf/generate', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk-your-api-key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    html: htmlContent,
    options: {
      engine: 'quality',
      format: 'A4',
    },
  }),
});

const result = await response.json();

// PDFをダウンロード
const pdfResponse = await fetch(result.data.download_url);
const pdfBuffer = await pdfResponse.arrayBuffer();
fs.writeFileSync('./output.pdf', Buffer.from(pdfBuffer));

console.log('PDF生成完了:', result.data.download_url);

日本語フォント対応の移行

wkhtmltopdf 時代の日本語フォント設定

wkhtmltopdf では日本語を表示するためにOSへのフォントインストールが必要でした:

# Ubuntu/Debianへのフォントインストール
sudo apt-get install -y fonts-noto-cjk

# または wkhtmltopdf に @font-face で指定

FUNBREW PDF では設定不要

FUNBREW PDF には Noto Sans JP がサーバーにプリインストールされているため、HTMLのCSSで font-family を指定するだけで日本語が正しく表示されます:

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <style>
    body {
      /* Noto Sans JP はプリインストール済み。Google Fonts読み込み不要 */
      font-family: 'Noto Sans JP', sans-serif;
      font-size: 14px;
      line-height: 1.7;
    }
    h1 { font-weight: 700; }
  </style>
</head>
<body>
  <h1>日本語タイトル</h1>
  <p>ひらがな・カタカナ・漢字・記号(①②③)が追加設定なしで表示されます。</p>
</body>
</html>

オフライン環境や Base64 埋め込みが必要な場合は日本語フォント完全ガイドを参照してください。

よくある移行時の問題と解決策

問題1: 余白が変わった

原因: wkhtmltopdf でCLIオプションで設定していた余白が、CSS @page に移行した際に値が変わった。

解決策: @page { margin: 上下 左右; } の値を確認。物理単位(mm, pt)を使うと予測しやすい:

@page {
  size: A4;
  margin: 15mm 20mm; /* 上下 15mm、左右 20mm */
}

問題2: フォントが変わった

原因: wkhtmltopdf ではOSフォントを使っていたが、FUNBREW PDF ではCSSの font-family 指定が必要。

解決策: bodyfont-family: 'Noto Sans JP', sans-serif; を追加する。

問題3: 改ページ位置が変わった

原因: Chromiumはwkhtmltopdfより厳密に break-inside: avoid を適用するため、ページの区切り位置が変わることがある。

解決策: 意図した改ページ制御を明示的にCSSで指定:

/* 改ページさせたくない要素に付ける */
.keep-together {
  break-inside: avoid;
  page-break-inside: avoid;
}

/* セクション開始時に必ず改ページ */
.new-section {
  break-before: page;
}

改ページ制御の詳細はHTML to PDF CSSスニペット集を参照してください。

問題4: レンダリング結果が微妙に違う

原因: ChromiumはwkhtmltopdfよりもCSSを厳密に解釈するため、古いCSSの書き方が正しく動作しない場合がある。

解決策: Playground でHTMLを貼り付けてリアルタイムにPDF出力を確認しながら調整する。

移行チェックリスト

移行作業の完了確認に使ってください:

□ FUNBREW PDF のAPIキー取得
□ CLIオプションの余白設定を CSS @page に移行
□ HTML の文字コードが UTF-8 であることを確認
□ 日本語フォントの font-family 指定を追加
□ 改ページ制御を break-inside / break-before で明示
□ テスト用HTML で PDF出力を確認(Playground活用)
□ 本番HTMLで生成テスト
□ 出力PDFのページ数・余白・フォントを目視確認

まとめ

wkhtmltopdf からの移行で得られるメリットをまとめます:

項目 wkhtmltopdf FUNBREW PDF(Chromium)
@page CSS 部分サポート 完全サポート
日本語フォント OS依存(要設定) プリインストール済み
CSS Grid/Flexbox 非対応/部分対応 完全対応
セキュリティ更新 停止 継続提供
ヘッダー/フッター CLIオプション CSS @page または API
JavaScript実行 制限あり 完全サポート

移行の複雑さは既存HTMLの品質によりますが、基本的なHTML→PDF変換は APIキーの取得とエンドポイントの変更だけ で完了します。

PlaygroundでHTMLをすぐに試せます。APIの詳細はAPIリファレンス、パフォーマンス最適化はPDF API本番運用チェックリストを参照してください。

関連リンク

Powered by FUNBREW PDF