1

I have a template split into 3 files(header, body and footer), all of them in html. I need to export a pdf file using

itextsharp lib.

I use the folowing code to do that.

Function to export the file

public string GeneratePDF(Dictionary<string, object> fieldValues, string pdfPath, string templatePath)
        {
            //Inicializes a New Document
            Document document = new Document(PageSize.A4,0,0,0,0);

            try
            {
                PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(pdfPath, FileMode.Create));                 
                PdfPageEvents events = new PdfPageEvents();
                //Initializes page events
                writer.PageEvent = events;
                //Open Document
                document.Open();

                //Gerar efetivamente o html
                TemplateHelper objTemplate = new TemplateHelper();
                //This function is not important, only replaces the content by a dictionary
                string htmlContent = objTemplate.GenerateHTML(fieldValues, templatePath);

                //Gerar o PD
                StringReader reader = new StringReader(htmlContent);
                XMLWorkerHelper.GetInstance().ParseXHtml(writer, document, reader);
            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
                document.Close(); 
            }

            return pdfPath;
        }

and the page events

public class PdfPageEvents : PdfPageEventHelper
{
    public override void OnStartPage(PdfWriter writer, Document document)
    {
            StreamReader template = new StreamReader(@"d:\Header.html");
            string htmlContent = template.ReadToEnd();
            StringReader reader = new StringReader(htmlContent);

            ElementList e = XMLWorkerHelper.ParseToElementList(htmlContent, "");
            PdfDiv div = (PdfDiv)e.First();
            document.Add(div.Content.First());
            template.Close();

        template = new StreamReader(@"d:\Footer.html");
        htmlContent = template.ReadToEnd();
        reader = new StringReader(htmlContent);

    }
    //começa com o cabeçalho
    public override void OnEndPage(PdfWriter writer, Document document)
    {
        StreamReader template = new StreamReader(@"d:\Footer.html");
        string htmlContent = template.ReadToEnd();
        StringReader reader = new StringReader(htmlContent);
        ElementList elementListFooter = XMLWorkerHelper.ParseToElementList(htmlContent, "");
        PdfDiv div = (PdfDiv)elementListFooter.First();
        PdfPTable t = (PdfPTable)div.Content.First();

        document.Add(t);
        template.Close();
    }
}

When I export to pdf, the header works fine, but the footer don't work. I tried to put the footer content on the footer of

the document, but unsuccessfully. If the content of the body is to large to fit in one page, the footer content is set

below the header and below the content on the last page. The follow image illustrates this problem.

PDF with one page. The footer is below of the content, but not positioned on the bottom of the page

Footer on the top of the first page and before body content

enter image description here

poveda
  • 33
  • 1
  • 5
  • 1
    Please **DO NOT** add any content in the `onStartPage()` method. That can lead to all kinds of undesired side-effects. This comment doesn't solve your problem, but the fact that you add the header in the `onStartPage()` method proves that you ignored the documentation. – Bruno Lowagie Nov 20 '15 at 14:29
  • 1
    Please **DO NOT** add content in `onEndPage()` using `document.Add()`. The `document` object passed to the event is a special `PdfDocument` object that should be used *for read-only* purposes only. This is what causes your problem. It also proves that you ignored the documentation. – Bruno Lowagie Nov 20 '15 at 14:31
  • Unrelated side-note, both `Document` and `PdfWriter` implement `IDisposable` so you can use the `using` paradigm on them instead of `try/catch` and you'll be guaranteed that `Close()` is called for you automatically during dispose. – Chris Haas Nov 20 '15 at 14:31
  • Furthermore: you are parsing the HTML of the header and the footer over and over again wasting plenty of CPU. Why don't you create the header and footer table once and add the same table objects to every page as a Form XObject. This will result in PDFs with a much lower file size. – Bruno Lowagie Nov 20 '15 at 14:33
  • Possible duplicate of [add header to pdf from html using itext](http://stackoverflow.com/questions/33394158/add-header-to-pdf-from-html-using-itext) – Bruno Lowagie Nov 20 '15 at 14:50

1 Answers1

1

To explain what Bruno is saying in the comments in a little more detail, if you Add() content in a page event, you could conceivably cause the document to generate a new page which would cause your page event to fire again. Even more fun, if your page event added too much content you could actually run into an infinite loop of add, overflow, new page, repeat.

If you want a true header and footer, set your Document margins to account for the heights of both and normal Document.Add() code will respect it. If you don't know how tall your header and/or footer is going to be each time you can try using this code to calculate the height.

Then you can use page events or two-pass to just add your header and footer and known fixed locations using DirectContent or ColumnText. Your content seems rather simple so you could probably convert it to just normal iTextSharp commands but if you want to stay in HTML for your header and footer see this post for parsing HTML to an element list and adding that to a ColumnText.

Community
  • 1
  • 1
Chris Haas
  • 53,986
  • 12
  • 141
  • 274