34
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-06

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に以下の記述を追加する必要があります。

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月現在)

Gemfile
gem 'wkhtmltopdf-binary', '0.12.3'

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

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

という顛末でした。

本番環境で動かすと、504 Gateway Time-outが表示される

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

終わりに

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

34
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?