55
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PORTAdvent Calendar 2018

Day 13

Rubyでpdfを生成する

Last updated at Posted at 2018-11-30

やりたいこと

履歴書作成機能を作成したい。
でもExcelとかでテンプレ作って埋め込むの辛い。

理想

フォーム入力すると完成された履歴書のpdfが吐き出されるようにしたい。


RubyでPDF生成する方法

  • wicked_pdf
    • レンダリングしたviewをそのままpdfにできる
    • cssでスタイルを調整できる
  • prawn
    • rubyのコードをゴリゴリ書いてpdf作る
    • なんか座標指定したりしてprocessingで遊んでる気分だった

wicked_pdf

htmlからpdfの変換は実はwkhtmltopdf-binayというgemが担当している
wicked_pdfはそれをラップしてrubyから使えるようにしているらしい

gem install

gem 'wkhtmltopdf-binary'
gem 'wicked_pdf'

wickedがwkhtmltopdfを使えるようにする

wicked_pdf.rb
WickedPdf.config = {
  exe_path: "#{Gem.loaded_specs['wkhtmltopdf-binary-aml'].full_gem_path}/bin/wkhtmltopdf"
}

適当にルーティング

routes.rb
get 'sample_pdfs/wicked', to: 'sample_pdfs#wicked'

適当にコントローラ作成

sample_pdfs_controller.rb
def wicked
  respond_to do |format|
    # とりあえずデバッグモードで動かす
    format.html { redirect_to action: :show, format: :pdf, debug: true }
    format.pdf do
      render layout: 'mypage/sample_pdfs',
             pdf: 'sample',
             template: 'sample_pdfs/show.html',
             disposition: 'inline',
             page_size: 'A4',
             show_as_html: params.key?('debug')
    end
  end
end

適当にview作成

wicked.html.slim
h1.title style="text-align: center; font-size: 100px;" wicked
p.date style="text-align: center; font-size: 50px;" = "#{l(DateTime.current)}"

table style="text-align: left; margin: auto; font-size: 50px;"
  thead
    tr
      th 名前
      th 年齢
      th 性別
      th 住所
  tbody
    tr
      td 井上
      td 22
      td td xxxx xxxx xxxx

div style="height: 500px; width: 500px; border: solid 1px red;"

出力

スクリーンショット 2018-11-28 11.13.52.png

styleを直書きしているが、もちろんcssファイルを別にすることは可能
また画像を貼り付けたりも可能

ただし、imageタグやlinkタグなどをwicked_pdf独自のタグに書き換えなければならない

# js
wicked_pdf_javascript_include_tag "filename"
# css
wicked_pdf_stylesheet_link_tag "filename"
# image
wicked_pdf_image_tag 'filename'

正直これがかなりめんどくさい。
app/assetsの中しか読めないので、cssとかjsをassets:precompileとかして出力したのをassetsで読んでいる場合はかなりめんどくさい


prawn

prawnprawn-tableをインストールする
prawn-tableは表組みの作成に必要

gem 'prawn'
gem 'prawn-table'

app以下にprawnディレクトリを作成してその中にpdf出力用のクラスを作る

sample_pdf.rb
class SamplePdf < Prawn::Document
  def initialize
    super()
    # 座標を表示
    stroke_axis
  end
end

適当にルーティングきって適当にcontroller作って適当にviwe作成

sample_pdfs_controller.rb
def prawn
  respond_to do |format|
    format.html
    format.pdf do
      # PDF文書を作成
      pdf = SamplePdf.new

      # 画面にPDFを表示する
      # disposition: "inline" によりPDFはダウンロードではなく画面に表示される
      send_data pdf.render,
        filename:    "sample.pdf",
        type:        "application/pdf",
        disposition: "inline"
    end
  end
end

座標が表示されたpdf出力される

履歴書っぽい雛形を作ってみる

sample_pdf.rb
class SamplePdf < Prawn::Document
  def initialize
    super()
    # 座標を表示
    stroke_axis
    font 'app/assets/fonts/ipaexm.ttf'
    move_down 10
    create_title
    create_profile
    create_address
  end

  def create_title
    table([
      [
        make_cell(content: '履歴書', width: 200, align: :left, size: 25),
        make_cell(content: I18n.l(Date.current, format: :long), width: 200, align: :right, size: 10, valign: :bottom)
      ]
    ], width:400) do
      row(0).borders = []
    end
  end

  def create_profile
    subtable = make_table([
      [
        make_cell(content: '生年月日', width: 70),
        make_cell(content: 'xxxx年 x月 xx日', width: 200, align: :center),
        make_cell(content: '性別', width: 50, align: :center),
        make_cell(content: '男', width: 80, align: :center)
      ]
    ], width: 400)

    kana_box = [
      make_cell(content: 'かな', width: 50),
      make_cell(content: 'xx xxxx', width: 350, align: :center)
    ]

    name_box = [
      make_cell(content: '氏名', width: 50),
      make_cell(content: 'xx xxx', width: 350, height: 40, align: :center, valign: :center, size: 20)
    ]

    data = [
      kana_box,
      name_box,
      [{ content: subtable, colspan: 2 }]
    ]

    table(data, position: :left, width: 400) do
      row(0).column(0).borders = [:top, :left]
      row(0).column(1).borders = [:right, :top]
      row(1).column(0).borders = [:top, :left]
      row(1).column(1).borders = [:top, :right]
      row(2).column(0).borders = [:top, :left, :right]
      row(1).border_lines = [:dashed, :solid, :solid, :solid]
    end
  end

  def create_address
    address_kana_box = [
      make_cell(content: 'かな', width: 50),
      make_cell(content: 'とうきょうとなかのくやよいちょう', width: 450, align: :center)
    ]

    address_box = [
      make_cell(content: '現住所', width: 50),
      make_cell(content: 'xxxxxxxxxxxxxxxxxx', width: 450, align: :center, size: 15)
    ]

    table([
      address_kana_box,
      address_box
    ], width: 500) do
      row(0).column(0).borders = [:top, :left]
      row(0).column(1).borders = [:right, :top]
      row(1).column(0).borders = [:top, :left, :bottom]
      row(1).column(1).borders = [:top, :right, :bottom]
      row(1).border_lines = [:dashed, :solid, :solid, :solid]
    end
  end
end

結果

image.png

途中までだけど履歴書っぽい


比べてみて

wickedはレンダリングされたhtmlをそのままpdfにできるので通常ならこっちの方が柔軟にpdfを作成できそう
prawnはコードは長くなるが、表や画像, テキストなどを任意の座標に置けるので、履歴書などフォーマットの決まったものなら、こっちでも十分作れそう

おまけ

履歴書サンプル完成しました。
スクリーンショット 2018-12-06 11.52.32.png

スクリーンショット 2018-12-06 11.52.40.png

募集

最後になりますが、PORT株式会社では自社サービスを支えてくれる優秀なエンジニアを募集しています。
ぜひprawnで履歴書を作成して応募いただけると幸いです。
話を聞いてみたい、会社を見てみたいといった方は弊社で開催している勉強会にお越しいただけるとイメージが湧きやすいと思いますので、そちらもぜひよろしくお願いします。

もくもく会ページ
https://freestyle-mokumoku.connpass.com/

酒がないとページ
https://sakeganaito.connpass.com/

55
47
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
47

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?