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:

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

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF.Android/PDFSaveAndOpen.cs

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.

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF.iOS/PreviewControllerDS.cs

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:

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF.iOS/PDFSaveAndOpen.cs

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>

https://github.com/officialdoniald/Xamarin.Forms.Save.Open.PDF/blob/master/Xamarin.Forms.Save.Open.PDF/Xamarin.Forms.Save.Open.PDF/MainPage.xaml.cs

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.

Leave a Reply

Your email address will not be published. Required fields are marked *