トラスト・ソフトウェア・システム
トラスト・ソフトウェア・システム

PDF アプリケーション サンプル

PDFファイルを開き、それを構文解析するサンプルアプリケーションを公開します。 ここで示すアプリケーションは、PDF構文を説明するためのものですので、すべてのPDFファイルを構文解析できるものではありません。
このサンプルは、PDF Toolsとは無関係です。PDF構文を説明するための簡単なサンプルアプリケーションの一部です。

1.PDFファイル構造 - PDFファイルを開く

PDFファイルを開き、ヘッダーボディークロスリファレンストレイラーの各部分の位置を取得します。

1.2 PDFファイルを開く

以下では、PDFファイルを開き、それを内部バッファに読み取ります。
このコードは、PDFファイルを読み取るOpen(wchar_t *fn)メソッドの一部です。
int           fd;
struct _stat  stat;

//Clear
if(data.cbSize){ Cleanup(); Init(); }	//dataを初期化します。

if(_wsopen_s(&fd, fn, _O_BINARY|_O_RDONLY, _SH_DENYNO, _S_IREAD|_S_IWRITE)) return -1;
_fstat(fd, &stat);

//すべて読み取る
data.pBlobData = new BYTE [(data.cbSize = stat.st_size)];
if(_read(fd, data.pBlobData, data.cbSize)<(int)data.cbSize) return -2;
_close(fd);
Cleanup()およびInit()は、PDFデータを格納するdataを初期化するために別途定義されたメソッドです。
メソッドの引数で与えられたファイル名のPDFを開きデータを内部の領域(BLOB data)に格納します。
dataはグローバルな変数で、サンプル メソッドからアクセスできます。

1.2 ヘッダー

ヘッダー部に記載されたPDFバージョンを確認します。このサンプルでは、「PDF1.5~1.7」を対象としています。
int     offs;
//PDFのバージョンチェック
if(strncmp((char*)data.pBlobData, "%PDF-1.", 7)) return -3;
if(!strchr("4567",(int)data.pBlobData[7])) return -4;
headerOffs = 0; headerLen = 8; 
if(!strchr(WHITE_SPACE,(int)data.pBlobData[8])){
    while((unsigned)headerLen<data.cbSize) headerLen++;
    return -4;
}
PDFのバージョンはファイルの先頭に記載されており、"%"で開始する8文字で構成されています。
WHITE_SPACEは以下のように宣言されていて、PDFのホワイトスペースを選別します。
#define WHITE_SPACE "\011\012\014\015\040"    //HorizontalTab, LineFeed, FormFeed, CarriageReturn, Space

1.3 トレイラー

トレイラー部に記載された「trailer」とクロスリファレンステーブルの位置を取得します。
//"startxref"をデータの終わりから検索する
offs = data.cbSize - 9;
while(strncmp((char*)(data.pBlobData+offs), "startxref", 9)) offs--;
if(ParseXRef(offs)) return -5;

//"trailer"を検索する("startxref"の前にある)
while(strncmp((char*)(data.pBlobData+offs), "trailer", 7)) offs--;
if(ParseTrailer(offs+7)) return -6;
「trailer」とクロスリファレンステーブルは、PDFファイルの最後に記載されていますので、データの最後から逆順でキーワードを検索します。
ParseXRef( )は、クロスリファレンステーブルの構文解析をするメソッドです。
ParseTrailer( )は、trailer部を構文解析をするメソッドです。

1.4 クロスリファレンステーブルの解析

クロスリファレンステーブルの構文解をするメソッドです。
int ParseXRef(int offs)
{
    BYTE    *pdf = data.pBlobData;

    offs += 9;            //"startxref"を読み飛ばす
    while(((pdf[offs]<'0')||(pdf[offs]>'9'))&&((ULONG)offs<data.cbSize)) offs++;
    if((pdf[offs]<'0')||(pdf[offs]>'9')) return -1;
    offs = atoi((char*)(pdf+offs));

    //チェック
    if(strncmp((char*)(pdf+offs), "xref", 4)) return -2;
    offs += 4;

    //オブジェクト番号
    while((ULONG)offs<(data.cbSize-1)){
        //クロスリファレンステーブル構文解析
        int     startNum, endNum;

        while((pdf[offs]<'0')||(pdf[offs]>'9')) offs++;      //数字まで読み飛ばし
        startNum = atoi((char*)(pdf+offs));
        while((pdf[offs]>='0')&&(pdf[offs]<='9')) offs++;    //数字を読み飛ばし
        while((pdf[offs]<'0')||(pdf[offs]>'9')) offs++;      //数字まで読み飛ばし
        endNum = atoi((char*)(pdf+offs));
        while((pdf[offs]>='0')&&(pdf[offs]<='9')) offs++;    //数字を読み飛ばし
        while((pdf[offs]<'0')||(pdf[offs]>'9')) offs++;      //数字まで読み飛ばし

        //参照用クロスリファレンス作成
        for(int i = startNum; i < endNum; i++){
            int     elemOffs = atoi((char*)pdf+offs);
            int     elemGen = atoi((char*)(pdf+offs+11));
            char    elemNf = (char)(pdf+offs)[17];

            CXRef   e(elemOffs, elemGen, elemNf);
            if(elemNf == 'n')
                if(ParseIndirectObj(i, &e)) return -1;
            xref[i] = e;
            offs += 20;
        }

        //サブセクションが続く?
        if((pdf[offs]<'0')||(pdf[offs]>'9')) return 0;
    }

    return -2;
}
クロスリファレンス・テーブルは1つ以上のサブセクションで構成され、それぞれでインダイレクトオブジェクトごとのオフセットを示しています。 オフセット値を示す部分は、固定フォーマットですので、簡単に解析しています。ここで得られたオフセットから、使用中のインダイレクトオブジェクトの構文をParseIndirectObj( )メソッドで解析します。

xrefは、以下のように宣言されています。
map<int,CXRef>    xref;
CXRefは、クロスリファレンス・テーブルのエントリー(PDFのオブジェクト)を格納するためのクラスで以下のように宣言されています。
PDFオブジェクトのうちディクショナリは、Streamオブジェクトを伴う場合があります。その目的のためにStreamキーワードのついたプロパティ(streamOffsなど)とメソッド(GetStreamOffs( )など)を用意しています。
class CXRef
{
private:
    int     offs;      //オフセット値
    int     gen;       //世代番号
    char    nf;        //'n':利用中、'f':削除済み、NULL:未定義

    int     objOffs;   //オブジェクトのオフセット値
    int     objLen;    //オブジェクトの長さ
    int     objType;   //オブジェクトの種類

    int     streamOrgLength;  //<< .../Length nnn ... >>に示された長さ(nnn)
    int     streamOffs;       //Streamオブジェクトのオフセット値(キーワードを含まないバイナリーの値のみ)
    int     streamLen;        //Streamオブジェクトの長さ(同上)

public:
    CXRef(void);
    CXRef(const CXRef&);
    CXRef(int, int, char);
    ~CXRef(void);
private:
    void Init();
    void Cleanup();

public:
    int GetOffs();
    int GetGen();
    int GetNF();
    int GetObjectOffs();
    int GetObjectLen();
    int GetObjectType();
    int GetStreamOrgLength();
    int GetStreamOffs();
    int GetStreamLen();
    bool IsDeleted();        //削除されたオブジェクト
    bool HasStream();        //このオブジェクトがStreamオブジェクトを内包していれば"真"。Dictionary以外では常に"偽"
    void SetObjectOffs(int);
    void SetObjectLen(int);
    void SetObjectType(int);
    void SetStreamOffs(int);
    void SetStreamLen(int);
    void SetObjectInfo(int offs, int len, int type);
    void SetObjectInfo(int offs, int len, int type);
    void SetObjectInfo(int offs, int len, int type,int streamOrgLength, int streamOffs, int streamLen);
};
>>> 次へ

サンプル アプリケーションについて

PDFデータを解析できるサンプルアプリケーションを公開しています。PDFParse17_1_0.zip(Winfows版)をダウンロードして、お手元のPDFデータを解析してください。 このアプリケーションのソースコードは、PDF Tools SDKをご購入されたお客様のご希望により開示しています。詳細はお問い合わせください(メール)。


(記載の会社名および製品名は、各社の登録商標および商標です。)
PDF Tools 製品 概要
PDF Security
PDF Validator
PDF to PDF/A Converter
PDF to Image Converter
PDF Imager-LP
Image to PDF Converter
PDF Printer
PDF Prep Tool Suite
PDF Optimizer
PDF Command Line Suite
PDF Extract