様々な基本になるサンプルを記録しています。

不定期更新です。

記事のサイドに使用している商品の紹介も掲載しているので、良ければご覧ください。

【MAGICK.NET】PDFを画像に変換する

昔の話だけど、ImageMagickというオープンソースを使ってPDFの変換を試みたことがある。
しかし、リファレンスが全く無かったのと、メモリリークが起こってしまい断念した。
今はImageMagickではなく、Magick.NETっていうオープンソース用のライブラリが公開されている。

以下からダウンロードできる。
Release Magick.NET 7.9.0.0 · dlemstra/Magick.NET · GitHub

ただ、これを動かすにはghostscriptをインストールする必要がある。
ポータブル版ではダメだった。
サービス終了のお知らせ


そんなわけで、それを使ってPDF変換をやってみることにした。
動作環境は.NET Framework4.0で、言語はVisual Basic.NET。
GhostScriptのバージョンは9.25。
Magick.NETのバージョンは、「Magick.NET-7.9.0.0-Q16-x64」を使用した。
x64のライブラリじゃないとメモリがパンクするような気がするし。

ちなみに
「Magick.NET-Q16-x64.dll」は参照として追加。
「Magick.NET-Q16-x64.Native.dll」はexeと同じ場所に配置した。
もちろん、ターゲットCPUはx64。

ソリューション構成はこんな感じ。
f:id:karinto441:20181024090925p:plain
「Magick.NET-Q16-x64.Native.dll」は参照しないで置いとくだけ。
ちなみにビルドと一緒にコピーしてしまえば楽なので、「常にコピー」にした方がいい。
Visual Studio2017でやってるけど、基本的にIDEは下位バージョンでも変わらない。

とりあえず、FORMアプリケーション作って、LOADイベントで動かしてみる

Imports ImageMagick
Public Class Form1


    ''' <summary>
    ''' PDFをPNGイメージに変換する
    ''' </summary>
    ''' <param name="pdf_name">PDFファイルパス</param>
    ''' <param name="Export_name">返還後のファイル名(拡張子はいらない)</param>
    Public Function PDFTOPNG(pdf_name As String, Export_name As String, ByRef Optional ERRMSG As String = "") As Boolean

        '読み込み設定クラスのインスタンス化
        Dim settings As New MagickReadSettings()
        '解像度の設定
        settings.Density = New Density(300, 300)

        '全部読み込むとメモリが足りなくなるので1ページずつ読み込むために最初のインデックスを設定
        settings.FrameIndex = 0

        'カラータイプの設定
        settings.ColorSpace = ColorSpace.sRGB
        settings.ColorType = ColorType.TrueColorAlpha

        'アンチエイリアスは無効にしておく
        settings.StrokeAntiAlias = False
        settings.TextAntiAlias = False


        'クラスのインスタンス化
        Dim images As New MagickImageCollection()

        Try

            While True

                '再インスタンス化
                images = New MagickImageCollection()

                'PDFの読み込み
                images.Read(pdf_name, settings)

                'EXIF情報を削除
                images.Item(0).Strip()

                '解像度を大きくしているので半分にリサイズする
                images.Item(0).Scale(percentage:=50)

                'ページ変数のカウントアップ
                settings.FrameIndex += 1

                'PNG形式で保存設定
                images.Item(0).Format = MagickFormat.Png

                'PNGファイル書き出し
                images.Item(0).Write(Export_name & CStr(settings.FrameIndex) & ".png")

                'ページの明示的開放
                images.Item(0).Dispose()

                'コレクションの明示的開放
                images.Dispose()

                'メモリがいっぱいになる可能性があるので明示的に開放したリソースを
                'ガーベジコレションを強制的に起こすことによって解放
                GC.Collect()


            End While

        Catch ex As Exception

            'エラーとして検知されるとき、読み込んだPDFのページ数以上で読み込みを行った場合は終了とする
            If InStr(ex.Message, "Requested FirstPage is greater than the number of pages in the file") <> 0 Then

                ERRMSG = ""
                Return True

            Else

                ERRMSG = ex.Message
                Return False

            End If

        Finally

            '念のため、最後に解放しておく
            images.Dispose()
            GC.Collect()

        End Try


    End Function

 'ロードイベント
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

        Dim errmsg As String = ""

        If Not PDFTOPNG("C:\temp\test.pdf", "PDFIMG", errmsg) Then

            MsgBox(errmsg)

        End If

    End Sub

End Class

一応、ネットで公開されているソースは基本的にC#で書かれていることが多いので、
参考にしたC#ソースをVBに書き換えて記述している。
世の中はやっぱり、C#なんだなと思った。


このソースは1ページずつ読み込んでPNG形式に保存している。
一気に読み込みこともできるんだけど、使用メモリが尋常じゃないほど増える。

62ページのPDFファイルがちょうどあったので、一気に変換しようとしたら
3GB以上もメモリを使用されてちょっと焦った。

PDF変換メソッドをクラスライブラリとかにすれば、ASP.NETでも組み込むことができるだろうな。
画像の劣化との戦いもあるけど、解像度をどう調整して、リサイズしていくかで奇麗になりそうだ。

また、PNG形式以外でも保存できるし、イメージをPDFに変換することもできるので、
結構、出来ることは広い。

でも・・・
これ、内部でghostscriptのコマンドが呼ばれてるだけなんだよね。
それが分かっていれば、ghostscript単体のみでなんでも行けてしまいそうな気がするな。