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

wicked_pdfからの移行ガイド

wicked_pdf(+ wkhtmltopdf)を使っているRailsアプリを、sghtmltopdfのgemへ移すための対応表と注意点。

wkhtmltopdfのオプションの対応状況はwkhtmltopdfオプション対応表wkhtmltopdfからの移行を参照してください。 ここでは「Rails/Rubyから見た違い」だけを扱います。

最小の置き換え

# Gemfile
- gem "wicked_pdf"
- gem "wkhtmltopdf-binary"
+ gem "sghtmltopdf"

コントローラはそのまま動くはずです。

def show
  respond_to do |format|
    format.pdf { render pdf: "invoice", template: "invoices/show", layout: "pdf" }
  end
end

外部プロセスの起動が無くなるので、wicked_pdfexe_path(wkhtmltopdfのバイナリの場所)の設定は不要になる。

設定の置き場所

# config/initializers/sghtmltopdf.rb
Sghtmltopdf.configure do |c|
  c.page_size   = "A4"
  c.margin_top  = "20mm"
  c.gothic_font = Rails.root.join("vendor/fonts/NotoSansJP-Regular.ttf")
end

wicked_pdfのWickedPdf.config = {...}に相当する。 マージ順は グローバル設定 → renderの引数で、後者が勝ちます。

オプション名の対応

wicked_pdfはネストしたHash(margin: {top: 10})を使うが、sghtmltopdfは CLIのフラグ名をそのままキーにした平坦なHashを使う(_-に対応する。page_size:--page-size)。 オプションの定義はRust側の1箇所に集約されていて、Ruby側はホワイトリストを持ちません。

wicked_pdfsghtmltopdf備考
pdf: "name"同じファイル名(.pdfは自動で付く)
template: / layout: / locals: / formats:同じRailsのビュー描画へそのまま渡る
disposition: / filename: / status:同じ既定のdispositioninline
show_as_html: true同じPDFにせずHTMLを返すデバッグ用
page_size: "A4"page_size: "A4"
page_height: / page_width:同じ単位付きの文字列("210mm")で渡す
orientation: "Landscape"同じ
margin: {top: 10, bottom: 10}margin_top: "10mm", margin_bottom: "10mm"wicked_pdfの数値はmm。単位を明示する
dpi: / zoom:同じ
grayscale: true同じ
background: falseno_background: true
encoding: "UTF-8"同じ
title:同じPDFのメタデータ
user_style_sheet:同じパスの配列も可
no_pdf_compression: true同じ
cover: "shared/cover"cover: <ファイルパス>テンプレート名ではなくHTMLファイルのパス(後述)
toc: {}toc: true見た目はtoc_header_text:などで調整
header: {left:, center:, right:}header_left: / header_center: / header_right:
header: {html: {template: "..."}}header_html: <ファイルパス>同上
header: {line: true, spacing: 5, font_name:, font_size:}header_line: true, header_spacing: 5, header_font_name:, header_font_size:footerも同様
outline: {}PDFアウトラインは非対応
disable_javascript / javascript_delay / window_statusJSは実行しない(設計上の非目標)
print_media_type常にprintメディア扱い
lowquality / viewport_size / disable_smart_shrinkingWebKit固有
exe_path / wkhtmltopdf外部プロセスを使わない
extra生のコマンドライン文字列は受けない。個別のキーで指定する

対応していないキーを渡すと、レンダリング時にSghtmltopdf::UsageErrorが理由付きで上がる(黙って無視はしない)。

表紙・ヘッダー・フッターのHTML

wicked_pdfはRailsのテンプレート名を受け取って内部で描画するが、sghtmltopdfの--cover/--header-html/--footer-htmlはファイルのパスを取ります(CLIと同じ経路に合流させるため)。 Railsのテンプレートを使いたい場合は、自分で描画して一時ファイルへ書き出してください。

def show
  header = Tempfile.new(["header", ".html"])
  header.write(render_to_string(template: "invoices/header", layout: false))
  header.flush

  render pdf: "invoice", template: "invoices/show", header_html: header.path
ensure
  header&.close!
end

ビューヘルパ

wicked_pdfsghtmltopdf
wicked_pdf_stylesheet_link_tagsghtmltopdf_stylesheet_link_tag
wicked_pdf_image_tagsghtmltopdf_image_tag
wicked_pdf_asset_pathsghtmltopdf_asset_path(見つからなければnil)
wicked_pdf_javascript_include_tag— (JSを実行しないので不要)
wicked_pdf_asset_base64— (ローカルファイルを直接読めるので不要)

素のstylesheet_link_tag/image_tagも、アセットがpublic/配下へprecompileされていればそのまま動きます。 PDFのレンダリングはHTTPサーバを介さないので、/assets/…のようなURLは--base-url(Railsでの既定はRails.root/public)を基準にローカルファイルとして解決される。

開発環境のようにアセットがまだpublic/に無い場合は、CSSの中身を<style>へ展開するsghtmltopdf_stylesheet_link_tagを使う。

<%= sghtmltopdf_stylesheet_link_tag "pdf" %>

既定値の違い

  • マージン: wkhtmltopdfは左右10mm。sghtmltopdfは四辺1in(96px)。 同じ見た目にしたければmargin_*を明示する
  • CLIオプションとCSSの@page: wkhtmltopdfはCLIが勝つが、sghtmltopdfは @page が勝つ(オプションは初期値)
  • ローカルファイルの参照範囲: Railsでは--allowの既定がRails.rootになる。アプリの外(例: /usr/share/fonts)のファイルを<img>@font-faceurl()で参照している場合は、Sghtmltopdf.configure { |c| c.allow = [Rails.root.to_s, "/usr/share/fonts"] }のように明示する。--font系(gothic_fontなど)で渡すフォントはこの制限を受けない
  • リモート取得: http(s)のアセット取得は既定で無効。必要ならallow_remote_assets: true

フォント

wkhtmltopdfはシステムのフォント設定に依存するが、sghtmltopdfはgothic_font/serif_font/mono_fontで指定できる。 日本語を出す場合は、コンテナのフォント事情に左右されないよう明示するのが安全。

Sghtmltopdf.configure do |c|
  c.gothic_font = Rails.root.join("vendor/fonts/NotoSansJP-Regular.ttf")
end

別プロセスへ逃がす(wicked_pdfには無い選択肢)

wicked_pdfはリクエストごとにwkhtmltopdfのプロセスを起動するが、sghtmltopdfのgemはアプリのプロセス内で変換する(重い処理の間はGVLを解放するのでPumaの他スレッドは止まらない)。 それでもアプリのCPUを使いたくない場合は、server_urlで別プロセス(sghtmltopdf server)へ委譲できる。

Sghtmltopdf.configure { |c| c.server_url = "http://pdf.internal:8080" }

負荷分散はLB(nginx・k8s Service)を前段に置く前提で、URLは1つだけ受ける。 到達できないときはSghtmltopdf::ServerErrorになり、ローカル変換へはフォールバックしない。

サーバモードではbase_urlallow・フォント指定などローカルパスを取るオプションはリクエストから指定できない(サーバ起動時にだけ設定できる)。 Railtieが入れる既定値は自動的に外れるが、configureで明示的に設定している場合は400(UsageError)になるので、サーバ側の起動オプションへ移す。

まだ無いもの

  • PDFの結合・アウトライン: 非対応

なお逐次出力(ストリーミング)はwicked_pdfには無い機能で、ブロック付きのrenderで使える。 ActionController::Liveと組み合わせれば、確定したページから順にレスポンスへ流せます → Ruby / Rails