Xamarin.Forms save and open PDF file
This project you can find at: https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF.
It could be a real situation that your app need to save or open a pdf file. How can it possible? Throught DependencyService. There are many companies, who deal with generationg PDF Document in Xamarin.Forms. We need to create a PDF Document first and than save it. But if you want to open an exists document, just read this blog post cointinue :D. Companies, whom you can download NuGet Packages to create PDF Document:
- https://www.pdftron.com/pdf-sdk/xamarin-library/
- https://www.syncfusion.com/kb/9174/how-to-create-a-pdf-file-in-xamarin
- etc…
First thing we need to do, that create an interface in the StandardLibrary. https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF/IPDFSaveAndOpen.cs
using System;
using System.IO;
using System.Threading.Tasks;
namespace Xamarin.Forms.Save.Open.PDF
{
public interface IPDFSaveAndOpen
{
Task SaveAndView(string fileName, String contentType, MemoryStream stream, PDFOpenContext context);
}
/// <summary>
/// Where should the PDF file open. In the app or out of the app.
/// </summary>
public enum PDFOpenContext
{
InApp,
ChooseApp
}
}
Implement it on the various platforms!
Android
using Android;
using Android.Content;
using Android.Content.PM;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using Android.Webkit;
using Java.IO;
using System;
using System.IO;
using System.Threading.Tasks;
using Android.App;
using Xamarin.Forms;
using Xamarin.Forms.Save.Open.PDF;
using Droid.Implementations;
[assembly: Dependency(typeof(PDFSaveAndOpen))]
namespace Droid.Implementations
{
public class PDFSaveAndOpen : IPDFSaveAndOpen
{
public async Task SaveAndView(string fileName, String contentType, MemoryStream stream, PDFOpenContext context)
{
string exception = string.Empty;
string root = null;
if (ContextCompat.CheckSelfPermission(Forms.Context, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
{
ActivityCompat.RequestPermissions((Activity)Forms.Context, new String[] { Manifest.Permission.WriteExternalStorage }, 1);
}
if (Android.OS.Environment.IsExternalStorageEmulated)
{
root = Android.OS.Environment.ExternalStorageDirectory.ToString();
}
else
root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Java.IO.File myDir = new Java.IO.File(root + "/PDFFiles");
myDir.Mkdir();
Java.IO.File file = new Java.IO.File(myDir, fileName);
if (file.Exists()) file.Delete();
try
{
FileOutputStream outs = new FileOutputStream(file);
outs.Write(stream.ToArray());
outs.Flush();
outs.Close();
}
catch (Exception e)
{
exception = e.ToString();
}
if (file.Exists() && contentType != "application/html")
{
string extension = MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString());
string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
Intent intent = new Intent(Intent.ActionView);
intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask);
Android.Net.Uri path = FileProvider.GetUriForFile(Forms.Context, Android.App.Application.Context.PackageName + ".provider", file);
intent.SetDataAndType(path, mimeType);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
switch (context)
{
case PDFOpenContext.InApp:
Forms.Context.StartActivity(intent);
break;
case PDFOpenContext.ChooseApp:
Forms.Context.StartActivity(Intent.CreateChooser(intent, "Choose App"));
break;
default:
break;
}
}
}
}
}
iOS
We need to create a PreviewController and that we have to show the PDF on this Controller.
using Foundation;
using QuickLook;
using System;
using System.IO;
namespace Xamarin.Forms.Save.Open.PDF.iOS
{
public class PreviewControllerDS : QLPreviewControllerDataSource
{
//Document cache
private QLPreviewItem _item;
//Setting the document
public PreviewControllerDS(QLPreviewItem item)
{
_item = item;
}
//Setting document count to 1
public override nint PreviewItemCount(QLPreviewController controller)
{
return 1;
}
//Return the document
public override IQLPreviewItem GetPreviewItem(QLPreviewController controller, nint index)
{
return _item;
}
}
public class QLPreviewItemFileSystem : QLPreviewItem
{
string _fileName, _filePath;
//Setting file name and path
public QLPreviewItemFileSystem(string fileName, string filePath)
{
_fileName = fileName;
_filePath = filePath;
}
//Return file name
public override string ItemTitle
{
get
{
return _fileName;
}
}
//Retun file path as NSUrl
public override NSUrl ItemUrl
{
get
{
return NSUrl.FromFilename(_filePath);
}
}
}
public class QLPreviewItemBundle : QLPreviewItem
{
string _fileName, _filePath;
//Setting file name and path
public QLPreviewItemBundle(string fileName, string filePath)
{
_fileName = fileName;
_filePath = filePath;
}
//Return file name
public override string ItemTitle
{
get
{
return _fileName;
}
}
//Retun file path as NSUrl
public override NSUrl ItemUrl
{
get
{
var documents = NSBundle.MainBundle.BundlePath;
var lib = Path.Combine(documents, _filePath);
var url = NSUrl.FromFilename(lib);
return url;
}
}
}
}
Implementation:
using System;
using System.Threading.Tasks;
using System.IO;
using Xamarin.Forms;
using UIKit;
using QuickLook;
using Xamarin.Forms.Save.Open.PDF.iOS;
[assembly: Dependency(typeof(PDFSaveAndOpen))]
namespace Xamarin.Forms.Save.Open.PDF.iOS
{
public class PDFSaveAndOpen : IPDFSaveAndOpen
{
//Method to save document as a file and view the saved document
public async Task SaveAndView(string filename, string contentType, MemoryStream stream, PDFOpenContext context)
{
//Get the root path in iOS device.
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filePath = Path.Combine(path, filename);
//Create a file and write the stream into it.
FileStream fileStream = File.Open(filePath, FileMode.Create);
stream.Position = 0;
stream.CopyTo(fileStream);
fileStream.Flush();
fileStream.Close();
//Invoke the saved document for viewing
UIViewController currentController = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (currentController.PresentedViewController != null)
currentController = currentController.PresentedViewController;
UIView currentView = currentController.View;
QLPreviewController qlPreview = new QLPreviewController();
QLPreviewItem item = new QLPreviewItemBundle(filename, filePath);
qlPreview.DataSource = new PreviewControllerDS(item);
currentController.PresentViewController(qlPreview, true, null);
}
}
}
Let’s just create a Page and try it: https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF/MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Save.Open.PDF.MainPage">
<StackLayout>
<Button HeightRequest="100" Text="Save and open PDF in app" Clicked="Button_Clicked"/>
<Button HeightRequest="100" Text="Save and open PDF with choose app" Clicked="Button_Clicked_1"/>
</StackLayout>
</ContentPage>
using System;
using System.IO;
namespace Xamarin.Forms.Save.Open.PDF
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
/// <summary>
/// Save and open the PDF file in the app.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Button_Clicked(object sender, EventArgs e)
{
MemoryStream stream = new MemoryStream();
await Xamarin.Forms.DependencyService.Get<IPDFSaveAndOpen>().SaveAndView(Guid.NewGuid() + ".pdf", "application / pdf", stream, PDFOpenContext.InApp);
}
/// <summary>
/// Save and open the PDF file with "choose app".
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void Button_Clicked_1(object sender, EventArgs e)
{
MemoryStream stream = new MemoryStream();
await Xamarin.Forms.DependencyService.Get<IPDFSaveAndOpen>().SaveAndView(Guid.NewGuid() + ".pdf", "application / pdf", stream, PDFOpenContext.ChooseApp);
}
}
}
Why is just a MemoryStream stream = new MemoryStream(); ?
Because I don’t have a pdf in stream. 😀 This is your task to create a stream (upload a pdf on your phone and create a Stream: pdf files are opened in the same method like other file types) and open it with this method.