kuujinbo_dot_info

Adding PDF Page Headers with iTextSharp

Updated 2012-01-16.

A common question on the iText mailing list; probably because it's not a simple, one-step process.

The recommended method is to create a class that inherits from PdfPageEventHelper, which you should be able to guess, allows you to handle numerous document events. Since the goal is to add a header to every page of the PDF document, the following ASP.NET web forms example is implemented using the following steps:

  1. Create a class in the code-behind file that overrides PdfPageEventHelper.
  2. Override the OnEndPage() method to write the page header content. You should not use OnStartPage() to write header content - you should only override the method to initialize class members.
  3. Instantiate a PdfPTable within the OnEndPage() method so everything is nicely formatted. We throw in an Image object, since that's also a popular question.
  4. Attach an instance object of our newly created class to PdfWriter's PageEvent property.

On to the code. First the class that inherits from PdfPageEventHelper, nested in your code-behind class:

  private class MyPageEventHandler : PdfPageEventHelper {
/*
 * We use a __single__ Image instance that's assigned __once__;
 * the image bytes added **ONCE** to the PDF file. If you create 
 * separate Image instances in OnEndPage()/OnEndPage(), for example,
 * you'll end up with a much bigger file size.
 */      
    public Image ImageHeader {get; set;}

    public override void OnEndPage(PdfWriter writer, Document document) {
// cell height 
      float cellHeight = document.TopMargin;
// PDF document size      
      Rectangle page = document.PageSize;

// create two column table
      PdfPTable head = new PdfPTable(2);
      head.TotalWidth = page.Width;

// add image; PdfPCell() overload sizes image to fit cell
      PdfPCell c = new PdfPCell(ImageHeader, true);
      c.HorizontalAlignment = Element.ALIGN_RIGHT;
      c.FixedHeight = cellHeight;
      c.Border = PdfPCell.NO_BORDER;
      head.AddCell(c);

// add the header text
      c = new PdfPCell( new Phrase(
        DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + " GMT",
        new Font(Font.FontFamily.COURIER, 8)
      ));
      c.Border = PdfPCell.NO_BORDER;
      c.VerticalAlignment = Element.ALIGN_BOTTOM;
      c.FixedHeight = cellHeight;
      head.AddCell(c);
      
// since the table header is implemented using a PdfPTable, we call
// WriteSelectedRows(), which requires absolute positions!
      head.WriteSelectedRows(
        0, -1,  // first/last row; -1 flags all write all rows
        0,      // left offset
        // ** bottom** yPos of the table
        page.Height - cellHeight + head.TotalHeight,
        writer.DirectContent
      );
    }
  }

In the button's event handler you stream the PDF to the client using this method:

  private void _streamPdf() {
    using (Document document = new Document()) {
      PdfWriter writer = PdfWriter.GetInstance(
        document, Response.OutputStream
      );
      
// the image we're using for the page header      
      Image imageHeader = Image.GetInstance(Request.MapPath(
        "~/kyouyuu/image/kuujinbo2.gif"
      ));
      
// instantiate the custom PdfPageEventHelper
      MyPageEventHandler e = new MyPageEventHandler() { 
        ImageHeader = imageHeader
      };
// and add it to the PdfWriter
      writer.PageEvent = e;
      document.Open();
      
// finally we add some pages with text to show the headers are working
      string text = filler.Text;
      for (int k = 0; k < 8; ++k) {
        text += " " + text;
        Paragraph p = new Paragraph(text);
        p.SpacingBefore = 8f;
        document.Add(p);
      }
    }

The inline comments should be self-explanatory.

Enter a sentence to fill the PDF document:

Yes, just like the Generating PDFs with iTextSharp page, the PDF is being dynamically created on each POST.