I have the following string of code setting bacground for all pdf fields:
using (var pdfReader = new PdfReader(template))
{
using (var pdfStamper = new PdfStamper(pdfReader, new FileStream(fileName, FileMode.Create)){FormFlattening = true})
{
var fields = pdfStamper.AcroFields;
foreach (var field in fields.Fields)
{
fields.SetFieldProperty(field.Key, "bgcolor", new BaseColor(Color.Red), null);
}
}
}
However, the problem is that background is being set not for all fields, just for some of them. I debugged, and the method is being run for all fields, and returning true every time. Fields are having exact same properties (only name is different). I ran out of ideas, somebody pls help.
Thanks, but after 4 hours trying I figured it out. It's not filling fields having no value. So you have to put at least String.Empty as a value, and background is getting filled after that. May be it will be useful and will save couple of hours for somebody.
If you want to change a field property, after you changed it, you have to regenerate the field for making the edit effective . http://developers.itextpdf.com/question/how-change-text-color-acroform-field
Stream inputPdfStream = new FileStream(BASE_PATH + inputFileName, FileMode.Open, FileAccess.Read, FileShare.None);
using (Stream outputPdfStream = new FileStream(BASE_PATH + outputFileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Document doc = new Document();
doc.Open();
var reader = new PdfReader(inputPdfStream);
var stamper = new PdfStamper(reader, outputPdfStream);
PdfWriter.GetInstance(doc, outputPdfStream);
AcroFields af = stamper.AcroFields;
if (colorInfos != null && colorInfos.Count > 0)
{
af.SetField("color_level_title", colorInfos[""]);
af.SetField("color_level_up_title", colorInfos[""]);
af.SetField("color_level_low_title", colorInfos[""]);
}
af.SetFieldProperty("hide_low_content", "bgcolor", BaseColor.WHITE, null);
af.RegenerateField("hide_low_content");
stamper.FormFlattening = true;
stamper.Close();
}
Related
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);
}
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.
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();
}
}
}
I have tried this now and its not working. form.GenerateAppearances = true; I merge my 2 documents and then save it. Then I open it again to populate all the fields. It says all the Acrofields keys are gone but when I open it in Nitro pro its there. Why can't I see them in code? Do I have to add something before I save?
private static void CombineAndSavePdf1(string savePath, List<string> lstPdfFiles)
{
using (Stream outputPdfStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
Document document = new Document();
PdfSmartCopy copy = new PdfSmartCopy(document, outputPdfStream);
document.Open();
PdfReader reader;
int totalPageCnt;
PdfStamper stamper;
string[] fieldNames;
foreach (string file in lstPdfFiles)
{
reader = new PdfReader(file);
totalPageCnt = reader.NumberOfPages;
for (int pageCnt = 0; pageCnt < totalPageCnt; )
{
//have to create new reader for each page or PdfStamper will throw error
reader = new PdfReader(file);
stamper = new PdfStamper(reader, outputPdfStream);
fieldNames = new string[stamper.AcroFields.Fields.Keys.Count];
stamper.AcroFields.Fields.Keys.CopyTo(fieldNames, 0);
foreach (string name in fieldNames)
{
stamper.AcroFields.RenameField(name, name);
}
copy.AddPage(copy.GetImportedPage(reader, ++pageCnt));
}
copy.FreeReader(reader);
}
}
}
You are merging the documents the wrong way. See MergeForms to find out how to do it correctly. The key line that is missing in your code, is:
copy.setMergeFields();
Without it, the fields disappear (as you have noticed).
There's also a MergeForms2 example that explains how to merge two identical forms. In this case, you need to rename the fields, because each field needs to have a unique name. I'm adding a reference to this second example, because I see that you also try renaming the fields. There is, however, a serious flaw in your code: you create a stamper object, but you never do stamper.close(). Your use of the reader object is also problematic. All in all, it would be best to throw away your code, and to start anew using the two examples from the official iText web site.
Update: I've added the tags itext and itextsharp to your question. Only then I noticed that you're using iTextSharp instead of iText. Porting the Java code to C# should be easy for a C# developer, but I've never written a C# program, so please use the JAVA examples as if you were using pseudo-code. The code in C# won't be that different.
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
Document document = new Document();
PdfCopy copy = new PdfSmartCopy(document, new FileOutputStream(dest));
copy.setMergeFields();
document.open();
List<PdfReader> readers = new ArrayList<PdfReader>();
for (int i = 0; i < 3; ) {
PdfReader reader = new PdfReader(renameFields(src, ++i));
readers.add(reader);
copy.addDocument(reader);
}
document.close();
for (PdfReader reader : readers) {
reader.close();
}
}
public byte[] renameFields(String src, int i) throws IOException, DocumentException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, baos);
AcroFields form = stamper.getAcroFields();
Set<String> keys = new HashSet<String>(form.getFields().keySet());
for (String key : keys) {
form.renameField(key, String.format("%s_%d", key, i));
}
stamper.close();
reader.close();
return baos.toByteArray();
}
I am stuck here. actually I am trying to fill a PDF form using asp.net. I get some help and write the following code:
private void fillForm()
{
try
{
string formFile = Server.MapPath("") + #"\Forms\fw4.pdf";
string savepath = Server.MapPath("") + #"\Forms\new_fw4.pdf";
PdfReader pdfReader = new PdfReader(formFile);
using (FileStream stream = new FileStream(savepath, FileMode.Create))
{
PdfStamper pdfStamper = new PdfStamper(pdfReader, stream);
AcroFields formFields = pdfStamper.AcroFields;
foreach (DictionaryEntry de in formFields.Fields)
{
formFields.SetField("field name", "field value");
}
pdfStamper.FormFlattening = true;
pdfStamper.Close();
}
}
catch
{
}
}
I want the program to show all fields in a List.
I am unable to iterate all available fields using the foreach loop. Its giving me this error:
Cannot convert type System.Collections.Generic.KeyValuePair<string,iTextSharp.text.pdf.AcroFields.Item> to System.Collections.DictionaryEntry
any help would be greatly appreciated.
As you have updated KeyValuePair try using item.Key & item.Value