Render and print PDF documents in C# and VB.NET

Everybody thinks about Adobe Reader when it comes to reading PDF documents. But what to do if you are a software developer and want to display PDF documents in your application? You do not want to ask the user to set up Adobe Reader and open the document there.

You may also need to implement other PDF-related requirements. For example, print PDF documents. Or generate thumbnails for uploaded PDF documents. Or support PDF editing in your desktop or web application.

All you need is a PDF library that can render and print PDF documents.

.NET library to view, edit, or print PDF documents

Docotic.Pdf library allows you to display, edit, and print PDF documents in C# and VB.NET. You can implement any complex PDF rendering flow using three core features.

The first one is the ability to convert PDF to images. You can implement almost everything based on this feature. From PDF printing to displaying a PDF on HoloLense or posting a PDF on Instagram. That is possible because images are supported everywhere.

The second key feature is the ability to draw PDF pages on the drawing surface of a System.Drawing.Graphics. It allows you to render and print PDFs in Windows Forms or WPF applications. To use this feature, add BitMiracle.Docotic.Pdf.Gdi extension DLL to your project.

Last but not least is the ability to extract PDF page objects. This feature allows you to build PDF viewer or PDF editor in your C# or VB.NET application.

You can download the binaries of the library or use its NuGet package. To try Docotic.Pdf without evaluation mode restrictions, you may get the free time-limited license key by using the form here.

.NET does not support printing of PDF documents out of the box. You need to transform your PDF document to an image, an XPS file, or draw it to a System.Drawing.Graphics surface. Then you can use .NET classes to print the output from within a Windows Forms or a WPF application.

In Windows Forms applications, you can use classes from System.Drawing.Printing namespace. There are also handy PrintDialog and PrintPreviewDialog classes for building printing UI.

Please note that it is not recommended to use anything from the System.Drawing namespace in macOS and Linux environments. The macOS and Linux implementations of the System.Drawing are incomplete and different from the Windows implementation. You might get incorrect and/or inconsistent results if you use the System.Drawing namespace in macOS and Linux environments.

WPF provides another PrintDialog class but does not provide classes for print preview. Luckily, WPF allows you to use PrintPreviewDialog class from System.Windows.Forms.dll. Thus, it's easier to use print classes from System.Windows.Forms and System.Drawing.Printing namespaces in WPF applications.

Look at the Print PDF in .NET demo application that comes in C# and VB.NET versions for WinForms and WPF. The application shows how to:

  • display a print preview for PDF documents
  • print PDF documents to a selected printer
  • set up the paper size, the scaling mode, and other print settings

The application uses Docotic.Pdf library, BitMiracle.Docotic.Pdf.Gdi extension DLL, and print classes from System.Windows.Forms and System.Drawing.Printing namespaces. You can use its PdfPrintDocument and PdfPrintHelper classes in your WPF or Windows Forms projects.

The PdfPrintDocument class describes the main printing logic. This class connects Docotic.Pdf with the System.Drawing.Printing.PrintDocument class. The key method is the printDocument_PrintPage event handler:

private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
    Graphics gr = e.Graphics;

    // Work in points to have consistent units for all contexts:
    // 1. Printer
    // 2. Print preview
    // 3. PDF
    gr.PageUnit = GraphicsUnit.Point;

    if (m_printAction == PrintAction.PrintToPreview)
    {
        gr.Clear(Color.LightGray);
        gr.FillRectangle(Brushes.White, m_printableAreaInPoints);
        gr.IntersectClip(m_printableAreaInPoints);

        gr.TranslateTransform(m_printableAreaInPoints.X, m_printableAreaInPoints.Y);
    }

    PdfPage page = m_pdf.Pages[m_pageIndex];
    PdfSize pageSizeInPoints = getPageSizeInPoints(page);

    if (m_printSize == PrintSize.FitPage)
    {
        float sx = (float)(m_printableAreaInPoints.Width / pageSizeInPoints.Width);
        float sy = (float)(m_printableAreaInPoints.Height / pageSizeInPoints.Height);
        float scaleFactor = Math.Min(sx, sy);

        centerContentInPrintableArea(gr, pageSizeInPoints, scaleFactor);
        gr.ScaleTransform(scaleFactor, scaleFactor);
    }
    else if (m_printSize == PrintSize.ActualSize)
    {
        centerContentInPrintableArea(gr, pageSizeInPoints, 1);
    }

    page.Draw(gr);

    ++m_pageIndex;
    e.HasMorePages = (m_pageIndex <= m_lastPageIndex);
}

First, we set up transformations of the printed page's Graphics object. It's important to respect the "Fit page" and "Actual size" settings. And we also display the printer's hard margins in the Print Preview dialog. Then we draw the current PDF page on the Graphics object using PdfPage.Draw extension method.

The PdfPrintHelper class allows you to use PdfPrintDocument with UI classes for printing. For example, you can show the print dialog for your PDF document like that:

using (var pdf = new PdfDocument("your_document.pdf"))
    action(pdf, getPrintSize());

public static DialogResult ShowPrintDialog(PdfDocument pdf, PrintSize printSize)
{
    using (var printDialog = new PrintDialog())
    {
        printDialog.AllowSomePages = true;
        printDialog.AllowCurrentPage = true;
        printDialog.AllowSelection = true;

        printDialog.PrinterSettings.MinimumPage = 1;
        printDialog.PrinterSettings.MaximumPage = pdf.PageCount;
        printDialog.PrinterSettings.FromPage = printDialog.PrinterSettings.MinimumPage;
        printDialog.PrinterSettings.ToPage = printDialog.PrinterSettings.MaximumPage;

        var result = printDialog.ShowDialog();
        if (result == DialogResult.OK)
        {
            using (var printDocument = new PdfPrintDocument(pdf, printSize))
                printDocument.Print(printDialog.PrinterSettings);
        }

        return result;
    }
}

That's it. Do the following to implement PDF printing in your Windows Forms or WPF application:

  • add PdfPrintDocument, PdfPrintHelper, and PrintSize files from the sample
  • add a reference to Docotic.Pdf library
  • add a reference to BitMiracle.Docotic.Pdf.Gdi extension DLL
  • (WPF only) add references to System.Windows.Forms and System.Drawing assemblies

Classes from System.Drawing.Printing namespace work well in Windows Forms and WPF applications. But, there are cases when you can't or shouldn't use System.Drawing.Printing.

For example, you should not use System.Drawing.Printing in Windows services or ASP.NET applications. System.Drawing.Printing might produce inconsistent results when printing on Linux or macOS. And you cannot use System.Drawing.Printing in Eto.Forms or Avalonia applications.

In such cases, you need to convert PDF document to image first. This C# sample shows how to save a PDF page as a PNG image or convert an entire PDF document to a multipage TIFF:

using (var pdf = new PdfDocument(@"your_document.pdf"))
{
    PdfDrawOptions options = PdfDrawOptions.Create();
    options.HorizontalResolution = printerDpi;
    options.VerticalResolution = printerDpi;

    // save one page
    pdf.Pages[0].Save("page0.png", options);

    // save the whole document as multipage bitonal TIFF
    options.Compression = ImageCompressionOptions.CreateBitonalTiff();
    pdf.SaveAsTiff("your_document.tiff", options);
}

Then, print images using an alternative printing framework or tool. Look at the Print PDF in Eto.Forms demo application that shows how to print PDF documents without System.Drawing.Printing.

Render PDF in C# and VB.NET

Displaying of PDF in your application is like printing. In ASP.NET, WPF, Eto.Forms, Avalonia, or any other application type, convert PDF to image and then display the image.

In a Windows Forms app, you can draw a PDF page on a System.Drawing.Graphics context from any control. Or you can convert a PDF page to a System.Drawing.Bitmap and then display the bitmap in a PictureBox.

This sample shows how to convert PDF page to Bitmap in C#:

using (var pdf = new PdfDocument("render.pdf"))
{
    const float TargetResolution = 300;
    
    PdfPage page = pdf.Pages[0];
    double scaleFactor = TargetResolution / page.Resolution;

    using (var bitmap = new Bitmap((int)(page.Width * scaleFactor), (int)(page.Height * scaleFactor)))
    {
        bitmap.SetResolution(TargetResolution, TargetResolution);

        using (Graphics gr = Graphics.FromImage(bitmap))
            page.Draw(gr);

        bitmap.Save("result.png");
    }
}

The above code requires you to add BitMiracle.Docotic.Pdf.Gdi extension DLL to your project.

Create PDF viewer or PDF editor in .NET application

Docotic.Pdf allows you to get detailed information about every object on a PDF page. You can extract text chunks, images, vector paths, form controls, and annotations from a PDF page.

Then in your application, you can render all the extracted objects. You can also implement the ability to select and edit the rendered objects.

Look at the Extract text, images, and paths from PDF sample. It shows how to extract and draw page objects to System.Drawing.Graphics context.

You can also extract text objects as vector paths. Use PdfPage.GetObjects(PdfObjectExtractionOptions) overload for that.

Conclusion

Use Docotic.Pdf library to display and print PDF documents in your .NET projects. Look at related C# and VB.NET samples:

Contact us if you have questions about PDF printing or rendering.