How to print PDF documents in Xamarin.Forms platform?
PDF documents can be printed in Xamarin Forms’ Android, iOS, and UWP platforms by obtaining the currently loaded PDF as stream and printing the stream using native printing functionalities available in the respective platforms using DependencyService of Xamarin Forms. These platform specific printing options can be used by defining an interface in the portable project and implementing the same in the platform projects using the specific code.
The following code snippets illustrate printing PDF documents in Xamarin Forms’ Android, iOS, and UWP platforms.
Portable
Define the following interface.
C#
namespace PDFPrintingSample
{
public interface IPrintService
{
void Print(Stream inputStream, string fileName);
}
}
Load the PDF to PdfViewer. Call the Print method with the stream obtained from PdfViewer and file name using DependencyService when the print button is clicked in the code behind. Give the name of the interface as the type parameter to the method DependencyService.Get<T>().
C#
public MainPage()
{
InitializeComponent();
documentStream = typeof(App).GetTypeInfo().Assembly.GetManifestResourceStream("PDFPrintingSample.Assets.GIS Succinctly.pdf");
pdfViewerControl.LoadDocument(documentStream);
printButton.Clicked += PrintButton_Clicked;
}
private void PrintButton_Clicked(object sender, EventArgs e)
{
//Obtain the PDF as a stream
Stream stream = pdfViewerControl.SaveDocument();
DependencyService.Get<IPrintService>().Print(stream, "GIS Succinctly.pdf");
}
Implement the interface in the platform projects with the available platform specific printing options.
Android
C#
//Register the Android implementation of the interface with DependencyService
[assembly: Dependency(typeof(PrintService))]
namespace PDFPrintingSample.Droid
{
class PrintService : IPrintService
{
public void Print(Stream inputStream, string fileName)
{
if (inputStream.CanSeek)
//Reset the position of PDF document stream to be printed
inputStream.Position = 0;
//Create a new file in the Personal folder with the given name
string createdFilePath = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), fileName);
//Save the stream to the created file
using (var dest = System.IO.File.OpenWrite(createdFilePath))
inputStream.CopyTo(dest);
string filePath = createdFilePath;
var activity = Xamarin.Essentials.Platform.CurrentActivity;
PrintManager printManager = (PrintManager)activity.GetSystemService(Context.PrintService);
PrintDocumentAdapter pda = new CustomPrintDocumentAdapter(filePath);
//Print with null PrintAttributes
printManager.Print(fileName, pda, null);
}
}
}
Define a class derived from the PrintDocumentAdapter and write the contents of the input file to the print destination in the OnWrite method.
C#
namespace PDFPrintingSample.Droid
{
internal class CustomPrintDocumentAdapter : PrintDocumentAdapter
{
internal string FileToPrint { get; set; }
internal CustomPrintDocumentAdapter(string fileDesc)
{
FileToPrint = fileDesc;
}
public override void OnLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras)
{
if (cancellationSignal.IsCanceled)
{
callback.OnLayoutCancelled();
return;
}
PrintDocumentInfo pdi = new PrintDocumentInfo.Builder(FileToPrint).SetContentType(Android.Print.PrintContentType.Document).Build();
callback.OnLayoutFinished(pdi, true);
}
public override void OnWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback)
{
InputStream input = null;
OutputStream output = null;
try
{
//Create FileInputStream object from the given file
input = new FileInputStream(FileToPrint);
//Create FileOutputStream object from the destination FileDescriptor instance
output = new FileOutputStream(destination.FileDescriptor);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = input.Read(buf)) > 0)
{
//Write the contents of the given file to the print destination
output.Write(buf, 0, bytesRead);
}
callback.OnWriteFinished(new PageRange[] { PageRange.AllPages });
}
catch (FileNotFoundException ee)
{
//Catch exception
}
catch (Exception e)
{
//Catch exception
}
finally
{
try
{
input.Close();
output.Close();
}
catch (IOException e)
{
e.PrintStackTrace();
}
}
}
}
}
iOS
C#
//Register the iOS implementation of the interface with DependencyService
[assembly: Dependency(typeof(PrintService))]
namespace PDFPrintingSample.iOS
{
class PrintService : IPrintService
{
public void Print(Stream inputStream, string fileName)
{
var printInfo = UIPrintInfo.PrintInfo;
printInfo.OutputType = UIPrintInfoOutputType.General;
printInfo.JobName = "Print PDF Sample";
//Get the path of the MyDocuments folder
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//Get the path of the Library folder within the MyDocuments folder
var library = Path.Combine(documents, "..", "Library");
//Create a new file with the input file name in the Library folder
var filepath = Path.Combine(library, fileName);
//Write the contents of the input file to the newly created file
using (MemoryStream tempStream = new MemoryStream())
{
inputStream.Position = 0;
inputStream.CopyTo(tempStream);
File.WriteAllBytes(filepath, tempStream.ToArray());
}
var printer = UIPrintInteractionController.SharedPrintController;
printInfo.OutputType = UIPrintInfoOutputType.General;
printer.PrintingItem = NSUrl.FromFilename(filepath);
printer.PrintInfo = printInfo;
printer.ShowsPageRange = true;
printer.Present(true, (handler, completed, err) => {
if (!completed && err != null)
{
Console.WriteLine("error");
}
});
}
}
}
UWP
C#
//Register the UWP implementation of the interface with DependencyService
[assembly: Xamarin.Forms.Dependency(typeof(PrintService))]
namespace PDFPrintingSample.UWP
{
class PrintService : IPrintService
{
internal int pageCount;
internal global::Windows.Data.Pdf.PdfDocument pdfDocument;
private PrintDocument printDocument;
private IPrintDocumentSource printDocumentSource;
string fileName;
double marginWidth = 0;
double marginHeight = 0;
private Canvas pdfDocumentPanel = new Canvas();
internal Dictionary<int, UIElement> printPreviewPages = new Dictionary<int, UIElement>();
private List<string> imagePaths = new List<string>();
IRandomAccessStream randomStream;
public async void Print(Stream inputStream, string fileName)
{
inputStream.Position = 0;
MemoryStream ms = new MemoryStream();
//Copy the input stream to a new MemoryStream
inputStream.CopyTo(ms);
ms.Position = 0;
//Convert the MemoryStream to a stream that allows random access
randomStream = await ConvertToRandomAccessStream(ms);
IAsyncOperation<global::Windows.Data.Pdf.PdfDocument> result = null;
//Create the IAsyncOperation object from the given random stream
result = global::Windows.Data.Pdf.PdfDocument.LoadFromStreamAsync(randomStream);
result.AsTask().Wait();
//Get the PdfDocument instance that represents the PDF document that is loaded
pdfDocument = result.GetResults();
result = null;
pageCount = (int)pdfDocument.PageCount;
fileName = fileName;
await IncludeCanvas();
try
{
UIDispatcher.Execute(async () =>
{
RegisterForPrint();
//Show the UI window with printing options
await PrintManager.ShowPrintUIAsync();
});
}
catch
{
UIDispatcher.Execute(async () =>
{
RegisterForPrint();
//Show the UI window with printing options
PrintManager.ShowPrintUIAsync();
});
}
}
//Method to convert the given MemoryStream to a stream that allows random access.
public async Task<IRandomAccessStream> ConvertToRandomAccessStream(MemoryStream memoryStream)
{
var randomAccessStream = new InMemoryRandomAccessStream();
MemoryStream contentStream = new MemoryStream();
memoryStream.CopyTo(contentStream);
using (var outputStream = randomAccessStream.GetOutputStreamAt(0))
{
using (var dw = new DataWriter(outputStream))
{
var task = new Task(() => dw.WriteBytes(contentStream.ToArray()));
task.Start();
await task;
await dw.StoreAsync();
await outputStream.FlushAsync();
await dw.FlushAsync();
outputStream.Dispose();
dw.DetachStream();
dw.Dispose();
}
}
return randomAccessStream;
}
PrintManager printMan;
private void RegisterForPrint()
{
printDocument = new PrintDocument();
// Save the DocumentSource.
printDocumentSource = printDocument.DocumentSource;
// Add an event handler which creates preview pages.
printDocument.Paginate += CreatePrintPreviewPages;
// Add an event handler which provides a specified preview page.
printDocument.GetPreviewPage += GetPrintPreviewPage;
// Add an event handler which provides all final print pages.
printDocument.AddPages += AddPrintPages;
// Create a PrintManager and add a handler for printing initialization.
printMan = PrintManager.GetForCurrentView();
printMan.PrintTaskRequested += PrintTaskRequested;
}
Image imageCtrl = new Image();
private async void AddPrintPages(object sender, AddPagesEventArgs e)
{
try
{
await PrepareForPrint(0, pageCount);
PrintDocument printDoc = (PrintDocument)sender;
printDoc.AddPagesComplete();
}
catch
{
PrintDocument printDoc = (PrintDocument)sender;
printDoc.InvalidatePreview();
}
}
private async Task<int> PrepareForPrint(int startIndex, int count)
{
StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
int result = await PrepareForPrint(startIndex, count, tempFolder);
tempFolder = null;
return result;
}
private async Task<int> PrepareForPrint(int p, int count, StorageFolder tempfolder)
{
for (int i = p; i < count; i++)
{
float zoomfactor = 1;
//Adjust the zoom factor according to the display properties
double customZoomFactor = zoomfactor * 96 / DisplayProperties.LogicalDpi;
ApplicationLanguages.PrimaryLanguageOverride = CultureInfo.InvariantCulture.TwoLetterISOLanguageName;
//Get the page from the PDF document with a given index
var pdfPage = pdfDocument.GetPage(uint.Parse(i.ToString()));
double pdfPagePreferredZoom = pdfPage.PreferredZoom /** m_pdfViewer.PrinterSettings.QualityFactor*/;
IRandomAccessStream randomStream = new InMemoryRandomAccessStream();
global::Windows.Data.Pdf.PdfPageRenderOptions pdfPageRenderOptions = new global::Windows.Data.Pdf.PdfPageRenderOptions();
Size pdfPageSize = pdfPage.Size;
//Set the height to which the page is to be printed
pdfPageRenderOptions.DestinationHeight = (uint)(pdfPageSize.Height * pdfPagePreferredZoom);
//Set the width to which the page is to be printed
pdfPageRenderOptions.DestinationWidth = (uint)(pdfPageSize.Width * pdfPagePreferredZoom);
await pdfPage.RenderToStreamAsync(randomStream, pdfPageRenderOptions);
//Create a new Image to which the page will be rendered
imageCtrl = new Image();
BitmapImage src = new BitmapImage();
randomStream.Seek(0);
//Obtain image source from the randomstream
src.SetSource(randomStream);
//set the image source to the image
imageCtrl.Source = src;
var DisplayInformation = Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
var dpi = DisplayInformation.LogicalDpi/96;
imageCtrl.Height = src.PixelHeight/dpi;
imageCtrl.Width = src.PixelWidth/dpi;
randomStream.Dispose();
pdfPage.Dispose();
printDocument.AddPage(imageCtrl);
}
return 0;
}
private void CreatePrintPreviewPages(object sender, PaginateEventArgs e)
{
PrintTaskOptions printingOptions = ((PrintTaskOptions)e.PrintTaskOptions);
PrintPageDescription pageDescription = printingOptions.GetPageDescription((uint)e.CurrentPreviewPageNumber);
marginWidth = pageDescription.PageSize.Width;
marginHeight = pageDescription.PageSize.Height;
AddOnePrintPreviewPage();
PrintDocument printDoc = (PrintDocument)sender;
printDoc.SetPreviewPageCount(pageCount, PreviewPageCountType.Final);
}
private void AddOnePrintPreviewPage()
{
for (int i = pdfDocumentPanel.Children.Count - 1; i >= 0; i--)
{
Canvas print = pdfDocumentPanel.Children[i] as Canvas;
if (print != null)
{
print.Width = marginWidth;
print.Height = marginHeight;
printPreviewPages.Add(i, print);
}
}
}
private async void GetPrintPreviewPage(object sender, GetPreviewPageEventArgs e)
{
PrintDocument printDoc = (PrintDocument)sender;
pdfDocumentPanel.Children.Remove(printPreviewPages[e.PageNumber - 1]);
printDoc.SetPreviewPage(e.PageNumber, printPreviewPages[e.PageNumber - 1]);
}
private async Task<int> IncludeCanvas()
{
for (int i = 0; i < pageCount; i++)
{
int pageIndex = i;
var pdfPage = pdfDocument.GetPage(uint.Parse(i.ToString()));
double width = pdfPage.Size.Width;
double height = pdfPage.Size.Height;
Canvas page = new Canvas();
page.Width = width;
page.Height = height;
page.VerticalAlignment = global::Windows.UI.Xaml.VerticalAlignment.Top;
page.HorizontalAlignment = global::Windows.UI.Xaml.HorizontalAlignment.Center;
page.Background = new Windows.UI.Xaml.Media.SolidColorBrush(global::Windows.UI.Color.FromArgb(255, 255, 255, 255));
page.Margin = new Thickness(0, 0, 0, 0);
double pdfPagePreferredZoom = pdfPage.PreferredZoom /** m_pdfViewer.PrinterSettings.QualityFactor*/;
IRandomAccessStream randomStream = new InMemoryRandomAccessStream();
global::Windows.Data.Pdf.PdfPageRenderOptions pdfPageRenderOptions = new global::Windows.Data.Pdf.PdfPageRenderOptions();
Size pdfPageSize = pdfPage.Size;
//Set the height to which the page is to be printed
pdfPageRenderOptions.DestinationHeight = (uint)(pdfPageSize.Height * pdfPagePreferredZoom);
//Set the width to which the page is to be printed
pdfPageRenderOptions.DestinationWidth = (uint)(pdfPageSize.Width * pdfPagePreferredZoom);
await pdfPage.RenderToStreamAsync(randomStream, pdfPageRenderOptions);
//Create a new Image to which the page will be rendered
imageCtrl = new Image();
BitmapImage src = new BitmapImage();
randomStream.Seek(0);
//Obtain image source from the randomstream
src.SetSource(randomStream);
//set the image source to the image
imageCtrl.Source = src;
var DisplayInformation = Windows.Graphics.Display.DisplayInformation.GetForCurrentView();
var dpi = DisplayInformation.LogicalDpi / 96;
imageCtrl.Height = src.PixelHeight / dpi;
imageCtrl.Width = src.PixelWidth / dpi;
randomStream.Dispose();
pdfPage.Dispose();
page.Children.Add(imageCtrl);
pdfDocumentPanel.Children.Add(page);
}
return 0;
}
PrintTask printTask;
MessageDialog msgDialog;
private async void PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs e)
{
printTask = e.Request.CreatePrintTask(fileName, sourceRequested => sourceRequested.SetSource(printDocumentSource));
printTask.Completed += printTask_Completed;
}
//Called when the printing operation completes
private void printTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
printTask.Completed -= printTask_Completed;
printMan.PrintTaskRequested -= PrintTaskRequested;
UIDispatcher.Execute(async () =>
{
msgDialog = new MessageDialog("Printing operation has been completed");
await msgDialog.ShowAsync();
});
}
}
//Helper class to run operations async
internal class UIDispatcher
{
private static CoreDispatcher Dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
/// <summary>
/// Executes the action using UIElement.
/// </summary>
/// <param name="action">An Action.</param>
internal static void Execute(Action action)
{
if (CoreApplication.MainView.CoreWindow == null
|| Dispatcher.HasThreadAccess)
action();
else
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).AsTask().Wait();
}
}
}
Sample link:
https://www.syncfusion.com/downloads/support/directtrac/general/ze/PDFPrintingSample1519956005.zip