Reputation: 2615
I have a PDF document with 3 Fields txt_FirstName
, txt_MiddleName
and txt_LastName
that I write into using iTextSharp.
I have a loop that creates the output file, writes to it, and closes the file.
The first time in the loop the file writes the first name and the middle name.
The second time in the loop the file should have the first name, middle name, and write the last name.
Issue: The problem is, when it goes to the loop the 2nd time around and writes the lastname the first name, and middle names disappear.
Goal: The main thing I want to do is write to the same PDF documents multiple times
Download PDF template: https://www.scribd.com/document/412586469/Testing-Doc
public static string templatePath = "C:\\temp\\template.pdf";
public static string OutputPath = "C:\\Output\\";
private static void Fill_PDF()
{
string outputFile = "output.pdf";
int counter = 1;
for (int i = 0; i < 2; i++)
{
PdfStamper pdfStamper;
PdfReader reader;
reader = new PdfReader(File.ReadAllBytes(templatePath));
PdfReader.unethicalreading = true;
if (File.Exists(OutputPath + outputFile))
{
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Append, FileAccess.Write));
}
else
{
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Create));
}
AcroFields pdfFormFields = pdfStamper.AcroFields;
if (counter == 1)
{
pdfFormFields.SetField("txt_FirstName", "Scooby");
pdfFormFields.SetField("txt_MiddleName", "Dooby");
counter++;
}
else if (counter == 2)
{
pdfFormFields.SetField("txt_LastName", "Doo");
}
pdfStamper.Close();
}
}
Upvotes: 3
Views: 1554
Reputation: 330
The problem is with using the template file again for the second iteration.
First iteration: works fine as expected!
Second iteration: you are reading the same file and writing only the last name. Finally, the output file created in the first iteration is being replaced.
Fix: After knowing if the output file exists in the location, choose the file source to read like below. This should fix the problem. Checked it personally and it worked!
if (File.Exists(OutputPath + outputFile))
{
reader = new PdfReader(File.ReadAllBytes(OutputPath + outputFile));
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Append, FileAccess.Write));
}
else
{
reader = new PdfReader(File.ReadAllBytes(templatePath));
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Create));
}
Upvotes: -1
Reputation: 95918
There are two major issues with the code. @Nick in his answer already pointed out the first: If in your second pass you want to edit a version of your document containing the changes from the first pass, you have to take the output document of the first pass as input of the second pass, not again the original template. He also presented code that fixed this issue.
The second issue is located here:
if (File.Exists(OutputPath + outputFile))
{
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Append, FileAccess.Write));
}
else
{
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Create));
}
If the output file already exists, you append the output of your PdfStamper
to it. This is wrong! The output of the PdfStamper
already contains the contents of the original PDF (from the PdfReader
) as far as they have not being changed. Thus, your code effectively produces a concatenation of the complete output PDF of the first pass and the complete output PDF of the second pass.
PDF is a binary format for which concatenating files like that does not result in a valid PDF file. Thus, a PDF viewer loading your final result tries to repair this double PDF assuming it is a single one. The result may or may not look like you want.
To fix the second issue, simply replace the if{...}else{...}
above by the contents of the else
branch only:
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Create));
(FileMode.Create
is defined as
Specifies that the operating system should create a new file. If the file already exists, it will be overwritten. This requires
Write
permission.FileMode.Create
is equivalent to requesting that if the file does not exist, useCreateNew
; otherwise, useTruncate
. If the file already exists but is a hidden file, anUnauthorizedAccessException
exception is thrown.
Thus, it will also do the required if there already is a file.)
You can recognize the problems of the code with the Append
in it by running it a few times and watch the output file grow and grow beyond need. Furthermore, if you open that file in Adobe Reader and close again, Adobe Reader offers to save the changes; the changes are the repair work.
You may have heard about incremental updates of PDFs where changes are appended to the original PDF. But this is different from a mere concatenation, the revisions in the result are linked specially and the offsets are always calculated from the start of the first revision, not from the start of the current revision. Furthermore, incremental updates should only contain changed objects.
iText contains a PdfStamper
constructor with 4 parameters, including a final boolean parameter append
. Using that constructor and setting append
to true
makes iText creates incremental updates. But even here you don't use FileMode.Append
...
Upvotes: 1
Reputation: 506
This seems like a straightforward bug. The first time through the loop, you load up the blank template and write the first and middle name. The second time through the loop, you load up the blank template again and write only the last name to it, then save to the same filename, overwriting it. If, during the second time through the loop, you want to load the file that already contains the first and middle name, you have to load up the output file you wrote the first time around, not the blank template again. Or if you want to load the blank template again, inside your if (counter == 2)
clause, you're going to have to write all 3 names, not just the last name.
I reproduced your bug, and got it working. Here's the code to the first solution I described (minor modification of your code):
public static string templatePath = "C:\\temp\\template.pdf";
public static string OutputPath = "C:\\temp\\output\\";
private static void Fill_PDF()
{
string outputFile = "output.pdf";
int counter = 1;
for (int i = 0; i < 2; i++)
{
PdfStamper pdfStamper;
PdfReader reader = null;
/********** here's the changed part */
if (counter == 1)
{
reader = new PdfReader(File.ReadAllBytes(templatePath));
} else if (counter == 2)
{
reader = new PdfReader(File.ReadAllBytes(OutputPath + outputFile));
}
/************ end changed part */
PdfReader.unethicalreading = true;
if (File.Exists(OutputPath + outputFile))
{
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Append, FileAccess.Write));
}
else
{
pdfStamper = new PdfStamper(reader, new FileStream(OutputPath + outputFile,
FileMode.Create));
}
AcroFields pdfFormFields = pdfStamper.AcroFields;
if (counter == 1)
{
pdfFormFields.SetField("txt_FirstName", "Scooby");
pdfFormFields.SetField("txt_MiddleName", "Dooby");
counter++;
}
else if (counter == 2)
{
pdfFormFields.SetField("txt_LastName", "Doo");
}
pdfStamper.Close();
}
}
Upvotes: 6