Hello, Haru Free PDF Library!

C++ での PDF ライブラリだと Haru Free PDF Library の評判が良いようなので,このライブラリの使い方を少しずつメモっていこうと思います.取り合えず,最も基本的なところ(PDF ドキュメント作成からファイルへの保存まで)とテキスト処理まで.

基本形

HPDF_New() で初期化して,必要な処理をいろいろやって HPDF_SaveToFile() でファイルに書き出す,と言うのが基本形となります.

#include <cstdio>
#include <stdexcept>
#include <hpdf.h>

/* ------------------------------------------------------------------------- */
/*
 *  trivial_error_handler
 *
 *  最も簡易的なエラーハンドラー.HPDF_*** から渡された 2 種のエラー番号
 *  をメッセージとして std::runtime_error() を投げる.
 */
/* ------------------------------------------------------------------------- */
void trivial_error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void* user_data) {
    char message[1024] = {};
    sprintf(message, "error: error_no = %04X, detail_no = %d", static_cast<unsigned int>(error_no), static_cast<int>(detail_no));
    throw std::runtime_error(message);
}

/* ------------------------------------------------------------------------- */
//  main
/* ------------------------------------------------------------------------- */
int main(int argc, char* argv[]) {
    HPDF_Doc document = HPDF_New(trivial_error_handler, NULL);
    if (!document) return -1;
    
    int status = 0;
    try {
        /*
         * 日本語の場合.
         * 90ms-RKSJ-H, 90ms-RKSJ-V, 90msp-RKSJ-H, EUC-H, EUC-V が利用可能となる.
         */
        HPDF_UseJPEncodings(document);
        
        /*
         * MS-(P)Mincyo, MS-(P)Gothic が利用可能となる.
         * これらのフォントを使用する場合にはコールする(必要なければ呼ばなくても良い?).
         */
        HPDF_UseJPFonts(document);
        
        /*
         * それ以外のフォントを使用する場合,予めフォントファイルをロードしておく必要がある.
         * ロードしたフォントは,関数から返されたフォント名を用いてアクセスする事になるため,
         * このフォント名をどこかで記憶しておく必要がある.
         */
        
        /*
         * 拡張子が *.ttf のファイルの場合.
         * 第 3 引数はフォントを埋め込むかどうか (HPDF_TRUE or HPDF_FALSE).
         */
        // const char* name = HPDF_LoadTTFontFromFile(document, "folder/filename.ttf", HPDF_TRUE);
        
        /*
         * 拡張子が *.ttc のファイルの場合.
         * *.ttc ファイルには複数のフォントが格納されているため,どのフォントを使用するかを
         * 第 3 引数にインデックスで指定する.
         * 第 4 引数はフォント埋め込みの有無 (HPDF_TRUE or HPDF_FALSE) を指定する.
         */
        // const char* name = HPDF_LoadTTFontFromFile2(document, "folder/filename.ttc", 0, HPDF_TRUE);

        /*
         * メイン (?) 処理.
         * 基本的には,HPDF_AddPage() で新しいページを作成し,
         * 返された HPDF_Page ポインタに対して表示したいコンテンツを追加していくと言う形となる.
         */
        {
            HPDF_Page page = HPDF_AddPage(document);
            // ... do something ...
        }
    
        HPDF_SaveToFile(document, "folder/filename.pdf");
    }
    catch (std::runtime_error& err) {
        std::cerr << err.what() << std::endl;
        status = -1;
    }
    
    HPDF_Free(document);
    return status;
}

テキスト処理

テキスト処理に関しては,PDF ファイルに "BT ... ET" と実際に記述する部分が薄くラップされているだけの形になっているので,PDF ファイルの内部 (stream 内部) 構造を知っていると各関数の役割がイメージしやすくなります.PDF ファイルの構造については,PDF by Hand - Kobu.Com 等を参照下さい.

HPDF_Page page = HPDF_AddPage(document);

/*
 * 使用するフォントをフォント名で取得する.
 * 第 2 引数に指定する文字列は,Courie, Helvetica, MS-Gothic, MS-Mincyo などや
 * HPDF_LoadTTFontFromFile() で返されたフォント名.
 * 第 3 引数にはエンコーディングを指定する.
 * 90ms-RKSJ-H は Shift_JIS (正確には CP932 か) の横書き.
 * ここで,例えば 90ms-RKSJ-V と指定すると,これ以降に出力するテキストは縦書きになる.
 */
HPDF_Font font = HPDF_GetFont(document, "font name", "90ms-RKSJ-H");

/*
 * PDF の (テキスト出力の) フォーマットは,"BT ... ET" と言う形になっている.
 * そのため,テキスト出力開始時に HPDF_Page_BeginText() を
 * テキスト出力終了時に HPDF_Page_EndText() を呼ぶ必要がある.
 */
HPDF_Page_BeginText(page);
{
    /*
     * 出力するテキストのフォントを設定する.
     * 第 2 引数にフォント名,第 3 引数にフォントサイズを指定する.
     */
    double font_size = 10.5;
    HPDF_Page_SetFontAndSize(page, font, font_size);
    
    /*
     * 出力するテキストの色を RGB で設定する.
     * ただし,PDF では RGB 各値を [0,1] で指定することになっている.
     * そのため,白 (#ffffff) は (1, 1, 1), 灰色 (#7f7f7f) は (0.5, 0.5, 0.5) となる.
     */
    HPDF_Page_SetRGBFill(page, 0, 0, 0);
    
    /*
     * 文字列を出力する方法は,大きく分けて 3 通りある.
     *
     * 1. HPDF_Page_TextOut() で出力.
     * HPDF_Page_TextOut() には,(x, y)座標,文字列を指定する.
     * この場合は,ここで指定された座標を始点として表示される.
     * ただし,PDF の座標は左下が原点となる.そのため,多くの場合は (0, page_height)
     * のように,y 座標の指定には注意する必要がある.
     *
     * HPDF_Page_TextOut(page, x, y, description);
     *
     *
     * 2. HPDF_Page_MoveTextPos() で移動しながら HPDF_Page_ShowText() で出力.
     * HPDF_Page_MoveTextPos() は,移動させる数値を現在の座標からの相対座標で指定する.
     * 始点となる座標をいったん決めて,(改行などで) 少しずつ移動させながらテキストを
     * 順次出力していく場合には,これら 2 つの関数を使って出力していく.
     * 尚,この際も PDF の原点に注意する.例えば,横書きで改行しながらテキストを
     * 出力する場合は,以下のようになる.
     *
     * HPDF_Page_MoveTextPos(page, 0, -font_size);
     * HPDF_ShowText(page, description);
     *
     *
     * 3. HPDF_Page_SetTextMatrix() を使用.
     * HPDF_Page_SetTextMatrix() は,拡大/縮小/回転/移動を 3x2 行列で指定するためのものである.
     * 例えば,1. の HPDF_Page_TextOut(page, x, y, description) を行列を用いて表すと
     * 以下のようになる.
     *
     * HPDF_Page_SetTextMatrix(page, 1, 0, 0, 1, x, y);
     * HPDF_ShowText(page, description);
     *
     * その他,簡単な例として,
     *
     * テキストを 2倍に拡大する場合:
     * HPDF_Page_SetTextMatrix(page, 2, 0, 0, 2, x, y);
     *
     * テキストを 60 度回転させる場合:
     * double radian = 60 / 180.0 * 3.141592;
     * HPDF_Page_SetTextMatrix(page, cos(radian), sin(radian), -sin(radian), cos(radian), x, y);
     *
     * 等がある.
     *
     */
    double x = 10.0;
    double y = HPDF_Page_GetHeight(page) - font_size;
    HPDF_Page_SetTextMatrix(page, 1, 0, 0, 1, x, y);
    HPDF_ShowText(page, "description");
     
    /*
     * その他,文字間隔の調整,文字の見た目 (中抜きされた文字等) などテキスト出力には
     * 様々なオプションが存在する.詳細は,
     * http://libharu.sourceforge.net/page_handling.html
     * http://libharu.sourceforge.net/demo/text_demo.c
     * などを参照.
     */
    // HPDF_Page_SetTextRenderingMode (page, HPDF_STROKE);
    // HPDF_Page_SetCharSpace(page, 1.5);
    // HPDF_Page_SetWordSpace(page, 2.5);
}
HPDF_Page_EndText(page);

C++ ラッパ

Haru Free PDF Libray を C++ でラッパしたものを作成しようと思ったら POCO C++ Libraries の sandbox にありました.まだ正式版ではないようなのですが,どんな感じになっているのか一度見てみようと思います.