Multipage PDF document from predefined template - c#

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();
}
}

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.

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)
{
}
}

Related

iTextSharp not dispose after close the object

I need to split a pdf and I have using this code:
using iTextSharp.text;
using iTextSharp.text.pdf;
private static void PDF_ExtractPages(string pdfFilePath, string outputPath)
{
int pageNameSuffix = 0;
// Intialize a new PdfReader instance with the contents of the source Pdf file:
//PdfReader reader = new PdfReader(pdfFilePath);
using (PdfReader reader = new PdfReader(pdfFilePath))
{
FileInfo file = new FileInfo(pdfFilePath);
string pdfFileName = file.Name.Substring(0, file.Name.LastIndexOf(".")) + "-";
for (int pageNumber = 1; pageNumber <= reader.NumberOfPages; pageNumber++)
{
pageNameSuffix++;
string newPdfFileName = string.Format(pdfFileName + "{0}", pageNameSuffix);
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileStream(outputPath + "\\" + newPdfFileName + ".pdf", FileMode.Create));
document.Open();
copy.AddPage(copy.GetImportedPage(reader, pageNumber));
document.Close();
copy.Close();
}
}
}
and I get this error on line:
using (PdfReader reader = new PdfReader(pdfFilePath))
type used in a using statement must be implicitly convertible to 'System.IDisposable'
if I remove the using with this code:
PdfReader reader = new PdfReader(pdfFilePath);
the code work but not dispose reader object, and when I try to open a PDF I get a message like: "this document is open by another user".
There is a way to dispose PdfReader and all inside code ?
Thanks !
That's why it doesn't implement IDisposable.
You can use a try-catch block which is a equivalent way.
PdfReader reader = null;
try
{
reader = new PdfReader(pdfFilePath);
// Some code here
}
catch (Exception)
{
// Take some action.
}
finally
{
reader.Dispose();
}

Insertion data into text file exception

I have written a code that crates a text file called contact and then try to store data and read data from it.
I have no problem in storing data but how can I store new data without override the oldest one?
and when I am finishing the insertion process an exception occurred : that my text file can not be accessed because it is being used by another process.
what should I do ??
here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
namespace ConsoleApplication9
{
[Serializable]
class contact
{
string name;
string address;
string phonenumber;
string emailaddress;
public override string ToString()
{
return name + " " + address + " " + phonenumber + " " + emailaddress;
}
public void AddContent(string cname, string caddress, string cphone, string cemail)
{
name = cname;
address = caddress;
phonenumber = cphone;
emailaddress = cemail;
FileStream file = new FileStream("contact.txt", FileMode.OpenOrCreate, FileAccess.Write);
BinaryFormatter bin = new BinaryFormatter();
contact person = new contact();
person.name = cname;
person.address = caddress;
person.phonenumber = cphone;
person.emailaddress = cemail;
bin.Serialize(file, person);
file.Close();
Console.WriteLine(" added ");
}
public void viewContact
{
FileStream file = new FileStream("contact.txt", FileMode.Open, FileAccess.Read);
BinaryFormatter bin = new BinaryFormatter();
List<string> contact_list = new List<string>();
//while (file != null)
using (StreamReader read = new StreamReader("contact.txt"))
{
string temp = read.ReadToEnd();
contact_list.Add(temp);
} file.Close();
for (int i = 0; i < contact_list.Count; i++)
{
Console.WriteLine(contact_list[i]);
}
}
}//end of class
Use FileMode.Append to append to existing file

Why does this code write a "corrupt file" that Word cannot open (not even "Open and Repair")?

I'm a newb, feel free talk down to me.
Sorry to be so vague; the code executes and writes a corrupt file. I can't get into the corrupt file to observe any errors/anomolies. The size does look like a sum of the files being merged.
I've googled the snot outta this and can't find anything I can understand how to implement.
The Word error on open is:
The file cannot be open because there are problems with the contents
and
Word found unreadable content, do you want to continue?
When I click Yes I get the first error again & then I'm out.
THe documents have content controls with the same name, but I've modded this to only add 1 doc in an effort to see if dupe content types would be a problem; same corrupt file results.
btw....my ultimate intent is to overwrite the "template" (...Aggregate Report.dotx) in place. But I can't get a valid file saved ANYWHERE, so...... :-/
using System;
using System.IO;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Word = DocumentFormat.OpenXml.Wordprocessing;
namespace BobsDocMerger.VisualWebPart1
{
[ToolboxItemAttribute(false)]
public class VisualWebPart1 : WebPart
{
// Visual Studio might automatically update this path when you change the Visual Web Part project item.
private const string _ascxPath = #"~/_CONTROLTEMPLATES/BobsDocMerger/VisualWebPart1/VisualWebPart1UserControl.ascx";
protected override void CreateChildControls()
{
System.Web.UI.Control control = this.Page.LoadControl(_ascxPath);
Controls.Add(control);
base.CreateChildControls();
Button btnSubmit = new Button();
btnSubmit.Text = "Assemble Documents";
btnSubmit.Click += new EventHandler(btnSubmit_Click);
Controls.Add(btnSubmit);
}
void btnSubmit_Click(object sender, EventArgs e)
{
SPFolder folder = SPContext.Current.ListItem.Folder;
char[] splitter = { '/' };
string[] folderName = folder.Name.Split(splitter);
string filePrefix = #"Weekly/" + folderName[0] + "/" + folderName[0];
SPFile template = folder.Files[filePrefix + " - Aggregate Report.dotx"];
SPFile file;
byte[] byteArray = template.OpenBinary();
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(mem, true))
{
MainDocumentPart mainPart = myDoc.MainDocumentPart;
//Loop thru content controls
foreach (Word.SdtElement sdt in mainPart.Document.Descendants<Word.SdtElement>().ToList())
{
Word.SdtAlias alias = sdt.Descendants<Word.SdtAlias>().FirstOrDefault();
if (alias != null)
{
//The 2 tags in the Report are AggregateHeader and AggregateBody
string sdtTitle = alias.Val.Value;
string sdtTag = sdt.GetFirstChild<SdtProperties>().GetFirstChild<Tag>().Val;
if (sdtTitle == "Merge")
{
for (int i = 0; i < folder.Files.Count; i++)
{
file = folder.Files[i];
//Do all files that are NOT the Aggregate Report
if (file.Name.IndexOf("Aggregate Report") == -1)
{
if (i == folder.Files.Count-1)
{
AddAltChunk(mainPart, sdt, file, true);
}
else
{
AddAltChunk(mainPart, sdt, file, false);
}
}
}
}
}
}
HttpResponse resp = HttpContext.Current.Response;
resp.ClearContent();
resp.ClearHeaders();
resp.AddHeader("Content-Disposition", "attachment; filename=Assembled Document.docx");
//resp.ContentEncoding = System.Text.Encoding.UTF8;
resp.ContentType = "application/msword";
resp.OutputStream.Write(mem.ToArray(), 0, (int)mem.Length);
resp.Flush();
resp.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
}
protected int id = 1;
void AddAltChunk(MainDocumentPart mainPart, Word.SdtElement sdt, SPFile filename,bool LastPass)
{
string altChunkId = "AltChunkId" + id;
id++;
byte[] byteArray = filename.OpenBinary();
AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(
AlternativeFormatImportPartType.WordprocessingML, altChunkId);
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
mem.Seek(0, SeekOrigin.Begin);
chunk.FeedData(mem);
}
Word.AltChunk altChunk = new Word.AltChunk();
altChunk.Id = altChunkId;
//Replace content control with altChunk information
DocumentFormat.OpenXml.OpenXmlElement parent = sdt.Parent;
parent.InsertBefore(altChunk, sdt);
if (LastPass) { sdt.Remove(); }
}
}
}
It looks like you're not calling .Seek() properly on the main memory stream, also you seem to be using that single memory stream for both input and output possibly at the same time. (Maybe it's correct, but it's just very confusing to me when it's mixed mode)
I'm assuming you can't access raw filenames and the filesystem:
using(Stream t = template.OpenBinaryStream())
{
using (WordprocessingDocument myDoc = WordprocessingDocument.Open(t, true))
{
using (XmlWriter writer = XmlWriter.Create(resp.OutputStream))
{
// TODO re-add merge logic here once it works
HttpResponse resp = HttpContext.Current.Response;
resp.ClearContent();
resp.ClearHeaders();
resp.AddHeader("Content-Disposition",
"attachment; filename=Assembled Document.docx");
//resp.ContentEncoding = System.Text.Encoding.UTF8;
resp.ContentType = "application/msword";
// resp.OutputStream.Write(mem.ToArray(), 0, (int)mem.Length);
/* new */ myDoc.MainDocumentPart.Document.WriteTo(writer);
resp.Flush();
resp.Close();
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
}
PS - I recommend getting a raw template to output correctly first. Then make one tiny change and see that output before re-adding your merge logic.

Creating a PDF file?

I looked into iTextSharp and SharpPDF and Report.Net as well as PDFSharp.
None of these open source projects have good documentation OR do not work with VS 2012.
Does anyone have a recommended solution or can show me the documentation?
My employer blocks many sites and although Google is not blocked, some of the results are.
I plan on using C# with WinForms and obtaining my data from an Access DB
Hey #Cocoa Dev get this a complete example with diferent functions.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using iTextSharp.text.pdf;
using System.Data;
using System.Text;
using System.util.collections;
using iTextSharp.text;
using System.Net.Mail;
public partial class PDFScenarios : System.Web.UI.Page
{
public string P_InputStream = "~/MyPDFTemplates/ex1.pdf";
public string P_InputStream2 = "~/MyPDFTemplates/ContactInfo.pdf";
public string P_InputStream3 = "~/MyPDFTemplates/MulPages.pdf";
public string P_InputStream4 = "~/MyPDFTemplates/CompanyLetterHead.pdf";
public string P_OutputStream = "~/MyPDFOutputs/ex1_1.pdf";
//Read all 'Form values/keys' from an existing multi-page PDF document
public void ReadPDFformDataPageWise()
{
PdfReader reader = new PdfReader(Server.MapPath(P_InputStream3));
AcroFields form = reader.AcroFields;
try
{
for (int page = 1; page <= reader.NumberOfPages; page++)
{
foreach (KeyValuePair<string, AcroFields.Item> kvp in form.Fields)
{
switch (form.GetFieldType(kvp.Key))
{
case AcroFields.FIELD_TYPE_CHECKBOX:
case AcroFields.FIELD_TYPE_COMBO:
case AcroFields.FIELD_TYPE_LIST:
case AcroFields.FIELD_TYPE_RADIOBUTTON:
case AcroFields.FIELD_TYPE_NONE:
case AcroFields.FIELD_TYPE_PUSHBUTTON:
case AcroFields.FIELD_TYPE_SIGNATURE:
case AcroFields.FIELD_TYPE_TEXT:
int fileType = form.GetFieldType(kvp.Key);
string fieldValue = form.GetField(kvp.Key);
string translatedFileName = form.GetTranslatedFieldName(kvp.Key);
break;
}
}
}
}
catch
{
}
finally
{
reader.Close();
}
}
//Read and alter form values for only second and
//third page of an existing multi page PDF doc.
//Save the changes in a brand new pdf file.
public void ReadAlterPDFformDataInSelectedPages()
{
PdfReader reader = new PdfReader(Server.MapPath(P_InputStream3));
reader.SelectPages("1-2"); //Work with only page# 1 & 2
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(Server.MapPath(P_OutputStream), FileMode.Create)))
{
AcroFields form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
foreach (string fieldKey in fieldKeys)
{
//Replace Address Form field with my custom data
if (fieldKey.Contains("Address"))
{
form.SetField(fieldKey, "MyCustomAddress");
}
}
//The below will make sure the fields are not editable in
//the output PDF.
stamper.FormFlattening = true;
}
}
//Extract text from an existing PDF's second page.
private string ExtractText()
{
PdfReader reader = new PdfReader(Server.MapPath(P_InputStream3));
string txt = PdfTextExtractor.GetTextFromPage(reader, 2, new LocationTextExtractionStrategy());
return txt;
}
//Create a brand new PDF from scratch and without a template
private void CreatePDFNoTemplate()
{
Document pdfDoc = new Document();
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, new FileStream(Server.MapPath(P_OutputStream), FileMode.OpenOrCreate));
pdfDoc.Open();
pdfDoc.Add(new Paragraph("Some data"));
PdfContentByte cb = writer.DirectContent;
cb.MoveTo(pdfDoc.PageSize.Width / 2, pdfDoc.PageSize.Height / 2);
cb.LineTo(pdfDoc.PageSize.Width / 2, pdfDoc.PageSize.Height);
cb.Stroke();
pdfDoc.Close();
}
private void fillPDFForm()
{
string formFile = Server.MapPath(P_InputStream);
string newFile = Server.MapPath(P_OutputStream);
PdfReader reader = new PdfReader(formFile);
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(newFile, FileMode.Create)))
{
AcroFields fields = stamper.AcroFields;
// set form fields
fields.SetField("name", "John Doe");
fields.SetField("address", "xxxxx, yyyy");
fields.SetField("postal_code", "12345");
fields.SetField("email", "johndoe#xxx.com");
// flatten form fields and close document
stamper.FormFlattening = true;
stamper.Close();
}
}
//Helper functions
private void SendEmail(MemoryStream ms)
{
MailAddress _From = new MailAddress("XXX#domain.com");
MailAddress _To = new MailAddress("YYY#a.com");
MailMessage email = new MailMessage(_From, _To);
Attachment attach = new Attachment(ms, new System.Net.Mime.ContentType("application/pdf"));
email.Attachments.Add(attach);
SmtpClient mailSender = new SmtpClient("Gmail-Server");
mailSender.Send(email);
}
private void DownloadAsPDF(MemoryStream ms)
{
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-Disposition", "attachment;filename=abc.pdf");
Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
Response.OutputStream.Flush();
Response.OutputStream.Close();
Response.End();
ms.Close();
}
//Working with Memory Stream and PDF
public void CreatePDFFromMemoryStream()
{
//(1)using PDFWriter
Document doc = new Document();
MemoryStream memoryStream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc, memoryStream);
doc.Open();
doc.Add(new Paragraph("Some Text"));
writer.CloseStream = false;
doc.Close();
//Get the pointer to the beginning of the stream.
memoryStream.Position = 0;
//You may use this PDF in memorystream to send as an attachment in an email
//OR download as a PDF
SendEmail(memoryStream);
DownloadAsPDF(memoryStream);
//(2)Another way using PdfStamper
PdfReader reader = new PdfReader(Server.MapPath(P_InputStream2));
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamper = new PdfStamper(reader, ms);
AcroFields fields = stamper.AcroFields;
fields.SetField("SomeField", "MyValueFromDB");
stamper.FormFlattening = true;
stamper.Close();
SendEmail(ms);
}
}
//Burst-- Make each page of an existing multi-page PDF document
//as another brand new PDF document
private void PDFBurst()
{
string pdfTemplatePath = Server.MapPath(P_InputStream3);
PdfReader reader = new PdfReader(pdfTemplatePath);
//PdfCopy copy;
PdfSmartCopy copy;
for (int i = 1; i < reader.NumberOfPages; i++)
{
Document d1 = new Document();
copy = new PdfSmartCopy(d1, new FileStream(Server.MapPath(P_OutputStream).Replace(".pdf", i.ToString() + ".pdf"), FileMode.Create));
d1.Open();
copy.AddPage(copy.GetImportedPage(reader, i));
d1.Close();
}
}
//Copy a set of form fields from an existing PDF template/doc
//and keep appending to a brand new PDF file.
//The copied set of fields will have different values.
private void AppendSetOfFormFields()
{
PdfCopyFields _copy = new PdfCopyFields(new FileStream(Server.MapPath(P_OutputStream), FileMode.Create));
_copy.AddDocument(new PdfReader(a1("1")));
_copy.AddDocument(new PdfReader(a1("2")));
_copy.AddDocument(new PdfReader(new FileStream(Server.MapPath("~/MyPDFTemplates/Myaspx.pdf"), FileMode.Open)));
_copy.Close();
}
//ConcatenateForms
private byte[] a1(string _ToAppend)
{
using (var existingFileStream = new FileStream(Server.MapPath(P_InputStream), FileMode.Open))
using (MemoryStream stream = new MemoryStream())
{
// Open existing PDF
var pdfReader = new PdfReader(existingFileStream);
var stamper = new PdfStamper(pdfReader, stream);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
foreach (string fieldKey in fieldKeys)
{
form.RenameField(fieldKey, fieldKey + _ToAppend);
}
// "Flatten" the form so it wont be editable/usable anymore
stamper.FormFlattening = true;
stamper.Close();
pdfReader.Close();
return stream.ToArray();
}
}
//Working with Image
private void AddAnImage()
{
using (var inputPdfStream = new FileStream(#"C:\MyInput.pdf", FileMode.Open))
using (var inputImageStream = new FileStream(#"C:\img1.jpg", FileMode.Open))
using (var outputPdfStream = new FileStream(#"C:\MyOutput.pdf", FileMode.Create))
{
PdfReader reader = new PdfReader(inputPdfStream);
PdfStamper stamper = new PdfStamper(reader, outputPdfStream);
PdfContentByte pdfContentByte = stamper.GetOverContent(1);
var image = iTextSharp.text.Image.GetInstance(inputImageStream);
image.SetAbsolutePosition(1, 1);
pdfContentByte.AddImage(image);
stamper.Close();
}
}
//Add Company Letter-Head/Stationary to an existing pdf
private void AddCompanyStationary()
{
PdfReader reader = new PdfReader(Server.MapPath(P_InputStream2));
PdfReader s_reader = new PdfReader(Server.MapPath(P_InputStream4));
using (PdfStamper stamper = new PdfStamper(reader, new FileStream(Server.MapPath(P_OutputStream), FileMode.Create)))
{
PdfImportedPage page = stamper.GetImportedPage(s_reader, 1);
int n = reader.NumberOfPages;
PdfContentByte background;
for (int i = 1; i <= n; i++)
{
background = stamper.GetUnderContent(i);
background.AddTemplate(page, 0, 0);
}
stamper.Close();
}
}
Try this example:
using iTextSharp.text;
// Set up the fonts to be used on the pages
private Font _largeFont = new Font(Font.FontFamily.HELVETICA, 18, Font.BOLD, BaseColor.BLACK);
private Font _standardFont = new Font(Font.FontFamily.HELVETICA, 14, Font.NORMAL, BaseColor.BLACK);
private Font _smallFont = new Font(Font.FontFamily.HELVETICA, 10, Font.NORMAL, BaseColor.BLACK);
public void Build()
{
iTextSharp.text.Document doc = null;
try
{
// Initialize the PDF document
doc = new Document();
iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc,
new System.IO.FileStream(System.IO.Directory.GetCurrentDirectory() + "\\ScienceReport.pdf",
System.IO.FileMode.Create));
// Set margins and page size for the document
doc.SetMargins(50, 50, 50, 50);
// There are a huge number of possible page sizes, including such sizes as
// EXECUTIVE, LEGAL, LETTER_LANDSCAPE, and NOTE
doc.SetPageSize(new iTextSharp.text.Rectangle(iTextSharp.text.PageSize.LETTER.Width,
iTextSharp.text.PageSize.LETTER.Height));
// Add metadata to the document. This information is visible when viewing the
// document properities within Adobe Reader.
doc.AddTitle("My Science Report");
doc.AddCreator("M. Lichtenberg");
doc.AddKeywords("paper airplanes");
// Add Xmp metadata to the document.
this.CreateXmpMetadata(writer);
// Open the document for writing content
doc.Open();
// Add pages to the document
this.AddPageWithBasicFormatting(doc);
this.AddPageWithInternalLinks(doc);
this.AddPageWithBulletList(doc);
this.AddPageWithExternalLinks(doc);
this.AddPageWithImage(doc, System.IO.Directory.GetCurrentDirectory() + "\\FinalGraph.jpg");
// Add page labels to the document
iTextSharp.text.pdf.PdfPageLabels pdfPageLabels = new iTextSharp.text.pdf.PdfPageLabels();
pdfPageLabels.AddPageLabel(1, iTextSharp.text.pdf.PdfPageLabels.EMPTY, "Basic Formatting");
pdfPageLabels.AddPageLabel(2, iTextSharp.text.pdf.PdfPageLabels.EMPTY, "Internal Links");
pdfPageLabels.AddPageLabel(3, iTextSharp.text.pdf.PdfPageLabels.EMPTY, "Bullet List");
pdfPageLabels.AddPageLabel(4, iTextSharp.text.pdf.PdfPageLabels.EMPTY, "External Links");
pdfPageLabels.AddPageLabel(5, iTextSharp.text.pdf.PdfPageLabels.EMPTY, "Image");
writer.PageLabels = pdfPageLabels;
}
catch (iTextSharp.text.DocumentException dex)
{
// Handle iTextSharp errors
}
finally
{
// Clean up
doc.Close();
doc = null;
}
}
You can always just create an html page and then convert that to pdf using wkhtmltopdf. This has the benefit of you not having to construct the pdf with a library such as iText. You just make a text file (html) and then pass it to the wkhtmltopdf executable.
See Calling wkhtmltopdf to generate PDF from HTML for more info.

How to get the stream for a template on a Document Library

I am trying to load the template file my SPDocumentLibrary has set, do some transforms in OpenXML then save it back to the library as a document. I think I have all the steps down except reading the template. I don't know the "correct" way to open it (I could just use a WebClient to download it but it makes me feel dirty just typing that). Here is what I have so far.
public string GetOrGenerateChecklist(string PracticeName, string ContractID, string EducationDate, string MainContactInfo, string Address)
{
using (SPWeb web = SPContext.Current.Web)
{
SPDocumentLibrary list = (SPDocumentLibrary)web.Lists["Educator Checklists"];
var templetAddr = String.Concat(web.Url, '/', list.DocumentTemplateUrl);
SPQuery query = new SPQuery();
query.Query = string.Concat(
//Snip
);
var items = list.GetItems(query);
//if document exists return existing document.
if (items.Count > 0)
return String.Concat(web.Url, "/Educator Checklists/", PracticeName, " - ", ContractID, ".docx");
MemoryStream documentStream;
//copy the stream to memory
using (Stream tplStream = ????????) //<------- my problem
{
documentStream = new MemoryStream((int)tplStream.Length);
CopyStream(tplStream, documentStream);
documentStream.Position = 0L;
}
using (WordprocessingDocument template = WordprocessingDocument.Open(documentStream, true))
{
template.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);
MainDocumentPart mainPart = template.MainDocumentPart;
mainPart.DocumentSettingsPart.AddExternalRelationship(
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate",
new Uri(templetAddr, UriKind.Absolute));
ReplaceText(mainPart, "#PracticeName#", PracticeName);
ReplaceText(mainPart, "#EducationDate#", EducationDate);
ReplaceText(mainPart, "#MainContactInfo#", MainContactInfo);
ReplaceText(mainPart, "#Address#", Address);
}
documentStream.Position = 0L;
list.RootFolder.Files.Add(String.Concat(PracticeName, " - ", ContractID, ".docx"), documentStream);
return String.Concat(web.Url, "/Educator Checklists/", PracticeName, " - ", ContractID, ".docx");
}
}
So my question is how do i get the template stored at templetAddr loaded in to the memory stream?
Also I am fairly new to sharepoint so if you see any other big mistakes I have made please let me know.
web.GetFile(templetAddr).OpenBinaryStream()
http://msdn.microsoft.com/en-us/library/ms476063.aspx (SPWeb.GetFile)
http://msdn.microsoft.com/en-us/library/ms470901.aspx (SPFile.OpenBinaryStream)
Here is a blog post that discusses some considerations when using the SharePoint object model to get an Open XML document, modify it, and put it back.
http://blogs.msdn.com/b/ericwhite/archive/2010/03/24/modifying-an-open-xml-document-in-a-sharepoint-document-library.aspx
Here is the smallest code to do so:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.SharePoint;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
class Program
{
static void Main(string[] args)
{
string siteUrl = "http://localhost";
using (SPSite spSite = new SPSite(siteUrl))
{
Console.WriteLine("Querying for Test.docx");
SPList list = spSite.RootWeb.Lists["Shared Documents"];
SPQuery query = new SPQuery();
query.ViewFields = #"<FieldRef Name='FileLeafRef' />";
query.Query =
#"<Where>
<Eq>
<FieldRef Name='FileLeafRef' />
<Value Type='Text'>Test.docx</Value>
</Eq>
</Where>";
SPListItemCollection collection = list.GetItems(query);
if (collection.Count != 1)
{
Console.WriteLine("Test.docx not found");
Environment.Exit(0);
}
Console.WriteLine("Opening");
SPFile file = collection[0].File;
byte[] byteArray = file.OpenBinary();
using (MemoryStream memStr = new MemoryStream())
{
memStr.Write(byteArray, 0, byteArray.Length);
using (WordprocessingDocument wordDoc =
WordprocessingDocument.Open(memStr, true))
{
Document document = wordDoc.MainDocumentPart.Document;
Paragraph firstParagraph = document.Body.Elements<Paragraph>()
.FirstOrDefault();
if (firstParagraph != null)
{
Paragraph testParagraph = new Paragraph(
new Run(
new Text("Test")));
firstParagraph.Parent.InsertBefore(testParagraph,
firstParagraph);
}
}
Console.WriteLine("Saving");
string linkFileName = file.Item["LinkFilename"] as string;
file.ParentFolder.Files.Add(linkFileName, memStr, true);
}
}
}
}

Categories

Resources