Reading PDF and XPS on your Windows 8 application using WinRT

PDF and XPS file formats are widely used across the world and you could need one day to display them inside your application.

Today I would like to share with you a simple way to do so by using an open source solution: MuPDF (a multiplatform lightweight PDF and XPS viewer).

MuPDF is already working on a WinRT solution you can grab on this GIT repo: https://git.ghostscript.com/?p=user/mvrhel/mupdf.git;a=summary

I also used the work of Libreliodev and especially their advanced port of MuPDF for WinRT.

The result is a simple but really useful Windows 8 Modern UI app that is able to display a PDF/XPS file: https://www.catuhe.com/msdn/pdfreader.zip



The solution is based on two projects:

  • MuPDF.winRT: a C++/CX WinRT component built upon MuPDF
  • PDFReader: A C#XAML project that used the WinRT component to display a PDF

For this article I created a C#/XAML application. But because MuPDF.winRT is a WinRT component, you can also use it from a HTML5/CSS3/Javascript app!

Using the WinRT component, you just have to use the following line to create a Document (which is the class used to interact with a PDF or a XPS):

pdfDocument = Document.Create(readBuffer, DocumentType.PDF, (int)Windows.Graphics.Display.DisplayProperties.LogicalDpi);

To draw a page, you just have to use the previous pdfDocument object and call the DrawPage method:

pdfDocument.DrawPage(PageNumber, buf, 0, 0, width, height, false);

This method draws a given page inside a IBuffer. To create it you just have to instantiate a WriteableBitmap and the inner PixelBuffer:

var size = Document.GetPageSize(PageNumber);
var width = (int)(size.X * ZoomFactor);
var height = (int)(size.Y * ZoomFactor);

var image = new WriteableBitmap(width, height);
IBuffer buf = new Buffer(image.PixelBuffer.Capacity);
buf.Length = image.PixelBuffer.Length;

The buffer is then copied back to the WriteableBitmap:

using (var stream = buf.AsStream())
{
    await stream.CopyToAsync(image.PixelBuffer.AsStream());
}

And finally the result is displayed on screen:

You can even display two pages on the same image:

await Task.Run(() => pdfDocument.DrawFirtPageConcurrent(PageNumber, buf, width, height));
await Task.Run(() => pdfDocument.DrawSecondPageConcurrent(PageNumber + 1, buf, width, height));

Using the mouse or the touch you can obviously navigate inside the document or zoom on pages. And with the power of vectorized PDF, you will always have the best display quality:


Optical zoom (bitmap based)


Vectorized zoom

To display these images, I used a simple FlipView so that I do not have to bother about gestures. The DataTemplate used for every page is based on a ScrollViewer in order to automaticaly handle zoom in and zoom out:

<FlipView Name="flipView">
    <FlipView.ItemTemplate>
        <DataTemplate>
            <ScrollViewer
                ZoomMode="Enabled" 
                HorizontalScrollMode="Auto"
                VerticalScrollMode="Auto"
                VerticalSnapPointsType="None"
                HorizontalSnapPointsType="None"
                HorizontalScrollBarVisibility="Auto"
                VerticalScrollBarVisibility="Auto"
                MinZoomFactor="1"            
                MaxZoomFactor="4">
                <Image Source="{Binding Image}" Width="{Binding Width}" Height="{Binding Height}" 
Stretch="Uniform" HorizontalAlignment="Center" Margin="0"/> </ScrollViewer> </DataTemplate> </FlipView.ItemTemplate> </FlipView>

Simple, isn’t it ?