Apache:PDFBox
javaで一般的なPDF編集ツールというとiTextかと思うが、(商用目的で利用してソースを公開しない場合)iTextの場合ライセンス購入が必要なため、導入においてハードルがある。
初期導入時点であれば(金銭面で)話も通しやすいかもしれないが、既存システムへの導入となると困ることがある。。
ライセンスフリーでPDFの編集を行えるツールとして検討したところ、ApacheからPDFBoxなるものが提供されているとのことで、その実装についてメモ。
導入
以下のサイトからダウンロード可能。
(Apacheのサイトです)
記事作成時点の最新は2.0.7だが、実装時は2.0.7リリース前であったため、サンプル実装は2.0.6で行っている。
ダウンロードしたjarをプロジェクトに取り込んで導入完了。
サンプル実装
実装環境は、windows10端末、java1.8。
ちょっとググれば既に日本語で実装サンプルが上がっている。
public static void main(String[] args) {
File pdfPath = new File("…/sample/sample.pdf");//元のPDF
File outPath = new File("…/sample/makepdf.pdf");//編集後のPDF
String tmpPath = "…/sample/temp.pdf";//一時ファイル
// 背景として透かす用のPDFを作成する
PDDocument tmp = new PDDocument();
PDRectangle rectangle = PDRectangle.A4;//A4サイズ
PDPage tmpPage = new PDPage(rectangle);
tmp.addPage(tmpPage);
File file = new File("/Windows/Fonts/msgothic.ttc");//マルチバイトを扱う場合フォントファイルを読み込む必要がある
try (TrueTypeCollection collection = new TrueTypeCollection(file)) {
PDPageContentStream cont = new PDPageContentStream(tmp, tmpPage);
PDFont font = PDType0Font.load(tmp, collection.getFontByName("MS-Gothic"), true);// MS ゴシックを指定
float fontSize = 50;// 出力する文字の大きさを指定
//float size = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;// 指定フォントにおける1文字の高さを取得
//cont.transform(new Matrix(1, 0, 0, 1, 20, 100));// 出力領域の位置を指定(Matrix(前半4つ「線形変換を表す2行2列の行列の 00, 01, 10, 11成分に対応します」だそな。。。後半2つ「 X軸、Y軸方向への平行移動の成分」))
PDExtendedGraphicsState state = new PDExtendedGraphicsState();
state.setNonStrokingAlphaConstant(0.2f);// 透過率:1は無透過、0は完全に透過
cont.stGraphicsStateParameters(state);
cont.setNonStrokingColor(255, 0, 0);//色の指定:RGBで行える
cont.beginText();//出力文字の指定を開始する
cont.setTextMatrix(Matrix.getRotateInstance(0.7, (rectangle.getWidth()/4), (rectangle.getHeight()/6)*2));//出力領域の位置を指定(arg0:傾き、arg1:X軸の位置、arg2:Y軸の位置)
cont.setFont(font, fontSize);//フォントを設定
cont.newLine();//新規行を生成
cont.showText("これはテストです。");// 出力文字を指定
cont.endText();//出力文字の指定を終了する
cont.close();
tmp.save(tmpPath);//PDFを出力
} catch (IOException e1) {
// 必要があれば例外処理
e1.printStackTrace();
} finally {
try {
tmp.close();
} catch (IOException e) {
e.printStackTrace();
}
}
HashMap<Integer, String> overlayGuide = new HashMap<Integer, String>();// どのページにどのドキュメントを差し込むかの指定を保持するMAP
try (PDDocument doc = PDDocument.load(pdfPath)) {//元のPDFを読み込む
for (int i = 0; i < doc.getPages().getCount(); i++) {//PDFのページ数繰り返す
overlayGuide.put(i + 1, tmpPath);//1オリジンでどのページにどのドキュメントを重ねるかを指定
}
Overlay overlay = new Overlay();
overlay.setInputPDF(doc);//元のPDFのPDDocumentを指定
overlay.setOverlayPosition(Overlay.Position.FOREGROUND);//差し込むドキュメントの位置を指定(前面or背面)
overlay.overlay(overlayGuide);// MAPの設定に従い元のPDFにドキュメントを差し込む
//PDFファイルを出力
doc.save(outPath);// 加工後のPDFを出力する
overlay.close();
doc.close();
} catch (InvalidPasswordException e) {
// 必要があれば例外処理
e.printStackTrace();
} catch (IOException e) {
// 必要があれば例外処理
e.printStackTrace();
} finally {
// 一時ファイルを削除する
File fl = new File(tmpPath);
fl.deleteOnExit();
}
}
#コメントアウト行は個人的なメモのため、読み飛ばして頂きたい。。
実行すると、社外秘とかにありがちな斜めになった文字が元PDFの全ページに重ねられて表示されます。
実装内容について簡単に
実装内容について簡単に説明。
透かし用の文字を記入したPDFを作成。
このPDFを透かしを反映したいPDFの全ページに重ねるようにして、編集後のPDFを出力している。
このページを重ねる役割を担っているのが、Overlayオブジェクト。
どのページにどのドキュメントを重ねるかを指定するのはMAPで行う。
このため、あるページでは透かしを入れる、あるページでは入れないなどの出しわけが可能。
Overlay overlay = new Overlay();
overlay.setInputPDF(doc);//元のPDFのPDDocumentを指定
overlay.setOverlayPosition(Overlay.Position.FOREGROUND);//差し込むドキュメントの位置を指定(前面or背面)
overlay.overlay(overlayGuide);// MAPの設定に従い元のPDFにドキュメントを差し込む
透かし用PDFの透かし設定は、PDExtendedGraphicsStateオブジェクトのsetNonStrokingAlphaConstant()で行っている。
引数はフロート値で、1は無透過、0は完全に透過なので、その間の数値で透過率を指定する。
PDPageContentStream cont = new PDPageContentStream(tmp, tmpPage);
PDExtendedGraphicsState state = new PDExtendedGraphicsState();
state.setNonStrokingAlphaConstant(0.2f);// 透過率:1は無透過、0は完全に透過
cont.setGraphicsStateParameters(state);
その他、理解できた範囲でコメントをつけているので、参考にして頂ければ幸い。
別の実装方法もあるかと思うのでお試し下さい。。。