Create a pdf file from a template in itextsharp and output as content.

I would like to open an existing pdf, add text, and then output it as content using itext sharp. I have the following code. Where it falls, I want me to output it as a memory stream, but there must be a filestream to open the source file.

Here is what I have. Obviously, the definition of PdfWriter will not work twice.

public static void Create(string path) { var Response = HttpContext.Current.Response; Response.Clear(); Response.ContentType = "application/pdf"; System.IO.MemoryStream m = new System.IO.MemoryStream(); Document document = new Document(); PdfWriter wri = PdfWriter.GetInstance(document, new FileStream(path, FileMode.Create)); PdfWriter.GetInstance(document, m); document.Open(); document.Add(new Paragraph(DateTime.Now.ToString())); document.NewPage(); document.Add(new Paragraph("Hello World")); document.Close(); Response.OutputStream.Write(m.GetBuffer(), 0, m.GetBuffer().Length); Response.OutputStream.Flush(); Response.OutputStream.Close(); Response.End(); } 
+8
itextsharp
source share
2 answers

You have a couple of problems that I will try to get through you.

Firstly, the Document object is intended only for working with new PDF files, and not for modifying existing ones. Basically, the Document object is a bunch of wrapper classes that abstract away from the underlying parts of the PDF specification and allow you to work with higher-level things, such as paragraphs and reflowable content. These abstractions turn what you think of β€œparagraphs” into raw commands that write a paragraph line one at a time without the relationship between the lines. When working with an existing document, there is no safe way to say how to melt the text so that these abstractions are not used.

Instead, you want to use the PdfStamper object. When working with this object, you have two options for working with potentially overlapping content, either your new text will be written on top of existing content, or the text will be written below. The two GetOverContent() or GetUnderContent() methods of the GetOverContent() object PdfStamper return a PdfContentByte object, which you can then write text with.

There are two main ways to write text manually or through a ColumnText object. If you made HTML, you can come up with a ColumnText object using a large fixed position, one row, one <TABLE> column. The advantage of ColumnText is that you can use higher-level abstractions like Paragraph .

Below is the full working C # 2010 WinForms application, targeting iTextSharp 5.1.2.0, which is shown above. See code comments for any questions. It should be pretty easy to convert to ASP.Net.

 using System; using System.IO; using System.Windows.Forms; using iTextSharp.text; using iTextSharp.text.pdf; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { string existingFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file1.pdf"); string newFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file2.pdf"); using (FileStream fs = new FileStream(existingFile, FileMode.Create, FileAccess.Write, FileShare.None)) { using (Document doc = new Document(PageSize.LETTER)) { using (PdfWriter writer = PdfWriter.GetInstance(doc, fs)) { doc.Open(); doc.Add(new Paragraph("This is a test")); doc.Close(); } } } //Bind a PdfReader to our first document PdfReader reader = new PdfReader(existingFile); //Create a new stream for our output file (this could be a MemoryStream, too) using (FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write, FileShare.None)) { //Use a PdfStamper to bind our source file with our output file using (PdfStamper stamper = new PdfStamper(reader, fs)) { //In case of conflict we want our new text to be written "on top" of any existing content //Get the "Over" state for page 1 PdfContentByte cb = stamper.GetOverContent(1); //Begin text command cb.BeginText(); //Set the font information cb.SetFontAndSize(BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1250, false), 16f); //Position the cursor for drawing cb.MoveText(50, 50); //Write some text cb.ShowText("This was added manually"); //End text command cb.EndText(); //Create a new ColumnText object to write to ColumnText ct = new ColumnText(cb); //Create a single column who lower left corner is at 100x100 and upper right is at 500x200 ct.SetSimpleColumn(100,100,500,200); //Add a higher level object ct.AddElement(new Paragraph("This was added using ColumnText")); //Flush the text buffer ct.Go(); } } this.Close(); } } } 

As for the second problem with FileStream vs MemoryStream , if you look at the method signature for almost every method (in fact, as far as I know) in iTextSharp, you will see that they all accept a Stream , not just a FileStream object. Every time you see this, even outside of iTextSharp, this means that you can pass any subclass of Stream that includes a MemoryStream object, everything else remains the same.

The code below is a slightly modified version above. I deleted most of the comments to make them shorter. The main change is that we use a MemoryStream instead of a FileStream . In addition, when we finish working with PDF, when you need to close the PdfStamper object before accessing the raw binary data. (The using status will do this for us automatically later, but also closes the stream, so we need to manually do it here.)

One more thing, never, never use the GetBuffer() method of a MemoryStream . This is similar to what you want (and I mistakenly used it too), but instead you want to use ToArray() . GetBuffer() contains uninitialized bytes, which usually create corrupted PDF files. In addition, instead of writing an HTTP response to the stream, I first store the bytes in an array. From a debugging point of view, this allows me to finish all the iTextSharp and System.IO code and make sure it is correct, and then do what I want with a raw byte array. In my case, I don't have a web server, so I write them to disk, but you can also just call Response.BinaryWrite(bytes)

 string existingFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file1.pdf"); string newFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "file2.pdf"); PdfReader reader = new PdfReader(existingFile); byte[] bytes; using(MemoryStream ms = new MemoryStream()){ using (PdfStamper stamper = new PdfStamper(reader, ms)) { PdfContentByte cb = stamper.GetOverContent(1); ColumnText ct = new ColumnText(cb); ct.SetSimpleColumn(100,100,500,200); ct.AddElement(new Paragraph("This was added using ColumnText")); ct.Go(); //Flush the PdfStamper buffer stamper.Close(); //Get the raw bytes of the PDF bytes = ms.ToArray(); } } //Do whatever you want with the bytes //Below I'm writing them to disk but you could also write them to the output buffer, too using (FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write, FileShare.None)) { fs.Write(bytes, 0, bytes.Length); } 
+13
source share

The second part of your question reads:

"displaying as content"

If this is what you really want, you can do this:

 Response.AddHeader("Content-Disposition", "attachment; filename=DESIRED-FILENAME.pdf"); 

Using a MemoryStream not required since Response.OutputStream available. Your sample code calls NewPage() and not , trying to add text to an existing page of your PDF, so one way to do what you requested is:

 Response.ContentType = "application/pdf"; Response.AddHeader("Content-Disposition", "attachment; filename=itextTest.pdf"); PdfReader reader = new PdfReader(readerPath); // store the extra text on the last (new) page ColumnText ct = new ColumnText(null); ct.AddElement(new Paragraph("Text on a new page")); int numberOfPages = reader.NumberOfPages; int newPage = numberOfPages + 1; // get all pages from PDF "template" so we can copy them below reader.SelectPages(string.Format("1-{0}", numberOfPages)); float marginOffset = 36f; /* * we use the selected pages above with a PdfStamper to copy the original. * and no we don't need a MemoryStream... */ using (PdfStamper stamper = new PdfStamper(reader, Response.OutputStream)) { // use the same page size as the __last__ template page Rectangle rectangle = reader.GetPageSize(numberOfPages); // add a new __blank__ page stamper.InsertPage(newPage, rectangle); // allows us to write content to the (new/added) page ct.Canvas = stamper.GetOverContent(newPage); // add text at an __absolute__ position ct.SetSimpleColumn( marginOffset, marginOffset, rectangle.Right - marginOffset, rectangle.Top - marginOffset ); ct.Go(); } 

I think you already realized that the Document / PdfWriter does not work in this situation :) This is the standard method for creating a new PDF document.

+3
source share

All Articles