Perlゼミ

  1. Perl
  2. 帳票作成
  3. here

基本図形の描画と表の作成 - 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に以下の表が書き出されます。

NameUnitCountPrice
Book1100033000
Book22000612000
Book3150057500
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);

出力結果 項目ありの表

Perlの書籍
  • 初めてのPerl 第7版

    Perl入門 定番の一冊
  • 業務に役立つPerl

    ログ解析など日本語を含むテキスト処理の実践!
  • 詳説 正規表現

    正規表現の詳細な解説
  • Perlの書籍販売 14冊 »
自己紹介
木本裕紀(きもとゆうき)

Twitter

フォロー、いいね、リツート、コメント歓迎

Youtube

チャンネル登録、いいね、コメント歓迎
Perlの求人広告募集中