ItextSharp Expected a Dict Object When trying to print - c#

I have a web page that allows a user to view a pdf and print pdf. The print pdf is a copy of the display pdf and i am using ItextSharp to inject the javascript to allow auto printing. I have a method that allows a user to upload a pdf and it calls this method below to copy the display copy into a pdf. Both pdf's are then saved in the database. However , when a user goes to click on the print button on my web page they receive the following error "expected a dict object". below is my code that adds in the auto print, which works fine for me but not on my clients site.
I am doing anything wrong that could be corrupting the file. The original pdf content is passed in as a Binary Object.
Any help on this is much appreciated as i am highly confused on this one. Also i am using ASP.NET MVC2.
MemoryStream originalPdf = new MemoryStream(Content.BinaryData);
MemoryStream updatedPdf = new MemoryStream();
updatedPdf.Write(Content.BinaryData,0, Content.BinaryData.Length);
PdfReader pdfReader = new PdfReader(originalPdf);
PdfStamper pdfStamper = new PdfStamper(pdfReader, updatedPdf);
if (autoPrinting)
{
pdfStamper.JavaScript = "this.print(true);\r";
}
else
{
pdfStamper.JavaScript = null;
}
pdfStamper.Close();
pdfReader.Close();
Content.BinaryData = updatedPdf.ToArray();

Don't write the original PDF to your output. pdfStamper.close() will do all the writing for you, even in append mode (which you're not using).
Your code should read:
MemoryStream originalPdf = new MemoryStream(Content.BinaryData);
MemoryStream updatedPdf = new MemoryStream();
// Don't do that.
//updatedPdf.Write(Content.BinaryData,0, Content.BinaryData.Length);
PdfReader pdfReader = new PdfReader(originalPdf);
PdfStamper pdfStamper = new PdfStamper(pdfReader, updatedPdf);
if (autoPrinting) {
pdfStamper.JavaScript = "this.print(true);\r";
} else {
pdfStamper.JavaScript = null;
}
pdfStamper.Close(); // this does it for you.
pdfReader.Close();
Content.BinaryData = updatedPdf.ToArray();
I'm surprised that this "works for you". If nothing else, I'd expect the JS to fail because the byte offsets would be all wrong... in fact, all your offsets would be all wrong. I think my ignorance of C# is showing.
But Write() behaves the way I thought it would, so I'm back to being surprised.

Related

Why my PDF is not readable after edited by iText?

My PDF is not readable after tried to edit the text.
How to make it works ?
my error message :
Adobe Reader could not open '495049.pdf' because it is either not a supported file type or because the file has been damaged (for example, it was sent as email attachment and wasn't correctly decoded)
Basically the objective is to edit PDF doc and replace particular text.
Input already in binary stream (byte[ ])
I worked on C# environment & iText for the PDF editing lib.
Here's my piece of code :
using (PdfReader reader = new PdfReader(doc.FileStream))
{
PdfDictionary dict = reader.GetPageN(1);
PdfObject pdfObject = dict.GetDirectObject(PdfName.CONTENTS);
if (pdfObject.IsStream())
{
PRStream stream = (PRStream)pdfObject;
byte[] data = PdfReader.GetStreamBytes(stream);
stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace("[ReplacmentText]", "Hello World")));
}
using (MemoryStream ms = new MemoryStream())
{
var ignored = new PdfStamper(reader, ms);
reader.Close();
return ms.ToArray();
}
}
Your main mistake is that you retrieve the contents of the memory stream before closing the stamper; actually you don't close it at all!
Only when closing the stamper, the final part of the PDF is written. Thus:
using (MemoryStream ms = new MemoryStream())
{
var ignored = new PdfStamper(reader, ms);
ignored.Close();
reader.Close();
return ms.ToArray();
}
Your other problem (probably not relevant for your current test documents but in general):
stream.SetData(System.Text.Encoding.ASCII.GetBytes(System.Text.Encoding.ASCII.GetString(data).Replace("[ReplacmentText]", "Hello World")));
This assumes very much, especially that the stream content only contains ASCII bytes, that the place holder "[ReplacementText]" (I assume this is the correct spelling) occurs in one piece and in the immediate content streams, that the font used to draw the place holder and its replacement uses an ASCII'ish encoding, and that this font has glyphs for all characters in "Hello World". Neither of these assumptions are automatically true.

Creating a new PDF from an existing template - odd behavior in Acrobat XI

I'm generating a new PDF from an existing template that has been created in LibreOffice. It contains one Text Box.
After the code compiles and successfully saves the PDF to a new file, if I open the newly created document in Acrobat Reader XI, it renders correctly, but, even if I don't modify the final document, upon closing the document, it asks "Do you want to save changes to "filename.pdf" before closing?"
I've read other posts on StackOverflow and their official site (iTextSharp), and found a solution, that maybe I'm implementing in a wrongful manner.
public string spdftemplate = #"C:\test\input.pdf";
public string newFile = #"C:\test\output.pdf";
private void FillFormsProperly()
{
PdfReader reader = new PdfReader(spdftemplate);
byte[] bytes;
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamper = new PdfStamper(reader, ms);
#region ForTesting
//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();
#endregion ForTesting
AcroFields pdfFormFields = stamper.AcroFields;
foreach (DictionaryEntry de in reader.AcroFields.Fields)
{
pdfFormFields.SetField(de.Key.ToString(), "test"); //"Text Box 1"
}
//string sTmp = "W-4 Completed for " + pdfFormFields.GetField("Text Box 1");
//MessageBox.Show(sTmp, "Finished");
//Flush the PdfStamper's buffer
stamper.FormFlattening = true;
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
using (FileStream fs = new FileStream(newFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
fs.Write(bytes, 0, bytes.Length);
}
}
The best answer I found was this : creating a pdf from a template in itextsharp and outputting as content disposition.
The above code is my (copy-paste more or less) implementation.
It's obvious that the file is corrupted (but still readable), how can I fix this ?
Your input.pdf contains a form field and the flag /NeedAppearances true. Your output.pdf does not contain a field anymore (obviously... you flattened the form after all) but it still contains that flag /NeedAppearances true.
This flag tells the PDF viewer (Acrobat Reader) to generate appearance streams for some form fields. Thus, the Reader inspects all fields to create the appearances where necessary. Afterwards it removes the flag. Because of this the document then is changed; even if there are no fields, at least the flag removal is a change.
This reminds of an iText issue which has been fixed in February last year in iText:
In some cases, Adobe Reader X asks if you want to "save changes" after closing a flattened PDF form. This was due to the presence of some unnecessary entries in the /AcroForm dictionary (for instance added when the form was created with OOo).
(iText revision 5089, February 29th, 2012, blowagie)
This change has been ported to iTextSharp in iTextSharp revision 323, March 3rd, 2012, psoares33.
Thus, you might want to update the iTextSharp version you use.

C# iTextsharp Replace Page of a multi-page PDF

Say, I now have a 5-page PDF called 'a.pdf' which page 2 and 4 are empty. And another 2-page PDF called 'b.pdf'. Now what I want is to copy the the first page of 'b.pdf' to page2 of 'a.pdf' and second page of 'b.pdf' to page 4 of 'a.pdf'.
I found it's quite hard to find any examples, what I found is someone provided here,
http://itextsharp.10939.n7.nabble.com/Replace-Pages-with-ItextSharp-td2956.html
Called 'PdfStamper.ReplacePage()', I guess this is what I'm looking for, but I did a simple demo but didn't work out. Can someone have a check for me?
string _outMergeFile = Server.MapPath("~/11/a.pdf");
string file2 = Server.MapPath("~/11/b.pdf");
PdfReader readerA = new PdfReader(_outMergeFile);
PdfReader readerB = new PdfReader(file2);
PdfStamper cc = new PdfStamper(readerA,new MemoryStream());
cc.ReplacePage(readerB, 1, 2);
cc.ReplacePage(readerB, 2, 4);
cc.Close();
Thanks in advance.
=================================================================================
Thanks to Jose's suggestion. The code works now. I'm now providing a simple sample here for others to reference.
public void MyFunction()
{
string _outMergeFile = Server.MapPath("~/11/a.pdf");
string file2 = Server.MapPath("~/11/b.pdf");
PdfReader readerA = new PdfReader(_outMergeFile);
PdfReader readerB = new PdfReader(file2);
PdfStamper cc = new PdfStamper(readerA, new FileStream(Server.MapPath("~/11/result.pdf"), FileMode.Append));
cc.ReplacePage(readerB, 1, 2);
cc.Close();
}
OK, I think I've found your problem. cc is created in memory, and I don't see any code to save the actual changes to the file before you close it, so the alterations made to the in-memory file are lost. One option is to create it with a new FileStream () instead of a memory stream

open byte array from memory stream in new tab or window

This seems to be a common problem but after a lengthy search I have yet to find a solution that fits my needs. I am using itextsharp to fill out a pdf form from which i create a byte array. (There are multiple pages of the same form with the same information that I combine into a single pdf document). I can do this without issues, the problem is when I display the final pdf document to the user I need to open it in a different window or tab WITHOUT first prompting the user to save or open the file. I also do NOT want to have to save a file on the server and reopen it using filestream. If I change the content-disposition to "inline" the pdf is displayed in the same browser window. This creates problems because the user than has to click the browser back button to navigate back to the site which restarts the form submission process. As it currently sits, my code successfully generates the pdf and prompts them to open or save it. This is the step I need to remove. I seem to remember some java snippet that opened the pdf in a new tab or window but I have been unsuccessful in replicating / finding it. I've also played with different headers but I've hit a wall. Any help would be greatly appreciated.
(I call the go method of my code below from a button click after necessary data validation.)
private void go()
{
List<byte[]> pdfs = new List<byte[]>();
while (PageNumber <= Convert.ToInt32(PageCountLabel.Text))
{
pdfs.Add(PopulatePDF());
}
MemoryStream ms = MergePDFs(pdfs);
//opens pdf in new tab after save/open option
Response.AddHeader("Content-Disposition", "attachment; filename=TheDocument.pdf");
Response.ContentType = "application/pdf";
Response.BinaryWrite(ms.ToArray());
Response.End();
}
//************fills in pdf form****************//
private byte[] PopulatePDF()
{
MemoryStream ms = new MemoryStream();
PdfStamper Stamper = null;
PdfReader Reader = new PdfReader(Server.MapPath("~/PDFTemplates/template1.pdf"));
try
{
string temp = Profile.ToString().ToUpper();
PdfCopyFields Copier = new PdfCopyFields(ms);
Copier.AddDocument(Reader);
Copier.Close();
PdfReader docReader = new PdfReader(ms.ToArray());
ms = new MemoryStream();
Stamper = new PdfStamper(docReader, ms);
AcroFields Fields = Stamper.AcroFields;
//fill form fields here
PageNumber++;
Stamper.FormFlattening = true;
}
finally
{
if (Stamper != null)
{
Stamper.Close();
}
}
return ms.ToArray();
}
//combines pdf pages into single document
private MemoryStream MergePDFs(List<byte[]> pdfs)
{
MemoryStream ms = new MemoryStream();
PdfCopyFields Copier = new PdfCopyFields(ms);
foreach (var pdf in pdfs)
Copier.AddDocument(new PdfReader(pdf));
Copier.Close();
return ms;
}
Put your PDF generation code in a new page MyPDFPage.aspx with parameters on the URL, then for example a button on your original page with an onclick event that uses window.open() from javascript:
<html>
<head>
<script>
function open_win()
{
window.open("MyPDFPage.aspx?fileid=0001", "_blank")
}
</script>
</head>
<body>
<input type="button" value="Open Window" onclick="open_win()">
</body>
</html>

put generated pdf file without saving it on the server

I have code (in a .ashx-file) that generates a PDF file from a PDF template. The generated pdf gets personalized with a name and a code. I use iTextSharp to do so.
This is the code:
using (var existingFileStream = new FileStream(fileNameExisting, FileMode.Open))
using (var newFileStream = new FileStream(fileNameNew, FileMode.Create))
{
var pdfReader = new PdfReader(existingFileStream);
var stamper = new PdfStamper(pdfReader, newFileStream);
var form = stamper.AcroFields;
var fieldKeys = form.Fields.Keys;
form.SetField("Name", name);
form.SetField("Code", code);
stamper.FormFlattening = true;
stamper.Close();
pdfReader.Close();
}
context.Response.AppendHeader("content-disposition", "inline; filename=zenith_coupon.pdf");
context.Response.TransmitFile(fileNameNew);
context.Response.ContentType = "application/pdf";
This works, but it saves the file on the server. I don't want to do that because there're going to be a lot of people downloading the PDF file and the server will be full in no time.
So my question is, how can I generate a PDF with iTextSharp without saving it and put it to the user?
Instead of using a FileStream you could use a MemoryStream and then use Response.Write() to output the stream contents.
You can use any Stream (for example MemoryStream) for the intermediate PDF (in your code currently named newFileStream) if you don't want to save it as a file - for sample code see http://www.developerfusion.com/code/6623/dynamically-generating-pdfs-in-net/ and http://forums.asp.net/t/1093198.aspx/1.
Just remember to rewind (i.e. set Position = 0) the MemoryStream before transmitting it to the client (for example by Response.Write or CopyTo (Response.OutputStream) )...

Categories