[C++/WinRT]コンソールアプリケーションでPDFファイルを画像ファイルにするサンプルコード

C++/WinRTの手習いとして、XAMLを使わないWin32のコンソールアプリケーションでPDFファイルをレンダリングして画像ファイルにするサンプルコードを作ってみました。

## 環境
[C++/WinRTの始め方](https://qiita.com/yottui/items/390bf7582fa1abb42766) を参考にしました。

## PDFファイルのレンダリング
Windows 8.1以降で使える、 WinRT のAPI(`Windows.Data.Pdf`)をそのまま使います。
(C++/WinRTではなく)[WinRTをそのまま使ったSusie Plugin](https://github.com/udaken/axwrpdf)をベースにしました。

## 入力ファイルのオープン
個人的にハマり所でした。

フルパス指定なら、`FileRandomAccessStream::OpenAsync()`を使用すればいいことはわかったのですが、カレントディレクトリから読み込もうとして
`OpenAsync(L"sample.pdf",...)`と指定すると、実行時に
`WinRT originate error - 0x80070057 : '指定されたパス (sample.pdf) が絶対パスではありません。相対パスは指定できません。'`
という例外が起きます。

そこで`OpenAsync(L".\\sample.pdf",...)`と指定すると、今度は
`WinRT originate error - 0x80070057 : 'パラメーターが間違っています。'`
という訳が分からないメッセージに変わります。

結局、Windows APIでカレントディレクトリを取得してフルパスにする方法しか見つけられませんでした。

WinRTの作法？に沿ってフォルダーから辿る場合は、`StorageFolder::GetFolderFromPathAsync`

```cpp
WCHAR cd[MAX_PATH];
GetCurrentDirectory(ARRAYSIZE(cd), cd);
auto folder = co_await StorageFolder::GetFolderFromPathAsync(cd);
auto file = co_await folder.GetFileAsync(L"sample.pdf");
auto inputstream = co_await file.OpenReadAsync();
```

## 出力ファイルのオープン(作成)
フルパスなら、入力ファイルとほぼ同じ。
`StorageFolder`の中に作る場合は、`CreateFileAsync()`を使う。

```cpp
hstring const filename = L"page1.png";
WCHAR cd[MAX_PATH];
GetCurrentDirectory(ARRAYSIZE(cd), cd);
auto folder = co_await StorageFolder::GetFolderFromPathAsync(cd);
auto outputFile = co_await folder.CreateFileAsync(filename, CreationCollisionOption::ReplaceExisting);
auto outputStream = co_await outputFile.OpenAsync(FileAccessMode::ReadWrite);
```

## サンプルコード
```cpp:CppWinRTSample.cpp
#include "pch.h"

#define STRICT

#define WIN32_LEAN_AND_MEAN             // Windows ヘッダーから使用されていない部分を除外します。
// Windows ヘッダー ファイル:
#include <windows.h>
#include <unknwn.h>

#include "winrt/Windows.Storage.Streams.h"
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Data.Pdf.h"

#include <iostream>
#pragma comment(lib, "windowsapp")
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Streams;
using namespace winrt::Windows::Data::Pdf;

template <typename T, typename = std::enable_if_t<std::is_convertible_v<T, std::string_view>>>
hstring to_hstring_from_lpcstr(T const& value)
{
	std::string_view const view(value);
	int const size = WINRT_MultiByteToWideChar(CP_ACP, 0, view.data(), static_cast<int32_t>(view.size()), nullptr, 0);

	if (size == 0)
	{
		return{};
	}

	impl::hstring_builder result(size);
	WINRT_VERIFY_(size, WINRT_MultiByteToWideChar(CP_ACP, 0, view.data(), static_cast<int32_t>(view.size()), result.data(), size));
	return result.to_hstring();
}

float g_scaling = 1;

IAsyncOperation<PdfDocument> GetPdfDOcument()
try
{
#if 0 //フルパス指定
	hstring const srcPath = to_hstring_from_lpcstr("D:\\sample.pdf");
	auto inputstream = co_await FileRandomAccessStream::OpenAsync(srcPath, FileAccessMode::Read);
#else //カレントディレクトリ
	WCHAR cd[MAX_PATH];
	GetCurrentDirectory(ARRAYSIZE(cd), cd);
	auto folder = co_await StorageFolder::GetFolderFromPathAsync(cd);
	auto file = co_await folder.GetFileAsync(L"sample.pdf");
	auto inputstream = co_await file.OpenReadAsync();
#endif
	auto doc = co_await PdfDocument::LoadFromStreamAsync(inputstream);
	UINT const count = doc.PageCount();

	for (UINT i = 0; i < count; i++)
	{
		PdfPageRenderOptions options;
		auto encoderId = options.BitmapEncoderId();
		auto page = doc.GetPage(i);

		if (g_scaling != 1)
		{
			auto size = page.Size();
			options.DestinationHeight(static_cast<uint32_t>(size.Height * g_scaling));
			options.DestinationWidth(static_cast<uint32_t>(size.Width * g_scaling));
		}

#if 0 //フルパス指定
		hstring const destPath = L"D:\\page" + to_hstring(i) + L".png";
		auto outputStream = co_await FileRandomAccessStream::OpenAsync(destPath, FileAccessMode::ReadWrite, StorageOpenOptions::None, FileOpenDisposition::CreateNew);
#else //カレントディレクトリ
		hstring const filename = L"page" + to_hstring(i) + L".png";
		WCHAR cd[MAX_PATH];
		GetCurrentDirectory(ARRAYSIZE(cd), cd);
		auto folder = co_await StorageFolder::GetFolderFromPathAsync(cd);
		auto outputFile = co_await folder.CreateFileAsync(filename, CreationCollisionOption::ReplaceExisting);
		auto outputStream = co_await outputFile.OpenAsync(FileAccessMode::ReadWrite);
#endif
		co_await page.RenderToStreamAsync(outputStream);
		page.Close();
		outputStream.Close();
	}
	co_return doc;
}
catch (const winrt::hresult_error& e)
{
	std::wcout << "hresult_error Exception!\n" << e.message().c_str();
}
catch (...)
{
	std::cout << "Exception!\n";
}


int main(int argc, char *argv[])
{
	winrt::init_apartment();
	//pdfRender().get()
	GetPdfDOcument().get();
	winrt::uninit_apartment();
}
```
```cpp:pch.h
#ifndef PCH_H
#define PCH_H


#define STRICT

#define WIN32_LEAN_AND_MEAN             // Windows ヘッダーから使用されていない部分を除外します。
// Windows ヘッダー ファイル:
#include <windows.h>
#include <unknwn.h>

#include "winrt/Windows.Storage.Streams.h"
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Data.Pdf.h"

#endif //PCH_H
```