Rails の 新興 PDF 生成ライブラリ rails_pdf を使ってみる

## 概要
[GitHub - igorkasyanchuk/rails_pdf: A reliable way to generate PDF of any complexity in Ruby on Rails apps](https://github.com/igorkasyanchuk/rails_pdf)

rails_pdf は Ruby on Rails 用の PDF 生成プラグイン で、ReLaXedJS [GitHub - RelaxedJS/ReLaXed: Create PDF documents using web technologies](https://github.com/RelaxedJS/ReLaXed) という Javascript ベースの PDF 生成プログラムをバックエンドとして利用します。

wicked_pdf (wkhtmltopdf) や ThinReports などと比較すると新進の gem で、現状はコードベースも大きなものではないのですが、ReLaXedJS は安定した、人気のあるプロジェクトですし、公式の推奨する PDF テンプレートの開発環境を整える(後述)ことで高速に開発を行うことができます。

また、rails_pdf が公式に謳っているように、「Rails の view/controller から PDF 生成のロジックを切り離す」ことを目的に作成されており、バックエンドサーバーとして利用されることが多くなった昨今の Rails の状況にもマッチしていると言えそうです。(とはいえ、テンプレートを作成して、そこに変数を埋め込んで PDF を生成するという方法は他のライブラリと変わりないのですが。)

## 環境構築

### 環境

* CentOS 7.4 (on Vagrant)
* Ruby 2.5.1
* Rails 5.2.3


#### 1. ReLaXedJS のインストール

npm で グローバルインストールできない場合は、下記リンクの手順に従う。
[Troubleshooting · RelaxedJS/ReLaXed Wiki · GitHub](https://github.com/RelaxedJS/ReLaXed/wiki/Troubleshooting)

``` bash:terminal
# グローバルインストール。"do not use sudo" とのこと。
$ npm i -g relaxedjs

> npm ERR! Error: EACCES: permission denied, access '/usr/lib/node_modules'

# 上記でパーミッションの問題でインストールできない場合はローカルにインストールする。
$ cd ~
$ git clone https://github.com/RelaxedJS/ReLaXed.git .
$ cd ReLaXed
$ npm install
$ sudo npm link --unsafe-perm=true

$ relaxed --version

> 0.2.1

```

#### 2. Chromium のインストール

[GitHub - RelaxedJS/ReLaXed: Create PDF documents using web technologies](https://github.com/RelaxedJS/ReLaXed#how-relaxed-works)

上記リンクの通り、ReLaXedJS は Puppeteer を経由して ヘッドレス Chromium を操作し、PDF を生成するので、Chromium ブラウザをインストールする必要がある。ReLaXedJS の依存関係に Chromium は含まれていて同時にインストールされるが、実際に ReLaXedJS を動かした際に共有ライブラリが見つからない旨のエラーが発生した場合の対応である。

``` bash:error
error while loading shared libraries: libXcomposite.so.1: cannot open shared object file: No such file or directory

```

[Troubleshooting · RelaxedJS/ReLaXed Wiki · GitHub](https://github.com/RelaxedJS/ReLaXed/wiki/Troubleshooting#failure-unfound-system-dependencies)

上記、トラブルシューティングの記事にも同様の記載がある。

``` bash:terminal
# CentOS の場合
$ sudo yum install -y epel-release
$ sudo yum install -y chromium

# Debian/Ubuntu の場合
$ sudo apt-get install chromium-browser

``` 

### 3. rails_pdf のインストール
[GitHub - igorkasyanchuk/rails_pdf: A reliable way to generate PDF of any complexity in Ruby on Rails apps](https://github.com/igorkasyanchuk/rails_pdf#installation)

Gemfile に `gem 'rails_pdf'` を追記して `$ bundle` する。


### (ver 0.2.0 以前のみ) 4. rails_pdf の修正

#### ローカル変数のアサイン
ver 0.2.0 時点で、テンプレートへのローカル変数のアサイン方法がコミットされていない。プルリクエストを自分で適用する必要がある。

当該の修正箇所はこちら。
[Allow to assign locals in renderer by gambala · Pull Request #5 · igorkasyanchuk/rails_pdf · GitHub](https://github.com/igorkasyanchuk/rails_pdf/pull/5/files)

10行程度の変更なので `(bundle_dir)/ruby/(ruby_version)/gems/rails_pdf-0.2.0/lib/rails_pdf/renderer.rb` を修正する。

この変更により、プルリクエストに記載のあるように `RailsPDF.template("sales/invoice.html.erb").assign(order: @order).render` という形で、テンプレートにローカル変数をアサインできるようになる。(Rails の partial template と似た感じですね。)

#### --no-sandbox オプションの追記 (セキュリティリスクあり/修正は自己責任で)

ReLaXedJS はデフォルトでは サンドボックス上で Chromium を操作するが、VM や コンテナ上で環境構築する際に、権限の関係でサンドボックスを利用できない場合があり、コマンド実行した時に下記のようなエラーメッセージが表示される。

``` bash:terminal
No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox.

```

メッセージにも警告されているように、セキュリティリスクを承知の上ですぐに動かしたい場合は、`--no-sandbox` オプションを `relaxed` コマンドに渡すことで、エラーを回避できる。

rails_pdf に適用する場合は、同様に `lib/rails_pdf/rendere.rb` の relaxed コマンド呼び出し箇所を修正する。


``` ruby:(bundle_dir)/ruby/(ruby_version)/gems/rails_pdf-0.2.0/lib/rails_pdf/renderer.rb

-   command = "#{RailsPDF.relaxed} #{input.path.to_s} #{output.path.to_s} --basedir / --build-once"
+   command = "#{RailsPDF.relaxed} #{input.path.to_s} #{output.path.to_s} --no-sandbox --basedir / --build-once"

```


## 動作テスト

付属のサンプルで PDF 生成ができるか確認する。

``` bash:terminal

$ bin/rails g rails_pdf basic_invoice report

$ bin/rails c

> RailsPDF.template("report/invoice.pug.erb").render_to_file("#{Rails.root}/public/invoice.pdf")

```

PDF が無事に生成されれば OK。



## テンプレートの開発環境

[Tips and recommendations · RelaxedJS/ReLaXed Wiki · GitHub](https://github.com/RelaxedJS/ReLaXed/wiki/Tips-and-recommendations#ideal-setup-for-the-atom-editor)

ReLaXedJS 公式におすすめ Tips として、Atom エディタを使用した開発環境の構築方法が紹介されている。(エディタは何でも良いと思う。)
relaxed コマンドはオプションなしで起動することで、ディレクトリ内のファイル変更を検知して、すぐに PDF ファイルを生成してくれるので高速に開発を進めることができる。

テンプレートファイルは html もしくは pug 形式となる。
Rails のプロジェクトとは別にテンプレートを作成してから、そのファイルを Rails にコピーし、変数を埋め込んで使う形になるので 最終的には html.erb もしくは pug.erb になる。


## トラブルシューティング


### 解決済み

#### サーバーの Chromium 環境で指定した日本語フォントが反映されない
テンプレートの開発は MacOS 、サーバーの開発環境は CentOS on Vagrant を使用しているが、MacOS 上では CSS で指定したフォントで PDF ファイルが生成されるが、CentOS 上の環境では最初日本語の部分が表示されなかった。

[Google Chrome・インストール・CentOS7](http://www.ajisaba.net/develop/chrome/install_centos7.html)
[【centos7】Chrome（ブラウザ）の文字化けを治す - 東京伊勢海老通信](https://altarf.net/computer/server_tips/3874)

上記を参考に、CentOS に日本語フォントをインストールしたら日本語部分が表示されるようにはなった。…が、CSS で指定したフォントではなく、インストールしたフォントで表示される。OS の locale を設定しても特に変わりなし。

日本語以外の英数字の部分は指定したフォントで表示されているので、フォント自体の読み込みに失敗しているわけではなさそう。
そもそも MacOS 上では問題のない話なので、 rails_pdf や ReLaXedJS の問題ではない。

##### (解決方法)

[Chrome&Chromium will not display webfonts, @import fonts on Ubuntu 16](https://github.com/GoogleChrome/puppeteer/issues/2692)

上記ポストに従い、 Node.js のヴァージョンを v8.11 -> v11.15.0 にアップデートしたら直った😅
(Web) フォントのロードを待てなかったとかそういう関係 ?