Itextsharp PDF getting Corrupted - c#

I am working on this feature which takes a pdf from a database and signs the PDF with an image of the signature which needs to be retrieved from the database. I am using iTextSharp for this, but somehow it is not working and is corrupting my PDF in the database.
This is the code of my controller
public ActionResult Approve(int? id)
{
ApplicationUser users = db.Users.Find(User.Identity.GetUserId());
Reports reports = db.Reports.Find(id);
if (reports == null || users == null) return View();
byte[] content = reports.Content;
byte[] signature = users.Signature;
iTextSharp.text.Image sigImg = iTextSharp.text.Image.GetInstance(signature);
PdfReader reader = new PdfReader(content);
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamper = new PdfStamper(reader, ms);
sigImg.SetAbsolutePosition(0f,0f);
sigImg.ScalePercent(90.0f); // 100.0f == same size
//Give some space after the image
sigImg.SpacingAfter = 1f;
sigImg.Alignment = Element.ALIGN_BOTTOM;
PdfContentByte over = stamper.GetOverContent(1);
over.AddImage(sigImg);
reports.Content = ms.ToArray();
content = reports.Content;
ms.Flush();
db.SaveChanges();
if(stamper!= null)
stamper.Close();
if(reader!= null)
reader.Close();
return File(content, "application/pdf");
// Clean up
}
}
What am I doing wrong here?

You retrieve the contents of the MemoryStream
reports.Content = ms.ToArray();
before you close the PdfStamper
if(stamper!= null)
stamper.Close();
which means the MemoryStream does not yet contain the complete stamped PDF.
Thus, change the order of commands, in particular close the stamper before retrieving the bytes from ms.
As an aside: Why do you check stamper!= null? It obviously cannot be null there...

Related

itextsharp merging pdfs an emailing

I am battling with some memorystream logic.
I have a method that receives a list of Id. uses them to pull pdfs from a webserver, and merges them into one pdf.
I want to then email this pdf (working in memory only)
private Stream GetWebReport(string selected_id)
{
var IdLst = selected_id.Split(',').ToList();
MemoryStream stream = new MemoryStream();
Document document = new Document();
PdfCopy pdf = new PdfCopy(document, stream);
PdfReader reader = null;
try
{
document.Open();
foreach (var id in IdLst)
{
int i = Convert.ToInt32(id);
string invoice_url = string.Concat("http://specialurl/", id);
var urlpdf = new System.Net.WebClient().OpenRead(invoice_url);
reader = new PdfReader(urlpdf);
pdf.AddDocument(reader);
reader.Close();
}
}
catch (Exception)
{
throw;
}
finally
{
if (document != null)
{
document.Close();
}
}
return stream;
}
but when I try use the resulting stream for an email
var mem = GetWebReport(selected_id);
mem.Seek(0, SeekOrigin.Begin);
Attachment att = new Attachment(mem, "Report for you", "application/pdf");
I get told:
System.ObjectDisposedException: 'Cannot access a closed Stream.'
So I am sure that my itextsharp logic is good (When I use a filestream I get the correct results).
I am sure that my logic in passing streams is what is faulty
Use
PdfCopy pdf = new PdfCopy(document, stream);
pdf.CloseStream = false;
This will keep the stream open after closing the pdf to be used elsewhere.

How to save PDF using ITextSharp?

I working with PDF annotations using ITextSharp. I was able to add annotations pretty smoothly.
But now I'm trying to edit them. It looks like my PdfReader object is actually updated. But for some reason I can't save it. As shown in the snippet below, I try to get the byte array from using a stamper. The byte array is only 1 byte longer than the previous version no matter how long is the annotation. And when I open the PDF saved on the file system, I still have the old annotation...
private void UpdatePDFAnnotation(string title, string body)
{
byte[] newBuffer;
using (PdfReader pdfReader = new PdfReader(dataBuffer))
{
int pageIndex = 1;
int annotIndex = 0;
PdfDictionary pageDict = pdfReader.GetPageN(pageIndex);
var annots = pageDict.GetAsArray(PdfName.ANNOTS);
if (annots != null)
{
PdfDictionary annot = annots.GetAsDict(annotIndex);
annot.Put(PdfName.T, new PdfString(title));
annot.Put(PdfName.CONTENTS, new PdfString(body));
}
// ********************************
// this line shows the new annotation is in here. Just have to save it somehow !!
var updatedBody = pdfReader.GetPageN(pageIndex).GetAsArray(PdfName.ANNOTS).GetAsDict(0).GetAsString(PdfName.CONTENTS);
Debug.Assert(newBody == updatedBody.ToString(), "Annotation body should be equal");
using (MemoryStream outStream = new MemoryStream())
{
using (PdfStamper stamp = new PdfStamper(pdfReader, outStream, '\0', true))
{
newBuffer = outStream.ToArray();
}
}
}
File.WriteAllBytes( #"Assets\Documents\AnnotedPdf.pdf", newBuffer);
}
Any idea what's wrong with my code?
PdfStamper does much of the writing at the time it is being closed. This implicitly happens at the end of its using block. But you retrieve the MemoryStream contents already in that block. Thus, the PDF is not yet written to the retrieved byte[].
Instead either explicitly close the PdfStamper instance before retrieving the byte[]:
using (PdfStamper stamp = new PdfStamper(pdfReader, outStream, '\0', true))
{
stamp.Close();
newBuffer = outStream.ToArray();
}
or retrieve the byte[] after that using block:
using (PdfStamper stamp = new PdfStamper(pdfReader, outStream, '\0', true))
{
}
newBuffer = outStream.ToArray();
Allright, I finally got it to work. The trick was the two last parameter in the PdfStamper instantiation. I tried it before with only 2 parameters and ended up with a corrupted file. Then I tried again and now it works... here's the snippet
private void UpdatePDFAnnotation(string title, string body)
{
using (PdfReader pdfReader = new PdfReader(dataBuffer))
{
PdfDictionary pageDict = pdfReader.GetPageN(pageIndex);
var annots = pageDict.GetAsArray(PdfName.ANNOTS);
PdfDictionary annot = annots.GetAsDict(annotIndex);
annot.Put(PdfName.T, new PdfString(title));
annot.Put(PdfName.CONTENTS, new PdfString(body));
using (MemoryStream ms = new MemoryStream())
{
PdfStamper stamp = new PdfStamper(pdfReader, ms);
stamp.Dispose();
dataBuffer = ms.ToArray();
}
}
}

how to return binary stream from iText pdf converter

Is it possible to return binary stream (byte[ ]) from pdfstamper ?
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 :
PdfReader reader = new PdfReader(Mydoc.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("[TextReplacement]", "Hello world")));
}
FileStream outStream = new FileStream(dest, FileMode.Create);
PdfStamper stamper = new PdfStamper(reader, outStream);
reader.Close();
return newPDFinStream // this result should be in stream byte[]
Understand that FileStream need to have output filepath like C:\location\new.pdf
is it possible to not temporary save it ? and directly return the binary?
Sure, just save it to a MemoryStream instead:
using (MemoryStream ms = new MemoryStream())
{
// Odd to have a constructor but not use the newly-created object.
// Smacks of the constructor doing too much.
var ignored = new PdfStamper(reader, ms);
return ms.ToArray();
}

itextsharp adding image to existing pdf

i am trying to add an image using itextsharp but not having any luck
there are a ton of tutorials for adding an image to a new pdf doc but not and existing pdf so the .add menthod is not avaivlable
i am tring to do use the stamper write method to add image
and i dont get any errors but no image shows up
PdfReader reader = new PdfReader(pdfIn); //get pdf
if (File.Exists(pdfOut)) File.Delete(pdfOut); //reset
FileStream fs = new FileStream(pdfOut, FileMode.Create);
PdfStamper stamper = new PdfStamper(reader, fs);
try
{
// Convert base64string to bytes array
Byte[] bytes = Convert.FromBase64String(base64decode);
iTextSharp.text.Image sigimage = iTextSharp.text.Image.GetInstance(bytes);//
sigimage.SetAbsolutePosition(10, 10);
sigimage.ScaleToFit(140f, 120f);
stamper.Writer.Add(sigimage);
}catch (DocumentException dex){//log exception here
}catch (IOException ioex){//log exception here
}
AcroFields fields = stamper.AcroFields;
//repeat for each pdf form fill field
fields.SetField("agencyName", name.Value);
stamper.FormFlattening = true; // set to true to lock pdf from being editable
stamper.Writer.CloseStream = true;
stamper.Close();
reader.Close();
fs.Close();
I think you try the following adding it to bytes
PdfReader reader = new PdfReader(pdfIn)
FileStream fs = new FileStream(pdfOut, FileMode.Create);
var stamper = new PdfStamper(reader, fs);
var pdfContentByte = stamper.GetOverContent(1);
iTextSharp.text.Image sigimage = iTextSharp.text.Image.GetInstance(bytes);
sigimage.SetAbsolutePosition(100, 100);
pdfContentByte.AddImage(sigimage);
using following code you can able to add image to each page in an existing pdf file. ( I use this code for desktop application)
string FileLocation = #"C:\\test\\pdfFileName.pdf"; // file path of pdf file
var uri = new Uri(#"pack://application:,,,/projrct_name;component/View/Icons/funnelGreen.png"); // use image from project/application folder (this image will insert to pdf)
var resourceStream = Application.GetResourceStream(uri).Stream;
PdfReader pdfReader = new PdfReader(FileLocation);
PdfStamper stamp = new PdfStamper(pdfReader, new FileStream(FileLocation.Replace(".pdf", "(tempFile).pdf"), FileMode.Create));iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(System.Drawing.Image.FromStream(resourceStream), System.Drawing.Imaging.ImageFormat.Png);
img.SetAbsolutePosition(125, 350); // set the position in the document where you want the watermark to appear.
img.ScalePercent(35f);// not neccessory, use if you want to adjust image
img.ScaleToFit(140f, 100f); // not neccessory, use if you want to adjust image
PdfContentByte waterMark;
for (int page = 1; page <= pdfReader.NumberOfPages; page++) // for loop will add image to each page. Based on the condition you can add image to single page also
{
waterMark = stamp.GetOverContent(page);
waterMark.AddImage(img);
}
stamp.FormFlattening = true;
stamp.Close();// closing the pdfStamper, the order of closing must be important
pdfReader.Close();
File.Delete(FileLocation);
File.Move(FileLocation.Replace(".pdf", "(tempFile).pdf"), FileLocation);

How to flatten already filled out PDF form using iTextSharp

I'm using iTextSharp to merge a number of pdf files together into a single file.
I'm using method described in iTextSharp official tutorials, specifically here, which merges files page by page via PdfWriter and PdfImportedPage.
Turns out some of the files I need to merge are filled out PDF Forms and using this method of merging form data is lost.
I've see several examples of using PdfStamper to fill out forms and flatten them.
What I can't find, is a way to flatten already filled out PDF Form and hopefully merge it with the other files without saving it flattened out version first.
Thanks
Just setting .FormFlattening on PdfStamper wasn't quite enough...I ended up using a PdfReader with byte array of file contents that i used to stamp/flatten the data to get the byte array of that to put in a new PdfReader. Below is how i did it. works great now.
private void AppendPdfFile(FileDTO file, PdfContentByte cb, iTextSharp.text.Document printDocument, PdfWriter iwriter)
{
var reader = new PdfReader(file.FileContents);
if (reader.AcroForm != null)
reader = new PdfReader(FlattenPdfFormToBytes(reader,file.FileID));
AppendFilePages(reader, printDocument, iwriter, cb);
}
private byte[] FlattenPdfFormToBytes(PdfReader reader, Guid fileID)
{
var memStream = new MemoryStream();
var stamper = new PdfStamper(reader, memStream) {FormFlattening = true};
stamper.Close();
return memStream.ToArray();
}
When creating the files to be merged, I changed this setting:
pdfStamper.FormFlattening = true;
Works Great.
I think this problem is same with this one: AcroForm values missing after flattening
Based on the answer, this should do the trick:
pdfStamper.FormFlattening = true;
pdfStamper.AcroFields.GenerateAppearances = true;
This is the same answer as the accepted one but without any unused variables:
private byte[] GetUnEditablePdf(byte[] fileContents)
{
byte[] newFileContents = null;
var reader = new PdfReader(fileContents);
if (reader.AcroForm != null)
newFileContents = FlattenPdfFormToBytes(reader);
else newFileContents = fileContents;
return newFileContents;
}
private byte[] FlattenPdfFormToBytes(PdfReader reader)
{
var memStream = new MemoryStream();
var stamper = new PdfStamper(reader, memStream) { FormFlattening = true };
stamper.Close();
return memStream.ToArray();
}

Categories