PDFlib

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

テキストフィールドのコピー

1) はじめに

PDFlib 製品の1つである PDFlib+PDIを使用して、既存 PDF ファイルが持つテキストフィールドを取得し、新規 PDF ファイルに出力する方法をご紹介します。

なお PDFlib 10 ではページ上のインタラクティブ要素の取り込みに対応したため、テキストフィールドを維持したままページを取り込むことができます。

2) テキストフィールドのコピーの方法

PDFlib+PDI には「テキストフィールドをコピーする」機能はありませんが、PDFlib+PDI に内蔵されている pCOS インターフェースを使って既存の PDF ファイルからフォームフィールドを取得し、PDFlib の機能で新規 PDF ファイルにフォームフィールドを作成することで、テキストフィールドのコピーを実現します。

フォームフィールドには様々な種類がありますのが、今回対象とするのはテキストを入力する textfield のみを対象とします。また下記のオプションをサポートします。

・読み取り専用
・入力必須
・複数行入力
・入力文字数制限

重要な点として、本サンプルではテキストフィールドのフォント情報の引き継ぎは行いません。PDFlib では入力 PDF 内のフォントデータを出力 PDF にコピーすることはできないため、これを実現する場合、あらかじめ使用される可能性のあるフォントファイルを用意しておき、入力 PDF の内容に合わせてフォントを選択する必要があります。
このサンプルではそこまでは行わず、常にシステムフォントの MS-Gothic で出力側のフォームフィールドを作成することにします 。Linux 等で実行する場合や他の任意のフォントを使用する場合、適切に変更する必要があります。

基本的な処理の流れは下記のとおりです。(今回は、Java を使用して記述しています)


/*
 * テキストフィールドのコピー
 *
 * 2021 Copyright (c) infoTek K.K. all rights reserved.
 *
 * 必要な製品:PDFlib+PDI/PPS 9
 */

import com.pdflib.PDFlibException;
import com.pdflib.pdflib;

class textfields {
    public static void main(String[] args) {

        pdflib p = null;
        String optlist;

        try {
            /* PDF オブジェクトを生成 */
            p = new pdflib();
            p.set_option("errorpolicy=return");

            /* 新規 PDF 文書を開く */
            if (p.begin_document("new_textfields.pdf", "") == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            /* 文書情報を設定 */
            p.set_info("Creator", "textfields.java");
            p.set_info("Author", "infoTek");
            p.set_info("Title", "テキストフィールドのコピー");

            /* フォームフィールド用のフォントを読み込み
             * Windows での実行を前提に MS-Gothic を使用しています。
             * Linux 等で実行する場合は適切なフォントに置き換えてください。
             */
            p.set_option("FontOutline={MS-Gothic={C:/Windows/Fonts/msgothic.ttc}}");
            int font = p.load_font("MS-Gothic", "unicode", "");

            /* 既存 PDF 文書を開く */
            int doc = p.open_pdi_document("textfields.pdf", "");
            if (doc == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            /* 既存 PDF 文書のぺージ数を pCOS 関数で取得する */
            int endpage = (int)p.pcos_get_number(doc, "length:pages");

            /* 既存 PDF 文書が複数ページがある場合、必要分繰り返す */
            for (int pageno = 1; pageno <= endpage; pageno++) {

                /* 既存 PDF ページを開く */
                int page = p.open_pdi_page(doc, pageno, "");
                if (page == -1) {
                    throw new Exception("Error: " + p.get_errmsg());
                }

                /* 新規 PDF ページをダミーのページサイズで作成する。 */
                p.begin_page_ext(10, 10, "");

                /* 新規 PDF ページ上に既存 PDF ページをインポートし、サイズを調整する */
                p.fit_pdi_page(page, 0, 0, "adjustpage");

                /* ページ上のテキストフィールドをコピーする */
                copy_textfields(p, doc, pageno, font);

                /* 既存 PDF ページを終了する */
                p.close_pdi_page(page);

                /* 新規 PDF ページを終了する*/
                p.end_page_ext("");
            }

            /* 既存 PDF 文書を終了する */
            p.close_pdi_document(doc);

            /* 新規 PDF 文書を終了する */
            p.end_document("");
        }
        catch (PDFlibException e) {
            System.err.println("PDFlib exception occurred:");
            System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() + ": " + e.get_errmsg());
        }
        catch (Exception e) {
            System.err.println(e);
        }
        finally {
            if (p != null) {
                p.delete();
            }
        }
    }

    public static void copy_textfields(pdflib p, int doc, int pageno, int font) throws PDFlibException, Exception {

        /* pageno ページ上の「注釈」の数を pCOS 関数で取得する */
        String pagepath = "pages[" + (pageno - 1) + "]";
        int annotscount = (int)p.pcos_get_number(doc, "length:" + pagepath + "/annots");
        if (annotscount == 0) {
            return;
        }

        /* 既存 PDF 文書内の「注釈」分だけ繰り返す */
        for (int i = 0; i < annotscount; i++) {

            String path = pagepath + "/annots[" + i + "]";

            /* 注釈のうち、Subtype が "Widget" かつ FT が "Tx" のもののみを対象にする */
            if (!(p.pcos_get_string(doc, path + "/Subtype").equals("Widget"))) {
                continue;
            }
            if (!(p.pcos_get_string(doc, path + "/FT").equals("Tx"))) {
                continue;
            }

            String optlist, type;

            /* 座標の取得
             * テキストフィールドの座標は /Rect から取得できる
             */
            double llx = p.pcos_get_number(doc, path + "/Rect[0]");
            double lly = p.pcos_get_number(doc, path + "/Rect[1]");
            double urx = p.pcos_get_number(doc, path + "/Rect[2]");
            double ury = p.pcos_get_number(doc, path + "/Rect[3]");

            /* フォント情報の取得
             * フォント情報は /DA から取得できる
             * このサンプルでは簡単のため、フォントを固定してフォントサイズのみを取得する
             */
            String[] da = p.pcos_get_string(doc, path + "/DA").split(" ");
            if(da.length < 2) {
                throw new Exception("cannot get fontsize");
            }
            String fontsize = da[1];
            optlist = "font={" + font + "} fontsize={" + fontsize + "}";

            /* 名前の取得
             * テキストフィールドの名前は /T から取得できる
             */
            String name = "";
            type = p.pcos_get_string(doc, "type:" + path + "/T");
            if(type.equals("string")) {
                name = p.pcos_get_string(doc, path + "/T");
            }

            /* 現在値の取得
             * テキストフィールドの現在地は /V から取得できる
             */
            type = p.pcos_get_string(doc, "type:" + path + "/V");
            if(type.equals("string")) {
                String value = p.pcos_get_string(doc, path + "/V");
                optlist += " currentvalue={" + value + "}";
            }

            /* テキストフラグの取得
             * テキストフィールドではいくつかのフラグをセットすることができ、
             * これを PDFlib の対応するオプションに変換する必要がある
             * ここでは良く使用するいくつかのフラグのみを扱う
             */
            type = p.pcos_get_string(doc, "type:" + path + "/Ff");
            if(type.equals("number")) {
                int Ff = (int) p.pcos_get_number(doc, path + "/Ff");
                if((Ff & 1) != 0) {
                    // ReadOnly
                    optlist += " readonly=true";
                }
                if((Ff & 2) != 0) {
                    // Required
                    optlist += " required=true";
                }
                if((Ff & 4096) != 0) {
                    // Multiline
                    optlist += " multiline=true";
                }
            }

            /* 最大長の取得 (任意)
             * テキストフィールドの最大長は /MaxLen から取得できる
             */
            type = p.pcos_get_string(doc, "type:" + path + "/MaxLen");
            if(type.equals("number")) {
                int maxLen = (int) p.pcos_get_number(doc, path + "/MaxLen");
                optlist += " maxchar={" + maxLen + "}";
            }

            /* テキストフィールドを作成する */
            p.create_field(llx, lly, urx, ury, name, "textfield", optlist);
        }
    }
}

3) インタラクティブ要素のコピーの注意点

PDFlib+PDI で既存の PDF ページからインポートできるのは、ページのコンテンツ部分のみです。リンク、しおり、注釈、フォームフィールドなどの、インタラクティブ要素は、インポートされません。

既存 PDF に、どのようなインタラクティブ要素が含まれているか、あらかじめ分かっている場合は、PDFlib+PDI に含まれる pCOS インターフェース の機能により、その情報を取得して PDFlib で新規の PDF に生成することが可能です。

4) pCOS パスによるインタラクティブ要素の取得

PDFlib 製品に含まれている pCOS インターフェースには、pCOSパスと呼ばれる PDF からページコンテンツ以外の属性情報を取得する擬似オブジェクトがあります。これを使用してインタラクティブ要素を取得することができます。

pCOS パスを使用したインタラクティブ要素取得のサンプルプログラムついては、ダウンロードページまたは、PDFlib pCOS サンプル集(クックブック)で Java、PHP のソースを公開していますのでご覧ください。

なお、pCOS パスが用意されていないインタラクティブ要素を取得したい場合、PDFの仕様に沿って取得することはできますが、PDF の仕様に関する知識が必要になります。

技術情報では、PDF の構造を分析し、出力するツール 「PDFinfo」 を公開しています。PDF にどんなインタラクティブ要素が含まれているか知りたい。pCOSパスの用意がないものについて、キーワードとなる情報を取得したい。といった場合にお役立てください。

5) 新規PDF へのインタラクティブ要素の生成

上記で取得したインタラクティブ要素を、PDFlibを使用して新規 PDF 上に生成します。サンプルプログラムについては、 ダウンロードページまたは、 PDFlib サンプル集(クックブック)で Java、PHP のソースを公開していますのでご覧ください。

6) おわりに

PDFlib 製品はご購入前でも、ダウンロードページからダウンロードし、評価版として試用することができます。評価版の状態でも、いくつかの制限を除き製品版と同様にお使いいただけますので、ぜひ PDFlib 製品をお試しください。

関連ページ

(Aug 4, 2021 - Dec 6, 2021)