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!