iTextSharp form filler works ... kinda - c#

I have an XFA enabled PDF document that I would like to fill out programatically. I'm using C# and iTextSharp 5.5.12.0
The document is password protected so I set PdfReader.unethicalreading = true;
The file I'm working with is the N-400.
The filled document has the fields set properly when I view it in Adobe Acrobat Reader. But if I open it in a browser (say IE or Chrome) the fields are empty.
I am opening the file in append mode.
Here's my code (SRC, XML and DEST are strings with full paths to the files).
using (FileStream pdf = new FileStream(SRC, FileMode.Open))
using (FileStream xml = new FileStream(XML, FileMode.Open))
using (FileStream filledPdf = new FileStream(DEST, FileMode.Create))
{
PdfReader.unethicalreading = true;
PdfReader pdfReader = new PdfReader(pdf);
pdfReader.RemoveUsageRights();
PdfStamper stamper = new PdfStamper(pdfReader, filledPdf, '\0', true);
string[] fields = stamper.AcroFields.Fields.Select(x => x.Key).ToArray();
for (int key = 0; key <= fields.Count() - 1; key++)
{
stamper.AcroFields.SetFieldProperty(fields[key], "setfflags", PdfFormField.FF_READ_ONLY, null);
}
stamper.Writer.CloseStream = false;
stamper.AcroFields.Xfa.FillXfaForm(xml);
stamper.Close();
pdfReader.Close();
}

Related

The userPassword is invalid after RemoveField when itextSharp5.5.10 is used

Here is a PDF file that requires a password to open, and I added an electronic signature to it. A password is still required to open the file after this. But after I use AcroFields.RemoveField to remove the electronic signature, a password is no longer required to open the file. Is this normal? How can I keep the password when opening the PDF file?
test pdf
https://github.com/IYinxf/PDFs/blob/master/Encrypted.pdf
password is 11111111
code
pdfReader = new PdfReader(strTempPath, Encoding.ASCII.GetBytes(strPassword));
if (!pdfReader.IsOpenedWithFullPermissions)
{
return ERR_PERMISSION_DENIED;
}
AcroFields af = pdfReader.AcroFields;
bool rv = af.RemoveField(fieldName);
According to the code responsible for keeping encryption information
if (reader.IsEncrypted() && (append || PdfReader.unethicalreading)) {
crypto = new PdfEncryption(reader.Decrypt);
}
(PdfStamperImp constructor)
this only happens if you are stamping in append mode or if the unethicalreading flag is set.
When testing your code in append mode, it turns out that the field is not removed. This is caused by the AcroFields field removal code not properly marking the correct updated objects in your PDF as used. When you do the marking manually, it works fine:
using (var pdfReader = new PdfReader(file, Encoding.ASCII.GetBytes(strPassword)))
using (FileStream output = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, output, '\0', true))
{
AcroFields af = pdfReader.AcroFields;
bool rv = af.RemoveField(fieldName);
pdfStamper.MarkUsed(pdfReader.Catalog);
for (int pageNo = 1; pageNo <= pdfReader.NumberOfPages; pageNo++)
{
pdfStamper.MarkUsed(pdfReader.GetPageN(pageNo));
}
}
Testing with the unethicalreading flag set to true works out-of-the-box:
PdfReader.unethicalreading = true;
using (var pdfReader = new PdfReader(file, Encoding.ASCII.GetBytes(strPassword)))
using (FileStream output = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
using (PdfStamper pdfStamper = new PdfStamper(pdfReader, output))
{
AcroFields af = pdfReader.AcroFields;
bool rv = af.RemoveField(fieldName);
}

The document has no catalog object (meaning: it's an invalid PDF)

I am reading and writing to the same PDF at the same time i am getting error "The document has no catalog object (meaning: it's an invalid PDF)" on this line "PdfReader pdfReader = new PdfReader(inputPdf2);" in the below code snippet.
iTextSharp.text.pdf.PdfCopy pdfCopy = null;
Document finalPDF = new Document();
//pdfReader = null;
FileStream fileStream = null;
int pageCount = 1;
int TotalPages = 20;
try
{
fileStream = new FileStream(finalPDFFile, FileMode.OpenOrCreate, FileAccess.Write);
pdfCopy = new PdfCopy(finalPDF, fileStream);
finalPDF.Open();
foreach (string inputPdf1 in inputPDFFiles)
{
if (File.Exists(inputPdf1))
{
var bytes = File.ReadAllBytes(inputPdf1);
PdfReader pdfReader = new PdfReader(bytes);
fileStream = new FileStream(inputPdf1, FileMode.Open, FileAccess.Write);
var stamper = new PdfStamper(pdfReader, fileStream);
var acroFields = stamper.AcroFields;
stamper.AcroFields.SetField(acrofiled.Key, "Page " + 1+ " of " + 16);
stamper.FormFlattening = true;
stamper.Close();
stamper.Dispose();
fileStream.Close();
fileStream.Dispose();
pdfReader.Close();
pdfReader.Dispose();
}
}
foreach (string inputPdf2 in inputPDFFiles)
{
if (File.Exists(inputPdf2))
{
PdfReader pdfReader = new PdfReader(inputPdf2);
int pageNumbers = pdfReader.NumberOfPages;
for (int pages = 1; pages <= pageNumbers; pages++)
{
PdfImportedPage page = pdfCopy.GetImportedPage(pdfReader, pages);
PdfCopy.PageStamp pageStamp = pdfCopy.CreatePageStamp(page);
pdfCopy.AddPage(page);
}
pdfReader.Close();
pdfReader.Dispose();
}
}
pdfCopy.Close();
pdfCopy.Dispose();
finalPDF.Close();
finalPDF.Dispose();
fileStream.Close();
fileStream.Dispose();
please help me in order to fix issue or give me any alternate approach
In your first loop you overwrite each of your files with a manipulated version like this:
var bytes = File.ReadAllBytes(inputPdf1);
PdfReader pdfReader = new PdfReader(bytes);
fileStream = new FileStream(inputPdf1, FileMode.Open, FileAccess.Write);
var stamper = new PdfStamper(pdfReader, fileStream);
[...]
Using FileMode.Open here is an error. You want to replace the existing file with a new one, and for such a use case you have to use FileMode.Create or FileMode.Truncate.
Using FileMode.Open results in the original file content remaining there and you writing into it. Thus, if your new file content is shorter than the original one (which can happen when flattening a form), your new file keeps a tail segment of the original file. In PDFs there are relevant lookup information at the end, so upon reading this new file the PdfReader finds the lookup information of the old file which don't match the new content anymore at all.
By the way, you create the PdfCopy like this:
fileStream = new FileStream(finalPDFFile, FileMode.OpenOrCreate, FileAccess.Write);
pdfCopy = new PdfCopy(finalPDF, fileStream);
This is wrong for the same reason: If there already is PDF there, FileMode.OpenOrCreate works just like FileMode.Open with the unwanted effects described above.
Thus, you should replace the FileMode values for streams you write to with FileMode.Create.

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

Copying PDF without loose form field structure with ItextSharp

I would like to get a pdf, keep somes pages, then save it to another destination without losing fieldstructure.
Here the code perfectly working for copying:
string sourceFolder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string sourceFile = Path.Combine(sourceFolder, "POMultiple.pdf");
string fileName = #"C:\Users\MyUser\Desktop\POMultiple.pdf";
byte[] file = System.IO.File.ReadAllBytes(fileName);
public static void removePagesFromPdf(byte[] sourceFile, String destinationFile, params int[] pagesToKeep)
{
//Used to pull individual pages from our source
PdfReader r = new PdfReader(sourceFile);
//Create our destination file
using (FileStream fs = new FileStream(destinationFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
using (Document doc = new Document())
{
PdfWriter writer = PdfWriter.GetInstance(doc, fs);
//Open the desitination for writing
doc.Open();
//Loop through each page that we want to keep
foreach (int page in pagesToKeep)
{
//Add a new blank page to destination document
doc.NewPage();
//Extract the given page from our reader and add it directly to the destination PDF
writer.DirectContent.AddTemplate(writer.GetImportedPage(r, page), 0, 0);
}
//Close our document
doc.Close();
}
}
}
But when I open "TestOutput.pdf" file in acrobat reader all my fields are empty.
Any Help ?
You need something like this:
PdfReader reader = new PdfReader(sourceFile);
reader.SelectPages(2-4,8-9);
PdfStamper stp = new PdfStamper(reader, new FileStream(destinationFile, FileMode.Create));
stp.Close();
reader.Close();

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

Categories