Rails5でPrawnによるPDFを作成する

# はじめに

Rails5でPDFの作成をするのに便利なgemのPrawnの使用方法について、備忘録的にまとめておきます。どなたかの役に立てば幸いです。

## 環境

| Rails            |         5.1.6      |
|:-----------------|:------------------:|

データベースはmysqlを使用


## gemの導入

まずはprawn導入用のgemをGemfileに追記します。

```ruby:Gemfile
gem 'prawn'
gem 'prawn-table'
```
bundle installします。

```terminal:terminal
$ bundle install
```
ローカルサーバー等は再起動してください。

## PDF用のクラスを作成

PDFの内容を作成するための専用クラスを作成します。
Prawnではcontrollerに直接PDFの内容を流し込んでもいいのですが、可読性・保守性ともに下がるため、専用のクラスを作成することがおすすめです。

```terminal:terminal
$ mkdir app/pdfs
$ touch app/pdfs/record_pdf.rb # record_pdf.rbというファイル名で作成、model名はRecordPdfとなる
```

PDF作成用のモデルを作成します。
**Prawn::Document**を継承するようにします。

```ruby:app/pdf/record_pdf.rb
class RecordPdf < Prawn::Document

# recordにモデルなどのデータを渡します
def initialize(record)

  # superで初期設定を指定します(ページサイズ、マージン等)
  super(
    page_size: 'A4',
    top_margin: 40,
    bottom_margin: 30,
    left_margin: 20,
    right_margin: 20
  )

  @record = record # インスタンスを受け取り。コンポーネント作成時などにレコード内のデータを使える

end

end
```

## フォントを指定

Prownはデフォルトでは日本語に対応していないので、日本語用のフォントを用意します。
日本語のフォントは[ここから](https://moji.or.jp/ipafont/ipaex00401/)ダウンロードできます。

ダウンロードしたフォントは**assets/fonts**に入れます。
(ちなみにここ以外から入手したフォントでも動作できます。著作権に注意してください。)

```ruby:app/pdf/record_pdf.rb
class RecordPdf < Prawn::Document

  def initialize(record)

    super(
      page_size: 'A4',
      top_margin: 40,
      bottom_margin: 30,
      left_margin: 20,
      right_margin: 20
    )

    font 'app/assets/fonts/ipaexg.ttf' # fontをパスで指定

    @record = record

  end

end
```

## controllerの作成

controllerを作成します。すでに作成済みのコントローラを介して使用することもできます。

```terminal:terminal
$ rails g controller record_pdfs index # コントローラ作成時にindexページも作成
```

```ruby:app/controllers/record_pdfs_controller.rb
class RecordPdfsController < ApplicationController

  def index
    @records = Record.all # pdf上で使用するレコードのインスタンスを作成
    respond_to do |format|
      format.html
      format.pdf do

        # pdfを新規作成。インスタンスを渡す。
        pdf = RecordPdf.new(@records)

        send_data pdf.render,
          filename:    "sample.pdf",
          type:        "application/pdf",
          disposition: "inline" # 画面に表示。外すとダウンロードされる。
      end
    end
  end

end
```

## PDFの表示

ページへのリンクはルーティングに依存するので、適宜確認して行ってください。
リンクの作成例は以下のようになります。

```erb
<%= link_to "PDFを表示", 適宜指定_path(format: "pdf") %>
```

モデルを編集します。

```ruby:app/pdf/record_pdf.rb
class RecordPdf < Prawn::Document

  def initialize(record)

    super(
      page_size: 'A4',
      top_margin: 40,
      bottom_margin: 30,
      left_margin: 20,
      right_margin: 20
    )

    font 'app/assets/fonts/ipaexg.ttf'

    @record = record
  
    # きちんと日本語も表示されるか確認
    text 'hello こんにちは 春夏秋冬'

  end

end
```

このように表示されればOK
![スクリーンショット 2018-09-06 17.30.58.png](https://qiita-image-store.s3.amazonaws.com/0/132062/36800130-16c8-7ef8-9b93-35eb4a6289d6.png)


## カスタマイズ

Prawnは座標指定でテキスト等の表示位置を決められるため、グリッドを表示させておくと編集が楽。

```ruby:app/pdf/record_pdf.rb
class RecordPdf < Prawn::Document

  def initialize(record)

    super(
      page_size: 'A4',
      top_margin: 40,
      bottom_margin: 30,
      left_margin: 20,
      right_margin: 20
    )

    @record = record

    font 'app/assets/fonts/ipaexg.ttf'

    stroke_axis # これでメモリが表示される
  
    text 'hello こんにちは 春夏秋冬'

  end

end
```

基準となるメモリが表示される

![スクリーンショット 2018-09-06 17.32.41.png](https://qiita-image-store.s3.amazonaws.com/0/132062/d9b273d6-7fbc-30cf-43ed-b488601ad8e9.png)

## PDF内のコンポーネント作成

```ruby:app/pdf/record_pdf.rb
class RecordPdf < Prawn::Document

  def initialize(record)

    super(
      page_size: 'A4',
      top_margin: 40,
      bottom_margin: 30,
      left_margin: 20,
      right_margin: 20
    )

    @record = record

    font 'app/assets/fonts/ipaexg.ttf'

    stroke_axis

    # 下記で作成したコンポーネントを表示順に
    header
    move_down 50
    contents

  end

  # コンポーネント作成
  def header
    text 'PDFのタイトル', size: 50
    move_down 20
    text '作成者氏名', size: 14
  end

  def contents
    text '本文'
    move_down 10
    text 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
          Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
          Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
  end

end

```

表示確認

![スクリーンショット 2018-09-06 17.47.03.png](https://qiita-image-store.s3.amazonaws.com/0/132062/703defd9-a765-6bcd-3feb-46031ecc5916.png)

## インスタンスの利用

コントローラで渡して入れば、データベースから取得したデータをPDF内で利用可能

```ruby:app/controllers/record_pdfs_controller.rb
class RecordPdfsController < ApplicationController

  def index
    @records = Record.all # pdf上で使用するレコードのインスタンスを作成
    respond_to do |format|
      format.html
      format.pdf do

        pdf = RecordPdf.new(@records) # ここで受け取っている

        send_data pdf.render,
          filename:    "sample.pdf",
          type:        "application/pdf",
          disposition: "inline" # 画面に表示。外すとダウンロードされる。
      end
    end
  end

end
```

```ruby:app/pdf/record_pdf.rb
class RecordPdf < Prawn::Document

  def initialize(record) # 受け取ったものがrecordに入っている

    super(
      page_size: 'A4',
      top_margin: 40,
      bottom_margin: 30,
      left_margin: 20,
      right_margin: 20
    )

    @record = record # メソッドで利用できるようにインスタンス化

    font 'app/assets/fonts/ipaexg.ttf'

    stroke_axis

    # 下記で作成したコンポーネントを表示順に
    header
    move_down 50
    contents

  end

  # コンポーネント作成
  def header
    text 'PDFのタイトル', size: 50
    move_down 20
    text '作成者氏名', size: 14
  end

  def contents
    text '本文'
    move_down 10
    text 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
          Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 
          Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 
          Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'

    move_down 50

    text "データの内容は#{@record.data}です" # 例えば、このように利用できる

  end

end

```



以上です。






## 参考

- [RailsでPrawnを使ってPDFを作成する](https://ruby-rails.hatenadiary.com/entry/20140907/1410078997)
- [【Railsエンジニア必見】PDF出力するPrawnを使ってみた](http://www.nowhere.co.jp/blog/archives/20161208-230004.html)
- [Prawn manual](http://prawnpdf.org/manual.pdf)