PDFlib

高度なPDFアプリケーションの開発を支援する定番プログラムライブラリー Supported by インフォテック株式会社

PDFlib TET サンプル集(クックブック)

本サンプルプログラムは、PDF テキスト抽出ライブラリーの実装である TET の基本的な機能を実際のプログラムで紹介したものです。

本サイトでダウンロードした TET は、一部機能の制限を除き、評価版として無償でお使いいただけます。

イメージの読み取り

PDFlib TET で、 PDF イメージを読み取るサンプルプログラムです。ここでは、イメージをメモリ上に抽出し、Java Image I/O API に引き渡してイメージのメタデータを取得します。

この JRE と一緒に提供される javax.imageio パッケージは、サポートされるフォーマットに制限があります。例えば、TIFF イメージのために out-of-the-box はサポートされません。TIFF や JPEG2000 等のその他のフォーマットを得るために、「Java Advanced Imaging Image I/O」ツールをインストールする必要があります。その際、プログラムや Ant ビルドファイルの変更は必要としません。ライブラリが classpath によって見つける事ができれば十分です。詳しくはダウンロードした クックブックの doc ディレクトリにある readme.txt をご覧ください。

テスト中、JAI 1.1. ライブラリには JPEG2000 ファイルに関する問題が見つかっています。この問題が原因の場合、下記のようなエラーになります。


java.lang.NullPointerException
at com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata.replace(J2KMetadata.java:962)
at com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata.addNode(J2KMetadata.java:631)
at jj2000.j2k.fileformat.reader.FileFormatReader.readFileFormat(FileFormatReader.java:279)
at com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata.<init>(J2KMetadata.java:135)
at com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReader.getImageMetadata(
      J2KImageReader.java:419)
at com.pdflib.cookbook.tet.image.image_reader.print_metadata(image_reader.java:243)
at com.pdflib.cookbook.tet.image.image_reader.main(image_reader.java:171)

この問題は、現在 JAI 1.2 デイリービルドで修正されています。


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.pdflib.TETException;
import com.pdflib.TET;

/**
 * PDFlib TET で、 PDF イメージを読み取るサンプルプログラムです。ここでは、イメージを
 * メモリ上に抽出し、Java Image I/O API に引き渡してイメージのメタデータを取得します。
 * 
 * この JRE と一緒に提供される javax.imageio パッケージは、サポートされるフォーマットに
 * 制限があります。例えば、TIFF イメージのために out-of-the-box はサポートされません。
 * TIFF や JPEG2000 等のその他のフォーマットを得るために、「Java Advanced Imaging Image 
 * I/O」ツールをインストールする必要があります。その際、プログラムや Ant ビルドファイルが 
 * の変更を必要としません。ライブラリclasspath によって見つける事ができれば十分です。詳し
 * くはダウンロードしたクックブックの doc ディレクトリにある readme.txt をご覧ください。
 * 
 * テスト中、JAI 1.1. ライブラリには JPEG2000 ファイルに関する問題が見つかっています。次に
 * 類似する例外は、その兆候と思われます。
 * 
 * java.lang.NullPointerException
 *    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata.replace(J2KMetadata.java:962)
 *    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata.addNode(J2KMetadata.java:631)
 *    at jj2000.j2k.fileformat.reader.FileFormatReader.readFileFormat
 *                                                                (FileFormatReader.java:279)
 *    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KMetadata.(J2KMetadata.java:135)
 *    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReader.getImageMetadata
 *                                                                  (J2KImageReader.java:419)
 *    at com.pdflib.cookbook.tet.image.images_in_memory.print_metadata
 *                                                                (images_in_memory.java:243)
 *    at com.pdflib.cookbook.tet.image.images_in_memory.main(images_in_memory.java:171)
 *
 * この問題は、現在 JAI 1.2 デイリービルドで修正されています。
 * 
 * 必要な製品: TET 4
 *
 * 必要なデータ: PDF 文書
 * 
 * @version $Id: images_in_memory.java,v 1.1 2010/07/20 14:13:23 stm Exp $
 */
public class images_in_memory
{
    /**
     * グローバルオプションリスト
     */
    static final String GLOBAL_OPTLIST = "searchpath={../resource/cmap "
        + "../resource/glyphlist ../input}";
    
    /**
     * 文書の特別なオプションリスト
     */
    static final String DOC_OPTLIST = "";
    
    /**
     * ページの特別なオプションリスト
     */
    static final String PAGE_OPTLIST = "granularity=page";
    
    /**
     * 基本的な画像抽出オプション(詳細は下記の通り)
     */
    static final String BASE_IMAGE_OPTLIST = "compression=auto format=auto";

    /**
     * System.out に送られるエンコーディング。
     * 例えば、コマンドウィンドウでデフォルトエンコーディングと異なる文字コードを
     * 指定することがする。
     */
    private static final String OUTPUT_ENCODING = System
            .getProperty("file.encoding");

    /**
     * メタデータツリーで使用される、インデントのための空白、またはタブ
     */
    private static final String METADATA_INDENTATION = "  ";
    
    /**
     * OUTPUT_ENCODING にて指定されたエンコーディングで System.out より出力
     */
    private static PrintStream out;
    
    public static void main(String argv[]) throws UnsupportedEncodingException {
        System.out.println("Using output encoding \"" + OUTPUT_ENCODING + "\"");
        out = new PrintStream(System.out, true, OUTPUT_ENCODING);

        TET tet = null;

        try {
            if (argv.length != 1) {
                throw new Exception("usage: images_in_memory ");
            }

            tet = new TET();

            tet.set_option(GLOBAL_OPTLIST);

            int doc = tet.open_document(argv[0], DOC_OPTLIST);
            if (doc == -1) {
                throw new Exception("Error " + tet.get_errnum() + "in "
                        + tet.get_apiname() + "(): " + tet.get_errmsg());
            }

            /* 文書のページ番号を取得する */
            int n_pages = (int) tet.pcos_get_number(doc, "length:pages");

            /* ページ数分ループする */
            for (int pageno = 1; pageno <= n_pages; ++pageno) {
                int page = tet.open_page(doc, pageno, PAGE_OPTLIST);

                if (page < 0) {
                    print_tet_error(tet, pageno);
                    continue; /* 次のページへ */
                }

                /* ページ上で全てのイメージを取得する */
                int imageno = -1;
                while (tet.get_image_info(page) == 1) {
                    imageno++;
                    
                    /*
                     * write_image_file() で"typeonlly"を指定することで、イメージファイルを
                     * 生成せずに、イメージの種類のみを返す
                     */
                    int imageType = tet.write_image_file(doc, tet.imageid,
                            BASE_IMAGE_OPTLIST + " typeonly");
                    
                    /*
                     * イメージの種別を表す数値を種別名へマップする。
                     * write_image_file()の戻り値については、TETのマニュアルを参照のこと。
                     */
                    String imageFormat;
                    switch (imageType) {
                    case 10:
                        imageFormat = "tiff";
                        break;

                    case 20:
                        imageFormat = "jpg";
                        break;

                    case 30:
                        imageFormat = "jpeg2000";
                        break;

                    case 40:
                        imageFormat = "raw";
                        break;

                    default:
                        System.err.println("Page " + pageno + " image "
                                + imageno
                                + ": write_image_file returned unknown value "
                                + imageType + ", skipping image, error: "
                                + tet.get_errmsg());
                        continue;
                    }
                    
                    /*
                     * イメージを抽出し、メモリに書き込む
                     */
                    byte imagedata[] = tet.get_image_data(doc, tet.imageid,
                            BASE_IMAGE_OPTLIST);

                    if (imagedata == null) {
                        print_tet_error(tet, pageno);
                        continue; /* 次のイメージを処理 */
                    }
                    
                    /*
                     * イメージのメタデータを抽出する
                     */
                    print_metadata(imagedata, imageFormat, pageno, imageno + 1);
                }

                if (tet.get_errnum() != 0) {
                    print_tet_error(tet, pageno);
                }

                tet.close_page(page);
            }

            tet.close_document(doc);
        }
        catch (TETException e) {
            System.err.println("TET exception occurred in extractor sample:");
            System.err.println("[" + e.get_errnum() + "] " + e.get_apiname()
                    + ": " + e.get_errmsg());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (tet != null) {
                tet.delete();
            }
        }
    }

    /**
     * ImageReader クラスよりバイナリイメージデータを取り出し、メタデータを出力する
     * 
     * @param imagedata
     *            イメージのバイナリデータ
     * @param imageFormat
     *            イメージのフォーマット名
     * @param pageno
     *            イメージデータが含まれているページ番号
     * @param imageno
     *            ページ内でのイメージデータの番号
     * 
     * @throws IOException
     *             ImageIO API によるエラー
     */
    private static void print_metadata(byte[] imagedata,
            String imageFormat, int pageno, int imageno) throws IOException {
        /*
         * ImageReader クラスよりバイナリ画像データを取り出す
         */
        Iterator<ImageReader> readerIterator = ImageIO.getImageReadersByFormatName(imageFormat);
        if (readerIterator != null && readerIterator.hasNext()) {
            /*
             * 初めの1つのみImageReaderオブジェクトを取り出す
             */
            ImageReader reader = (ImageReader) readerIterator.next();
            
            /*
             * バイナリデータから ImageInputStream を生成し、 ImageReaderインスタンスに
             * 設定する
             */
            ImageInputStream inputStream =
                ImageIO.createImageInputStream(new ByteArrayInputStream(imagedata));
            reader.setInput(inputStream);
            
            /*
             * メタデータを取得し、出力する
             */
            IIOMetadata metadata = reader.getImageMetadata(0);
            if (metadata != null) {
                String format = metadata.getNativeMetadataFormatName();
                Node tree = metadata.getAsTree(format);
                print_metadata(tree, pageno, imageno);
            }
        }
        else {
            System.err.println("No Java ImageReader available for suffix "
                    + imageFormat);
        }
    }

    /**
     * DOM ツリーからメタデータを出力する
     * 
     * @param tree
     *            出力のための DOM ツリー
     * @param pageno
     *            メタデータを持つイメージのページ番号
     * @param imageno
     *            ページ内のイメージの数
     */
    private static void print_metadata(Node tree, int pageno, int imageno) {
        out.println("Metadata for image "
                + imageno + " on page " + pageno + ":");
        print_metadata(tree, 0);
    }
    
    /**
     * DOM サブツリーをたどり、ノード名と値を出力する
     * 
     * @param node
     *            出力するサブツリー
     * @param level
     *            インデントに使用される総ツリー内の現在のレベル
     */
    private static void print_metadata(Node node, int level) {
        String indentation = get_indentation(level);
        out.print(indentation + "node=\"" + node.getNodeName() + "\"");
        String value = node.getNodeValue();
        if (value != null) {
            out.print(" value=\"" + value  + "\"");
        }
        out.println();
        NamedNodeMap map = node.getAttributes();
        if (map != null) {
            int length = map.getLength();
            if (length > 0) {
                out.print(indentation + " "); 
                for (int i = 0; i < length; i++) {
                    Node attr = map.item(i);
                    out.print(" " + attr.getNodeName() + "=\""
                            + attr.getNodeValue() + "\"");
                }
                out.println();
            }
        }
        
        NodeList children = node.getChildNodes();
        for (int i = 0; i < children.getLength(); i += 1) {
            print_metadata(children.item(i), level + 1);
        }
    }

    /**
     * パラメータのレベルからインデントの空白文字数を算出する
     * 
     * @param level
     *            インデントのレベル
     *            
     * @return インデントのレベルから算出した、空白数
     */
    private static String get_indentation(int level) {
        StringBuffer indentation = new StringBuffer();
        for (int i = 0; i < level; i += 1) {
            indentation.append(METADATA_INDENTATION);
        }
        return indentation.toString();
    }

    /**
     * TET エラーレポート
     * 
     * @param tet
     *            TET オブジェクト
     * @param pageno
     *            エラーとなったページ番号
     */
    private static void print_tet_error(TET tet, int pageno) {
        System.err.println("Error " + tet.get_errnum() + " in  "
                + tet.get_apiname() + "() on page " + pageno + ": "
                + tet.get_errmsg());
    }
}
(May 6, 2010 - Jul 17, 2015)