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形式でまとめてみた
公式ドキュメント。オプションがたくさん載っています
mileszs/wicked_pdf: PDF generator (from HTML) plugin for Ruby on Rails
どうやって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
stylesheet_link_tag
に対応するwicked_pdfのヘルパーは用意されていますが、今回のプロジェクトではWebpackerでJS/CSSの管理をしており、stylesheet_pack_tag
を使っていたので困りました。対応策として今回は、
- app/assets/stylesheets/pdf.css を用意して利用する
- webpackで管理していた画像を使う場合は、必要なものだけをS3にアップロードし読み込む
ことでお茶を濁しました。
pdf.cssはプリコンパイルする必要があるため、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
この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
上記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月現在)
gem 'wkhtmltopdf-binary', '0.12.3'
wkhtmltopdfの0.12.4では、MacOSのみフォントが縮小して表示されてしまうバグが報告されています。こちらの状況が近いです→ wkhtmltopdf generates tiny output on Mac - Stack Overflow
当初ローカル環境で実装し始めの頃はそのようなバグは知る由もないので、
- 「PDFにするとフォントサイズめちゃくちゃ小さくなるけどそういうもんなのかな」と誤認し、CSSでフォントサイズを上げてレイアウトを調整した
- デプロイ先のLinux環境ではフォントサイズのバグはないため、そのまま大きく指定されたフォントサイズで表示されてしまった
という顛末でした。
本番環境で動かすと、504 Gateway Time-outが表示される
ローカル環境、ステージング環境で動かせたので、いざ本番環境にデプロイすると 504 Gateway Time-out
が発生し非常に困りました...
こちらは本番環境で動かしているロードバランサーで、設定しているアイドルタイムアウトの秒数にPDFの生成が間に合わないためタイムアウトになっていることが原因でした。AWS ELBのアイドルタイムアウトの秒数を延ばしたことで解決しました。
終わりに
わからないことをググると、大抵wicked_pdfかwkhtmltopdfのGitHub Issueに行き着くことが多かった気がします。参考にしていただければ嬉しいです!