How to add HTML headers and footers to a page?

How to add header to pdf from an html source using itext?

Currently, we have extended PdfPageEventHelper and overriden these methods. Works fine but it throws a RuntimeWorkerException when I get to 2+ pages.

 @Override
    void onStartPage(PdfWriter writer, Document document) {
        InputStream is = new ByteArrayInputStream(header?.getBytes());
        XMLWorkerHelper.getInstance().parseXHtml(writer, document, is);
    }

    @Override
    void onEndPage(PdfWriter writer, Document document) {
        InputStream is = new ByteArrayInputStream(footer?.getBytes());
        XMLWorkerHelper.getInstance().parseXHtml(writer, document, is);

    }

Bruno's anwser is correct but it didn't worked for me completely as XMLWorkerHelper.parsetoElementsList was not able to parse some system fonts on the other hand XMLWorkerHelper.getInstance().parseXHtml(writer, document, is); } was able to parse system fonts correctly so i have to go down the route of elements handler which worked a treat here's the code in C#

    /// <summary>
    /// returns pdf in bytes.
    /// </summary>
    /// <param name="contentsHtml">contents.</param>
    /// <param name="headerHtml">header contents.</param>
    /// <param name="footerHtml">footer contents.</param>
    /// <returns></returns>
    public Byte[] GetPDF(string contentsHtml, string headerHtml, string footerHtml)
    {
        // Create a byte array that will eventually hold our final PDF
        Byte[] bytes;

        // Boilerplate iTextSharp setup here

        // Create a stream that we can write to, in this case a MemoryStream
        using (var ms = new MemoryStream())
        {
            // Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
            using (var document = new Document(PageSize.A4, 40, 40, 120, 120))
            {
                // Create a writer that's bound to our PDF abstraction and our stream
                using (var writer = PdfWriter.GetInstance(document, ms))
                {
                    // Open the document for writing
                    document.Open();

                    var headerElements = new HtmlElementHandler();
                    var footerElements = new HtmlElementHandler();

                    XMLWorkerHelper.GetInstance().ParseXHtml(headerElements, new StringReader(headerHtml));

                    XMLWorkerHelper.GetInstance().ParseXHtml(footerElements, new StringReader(footerHtml));

                    writer.PageEvent = new HeaderFooter(headerElements.GetElements(), footerElements.GetElements());

                    // Read your html by database or file here and store it into finalHtml e.g. a string
                    // XMLWorker also reads from a TextReader and not directly from a string
                    using (var srHtml = new StringReader(contentsHtml))
                    {
                        // Parse the HTML
                        iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, document, srHtml);
                    }

                    document.Close();
                }
            }

            // After all of the PDF "stuff" above is done and closed but **before** we
            // close the MemoryStream, grab all of the active bytes from the stream
            bytes = ms.ToArray();
        }
        return bytes;
    }
}

page events and elements handler code is here

public partial class HeaderFooter : PdfPageEventHelper
{
    private ElementList HeaderElements { get; set; }
    private ElementList FooterElements { get; set; }

    public HeaderFooter(ElementList headerElements, ElementList footerElements)
    {
        HeaderElements = headerElements;
        FooterElements = footerElements;
    }

    public override void OnEndPage(PdfWriter writer, Document document)
    {
        base.OnEndPage(writer, document);
        try
        {
            ColumnText headerText = new ColumnText(writer.DirectContent);
            foreach (IElement e in HeaderElements)
            {
                headerText.AddElement(e);
            }
            headerText.SetSimpleColumn(document.Left, document.Top, document.Right, document.GetTop(-100), 10, Element.ALIGN_MIDDLE);
            headerText.Go();

            ColumnText footerText = new ColumnText(writer.DirectContent);
            foreach (IElement e in FooterElements)
            {
                footerText.AddElement(e);
            }
            footerText.SetSimpleColumn(document.Left, document.GetBottom(-100), document.Right, document.GetBottom(-40), 10, Element.ALIGN_MIDDLE);
            footerText.Go();
        }
        catch (DocumentException de)
        {
            throw new Exception(de.Message);
        }
    }
}

public class HtmlElementHandler : IElementHandler
{
    public ElementList Elements { get; set; }

    public HtmlElementHandler()
    {
        Elements = new ElementList();
    }

    public ElementList GetElements()
    {
        return Elements;
    }

    public void Add(IWritable w)
    {
        if (w is WritableElement)
        {
            foreach (IElement e in ((WritableElement)w).Elements())
            {
                Elements.Add(e);
            }
        }
    }
}

It is forbidden to add content in the onStartPage() event in general. It is forbidden to add content to the document object in the onEndPage(). You should add your header and your footer in the onEndPage() method using PdfWriter, NOT document. Also: you are wasting plenty of CPU by parsing the HTML over and over again.

Please take a look at the HtmlHeaderFooter example.

It has two snippets of HTML, one for the header, one for the footer.

public static final String HEADER =
    "<table width=\"100%\" border=\"0\"><tr><td>Header</td><td align=\"right\">Some title</td></tr></table>";
public static final String FOOTER =
    "<table width=\"100%\" border=\"0\"><tr><td>Footer</td><td align=\"right\">Some title</td></tr></table>";

Note that there are better ways to describe the header and footer than by using HTML, but maybe it's one of your requirements, so I won't ask you why you don't use any of the methods that is explained in the official documentation. By the way: all the information you need to solve your problem can also be found in that free ebook so you may want to download it...

We will read these HTML snippets only once in our page event and then we'll render the elements over and over again on every page:

public class HeaderFooter extends PdfPageEventHelper {
    protected ElementList header;
    protected ElementList footer;
    public HeaderFooter() throws IOException {
        header = XMLWorkerHelper.parseToElementList(HEADER, null);
        footer = XMLWorkerHelper.parseToElementList(FOOTER, null);
    }
    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        try {
            ColumnText ct = new ColumnText(writer.getDirectContent());
            ct.setSimpleColumn(new Rectangle(36, 832, 559, 810));
            for (Element e : header) {
                ct.addElement(e);
            }
            ct.go();
            ct.setSimpleColumn(new Rectangle(36, 10, 559, 32));
            for (Element e : footer) {
                ct.addElement(e);
            }
            ct.go();
        } catch (DocumentException de) {
            throw new ExceptionConverter(de);
        }
    }
}

Do you see the mechanism we use to add the Element objects obtained from XML Worker? We create a ColumnText object that will write to the direct content of the writer (using the document is forbidden). We define a Rectangle and we using go() to render the elements.

The results is shown in html_header_footer.pdf.