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

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

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

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

[docs.microsoft.com](https://docs.microsoft.com/ja-jp/windows/apps/desktop/modernize/desktop-to-uwp-enhance#set-up-your-project)を参考にして、NuGetまたは手動でアセンブリへの参照を加えます。

### NuGetの場合

要点を引用します。
> 3. [NuGet パッケージ マネージャー] ウィンドウで、 [参照] タブを選択して、Microsoft.Windows.SDK.Contracts を検索します。
>
> 4. 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](https://docs.microsoft.com/ja-jp/uwp/api/windows.data.pdf?view=winrt-18362)を見れば大体わかるかと思います。
ざっくりと下記のようになります。

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

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

必要に応じて`PdfPageRenderOptions`で多少カスタマイズできます。
画像をpngではなくbmpなどにする場合は、`BitmapEncoderId`に[WICのEncoder Id](https://docs.microsoft.com/ja-jp/windows/win32/wic/-wic-guids-clsids)を指定すれば良さそうです。

## サンプルコード

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

### WinForms

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

```cs: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の例の

```cs
pictureBox1.Image?.Dispose();
pictureBox1.Image = Image.FromStream(memStream);
```
の代わりに、

```cs
image1.Source = BitmapDecoder.Create(memStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
```
としてください。
他の部分はWPFに合わせて適宜変えてください。
OnLoadが嫌なら、`using`のタイミングを調整してください。