November 27, 2007

完成済みPDFにヘッダを埋め込む

とある事情で40個くらいの完成済みPDF全ての1ページ目のみにヘッダを入れる必要がでてきました。このような編集機能を総称するとアノテーションというそうで、製品版のAdobe Acobat(無償のReaderではなく、作成や編集機能がついたもの)を持っているとGUI操作でできるようです。
しかしながら普段からPDFはTeX経由で作っていたり、M$のwordやPower Pointをあえて使う必要があっても無償のPrimoPDFでPDF作りに励んでいるため、残念ながら手元にAcobatがありません。またこの40個程度という数が微妙で、GUI操作、というかマウスを使う操作が比較的嫌いな僕としては、CUIなコマンドツールの方がうれしいです。
そこで今回は色々と調べまわってみた結果、解決にたどり着くことができましたので、それをまとめておこうと思います。

トップページから読まれている方は続きをどうぞ。

まずはGUIでも構わないので、このような機能が無償で提供されているツールがあるか調べてみました。『無料 PDF 編集』でGoogleを引いてみると、『PDF編集ツール情報』というページがPDFに対して行う操作別に整理されており参考になりました。実は以前、PDFの結合について記事を書いたことがありましたが、そのときも解決法を模索するにあたって、このページから有用なヒントを得た記憶があります。
残念ながら完全に目的に一致したツール(無償で特定ページのみヘッダが入れれる、かつCUI操作可能)を探し当てることはできませんでしたが、完成済みPDFにヘッダやページ番号を埋め込むアノテーション機能を持つツールの多くは、iTextという共通のPDF操作ライブラリを使用していることがわかりました。

iTextはJavaのライブラリです。Javaはここ久しく触っていないため、できればRubyネイティブのライブラリが良かったのですが、iTextの豊富な機能に押されてJavaでちょっとしたプログラムを組むことにしました(実はRuby Java BridgeによってRubyからiTextをコントロール可能だそうですが、少し手間がかかりそうであったので試していません)。実際iTextについて検索をしてみると、『PDFファイルをパスワード設定し、ヘッダーとフッターを追加』というかなり近い内容がヒットし、またiTextの公式APIドキュメントや、氏原さんという方が公開されている日本語チュートリアルなど参考になるものが多数あるので、Javaで目的のコードを書くこと自体、結果的には苦痛になりませんでした。

で完成した目的のコードは以下のようなものです。

package pdf;

import java.io.FileOutputStream;
import java.io.IOException;
import com.lowagie.text.*;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.HeaderFooter;
import com.lowagie.text.PageSize;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;

public class PdfAddHeader {
    
    public static void main(String[] args) throws Exception {
        System.out.println("Add Header");
        
        try {
        
            try {
                
                // TemplatePageの作成
                Document document = new Document(PageSize.A4,
                        PageSize.A4.getWidth() / 210 * 25,
                        PageSize.A4.getWidth() / 210 * 25,
                        PageSize.A4.getWidth() / 294 * 25,
                        PageSize.A4.getWidth() / 294 * 25);
                
                BaseFont bfArial =
                    BaseFont.createFont(
                     "C:\\WINDOWS\\FONTS\\ARIAL.TTF",
                     BaseFont.CP1252,
                     BaseFont.NOT_EMBEDDED);

                PdfWriter.getInstance(document, new FileOutputStream("template.pdf"));
                
                // set header
                String posterNum = (args.length < 1) ? "hoge" : args[0];
                HeaderFooter header = new HeaderFooter(
                        new Phrase(9,
                     "Header1\r\n" +
                     "Header2 NUM-" + posterNum, new Font(bfArial, 9)), false);
                header.setAlignment(Element.ALIGN_RIGHT);
                header.setBorder(Rectangle.NO_BORDER);
                document.setHeader(header);
            
                document.open();             
                document.add(new Paragraph(" "));
                
                document.close();
            
            } catch (DocumentException de) {
                System.err.println(de.getMessage());
            } catch (IOException ioe) {
                System.err.println(ioe.getMessage());
            }
            
            PdfReader reader_orig = new PdfReader("orig.pdf");
            
            PdfStamper stamp = new PdfStamper(reader_orig, new FileOutputStream("modified.pdf"));
            PdfReader reader_template = new PdfReader("template.pdf");
            
            if(reader_orig.getNumberOfPages() > 1){
                PdfContentByte under = stamp.getUnderContent(1);
                under.addTemplate(stamp.getImportedPage(reader_template, 1), 0, 0);
            }
            stamp.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        
        System.out.println("DONE!");
    }
}

これで作成済みPDFのorig.pdfに対して、1ページ目、上下左右25mmあるマージンのうち右上に、Arial 9ポイント2段の右揃いでヘッダ情報が記入されたPDFが、modified.pdfという名前で出力されるようになりました。あとは適当に改造して、外部からRubyで40個程度のPDFを処理するスクリプトを用意し、めでたく問題解決となりました。

少し補足をしておきますと日本語でヘッダを作る場合、フォントに注意する必要があるようです。その辺りの事情は@ITのiTextの解説ページ等をご覧ください。

14:48 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク | この記事をdel.icio.usでブックマーク | トラックバック
このエントリーのトラックバックURL: http://fenrir.naruoka.org/mt/mt-tb.cgi/604
コメント
コメントする









名前、アドレスを登録しますか?
(次回以降コメント入力が楽になります)
  • 匿名でのコメントは受け付けておりません。
  • 名前(ハンドル名可)とメールアドレスは必ず入力してください。
  • メールアドレスを表示されたくないときはURLも必ず記入してください。
  • コメント欄でHTMLタグは使用できません。
  • コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
  • コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。