基本図形の描画と表の作成 - PDF::API2で帳票作成
PDF::API2で、表を作成する方法を解説します。
PDF::API2には、直線や塗りつぶし可能な円、三角形、四角形、それ以上の多角形を作成できる機能があります。
直線を書くことができれば、それを組み合わせて表を作成することができます。表に背景色を設定したい場合は、塗りつぶし可能な四角形を書くことで実現できます。
HTMLのテーブルは表を作成するのに使えます。ここでは、以下の構造をPDFの表として表現できるようにしてみましょう。
<table> <tr><td>見出し1</td><td>見出し2</td><td>見出し3</td></tr> <tr><td>項目</td><td>項目</td><td>項目</td></tr> <tr><td>項目</td><td>項目</td><td>項目</td></tr> </table>
見出し1 | 見出し2 | 見出し3 |
項目 | 項目 | 項目 |
項目 | 項目 | 項目 |
基本的な図形を描画する
まず最初に、表を作成するための基礎として、PDF::API2で基本的な図形を描画してみましょう。
グラフィック用のコンテンツオブジェクトを生成
まず最初に、グラフィック用のコンテンツオブジェクトを生成します。ページオブジェクトからgfxメソッドを呼び出すことで、グラフィック用のコンテンツオブジェクトを生成できます。これはPDF::API2::Contentオブジェクトです。
# PDFオブジェクトの生成 my $pdf = PDF::API2->new; # ページの作成 my $page = $pdf->page; # グラフィック用のコンテンツオブジェクトの生成 my $gfx = $page->gfx;
線を書く
最も基本的な描画として、PDF::API2で線を書いてみましょう。
moveメソッドで、新しいパスが作成され、始点に移動します。lineメソッドで、パスを終点まで移動します。
まだ、実際に線は書かれません。実際に線を書くには、strokeメソッドを呼び出します。
# 線の始点の設定 my $start_x = 100; my $start_y = 100; $gfx->move($start_x, $start_y); # 線の終点を設定 my $end_x = 300; my $end_y = 300; $gfx->line($end_x, $end_y); # 線を描画 $gfx->stroke;
PDF::API2の図形の描画では、パスをつなげて描画するという感覚を覚えてください。
線の太さを設定
線の太さは、linewidthメソッドで、設定できます。strokeする前に呼び出してください。
# 線の太さの設定 $gfx->linewidth(3);
線の幅は、円や多角形を書くときの設定の場合も同じです。
線の色を設定
線の色は、strokecolorメソッドで、設定できます。strokeする前に呼び出してください。色は、色名、RGB、CMYKで指定できます。各色に1バイト、2バイト、3バイト、または4バイトの値を持つことができます。たとえば、シアンは%F000または、%FFFF000000000000として設定できます。
# 線の色を設定(色名) $gfx->strokecolor('red'); # 線の色を設定(RGB) $gfx->strokecolor('#0123ab'); # 線の色を設定(CMYK) $gfx->strokecolor('%FF000000');
線の色は、円や多角形を書くときの設定の場合も同じです。
色の書式は、塗りつぶしの色の設定の場合も同じです。
線で囲んで三角形を書く
線を書く応用として、線で囲んで三角形を書いてみましょう。lineメソッドで、点をつなげて、最後に始点に戻ります。
use strict; use warnings; use utf8; use PDF::API2; # PDFオブジェクトの生成 my $pdf = PDF::API2->new; my $page = $pdf->page; my $gfx = $page->gfx; # 点P1 my $x1 = 100; my $y1 = 100; $gfx->move($x1, $y1); # 点P2 my $x2 = 300; my $y2 = 300; $gfx->line($x2, $y2); # 点P3 my $x3 = 500; my $y3 = 100; $gfx->line($x3, $y3); # 点P1に戻る $gfx->line($x1, $y1); # 線を描画 $gfx->stroke; my $pdf_file = 'render_tri.pdf'; $pdf->saveas($pdf_file);
polyメソッドを使うと、moveする点を設定して、複数の点を渡して、連続した線を描画できます。
use strict; use warnings; use utf8; use PDF::API2; # PDFオブジェクトの生成 my $pdf = PDF::API2->new; my $page = $pdf->page; my $gfx = $page->gfx; # 点P1 my ($x1, $y1) = (100, 100); # 点P2 my ($x2, $y2) = (300, 300); # 点P3 my ($x3, $y3) = (500, 100); # polyメソッドで、三角形を一度に描画 $gfx->poly($x1, $y1, $x2, $y2, $x3, $y3, $x1, $y1); # 線を描画 $gfx->stroke; my $pdf_file = 'render_tri.pdf'; $pdf->saveas($pdf_file);
塗りつぶす
パスで囲むと、塗りつぶすことができます。塗りつぶすにはfillメソッドを使用します。塗りつぶしのデフォルト色は、黒です。fillは、strokeの前に呼び出す必要があることに注意してください。
# 塗りつぶす(strokeの前で) $gfx->fill; # 線を書く $gfx->stroke;
塗りつぶしと線を書くのを同時に行うfillstrokeメソッドもあります。
# 塗りつぶして、線を書く $gfx->fillstroke;
塗りつぶしの色を設定
塗りつぶしの色は、fillcolorメソッドで、設定できます。fillする前に呼び出してください。色は、色名、RGB、CMYKで指定できます。各色に1バイト、2バイト、3バイト、または4バイトの値を持つことができます。たとえば、シアンは%F000または、%FFFF000000000000として設定できます。
# 塗りつぶしの色を設定(色名) $gfx->fillcolor('red'); # 塗りつぶしの色を設定(RGB) $gfx->fillcolor('#0123ab'); # 塗りつぶしの色を設定(CMYK) $gfx->fillcolor('%FF000000');
四角形を書く
PDF::API2には、四角形を書くための便利なメソッドがあります。すべての図形は、上記の方法で、線をつなげて作成できますが、線をつなげる方法は、手順が多いので、四角形などの基本的な図形を描画するメソッドが、準備されています。
四角形を作成して、薄いグレーで、背景を塗りつぶしてみましょう。四角形を書くには、rectメソッドとrectxyメソッドがありますが、始点と対角線の向かい側の点を指定するだけで、四角形が書けるrectxyを使ってみましょう。
use strict; use warnings; use utf8; use PDF::API2; # PDFオブジェクトの生成 my $pdf = PDF::API2->new; # ページの作成 my $page = $pdf->page; # グラフィック用のコンテンツオブジェクトの生成 my $gfx = $page->gfx; # 点P1 my $x1 = 100; my $y1 = 100; # 対角線の向かいの点P3 my $x3 = 300; my $y3 = 300; # 四角形を描画 $gfx->rectxy($x1, $y1, $x3, $y3); # 線の色を、黒より少し薄い色に $gfx->strokecolor('#333'); # 背景色をとても薄いグレーに $gfx->fillcolor('#eee'); # 四角形を塗りつぶして線を描画 $gfx->fillstroke; my $pdf_file = 'basic_rect.pdf'; $pdf->saveas($pdf_file);
そのまま実行できる四角形を描画できるサンプルです。
その他の基本図形を書く
その他の基本図形を書きたい場合に、どのメソッドを使うのかを紹介しておきます。
円を書く
円を書く場合は、circleメソッドを使用します。リストの箇条書きの●などに利用できます。
楕円を書く
楕円を書く場合は、ellipseメソッドを使用します。
扇形を書く
扇形を書きたい場合は、pieメソッドを使用します。
円弧を書く
円弧を書く場合は、arcあるいはbogenメソッドを使用します。bogenメソッドは、円弧の始点と終点を指定できるので、角丸などに利用できます。
楕円の円弧を書く
楕円の円弧を書く場合は、bogenメソッドを使用します。
曲線(ペジェ曲線)を書く
曲線(ペジェ曲線)を書くには、curveメソッドを使用します。ペジェ曲線のアルゴリズムによる曲線が書けます。
曲線(スプライン曲線)を書く
曲線(スプライン曲線)を書くには、splineメソッドを使用します。点を滑らかにつなげた曲線が書けます。
多角形を描画する
多角形を描画するには、polyメソッドを使用します。三角形や、五角形や六角形などを描画できます。
図形を回転させる
図形を回転させるには、rotateメソッドを使用します。
図形を拡大・縮小する
図形を拡大・縮小させるには、scaleメソッドを使用します。
水平線を引くには
水平線を簡易的に描画できるhlineメソッドがあります。
縦線を引くには
縦線を簡易的に描画できるvlineメソッドがあります。
破線を引くには
波線を引くには、linedashメソッドを使用します。
表を作成する
では、表を作成してみましょう。
表の考え方は、まず一番外側に、枠があります。それぞれの項目に対しても枠があります。項目の枠と外枠を重ねることができれば、表の完成です。四角形を組み合わせて、外側を重ねることで、描画できそうです。
でも、この方法問題点が発覚しました。同じに位置に二回描画すると、線が濃くなってしまうのです。
ですので、縦と横に線を引くというシンプルな方法で、表を作成してみます。
use PDF::API2; # PDFオブジェクトの生成 my $pdf = PDF::API2->new; # 用紙サイズのデフォルトのグローバル設定(用紙サイズを取得するため) $pdf->mediabox(undef); # ページの高さと幅 my @page_size_infos = $pdf->mediabox; my $page_width = $page_size_infos[2]; my $page_height = $page_size_infos[3]; # 項目の高さ my $item_height = 25; # 行の数 my $rows_count = 20; # 各項目の幅 my $name_width = 230; my $unit_price_width = 80; my $count_width = 80; my $price_width = 80; my $table_width = $name_width + $unit_price_width + $count_width + $price_width; # テーブルの高さ my $table_height = $item_height * $rows_count; # テーブルの始点 # 表の外枠 my $table_start_x = 50; my $table_start_y = $page_height - 50; my $table_other_side_x = $table_start_x + $table_width; my $table_other_side_y = $table_start_y - $table_height; # ページの作成 my $page = $pdf->page; # グラフィック用のコンテンツオブジェクトの生成 my $gfx = $page->gfx; # 上の線を引く $gfx->move($table_start_x, $table_start_y); $gfx->line($table_start_x + $table_width, $table_start_y); # 左の線を引く $gfx->move($table_start_x, $table_start_y); $gfx->line($table_start_x, $table_start_y - $table_height); my $cur_x = $table_start_x; my $cur_y = $table_start_y; # 各項目の枠の描画 for (my $column = 0; $column < $rows_count; $column++) { # 名前の枠(右と下) $gfx->move($cur_x + $name_width, $cur_y); $gfx->line($cur_x + $name_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $name_width, $cur_y - $item_height); $cur_x += $name_width; # 単価の枠(右と下) $gfx->move($cur_x + $unit_price_width, $cur_y); $gfx->line($cur_x + $unit_price_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $unit_price_width, $cur_y - $item_height); $cur_x += $unit_price_width; # 数量の枠(右と下) $gfx->move($cur_x + $count_width, $cur_y); $gfx->line($cur_x + $count_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $count_width, $cur_y - $item_height); $cur_x += $count_width; # 価格の枠(右と下) $gfx->move($cur_x + $price_width, $cur_y); $gfx->line($cur_x + $price_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $price_width, $cur_y - $item_height); $cur_x += $price_width; # 次の行へ $cur_x = $table_start_x; $cur_y -= $item_height; } # 線の色を、黒より少し薄い色に $gfx->strokecolor('#333'); # 四角形を塗りつぶして線を描画 $gfx->stroke; my $pdf_file = 'table.pdf'; $pdf->saveas($pdf_file);
情報が何も入っていない表が完成しました。
見出しと項目が入った表のサンプル
最後に、見出しと項目が入った表のサンプルを作成してみましょう。
PDFに以下の表が書き出されます。
Name | Unit | Count | Price |
---|---|---|---|
Book1 | 1000 | 3 | 3000 |
Book2 | 2000 | 6 | 12000 |
Book3 | 1500 | 5 | 7500 |
use strict; use warnings; use utf8; use PDF::API2; # PDFオブジェクトの生成 my $pdf = PDF::API2->new; # 用紙サイズのデフォルトのグローバル設定(用紙サイズを取得するため) $pdf->mediabox(undef); # ページの高さと幅 my @page_size_infos = $pdf->mediabox; my $page_width = $page_size_infos[2]; my $page_height = $page_size_infos[3]; # 項目の高さ my $item_height = 25; # 行の数 my $rows_count = 20; # 各項目の幅 my $name_width = 230; my $unit_price_width = 80; my $count_width = 80; my $price_width = 80; my $table_width = $name_width + $unit_price_width + $count_width + $price_width; # テーブルの高さ my $table_height = $item_height * $rows_count; # テーブルの始点 # 表の外枠 my $table_start_x = 50; my $table_start_y = $page_height - 50; my $table_other_side_x = $table_start_x + $table_width; my $table_other_side_y = $table_start_y - $table_height; # ページの作成 my $page = $pdf->page; # グラフィック用のコンテンツオブジェクトの生成 my $gfx = $page->gfx; # テキスト用のコンテンツオブジェクトの生成 my $text = $page->text; my $font = $pdf->corefont('Helvetica'); my $font_bold = $pdf->corefont('Helvetica-Bold'); my $font_size = 11; $text->font($font, $font_size); my $text_height = $font_size; my $books = [ { name => 'Book1', unit_price => 1000, count => 3, }, { name => 'Book2', unit_price => 2000, count => 6, }, { name => 'Book3', unit_price => 1500, count => 5, } ]; my $books_length = @$books; # 上の線を引く $gfx->move($table_start_x, $table_start_y); $gfx->line($table_start_x + $table_width, $table_start_y); # 左の線を引く $gfx->move($table_start_x, $table_start_y); $gfx->line($table_start_x, $table_start_y - $table_height); my $cur_x = $table_start_x; my $cur_y = $table_start_y; my $item_padding_top = 17; my $item_padding_left = 5; # 各項目の枠の描画 for (my $column = 0; $column < $rows_count; $column++) { my $book_index = $column - 1; # 名前の枠(右と下) # 見出し $text->translate($cur_x + $item_padding_left, $cur_y - $item_padding_top); if ($column == 0) { $text->font($font_bold, $font_size); $text->text('Name'); } # 項目 else { $text->font($font, $font_size); if ($book_index < $books_length) { $text->text($books->[$book_index]{name}); } } $gfx->move($cur_x + $name_width, $cur_y); $gfx->line($cur_x + $name_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $name_width, $cur_y - $item_height); $cur_x += $name_width; # 単価の枠(右と下) $text->translate($cur_x + $item_padding_left, $cur_y - $item_padding_top); if ($column == 0) { $text->font($font_bold, $font_size); $text->text('Unit'); } # 項目 else { $text->font($font, $font_size); if ($book_index < $books_length) { $text->text($books->[$book_index]{unit_price}); } } $gfx->move($cur_x + $unit_price_width, $cur_y); $gfx->line($cur_x + $unit_price_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $unit_price_width, $cur_y - $item_height); $cur_x += $unit_price_width; # 数量の枠(右と下) # 見出し $text->translate($cur_x + $item_padding_left, $cur_y - $item_padding_top); if ($column == 0) { $text->font($font_bold, $font_size); $text->text('Count'); } # 項目 else { $text->font($font, $font_size); if ($book_index < $books_length) { $text->text($books->[$book_index]{count}); } } $gfx->move($cur_x + $count_width, $cur_y); $gfx->line($cur_x + $count_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $count_width, $cur_y - $item_height); $cur_x += $count_width; # 価格の枠(右と下) # 見出し $text->translate($cur_x + $item_padding_left, $cur_y - $item_padding_top); if ($column == 0) { $text->font($font_bold, $font_size); $text->text('Pirce'); } # 項目 else { $text->font($font, $font_size); if ($book_index < $books_length) { $text->text($books->[$book_index]{unit_price} * $books->[$book_index]{count}); } } $gfx->move($cur_x + $price_width, $cur_y); $gfx->line($cur_x + $price_width, $cur_y - $item_height); $gfx->move($cur_x, $cur_y - $item_height); $gfx->line($cur_x + $price_width, $cur_y - $item_height); $cur_x += $price_width; # 次の行へ $cur_x = $table_start_x; $cur_y -= $item_height; } # 線の色を、黒より少し薄い色に $gfx->strokecolor('#333'); # 四角形を塗りつぶして線を描画 $gfx->stroke; my $pdf_file = 'table_items.pdf'; $pdf->saveas($pdf_file);