evapisces evapisces - 1 year ago 105
C# Question

iTextSharp - Fill out PDF mulitple times and merge into one pdf

I have a process that accepts data from an HTML page, and stamps that data onto a PDF and saves the PDF to a 3rd party imaging system.

The HTML page accepts info for up to 8 users. However, the PDF only has room for up to 4 users. So if info for more than 4 is entered, I need to fill out the PDF twice and merge those two PDFs into one file to submit to the imaging system.

I've tried nesting streams, pdfReaders, pdfStampers, but the resulting PDF is always corrupt. Any help would be greatly appreciated. Thanks in advance.

Here's the code I have currently that handles stamping the PDF if there are 1 - 4 users entered:

var pdfTemplate = ConfigurationManager.AppSettings["PdfFileLocation"] + formName + ".pdf";
var pdfReader = new PdfReader(pdfTemplate);

using (var stream = new MemoryStream())
{
using (var pdfStamper = form == null
? new PdfStamper(pdfReader, stream, '\0', false)
: processType == 1 // printing form
? new PdfStamper(pdfReader, new FileStream(Path.GetTempPath() + "UserForm_" + UserNumber + ".pdf", FileMode.Create)) // temporarily save form
: new PdfStamper(pdfReader, stream, '\0', false)) // use a stream if submitting
{
var overContent1 = pdfStamper.GetOverContent(1);
var overContent2 = pdfStamper.GetOverContent(2);
var overContent3 = pdfStamper.GetOverContent(3);
var overContent4 = pdfStamper.GetOverContent(4);
var pdfFormFields = pdfStamper.AcroFields;

... Code to fill out other pages of the PDF goes here ...

#region User Form Page #3

foreach (var user in users)
{
var eligibility = _sharedFormService.GetEligibility(user.Eligibility);
pdfFormFields.SetField("User" + nameof(user.Name) + user.Id, user.Name);
pdfFormFields.SetField("User" + nameof(user.Eligibility) + user.Id, eligibility);
pdfFormFields.SetField("User" + nameof(user.Title) + user.Id, user.Title);
pdfFormFields.SetField("User" + nameof(user.Type) + user.Id, user.Type);
pdfFormFields.SetField("User" + nameof(user.FamilyMemberName) + user.Id, user.FamilyMemberName);

try
{
if (user.Signature != null)
{
var sigImage = _imageHelpers.LoadImage(user.Signature);
var image = Image.GetInstance(sigImage, System.Drawing.Imaging.ImageFormat.Gif);
var sigPostitions = pdfFormFields.GetFieldPositions("User" + nameof(user.Signature) + user.Id)[0].position;

image.Transparency = new int[] { 255, 255 };
image.SetAbsolutePosition(sigPostitions.Left, sigPostitions.Bottom);
image.ScalePercent(15);

overContent3.AddImage(image);
}
}
catch (DocumentException dex)
{
response.Success = false;
response.ErrorMessage += "StampPdf - UserSignature" + user.Id + ": " + (dex.InnerException != null
? dex.InnerException.Message
: dex.Message) + ", ";
}
catch (IOException ioex)
{
response.Success = false;
response.ErrorMessage += "StampPdf - UserSignature" + user.Id + ": " + (ioex.InnerException != null
? ioex.InnerException.Message
: ioex.Message) + ", ";
}
}

#endregion

pdfStamper.FormFlattening = true;
}

response.Stream = stream.ToArray();
}

Answer Source

After a few more days of Googling, I finally found the answer I was looking for from the iText FAQ Site, under "Merging identical forms (having identical fields)" (http://developers.itextpdf.com/question/how-merge-forms-different-files-one-pdf)

Here's the code I ended up using:

    public static byte[] ManipulatePdf(string src, string dest)
    {
        var ms = new MemoryStream();
        Document document = new Document();
        PdfCopy copy = new PdfSmartCopy(document, ms);
        copy.SetMergeFields();
        document.Open();
        List<PdfReader> readers = new List<PdfReader>();
        for (int i = 0; i < 2;)
        {
            PdfReader reader = new PdfReader(RenameFields(src, ++i));
            readers.Add(reader);
            copy.AddDocument(reader);
        }
        document.Close();
        foreach (var reader in readers)
        {
            reader.Close();
        }

        return ms.ToArray();
    }

    public static byte[] RenameFields(String src, int i)
    {
        MemoryStream baos = new MemoryStream();
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader, baos);
        AcroFields form = stamper.AcroFields;
        var keys = new HashSet<string>(form.Fields.Keys);
        foreach (var key in keys)
        {
            form.RenameField(key, string.Format("{0}_{1}", key, i));
        }
        stamper.Close();
        reader.Close();
        return baos.ToArray();
    }
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download