Reputation: 1074
I have a requirement to generate invoice reports in PDF using some predefined company templates. I am able to create/generate SINGLE PAGE PDF reports using iTextSharp.
Problem: The problem comes when the invoice statement spans MULTIPLE PAGES. I am not able to extend the report(invoice statement) to next(2nd) page. If all the data can not be accommodated on one page it should be written on 2nd page, while still using the company template.
The template is present at following path:
HostingEnvironment.MapPath("~/Content/InvoiceTemplate/invoiceTemplate.pdf")
I am using iTextSharp library to create documents. Below is the code used to generate the PDF:
public class pdfStatementController : Controller {
Models.DYNAMICS_EXTEntities _db = new Models.DYNAMICS_EXTEntities();
//
// GET: /pdfStatement/
public ActionResult SendPdfStatement(string InvoiceNumber) {
try {
InvoiceNumber = InvoiceNumber.Trim();
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
List<Models.Statement> statementList = new List<Models.Statement>();
statementList = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters).ToList<Models.Statement>();
pdfStatementController.WriteInTemplate(statementList);
return RedirectToAction("Invoice", "Invoice", new { id = statementList.FirstOrDefault().Customer_ID.ToString().Trim() });
} catch (Exception e) {
return View("Error");
}
}
public static void WriteInTemplate(List<Models.Statement> statementList) {
string invoiceNumber = statementList.FirstOrDefault().Invoice.ToString().Trim();
string month = null;
string day = null;
string year = null;
PdfReader pdfReader = new PdfReader(
HostingEnvironment.MapPath(
"~/Content/InvoiceTemplate/invoiceTemplate.pdf"));
FileStream fileStream = new FileStream(
HostingEnvironment.MapPath(
"~/Content/reports/" + invoiceNumber + ".pdf"),
FileMode.Create);
PdfStamper pdfStamper = new PdfStamper(pdfReader, fileStream);
AcroFields pdfFields = pdfStamper.AcroFields;
pdfFields.SetField("BillToCompany", statementList.FirstOrDefault().BillToCompany.ToString().Trim().ToUpper());
pdfFields.SetField("BillToContact", statementList.FirstOrDefault().BillToContact.ToString().Trim().ToUpper());
pdfFields.SetField("CustomerId", statementList.FirstOrDefault().Customer_ID);
pdfFields.SetField("InvoiceNumber", statementList.FirstOrDefault().Invoice.ToString().Trim());
pdfFields.SetField("JobNumber", statementList.FirstOrDefault().JobNumber.ToString().Trim());
pdfFields.SetField("Caller", statementList.FirstOrDefault().Caller.ToString().Trim());
pdfStamper.FormFlattening = true; // generate a flat PDF
pdfStamper.Close();
pdfReader.Close();
}
}
Upvotes: 6
Views: 8195
Reputation: 1074
Though the last answer is a very good one and helped me to solve my problem, i am putting it here to sum up the question.
Problem: I had a scenario to generate multipage pdf document in Company provided template. Needed to generate invoice statements and attach them to email via Microsoft outlook email Client.
I use MVC3, ASP.NET 4.0, Entity Framework
Solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Data;
using System.Data.Objects;
using System.IO;
using iTextSharp;
using iTextSharp.text;
using iTextSharp.text.html;
using iTextSharp.text.pdf;
using iTextSharp.text.xml;
namespace InvoiceSearchTool.Controllers
{
public class pdfStatementController : Controller
{
Models.DYNAMICS_EXTEntities _db = new Models.DYNAMICS_EXTEntities();
//
// GET: /pdfStatement/
public ActionResult SendPdfStatement(string InvoiceNumber)
{
try
{
InvoiceNumber = InvoiceNumber.Trim();
List<Models.Statement> statementList = new List<Models.Statement>();
//this is if you use entity framework
{
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
statementList = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters).ToList<Models.Statement>();
}
//others can simply use line like
//statementList = GetStatementList(inviceNumber);
pdfStatementController.WriteInTemplate(statementList);
return RedirectToAction("Invoice", "Invoice", new { id = statementList.FirstOrDefault().Customer_ID.ToString().Trim() });
}
catch (Exception e)
{
return View("Error");
}
}
public static void WriteInTemplate(List<Models.Statement> statementList)
{
try
{
string invoiceNumber = statementList.FirstOrDefault().Invoice.ToString().Trim();
using (Document document = new Document())
{
FileStream fileStream = new FileStream(HostingEnvironment.MapPath("~/Content/reports/" + invoiceNumber + ".pdf"), FileMode.Create);
using (PdfSmartCopy smartCopy = new PdfSmartCopy(document, fileStream))
{
document.Open();
PdfReader pdfReader = new PdfReader(HostingEnvironment.MapPath("~/Content/InvoiceTemplate/invoiceTemplate.pdf"));
using (var memoryStream = new MemoryStream())
{
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, memoryStream))
{
string month = null;
string day = null;
string year = null;
AcroFields pdfFields = pdfStamper.AcroFields;
{//billing address
pdfFields.SetField("BillToCompany", statementList.FirstOrDefault().BillToCompany.ToString().Trim().ToUpper());
pdfFields.SetField("BillToContact", statementList.FirstOrDefault().BillToContact.ToString().Trim().ToUpper());
pdfFields.SetField("ShipToCompany", statementList.FirstOrDefault().ShipToCompany.ToString().Trim().ToUpper());
pdfFields.SetField("ShipToContact", statementList.FirstOrDefault().ShipToContact.ToString().Trim().ToUpper());
pdfFields.SetField("PONumber", statementList.FirstOrDefault().PurchaseOrderNo.ToString().Trim());
pdfFields.SetField("OrderNumber", statementList.FirstOrDefault().Order_Number.ToString().Trim());
pdfFields.SetField("ShippingMethod", statementList.FirstOrDefault().Shipping_Method.ToString().Trim());
pdfFields.SetField("PaymentTerms", statementList.FirstOrDefault().Payment_Terms.ToString().Trim());
}
pdfStamper.FormFlattening = true; // generate a flat PDF
}
pdfReader = new PdfReader(memoryStream.ToArray());
smartCopy.AddPage(smartCopy.GetImportedPage(pdfReader, 1));
}
}
}
emailController.CreateMessageWithAttachment(invoiceNumber);
}
catch (Exception e)
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Net;
using System.Net.Mail;
using System.Web.Hosting;
using System.Net.NetworkInformation;
using System.Data.Objects;
namespace InvoiceSearchTool.Controllers
{
public class emailController : Controller
{
//
// GET: /email/
public static void CreateMessageWithAttachment(string invoiceNumber)
{
try
{
Outlook.Application oApp = new Outlook.Application();
Outlook.MailItem email = (Outlook.MailItem)(oApp.CreateItem(Outlook.OlItemType.olMailItem));
Models.DYNAMICS_EXTEntities _db = new Models.DYNAMICS_EXTEntities();
string recipient = null;
string messageBody = null;
#region set email recipients
{
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", invoiceNumber);
List<Models.EmailAddress> emailList = _db.ExecuteFunction<Models.EmailAddress>("uspGetEmailAddress", parameters).ToList<Models.EmailAddress>();
if(!string.IsNullOrEmpty(emailList[0].Email.ToString()))
recipient = emailList[0].Email.ToString().Trim();
else
recipient = " ";
email.Recipients.Add(recipient);
}
#endregion
//email subject
email.Subject = "Invoice # " + invoiceNumber;
#region set email Text
{
Models.EmailText emailText = _db.ExecuteFunction<Models.EmailText>("uspEmailText").SingleOrDefault();
messageBody = emailText.EmailTextLine1.ToString().Trim() + "\n\n\n\n\n\n\n\n\n";
messageBody += emailText.EmailTextLine2.ToString().Trim() + "\n";
messageBody += emailText.EmailTextLine3.ToString().Trim();
email.Body = messageBody;
}
#endregion
#region email attachment
{
string fileName = invoiceNumber.Trim();
string filePath = HostingEnvironment.MapPath("~/Content/reports/");
filePath = filePath + fileName + ".pdf";
fileName += ".pdf";
int iPosition = (int)email.Body.Length + 1;
int iAttachType = (int)Outlook.OlAttachmentType.olByValue;
Outlook.Attachment oAttach = email.Attachments.Add(filePath, iAttachType, iPosition, fileName);
}
#endregion
email.Display();
//uncomment below line to SendAutomatedEmail emails atomaticallly
//((Outlook.MailItem)email).Send();
}
catch (Exception e)
{
}
}
Upvotes: 0
Reputation: 9372
Your code looks good and is only missing a couple of intermediate steps.
Since you're using the same PDF template for every page (when two or more pages need to be generated), instead of using a PdfStamper
to add content directly to the Document
, you use a PdfSmartCopy
or PdfCopy
object.
The PdfStamper
is still needed. However, in this case it's used to create an in-memory (single) page filled with data as you as you iterate over your Models.Statement
collection.
In other words, PdfSmartCopy/PdfCopy
maintains your statements as a whole, (total pages) and PdfStamper
is used as a buffer that adds your individual statements page-by-page to your PDF. Here's a simple working example HTTP hander (.ashx):
<%@ WebHandler Language="C#" Class="copyFillTemplate" %>
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using iTextSharp.text;
using iTextSharp.text.pdf;
public class copyFillTemplate : IHttpHandler {
public void ProcessRequest (HttpContext context) {
HttpServerUtility Server = context.Server;
HttpResponse Response = context.Response;
Response.ContentType = "application/pdf";
// template used to test __this__ example;
// replace with __your__ PDF template
string pdfTemplatePath = Server.MapPath(
"~/app_data/template.pdf"
);
// this example's test data; replace with __your__ data collection
List<Statement> statementList = Statement.GetStatements();
// COPY FROM HERE
using (Document document = new Document()) {
// PdfSmartCopy reduces PDF file size by reusing parts
// of the PDF template, but uses more memory. you can
// replace PdfSmartCopy with PdfCopy if memory is an issue
using (PdfSmartCopy copy = new PdfSmartCopy(
document, Response.OutputStream)
)
{
document.Open();
// used to test this example
int counter = 0;
// generate one page per statement
foreach (Statement statment in statementList) {
++counter;
// replace this with your PDF form template
PdfReader reader = new PdfReader(pdfTemplatePath);
using (var ms = new MemoryStream()) {
using (PdfStamper stamper = new PdfStamper(reader, ms)) {
AcroFields form = stamper.AcroFields;
// replace this with your field data for each page
form.SetField("title", counter.ToString());
stamper.FormFlattening = true;
}
reader = new PdfReader(ms.ToArray());
// add one page at a time; assumes your template is only one page.
// if your template is more than one page you will need to
// call GetImportedPage() for each page in your template
copy.AddPage(copy.GetImportedPage(reader, 1));
}
}
}
// COPY TO HERE
}
}
public bool IsReusable { get { return false; } }
public class Statement {
public string FieldName, FieldValue;
public static List<Statement> GetStatements() {
List<Statement> s = new List<Statement>();
for (int i = 0; i < 5; ++i) {s.Add(new Statement());}
return s;
}
}
}
Hopefully the inline comments help. And you obviously need to remove replace some parts I used to test the example code.
Upvotes: 7