Create PDF in .NET Core Using itext7

davidelegbede

David Elegbede

Posted on February 3, 2022

Create PDF in .NET Core Using itext7

Hello,

I am super excited as this is my first ever blog post ever!!!
It came about as a result of a task where I needed to create a pdf document backend in .NET Core. I stumbled on itext7 and I never looked back.

In this article, I explain how I created a bank account statement using the itext7 library which can be found at nuget.org. At the time of writing this article, version 7.2.1 is the most stable version of the library. The sample project I created and used in this article is publicly available on github. A sample account statement pdf can also be found here.

Breakdown of the project

The first step was to declare the path where the file will be saved, the font type and color, the document and the orientation, and the margins the document will have.



string pdfPathFolder = "C:/PdfFolder";
if (!Directory.Exists(pdfPathFolder))
{
    Directory.CreateDirectory(pdfPathFolder);
}
string fileName = "MyStatement" + DateTime.Now.ToString("ddMMMMyyyyHHmmssfffff") + ".pdf";
                string fullFilePath = System.IO.Path.Combine(pdfPathFolder, fileName);
                PdfFont font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA, PdfEncodings.CP1252, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
DeviceRgb purpleColour = new(128, 0, 128);
DeviceRgb offPurpleColour = new(230, 230, 250);

PdfDocument pdfDocument = new(new PdfWriter(new FileStream(fullFilePath, FileMode.Create, FileAccess.Write)));
Document document = new(pdfDocument, PageSize.A4, false); //find a way to include the margins
document.SetMargins(20, 20, 20, 40);


Enter fullscreen mode Exit fullscreen mode

In doing this, I noted the width and height of the document. This was important in setting the width and positioning of the elements created and added to the file.

I took advantage of the IEventHandler interface provided by itext7 to create the Header and Footer classes for the document.
The header is the company logo, while the footer is the bank address and page numbers.



pdfDocument.AddEventHandler(PdfDocumentEvent.START_PAGE, new PDFHeaderEventHandler());
PDFFooterEventHandler currentEvent = new();
pdfDocument.AddEventHandler(PdfDocumentEvent.END_PAGE, currentEvent);


Enter fullscreen mode Exit fullscreen mode

The header class can be seen below



public class PDFHeaderEventHandler : IEventHandler
{
    public void HandleEvent(Event currentEvent)
    {
        try
        {
              PdfDocumentEvent docEvent = (PdfDocumentEvent)currentEvent;
                string logoPath = "C:/Assets/Logo.png";
                var logo = ImageDataFactory.Create(logoPath);
                PdfPage page = docEvent.GetPage();
                PdfDocument pdf = docEvent.GetDocument();
                Rectangle pageSize = page.GetPageSize();
                PdfCanvas pdfCanvas = new(page.GetLastContentStream(), page.GetResources(), pdf);
                if (pdf.GetPageNumber(page) == 1)
                {
                    //i want the logo just on page 1
                    pdfCanvas.AddImageAt(logo, pageSize.GetWidth() - logo.GetWidth() - 480, pageSize.GetHeight() - logo.GetHeight() - 15, true);
                    _ = new Canvas(pdfCanvas, pageSize);
                }
                else
                {
                    _ = new Canvas(pdfCanvas, pageSize);
                }


            }
            catch (Exception)
            {
                throw;
            }
        }
    }


Enter fullscreen mode Exit fullscreen mode

The Footer class which contains the HandleEvent method and the WritePageNumbers method can be seen below



    public class PDFFooterEventHandler : IEventHandler
    {
        public void HandleEvent(Event currentEvent)
        {
            try
            {
                PdfDocumentEvent docEvent = (PdfDocumentEvent)currentEvent;
                PdfPage page = docEvent.GetPage();
                PdfDocument pdf = docEvent.GetDocument();
                Rectangle pageSize = page.GetPageSize();
                PdfCanvas pdfCanvas = new(page.GetLastContentStream(), page.GetResources(), pdf);
                int pageNumber = pdf.GetPageNumber(page);
                int numberOfPages = pdf.GetNumberOfPages();
                PdfFont font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA, PdfEncodings.CP1252, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
                DeviceRgb offPurpleColour = new(230, 230, 250);
                float[] tableWidth = { 445, 50F };
                Table footerTable = new Table(tableWidth).SetFixedPosition(0F, 15F, pageSize.GetWidth()).SetBorder(Border.NO_BORDER);

                var botom = pageSize.GetBottom() + 15F;
                var getwidth = pageSize.GetWidth();

                footerTable.AddCell(new Cell().Add(new Paragraph("Goodman Bank PLC is authorised by the Prudential Regulation Authority and regulated by the Financial Conduct Authority and the Prudential Regulation Authority(FRN: 54113411) Registered in Brussels and Vienna (Company Number: 23336654) Registered Office: 15 Downing Street, London XY11 6TF"))
                                    .SetFont(font).SetFontSize(7F).SetBackgroundColor(offPurpleColour).SetBorder(Border.NO_BORDER).SetPaddingLeft(25F).SetPaddingRight(10F));

                Canvas canvas = new(pdfCanvas, pageSize);
              canvas.Add(footerTable).SetBorder(Border.NO_BORDER);

            }
            catch (Exception)
            {
                //_logger.LogError(ex, "An error occurred while in HandleEvent method in PDFFooterEventHandler class : {RequestId}");
                throw;
            }
        }

        public void WritePageNumbers(PdfDocument pdf, Document document)
        {
            PdfFont font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA, PdfEncodings.CP1252, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
            DeviceRgb offPurpleColour = new(230, 230, 250);
            int numberOfPages = pdf.GetNumberOfPages();

            for (int i = 1; i <= numberOfPages; i++)
            {
                // Write aligned text to the specified by parameters point
                document.ShowTextAligned(new Paragraph("Page " + i + " of " + numberOfPages).SetFont(font).SetFontSize(7F).SetBackgroundColor(offPurpleColour).SetBorder(Border.NO_BORDER).SetWidth(50F).SetPaddings(8F, 28F, 9F, 7F).SetTextAlignment(TextAlignment.RIGHT),
                        555, 15.5f, i, TextAlignment.CENTER, VerticalAlignment.BOTTOM, 0);
            }
        }
    }


Enter fullscreen mode Exit fullscreen mode

I added the address table and summary account table to the document next. Elements such as table, cell, paragraph, and list can be styled independently to achieve whatever goal you have in mind. The elements created was added to the document using the document.Add() method.
This is what the header, address table, summary of account table looks on the document.

The address and summary of account



Table addressTable = new Table(new float[] { 345F }).SetFontSize(10F).SetFontColor(ColorConstants.BLACK).SetFont(font).SetBorder(Border.NO_BORDER)
                    .SetMarginTop(150F).SetMarginLeft(10f);
                addressTable.AddCell(new Cell().Add(new Paragraph("MR JOHN DOE"))
                    .Add(new Paragraph("1004, BORIS ROAD"))
                    .Add(new Paragraph("LONDON"))
                    .Add(new Paragraph("XY01 3WP")).SetBorder(Border.NO_BORDER));

                Table summaryTable = new Table(new float[] { 130F }).SetFontSize(8F).SetFontColor(ColorConstants.BLACK).SetFont(font).SetBorder(Border.NO_BORDER);
                summaryTable.AddCell(new Cell().Add(new Paragraph("Goodman Bank Account")).SetBorder(Border.NO_BORDER).SetFontColor(purpleColour).SetFontSize(14F));
                summaryTable.AddCell(new Cell().Add(new Paragraph("14 Dec 2021 - 13 Jan 2022")).SetBorder(Border.NO_BORDER));
                summaryTable.AddCell(new Cell().Add(new Paragraph("Mr John Doe")).SetBorder(Border.NO_BORDER));

                List summaryBullets = new();
                summaryBullets.Add(new ListItem("Sort Code 00-01-02"));
                summaryBullets.Add(new ListItem("Account No. 00011122"));
                summaryBullets.Add(new ListItem("SWIFTBIC CNGDVV11"));
                summaryBullets.Add(new ListItem("IBAN CFDSA 111 2344 2233"));
                summaryTable.AddCell(new Cell().Add(summaryBullets).SetBorder(Border.NO_BORDER).SetPaddingBottom(10f));

                summaryTable.AddCell(new Cell().Add(new Paragraph("At a glance")).SetFontSize(12F).SetBorder(Border.NO_BORDER).SetBackgroundColor(purpleColour).SetFontColor(ColorConstants.WHITE)
                    .SetBorderTopLeftRadius(new BorderRadius(10F)).SetBorderTopRightRadius(new BorderRadius(10F)));

                summaryTable.AddCell(new Cell().Add(new Table(new float[] { 65f, 65f }).AddCell(new Cell().Add(new Paragraph("Start balance")).SetTextAlignment(TextAlignment.LEFT).SetBorder(Border.NO_BORDER)).
                    AddCell(new Cell().Add(new Paragraph("£2000.00")).SetTextAlignment(TextAlignment.RIGHT).SetBorder(Border.NO_BORDER))).SetBorder(Border.NO_BORDER)
                    .SetBorderBottom(new SolidBorder(purpleColour, 1F)));
                summaryTable.AddCell(new Cell().Add(new Table(new float[] { 65f, 65f }).AddCell(new Cell().Add(new Paragraph("Money In")).SetTextAlignment(TextAlignment.LEFT).SetBorder(Border.NO_BORDER)).
                    AddCell(new Cell().Add(new Paragraph("£2720.00")).SetTextAlignment(TextAlignment.RIGHT).SetBorder(Border.NO_BORDER))).SetBorder(Border.NO_BORDER)
                    .SetBorderBottom(new SolidBorder(purpleColour, 1F)));
                summaryTable.AddCell(new Cell().Add(new Table(new float[] { 65f, 65f }).AddCell(new Cell().Add(new Paragraph("Money out")).SetTextAlignment(TextAlignment.LEFT).SetBorder(Border.NO_BORDER)).
                    AddCell(new Cell().Add(new Paragraph("£720.00")).SetTextAlignment(TextAlignment.RIGHT).SetBorder(Border.NO_BORDER))).SetBorder(Border.NO_BORDER)
                    .SetBorderBottom(new SolidBorder(purpleColour, 1F)));
                summaryTable.AddCell(new Cell().Add(new Table(new float[] { 65f, 65f }).AddCell(new Cell().Add(new Paragraph("End Balance")).SetTextAlignment(TextAlignment.LEFT).SetBorder(Border.NO_BORDER)).
                    AddCell(new Cell().Add(new Paragraph("£4000.00")).SetTextAlignment(TextAlignment.RIGHT).SetBorder(Border.NO_BORDER))).SetBorder(Border.NO_BORDER)
                    .SetBorderBottom(new SolidBorder(purpleColour, 1F)));

//============================== This merges Address table and Summary Table ========================================
                Table addressSummaryMergeTable = new Table(new float[] { 385F, 30F, 130F }).SetFont(font);
                addressSummaryMergeTable.AddCell(new Cell().Add(addressTable).SetBorder(Border.NO_BORDER).SetHorizontalAlignment(HorizontalAlignment.LEFT));
                addressSummaryMergeTable.AddCell(new Cell().Add(new Paragraph("")).SetBorder(Border.NO_BORDER));
                addressSummaryMergeTable.AddCell(new Cell().Add(summaryTable).SetBorder(Border.NO_BORDER).SetHorizontalAlignment(HorizontalAlignment.RIGHT));

                document.Add(addressSummaryMergeTable);


Enter fullscreen mode Exit fullscreen mode

I added the the Statement title and Notice Board tables next. The result can be seen below.

Statement title and Notice board



                Table headerTable = new Table(new float[] { 300F }).SetFontColor(purpleColour).SetFont(font).SetBorder(Border.NO_BORDER).SetHorizontalAlignment(HorizontalAlignment.LEFT);
                headerTable.AddCell(new Cell().Add(new Paragraph("Your Goodman Bank Account Statement")).SetBorder(Border.NO_BORDER).SetFontSize(15F));
                headerTable.AddCell(new Cell().Add(new Paragraph("Current account statement")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetPaddingTop(7F));

                Table noticeBoard = new Table(new float[] { 130F }).SetFont(font).SetBorder(Border.NO_BORDER).SetFontSize(8F).SetHorizontalAlignment(HorizontalAlignment.RIGHT);
                noticeBoard.AddCell(new Cell().Add(new Paragraph("NoticeBoard")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBackgroundColor(purpleColour).SetFontColor(ColorConstants.WHITE)
                    .SetBorderTopLeftRadius(new BorderRadius(10F)).SetBorderTopRightRadius(new BorderRadius(10F)));
                noticeBoard.AddCell(new Cell().Add(new Paragraph("Your deposit is eligible for protection by the Financial Services Compensation Scheme")).SetBorder(Border.NO_BORDER));

                //=========================================================== Merges Header and NoticeBoard =========================================================
                Table summNoticeBoardMerge = new Table(new float[] { 385F, 30F, 130F }).SetFont(font).SetMarginTop(10F);
                summNoticeBoardMerge.AddCell(new Cell().Add(headerTable).SetBorder(Border.NO_BORDER).SetHorizontalAlignment(HorizontalAlignment.LEFT));
                summNoticeBoardMerge.AddCell(new Cell().Add(new Paragraph("")).SetBorder(Border.NO_BORDER));
                summNoticeBoardMerge.AddCell(new Cell().Add(noticeBoard).SetBorder(Border.NO_BORDER).SetHorizontalAlignment(HorizontalAlignment.RIGHT));

                document.Add(summNoticeBoardMerge);


Enter fullscreen mode Exit fullscreen mode

I added the list of transactions table and miscelleanous table next with the result below.

Transaction table



Table transactionsTable = new Table(new float[] { 80, 110, 70, 70, 70 }).SetFont(font).SetFontSize(10F).SetFontColor(ColorConstants.BLACK).SetBorder(Border.NO_BORDER).SetMarginTop(10);
transactionsTable.AddCell(new Cell(1, 2).Add(new Paragraph("Your transactions").SetPadding(3)).SetBorder(Border.NO_BORDER).SetBackgroundColor(purpleColour)                        .SetFontColor(ColorConstants.WHITE).SetPaddingBottom(3F).SetFontSize(11F).SetBorderTopRightRadius(new BorderRadius(10F)).SetBorderTopLeftRadius(new BorderRadius(10F)).SetBorderBottom(new SolidBorder(purpleColour, 1F)));
transactionsTable.AddCell(new Cell(1, 3).Add(new Paragraph("")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBorderBottom(new SolidBorder(purpleColour, 1F)));
transactionsTable.AddCell(new Cell().Add(new Paragraph("Date")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph("Description")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph("Money out")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetTextAlignment(TextAlignment.RIGHT));
transactionsTable.AddCell(new Cell().Add(new Paragraph("Money In")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetTextAlignment(TextAlignment.RIGHT));
transactionsTable.AddCell(new Cell().Add(new Paragraph("Balance")).SetBorder(Border.NO_BORDER).SetFontSize(11F).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetTextAlignment(TextAlignment.RIGHT));
transactionsTable.AddCell(new Cell().Add(new Paragraph("14 Dec")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph("Start balance")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph(" ")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph(" ")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph("2,000.00")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour).SetTextAlignment(TextAlignment.RIGHT));

int backgroundCounter = 0;
for (int i = 0; i < _transactionDetails.Count; i++)
{
                    //=============================================== fill in data under the headers =====================================================
                    transactionsTable.AddCell(new Cell().Add(new Paragraph(_transactionDetails[i].TransactionDate)).SetBackgroundColor((backgroundCounter % 2 == 0) ? ColorConstants.WHITE : offPurpleColour)
                        .SetBorder(new SolidBorder(ColorConstants.WHITE, 1F)).SetFontColor(ColorConstants.BLACK));
                    transactionsTable.AddCell(new Cell().Add(new Paragraph(_transactionDetails[i].Remarks)).SetBackgroundColor((backgroundCounter % 2 == 0) ? ColorConstants.WHITE : offPurpleColour)
                        .SetBorder(new SolidBorder(ColorConstants.WHITE, 1F)).SetFontColor(ColorConstants.BLACK).SetKeepTogether(true));
                    transactionsTable.AddCell(new Cell().Add(new Paragraph(_transactionDetails[i].MoneyOut)).SetBackgroundColor((backgroundCounter % 2 == 0) ? ColorConstants.WHITE : offPurpleColour)
                        .SetBorder(new SolidBorder(ColorConstants.WHITE, 1F)).SetFontColor(ColorConstants.BLACK).SetTextAlignment(TextAlignment.RIGHT));
                    transactionsTable.AddCell(new Cell().Add(new Paragraph(_transactionDetails[i].MoneyIn)).SetBackgroundColor((backgroundCounter % 2 == 0) ? ColorConstants.WHITE : offPurpleColour)
                        .SetBorder(new SolidBorder(ColorConstants.WHITE, 1F)).SetFontColor(ColorConstants.BLACK).SetTextAlignment(TextAlignment.RIGHT));
                    transactionsTable.AddCell(new Cell().Add(new Paragraph(_transactionDetails[i].Balance)).SetBackgroundColor((backgroundCounter % 2 == 0) ? ColorConstants.WHITE : offPurpleColour)
                        .SetBorder(new SolidBorder(ColorConstants.WHITE, 1F)).SetFontColor(ColorConstants.BLACK).SetTextAlignment(TextAlignment.RIGHT));
backgroundCounter++;
}
transactionsTable.AddCell(new Cell().Add(new Paragraph("13 Jan")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph("End balance")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph(" ")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph(" ")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour));
transactionsTable.AddCell(new Cell().Add(new Paragraph("4,000.00")).SetBorder(Border.NO_BORDER).SetBorderBottom(new SolidBorder(purpleColour, 0.5F)).SetBorderRight(new SolidBorder(ColorConstants.WHITE, 0.5F)).SetFontColor(purpleColour).SetBackgroundColor(offPurpleColour).SetTextAlignment(TextAlignment.RIGHT));

document.Add(transactionsTable);

Table miscTable = new Table(new float[] { 95F, 305F }).SetFont(font).SetFontSize(10F).SetFontColor(ColorConstants.BLACK).SetBorder(Border.NO_BORDER).SetMarginTop(20).SetKeepTogether(true);
miscTable.AddCell(new Cell().Add(new Paragraph("Anything Wrong?").SetFontColor(purpleColour)).SetBorder(Border.NO_BORDER));
                miscTable.AddCell(new Cell().Add(new Paragraph("If you've spotted any incorrect or unusual transactions, kindly contact us immediately")).SetBorder(Border.NO_BORDER));

document.Add(miscTable);


Enter fullscreen mode Exit fullscreen mode

Finally, I wrote the page numbers on the document and called the document.Close() method to close the document and make it available in the folder path declared.

Conclusion

I had a lot of fun working with this library. You should give it a shot.

To find out more about itext7, you can check out the documentation here. Sample documentation codes from itext can also be found here.

If this article was helpful, please leave a clap and/or share.

💖 💪 🙅 🚩
davidelegbede
David Elegbede

Posted on February 3, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Create PDF in .NET Core Using itext7
csharp Create PDF in .NET Core Using itext7

February 3, 2022