13.5. 描画

13.5.1. ジオメトリ

PDF は PostScript と同じジオメトリを使用します。ページの左下隅を基準とし、 デフォルトではポイント数 (1 インチの 1/72) で場所を指定します。

ページの大きさはページオブジェクトから取得できます。

$width  = $pdfPage->getWidth();
$height = $pdfPage->getHeight();
            

13.5.2. 色

PDF には、色を表現するためのさまざまな方法があります。Zend_Pdf では、 グレイスケール、RGB および CMYK 色空間をサポートしています。 Zend_Pdf_Color オブジェクトが要求される箇所では、 これらのどれでも使用することができます。それぞれの色空間に対応する機能を提供するのが Zend_Pdf_Color_GrayScaleZend_Pdf_Color_RGB および Zend_Pdf_Color_CMYK クラスです。

// $grayLevel (float 型の数値)。0.0 (黒) - 1.0 (白)
$color1 = new Zend_Pdf_Color_GrayScale($grayLevel);

// $r, $g, $b (float 型の数値)。0.0 (最低の強度) - 1.0 (最大の強度)
$color2 = new Zend_Pdf_Color_RGB($r, $g, $b);

// $c, $m, $y, $k (float 型の数値)。0.0 (最小の強度) - 1.0 (最大の強度)
$color3 = new Zend_Pdf_Color_CMYK($c, $m, $y, $k);
            

13.5.3. 図形の描画

描画操作は、PDF のページに対して行われます。

基本図形のセットが Zend_Pdf_Page クラスで提供されています。

/**
 * x1,y1 から x2,y2 まで直線を描画します。
 *
 * @param float $x1
 * @param float $y1
 * @param float $x2
 * @param float $y2
 */
public function drawLine($x1, $y1, $x2, $y2);
            

/**
 * 矩形を描画します。
 *
 * 描画方法
 * Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - 輪郭を描画して塗りつぶします (デフォルト)
 * Zend_Pdf_Page::SHAPE_DRAW_STROKE      - 輪郭を描画します
 * Zend_Pdf_Page::SHAPE_DRAW_FILL        - 矩形を塗りつぶします
 *
 * @param float $x1
 * @param float $y1
 * @param float $x2
 * @param float $y2
 * @param integer $fillType
 */
public function drawRectangle($x1, $y1, $x2, $y2, $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE);
            

/**
 * 多角形を描画します。
 *
 * $fillType が Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE あるいは Zend_Pdf_Page::SHAPE_DRAW_FILL
 * の場合、多角形は自動的に閉じられます。このメソッドについての詳細は、
 * PDF のドキュメント (section 4.4.2 Path painting Operators, Filling)
 * を参照ください。
 *
 * @param array $x  - float の配列 (頂点の X 座標)
 * @param array $y  - float の配列 (頂点の Y 座標)
 * @param integer $fillType
 * @param integer $fillMethod
 */
public function drawPolygon($x, $y,
                            $fillType = Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE,
                            $fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING);
            

/**
 * 中心が x, y で半径が radius の円を描画します。
 *
 * 角度はラジアンで指定します。
 *
 * Method signatures:
 * drawCircle($x, $y, $radius);
 * drawCircle($x, $y, $radius, $fillType);
 * drawCircle($x, $y, $radius, $startAngle, $endAngle);
 * drawCircle($x, $y, $radius, $startAngle, $endAngle, $fillType);
 *
 *
 * これは本当の円ではありません。PDF は 3 次ベジエ曲線しかサポートしていないからです。
 * とはいえ、本当の円にかなり近くなります。
 * 本当の円との誤差は、最大でも半径の 0.00026 倍にしかなりません
 * (角度が PI/8, 3*PI/8, 5*PI/8, 7*PI/8, 9*PI/8, 11*PI/8, 13*PI/8 そして 15*PI/8 の場合)。
 * 0, PI/4, PI/2, 3*PI/4, PI, 5*PI/4, 3*PI/2 そして 7*PI/4 の場合は、円の正確な接線となります。
 *
 * @param float $x
 * @param float $y
 * @param float $radius
 * @param mixed $param4
 * @param mixed $param5
 * @param mixed $param6
 */
public function  drawCircle($x, $y, $radius, $param4 = null, $param5 = null, $param6 = null);
            

/**
 * 指定した矩形に内接する楕円を描画します。
 *
 * Method signatures:
 * drawEllipse($x1, $y1, $x2, $y2);
 * drawEllipse($x1, $y1, $x2, $y2, $fillType);
 * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
 * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle, $fillType);
 *
 * 角度はラジアンで指定します。
 *
 * @param float $x1
 * @param float $y1
 * @param float $x2
 * @param float $y2
 * @param mixed $param5
 * @param mixed $param6
 * @param mixed $param7
 */
public function drawEllipse($x1, $y1, $x2, $y2, $param5 = null, $param6 = null, $param7 = null);
            

13.5.4. テキストの描画

テキストに対する描画操作も、PDF のページに対して行われます。 ベースラインの x 座標および y 座標を指定することで、 ページ内の任意の場所にテキストを 1 行描画することができます。 現在のフォントおよびフォントサイズを使用して、描画操作が行われます (詳細は、以下を参照ください)。

/**
 * 指定した位置にテキストを描画します。
 *
 * @param string $text
 * @param float $x
 * @param float $y
 * @param string $charEncoding (オプション) ソーステキストの文字エンコーディング。
 *   デフォルトは現在のロケールです。
 * @throws Zend_Pdf_Exception
 */
public function drawText($text, $x, $y, $charEncoding = '');
            

例 13.5. ページ上への文字列の描画

<?php
...
$pdfPage->drawText('Hello world!', 72, 720);
...
?>
            

デフォルトでは、現在のロケールのエンコーディングによって テキストの文字列が解釈されます。異なるエンコーディングを使用している場合 (例えば、ディスク上のファイルから UTF-8 の文字列を読み込んだり レガシーなデータベースから MacRoman の文字列を取得したりなど) は、 描画の際に文字エンコーディングを指定することができます。 そうすることで、Zend_Pdf が変換処理を行います。PHP の iconv() 関数がサポートしているエンコーディングなら、すべて入力として使用することが可能です。

例 13.6. UTF-8 でエンコードされた文字列をページに描画する

<?php
...
// UTF-8 エンコードされた文字列をディスクから読み込みます
$unicodeString = fread($fp, 1024);

// 文字列をページ上に描画します
$pdfPage->drawText($unicodeString, 72, 720, 'UTF-8');
...
?>
            

13.5.5. フォントの使用

Zend_Pdf_Page::drawText() は、 現在設定されているフォントおよびフォントサイズを使用します。 これは Zend_Pdf_Page::setFont() メソッドで設定できます。

/**
 * 現在のフォントを設定します。
 *
 * @param Zend_Pdf_Resource_Font $font
 * @param float $fontSize
 */
public function setFont(Zend_Pdf_Font $font, $fontSize);
            

PDF ドキュメントは、PostScript Type 1 フォントおよび TrueType フォントだけでなく、 PDF 用の特別な形式である Type 3 フォントや複合フォントもサポートしています。 すべての PDF ビューアには、以下の 14 種類の標準 Type 1 フォントが組み込まれています。 その内容は Courier (4 種類)、Helvetica (4 種類)、Times (4 種類)、Symbol そして Zapf Dingbats です。

現在 Zend_Pdf は、標準の 14 種類の PDF フォントだけでなく 独自の TrueType フォントもサポートしています。フォントオブジェクトを取得するには、 2 種類のファクトリーメソッドのいずれかを使用します。使用するメソッドは、 標準の 14 種類の PDF フォントの場合は Zend_Pdf_Font::fontWithName($fontName)、 独自のフォントの場合は Zend_Pdf_Font::fontWithPath($filePath) です。

例 13.7. 標準フォントの作成

<?php
...
// 新しいフォントを作成します。
$font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA);

// フォントを適用します。
$pdfPage->setFont($font, 36);
...
?>
            

14 種類の標準フォント名を表す定数は、Zend_Pdf_Font クラスで定義されています。

  • Zend_Pdf_Font::FONT_COURIER

  • Zend_Pdf_Font::FONT_COURIER_BOLD

  • Zend_Pdf_Font::FONT_COURIER_ITALIC

  • Zend_Pdf_Font::FONT_COURIER_BOLD_ITALIC

  • Zend_Pdf_Font::FONT_TIMES

  • Zend_Pdf_Font::FONT_TIMES_BOLD

  • Zend_Pdf_Font::FONT_TIMES_ITALIC

  • Zend_Pdf_Font::FONT_TIMES_BOLD_ITALIC

  • Zend_Pdf_Font::FONT_HELVETICA

  • Zend_Pdf_Font::FONT_HELVETICA_BOLD

  • Zend_Pdf_Font::FONT_HELVETICA_ITALIC

  • Zend_Pdf_Font::FONT_HELVETICA_BOLD_ITALIC

  • Zend_Pdf_Font::FONT_SYMBOL

  • Zend_Pdf_Font::FONT_ZAPFDINGBATS

任意の TrueType フォント (通常は '.ttf' という拡張子です) も使用できますし、 TrueType アウトラインを含む OpenType フォント (拡張子は '.otf') を使用することも可能です。現在はまだサポートしていませんが、将来は Mac OS X の .dfont ファイルや Microsoft TrueType Collection (拡張子 '.ttc') ファイルもサポートする予定です。

TrueType フォントを使用するには、フォントへのフルパスを指定しなければなりません。 何らかの理由でフォントが読み込めなかった場合、あるいはそれが TrueType フォントでなかった場合は、ファクトリーメソッドが例外をスローします。

例 13.8. TrueType フォントの作成

<?php
...
// 新しいフォントを作成します
$goodDogCoolFont = Zend_Pdf_Font::fontWithPath('/path/to/GOODDC__.TTF');

// フォントを適用します
$pdfPage->setFont($goodDogCoolFont, 36);
...
?>
            

デフォルトでは、独自のフォントは PDF ドキュメントに埋め込まれます。 そのため、閲覧者のシステムにそのフォントがインストールされていなくても、 ページをきちんと閲覧できるようになります。ファイルの大きさが気になる場合は、 ファクトリーメソッドのオプションで「フォントを埋め込まない」ことを指定することができます。

例 13.9. TrueType を作成するが、PDF ドキュメントには埋め込まない

<?php
...
// 新しいフォントを作成します
$goodDogCoolFont = Zend_Pdf_Font::fontWithPath('/path/to/GOODDC__.TTF',
                                               Zend_Pdf_Font::EMBED_DONT_EMBED);

// フォントを適用します
$pdfPage->setFont($goodDogCoolFont, 36);
...
?>
            

PDF ファイルにフォントが埋め込まれていないけれども 閲覧者のシステムにはそのフォントがインストールされている場合は、ドキュメントは通常通りに閲覧できます。 もし適切なフォントがインストールされていないは、PDF 閲覧アプリケーションが適切な代替フォントを選択します。

中には、PDF ドキュメントへの埋め込みを禁止するようなライセンスを使用しているフォントもあります。 これをあなどってはいけません。もし埋め込めないフォントを利用しようとすると、 ファクトリーメソッドは例外をスローします。

このようなフォントを使用することも可能ですが、そのためには、 上で説明した「埋め込まない」フラグを使用するか、あるいは例外を抑制しなければなりません。

例 13.10. 埋め込みが禁止されているフォントで、例外をスローさせないようにする

<?php
...
$font = Zend_Pdf_Font::fontWithPath('/path/to/unEmbeddableFont.ttf',
                                    Zend_Pdf_Font::EMBED_SUPPRESS_EMBED_EXCEPTION);
...
?>
            

利用者にフォントを選択させる場合などは、この抑制方法を使用することをお勧めします。 PDF ドキュメントに埋め込めるフォントなら埋め込むでしょうし、 埋め込めないフォントは埋め込まないでしょう。

フォントのサイズは比較的大きく、中には 10 メガバイトに達するものもあります。 デフォルトでは埋め込みフォントは Flate 圧縮され、平均して 50% ほどサイズを節約できます。 何らかの理由でフォントを圧縮したくない場合は、以下のオプションで圧縮を無効にすることができます。

例 13.11. 埋め込みフォントを圧縮しない

<?php
...
$font = Zend_Pdf_Font::fontWithPath('/path/to/someReallyBigFont.ttf',
                                    Zend_Pdf_Font::EMBED_DONT_COMPRESS);
...
?>
            

最後に、必要に応じていくつかの埋め込みオプションをビット OR 演算子で連結することもできます。

例 13.12. フォントの埋め込みオプションを組み合わせる

<?php
...
$font = Zend_Pdf_Font::fontWithPath($someUserSelectedFontPath,
                                    (Zend_Pdf_Font::EMBED_SUPPRESS_EMBED_EXCEPTION |
                                     Zend_Pdf_Font::EMBED_DONT_COMPRESS));
...
?>
            

13.5.6. 画像の描画

Zend_Pdf_Page クラスの drawImage() メソッドで、 画像の描画を行います。

/**
 * ページ内の指定した位置に画像を描画します。
 *
 * @param Zend_Pdf_Image $image
 * @param float $x1
 * @param float $y1
 * @param float $x2
 * @param float $y2
 */
public function drawImage(Zend_Pdf_Image $image, $x1, $y1, $x2, $y2);
            

画像オブジェクトは Zend_Pdf_Image クラスを継承したクラスで表されます。

現在は JPG、PNG および TIFF 画像がサポートされています。

例 13.13. 画像の描画

<?php
...
// (以下のいずれかを使用して) 画像オブジェクトを直接作成します
$image = new Zend_Pdf_Image_JPEG('my_image.jpg');
$image = new Zend_Pdf_Image_TIFF('my_image.tiff');
$image = new Zend_Pdf_Image_PNG('my_image.png');

// あるいは Zend_Pdf_ImageFactory で適切な形式を読み込みます
$image = Zend_Pdf_ImageFactory::factory('my_image.jpg');

$pdfPage->drawImage($image, 100, 100, 400, 300);
...
?>
            

重要! Zend_Pdf_Image_JPEG クラスは PHP の GD 拡張モジュールを必要とします。 重要! Zend_Pdf_Image_PNG クラスでアルファチャネルを使用した画像を扱うには、ZLIB 拡張モジュールを必要とします。

詳細な情報は、PHP のドキュメント (http://www.php.net/manual/ja/ref.image.php), (http://www.php.net/manual/ja/ref.zlib.php) を参照ください。

13.5.7. 直線の描画スタイル

直線の描画スタイルは、線幅と線の色、そして破線のパターンで定義されます。 これらはすべて、Zend_Pdf_Page クラスのメソッドで設定します。

/** 線の色を設定します。*/
public function setLineColor(Zend_Pdf_Color $color);

/** 線の幅を設定します。*/
public function setLineWidth(float $width);

/**
 * 破線のパターンを設定します。
 *
 * pattern は float の配列です: array(on_length, off_length, on_length, off_length, ...)
 * phase は線の開始位置から移動する距離です。
 *
 * @param array $pattern
 * @param array $phase
 */
public function setLineDashingPattern($pattern, $phase = 0);
            

13.5.8. 塗りつぶしのスタイル

Zend_Pdf_Page::drawRectangle()Zend_Pdf_Page::drawPoligon()Zend_Pdf_Page::drawCircle() および Zend_Pdf_Page::drawEllipse() メソッドは、オプションのパラメータとして $fillType を受け取ります。これは以下のいずれかの値となります。

  • Zend_Pdf_Page::SHAPE_DRAW_STROKE - 図形の輪郭を描画します

  • Zend_Pdf_Page::SHAPE_DRAW_FILL - 図形を塗りつぶすだけです

  • Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE - 輪郭を描画し、塗りつぶします (デフォルトの挙動です)

Zend_Pdf_Page::drawPoligon() メソッドには、さらにパラメータ $fillMethod を指定することができます。

  • Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING (デフォルトの挙動)

    PDF リファレンス によると、これは以下のように定義されています。

    nonzero winding number ルールは、ある点がパスの内側にあるかどうかを 判断するため、その点からどこかの方向に放射線を引いて その線がパスを構成する線と交わる場所を調べます。 0 からカウントをはじめ、放射線の左から右にパスの線が横切った場合に +1、放射線の右から左に横切った場合に -1 します。 すべての交点について調べた後、もし結果が 0 ならその点はパスの外側です。 0 でなければ内側です。 注意: この方式では、放射線とパスの線が同一になった場合や 放射線がパスの線の接線となった場合のことを指定していません。 放射線は任意の方向に伸ばせるので、このような状況にならないような放射線が選ばれます。 単純な凸状のパスの場合、この方式で判断した内側・外側は、 直感的に予想できるのと同じ結果になります。 ただ、パスを構成する線自身が交わっているなどの複雑なパスの場合は、 興味深い結果となります。この例を、(PDF リファレンスの) 図 4.10 に示します。 5 本の直線を互いに交差させて作成した星型の場合、このルールでは 星型で囲まれるすべての部分をパスの内側として扱います。真ん中の 五角形も内側となります。2 つの同心円からなるパスの場合、 2 つの円が同じ方向に描画された際には両方の円に囲まれている部分が 内側となります。2 つの円が反対方向に描画された際には、2 つの円からなる 「ドーナツ型」の部分が内側となります。このルールの場合は、 「ドーナツの穴」の部分は外側という扱いになります。

  • Zend_Pdf_Const::FILLMETHOD_EVENODD

    PDF リファレンス によると、これは以下のように定義されています。

    nonzero winding number ルールに対するもうひとつのルールが even-odd ルールです。 このルールでは、ある点が「内側である」かどうかを判断する材料として、 その点からどこかの方向に放射線を引いてその線がパスを構成する線と何回交わるか ということを用います。交わる回数が奇数だった場合、その点は内側です。 交わる回数が偶数だった場合、その点は外側です。単純なパスの場合は、 これは nonzero winding number ルールと同じ結果になります。 しかし、複雑な図形の場合は異なる結果となります。 複雑なパスに対して even-odd ルールを適用した場合の例を (PDF リファレンスの) 図 4.11 に示します。このルールの場合、5 本の交差する直線からなる星型では、 三角形の部分のみが内側として扱われます。真ん中の五角形は、内側とはみなされません。 2 つの同心円の場合、2 つの円からなる「ドーナツ型」の部分のみが内側として扱われます。 これは、円の描画された方向に依存しません。

13.5.9. 回転

描画操作を適用する前に、PDF のページを回転させることができます。 それには Zend_Pdf_Page::rotate() メソッドを使用します。

/**
 * ($x, $y) を中心にし、指定した角度 (ラジアン) だけ回転します。
 *
 * @param float $angle
 */
public function rotate($x, $y, $angle);
            

13.5.10. グラフィックの状態の保存/復元

好きな時点でのグラフィックの状態 (現在のフォント、フォントサイズ、線の色、塗りつぶしの色、線の形式、 ページの回転、クリップ領域) を保存/復元することができます。 保存操作はグラフィックの状態をスタックに保存し、復元の際にはそこから取り出されます。

Zend_Pdf_Page クラスには、これらの操作を行うための 2 つのメソッドがあります。

/**
 * このページのグラフィックの状態を保存します。
 * 現在適用されているスタイル・位置・クリップ領域および
 * 回転/移動/拡大縮小などを情報を保存します。
 */
public function saveGS();

/**
 * 直近の saveGS() で保存されたグラフィックの状態を復元します。
 */
public function restoreGS();
            

13.5.11. 描画領域のクリッピング

PDF および Zend_Pdf モジュールは、描画領域のクリッピングに対応しています。 描画演算子が影響を及ぼす範囲を、このクリップ領域内に制限します。 クリップ領域の初期値は、ページ全体です。

Zend_Pdf_Page クラスでは、 クリッピングに関連するいくつかのメソッドを提供しています。

/**
 * 矩形のクリップ領域を設定します。
 *
 * @param float $x1
 * @param float $y1
 * @param float $x2
 * @param float $y2
 */
public function clipRectangle($x1, $y1, $x2, $y2);
            

/**
 * 多角形のクリップ領域を設定します。
 *
 * @param array $x  - float の配列 (頂点の X 座標)
 * @param array $y  - float の配列 (頂点の Y 座標)
 * @param integer $fillMethod
 */
public function clipPolygon($x, $y, $fillMethod = Zend_Pdf_Page::FILL_METHOD_NON_ZERO_WINDING);
            

/**
 * 円形のクリップ領域を設定します。
 *
 * @param float $x
 * @param float $y
 * @param float $radius
 * @param float $startAngle
 * @param float $endAngle
 */
public function clipCircle($x, $y, $radius, $startAngle = null, $endAngle = null);
            

/**
 * 楕円のクリップ領域を設定します。
 *
 * メソッドの書式
 * drawEllipse($x1, $y1, $x2, $y2);
 * drawEllipse($x1, $y1, $x2, $y2, $startAngle, $endAngle);
 *
 * @todo $x2-$x1 == 0 や $y2-$y1 == 0 のような特別な場合への対応
 *
 * @param float $x1
 * @param float $y1
 * @param float $x2
 * @param float $y2
 * @param float $startAngle
 * @param float $endAngle
 */
public function clipEllipse($x1, $y1, $x2, $y2, $startAngle = null, $endAngle = null);
            

13.5.12. スタイル

Zend_Pdf_Style クラスがスタイルに関する機能を提供します。

スタイルは、グラフィックの状態に関する複数の設定を保存し、 PDF のページに 1 回の操作でそれを適用するために使用されます。

/**
 * このページの描画操作で使用する予定のスタイルを設定します。
 *
 * @param Zend_Pdf_Style $style
 */
public function setStyle(Zend_Pdf_Style $style);

/**
 * スタイルを返し、それをページに適用します。
 *
 * @return Zend_Pdf_Style|null
 */
public function getStyle();
            

Zend_Pdf_Style クラスでは、 さまざまなグラフィックの状態を設定あるいは取得するためのメソッドが提供されています。

/**
 * 線の色を設定します。
 *
 * @param Zend_Pdf_Color $color
 */
public function setLineColor(Zend_Pdf_Color $color);
            

/**
 * 線の色を取得します。
 *
 * @return Zend_Pdf_Color|null
 */
public function getLineColor();
            

/**
 * 線の幅を設定します。
 *
 * @param float $width
 */
public function setLineWidth($width);
            

/**
 * 線の幅を取得します。
 *
 * @return float
 */
public function getLineWidth();
            

/**
 * 破線のパターンを設定します。
 *
 * @param array $pattern
 * @param float $phase
 */
public function setLineDashingPattern($pattern, $phase = 0);
            

/**
 * 破線のパターンを取得します。
 *
 * @return array
 */
public function getLineDashingPattern();
            

/**
 * 破線の位相を取得します。
 *
 * @return float
 */
public function getLineDashingPhase();
            

/**
 * 塗りつぶし色を設定します。
 *
 * @param Zend_Pdf_Color $color
 */
public function setFillColor(Zend_Pdf_Color $color);
            

/**
 * 塗りつぶし色を取得します。
 *
 * @return Zend_Pdf_Color|null
 */
public function getFillColor();
            

/**
 * 現在のフォントを設定します。
 *
 * @param Zend_Pdf_Resource_Font $font
 * @param float $fontSize
 */
public function setFont(Zend_Pdf_Resource_Font $font, $fontSize);
            

/**
 * 現在のフォントサイズを変更します。
 *
 * @param float $fontSize
 */
public function setFontSize($fontSize);
            

/**
 * 現在のフォントを取得します。
 *
 * @return Zend_Pdf_Resource_Font $font
 */
public function getFont();
            

/**
 * 現在のフォントサイズを取得します。
 *
 * @return float $fontSize
 */
public function getFontSize();