-1

I have a W9 PDF document that I am filling with data for just a single record per button click. Now the client would like to create a single document where each record is a page in the document. Below is our code to create a PDF for each employee.

    protected void lnkFillFields_Click(object sender, EventArgs e)
    {
        using (Library.Data.PDFData data = new Library.Data.PDFData())
        {
            try
            {   
                Document document = new Document();
                PdfCopy writer = new PdfCopy(document, Response.OutputStream);
                document.Open();

                foreach (EmployeeData emp in data.sp_select_employee_data())
                {
                    //Creates a PDF from a byte array
                    PdfReader reader =
                        new PdfReader((Byte[])data.sp_select_doc(16).Tables[0].Rows[0]["doc"]);

                    //Creates a "stamper" object used to populate interactive fields
                    MemoryStream ms = new MemoryStream();
                    PdfStamper stamper = new PdfStamper(reader, ms);

                    try
                    {
                        //MUST HAVE HERE BEFORE STREAMING!!!
                        //This line populates the interactive fields with your data.
                        //  false = Keeps the fields as editable
                        //  true  = Turns all of the editable fields to their read-only equivalent
                        stamper.FormFlattening = false;

                        //fill in PDF here

                        stamper.Close();
                        reader.Close();

                        MergePDFs(writer, ms);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }

                }

                document.Close();

                //Stream the file to the user
                Response.ContentType = "application/pdf";
                Response.BufferOutput = true;
                Response.AppendHeader("Content-Disposition", "attachment; filename=W9"+ "_Complete.pdf");
                Response.Flush();
                Response.End();
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
    }

Inserting a page wasn't the way to go. Instead, merging the documents was essentially what we wanted. Therefore, we have come up with this method:

UPDATE Below is the method that we came up with that successfully stitches a new PDF to the previous one.

    private static void MergePDFs(PdfCopy writer, MemoryStream ms)
    {
        PdfReader populated_reader = new PdfReader(ms.ToArray());
        //Add this pdf to the combined writer
        int n = populated_reader.NumberOfPages;
        for (int i = 1; i <= n; i++)
        {
            PdfImportedPage page = writer.GetImportedPage(populated_reader, i);
            writer.AddPage(page);
        }
    }

What we need to do is create all of this in memory, then spit it out to the user for download.

Jim
  • 149
  • 2
  • 11
  • 2
    We are not here to write your code for you. Show us what you have done and then we will attempt to help you. – Andre Lombaard Sep 20 '13 at 23:23
  • I wasn't looking for you to write my code, rather pointing me to an article or tutorial that you have found helpful in your experience. – Jim Sep 23 '13 at 15:41

1 Answers1

1

Check out kuujinbo's tutorial here for combining/stitching PDFs together.

Before you do that, you'll also need to obviously generate the PDFs, too. You might be tempted to try to do it all in one pass which will work but will be harder to debug. Instead, I'd recommend making two passes, the first to create the individual documents and the second to combine them. Your first pass can temporarily writer them to either disk or memory. Your code (and kuujinbo's) actually writes directly to the Response stream which is completely valid, too, but also much harder to debug, especially if you wrap everything in a giant try/catch.

The number of PDFs you're joining and the frequency of generation should determine where you're temporarily storing the first pass two. If you're only doing up to a dozen or two and they're not giant I would persist to a MemoryStream and work with the .ToArray() byte data on that.

If you've got more PDFs than that or they're fairly large or this routine gets called often or you have RAM constraints you might be better persisting them to a unique folder first, stitching them and then deleting that folder.

Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • Chris, thanks. We were able to do it all in one pass, for now. Do you know what utility would be good to use to see how much RAM is being taken up by doing this in a single pass, then comparing that to the second method using a temporary unique folder? – Jim Oct 07 '13 at 18:08
  • See this question which has a couple of paid and free options. http://stackoverflow.com/questions/399847/net-memory-profiling-tools – Chris Haas Oct 08 '13 at 14:16