Search This Blog

iTextSharp continued: lists, lines, pdf in memory and page numbers


posted on Wednesday, April 11, 2012

As I promised, today I'll go a bit deeper in some of the great functionalities of iTextSharp which I introduced in my last post:  http://thiscouldbeuseful.blogspot.com/2012/04/export-to-pdf-using-itextsharp-basic.html.

The code of today shows how to add lists, horizontal lines, how to create PDF-document in memory and how to add page numbers! Sounds good? Okay, let's get to it!

protected void btnCreatePdf_Click(object sender, EventArgs e)
        {
            try
            {
                // set the file name
                string file = "C:/pdf/MyPdf.pdf";

                // create a pdf document
                Document document = new Document();

                // set the page size, set the orientation
                document.SetPageSize(PageSize.A4.Rotate());

                // create a writer instance - the document will be kept in memory
                MemoryStream pdfStream = new MemoryStream();
                PdfWriter pdfWriter = PdfWriter.GetInstance(document, pdfStream);

                // open the document
                document.Open();

                // define fonts
                Font titleFont = FontFactory.GetFont("Helvetica", 14, Font.BOLD, new iTextSharp.text.BaseColor(0, 179, 212));
                Font textFont = FontFactory.GetFont("Helvetica", 9, Font.NORMAL, BaseColor.BLACK);

                // create a paragraph and add it to the document
                Paragraph title = new Paragraph("This PDF-document was created using iTextSharp!");
                title.Font = titleFont;
                title.IndentationLeft = 50f;
                document.Add(title);

                // add line below title
                LineSeparator line = new LineSeparator(1f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1);
                document.Add(new Chunk(line));

                // add list
                string[] listItems = new string[] { "item one", "item two", "item three" };
                List list = new List(List.UNORDERED, 10f);
                list.SetListSymbol("\u2022");
                list.IndentationLeft = 15f;

                foreach (string listItem in listItems)
                {
                    list.Add(new iTextSharp.text.ListItem(listItem, textFont));
                }

                document.Add(list);

                // close the document
                pdfWriter.CloseStream = true;
                document.Close();

                // add page numbers
                Document copyDoc = new Document();
                PdfCopy copyPdf = new PdfCopy(copyDoc, new FileStream(file, FileMode.Create));
                copyPdf.SetPageSize(PageSize.A4.Rotate());
                copyDoc.Open();

                // read the initial pdf document
                PdfReader reader = new PdfReader(pdfStream.ToArray());
                int totalPages = reader.NumberOfPages;

                PdfImportedPage copiedPage = null;
                iTextSharp.text.pdf.PdfCopy.PageStamp stamper = null;

                for (int i = 0; i < totalPages; i++)
                {
                    // get the page and create a stamper for that page
                    copiedPage = copyPdf.GetImportedPage(reader, (i + 1));
                    stamper = copyPdf.CreatePageStamp(copiedPage);

                    // add a page number to the page
                    ColumnText.ShowTextAligned(stamper.GetUnderContent(), Element.ALIGN_CENTER, new Phrase((i + 1) + "/" + totalPages, textFont), 820f, 15, 0);
                    stamper.AlterContents();

                    // add the altered page to the new document
                    copyPdf.AddPage(copiedPage);
                }

                copyDoc.Close();
                reader.Close();

                // flush and clear the document from memory
                pdfStream.Flush();
                pdfStream.Close();

                // open the pdf document
                Process.Start(file);
            }
            catch (Exception)
            {
                lblError.Text = "An error ocurred, the PDF-document could not be created.";
            }
        }

To add a horizontal line in your PDF-document, you could use a graph or a table but the easiest way is to use the LineSeparator-object. You can set the width of the line, the size in percentage, the color, the alignment and the offset.

      // add line below title
      LineSeparator line = new LineSeparator(1f, 100f, BaseColor.BLACK, Element.ALIGN_CENTER, -1);
      document.Add(new Chunk(line));

To add a list you can use the List and ListItem objects. You can create an ordered or unordered list and use different symbols for the list or even roman numbering. The font for the list should be set at the list item level.

       
       // add list
       string[] listItems = new string[] { "item one", "item two", "item three" };
       List list = new List(List.UNORDERED, 10f);
       list.SetListSymbol("\u2022");
       list.IndentationLeft = 15f;

       foreach (string listItem in listItems)
       {
             list.Add(new iTextSharp.text.ListItem(listItem, textFont));
       }

       document.Add(list);

To create you PDF-document in memory instead of saving it to a file, you need to use MemoryStream instead of FileStream. In this example you can find code for how to use both options. I combined them to create two documents but create only one file. If you create the document in memory, make sure to flush and close the stream afterwards.

      MemoryStream pdfStream = new MemoryStream();
      PdfWriter pdfWriter = PdfWriter.GetInstance(document, pdfStream);

      string file = "C:/pdf/MyPdf.pdf";
      PdfCopy copyPdf = new PdfCopy(copyDoc, new FileStream(file, FileMode.Create));

And finally, the hardest one, page numbering. iTextSharp makes it possible to add repeated content to your document, such as footers, headers or page numbers. But this can only be done after the current page number and the total number of pages is known. So one of the possibilities to implement page numbering is to create a new document in which you copy the pages of the original document. As you copy the pages the page numbers are calculated and added.

You'll use the PageStamp object to add a ColumnText to the existing page before you copy it. This ColumnText-object will allow you to add text at an absolute position. You can set the x and y postition and also a rotation. The GetUnderContent defines that you want to write under the original content. If you want to write over the original content, you should use GetOverContent. Finally, use AlterContents() to make the change and AddPage to add the page to the new document.

                // add page numbers
                Document copyDoc = new Document();
                PdfCopy copyPdf = new PdfCopy(copyDoc, new FileStream(file, FileMode.Create));
                copyPdf.SetPageSize(PageSize.A4.Rotate());
                copyDoc.Open();

                // read the initial pdf document
                PdfReader reader = new PdfReader(pdfStream.ToArray());
                int totalPages = reader.NumberOfPages;

                PdfImportedPage copiedPage = null;
                iTextSharp.text.pdf.PdfCopy.PageStamp stamper = null;

                for (int i = 0; i < totalPages; i++)
                {
                    // get the page and create a stamper for that page
                    copiedPage = copyPdf.GetImportedPage(reader, (i + 1));
                    stamper = copyPdf.CreatePageStamp(copiedPage);

                    // add a page number to the page
                    ColumnText.ShowTextAligned(stamper.GetUnderContent(), Element.ALIGN_CENTER, new Phrase((i + 1) + "/" + totalPages, textFont), 820f, 15, 0);
                    ColumnText.ShowTextAligned(stamper.GetUnderContent(), Element.ALIGN_CENTER, new Phrase("test"), 1f, 1f, 1f);
                    stamper.AlterContents();

                    // add the altered page to the new document
                    copyPdf.AddPage(copiedPage);
                }

                copyDoc.Close();
                reader.Close();

So that was it! Here you can find my code and the resulting PDF-document

To be continued!


Update: since sharing is caring, I'd like to add (read: admit) that while I was creating this example, I stumbled upon an error.  It took me a while to figure out what is was since Google wasn't being a friend (no worries, we're good now) and I couldn't find anything online about the issue.

At copyDoc.Close() I got a NullReference error. Turns out I made a typo and so I was opening the PdfWriter instead of the PdfDocument, the reason why you should give two objects such a similar name...   So my document remained closed as I added content to it, when I tried to close it, there was nothing to close so it threw an error. Stupid but honest mistake since the PdfWriter has an Open()-method as well. So, hopefully this can be useful to someone making the same mistake :)


Could be useful, right?



2 comments:

  1. Thank you so much! this is an absolute awesome example! i wanted to use memory streams and first create the whole document, then read it into a temp stream and then add the page numbers and close it...this example helped me a lot in achieving exactly that:) thanx

    ReplyDelete