I'm trying to make a PDF with a footer on it using iTextSharper. This is working fine, for the most part. I want to add a footer to each page.
My code does add the footer correctly, however, the text is overlapping with the content of the PDF as you can see in the image:
The controller action:
public ActionResult GeneratePDF(int id)
{
Order order = db.Orders.Where(x => x.ID == id).SingleOrDefault();
return new HFPdfResult(order, "OrderPDF");
}
The HFPdfResult (ViewResult
) class:
using System.Web.Mvc;
using RentproDC.Models.RazorPDF;
namespace RentproDC.Models.RazorPDF
{
public class HFPdfResult : ViewResult
{
//Constructors
public HFPdfResult(object model, string name)
{
ViewData = new ViewDataDictionary(model);
ViewName = name;
}
public HFPdfResult() : this(new ViewDataDictionary(), "Pdf")
{
}
public HFPdfResult(object model) : this(model, "Pdf")
{
}
//Override FindView to load PdfView
protected override ViewEngineResult FindView(ControllerContext context)
{
var result = base.FindView(context);
if (result.View == null)
return result;
var pdfView = new HFPdfView(result);
return new ViewEngineResult(pdfView, pdfView);
}
}
}
The HFPdfView class:
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Web.Mvc;
using System.Xml;
using iTextSharp.text;
using iTextSharp.text.html;
using iTextSharp.text.pdf;
using iTextSharp.text.xml;
using iTextSharp.text.html.simpleparser;
using System.Collections.Generic;
namespace RentproDC.Models.RazorPDF
{
public class HFPdfView : IView, IViewEngine
{
private readonly ViewEngineResult _result;
public HFPdfView(ViewEngineResult result)
{
_result = result;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
// generate view into string
var sb = new System.Text.StringBuilder();
TextWriter tw = new System.IO.StringWriter(sb);
_result.View.Render(viewContext, tw);
var resultCache = sb.ToString();
// detect itext (or html) format of response
XmlParser parser;
using (var reader = GetXmlReader(resultCache))
{
while (reader.Read() && reader.NodeType != XmlNodeType.Element)
{
// no-op
}
if (reader.NodeType == XmlNodeType.Element && reader.Name == "itext")
parser = new XmlParser();
else
parser = new HtmlParser();
}
// Create a document processing context
var document = new Document(PageSize.A4, 36, 36, 36, 120);
document.Header = new HeaderFooter(new Phrase("Test header"), false);
document.Open();
// associate output with response stream
var pdfWriter = PdfWriter.GetInstance(document, viewContext.HttpContext.Response.OutputStream);
pdfWriter.PageEvent = new PdfFileEvents();
pdfWriter.CloseStream = false;
// this is as close as we can get to being "success" before writing output
// so set the content type now
viewContext.HttpContext.Response.ContentType = "application/pdf";
// parse memory through document into output
using (var reader = GetXmlReader(resultCache))
{
parser.Go(document, reader);
}
pdfWriter.Close();
}
private static XmlTextReader GetXmlReader(string source)
{
byte[] byteArray = Encoding.UTF8.GetBytes(source);
MemoryStream stream = new MemoryStream(byteArray);
var xtr = new XmlTextReader(stream);
xtr.WhitespaceHandling = WhitespaceHandling.None; // Helps iTextSharp parse
return xtr;
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName,
bool useCache)
{
throw new System.NotImplementedException();
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName,
bool useCache)
{
throw new System.NotImplementedException();
}
public void ReleaseView(ControllerContext controllerContext, IView view)
{
_result.ViewEngine.ReleaseView(controllerContext, _result.View);
}
}
public class PdfFileEvents : PdfPageEventHelper
{
public override void OnEndPage(PdfWriter pi, Document doc)
{
PdfContentByte cb = pi.DirectContent;
ColumnText ct = new ColumnText(cb);
List list = new List(List.ORDERED);
string text = "<ol><li><span style='color: rgb(34, 34, 34); font-family: arial, sans-serif; font-size: 13px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 16.12px; orphans: auto; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px; -webkit-text-stroke-width: 0px; display: inline !important; float: none; background-color: rgb(255, 255, 255); font-size: 9px;'>"+RentProModels.Models.Settings.Get("CompanyName") + "<br />" + RentProModels.Models.Settings.Get("CompanyTelephone") + "<br />" +
RentProModels.Models.Settings.Get("CompanyEmail") + "<br />" +
"KVK " + "1234568789" + "<br />" +
"Bank ABN01Blahh12341</span></li></ol>";
ArrayList htmlarraylist = HTMLWorker.ParseToList(new StringReader(text), null);
for (int k = 0; k < htmlarraylist.Count; k++)
{
list.Add((IElement)htmlarraylist[k]);
}
ct.AddElement(list);
ct.SetSimpleColumn(500, 79, 900, 5); //curPos = verder naar boven
ct.Go();
}
public override void OnStartPage(PdfWriter pi, Document doc)
{
PdfContentByte cb = pi.DirectContent;
ColumnText ct = new ColumnText(cb);
cb.BeginText();
cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 12.0f);
cb.SetTextMatrix(doc.LeftMargin, doc.PageSize.Height - doc.TopMargin);
cb.ShowText(String.Format("{0} {1}", "Dit is een", "Header"));
cb.EndText();
}
}
}
Order view (it loads the Order
object into this view, and generates a PDF based on that):
@model RentPro.Models.Tables.Order
@using RentProModels.Models
@using RentPro.Models.Tables
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body style="font-size: 12px;">
<table width="100%" widths="60;40">
<row>
<td>
<newline />
<newline />
<h2 style="text-decoration:underline;">Pakbon</h2>
</td>
<td>
@* <img url="@System.Web.Hosting.HostingEnvironment.MapPath("/")@Settings.Get("PicturesPath") /../Images/@Settings.Get("LogoFile")" width="239" height="83" />*@
<newline />
<newline />
</td>
</row>
</table>
<table width="100%" cellpadding="0.0" widths="50;17;30">
@if (@Model.Delivery.Company != "nvt")
{
<row>
<cell>
<p style="font-weight:bold;">@Model.Delivery.Company</p>
</cell>
<cell></cell>
<cell></cell>
</row>
}
<row>
<cell><p style="font-weight:bold;">@Model.Delivery.FirstName @Model.Delivery.LastName</p></cell>
<cell>Order Nr:</cell>
<cell>@Model.ID</cell>
</row>
<row>
<cell><p style="font-weight:bold;">@Model.Delivery.StreetName @Model.Delivery.HouseNumber</p></cell>
<cell>Order Datum:</cell>
<cell>@Model.PlaceDate.ToShortDateString()</cell>
</row>
<row>
<cell><p style="font-weight:bold;">@Model.Delivery.ZipCode, @Model.Delivery.City</p></cell>
<cell>Transport:</cell>
<cell>@Model.TransportCarrier.Name</cell>
</row>
<row>
<cell><p style="font-weight:bold;">@Model.Phonenumber</p></cell>
<cell>Start Datum:</cell>
<cell>@Model.StartDate.ToShortDateString()</cell>
</row>
<row>
@{ string KVKofBTW;
if (Model.KVKnummer != null && Model.KVKnummer != "")
{
if (Model.Billing.Country.Name == "Nederland")
{
KVKofBTW = "KVK:";
}
else
{
KVKofBTW = "BTW:";
}
}
else
{
KVKofBTW = "";
}
}
<cell><p style="font-weight:bold;">@KVKofBTW @Model.KVKnummer</p></cell>
<cell>Eind Datum:</cell>
<cell>@Model.EndDate.ToShortDateString()</cell>
</row>
<row>
<cell></cell>
<cell>Aantal Dagen:</cell>
<cell>@((Model.EndDate.Date - Model.StartDate.Date).Days + 1)</cell>
</row>
<row>
<cell>Opmerkingen:</cell>
<cell></cell>
<cell></cell>
</row>
<row>
<cell colspan="3">@Model.Note</cell>
</row>
</table>
<table width="100%" widths="10;60" cellpadding="2">
<row>
<cell>
<newline />
</cell>
<cell></cell>
</row>
<row>
<cell><p style="font-style:italic;">Aantal</p></cell>
<cell><p style="font-style:italic;">Artikel</p></cell>
</row>
@foreach (var item in Model.Items)
{
<row>
<cell>@item.ProductCode</cell>
<cell>@item.Amount</cell>
<cell>@item.ProductTitle</cell>
<cell> [ ]</cell>
<cell> [ ]</cell>
<cell> [ ]</cell>
</row>
if (RentProModels.Models.Settings.GetBool("PakBonAccessory"))
{
foreach (RentPro.Models.Tables.Accessory accesory in item.Product.Accessories)
{
<row>
<cell></cell>
<cell></cell>
<cell>@accesory.Name</cell>
<cell> [ ]</cell>
<cell> [ ]</cell>
<cell> [ ]</cell>
</row>
}
}
}
</table>
<table width="100%" widths="60;50">
<row>
<cell>
<newline />
<newline />
<newline />
<newline />
<newline />
<newline />
</cell>
<cell></cell>
</row>
<row>
<cell>Handtekening voor ontvangst:</cell>
<cell>Retour ontvangen door:</cell>
</row>
<row>
<cell>Naam Klant:</cell>
<cell>Naam:</cell>
</row>
<row>
<cell>Handtekening:</cell>
<cell>Handtekening:</cell>
</row>
</table>
</body>
</html>
I saw a SO post about setting the document's size, I do that, but it doesn't seem to work:
var document = new Document(PageSize.A4, 36, 36, 36, 120);
I gave it a margin of 120 user units on the bottom side, and I only use 110 so why is it still overlapping?
Any help would be greatly appreciated.
After some messing around I decided that it was indeed best to update my project to use a more recent version of iTextSharp.
I've completely gotten rid of the
RazorPDF
library and now work with iTS 5.5.8. I thought I'd share my updated code incase everyone ever runs into this problem.This has also fixed the problem in my OP about the footer overlapping with the PDF content.
New code:
ControllerExtension:
Usage in controller: