Rails(Ruby)でPDFを作成する方法 基礎編

投稿日時:
Ruby

はじめに


今回はRailsでPDFを作成する方法について記載します。


上記の方法はググると記事が色々出てきますが、私は実際に仕事でRailsでPDFを商品として開発した経験があるので、

今回は実践的な方法を記載していこうと思います。


少し長くなりそうなので、2−3の記事に分けて書きます。

ざっと以下のような章立てを考えています。


  1. 基礎編
  2. 応用編
  3. グラフ作成編

今回は基礎編です。



Prawn


今回使用するGemはPrawnというGemです。

Githubはこちら。


RailsでPDFを作成する際に用いるGemの中ではもっともポピュラーなものだと思われます。

このGemを使用することでRubyのみでPDFの作成を完結することができます。



準備


新規Railsプロジェクトを作成し、Gemfileprawnを記述して、bundle installします。


rails new rails-pdf -d mysql

# Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.0'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.2', '>= 6.0.2.1'
# Use mysql as the database for Active Record
gem 'mysql2', '>= 0.4.4'
# Use Puma as the app server
gem 'puma', '~> 4.1'

------省略------

gem 'prawn'
bundle config path vendor/bundle
bundle install

DBも作っておきます。


be rake db:create

rails serverを起動しておきましょう。


bundle exec rails s



PDF確認用のControllerの作成


毎度毎度PDFを吐き出して、開いて確認するのは面倒なのでひとまずブラウザで確認できるようにしておきます。

ここでは準備としてControllerの作成とroutingの追加のみしておきます。


# app/controllers/basic_pdf_controller.rb
class BasicPdfController < ApplicationController
  def index
  end
end

# config/routes.rb
Rails.application.routes.draw do
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  resources :basic_pdf, only: :index
end

ブラウザでlocalhost:3000/basic_pdfにアクセスし、Missing templateエラーが出れば、OKです。



PDFを作成し、ブラウザで確認する

PDFを作成するclassを作っていきます。

今回はlib/配下に作成していきます。

ディレクトリ構成は以下のような感じです。

lib
├── assets
├── pdf
│   ├── practice_pdf
│   │   └── basic_pdf.rb
│   └── practice_pdf.rb
└── tasks

ここで、lib/pdfはRailsでauto loadされないため、ロードの設定をconfig/application.rbに追加しておきます。


# config/application.rb
require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module RailsPdf
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.

    # これを追加
    config.eager_load_paths += %W(#{Rails.root}/lib/pdf)
  end
end

追加したらディレクトリ作成をやっていきます。

一度rails serverを再起動しないとうまくauto loadしてくれないかもしれません。


ディレクトリを作成し、ファイルを作ります。


# lib/pdf/practice_pdf.rb
module PracticePdf
end

# lib/pdf/practice_pdf/basic_pdf.rb
module PracticePdf
  class BasicPdf < Prawn::Document
    def initialize
      # 新規PDF作成
      super(page_size: 'A4')
      # 座標を表示
      stroke_axis
    end
  end
end

lib/pdf/practice_pdf/basic_pdf.rbがPDF作成のメインのファイルです。

そのためこのファイルにPrawn::Documentを継承させます。


BasicPdfinitializesuper()を実行していますが、これによりPrawn::Documentinitializeが実行され、白紙のPDFが作成されます。

Prawn::Documentinitializeに設定できるoptionはいくつかありますが、ここでは説明は省きます。

詳細はソースを見てください。


stroke_axisメソッドはPDFに座標軸を表示するメソッドです。

開発時は座標軸を参考に、文字や図の配置場所を決めます。


ここまでの設定でPDFを作成する準備は整いました。

あとは先ほど作成したBasicPdfControllerに設定を追加して、ブラウザで確認できるようにします。


# app/controllers/basic_pdf_controller.rb
class BasicPdfController < ApplicationController
  def index
    respond_to do |format|
      format.pdf do
        basic_pdf = PracticePdf::BasicPdf.new().render
        send_data basic_pdf,
          filename: 'basic_pdf.pdf',
          type: 'application/pdf',
          disposition: 'inline'
      end
    end
  end
end

ブラウザでlocalhost:3000/basic_pdf.pdfにアクセスし、座標軸付きの白紙のPDFが表示されればOKです。



文字の描画


ではまずは文字をPDFに描画していくことから始めます。

実はPrawnにはいくつか文字を描画するメソッドがありますが、私はtext_boxしか使いません。


text_boxは指定の場所に文字列を表示するだけのメソッドです。

borderをつけたりはできません。

しかし、細かいデザインの作成を行う場合このtext_boxのみを使うことが最もシンプルで簡単だと思ったため私は文字の描画にはこのメソッドのみを使っています。

borderなどはまた別の線を引くメソッドを用いて実現しています。


では実際に文字を描画します。

text_boxの使い方は以下です。

text_box 'テキスト', size: 12, at: [0, 600], width: 520, height: 12, align: :center, valign: :center

  • 'テキスト'の部分が表示したい文字列
  • sizeが文字の大きさ
  • atが描画する座標軸の[横軸, 縦軸]
  • widthが描画用に確保する幅
  • heightが高さ
  • alignは描画用に確保した幅内での横軸の描画方法
  • valignは描画用に確保した幅内での縦軸の描画方法

では簡単な表紙を作ってみます。


# lib/pdf/practice_pdf/basic_pdf.rb
module PracticePdf
  class BasicPdf < Prawn::Document
    def initialize
      # 新規PDF作成
      super(page_size: 'A4')
      # 座標を表示
      stroke_axis

      cover
    end

    def cover
      text_box 'サブタイトル', size: 12, at: [0, 600], width: 520, height: 12, align: :center

      text_box 'タイトル', size: 24, at: [0, 500], width: 520, height: 24, align: :center

      text_box 'kuzugr', size: 12, at: [0, 12], width: 520, height: 12, align: :right
    end
  end
end

新たにcoverメソッドを追加しました。


これで再度ブラウザでlocalhost:3000/basic_pdf.pdfにアクセスにしてみます。

すると、rawn::Errors::IncompatibleStringEncodingのようなエラーが出ました。


これはfontを指定してやることで解決できます。

今回はこちらのfontを使用します。


app/assets/fonts/SourceHanSans-Bold.ttfSourceHanSans-Regular.ttfをコピーし、moduleにpathを記述します。


# lib/pdf/practice_pdf.rb
module PracticePdf
  # フォント
  FONT = 'app/assets/fonts/SourceHanSans-Regular.ttf'
  FONT_BOLD = 'app/assets/fonts/SourceHanSans-Bold.ttf'
end

そしてPDF作成部にフォントを使用するよう記述します。

fontメソッドの引数に使用するフォントのパスを渡すことでフォントを使用できます。


# lib/pdf/practice_pdf/basic_pdf.rb
module PracticePdf
  class BasicPdf < Prawn::Document
    def initialize
      # 新規PDF作成
      super(page_size: 'A4')
      # 座標を表示
      stroke_axis

      cover
    end

    def cover
      font FONT
      text_box 'サブタイトル', size: 12, at: [0, 600], width: 520, height: 12, align: :center

      font FONT_BOLD
      text_box 'タイトル', size: 24, at: [0, 500], width: 520, height: 24, align: :center

      font FONT
      text_box 'kuzugr', size: 12, at: [0, 12], width: 520, height: 12, align: :right
    end
  end
end

<br<

再度ブラウザでlocalhost:3000/basic_pdf.pdfにアクセスにしてみると以下のようなPDFが表示されているはずです。

image description


なんとなくtext_boxfontの使い方は掴めたでしょうか?

ちなみにfontメソッドは一度呼び出すとそれ以降全てがそのフォントになってしまうため、元のフォントに戻す場合は再度fontメソッドを使用する場合があります。


また、text_boxのワンポイントテクニックとして、heightをsizeと同じ大きさにすることによって高さがぴったりになるので、valignを気にする必要がなくなります。



長くなったので基礎編の続きは次の記事に書きます。

次回は文字色の変更、線の描画、画像の貼り付けなどを紹介します。