LoginSignup
4
3

More than 3 years have passed since last update.

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

Posted at

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

環境

C++/WinRTの始め方 を参考にしました。

PDFファイルのレンダリング

Windows 8.1以降で使える、 WinRT のAPI(Windows.Data.Pdf)をそのまま使います。
(C++/WinRTではなく)WinRTをそのまま使ったSusie Pluginをベースにしました。

入力ファイルのオープン

個人的にハマり所でした。

フルパス指定なら、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

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()を使う。

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);

サンプルコード

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();
}
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
4
3
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
4
3