iTextでPDFを最適化する

iTextを使って、フォントや画像を埋め込んだ複数のPDFを1つのPDFにするケースはよくあると思います。
マージする先のDocumentのPdfWriterと、マージされる各PDFのPdfReaderを使用してマージしたりしますが、この場合、複数のPDFに同一のフォントや画像が埋め込まれていたとしても、出来上がったPDFにはそれらのフォントや画像が全部入ってしまい、PDFのサイズが大きくなってしまいます。

 Document doc = new Document();
 FileOutputStream fout = new FileOutputStream(new File("hoge.pdf"));
 PdfWriter writer = PdfWriter.getInstance(document, fout);
 
 String[] files = {"foo.pdf","bar.pdf","moge.pdf"};
 for(int i = 0;i < files.length;i++){
   PdfReader reader = new PdfReader(files[i]);
   int pageNum = reader.getNumberOfPages();
   for(int p = 1;p <= pageNum;p++){
    PdfImportedPage page = writer.getImportedPage(reader, p);
    PdfContentByte cb = writer.getDirectContent();
    cb.addTemplate(page, 1f, 0, 0, 1f, 0, 0);
   }
 }

例えばこの例だと、foo.pdfとbar.pdfに同一のフォントが入っていた場合でも、出来上がるhoge.pdfには同一の2つのフォントデータが入ってしまいます。


これを回避するには、PdfSmartCopyクラスを使用します。

 Document doc = new Document();
 FileOutputStream fout = new FileOutputStream(new File("hoge.pdf"));
 PdfSmartCopy copy= new PdfSmartCopy(document, fout);
 
 String[] files = {"foo.pdf","bar.pdf","moge.pdf"};
 for(int i = 0;i < files.length;i++){
   PdfReader reader = new PdfReader(files[i]);
   int pageNum = reader.getNumberOfPages();
   for(int p = 1;p <= pageNum;p++){
    PdfImportedPage page = copy.getImportedPage(reader, p);
    copy.addPage(page);
   }
 }

PdfSmartCopyは内部にPDFリソースのマップを持っており、同一のリソースの場合は同じところを見るようにPDFを生成してくれます。
以下PdfSmartCopyのJavaDocです。

/**
 * PdfSmartCopy has the same functionality as PdfCopy,
 * but when resources (such as fonts, images,...) are
 * encountered, a reference to these resources is saved
 * in a cache, so that they can be reused.
 * This requires more memory, but reduces the file size
 * of the resulting PDF document.
 */

フォントや画像の量にもよると思いますが、私の環境だとAcrobat Professionalの標準的な最適化よりもサイズを小さくすることが出来ました。
PDFのサイズを気にする向きは、これを利用されたらいいと思います。