こんにちは!SmartHRで文書配付機能の開発を担当している、aanzaiです。 2022年末から2023年2月にかけて、文書配付機能で使用しているPDFのレンダリングライブラリの置き換えを行ったため、具体的にどのように移行したかをご紹介します。
文書配付機能の紹介
文書配付機能(旧:雇用契約)は、SmartHRの最初のオプション機能として開発された機能で、事前に作成した書類テンプレートをもとに、SmartHRに保存された従業員情報を差し込んで書類PDFを作成し、従業員に配付したり、契約書として合意を取ったりすることができる機能です。 書類テンプレートのレイアウトは、ユーザーがWYSIWYGエディタで作成したものがHTMLとして保存されています。書類を配付する際は、このレイアウトHTMLに従業員情報を差し込み、PDFに変換します。
PDFレンダリングライブラリ移行の理由
文書配付機能では、昨年まではwkhtmltopdfをWicked PDFというgem経由で使用していました。 wkhtmltopdfは内部的にQtWebkitというレンダリングエンジンを使用しており、そのPDF出力機能を利用して、HTMLをPDFに変換しています。
文書配付機能では、wkhtmltopdfを古いバージョンで運用してきましたが、依存ライブラリの互換性の関係上、サーバー側のOSが更新できない状態となっていました。 OSのサポート期限が迫っているため、一度wkhtmltopdfのアップデートを試みたのですが、一部のレイアウトでテーブルの改行位置が異なるなど、レンダリング結果に差異が発生したため、アップデートを断念しました。
その後もwkhtmltopdfのアップデート方法を検討しましたが、レイアウト結果の差異を解消する手段が見つからなかったことと、レンダリングに利用されているQtWebkitのアップデートが滞っているなどwkhtmltopdf自体の不安要素があったことから、別のライブラリへの移行を検討しました。
(※) wkhtmltopdfは2023年1月にリポジトリがアーカイブされています。 https://wkhtmltopdf.org/status.html によると、wkhtmltopdfが利用していたQtWebkitがメンテナンスされていないことに起因しているようです。
移行先のライブラリについて
先述の通り、文書配付機能ではHTMLで作成したレイアウトをPDFに変換する仕組みのため、ヘッドレスブラウザ等でHTMLをPDFに変換できるライブラリを探しました。 https://speakerdeck.com/morimorihoge/20220125-ling-he-ban-railsapuridepdfsheng-cheng-surutekunitukuji-in-yin-zuo-rails-number-41?slide=28 等を参考に、Puppeteer経由でPDFを出力できる、Groverというgemを採用しました。
元々のシステム構成に加えて、Puppeteer(+Node.js)という依存先が増えてしまうことが懸念点ではありますが(代替案について後述します)、Chromiumを使用してPDFを生成できるため、ブラウザ部分に関してはメンテナンスが保証されていそうな点と、他のライブラリに移行する場合も、ブラウザがChromiumであれば、ユーザーに影響があるようなレンダリング結果の大きな差異がでなさそうというのが大きな理由です。
実際の移行について
PDFのレンダリング結果に問題が出てしまうと、場合によっては文書配付が事実上利用できない状態となってしまうため、リリース時期の検討および安全な移行手段について検討しました。 まず、移行プランとしては二つ案があり、
- 権限システムの変更の際と同様に、テナントごとに切り替えフラグと設定画面を用意し、任意のタイミングで確認し切り替えをお願いする
- 新ライブラリでのレンダリング結果が確認できる画面を用意して事前に確認をお願いし、切り替えは一斉に行う
のいずれかの方針をとることになりました。 その後、Groverでのレンダリング結果を幾つかのテスト用テンプレートで確認した結果、フォントサイズ等の細かい違いはあるものの、懸念していたテーブルや改行周りの処理の違いなどが発生しないことが確認でき、表示崩れのリスクは少ないと判断できたため、二番目の一斉に切り替えるプランで移行を行うことを決定しました。
移行の時期については、年末から1月にかけては年末調整期間であることと、3月は雇用契約などで文書配付機能にとっての繁忙期であることから、2月に決定されました。
元々のコードでは、WickedPdfが直接呼び出される形になっていましたが、Groverに変更後も同じインターフェースを利用できるようにするため、同じインターフェースを持つWickedPdfGenerator
とGroverPdfGenerator
を用意し、元のコードをWickedPdfGenerator
を呼び出す形に置き換えました。
変更前のコード
WickedPdf.new.pdf_from_string(document.to_html, encoding: "UTF-8")
変更後のコード
PdfGenerator::WickedPdfGenerator.new.generate(document.to_html, encoding: "UTF-8")
移行期間完了後は、元のコードでWickedPdfGenerator
を使用していた箇所を、切り替え後のGroverPdfGenerator
が呼び出されるよう変更し、WickedPdfGenerator
は削除しました。
移行検証時に発生した問題とその解消について
移行のためにレンダリング結果を検証していたところ、一部の書類で下記のようなレンダリング結果になってしまう事象が発生していました。
原因として、文書配付機能の無料トライアルでは、以下のようなウォーターマークが書類の背景として表示されるのですが、
文書配付の開発初期に、このウォーターマークに関連する実装で、以下のように transform: rotate(30deg)
が挿入されていたことが原因でした。
html { height: 100%; width: 100%; transform: rotate(30deg); background-image: url("ウォーターマークのURL"); }
開発初期から残っていたCSSですが、wkhtmltopdfでは反映されていなかったため、Groverに移行することで初めて発覚し、修正されました。
Puppeteerの代替案について
今回の移行を検討した際には存在を認識できていなかったのですが、Ferrum(https://github.com/rubycdp/ferrum)というgemが開発されています。FerrumはChrome DevTools ProtocolをRubyから操作することができ、Puppeteerと同じような役割をRubyのみで実現しています。 こちらを利用すると、Puppeteerを挟まずに直接Chromiumを動かしてPDFを出力できるため、RailsアプリケーションなどでHTMLからPDFを生成したい場合は、こちらの利用を検討すると良いと思います。
まとめ
文書配付機能で使用していたPDFレンダリングライブラリを移行することになった経緯と、ライブラリ移行のプロセスについてご紹介しました。 文書配付チームで次の記事を書く機会があれば、PDFのパスワードや電子署名についての解説記事を書きたいと思っています。
参考にしたURL等
- 令和版!RailsアプリでPDF生成するテクニック集 in 銀座Rails#41 | Speacker Deck
- wkhtmltopdfの次どうするか問題 | おもしろwebサービス開発日記
We are hiring!
PDFを扱うプロダクトに自信のある方、 SmartHRではエンジニアを大募集しています! 詳しくは以下のリンクをご覧ください。