(Print this page)

Generating PDF from .Net easily
Published date: Wednesday, March 25, 2020
On: Moer and Éric Moreau's web site

I always love to explore new libraries, especially when they offer free and innovative solutions to problems I experienced on my daily job. The one used here today for quite some time and found time today to test and explore it.

This library lets you create PDF files, yes, another way of creating PDFs but it is worth the look! This library is called the PDF File Writer.

Available source code

Both VB and C# versions are provided this month.

The solution was created using Visual Studio 2017 but should also work in previous versions.

Downloading the required DLL

The downloadable attachment only contains the DLL of the library required to execute a quick test.

If you want the full examples demonstrating a lot more features available from this library, have a look at original page: https://www.codeproject.com/Articles/570682/PDF-File-Writer-Csharp-Class-Library-Version-1-26.

You can also get the full source code of the library from that link.

What are we going to build here?

I am mostly building business applications and often must create quick reports from data available in datatables (or other list of data) in my app and found that this library can be a good fit for that. Do not hesitate to read the full article from Code Project as the library lets you build much more complicated PDF files that I will not be covering here.

What will be done in this article is to show how easy it is to generate a PDF from a data table object you have in your applications. The PDF that you see in the background of figure 1 requires less then 150 lines of code total.

Figure 1: The demo application in action

Creating the project

The first thing you need to do is too get the PdfFileWriter.dll and put it in a place where your application will be able to reference it. You can get that file at least from the downloadable attachment to this article or from the Code Project site.

After the file has been stored somewhere (presumably on your C: drive), add a reference from your project to this DLL by using the Browse button from the Reference Manager dialog.

In this first snippet of code, what we see is that some declarations are done to hold some value like the content of the document and the page we will create. The staring method initializes some properties that will be used along the code, call a method to load fake data (CreateDataTable), proceed to the creation of the PDF file (GeneratePDF) and open the PDF in the default DFP viewer application.

 Private _document As PdfDocument
 Private _page As PdfPage
 Private _contents As PdfContents
 Private _normalFont As PdfFont
 Private _tableTitleFont As PdfFont
 Private _pageNumber As Integer

 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
     listBox1.Items.Add("Starting the process")

     'create a source of test data
     listBox1.Items.Add("Creating the data source...")
     Dim dt As DataTable = CreateDataTable()
     dataGridView1.DataSource = dt

     'Create empty document and some fonts
     listBox1.Items.Add("Creating the document and some properties...")
     _document = New PdfDocument(PaperType.Letter, False, UnitOfMeasure.Inch, "Test.pdf")
     _normalFont = PdfFont.CreatePdfFont(_document, "Arial", FontStyle.Regular, True)
     _tableTitleFont = PdfFont.CreatePdfFont(_document, "Times New Roman", FontStyle.Bold, True)

     'Generate the PDF file
     listBox1.Items.Add("Generate the PDF file...")
     GeneratePDF(dt)

     'Complete the process
     listBox1.Items.Add("Closing the file...")
     _document.CreateFile()

     'start default PDF reader and display the file
     listBox1.Items.Add("Opening the newly generated file...")
     Process.Start("Test.pdf")

     listBox1.Items.Add("Process is completed")
 End Sub
private readonly PdfDocument _document;
private PdfPage _page;
private PdfContents _contents;
private readonly PdfFont _normalFont;
private readonly PdfFont _tableTitleFont;
private int _pageNumber;

public Form1()
{
    InitializeComponent();

    listBox1.Items.Add("Starting the process");

    //create a source of test data
    listBox1.Items.Add("Creating the data source...");
    DataTable dt = CreateDataTable();
    dataGridView1.DataSource = dt;

    // Create empty document and some fonts
    listBox1.Items.Add("Creating the document and some properties...");
    _document = new PdfDocument(PaperType.Letter, false, UnitOfMeasure.Inch, "Test.pdf");
    _normalFont = PdfFont.CreatePdfFont(_document, "Arial", FontStyle.Regular, true);
    _tableTitleFont = PdfFont.CreatePdfFont(_document, "Times New Roman", FontStyle.Bold, true);

    //Generate the PDF file
    listBox1.Items.Add("Generate the PDF file...");
    GeneratePDF(dt);

    // Complete the process
    listBox1.Items.Add("Closing the file...");
    _document.CreateFile();

    // start default PDF reader and display the file
    listBox1.Items.Add("Opening the newly generated file...");
    Process.Start("Test.pdf");

    listBox1.Items.Add("Process is completed");
}

The GeneratePDF method reads like this:

Public Sub GeneratePDF(ByVal pDataTable As DataTable)
    Const columnID As Integer = 0
    Const columnCode As Integer = 1
    Const columnDesc As Integer = 2
    Const columnPrice As Integer = 3

    ' Add new page
    _page = New PdfPage(_document)
    ' Add contents to page
    _contents = New PdfContents(_page)

    ' create table
    Dim table As PdfTable = New PdfTable(_page, _contents, _normalFont, 9.0)
    table.SetColumnWidth(1.0, 1.0, 2.0, 1.0)

    ' event handlers for headers and footers
    AddHandler table.TableStartEvent, AddressOf CreateTableStart
    AddHandler table.TableEndEvent, AddressOf CreateTableEnd
    table.HeaderOnEachPage = True

    ' set all borders
    table.Borders.SetAllBorders(0.012, Color.DarkGray, 0.0025, Color.DarkGray)

    ' define the header
    table.DefaultHeaderStyle.Alignment = ContentAlignment.MiddleCenter
    table.Header(columnID).Value = "ID"
    table.Header(columnCode).Value = "Code"
    table.Header(columnDesc).Value = "Description"
    table.Header(columnPrice).Value = "Price"

    ' make some changes to default cell style
    table.DefaultCellStyle.Alignment = ContentAlignment.MiddleLeft

    ' create private styles for price column
    Dim normalPriceStyle As PdfTableStyle = table.CellStyle
    normalPriceStyle.Alignment = ContentAlignment.MiddleRight
    normalPriceStyle.Format = "#,##0.00"

    Dim highPriceStyle As PdfTableStyle = table.CellStyle
    highPriceStyle.BackgroundColor = Color.Red
    highPriceStyle.ForegroundColor = Color.White
    highPriceStyle.Alignment = ContentAlignment.MiddleRight
    highPriceStyle.Format = "#,##0.00"

    'loop through each rows
    For Each row As DataRow In pDataTable.Rows
        table.Cell(columnID).Value = row("ID")
        table.Cell(columnCode).Value = row("Code")
        table.Cell(columnDesc).Value = row("Description")
        Dim decPrice As Decimal
        Decimal.TryParse(row("Price").ToString(), decPrice)
        table.Cell(columnPrice).Value = decPrice

        table.Cell(columnPrice).Style = If(decPrice > 80, highPriceStyle, normalPriceStyle)

        table.DrawRow()
    Next

    table.Close()
End Sub

Private Sub CreateTableStart(ByVal pPdfTable As PdfTable, ByVal pTableStartPos As Double)
    Dim posX As Double = 0.5 * (pPdfTable.TableArea.Left + pPdfTable.TableArea.Right)
    Dim posY As Double = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.05
    pPdfTable.Contents.DrawText(_tableTitleFont, 16.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Blue, "Product list")
End Sub

Private Sub CreateTableEnd(ByVal pPdfTable As PdfTable, ByVal pTableEndPos As Double)
    Dim posX As Double = pPdfTable.TableArea.Left
    Dim posY As Double = pTableEndPos - _tableTitleFont.Ascent(12.0) - 0.05
    _pageNumber += 1
    pPdfTable.Contents.DrawText(_tableTitleFont, 12.0, posX, posY, TextJustify.Left, DrawStyle.Normal, Color.Black, "page " & _pageNumber)
End Sub
public void GeneratePDF(DataTable pDataTable)
{
    const int columnID = 0;
    const int columnCode = 1;
    const int columnDesc = 2;
    const int columnPrice = 3;

    // Add new page
    _page = new PdfPage(_document);
    // Add contents to page
    _contents = new PdfContents(_page);

    // create table
    PdfTable table = new PdfTable(_page, _contents, _normalFont, 9.0);
    table.SetColumnWidth(1.0, 1.0, 2.0, 1.0);

    // event handlers for headers and footers
    table.TableStartEvent += CreateTableStart;
    table.TableEndEvent += CreateTableEnd;
    table.HeaderOnEachPage = true;

    // set all borders
    table.Borders.SetAllBorders(0.012, Color.DarkGray, 0.0025, Color.DarkGray);

    // define the header 
    table.DefaultHeaderStyle.Alignment = ContentAlignment.MiddleCenter;
    table.Header[columnID].Value = "ID";
    table.Header[columnCode].Value = "Code";
    table.Header[columnDesc].Value = "Description";
    table.Header[columnPrice].Value = "Price";

    // make some changes to default cell style
    table.DefaultCellStyle.Alignment = ContentAlignment.MiddleLeft;

    // create private styles for price column
    PdfTableStyle normalPriceStyle = table.CellStyle;
    normalPriceStyle.Alignment = ContentAlignment.MiddleRight;
    normalPriceStyle.Format = "#,##0.00";
    
    PdfTableStyle highPriceStyle = table.CellStyle;
    highPriceStyle.BackgroundColor = Color.Red;
    highPriceStyle.ForegroundColor = Color.White;
    highPriceStyle.Alignment = ContentAlignment.MiddleRight;
    highPriceStyle.Format = "#,##0.00";

    // loop through each rows
    foreach (DataRow row in pDataTable.Rows)
    {
        table.Cell[columnID].Value = row["ID"];
        table.Cell[columnCode].Value = row["Code"];
        table.Cell[columnDesc].Value = row["Description"];

        decimal decPrice;
        decimal.TryParse(row["Price"].ToString(), out decPrice);
        table.Cell[columnPrice].Value = decPrice;
        table.Cell[columnPrice].Style = decPrice > 80 ? highPriceStyle : normalPriceStyle;

        table.DrawRow();
    }

    table.Close();
}

private void CreateTableStart(PdfTable pPdfTable, double pTableStartPos)
{
    double posX = 0.5 * (pPdfTable.TableArea.Left + pPdfTable.TableArea.Right);
    double posY = pTableStartPos + _tableTitleFont.Descent(16.0) + 0.05;
    pPdfTable.Contents.DrawText(_tableTitleFont, 16.0, posX, posY, TextJustify.Center, DrawStyle.Normal, Color.Blue, "Product list");
}

private void CreateTableEnd(PdfTable pPdfTable, double pTableEndPos)
{
    double posX = pPdfTable.TableArea.Left;
    double posY = pTableEndPos - _tableTitleFont.Ascent(12.0) - 0.05;
    _pageNumber += 1;
    pPdfTable.Contents.DrawText(_tableTitleFont, 12.0, posX, posY, TextJustify.Left, DrawStyle.Normal, Color.Black, "page " + _pageNumber);
}

This is all you need! You define your columns, add some formatting/styling if you want and add your lines one by one.

Running the project

If you run this test project, the PDF will be automatically generated and opened in the default viewer because the important code is in the Load event handler of the form (or the constructor in C#).

Another example of usage

Another person decided to demonstrate the usage of this library even including a demo from .Net Core. In fact, he built an extra layer on top the PDF File Writer adding features like grouping to tables. You can check this extra layer at https://www.codeproject.com/Articles/1075061/A-Generic-and-Advanced-PDF-Data-List-Reporting-Too.

Conclusion

This free and complete library is yet another one that can create PDF from a .Net application. Try it, you will surely love it!

As said earlier, I only demonstrate one feature of this library. There are many more that you can explore.


(Print this page)