Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HTTPサーバモード

常駐させて、HTTP経由で変換を受け付けるモードです。 アプリケーションからプロセスを起動する必要がなくなり、負荷分散もロードバランサに任せられます。

sghtmltopdf server --listen 127.0.0.1:8080 --font NotoSansJP-Regular.ttf
# → 標準出力に `listening on 127.0.0.1:8080` が出る
curl --data-binary @invoice.html \
     'http://127.0.0.1:8080/pdf?page-size=A4&margin-top=20mm&toc' \
     -o invoice.pdf

常駐させるなら、日本語フォントを同梱したDockerイメージが手軽です(引数なしでこのサーバとして起動します)。

docker run --rm -p 8080:8080 ghcr.io/waka/sghtmltopdf

起動オプション

オプション既定説明
--listen <ADDR:PORT>127.0.0.1:8080待ち受けアドレス。:0で空きポートを自動割り当て
--workers <N>CPUコア数同時に変換するワーカースレッド数
--max-queue <N>ワーカー数×4受理待ちの上限。超えると503
--max-body-size <BYTES>4194304 (4MiB)リクエストボディの上限
--timeout <SECS>301リクエストに与える秒数。キュー待ちと変換の合計(超えると504)
--font <PATH> ほかフォント指定リクエストからは変更できない
--enable-local-file-access / --allow <PATH> / --allow-remote-assetsすべて禁止明示的に許可する場合のみ

認証とTLSは持ちません。 外部へ公開する場合はリバースプロキシを前段に置いてください。

エンドポイント

メソッド・パス説明
POST /pdfボディのHTMLをPDFへ変換して返す(application/pdf)
POST /pdf?stream=1同上。chunked transfer encodingでページが確定したそばから流す
GET /healthzok
GET /versionsghtmltopdf <version>

クエリパラメータ

CLIのロングオプションから--を取った名前がそのまま使えます。 値の解釈もCLIと同一です(同じパーサへ通しているため)。

指定できるのは許可リストに載っているものだけです。 ページの体裁・PDFのメタデータ・ヘッダー/フッターの文字列・目次の見た目など、リクエストごとに変わってよく、かつサーバのファイルシステムにもネットワークにも触れないオプションが対象です。 それ以外は400を返します。

クエリ相当するCLIオプション
?page-size=A4--page-size A4
?margin-top=20mm--margin-top 20mm
?toc--toc(値なしは真)
?grayscale=1 / =true--grayscale
?grayscale=0 / =false指定なしと同じ

値はパーセントエンコードできます(%XX+)。

各オプションの意味はCLIリファレンスを参照してください。

サーバ起動時にだけ指定できるオプション

以下は許可リストに含まれず、指定すると400を返します。 ローカルパスを取るもの・出力先・アクセス制御・ログ設定はサーバ起動時にだけ設定できます。

font, font-index, gothic-font, gothic-font-index, serif-font, serif-font-index,
mono-font, mono-font-index, output, cover, header-html, footer-html,
user-style-sheet, base-url, allow, enable-local-file-access,
disable-local-file-access, allow-remote-assets, log-level, quiet

ステータスコード

コード状況
200成功(Content-Type: application/pdf)
400未知/禁止のクエリキー、値の形式不正、ボディが空
404未知のパス
405使えないメソッド
413ボディが--max-body-size超過
500レンダリング失敗
503キュー溢れ(--max-queue超過)
504--timeout超過(キュー待ち、または変換が長すぎる)

ストリーミング

  • 入力: リクエストボディは読み切らずに、64KiBずつエンジンへ流します。
    • 大きなHTMLを丸ごとメモリに載せません
  • 出力: 既定はバッファ返却(Content-Length付き)。?stream=1を付けるとchunked transfer encodingで、ページが確定したそばから流します
curl --data-binary @big.html 'http://127.0.0.1:8080/pdf?stream=1' -o out.pdf

エンジン側のストリーミングモード(?streaming)と併用すると、入力・レンダリング・出力のすべてが逐次処理になります。

メモリの見積もり

変換に必要なメモリは、入力の大きさにほぼ比例します。 実測(最適化ビルド)では次のとおりでした。

要因単価抑えているもの
要素の数472B〜1210B/ノードノード数上限(50万)
テキストの量約185MiB/入力1MiB--max-body-size

どちらの上限も、既定では最悪およそ600〜750MiBに収まる値にしてあります。 ワーカーは同時に変換するので、プロセス全体では「ワーカー数 × この値」が必要です。 既定(--workersはCPUコア数)のまま8コアの機械で動かすと最悪6GiB程度になるため、コンテナのメモリ制限に合わせて--workers--max-body-sizeを調整してください。

要素数が上限を超えると400を返します。 ?streamingを付けると処理済みの部分が随時解放されるため、要素数の上限には当たりにくくなります。

既知の限界

  • --timeoutはキュー待ちと変換の合計に効きます。変換の打ち切り判定はチャンク投入ごと・トップレベル要素ごと・ページ書き出しごとに行うため、超過に気づくのは最大でその1区間ぶん遅れます。レイアウトの1回の呼び出しの内側までは見ません
  • 実測では、10MiBの重いHTMLに--timeout 2を指定した場合の実際の応答は2.1〜5.2秒でした(打ち切り後に大きなDOMを破棄する時間も含みます)。指定より早く返ることはありません
  • ?stream=1のとき、ヘッダ送信後に失敗してもステータスは200のままになります(パイプが閉じ、クライアントには不完全なPDFが届きます)。クエリの不正・空ボディ・サイズ超過はヘッダ送信前に検出するので400/413で返ります
  • ?stream=1のときは入力を先に読み切ります(HTTPライブラリの制約で、ボディ読み取りと応答が排他のため)。入力と出力のストリーミングは同時には使えません

Railsから使う

Ruby gemには、変換をこのサーバへ委譲するserver_url設定があります。 Ruby / Railsを参照してください。