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.