LoginSignup
10
12

More than 3 years have passed since last update.

[C#/WinRT] 外部ライブラリを使用せずにPDFファイルを画像化してWinForms(またはWPF)で表示する

Last updated at Posted at 2020-06-16

PDFium等の外部ライブラリを使用しなくても、Windows 8以降にはPDFファイルを画像としてレンダリングできるAPIがWinRTとしてOSに在ります。
UWP(Windowsストア)アプリでは特に苦もなく使用できるはずです。

WinForms(またはWPF)からは比較的最近利用できるようになりました。
この記事ではWinFormsのPictureBox(またはWPFのImage)に表示するサンプルコードを示します。

NuGetまたは手動で参照を追加する

docs.microsoft.comを参考にして、NuGetまたは手動でアセンブリへの参照を加えます。

NuGetの場合

要点を引用します。

  1. [NuGet パッケージ マネージャー] ウィンドウで、 [参照] タブを選択して、Microsoft.Windows.SDK.Contracts を検索します。

  2. Microsoft.Windows.SDK.Contracts パッケージが見つかったら、 [NuGet パッケージ マネージャー] ウィンドウの右側のペインで、ターゲットにする Windows 10 のバージョンに基づいて、インストールするパッケージのバージョンを選択します。

  • 10.0.18362.xxxx:Windows 10 バージョン 1903 の場合は、これを選択します。
  • 10.0.17763.xxxx:Windows 10 バージョン 1809 の場合は、これを選択します。
  • 10.0.17134.xxxx:Windows 10 バージョン 1803 の場合は、これを選択します。

PDFファイルをレンダリングするAPI

docs.microsoft.comを見れば大体わかるかと思います。
ざっくりと下記のようになります。

  1. Windows.Data.Pdf.PdfDocument.LoadFrom{Stream,File}AsyncPdfDocumentオブジェクトを取得。
  2. PdfDocument.GetPagePdfPageオブジェクトを取得。
  3. PdfPage.RenderToStreamAsyncで、引数に渡したストリームに画像(デフォルトはpng)のバイトデータが書き込まれる。
  4. バイトデータをImage(WPFであればBitmapDecoder)でイメージにする。

ただし、ストリームはSystem.IO.Streamではありません。
Windows.Storage.Streams.IRandomAccessStreamを使用する必要があります。
直接ファイルから構築するならFileRandomAccessStreamを使用すればいいですが、実際のWinFormsアプリに組み込むならIStreamではないと使い勝手が悪いです。
WindowsRuntimeStreamExtensions.AsRandomAccessStream拡張メソッドでIRandomAccessStreamを取得できます。

必要に応じてPdfPageRenderOptionsで多少カスタマイズできます。
画像をpngではなくbmpなどにする場合は、BitmapEncoderIdWICのEncoder Idを指定すれば良さそうです。

サンプルコード

フォームを表示した後、1秒毎にページを表示してきます。

WinForms

適当なフォームに、pictureBox1が張り付いている前提です。

Form1.cs
using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;

using Windows.Data.Pdf;

namespace WinFormsProject1
{
    public partial class Form1 : Form
    {
        public Form1() => InitializeComponent();

        private async void Form1_Load(object sender, EventArgs e)
        {
            using (var pdfStream = File.OpenRead("sample.pdf"))
            using (var winrtStream = pdfStream.AsRandomAccessStream())
            {
                var doc = await PdfDocument.LoadFromStreamAsync(winrtStream);
                for (var i = 0u; i < doc.PageCount; i++)
                {
                    using (var page = doc.GetPage(i))
                    using (var memStream = new MemoryStream())
                    using (var outStream = memStream.AsRandomAccessStream())
                    {
                        await page.RenderToStreamAsync(outStream);

                        pictureBox1.Image?.Dispose();
                        pictureBox1.Image = Image.FromStream(memStream);

                        await Task.Delay(1000).ConfigureAwait(false);
                    }
                }
            }
        }
    }
}

C# 8.0であればusing文を簡略化できるかと思います。

WPF

WinFormsの例の

pictureBox1.Image?.Dispose();
pictureBox1.Image = Image.FromStream(memStream);

の代わりに、

image1.Source = BitmapDecoder.Create(memStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];

としてください。
他の部分はWPFに合わせて適宜変えてください。
OnLoadが嫌なら、usingのタイミングを調整してください。

10
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
12