先日、下のように表を出力するPDF を作成した。

単純に思えるかもしれないが、以下の様な要望と技術的問題があった。
- 行の高さを固定せず文字量やデータ量に応じて自動拡張したい
- 文字量とデータ件数は動的変化する
- 行の途中で改行しない
- 各ページの上部に固定ヘッダーを表示したい
- その上で表のタイトル行も毎ページ表示したい
これらを満たすためには、表に1行ずつ追加した場合の高さを取得できる必要がある。
無償のPDF ライブラリを探したところ、OpenPDF でできることが分かった。
ColumnText にgo(simulate) という機能があり、こちらを使うと描画する前にコンテンツの高さを取得することができる。
Javadoc
コードの例
// 新規PDF ドキュメント作成(マージンとファイル名は適当)
Document document = new Document(PageSize.A4, 36, 36, 36, 36);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("sample.pdf"));
document.open();
PdfContentByte cb = writer.getDirectContent();
// シミュレーション用のcolumNText を作成
ColumnText columnText = new ColumnText(cb);
// 左下(0,0) 右上(1000,1000) のシミュレーション領域を作成
float baseHeight = 1000;
columnText.setSimpleColumn(0, 0, 1000, baseHeight);
// デフォルトフォントでは日本語が表示されないのでNotoSans を指定
BaseFont baseFont = BaseFont.createFont("NotoSansJP-Regular.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
Font font = new Font(baseFont, 18);
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell(new Phrase("改行込み\n長い長い\nテキスト", font));
// 計算を簡略化するためセルの余白にゼロを指定
cell.setPadding(0);
table.addCell(cell);
columnText.addElement(table);
// シミュレーションモードで実行
columnText.go(true);
// table 描画後のyLine を取得
float yLine = columnText.getYLine();
// 領域の高さからyLine を引いた数値が高さになる
float height = baseHeight - yLine;
System.out.println(height); // 54.0 (フォント18pt x 3行 + セルの余白0)
build.gradle
implementation("com.github.librepdf:openpdf:2.0.5")
columnText.go(true) が重要な点である。これを使うと描画せずに高さを取得できる。表の高さがページの余白の高さに収まるか判定すれば要件を満たせる。
余談だが、フォント(NotoSans)はこちらを使用した。

