RailsのPDFライブラリをReactを活用して出力する(PrawnによるPDF出力の実装ガイド)
目次[非表示]
- 1.はじめに
- 2.Prawnとは
- 3.事前準備
- 4.ReactでPDFを出力する上での問題点
- 5.Prawnを別言語上でPDF出力する方法
- 6.まとめ
- 7.お悩みご相談ください
- 8.参考資料
はじめに
DIVXでエンジニアをしている松野と申します。
今回の記事では、Railsライブラリの一つであるPrawnを用いて、PDFを異なる言語を介して出力する方法について説明したいと思います。
このテーマを選んだ理由としては、過去の案件で2言語を使用したPDF出力機能の実装に非常に苦労した経験があるためです。
ただ解説するだけでは理解が難しいと思うため、実際にフロントエンドをReact、バックエンドをRailsのAPIを使用した簡単なページを例にPrawnで描写したPDFファイルをフロントから実際に出力させてみたいと思います。
Prawnとは
そもそもPrawnというものが何かわからない!という方ももちろんいらっしゃると思います。
PrawnはRuby用のPDF生成ライブラリで、Railsだけでなく、Rubyのアプリケーションから直接PDFドキュメントを作成できます。
RailsにはPDFkitやWicked PDFといったPDF生成ライブラリがありますが、これらはHTML画面をPDFに変換して出力します。一方、PrawnはRubyのコードを使用して直接PDFを描画し出力するため、Rubyに慣れている方であれば、複雑なPDFを簡単に作成できるメリットがあります。
事前準備
まずはReactのアプリを作成し、PDFを出力するボタンとボタンを押下した際に発生するイベントを作成します。
pdf.tsx
pdfContainer.tsx
次に、Gemfileにprawnを記述し、Gemをインストールします。
prawnがインストールされたことを確認したら、まずはコントローラ部分を記述していきます。
pdf_controller.rb
次にPrawn::Documentを継承したクラスを作成します。
pdf.rb
これらのファイルを作成し、ReactとRailsを経由するAPIを作成することで準備完了です。
ReactでPDFを出力する上での問題点
事前準備を行っただけではPrawnで描写したPDFファイルがダウンロードされるわけではありません。このままReactで記述したPDF出力ボタンを押下すると、APIを経由してPrawnで描写したPDFファイルは返却されますが、この場合、問題が2点発生します。
1点目は、PDFファイルが正しく生成されている場合、APIからPDFデータが返却されますが、Reactでそのデータをダウンロードするための処理が必要な点です。
まず、PDFファイルを出力するには、Railsのコントローラーでpdf_issuanceアクションを実行する必要があります。このアクションでは、RailsからのレスポンスとしてPDFデータを用意し、Content-Dispositionヘッダーに「attachment」を設定することで、ブラウザがファイルを直接表示するのではなく、ユーザーにダウンロードさせるようにしてからPDFデータを返すようにしています。
しかし、ReactではaxiosというHTTP通信を行うライブラリを使用しており、axiosからAPIにアクセスしてPDFデータを取得するため、axiosのヘッダにはContent-Dispositionの情報はなく、React側でPDFデータを処理する際にその情報が利用できないため、データだけ返却される状態になってしまいます。
つまりRailsで設定したContent-DispositionヘッダーはReactへのレスポンスには反映されていないため、PDFデータは返却されますが、React側での適切な処理がないと、ユーザーがダウンロードできる形で表示されません。
したがって、ReactでPDFデータを出力する処理を記述する必要があります。
2点目は、ブラウザによってPDFの表示方法が異なる場合があり、特に<a>タグのdownload属性がすべてのブラウザでサポートされているわけではない点です。
この問題の原因は、PDFデータのリンク作成時に指定する<a>タグのダウンロード属性がブラウザによってサポートされていないことにあります。
ReactでPDFを出力・ダウンロードする際には、PDFデータをBlobとして生成し、それをURL.createObjectURLを用いてBlob URLを作成し、PDFダウンロード用のリンクを作成する必要があります。この際にHTMLのダウンロード属性を追加しますが、この属性は主要なモダンブラウザでサポートされていますが、特定のバージョンや設定によっては異なる動作をすることがあります。スマートフォン版の一部のブラウザでは動作が異なる場合がありますが、主要なブラウザではダウンロード属性はサポートされています。
一部のブラウザではダウンロード属性の挙動が異なる可能性があります。Safariでは、PDFファイルが新しいタブで表示されることが一般的ですが、ユーザーの設定やバージョンによって異なる場合があります。出力方法を統一するためには、各ブラウザの挙動を考慮した実装が必要ですが、ダウンロード属性自体の利用が非推奨であるわけではありません。
Prawnを別言語上でPDF出力する方法
以上の注意点を踏まえてReactでPDFを出力してみようと思います。
前述の通り、prawnで描写したPDFファイルを別言語上で出力する場合、返却されたPDFデータをblobオブジェクトにする必要があります。
APIから返却されたデータはpdfContainer.tsxにおけるresult.dataに格納されているため、こちらをblobオブジェクトにしたあと、PDFダウンロード用のリンクを作成します。
pdfContainer.tsx
次にそれぞれのブラウザごとに出力方法を条件分岐するようにします。
今回はReactライブラリであるreact-device-detectを利用して判別していきたいと思います。
react-device-detectは、ユーザーエージェントを検出し、特定のデバイスやブラウザに基づいて条件分岐を行うためのライブラリです。Webサイトが、ユーザーエージェントによって異なる内容を表示したり、 特定のユーザーエージェントにのみ内容を表示する特徴を活かして、ブラウザのユーザーエージェント文字列を解析・特定を行ってくれます。
ではまず、react-device-detectをインストールします。
インストール後、Reactのコンテナ部分の処理にreact-device-detectをインポートします。
今回はSafari、Chrome、Edgeごとに処理を分けるようにします。
この記述でリンクURLを作成することでダウンロード属性がサポートされているChromeではPDFファイルをダウンロードすることができます。
SafariやEdgeでは、Blobで作成したURLを開くことでPDFを表示できますが、ダウンロードする際にはブラウザの設定によって異なる挙動を示すことがあります。必要に応じて、ユーザーに手動で保存を促すことができます。
この実装で、PDFデータを各ブラウザごとに適切に処理することが可能になりますが、ブラウザによってはユーザーの操作が必要な場合があります。
まとめ
今回はRailsのPDFライブラリであるPrawnを使用して、ReactアプリケーションからPDFを出力する方法を記事にしてみました。
Prawnは別言語で出力すると一手間かかってしまうものの、各ブラウザの挙動とRailsを知っていれば簡単にPDFファイルを描写して出力することができるため、
少しでも皆様のためになる記事になっていたら幸いです。最後まで御覧いただきありがとうございました。
お悩みご相談ください
参考資料
【Rails】PDF出力機能を実装できるGem「Prawn」の使い方と実例(出力サンプル)をご紹介