2016-2-28[日] libharu を使ってjpg画像を右綴じpdfに変換

本の自炊で右綴じ化のために Acrobat を立ち上げるのは面倒で、他のフリーのpdfツールもたいていGUIで面倒そうで手頃なのが見つからないのでコマンドラインツールを作ってみることにした。

pdf作成で手頃そうなライブラリとして libharu(libhpdf.lib)というのがあったのでそれを採用... なんだけど、pdf 読込がよくわからず(出来ないのか出来ても面倒なのか...)。

もっとも jpg画像からpdf化に imagemagick を使ってるのだからその工程も自作ツールでやればよい話、と気づけばわりと簡単だった。(imagemagick では空白や日本語のあるファイル名の扱いに難ありだったし)

まず複数のjpgから1つのpdf生成するのは jpeg_demo.c あたりをいじって以下

#include <stdio.h>
#include <setjmp.h>
#include <hpdf.h>

static jmp_buf s_jmp_buf_env;

static void errorHandlerForHaru(HPDF_STATUS err, HPDF_STATUS   detNo, void *udat) {
    printf ("ERROR: err=%04X, detNo=%u\n", (HPDF_UINT)err, (HPDF_UINT)detNo);
    longjmp(s_jmp_buf_env, 1);
}

int main (int argc, char *argv[]) {
    if (argc < 2) {
        printf("usage> jpg2pdf jpgfile(s).jpg ...\n");
        return 1;
    }
    HPDF_Doc    pdf = HPDF_New (&errorHandlerForHaru, NULL);
    if (!pdf) {
        printf ("error: cannot create PdfDoc object\n");
        return 1;
    }
    if (setjmp(s_jmp_buf_env)) {
        HPDF_Free (pdf);
        return 1;
    }
    HPDF_SetCompressionMode (pdf, HPDF_COMP_ALL);
    /*☆*/
    for (size_t i = 1; i < argc; ++i) {
        HPDF_Page   page = HPDF_AddPage (pdf);
        if (!page)
            return 1;
        HPDF_Image  image   = HPDF_LoadJpegImageFromFile(pdf, argv[i]);
        if (!image) {
            printf("load error %s\n", argv[i]);
            return 1;
        }
        HPDF_REAL img_w = HPDF_REAL(HPDF_Image_GetWidth(image));
        HPDF_REAL img_h = HPDF_REAL(HPDF_Image_GetHeight(image));
        HPDF_Page_SetWidth (page, img_w);
        HPDF_Page_SetHeight(page, img_h);
        HPDF_Page_DrawImage (page, image, 0, 0, img_w, img_h);
    }
    HPDF_SaveToFile (pdf, "a.pdf");
    HPDF_Free (pdf);
    return 0;
}

コマンドライン引数で渡された jpgファイル(名) を順に突っ込んでるだけ。(コマンドライン引数なんで1オリジンだったり、出力ファイル名無精して a.pdf だったりするけれど)。 これだけで画像複数頁のpdfができちゃうので... ライブラリ、ありがたいです。

次に頁の開き方を設定。/*☆*/あたりに

    HPDF_SetPageLayout(pdf, HPDF_PAGE_LAYOUT_TWO_PAGE_RIGHT);

を挿入。ここでは見開き奇数ページ始まりにしてみた。

で、右綴じ。これについてはここらあたりをみて "ViewerPreferences" の Direction"に"R2L"を設定すりゃいいとのこと、libharuの HPDF_Catalog_SetViewerPreference が用途的にはそれなのだけど残念ながら"Direction"には未対応だったので見よう見まねで改造。

HPDF_STATUS  addR2L(HPDF_Doc pdf) {
    HPDF_Catalog    catalog     = pdf->catalog;
    HPDF_Dict       preferences = HPDF_Dict_New(catalog->mmgr);
    if (!preferences)
        return catalog->error->error_no;
    HPDF_STATUS ret = HPDF_Dict_Add(catalog, "ViewerPreferences", preferences);
    if (ret != HPDF_OK)
        return ret;
    ret = HPDF_Dict_AddName(preferences, "Direction", "R2L");
    if (ret != HPDF_OK)
        return ret;
    return HPDF_OK;
}

というのを用意、/*☆*/ の位置に呼び出しの addR2L(pdf); を挿入でok。

タイトルと著者名は (これも /*☆*/付近)

    HPDF_SetInfoAttr(pdf, HPDF_INFO_TITLE , "本のタイトル");
    HPDF_SetInfoAttr(pdf, HPDF_INFO_AUTHOR, "著者名");

と用意されたもので楽ちん... なんだが日本語が化けてしまう。

日本語関係の設定が必要のようだ。jfont_demo.c 等いろいろみて

    HPDF_UseJPEncodings(pdf);
    HPDF_UseJPFonts(pdf);
    HPDF_SetCurrentEncoder(pdf, "90msp-RKSJ-H");

を タイトルや著者を設定する前に記述しとく。
(3行目の"90msp-RKSJ-H"指定は、HPDF_UseJPEncodings(pdf);内で設定されているモノからプロポーショナルなゴシック体を選んだ)
これでacrobatのプロパティでタイトル&著者がちゃんと日本語で見れたので、めでたしめでたし、としておく。

HPDF_SetCurrentEncoder が肝、というか気づけずにここが一番苦労した。他のツールで生成したpdfとバイナリ見比べて、TitleやAuthorは7bit範囲はasciiだけどそれ以上だとUTF16(big endian)ベースになってるとか、utf8で別のxmlな記述スタイルになってるものとか... 結局utf16扱ってる処理から遡ってSetCurrentEncoderに行き着いた、と.


ということで、上記をもとに作ってみたコマンドラインツールはこちら
(※2/29追記: libharuビルド・バッチの不具合修正. exeは前と同じ)