Loupe

[Windows 8.1] Afficher des documents PDF directement dans votre applications !

L’une des grosses nouveautés, pour moi, de Windows 8.1 ne concerne pas les tailles des tuiles ou bien même le nouveau menu Démarrer mais la possibilité, pour les développeurs d’applications, d’afficher directement des documents PDF dans des applications Windows Store.

En effet, une telle fonctionnalité n’était possible (sous Windows 8) qu’en passant par des SDK d’éditeurs tiers (tel que FoxIt) mais cela impliquait d’acheter une licence (qui pouvait, parfois, être relativement chère), d’avoir une dépendance à un composant externe (et donc devoir subir le rythme des mises à jours), etc.

 

Windows 8.1 introduit différentes classes, que l’on retrouvent dans le namespace Windows.Data.Pdf et qui permettent aux développeurs de manipuler ces documents (ouverture, accès à une page spécifique, affichage à l’utilisateur, gestion du zoom, etc.) directement dans une application. A noter que pour afficher le PDF, la technique consiste à récupérer les pages et à la afficher en tant qu’images!

 

En termes de développement, c’est tout ce qu’il y a de plus simple. En effet, on commence par charger un document (depuis un fichier ou depuis une Stream):

var filePicker = new FileOpenPicker();
filePicker.FileTypeFilter.Add(".pdf");

var pdfFile = await filePicker.PickSingleFileAsync();

pdfDocument = await PdfDocument.LoadFromStreamAsync(await pdfFile.OpenAsync(Windows.Storage.FileAccessMode.Read));

A partir de là, il ne reste plus qu’à récupérer chaque page:

var pagesAsImages = new List<BitmapImage>();

var pdfPageCount = pdfDocument.PageCount;

for (uint i = 0; i < pdfPageCount; i++)
{
    using(var pdfPage = pdfDocument.GetPage(i))
    {
        //await pdfPage.PreparePageAsync();

        using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
        {
            await pdfPage.RenderToStreamAsync(stream);

            var bitmapImage = new BitmapImage();
            await bitmapImage.SetSourceAsync(stream);

            pagesAsImages.Add(bitmapImage);
        }
    }
}

this.flipView.ItemsSource = pagesAsImages;

Enfin, on modifie notre XAML:

<FlipView x:Name="flipView" 
          Grid.Row="1">
    <FlipView.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}" />
        </DataTemplate>
    </FlipView.ItemTemplate>
</FlipView>

Et le tour est joué !

image image

Etant donné que nous sommes en train de manipuler des images qui proviennent d’un PDF, il semble logique qu’on permette à l’utilisateur d’appliquer un zoom sur les images.

La technique, là encore, est simple: on embarque l’image dans un ScrollViewer:

<ScrollViewer HorizontalScrollBarVisibility="Auto"
              VerticalScrollBarVisibility="Auto"
              MinZoomFactor="1"
              MaxZoomFactor="5"
              ViewChanged="ScrollViewer_ViewChanged">
    <Image Source="{Binding}" />
</ScrollViewer>

Bien que cela fonctionne, on se rend vite compte que la qualité n’est pas vraiment au rendez-vous:

image

Pour contourner le problème, il suffit d’utiliser la classe PdfPageRenderOption qui permet, entre autre, de spécifier la couleur d’arrière-plan, le type d’encodage mais aussi le facteur de grossissement:

private async void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    if (!e.IsIntermediate)
    {
        var zoomFactor = ((ScrollViewer)sender).ZoomFactor;

        var currentPage = pdfDocument.GetPage((uint)this.flipView.SelectedIndex);
        var renderOptions = new PdfPageRenderOptions();
        renderOptions.DestinationHeight = (uint)(currentPage.Size.Height * zoomFactor);

        var flipViewItem = this.flipView.ContainerFromIndex(this.flipView.SelectedIndex) as FlipViewItem;
        if(flipViewItem != null)
        {
            var bitmapImage = flipViewItem.Content as BitmapImage;
            if (bitmapImage != null)
            {
                using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
                {
                    await currentPage.RenderToStreamAsync(stream, renderOptions);

                    await bitmapImage.SetSourceAsync(stream);

                    flipViewItem.Content = bitmapImage;
                }
            }
        }
    }
}

Le résultat, visible ci-dessous, est sans appel:

image

 

Voilà une nouvelle API de Windows 8.1 qui ouvre tout un tas de nouvelles possibilités pour vos applications !

 

Happy coding! Smile

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus