Haru free PDF library を試してみる

はじめに

PDF 作成用のライブラリです。公式 Wiki は下記参照。Ruby のドキュメントはありませんが、ほとんどのメソッドは命名方式を Ruby らしく変更する事でそのまま流用可能です。

http://libharu.org/wiki/Main_Page

困ったことに(ってすごいことなんですが)いろいろな言語や環境で使えるため、カテゴリ分けに苦しみます。まあ、一応私は Ruby から使うのでそういう方向に。

Java にも iText という日本語が扱えるライブラリは存在するのですが、私が Ruby の方が書けるのでこちらも勉強する事にしました。

インストール

必要なファイルは下記アドレスからダウンロードできます。

http://sourceforge.net/projects/libharu/

Mac を使うので、『libharu_2_0_8.tgz』(記事執筆時現在)を使います。詳しいインストール方法については、下記のアドレスが非常に参考になりました。

http://d.hatena.ne.jp/yaakaito/20090115/1232006226

さらに Ruby を使う場合は以下の作業を行います。

cd libharu-X.X.X/if/ruby
ruby extconf.rb
sudo make install

文書を作ってみる

ここからは、実際に PDF を作りつつ Ruby から操作する場合の基本を勉強してみます。先ほどの libharu-X.X.X/if/ruby ディレクトリ内にデモが入っているため、そこを眺めつつ試してみました。

ライブラリの呼び出し
include "hpdf"

当たり前で申し訳ないのですが、これを忘れるとどうにもならないので。

PDF ドキュメントの初期化

ライブラリで使う PDF ドキュメントは HPDFDoc クラスとして扱われます。

pdf = HPDFDoc.new
ページの追加

PDF はページを分ける事もできるので、実際に何かを書きこむ場合にはページに対して処理を行います。

page = pdf.add_page

これで PDF ドキュメントにページを追加して、そのページを page に渡すことができました。

ページサイズの指定

ページサイズとは A4 など、実際の文書で使われるサイズの事です。

page.set_size(HPDFDoc::HPDF_SIZE_A5, HPDFDoc::HPDF_PAGE_PORTRAIT)

引数はひとつめがサイズ、ふたつ目が縦横の指定。Ruby 向けの解説は見つかりませんでしたが、定数は共通のようなのでおそらくこの辺が指定できるのではないかと。

  • HPDF_PAGE_SIZE_LETTER
  • HPDF_PAGE_SIZE_LEGAL
  • HPDF_PAGE_SIZE_A3
  • HPDF_PAGE_SIZE_A4
  • HPDF_PAGE_SIZE_A5
  • HPDF_PAGE_SIZE_B4
  • HPDF_PAGE_SIZE_B5
  • HPDF_PAGE_SIZE_EXECUTIVE
  • HPDF_PAGE_SIZE_US4x6
  • HPDF_PAGE_SIZE_US4x8
  • HPDF_PAGE_SIZE_US5x7
  • HPDF_PAGE_SIZE_COMM10

縦横はこんな感じ。

  • HPDF_PAGE_PORTRAIT(縦)
  • HPDF_PAGE_LANDSCAPE(横)
座標系の注意

座標は数学のそれよろしく、左下を原点として右上に進むほど値が増えていきます。他のケースでもこうなっている事が多いので、慣れておくといいと思います。

色の指定

テキストや図などを塗る色の指定。YMCK などいろいろな形式が使えますが、最も一般的な RGB 形式について説明します。他の色指定については公式 Wiki を参照してください。

page.set_rgb_fill(1, 0, 1)
page.set_rgb_stroke(0, 1, 0)

それぞれ、塗りつぶしと線の色を RGB で指定します。値は0〜1を使用。文字に使う場合はレンダリングモード(後述)によって塗り、線の色が変更されます。

テキストの追加

テキストを書きだします。

まずはフォントと文字サイズの指定から。日本語は後回しにします。このライブラリでは以下のフォントが扱えるようです(公式 Wiki から転載)。

Type of Font Description
Base14 Font The built-in font of PDF. Can be used for all viewer applications.
Type1 Font A font format used by PostScript.
TrueType Font Widely used outline font format.
CID Font Font format for multi-byte characters. Developed by Adobe.

とりえず built-in らしいフォントを使ってみます。

font = pdf.get_font("Helvetica", nil)

二つ目の引数はエンコーディング指定なので、ここでは無視。

フォントが決まったところで、テキストを追加します。テキスト編集は開始、終了用のメソッドに挟まれます。以下、明記しない限りこの間で行っているものとします。

page.begin_text
# 何か
page.end_text

まずは先ほど設定したフォントと文字サイズを指定します。

page.set_font_and_size(font, 10)

このメソッドでフォントとサイズを一気に指定できます。

続いて、テキストを出力してみます。まずは領域を気にせずに出力。

page.text_out(100, 100, "test for output PDF")

引数は左端からの距離、下端からの距離、出力されるテキストになります。ページサイズの限界に達しようと、ひたすらテキストを出力されてしまいます。

続いて、領域を指定してのテキスト出力。

page.text_rect(100, 100, 200, 50, "test for output PDF", HPDFDoc::HPDF_TALIGN_LEFT)

引数は左端、上端、右端、下端の位置、出力されるテキスト、テキストの行寄せの指定です。前4つで領域を指定して、その中にテキストを出力します。もちろん、十分な領域を確保しないときちんと出力されません(文字が消えるようです)。

なお、行寄せの指定は以下の定数を用います。

定数 方向
HPDF_TALIGN_LEFT
HPDF_TALIGN_RIGHT
HPDF_TALIGN_CENTER 中央
HPDF_TALIGN_JUSTIFY 両端

最後に、テキストを1文字ずつ出力する方法。縦にテキストを配置したいなど、特殊なケースではこれを使わざるを得ないと思います。

text = "test for Output PDF"
text.length.times do |i|
  page.show_text(text[i, 1])
end

ここでテキストが出力される位置は『ページの現在位置』になるのですが、これを指定するメソッドも別途存在します。まとめて解説します。

# 指定の絶対座標へ現在位置を変更(x座標, y座標)
page.move_text_pos(100, 100)

# 現在位置から見た相対位置で座標を変更(x座標, y座標)
page.move_text_pos2(-50, 100)

# 垂直方向に現在位置を変更
page.set_text_rise(10)

# マトリックスを使って現在位置を指定(申し訳ありませんが意味不明です)
rad = 45 / 180 * 3.141592
page.set_text_matrix(Math.cos(rad), Math.sin(rad), -Math.sin(rad), Math.cos(rad), x, y)

最後に、よく使うであろうテキスト関連のメソッドを紹介。

# 行の間隔(line spacing)を調整、デフォルト値は「0」
page.set_text_leading(10)

# 水平方向のスケーリングを指定、デフォルト値は「100」
page.set_horizontal_scalling(110)

# テキストを改行して出力
page.show_text_next_line("a")

# 単語の間隔を調整、デフォルト値は「0」
page.set_word_space(1)

# さらに単語および文字間隔を設定、どちらもデフォルト値は「0」
page.show_text_next_line_ex(1, 1, "a")

# テキストのレンダリングモードを指定
page.set_text_rendering_mode(HPDFDoc::HPDF_FILL_THEN_STROKE)

レンダリングモードについては、以下の定数が使えます。

  • HPDF_FILL
  • HPDF_STROKE
  • HPDF_FILL_THEN_STROKE
  • HPDF_INVISIBLE
  • HPDF_FILL_CLIPPING
  • HPDF_STROKE_CLIPPING
  • HPDF_FILL_STROKE_CLIPPING
  • HPDF_CLIPPING

実際の見た目については公式 Wiki を参照。

http://libharu.org/wiki/Documentation/API/Graphics#HPDF_Page_SetTextRenderingMode.28.29

傾きの指定

「かぶき」ではなく「かたむき」。文字や(まだ説明していませんが)線、円などを傾ける時には、ページそのもので傾きを制御することができます。

実際の制御の前に、ページが持っているグラフィック情報(ここでは傾きに関する情報)そのものを保存、解放する方法について。

page.gsave
# 操作
page.grestore

page.gsave で現在のグラフィック情報をスタックに保存し grestore で再度その情報を用いる事ができます。特に初期状態のグラフィック情報を保存しておくのは便利でしょう。

では、傾きを操作します。

rad1 = 45 / 180 * 3.141592
x = y = 100
page.concat(page, Math.cos(rad1), Math.sin(rad1), -Math.sin(rad1), Math.cos(rad1), 220, 350)

このように concat メソッドを用いる事で、傾きを制御できます。このときの傾け方で工夫もできるようなのですが、私は数学は分からないので解説できません。上の例は公式 Wiki から持ってきた45度傾ける方法だそうなので、数学のわかる方なら応用可能だろうと思います。

線と図形

線や図形を扱うメソッドはいろいろと存在します。詳しいメソッドの解説は公式 Wiki を参照してもらうとして、基本的な部分だけ解説します。

まず、線や図は path(適当な訳を思いつかないので原文そのまま)という概念で描画します。これは「ある点からある点に線を引いた」や、「ある点を中心にある半径の円を描いた」といった情報を数値化したもので、最終的に描画される際には stroke 系のメソッドを用いる必要があります。
stroke 系のメソッドにも種類があります(API 参照)が、とにかくそれらのメソッドを使わないと描画が行われないことは覚えておいてください。以下、最後には stroke を使う前提で話を進めます。

page.stroke

次に path の考え方を、線の描画を使って説明します。

まず、描画を開始するポイントを move_to で指定します。

page.move_to(200, 200)

引数はそれぞれx, y座標です。これで、ある path の開始位置が x = 200, y = 200 に指定されました。

続いて、そこから x = 100, y = 200 に向かって線を引きます。線を引くには line_to メソッドを用います。

page.move_to(200, 200)
page.line_to(100, 200)

line_to も引数はx, y座標です。また、線を引いた後の現在位置は line_to で指定した位置に変更されます。

ここからさらに x = 100, y = 100 に向かって線を引いてみます。

page.move_to(200, 200)
page.line_to(100, 200)
page.line_to(100, 100)

このように線が引けました。最後に、 path で指定された領域を閉じてみます。close_path メソッドを用います。

page.move_to(200, 200)
page.line_to(100, 200)
page.line_to(100, 100)
page.close_path

図では分かりにくいかもしれませんが、path の現在位置から開始位置に向かって直線が引かれます。

線はこれくらいにして、図形を描画しましょう。直線を使う図は線だけで簡単に描けるので、楕円を描画してみます。引数は中心のx, y座標、x軸方向の半径、y軸方向の半径です。

page.move_to(200, 200)
page.line_to(100, 200)
page.line_to(100, 100)
page.close_path
page.ellipse(150, 150, 50, 60)

この他にも曲線や孤など、様々な図形を描画する事ができます。必要であれば API から確認してみてください。

最後に、線や図形の描画時に線の太さ等を変更する方法をいくつかまとめておきます。

# 線の太さを変更
page.set_line_width(5)

# 線の先端部分の描画形式を変更
page.set_line_cap(HPDFDoc::HPDF_ROUND_END)

# 線の結合部分の描画形式を変更
page.set_line_join(HPDFDoc::HPDF_ROUND_JOIN)

先端及び結合部分の定数及びサンプルは、下記アドレスから。

ただ、私がデモを操作した感じ、結合部分はどの形式でも変化が見られませんでした。線の太さの問題なのか、環境の問題なのかは不明。

この他にも破線を使ったり、塗りの指定を非常に細かく行ったりする事もできます。そのあたりも API を参照して試してみてください。

PDF の保存

出来上がった PDF は保存しなくては意味がありません。保存用のメソッドは save_to_file を用います。作成されたファイルは、スクリプトを呼び出したディレクトリに引数に指定した名称で保存されます。

pdf.save_to_file("test.pdf")

なお C の場合の API には HPDF_SaveToStream() というメソッドも紹介されているのですが save_to_stream という Ruby 用のメソッドは存在しないようです。

基本はこのあたりまで。別記事で日本語の扱い等、さらに実践的な内容に踏み込んでいきます。