はじめに
前回は PDFBox による文章の Box 表示を見ました。
PDFBox が提供する PDF 操作の API は、低レベルな操作に限定されており、文章の折返しなども自身で処理する必要がありました。
テーブルの表示も同じく専用の API などは用意されておらず、線画を組み合わせて自身で描画する必要があります。
PDFBox での矩形描画
テーブルのセルを描画するには、単に四角形を描画することになります。
addRect(x, y, width, height)
が用意されているため、以下のように四角形を描画できます。
float x = 10; float y = 10; float width = 100; float height = 100; content.addRect(x, y, width, height); content.stroke();
この場合、x, y は、四角形の左下の座標となるので注意が必要です。
または、moveTo()
, lineTo()
, stroke()
を組み合わせて四辺を描画します。
content.moveTo(x, y); content.lineTo(x + width, y); content.lineTo(x + width, y + height); content.lineTo(x , y + height); content.lineTo(x , y); content.stroke();
PDFBox でのテーブル描画
単純に線画とテキストの表示を組み合わせて以下のようにテーブルを描画することができます。
try (PDDocument doc = new PDDocument()) { PDPage page = new PDPage(PDRectangle.A4); doc.addPage(page); try (PDPageContentStream content = new PDPageContentStream(doc, page)) { String[][] data = {{"1", "first", "One"}, {"2", "second", "Two"}, {"3", "third", "Three"}}; drawTable(page, content, data, 700, 100); } doc.save("build/example4.pdf"); } catch (IOException e) { throw new RuntimeException(e); }
テーブルの描画は以下のようになります。
public static void drawTable(PDPage page, PDPageContentStream content, String[][] data, float posY, float margin) throws IOException { final PDFont font = PDType1Font.HELVETICA_BOLD; float fontSize = 12; float fontHeight = font.getFontDescriptor() .getFontBoundingBox().getHeight() / 1000 * fontSize; final int rows = data.length; final int cols = data[0].length; final float rowHeight = fontHeight * 1.5f; final float tableWidth = page.getMediaBox().getWidth() - margin * 2; final float tableHeight = rowHeight * rows; final float colWidth = tableWidth / cols; float y = posY; for (int i = 0; i <= rows; i++) { content.moveTo(margin, y); content.lineTo(margin + tableWidth, y); content.stroke(); y -= rowHeight; } float x = margin; for (int i = 0; i <= cols; i++) { content.moveTo(x, posY); content.lineTo(x, posY - tableHeight); content.stroke(); x += colWidth; } content.setFont(PDType1Font.HELVETICA_BOLD, fontSize); final float cellMargin = 5; float textX = margin + cellMargin; float textY = posY - fontHeight; for (final String[] rowData : data) { for (String text : rowData) { content.beginText(); content.newLineAtOffset(textX, textY); content.showText(text); content.endText(); textX += colWidth; } textY -= rowHeight; textX = margin + cellMargin; } }
以下のような PDF が出力されます。
セルテキストの Box 表示
上記は、単純に線画とテキスト表示を行っているため、長いテキストがあると以下のような表示になってしまいます。
セル幅に収まるかどうかを font.getStringWidth(text)
で調べつつ、折返しを行い、行の高さを調整しなければなりません。
これらは自作してもいいですが、easytable
を使うこともできます。
easytable によるテーブル表示
依存に以下を追加します。
implementation 'com.github.vandeseer:easytable:0.6.4'
easytable によるテーブル作成は以下のようになります。
private static void drawPage(PDDocument doc, PDPage page) { try (PDPageContentStream content = new PDPageContentStream(doc, page)) { Table table = Table.builder() .font(PDType1Font.HELVETICA_BOLD).fontSize(12) .addColumnsOfWidth(120, 120, 120) .addRow(Row.builder() .add(TextCell.builder().text("1").borderWidth(1).build()) .add(TextCell.builder().text("first long long long text").borderWidth(1).build()) .add(TextCell.builder().text("One").borderWidth(1).build()) .build()) .addRow(Row.builder() .add(TextCell.builder().text("2").borderWidth(1).build()) .add(TextCell.builder().text("second").borderWidth(1).build()) .add(TextCell.builder().text("Two").borderWidth(1).build()) .build()) .addRow(Row.builder() .add(TextCell.builder().text("3").borderWidth(1).build()) .add(TextCell.builder().text("third").borderWidth(1).build()) .add(TextCell.builder().text("Three").borderWidth(1).build()) .build()) .build(); TableDrawer tableDrawer = TableDrawer.builder() .contentStream(content) .startX(100) .startY(700) .table(table) .build(); tableDrawer.draw(); } catch (IOException e) { throw new RuntimeException(e); } }
出力結果は以下のようになります。
長いテキストが折り返し表示されました。
まとめ
PDFBox でテーブルを表示する方法について見ました。
easytable
を使うことでセル内の折返しなど、細かなことを気にせずにテーブル描画ができます。
easytable
を使わずとも、そう難しいことでもないので、ユーティリティを作ってしまうのも良いかもしれません。
- 作者:John Whitington
- 発売日: 2012/05/25
- メディア: 単行本(ソフトカバー)