Mobile

9 mai, 2019

O X do Xamarin Forms – Exibindo arquivos PDF

Publicidade

Fala, galera! Beleza?

Acredito que este seja o tema que mais me pediram até hoje: “como exibir arquivos PDF em Xamarin.Forms”.

Atendendo à todos que pediram e, claro, aos que não pediram também, hoje lhes trago uma das formas de abrir PDF com Xamarin.Forms (inclusive a que utilizo).

Arquivos PDF são muito comuns, pois comprimem e agrupam imagens, fazendo seu uso constante em diversas aplicações.

Muitas vezes precisamos exibi-los em nosso app, então hoje veremos uma forma simples e funcional.

Configurando os projetos

Gosto de exibir PDFs da seguinte forma:

  • Baixo, gravo no dispositivo e o carrego

Isso pode variar de acordo com a sua necessidade, como guardar todos os PDFs baixados ou apenas usar um nome temporário para exibição.

Como pode variar de app para app, vou mostrar uma forma e você pode adaptar para sua necessidade.

Para exibir o PDF vamos precisar de três coisas.

  • 1. Uma interface para gravar o arquivo Local
  • 2. Uma WebView Customizada
  • 3. Custom renderer por plataforma e uma biblioteca chamada pdfjs para Android

Configurando o projeto compartilhado

Vamos começar criando nossa Webview customizada. É possível apenas utilizar como base a webview do Xamarin.Forms, mas eu gosto de criar uma propriedade, caso eu precise fazer tratamentos ou outras utilizações:

 public class PdfWebView : WebView
    {
        public static readonly BindableProperty UriProperty = BindableProperty.Create("Uri", typeof(string), typeof(PdfWebView), default(string));

        public string Uri
        {
            get { return (string)GetValue(UriProperty); }
            set { SetValue(UriProperty, value); }
        }
    }

Em seguida, criaremos uma interface para gravar os arquivos nas duas plataformas. Você pode utilizar o Xamarin.Essentials ou criar a sua, caso queira customizar algo:

 public interface ILocalFileProvider
    {
        Task<string> SaveFileToDisk(Stream stream, string fileName);
    }

Pronto! Feito isso, mais à frente voltaremos neste projeto.

Agora precisamos criar um renderer para nosso PdfWebView e a implementação da interface para salvar os arquivos. Vamos fazer isso por plataforma.

Configurando o iOS

Vamos implementar o Renderer PdfWebViewRenderer:

using System;
using Foundation;
using LeitorPdfExemplo.Controls;
using LeitorPdfExemplo.iOS.Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(PdfWebView), typeof(PdfWebViewRenderer))]
namespace LeitorPdfExemplo.iOS.Renderers
{
    public class PdfWebViewRenderer : ViewRenderer<PdfWebView, UIWebView>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<PdfWebView> e)
        {
            base.OnElementChanged(e);

            if (Control == null)
            {
                SetNativeControl(new UIWebView());
            }
            if (e.OldElement != null)
            {
                // Cleanup
            }
            if (e.NewElement != null)
            {
                var customWebView = Element as PdfWebView;
                string fileName = customWebView.Uri;

                if (!string.IsNullOrEmpty(fileName))
                {
                    Control.LoadRequest(new NSUrlRequest(new NSUrl(fileName, false)));
                    Control.ScalesPageToFit = true;
                }
            }
        }
    }
}

E implementar a interface para salvar o arquivo na pasta pdfjs:

using System;
using System.IO;
using System.Threading.Tasks;
using LeitorPdfExemplo.Helpers;
using LeitorPdfExemplo.iOS.Helpers;
using Xamarin.Forms;

[assembly: Dependency(typeof(LocalFileProvider))]
namespace LeitorPdfExemplo.iOS.Helpers
{
    public class LocalFileProvider : ILocalFileProvider
    {
        private readonly string _rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "pdfjs");

        public async Task<string> SaveFileToDisk(Stream stream, string fileName)
        {
            if (!Directory.Exists(_rootDir))
                Directory.CreateDirectory(_rootDir);

            var filePath = Path.Combine(_rootDir, fileName);

            using (var memoryStream = new MemoryStream())
            {
                await stream.CopyToAsync(memoryStream);
                File.WriteAllBytes(filePath, memoryStream.ToArray());
            }

            return filePath;
        }
    }
}
  • iOS check!

Configurando o Android

O Android é um pouco mais chato que o iOS, mas nada de outro mundo.

Antes de tudo, devemos importar a biblioteca pdfjs na pasta Assets:

Você pode pegar a pasta no exemplo no final do artigo. Em seguida, para não termos problemas, daremos a permissão de leitura e escrita de arquivos no AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" package="br.com.bertuzzi.leitorpdfexemplo" android:versionCode="2">
	<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
    
	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
	<application android:label="LeitorPdfExemplo.Android"></application>
</manifest>

Implementaremos o Renderer PdfWebViewRenderer:

using System;
using Android.Content;
using LeitorPdfExemplo.Controls;
using LeitorPdfExemplo.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(PdfWebView), typeof(PdfWebViewRenderer))]
namespace LeitorPdfExemplo.Droid.Renderers
{
    public class PdfWebViewRenderer : WebViewRenderer
    {
        public PdfWebViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                var customWebView = Element as PdfWebView;

                Control.Settings.AllowFileAccess = true;
                Control.Settings.AllowFileAccessFromFileURLs = true;
                Control.Settings.AllowUniversalAccessFromFileURLs = true;

                if (!string.IsNullOrEmpty(customWebView.Uri))
                {
                    Control.LoadUrl($"file:///android_asset/pdfjs/web/viewer.html?file={System.Net.WebUtility.UrlEncode(customWebView.Uri)}");
                }
            }
        }
    }
}

E claro, a implementação da interface para salvar os arquivos:

using System;
using System.IO;
using System.Threading.Tasks;
using LeitorPdfExemplo.Droid.Helpers;
using LeitorPdfExemplo.Helpers;
using Xamarin.Forms;

[assembly: Dependency(typeof(LocalFileProvider))]
namespace LeitorPdfExemplo.Droid.Helpers
{
    public class LocalFileProvider : ILocalFileProvider
    {
       
        private readonly string _rootDir = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "pdfjs");

        public async Task<string> SaveFileToDisk(Stream pdfStream, string fileName)
        {
            if (!Directory.Exists(_rootDir))
                Directory.CreateDirectory(_rootDir);

            var filePath = Path.Combine(_rootDir, fileName);

            using (var memoryStream = new MemoryStream())
            {
                await pdfStream.CopyToAsync(memoryStream);
                File.WriteAllBytes(filePath, memoryStream.ToArray());
            }

            return filePath;
        }
    }
}
  • Android Check!

Utilizando e exibindo o PDF

Depois de tudo configurado vamos abrir nosso PDF. Para isso, vamos criar uma página e configurar o controle:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
              xmlns:controls="clr-namespace:LeitorPdfExemplo.Controls"
              xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" 
             ios:Page.UseSafeArea="true"
             Padding="0,20,0,0"
              x:Class="LeitorPdfExemplo.MainPage">
     <StackLayout>
       
        <controls:PdfWebView x:Name="PdfView" 
                             HorizontalOptions="FillAndExpand"
                             VerticalOptions="FillAndExpand" />
        
    </StackLayout>
</ContentPage>

E agora carregar o PDF:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using LeitorPdfExemplo.Helpers;
using Xamarin.Forms;

namespace LeitorPdfExemplo
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(true)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            CarregarPdf();
        }

        private void CarregarPdf()
        {
            var dependency = DependencyService.Get<ILocalFileProvider>();

            if (dependency == null)
            {
                DisplayAlert("Erro ao carregar dependencia", "Dependencia não encontrada", "OK");

                return;
            }

            var localPath = string.Empty;

            string url = "https://repositorio.unesp.br/bitstream/handle/11449/118389/000793203.pdf";

            var fileName = Guid.NewGuid().ToString();

            using (var httpClient = new HttpClient())
            {
                var pdfStream = Task.Run(() => httpClient.GetStreamAsync(url)).Result;
                localPath =
                    Task.Run(() => dependency.SaveFileToDisk(pdfStream, $"{fileName}.pdf")).Result;
            }

            if (string.IsNullOrWhiteSpace(localPath))
            {
                DisplayAlert("Error baixar PDF", "não foi possivel encontrar o arquivo", "OK");

                return;
            }

            PdfView.Uri = localPath;
        }
    }
}

Basicamente baixamos o PDF, gravamos no app e chamamos a PdfView:

Bem legal, não?

Caso queira baixar o código utilizado no exemplo, clique aqui.

Quer ver outros artigos sobre Xamarin? Acesse este link.

Espero ter ajudado.

Aquele abraço!