{"id":96465,"date":"2017-06-26T07:00:00","date_gmt":"2017-06-26T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=96465"},"modified":"2019-03-13T01:13:19","modified_gmt":"2019-03-13T08:13:19","slug":"20170626-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170626-00\/?p=96465","title":{"rendered":"Extracting pages from a PDF document and saving them as separate image files, C# edition"},"content":{"rendered":"<p>Today&#8217;s Little Program extracts the pages from a PDF document and saves them as separate image files. Why? Because I needed to do that. <\/p>\n<p>I&#8217;ll start with the <a HREF=\"https:\/\/github.com\/Microsoft\/Windows-universal-samples\/tree\/v1.0.11\/Samples\/PdfDocument\">PDF Document<\/a> sample program and change it so that instead of displaying pages on the screen, it saves them to disk. <\/p>\n<p>Take the C# sample and make these changes to <code>Scenario1_Render.xaml.cs<\/code>:<\/p>\n<pre>\nprivate async void ViewPage()\n{\n    rootPage.NotifyUser(\"\", NotifyType.StatusMessage);\n\n    uint pageNumber;\n    if (!uint.TryParse(PageNumberBox.Text, out pageNumber) ||\n        (pageNumber &lt; 1) || (pageNumber &gt; pdfDocument.PageCount))\n    {\n        rootPage.NotifyUser(\"Invalid page number.\", NotifyType.ErrorMessage);\n        return;\n    }\n\n    <font COLOR=\"blue\">\/\/ New: Ask the user for the output file.\n    var save = new FileSavePicker();\n    save.FileTypeChoices[\"PNG image\"] = new[] { \".png\" };\n    var outfile = await save.PickSaveFileAsync();\n    if (outfile == null) return;<\/font>\n\n    Output.Source = null;\n    ProgressControl.Visibility = Visibility.Visible;\n\n    \/\/ Convert from 1-based page number to 0-based page index.\n    uint pageIndex = pageNumber - 1;\n\n    using (PdfPage page = pdfDocument.GetPage(pageIndex))\n    <font COLOR=\"blue\">using (var transaction = await outfile.OpenTransactedWriteAsync())\n    {\n        await page.RenderToStreamAsync(transaction.Stream);\n    }<\/font>\n    ProgressControl.Visibility = Visibility.Collapsed;\n}\n<\/pre>\n<p>Actually, I kind of gutted the program and replaced it with my own stuff. The only interesting parts that remain from the original program are the <code>Load&shy;Document<\/code> method (not shown here) which loads the PDF file into the <code>pdfDocument<\/code> variable, and the part that obtains the <code>PdfPage<\/code> from the user-specified page number. <\/p>\n<p>We ask for the output file, obtain a write stream to that file, and ask the <code>page<\/code> to render into that stream. The default options generate a bitmap in PNG format whose size matches the declared <code>Size<\/code> of the page. <\/p>\n<p>The PNG format was fine for my purposes, but the size wasn&#8217;t. WinRT view pixels are 1\/96 of an inch, so the resulting bitmap was rendered as if printed to a 96 DPI printer. That&#8217;s the resolution of a first-generation fax machine, which isn&#8217;t all that great. I wanted 192 DPI, so I needed to render the image at double-size. <\/p>\n<pre>\n    using (PdfPage page = pdfDocument.GetPage(pageIndex))\n    using (var transaction = await outfile.OpenTransactedWriteAsync())\n    {\n        <font COLOR=\"blue\">var options = new PdfPageRenderOptions();\n        options.DestinationHeight = (uint)(page.Size.Height * 2);\n        options.DestinationWidth = (uint)(page.Size.Width * 2);\n        await page.RenderToStreamAsync(transaction.Stream, options);<\/font>\n    }<\/font>\n<\/pre>\n<p>(If I had wanted to change the file format, I&#8217;d have set the <code>options.Bitmap&shy;Encoder&shy;Id<\/code> to something like <code>Bitmap&shy;Encoder.<\/code><code>Jpeg&shy;Encoder&shy;Id<\/code>.) <\/p>\n<p>I didn&#8217;t have a large document to convert, so changing the page number and clicking the (now-mislabeled) &#8220;View&#8221; button a dozen times <a HREF=\"https:\/\/xkcd.com\/1205\/\">wasn&#8217;t that big of a deal<\/a>. <\/p>\n<p>For the rest of the week, I&#8217;m going to be translating this program into C++\/CX (twice) and JavaScript (twice). <\/p>\n<p>Twice? <\/p>\n<p>Yes, twice. You&#8217;ll see. <\/p>\n<p>And then there will be a bonus. <\/p>\n<p>I can sense your anticipation. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>A Little Program I needed.<\/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-96465","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A Little Program I needed.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96465","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=96465"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/96465\/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=96465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=96465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=96465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}