読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

Railsで帳票作成ツールのThinReportsを使ってPDFを作成

Rails gem PDF Generation

ThinReportsとは、株式会社マツケイが開発する日本製のRubyで書かれたOSSの帳票作成ツールです。
GPLやMITライセンスのため商用利用が可能です。

ThinReportsEditorという専用のレイアウトエディターでPDFのひな形を作成し、RubyやRailsでそのひな形を読み込み、動的な値を設定し、表示するという流れになっています。

今回は、Railsで ThinReport を使って注文票のPDFを作成する手順を説明します。

f:id:nipe880324:20140907233932p:plain:w480


他にもPDFを作成するGemがあり、「Ruby/RailsのPDF作成Gemまとめ 」でまとめています。

動作確認

  • Mac OS X 10.9.4
  • Ruby 2.1.2
  • Rails 4.1
  • ThinReports Editor 0.7.7.2
  • ThinReports Generator 0.7.7

目次

  1. 準備(注文詳細画面の作成)
  2. ThinReportsの導入
  3. ThinReports で動的データを表示
  4. ThinReports でテーブルを表示
  5. 結論

1. 準備(注文詳細画面の作成)

まず、注文書のPDFを作成するために簡単な注文詳細画面を作成しましょう。
作成する画面は次の画面です。
f:id:nipe880324:20140906204302p:plain:w480

手順はこちらを参照してください。



2. ThinReportsの導入

ThinReports Editor のインストール

ダウンロードページからお使いのOSに合わせたThinReports Editorをダウンロードして、インストールします。

ThinReports Generator のRailsへの導入

まず、「1. 準備(注文詳細画面の作成」で作成したRailsプロジェクトのGemfileに下記を追加します。

# Gemfile
...
# For PDF Generation
gem "thinreports"

ThinReportsをインストールします。

$ bundle install

ThinReports Editor で帳票テンプレートの作成

作成する帳票のイメージを確認するために、Webの注文詳細画面を確認してみましょう。
このような画面をステップバイステップで作っていきます。
f:id:nipe880324:20140906204302p:plain:w480


注文番号と注文日の表示、テーブルの表示がありますが、まずは、"Order"という静的な文字のみを表示しましょう。

ThinReports Editor を起動し、ツールバーの[新規作成]から新しいレイアウトを作成します。
ページタイトルには[order_pdf]、用紙設定に[A4]を設定します。
f:id:nipe880324:20140907233142p:plain:w480

では、ThinReports Editor で帳票のテンプレートを作成していきます。
左側の[A](テキストツール)を選択し、PDFをクリックするとダイアログが表示されます。
ダイアログ内に "Order" と入力し、[OK]ボタンを押します。
f:id:nipe880324:20140907233204p:plain:w480

フォントやサイズ、位置などの設定は、右側の[PROPERTY]でできます。
フォントのサイズや位置などを次の画像の通りに変更してください。
f:id:nipe880324:20140907233214p:plain:w480

左側の矢印マークを選択することで、PDF上に作成したオブジェクトの移動ができます。

次に、左側の[/]を選択し、線を追加します。[Shift]を押しながら作成するとまっすぐの線が引けます。
この横線も[PROPERTY]を次のように変更してください。
f:id:nipe880324:20140907233231p:plain:w480

次に、上のバーから[保存]を押して、"order_pdf" という名前で保存します。
すると、"order_pdf.tif" といるファイルができます。

これを、app/pdfs/ディレクトリを作成し、そこに配置します。

ViewとControllerの修正

ViewにPDFを表示するためのリンクを追加します。

# app/views/orders/show.html.erb
...
# ファイルの一番下に追加する
<%= link_to "PDFで表示",
              order_path(@order, format: "pdf"),
              :class => 'btn btn-primary' %>

Controllerのshowアクションにrespond_toを追加します。

# app/controllers/orders_controller.rb
  ...
  # GET /orders/1
  # GET /orders/1.json
  def show
    respond_to do |format|
      format.html # show.html.erb
      format.pdf do

        # Thin ReportsでPDFを作成
        # 先ほどEditorで作ったtlfファイルを読み込む
        report = ThinReports::Report.new(layout: "#{Rails.root}/app/pdfs/order_pdf.tlf")

        # 1ページ目を開始
        report.start_new_page

        # ブラウザでPDFを表示する
        # disposition: "inline" によりダウンロードではなく表示させている
        send_data
          report.generate,
          filename:    "#{@order.id}.pdf",
          type:        "application/pdf",
          disposition: "inline"
      end
    end
  end
  ...

PDFファイルを表示してみましょう。PDFが表示されました。
この調子で、Editor で帳票を作成し、Railsのプログラム上でPDFを表示していきます。
f:id:nipe880324:20140907233252p:plain:w480



ThinReports で動的データを表示

まずは、PDFを表示することができましたので、次は動的なデータの「注文番号」と「注文日」を表示できるようにしましょう。

Editor で動的データの表示領域を追加

まずは、ThinReports Editor を開き、テキストツール([A]マーク)を使い「注文番号」と「注文日」を追加します。
f:id:nipe880324:20140907233315p:plain:w480
f:id:nipe880324:20140907233318p:plain:w480

次に、動的なデータを表示する領域を追加します。
左側の点線の[A]を選択し、て次のように設定します。
重要な箇所として、[PROPERTY]の[ID]の値です。これを見て、コードから値を設定することができるようになります。
f:id:nipe880324:20140907233337p:plain:w480
f:id:nipe880324:20140907233339p:plain:w480

Controller で値を設定

Controllerで値を設定しましょう。

# app/controllers/orders_controller.rb
  ...
  # GET /orders/1
  # GET /orders/1.json
  def show
    respond_to do |format|
      format.html # show.html.erb
      format.pdf do

        # Thin ReportsでPDFを作成
        # 先ほどEditorで作ったtlfファイルを読み込む
        report = ThinReports::Report.new :layout => "app/pdfs/order_pdf.tlf"

        # 1ページ目を開始
        report.start_new_page

        ### 追加箇所 開始 ###
        # 注文番号と注文日の値を設定
        # itemメソッドでtlfファイルのIDを指定し、
        # valueメソッドで値を設定します
        report.page.item(:order_id).value(@order.id)
        report.page.item(:purchased_at).value(@order.purchased_at)
        ### 追加箇所 終了 ###

        # ブラウザでPDFを表示する
        # disposition: "inline" によりダウンロードではなく表示させている
        send_data report.generate,
                  filename:    "#{@order.id}.pdf",
                  type:        "application/pdf",
                  disposition: "inline"
      end
    end
  end
  ...

では、画面を再表示させましょう。
「注文番号」と「注文日」が表示されています :)
f:id:nipe880324:20140907233359p:plain:w480


4. ThinReports でテーブルを表示

では、表を追加していきましょう。

Editor で表を追加

表を追加するには、左側の枠組みのマーク(f:id:nipe880324:20140907233410p:plain:h20:w20)を使います。
設定内容は次の通りです。
f:id:nipe880324:20140907233426p:plain:w480

作成した表の[header]と[detail]をそれぞれ選択し、[高さ]を"20"に設定します。
f:id:nipe880324:20140907233428p:plain:w480

次に[header]部に[A]でテキストを追加します。
各設定パラメータは次の通りです。

ラベル左位置上位置高さサイズ横位置
#65245501210.5中央揃え
品名125245501210.5中央揃え
単価295245501210.5中央揃え
数量370245501210.5中央揃え
値段455245501210.5中央揃え

f:id:nipe880324:20140907233456p:plain:w480

今度は[detail]部にデータの下線を[/]で追加します。
f:id:nipe880324:20140907233456p:plain:w480

次に[detail]部に点線の[A]で動的なデータ表示領域を追加します。
各設定パラメータは次の通りです。

ID左位置上位置サイズフォント横位置
id852655010.5IPA ゴシック左揃え
product_name14026513010.5IPA ゴシック左揃え
price2752655060IPA ゴシック右揃え
quantity3602654010.5IPA ゴシック右揃え
total_price4152658010.5IPA ゴシック右揃え

f:id:nipe880324:20140907233509p:plain:w480

表にデータを設定する

Controller にテーブルにデータを設定するコードを書いていきたいですが、今後ソースが増えていくと想定されるので、まずは別クラスに切り分けることにします。
app/pdfs/order_pdf.rbを作成し、OrdersControllerのshowアクションからコードをコピーします。
@orderorderに変えることことを見逃さないように注意してください。

# app/pdfs/order_pdf.rb
class OrderPDF

  # Classメソッドを定義
  def self.create order
    # Thin ReportsでPDFを作成
    # 先ほどEditorで作ったtlfファイルを読み込む
    report = ThinReports::Report.new :layout => "app/pdfs/order_pdf.tlf"

    # 1ページ目を開始
    report.start_new_page

    # 注文番号と注文日の値を設定
    # itemメソッドでtlfファイルのIDを指定し、
    # valueメソッドで値を設定します
    report.page.item(:order_id).value(order.id)
    report.page.item(:purchased_at).value(order.purchased_at)

    # ThinReports::Reportを返す
    return report
  end
end

そして、Controller側を書き直します。

# app/controllers/orders_controller.rb
  ...
  # GET /orders/1
  # GET /orders/1.json
  def show
    respond_to do |format|
      format.html # show.html.erb
      format.pdf do

        # order情報を設定したThinReportを作成する
        report = OrderPDF.create @order

        # ブラウザでPDFを表示する
        # disposition: "inline" によりダウンロードではなく表示させている
        send_data report.generate,
                  filename:    "#{@order.id}.pdf",
                  type:        "application/pdf",
                  disposition: "inline"
      end
    end
  end
  ...

では、画面を確認してみましょう。何も変わっていないことが確認できると思います。
f:id:nipe880324:20140907233359p:plain:w480

これでテーブルのソースを追加するベースができたので、テーブルに値を設定するソースを追加していきます。

# app/pdfs/order_pdf.rb
class OrderPDF

  # Classメソッドを定義
  def self.create order
    # Thin ReportsでPDFを作成
    # 先ほどEditorで作ったtlfファイルを読み込む
    report = ThinReports::Report.new :layout => "app/pdfs/order_pdf.tlf"

    # 1ページ目を開始
    report.start_new_page

    # 注文番号と注文日の値を設定
    # itemメソッドでtlfファイルのIDを指定し、
    # valueメソッドで値を設定します
    report.page.item(:order_id).value(order.id)
    report.page.item(:purchased_at).value(order.purchased_at)

    ### 追加部分 開始 ###
    # テーブルの値を設定
    # list に表のIDを設定する(デフォルトのID値: default)
    # add_row で列を追加できる
    # ブロック内のrow.valuesで値を設定する
    order.line_items.each do |item|
      report.list(:default).add_row do |row|
        row.values id:           item.id,
                   product_name: item.product_name,
                   price:        item.price,
                   quantity:     item.quantity,
                   total_price:  item.total_price
      end
    end
    ### 追加部分 終了 ###

    # ThinReports::Reportを返す
    return report
  end
end

では、画面を開き直してみましょう。表が表示されています :)
f:id:nipe880324:20140907233703p:plain:w480

Editor で合計を追加

今度は、合計部分を表示させましょう。
ThinReports Editor を開き、テーブルを選択し、[フッター]にチェックを入れます。
f:id:nipe880324:20140907233735p:plain:w480

その後、テーブル上の[footer]を選択し、[高さ]を"20"にします。
f:id:nipe880324:20140907233747p:plain:w480

そして、[/]で[footer]の上部に横線を追加します。
f:id:nipe880324:20140907233810p:plain:w480

次に、[□]で[footer]の合計部分に色を設定します。
f:id:nipe880324:20140907233825p:plain:w480

"合計" というラベルを表示用に追加します。
通常の[A]で追加すると、上記で作成した色により見えなくなってしまうため、点線の[A]で追加した。
f:id:nipe880324:20140907233902p:plain:w480

注文の合計を表示する領域を追加します。
f:id:nipe880324:20140907233904p:plain:w480

合計にデータを設定する

では、[footer]に追加した動的なデータの表示領域に値を設定します。
あまり直感的でないですが、他の設定方法が分からなかったので、参考文献の見積書サンプルで設定した方法で設定しました。そのため、ソースコードの構成が大きく変わっています。

# app/pdfs/order_pdf.rb
class OrderPDF

  # Classメソッドを定義
  def self.create order

    # Thin ReportsでPDFを作成
    report = ThinReports::Report.create do |r|

      # ThinReports Editorで作成したファイルを読み込む
      r.use_layout "#{Rails.root}/app/pdfs/order_pdf.tlf" do |config|
        # テーブルの[footer]部分の値を設定
        # イベントで設定する方法以外の方法が分からなかった
        config.list(:default) do
          events.on :footer_insert do |e|
            e.section.item(:label).value("合計")
            e.section.item(:total_price).value(order.total_price)
          end
        end
      end

      # 1ページ目を開始
      r.start_new_page

      # 注文番号と注文日の値を設定
      # itemメソッドでtlfファイルのIDを指定し、
      # valueメソッドで値を設定します
      r.page.item(:order_id).value(order.id)
      r.page.item(:purchased_at).value(order.purchased_at)

      # テーブルの値を設定
      # list に表のIDを設定する(デフォルトのID値: default)
      # add_row で列を追加できる
      # ブロック内のrow.valuesで値を設定する
      order.line_items.each do |item|
        r.list(:default).add_row do |row|
          row.values id:           item.id,
                     product_name: item.product_name,
                     price:        item.price,
                     quantity:     item.quantity,
                     total_price:  item.total_price
        end
      end
    end

    # ThinReports::Reportを返す
    return report
  end
end

では、画面を確認しましょう。値が表示されてます。 :D
f:id:nipe880324:20140907233932p:plain:w480


5. 結論

なかなかソースコードは癖がありますが、ThinReports Editor で帳票を作成できるというところは大きなメリットだと思います。
帳票の品質にこだわりたいときは、使ってみる価値があると思います。

もっと細かいことを知りたい場合は、「ThinReports 公式サイト - サンプル集」で勉強してください。

よくわからない箇所や間違いがありましたらコメントをください。