In this short blogpost I want to show you how I’ve used the Windows.Data.Pdf.PdfDocument class in order to render a PDF file inside my UWP app.
In order to open a PDF from a UWP app, you can ofcourse, always use Windows.System.Launcher.LaunchUriAsync like this:
Windows.System.LauncherOptions options = new Windows.System.LauncherOptions(); options.ContentType = "application/pdf"; Windows.System.Launcher.LaunchUriAsync(new Uri(fileUrl), options);
This works great, but your file is being opened in an other app. And this might not be what you want. We just want the content of the PDF file being rendered inside our UWP app itself.
And that’s very simple to do!
First we need to load a PDF document in a Windows.Data.Pdf.PdfDocument object. There are two way to do this: From a StorageFile or from a Stream.
public async void OpenLocal() { StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/pdffile.pdf")); PdfDocument doc = await PdfDocument.LoadFromFileAsync(f); Load(doc); }
public async void OpenRemote() { HttpClient client = new HttpClient(); var stream = await client.GetStreamAsync(url); var memStream = new MemoryStream(); await stream.CopyToAsync(memStream); memStream.Position = 0; PdfDocument doc = await PdfDocument.LoadFromStreamAsync(memStream.AsRandomAccessStream()); Load(doc); }
This PdfDocument object has a method GetPage(int pageIndex) which returns a PdfPage object. This object represents a single page of our PdfDocument and has this interesting method: RenderToStreamAsync. This method accepts a IRandomAccessStream and optionally PdfPageRenderOptions.
Once the page is rendered to the Stream, we can set it to a BitmapImage by using the BitmapImage‘s SetSource(Asyc) method.
Once the page is rendered to the Stream, we can set it to a BitmapImage by using the BitmapImage‘s SetSource(Asyc) method.
var page = pdfDoc.GetPage(0); using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream()) { await page.RenderToStreamAsync(stream); await bitmapImage.SetSourceAsync(stream); }
This BitmapImage can now be easily bound to the Source property of an Image in XAML. Or, in this case, we can bind our collection of PdfPages to an ItemsControl and display an Image for each item.
<ScrollViewer ZoomMode="Enabled" Background="DarkGray" Grid.Column="1"> <ItemsControl ItemsSource="{Binding PdfPages, ElementName=root}"> <ItemsControl.ItemTemplate> <DataTemplate> <Image Source="{Binding}" Margin="0 2" /> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </ScrollViewer>
Complete code-behind:
public MainPage() { this.InitializeComponent(); } public async void OpenLocal() { StorageFile f = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/pdffile.pdf")); PdfDocument doc = await PdfDocument.LoadFromFileAsync(f); Load(doc); } public async void OpenRemote() { HttpClient client = new HttpClient(); var stream = await client.GetStreamAsync("http://www.adobe.com/content/dam/Adobe/en/accessibility/products/acrobat/pdfs/acrobat-x-accessible-pdf-from-word.pdf"); var memStream = new MemoryStream(); await stream.CopyToAsync(memStream); memStream.Position = 0; PdfDocument doc = await PdfDocument.LoadFromStreamAsync(memStream.AsRandomAccessStream()); Load(doc); } async void Load(PdfDocument pdfDoc) { PdfPages.Clear(); for (uint i = 0; i < pdfDoc.PageCount; i++) { BitmapImage image = new BitmapImage(); var page = pdfDoc.GetPage(i); using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream()) { await page.RenderToStreamAsync(stream); await image.SetSourceAsync(stream); } PdfPages.Add(image); } } public ObservableCollection<BitmapImage> PdfPages { get; set; } = new ObservableCollection<BitmapImage>();
And that’s all there is to it!
UPDATE
I made a PdfViewerControl you can use in order to easily display PDF files in your UWP apps. It’s available, together with the source code in GitHub.
See this blogpost: UWP Simple PDF viewer
February 8, 2016 at 10:29 am
Nice as a simple demostration, but in a real world app you would have to buy a 3rd party control, because of search, re-rendering after zoom and overall performance. I know :/
May 25, 2016 at 3:47 pm
thanks thats a great article!
if all articles were like this one….
very helpful
June 14, 2016 at 3:43 pm
Please the source code? 🙂 Thanks
June 28, 2016 at 7:53 pm
All code is in the post. But I’ll upload the code one of these days.
July 10, 2016 at 6:15 pm
1.How can i open PasswordProtected pdf files?
2.How do you rerender pages while zooming?
August 26, 2016 at 9:17 am
Hi!
According to the msdn page, you should be able to pass in a password to the LoadFromFileAsync method if you want to open protected pdf files:
https://msdn.microsoft.com/en-us/library/windows/apps/dn263419.aspx
July 21, 2016 at 8:00 pm
When I try to open a local pdf, I get the following error: System.Runtime.InteropServices.COMException
what is the solution?
August 26, 2016 at 9:03 am
Hi! I’m uploading the sources at this very moment and will post a link asap. Then you can check if you get the same exception with my sources. If that still doesn’t work, let me know.
August 17, 2016 at 10:51 am
Hi!
I couldn’t get it work. Sources needed. 🙁
August 26, 2016 at 9:02 am
Hi! Sorry for my late reply. I’ll upload the sources in a minute and post a link in the post.
August 26, 2016 at 9:14 am
Hi guys!
I’ve added a link to the sources.
September 13, 2016 at 7:26 pm
it work with me , but only for your PDF , I tried many times to put another file , in assets and open it by the same url but I got exception file not found
September 13, 2016 at 9:13 pm
Hi Mario,
Could you please send me your source code via mail? I will take a look at it, it’s impossible to determine your issue otherwise.
Thanks!
September 17, 2016 at 6:41 pm
Thanks Pieter for solving my problem
the mistake was that I wasn’t ‘packaging’ your pdf file with your app
and the solution done by the following steps
Right-click the Assets folder in Visual Studio and select Add => Existing Item and then select your pdf file.
Next, select your pdf file in Visual Studio, go to the “Properties Window” and select Content as Build Action.
September 17, 2016 at 6:42 pm
sorry I mean my pdf with my app
September 17, 2016 at 6:56 pm
Glad that in the end everything worked out for you, Mario!
November 29, 2016 at 12:01 pm
Hi,
Nice tutorial & It helped me a lot to create my notice board application.
One problem i am facing is that resolution of image is too low. Is there a way to get the proper resolution of image?
Thanks in advance!
August 16, 2017 at 11:42 pm
Hi,
Your article helped me a lot to create a pdf file from stream. I need a help to get the currentPage no while scrolling the pdf pages.
Eg:- Page 2 of 80.
Thanks in advance!
August 17, 2017 at 2:50 pm
Hi there,
That is a good question. I’ll try to take a look at it asap. If I find a way to support this kind of behaviour, I’ll add it to my source code and will drop you a message.
Keep an eye on this post!
Pieter
March 21, 2018 at 10:14 am
Nice indeed, however doesn’t work with mine because of binding. I tried many things until I decided to put only one image, and render it, which works great. Target Win 10 Fall Creator Update (16299).
October 17, 2018 at 7:08 am
Hi, I used this sample to load the pdf file but it’s not loading, can anyone help me.
February 8, 2019 at 6:17 pm
To show the PDF using the default app, add the PDF to the Assets folder and set Build Action to “Content” and use this code:
StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(@”Assets\MyDoc.pdf”);
var result = await Launcher.LaunchFileAsync(file);
March 19, 2019 at 8:49 am
informative post, thank you
February 24, 2020 at 6:08 pm
The PDF images are not showing up. Debug.Write shows that its looping, but no image showing up
March 14, 2020 at 9:11 am
If you provide me a code sample so I can reproduce your issue, then I’d love to take a look at it.
April 22, 2022 at 3:08 pm
Hi,
This really helped a lot but I faced one problem, On Zooming the pdf page it is not fit to the page or lets say not properly aligned. Any suggestions do you have ?