Apache:PDFBoxを使って透かしを入れる


Apache:PDFBox

javaで一般的なPDF編集ツールというとiTextかと思うが、(商用目的で利用してソースを公開しない場合)iTextの場合ライセンス購入が必要なため、導入においてハードルがある。

初期導入時点であれば(金銭面で)話も通しやすいかもしれないが、既存システムへの導入となると困ることがある。。

ライセンスフリーでPDFの編集を行えるツールとして検討したところ、ApacheからPDFBoxなるものが提供されているとのことで、その実装についてメモ。

導入

以下のサイトからダウンロード可能。

(Apacheのサイトです)

記事作成時点の最新は2.0.7だが、実装時は2.0.7リリース前であったため、サンプル実装は2.0.6で行っている。

ダウンロードしたjarをプロジェクトに取り込んで導入完了。

サンプル実装

実装環境は、windows10端末、java1.8。

ちょっとググれば既に日本語で実装サンプルが上がっている。

PDFBoxを使って透かしを入れるサンプル
    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 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);

その他、理解できた範囲でコメントをつけているので、参考にして頂ければ幸い。

別の実装方法もあるかと思うのでお試し下さい。。。