wicked_pdfでHTML→PDF出力を実装していて詰まった箇所まとめ

Railsで開発しているサービスで、WebページをPDF書き出しする実装を担当することになりました。RailsでPDF書き出しを行う方法はいくつかありますが、今回はHTML/CSSでレイアウトを組みPDFを出力することができる**wicked_pdf**を採用しました。

本記事では、実装時に詰まった箇所をまとめてシェアします。詰まりポイントは人それぞれに存在すると思いますので、1例として参考にしていただければ幸いです。

# バージョン情報

```
Ruby: 2.3.1
Rails: 5.2.1
wicked_pdf: 1.1.0
wkhtmltopdf-binary: 0.12.3
```

# 先人たちの記事

CSSでレイアウトを組む際に参考にしました。
[Wicked PDFでハマらないために知っておきたいことをQ&A形式でまとめてみた](https://qiita.com/soyanchu/items/1cfb0280700ae8f36f1b)

公式ドキュメント。オプションがたくさん載っています
[mileszs/wicked_pdf: PDF generator (from HTML) plugin for Ruby on Rails](https://github.com/mileszs/wicked_pdf)

# どうやってPDF出力するの？
```
class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])

    respond_to do |format|
      format.html
      format.pdf do
        render pdf: "投稿内容",  # ファイル名
               layout: 'pdf.html', # レイアウト。 app/views/layoutから指定されます
               template: 'posts/pdf.html.slim',
               encording: 'UTF-8', # 日本語フォントを使用するために必要。
    end
  end
end
```
今回はもともとあったshowアクションに、**respond_toでpdfフォーマットを追加する**ことで実装しました。
PDFを表示させる際のlink_toは以下です。

```
= link_to posts_path(format: :pdf, id: @post.id), class: "btn bg-white text-dark" do
  i.fas.fa-print
  p.d-inline.pl-2 印刷する
```

# Webpackerを使っているとCSS表示ができない
2018年11月7日現在まだ対応していないようです。プルリクエストは出されているので、近いうちに対応するでしょう
[Add support for webpacker stylesheets & Javascript by LeKristapino · Pull Request #739](https://github.com/mileszs/wicked_pdf/pull/739)

`stylesheet_link_tag`に対応するwicked_pdfのヘルパーは用意されていますが、今回のプロジェクトではWebpackerでJS/CSSの管理をしており、`stylesheet_pack_tag`を使っていたので困りました。対応策として今回は、

- app/assets/stylesheets/pdf.css を用意して利用する
- webpackで管理していた画像を使う場合は、必要なものだけをS3にアップロードし読み込む

ことでお茶を濁しました。


pdf.cssはプリコンパイルする必要があるため、`config/initializer/assets.rb`に以下の記述を追加する必要があります。

```ruby:config/initializer/assets.rb
Rails.application.config.assets.precompile += ['pdf.css']
```

# フッターにページ番号を表示したい
renderのオプションで、以下を追加すればできました。

```
render pdf: 'file_name',
       〜〜中略〜〜
       footer: {
         center: '[page] / [topage]',  # ex) 1 / 10
         font_size: 8  # フォントサイズも指定できる
       }
```

# BootstrapのFlexboxが使えない？
debug=trueを指定してHTMLデバッグをしている際はレイアウトが崩れないのだけど、Bootstrapの`justify-content-between`クラスを指定して両端に要素を配置している箇所が、PDF表示にすると崩れてしまい困りました。
[flexbox layout doesn't work · Issue #1522](https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1522)
このissueによると、wicked_pdfの内部処理で使用している**wkhtmltopdfがまだFlexboxに対応していない**のが原因のようです。いずれ対応することもあるかもしれませんが、今回はFlexBoxは諦め、必要な箇所でfloatを指定することにしました。

# tableタグを使用している際に改ページが発生すると、thead要素とtbody要素が重なる
tableタグの途中で改ページが発生した場合、2ページ目でもデフォルトで再度thead要素を表示してくれるのですが、そのthead要素とtbody要素が重なってしまって困りました。
[overlapping text when repeating headers/footers in table · Issue #1524](https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1524#issuecomment-433796346)
上記issueのコメントを参考に、以下のようにcssを指定したところ解決しました。

```
.table {
  border-collapse: collapse !important;
  width: 100%;
  margin-bottom: 20px;
}
.table thead {
  display: table-header-group;
  height: 1.1cm;
}
.table tr {
  page-break-inside: avoid !important;
}
```

# サーバーにデプロイするとフォントサイズが変わってしまう
AWS EC2で動かしているステージング環境にデプロイすると、ローカル環境と比べて生成されたPDFのフォントサイズが大きくなってしまいレイアウトが崩れて困りました。

こちらは結論を言うと、wkhtmltopdfの最新バージョン(0.12.4)のバグであり、バージョンを下げることにより解決しました。(2018年11月現在)

```ruby:Gemfile
gem 'wkhtmltopdf-binary', '0.12.3'
```

wkhtmltopdfの0.12.4では、**MacOSのみフォントが縮小して表示されてしまうバグ**が報告されています。こちらの状況が近いです→ [wkhtmltopdf generates tiny output on Mac - Stack Overflow](https://stackoverflow.com/questions/40814680/wkhtmltopdf-generates-tiny-output-on-mac) 
当初ローカル環境で実装し始めの頃はそのようなバグは知る由もないので、

1. 「PDFにするとフォントサイズめちゃくちゃ小さくなるけどそういうもんなのかな」と誤認し、CSSでフォントサイズを上げてレイアウトを調整した
2. デプロイ先のLinux環境ではフォントサイズのバグはないため、そのまま大きく指定されたフォントサイズで表示されてしまった 


という顛末でした。

# 本番環境で動かすと、504 Gateway Time-outが表示される
ローカル環境、ステージング環境で動かせたので、いざ本番環境にデプロイすると `504 Gateway Time-out` が発生し非常に困りました...
こちらは本番環境で動かしているロードバランサーで、設定している**アイドルタイムアウトの秒数にPDFの生成が間に合わない**ためタイムアウトになっていることが原因でした。AWS ELBのアイドルタイムアウトの秒数を延ばしたことで解決しました。

# 終わりに
わからないことをググると、大抵wicked_pdfかwkhtmltopdfのGitHub Issueに行き着くことが多かった気がします。参考にしていただければ嬉しいです！