iText+pdfmonkで楽々PDF作成、その2

2020年3月15日

pdfmonkについての全投稿は/tag/pdfmonkにあるので参照されたい

iText+pdfmonkで楽々PDF作成、その1の続きである。

iTextのラッパであること

pdfmonkはiTextライブラリを簡単に扱うためのラッパライブラリであるが、「良く使う機能」を中心にサポートしているものであり、すべての機能を簡単に使えるわけではない。ただ、それらのラッパの内部にあるiTextの元々のオブジェクトにはアクセス可能であるため、それを使えばiTextのできることであれば、何でもできる。

基本的な考え方

これはプログラムによってpdfファイルを生成するものであり、既存のpdfを操作するものではない。ただし、既存pdfを読み込んで、その「上から」他のものを描画することはできる(既存部分を変更することはできない)。基本的な処理の方式としては以下の通り。

  • 出力先のpdfファイル(新規作成)を指定して、ドキュメントオブジェクトを作成する。
  • 作成直後は1ページ目が準備されるので、そこに描画する。
  • 改ページをしていって次々に描画する(テーブル描画では、テーブルがページをはみ出す場合、自動改ページされることもある)。
  • ドキュメントをクローズする

すると、pdfファイルができあがる。この「描画」であるが、pdfにはもともと4つのレイヤーがあり、それぞれのレイヤーの描画方式には違いがあり、それらのレイヤーに描画されたものが合成されて結果となる。当然、最も手前のレイヤーに描画したものは、他のレイヤー描画に隠されることはない。

しかし、私もそうだが、おおよその人間は4つもレイヤーは必要無いと思われる。今後の説明では、最も手前のレイヤーにのみ話を絞ることにする。

また、前回にも書いたが、元々のpdfは左下を原点としたポイント座標系になっているのだが、これが非常に直感に反するため使いにくい。pdfmonkでは、(紙のマージン領域を除く)左上を原点とし、さらにミリメートルの座標系を使えるようにしてある。先の手順を書き直すとこうなる。

  • 出力先pdfファイル、ドキュメントのジオメトリ(サイズとマージン)を指定し、ドキュメントオブジェクトを作成する。
  • 最も手前のレイヤーを表すキャンバスを取得する。このままキャンバスに描画すると1ページ目への描画になる。
  • このとき、(マージンを除く)左上を原点とするミリメートル座標系を指定して描くことができる。これは絶対座標指定による描画になる。
  • 絶対位置指定による描画のほか、キャンバスは「現在のy位置」を持っており、キャンバスにテーブルを描画するとそこから描画され始め、ページを溢れた場合には自動改ページされる。
  • 自動改ページあるいは改ページ指示をして2ページ目を用意する。
  • このようにして次々に描画していく。
  • 最後にドキュメントをクローズすればpdfファイルが完成する。

基本的な流れは以上であり、ページ単位で次々に描画していくということである。改ページをした後では、前のページに戻ることはできない。

ページ番号のふりかた

しかし、例えば、各ページに「第*ページ/総*ページ」のような文言を描画したい場合がある。これには、各ページの描画中に最終的な総ページ数がわかっていなければならない。このやり方には二通りある。

テンプレートを使う方法

通常のテンプレートの意味とは全く異なるので注意されたい。pdfmonk(iText)で言うところのテンプレートとはこういうことである。

  • まず最初に各ページの片隅にでも小さなテンプレートを置いておき、それをプログラム上に保存しておく。
  • ドキュメントをクローズする直前、合計ページ数がわかるので、置いてきたすべてのテンプレートを描画する。
  • ドキュメントをクローズする。

どういうわけかテンプレートだけは、後からでも描画可能なのである。この機能を使用すれば、ドキュメントクローズ直前に全ページ番号をふることができる。

※前回示した例ではこの方法を使ってページ番号をふっている。

スタンパーを使う方法

これは、既存のpdfに対しても利用可能な方法である。

  • ドキュメントクローズし、いったんpdfの作成を完了する。
  • 再度同じpdfをスタンパーで入力として開く、スタンパーの描画結果出力pdfも指定しなくてはいけない。
  • もちろん既存pdfのページ数は既知である。
  • 改ページをしながら各ページに「上書き」する。
  • スタンパーを閉じる。入力pdfは元のままで、新たに出力pdfが作成される。

コンテキストについて

pdfmonkにはあるが、iTextには無い概念として「コンテキスト」がある。

pdfやiTextに限らず何でもそうだと思うが、文字を描画するとかテーブルを描画するといった場合、それをどんな種類のフォントでどんな大きさで行うのかを指定しなくてはいけない。テーブルセルの描画でも、フォントの指定やセルのスタイル・カラー等の指定が必要である。

これをいちいち指定していくのは面倒なものだが、かといって、そう頻繁に変わるわけではない。例えば、あちこちでフォントサイズが変わってしまったら、かえって見にくくなってしまう。重要なところだけ(例えば、文書タイトル、お客様名、赤字決算金額)だけ強調したいなどという使い方だろう。

このため、フォント、ベースフォント、テーブルセルのスタイルをひとまとめにしたコンテキストというものを作成してある。

同じスタイルでよければ、一つのコンテキストを使い回せばよいし、少しだけ違うスタイルにしたい場合は、これを複製し、少しだけ変えた状態で使えばよい。

座標や長さの指定

座標や長さの指定は、ポイント、ミリメートル、インチのいずれも利用でき、しかも混ぜて使うこともできる。
このため、少々面倒な仕様と思うかもしれないが、できるだけ簡単に使えるようにしたつもりだ。

このシステムでは単位は以下のenumで指定される(簡略化してある)。

public enum MkUnit   {
  /** ミリメートル */
  MM,
  /** インチ */
  IN,
  /** ポイント */
  PT;
}

そして、長さを表すものには、常に単位がつけられる。

public class MkLen implements Comparable<MkLen> {
  /** 単位 */
  public final MkUnit unit;

  /** 値 */
  public final float value;
}

基本的には、MkLenオブジェクトを使って長さや座標を指定することが必要なのだが、ただし、以下のように記述することもできる。

    image.setSizeAspect(MkUnit.MM, 50, 50);

これは、最大50mm × 50mmの大きさに、画像のアスペクト比を保持しつつ引き伸ばす操作である。このような方式で、異なる単位を自由に使えるようにしてある。

(続く)