🖨️
Go言語でHTMLテンプレートをPDF/PNGに変換する方法
ゴール
- Go で HTML テンプレを html/template から生成
- chromedp(headless Chrome)で PDF と PNG を作る
- 一発で試せるサンプル(CLI フラグ付き)
0. 前提(macOS, Apple Silicon)
- Go 1.21+(推奨 1.22 以上)
- Google Chrome (または Chromium) がインストール済み
(未導入なら brew install --cask google-chrome)
1. プロジェクト初期化
mkdir html2pdf-go && cd html2pdf-go
go mod init example.com/html2pdf
go get github.com/chromedp/chromedp
2. HTML テンプレートを用意
template.html(例:簡易レシート)
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>{{.Title}}</title>
<style>
:root{ --brand:#0ea5e9; --muted:#64748b; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; margin: 32px; }
h1 { color: var(--brand); margin: 0 0 8px; }
.meta { color: var(--muted); font-size: 14px; margin-bottom: 24px; }
table { border-collapse: collapse; width: 100%; }
th, td { padding: 8px 12px; border-bottom: 1px solid #e2e8f0; text-align: left; }
tfoot td { font-weight: bold; }
.footer { margin-top: 32px; color: var(--muted); font-size: 12px; }
</style>
</head>
<body>
<h1>{{.Title}}</h1>
<div class="meta">発行日: {{.IssuedAt}} / 請求先: {{.Customer}}</div>
<table>
<thead><tr><th>品目</th><th>数量</th><th>単価</th><th>小計</th></tr></thead>
<tbody>
{{range .Items}}
<tr>
<td>{{.Name}}</td>
<td>{{.Qty}}</td>
<td>¥{{.Price}}</td>
<td>¥{{.Subtotal}}</td>
</tr>
{{end}}
</tbody>
<tfoot>
<tr><td colspan="3">合計</td><td>¥{{.Total}}</td></tr>
</tfoot>
</table>
<div class="footer">
これはサンプル出力です。PDF 印刷や PNG スクショで見栄えを確認できます。
</div>
</body>
</html>
3. Go コード(chromedp で PDF / PNG)
main.go
package main
import (
"bytes"
"context"
"encoding/base64"
"flag"
"html/template"
"log"
"os"
"path/filepath"
"time"
"github.com/chromedp/cdproto/emulation"
"github.com/chromedp/cdproto/page"
"github.com/chromedp/chromedp"
)
// テンプレに流し込むデータ構造
type Item struct {
Name string
Qty int
Price int
Subtotal int
}
type ViewData struct {
Title string
IssuedAt string
Customer string
Items []Item
Total int
}
func main() {
var tplPath, outPDF, outPNG, chromeExec string
var width, height int
flag.StringVar(&tplPath, "tpl", "template.html", "HTML template path")
flag.StringVar(&outPDF, "pdf", "out.pdf", "PDF output path")
flag.StringVar(&outPNG, "png", "out.png", "PNG output path")
flag.StringVar(&chromeExec, "chrome-path", "", "Custom Chrome/Chromium executable (optional)")
flag.IntVar(&width, "w", 1280, "viewport width for PNG")
flag.IntVar(&height, "h", 800, "viewport height for PNG")
flag.Parse()
// 1. テンプレ読み込み + データ適用
tpl, err := template.ParseFiles(tplPath)
must(err)
// サンプルデータ
data := ViewData{
Title: "ご請求書",
IssuedAt: time.Now().Format("2006-01-02"),
Customer: "exMedia 株式会社",
Items: []Item{
{"クラウド利用料", 3, 12000, 3 * 12000},
{"サポート費", 1, 2000, 1 * 20000},
},
}
for _, it := range data.Items {
data.Total += it.Subtotal
}
var buf bytes.Buffer
must(tpl.Execute(&buf, data))
html := buf.String()
// data URL で読み込ませる (ファイル保存不要)
dataURL := "data:text/html;base64," + base64.StdEncoding.EncodeToString([]byte(html))
// 2. chromedp セッション作成
allocOpts := append(chromedp.DefaultExecAllocatorOptions[:])
if chromeExec != "" {
allocOpts = append(allocOpts, chromedp.ExecPath(chromeExec))
}
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), allocOpts...)
defer cancel()
// タイムアウト (ページが重い場合は延長)
ctx, cancel := context.WithTimeout(allocCtx, 30*time.Second)
defer cancel()
ctx, cancel = chromedp.NewContext(ctx)
defer cancel()
// 3. ページを開いて PDF 生成 & PNG 取得
var pdfBytes, pngBytes []byte
err = chromedp.Run(
ctx,
// Viewport は PNG の見え方に効く (PDF には影響しない)
emulation.SetDeviceMetricsOverride(int64(width), int64(height), 1.0, false),
chromedp.Navigate(dataURL),
chromedp.WaitReady("body"),
// PDF (A4/背景印刷あり)
chromedp.ActionFunc(func(ctx context.Context) error {
pdf, _, err := page.PrintToPDF().WithPrintBackground(true).
// 用紙サイズを変える例: A4 = 8.27 x 11.69 inch
WithPaperWidth(8.27).
WithPaperHeight(11.69).
Do(ctx)
if err != nil {
return err
}
pdfBytes = pdf
return nil
}),
// フルページ PNG(品質 90)
chromedp.FullScreenshot(&pngBytes, 90),
)
must(err)
// 4. 保存
must(os.WriteFile(filepath.Clean(outPDF), pdfBytes, 0o644))
must(os.WriteFile(filepath.Clean(outPNG), pngBytes, 0o644))
log.Printf("✅ Done. PDF=%s, PNG=%s\n", outPDF, outPNG)
}
func must(err error) {
if err != nil {
log.Fatal(err)
}
}
4. 実行
go run . \
-tpl template.html \
-pdf invoice.pdf \
-png invoice.png
- invoice.pdf(A4, 背景印刷あり)
- invoice.png(1280×800 のフルページスクリーンショット)
- 下記が生成された画像

7. まとめ
- chromedp を使えば、Go から Chrome を操作して HTML → PDF/PNG を安定生成できる
- テンプレは html/template、実体は data URL で渡すと楽
- 画像/フォントは data URL or file:// にして解決
Discussion