Tech Racho エンジニアの「?」を「!」に。
  • 開発

【Rails】Prawnで縦書きを実現する

PrawnはRubyで使えるPDF生成ライブラリです。
Railsを使った案件でPDFを作成する場合は、よくPrawnを利用しています。
今回、縦書きに対応する必要があったのですが、Prawnが提供しているメソッドだけだと対応できないので、自作してみました。
基本的な考え方としては、「文字を1文字ずつ区切り縦に並べる」です。
後は自動改行や複数行になった際の中心の調整を対応していきます。

※今回の縦書きメソッドは宛名書き用に作成しています。
文章への対応となると約物等への対応が必要になり、ものすごく大変になります。
今回は長音符のみ90度回転させる方法で対応しています。
UTR50を見るとさらにy軸を中心に180度回転させるのが正しいように見えますが、今回は対応していません。
フォントに縦書き用のグリフがある場合は、それを使って対応することもできます。

今回のバージョン
Rails 3.2.14
prawn 0.12.0

Gemのインストール

gem install prawn
//Gemfileに記述する場合
gem prawn

縦書きメソッドの作成

# -*- coding: utf-8 -*-
require ("#{Rails.root}/lib/module/string_extention.rb")
# PDFドキュメント生成クラスであるPrawn::Documentを継承したクラスを作る
class ExtentionPrawnDocument < Prawn::Document
  YOHAKU = 2 #文字間隔調整の値
  # Options
  # at (required),  [x,y] The position at which to start the text
  # size
  # style
  # kerning boolean
  # center boolean automatically text start index with at
  # height New line when you exceed this value
  def vertical_text(text, options = {}) 
    letters = text.split(//) # 1文字分割して配列にする
    f_size = options[:size] || font_size # font_sizeはPrawn::Document内で定義されているメソッドです。
    options[:at] ||= [0, 0]
    origin_at = options[:at]

    # 自動改行
# height指定されていなければ改行しない。この高さを超えたら改行
    if options[:height] 
      height = options[:height].to_i
      letter_line_height = 0
      letters.each_with_index do |letter, i|
        letter_line_height = 0 and next if letter.breakcode?
        letter_line_height += f_size + YOHAKU
        if letter_line_height > height
          letters.insert(i, "\n")
          letter_line_height = 0
        end
      end
    end

    #改行して複数行になったときに自動で中央を調整するか
    if options[:center]
      line_count = calculate_line_count(letters)
      align_index = (f_size / 2) * (line_count -1)
      options[:at] = [options[:at][0] + align_index, options[:at][1]]
    end
    letters.each do |letter|
 # 改行コードの場合はx座標をずらして次へ
      if letter.breakcode?
        options[:at] = [options[:at][0] - f_size - YOHAKU, origin_at[1]]
        next
      end
# 長音符の場合は例外処理を行う-90度回転させる。ただ回転させるだけだとテキストのベースラインの関係から文字が重なるのでy座標を調整する
      if letter.macron? 
        options[:rotate] = -90
        options[:at] = [options[:at][0], options[:at][1] + f_size]
      else
        options[:rotate] = 0
      end
      draw_text(letter, options) # 文字の描画
      options[:at] = [options[:at][0], options[:at][1] - f_size] if letter.macron? #長音符の場合は例外処理したy座標を元に戻す
      options[:at] = [options[:at][0], options[:at][1] - f_size - YOHAKU] #y座標をフォントサイズ+余白分ずらす
    end
  end

  private
  # 行数を数えるメソッド
  # Args
  # arry: 1文字ごとの配列
  def calculate_line_count(arry)
    line_count = arry.count("\n")
    line_count = arry.count("\r") if line_count == 0
    line_count = arry.count("\r\n") if line_count == 0
    line_count += 1
    return line_count
  end

end

vertical_textのOptionはcenter, height以外はdraw_textで指定されているoptionになります。
draw_textにはその他にrotateがありますが、長音符対応で使用する関係上、
引数に指定されても意味のないものになるので、記載していません。

breakcode?とmacron?はStringクラスを拡張しています。

class String
 # 改行コードか
  def breakcode? 
    self == "\n" || self == "\r" || self == "\r\n"
  end
  # 長音符(ー)か
  def macron?
    self == "ー"
  end
end

縦書きメソッドの呼び出し

pdf = ExtentionPrawnDocument.new({:page_layout => :landscape, :page_size => 'A4', :left_margin => 0, :right_margin => 0, :top_margin => 0, :bottom_margin => 0})
pdf.vertical_text '縦書きサンプルでーす', at: [100, 100]
pdf.render

出力サンプル

フォントは青柳衡山フォントです。
tech


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。