サイトアイコン CAD日記

PDFium第2弾はPDFを画像化するWindowsフォームアプリケーションなり

「PDFium ViewerによるPDFレンダリングがイケてる」では、最低限のWindowsフォームアプリケーションを作ったわけだが、その第2弾は普通の人(非プログラマー)でも使えるPDFの画像変換アプリまで仕上げてみた。

PDFアレコレ紹介ページ
zip解凍後、exeダブルクリックして実行してみたまえ。PDFファイルを指定して画像変換ボタンを押せば、PDFと同一フォルダ内にページ名が付加されたJpegファイルができあがる。解像度と画像形式の指定ができるから、ファイルサイズを小さい画像にすることもできる。変換に要した時間まで明示するあたりはおいらのこだわり。動作要件は.NET Framework 4.6が入っていること。もしかしたら4.6.1以上じゃないとダメかも。Windows 10であれば動くだろうが、Windows 7あたりでは素直には動かないと思われる。なにぶん、.NETアプリを作るのははじめてなもんだからわからんことが多い。以下はお決まりの文言なので、一応書いておく。
≪著作権および免責事項≫
本ソフトはフリーソフトです。個人/団体/社内利用を問わず、ご自由にお使い下さい。なお,著作権は作者である caddiary が保有しています。このソフトウェアを使用したことによって生じたすべての障害・損害・不具合等に関しては、私と私の関係者および私の所属するいかなる団体・組織とも、一切の責任を負いません。各自の責任においてご使用ください。

いちいちダウンロードしてまでどんな性能なのかを確認したくない人もいるだろうから、仕様を書いてみる。
●PDFファイル
「…」ボタンでファイルを選ぶことができるし、エクスプローラからドラッグ&ドロップしてもよし。
●解像度
初期値300で100~600の切りのいい数値を選択。数値入力も可能なので1200とかとんでもない値を入れても64bit環境なら動くだろう。上限も下限もないので10000や-100だと落ちるはず。ちゃんと入力値チェックすればよかったと今さらながら思う。
●画像形式
Bitmapフルカラーはアホみたいにデカいファイルができる。Bitmapグレースケールは8bit256色の白黒画像になる。Bimap白黒2値は、しきい値を基準に白黒はっきりつけるから見事に小さいファイルができる。Jpegは品質値に応じた適度なファイルサイズになり、最も一般的な画像形式なのでデフォルトにした。PNGはそこそこデカいサイズになるから実用的ではないかもしれんが、新しい画像形式なので気分で導入。
●変換結果
成功か失敗を表示。
●結果詳細
失敗時の原因を表示。入力値のチェックが中心で、PDFiumや画像処理のあたりはtry,catchで例外エラーとしただけ。
●所要時間
スタートから終了までの時間計測してミリ秒単位まで表示。

ソースコードについては、主要なところ(画像変換ボタン押下時処理)だけ公開する。PDFiumの処理は全体の中のほんの一部で、Load・PageSizes・Render・Disposeの4つだけ。Loadで読み込んで、PageSizesでページごとの用紙サイズを取得してピクセル数に反映、RenderでImage化して、Disposeでファイルロック解除。このレベルだったら短時間で組めるレベルだったが、そこに各種画像形式の保存を盛り込んだのが苦労のポイント(ここはソース公開せず)。Jpegの品質を考慮した保存は序の口で、グレースケールはネットに落ちていたからそのまま適用した。白黒2値が最も苦労した。最初に見つけたサイトのやり口でやったら変換スピードが出ず、さらに調べて「.NETによる画像処理の高速化Tips:非unsafe編」にたどり着いた。Bitmap::GetPixcelが遅い原因ということで、素晴らしい記事だった。今後もお世話になることだろう。

private void button2_Click(object sender, EventArgs e)
{
    // 結果コントロール初期化
    UpdateResult("", "");
    textBox5.Text = "";
    Update();

    // 入力コントロールチェック
    int type = comboBox1.SelectedIndex;
    string strSrc = textBox1.Text;
    string strKzd = comboBox2.Text;
    float kzd = 0;
    string strQuality = textBox4.Text;
    int quality = 0;
    string strSki = textBox6.Text;
    int ski = 0;
    if (!CheckCntrol(type, strSrc, strKzd, ref kzd, strQuality, ref quality, strSki, ref ski))
        return;

    // 変換先ファイル名
    int pos = strSrc.LastIndexOf('.');
    string strDst = strSrc.Substring(0, pos) + "[{0}]";
    if (type == 0 || type == 1 || type == 2)
        strDst += ".bmp";
    else if (type == 3)
        strDst += ".jpg";
    else if (type == 4)
        strDst += ".png";
    else
        return;

    // Waitカーソル
    Cursor preCursor = Cursor.Current;
    Cursor.Current = Cursors.WaitCursor;

    // 時間計測開始
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

    // 画像変換
    bool stat = true;
    try
    {
        pdfDoc = PdfDocument.Load(strSrc);
        for (int i = 0; i < pdfDoc.PageCount; i++)
        {
            SizeF size = pdfDoc.PageSizes[i];
            int w = Point2Pixel(size.Width, (double)kzd);
            int h = Point2Pixel(size.Height, (double)kzd);
            Image img = pdfDoc.Render(i, w, h, kzd, kzd, false);
            string output = string.Format(strDst, i + 1);
            if (type == 0)  // Bitmap(フルカラー)
                img.Save(output, ImageFormat.Bmp);
            else if (type == 1)  // Bitmap(グレースケール)
                SaveBmpGraycale(img, output);
            else if (type == 2)  // Bitmap(白黒2値)
                SaveBmp1bpp(img, output, ski);
            else if (type == 3)  // Jpeg
                SaveJpeg(img, output, quality);
            else if (type == 4)  // PNG
                img.Save(output, ImageFormat.Png);
        }
    }
    catch(Exception)
    {
        UpdateResult("失敗", "PDFiumで例外エラー");
        stat = false;
    }
    finally
    {
        if(stat)
            UpdateResult("成功", "");
        Cursor.Current = preCursor;  // Waitカーソル解除
        if (!pdfDoc.Equals(null))
        {
            pdfDoc.Dispose();  // 明示的に破棄する必要あり(PDFがロックされてしまう)
            pdfDoc = null;
        }
    }
    // 時間計測終了
    sw.Stop();
    TimeSpan ts = sw.Elapsed;
    int t1 = (int)ts.TotalMilliseconds;
    double t2 = t1 / 1000.0;
    textBox5.Text = t2.ToString();
}

とにかくC#は実装が楽。わからないことはググればすぐに出てくるのが最高。C++のような難解な仕組みがないので、サンプルコードの適用が容易。Visual Studioは2017 Communityを使いはじめただけど、これがちょー便利。コードの自動成形、呼び出し関数のリストアップ、usingの自動挿入なんてことをやってくれるので、タイピングの手間を省けるし、タイピングミスやらビルド時にエラーが出る箇所をリアルタイムで明示してくれる。おいらのVisualStudio使い込み期は2005だったから、2017の便利さに驚愕した。しかも今はVisual Studioって無料なんですな。高額なMSDNの契約してないと使えないって思い込んでたけど、時代は変わったもんだ。

さて、フォームアプリ(つまりWindowsアプリ)としてはこのへんにしといて、今後はWebアプリの開発に着手する。だって、イマドキわざわざアプリをダウンロードして使うなんて人はマレで、昭和または平成中期までをメインに生きた人間くらい。ブラウザからサクサクと操作したくなる人間の目を引き実際に使わせれば、本ブログのアクセス数が増えて広告収入がガッツリ入ってくるという目算。いずれはCADデータ(AutoCADやJw_cad)をPDF化するとかできるとおもしろい。とにかくPDFiumに魅せられているところなので、その活用方法を模索していく所存なり。

モバイルバージョンを終了