{"id":96485,"date":"2017-06-29T07:00:00","date_gmt":"2017-06-29T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96485"},"modified":"2019-03-13T01:13:31","modified_gmt":"2019-03-13T08:13:31","slug":"20170629-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170629-00\/?p=96485","title":{"rendered":"Extracting pages from a PDF document and saving them as separate image files, C++\/CX edition with explicit tasks"},"content":{"rendered":"<p><!-- backref: Extracting pages from a PDF document and saving them as separate image files, C# edition -->At the start of this series<\/a>, we converted the C# version of the <a HREF=\"https:\/\/github.com\/Microsoft\/Windows-universal-samples\/tree\/v1.0.11\/Samples\/PdfDocument\">PDF Document<\/a> sample program so that it saved the pages to disk as image files. Today we&#8217;ll port those changes to C++\/CX with tasks. <\/p>\n<pre>\nvoid Scenario1_Render::ViewPage()\n{\n    rootPage-&gt;NotifyUser(\"\", NotifyType::StatusMessage);\n\n    unsigned long pageNumber =\n        wcstoul(PageNumberBox-&gt;Text-&gt;Data(), nullptr, 10);\n\n    if ((pageNumber &lt; 1) || (pageNumber &gt; pdfDocument-&gt;PageCount))\n    {\n        rootPage-&gt;NotifyUser(\"Invalid page number.\",\n                                NotifyType::ErrorMessage);\n        return;\n    }\n\n    Output-&gt;Source = nullptr;\n    ProgressControl-&gt;Visibility =\n        Windows::UI::Xaml::Visibility::Visible;\n\n    \/\/ Convert from 1-based page number to 0-based page index.\n    unsigned long pageIndex = pageNumber - 1;\n\n    <font COLOR=\"blue\">auto picker = ref new FileSavePicker();\n    picker-&gt;FileTypeChoices-&gt;Insert(\"PNG image\",\n        ref new Platform::Collections::Vector&lt;String^&gt;({ \".png\" }));\n    create_task(picker-&gt;PickSaveFileAsync())\n        .then([this, pageIndex](StorageFile^ outfile)\n    {\n        if (outfile)\n        {\n            auto page = pdfDocument-&gt;GetPage(pageIndex);\n\n            return create_task(outfile-&gt;OpenTransactedWriteAsync())\n                .then([this, page](StorageStreamTransaction^ transaction)\n            {\n                auto options = ref new PdfPageRenderOptions();\n                options-&gt;DestinationHeight = (unsigned)(page-&gt;Size.Height * 2);\n                options-&gt;DestinationWidth = (unsigned)(page-&gt;Size.Width * 2);\n                return create_task(page-&gt;RenderToStreamAsync(transaction-&gt;Stream, options))\n                    .then([this, page, transaction]()\n                {\n                    delete transaction;\n                    delete page;\n                });\n            });\n        }\n        else\n        {\n            return task_from_result();\n        }\n    })<\/font>.then([this]()\n    {\n        ProgressControl-&gt;Visibility =\n            Windows::UI::Xaml::Visibility::Collapsed;\n    });\n}\n<\/pre>\n<p>This code is structured the same as <!-- backref: Extracting pages from a PDF document and saving them as separate image files, JavaScript edition with Promises -->the JavaScript Promise-based version<\/a>. But unlike JavaScript, we cannot capture local variables by reference because the stack will unwind before the continuation runs. We have to capture them by value. <\/p>\n<p>As with JavaScript, C++\/CX lacks a <code>using<\/code> keyword, so we must explicitly close the closable objects when we are done with them. In C++\/CX, this projected as the <code>delete<\/code> keyword. <\/p>\n<p>Since C++\/CX is a strongly-typed language, our tasks are a bit more annoying because we have to make sure all code paths return the same type, because a function can have only one return type. This means adding a <code>return task_<\/code><code>from_<\/code><code>result()<\/cODE> to the <code>else<\/code> branch so that all code paths return a <code>task&lt;void&gt;<\/code>. <\/p>\n<p>You might be able to guess what the next step in our adventure is going to be. We'll take it up next time. <\/p>\n<p><b>Bonus chatter<\/b>: Here's the C++\/WinRT version. This is a bit of a spurious exercise because the XAML compiler doesn't support C++\/WinRT yet, which means that you'll see a mix of C++\/CX code (when interacting with XAML) and C++\/WinRT code. <\/p>\n<pre>\nvoid Scenario1_Render::ViewPage()\n{\n    rootPage-&gt;NotifyUser(\"\", NotifyType::StatusMessage);\n\n    unsigned long pageNumber =\n        wcstoul(PageNumberBox-&gt;Text-&gt;Data(), nullptr, 10);\n\n    if ((pageNumber &lt; 1) || (pageNumber &gt; <font COLOR=\"blue\">pdfDocument.PageCount()<\/font>))\n    {\n        rootPage-&gt;NotifyUser(\"Invalid page number.\",\n                                NotifyType::ErrorMessage);\n        return;\n    }\n\n    Output-&gt;Source = nullptr;\n    ProgressControl-&gt;Visibility =\n        Windows::UI::Xaml::Visibility::Visible;\n\n    \/\/ Convert from 1-based page number to 0-based page index.\n    unsigned long pageIndex = pageNumber - 1;\n\n    <font COLOR=\"blue\">FileSavePicker picker;\n    picker.FileTypeChoices().Insert(\"PNG image\", { \".png\" });<\/font>\n    create_task(picker.PickSaveFileAsync())\n        .then([this, pageIndex](<font COLOR=\"blue\">adapter<\/font> outfile)\n    {\n        if (outfile)\n        {\n            <font COLOR=\"blue\">auto page = pdfDocument.GetPage(pageIndex);<\/font>\n\n            return create_task(outfile.OpenTransactedWriteAsync())\n                .then([this, page](<font COLOR=\"blue\">adapter<\/font> transaction)\n            {\n                <font COLOR=\"blue\">PdfPageRenderOptions options;<\/font>\n                <font COLOR=\"blue\">options.DestinationHeight<\/font>((unsigned)(page-&gt;Size.Height * 2));\n                <font COLOR=\"blue\">options.DestinationWidth<\/font>((unsigned)(page-&gt;Size.Width * 2));\n                create_task(<font COLOR=\"blue\">page.RenderToStreamAsync<\/font>(<font COLOR=\"blue\">transaction.Stream()<\/font>, options))\n                    .then([this, page, transaction]()\n                {\n                    <font COLOR=\"blue\">transaction.Close();\n                    page.Close();<\/font>\n                });\n            });\n        }\n        else\n        {\n            return task_from_result();\n        }\n    }).then([this]()\n    {\n        ProgressControl-&gt;Visibility =\n            Windows::UI::Xaml::Visibility::Collapsed;\n    });\n}\n<\/pre>\n<p>The changes are as follows: <\/p>\n<ul>\n<li>Method calls use dot notation rather than arrow notation. <\/li>\n<li>Fetching properties is done by calling a method (via dot)     with the name of the property, and no parameters. <\/li>\n<li>Setting properties is done by calling a method (via dot)     and passing the desired new value as the parameter. <\/li>\n<li>Closing an object is done by calling the <code>Close()<\/code>     method, as opposed to C++\/CX which overloaded the <code>delete<\/code>     operator. <\/li>\n<li>Constructing an object is done by merely declaring it.<\/li>\n<li>Wrapping a pointer is done by constructing an object around it.     (Though it is common to use assignment-style construction.)<\/li>\n<li>C++\/WinRT lets you pass an initializer list when an aggregate     is expected. <\/li>\n<li>The parameter to task continuations is an <code>adapter<\/code>. <\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Sliding over to C++\/CX.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-96485","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Sliding over to C++\/CX.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96485","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=96485"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96485\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=96485"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96485"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96485"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}