ASP.NET 2.0 Resources

Powered by Blogger

Filling and processing Adobe PDF Forms with iTextSharp

As i have blogged before, iTextSharp is a .NET port of iText Java-PDF library. Current version of iTextSharp 3.1.5 (2006-09-14) is based on iText 1.4.5 and among other cool things allows you to work with Adobe's FDF (Form Data Format) data. FDF is used by PDF forms created with the form tools in Adobe Acrobat. These PDF forms can be displayed and filled by users directly in web browser and then submited back to the server for further processing - for example flatten them to downloadable PDF file. This communication between client and server uses Adobe FDF protocol.

With iTextSharp you can easily create FDF file to prefill values in the form and also to process form submitted data back on the server. I am currently using this technique in one application, so i decided to drop a little demo project here. In this application advisors fill client's contract data directly to Adobe PDF form and submitting them back to system. The contract PDF form is prefilled with date, advisor's name, address and other data. When the advisor submits this form back to server, it's processed by ASP.NET, stored to database and finally flattened to classic downloadable PDF file.

This demo project is build for ASP.NET 2.0 and you will need iTextSharp library for it to work. It's made as simple as possible, and you can download PDFFormDemo.zip right now. It shows the basic steps to create FDF stream with prefilled data and to catch form submit back on the server.

The first file Default.aspx is used as a simple ASP.NET form to enter some user data (in this example it's first and last name). The form contains two simple text boxes and one submit button. Let's take a look at the submit button's onclick event...

   1:  private readonly string pdfFormFileName = "PDFForm.pdf";
   2:   
   3:  protected void OpenPDF_Click(object sender, EventArgs e)
   4:  {
   5:      Response.Clear();
   6:      Response.ContentType = "application/vnd.fdf";
   7:      
   8:      FdfWriter fdfWriter = new FdfWriter();
   9:   
  10:      fdfWriter.File = GetAbsolutePath() + pdfFormFileName;
  11:   
  12:      fdfWriter.SetFieldAsName("txtFirstName", FirstName.Text);
  13:      fdfWriter.SetFieldAsName("txtLastName", LastName.Text);
  14:   
  15:      Response.AddHeader("Content-disposition", "inline; filename=FlatPDFForm.fdf");        
  16:      fdfWriter.WriteTo(Response.OutputStream);        
  17:      Response.End();
  18:  }

This one is quite simple, we change response content type to application/vnd.fdf and use FdfWriter class to create FDF stream. First and Last name are stored to this stream (txtFirstName and txtLastName are the names of Adobe PDF Form fields). The File property of FdfWriter class associates this FDF file with our PDF form (PDFForm.pdf) and this file is opened in the client's browser (the path is abolute Url location to PDF file). We also set content disposition to open this file inline in the browser.

After this Onclick handler executes, PDF file opens in your browser. You now have prefilled values for FirstName and LastName in the PDF form and you can further change them so as to fill other form fields. After you click the form's submit button the data is submitted as FDF stream to the server (to the location hardcoded in PDF file - in our example ProcessFDF.ashx). The ProcessFDF.ashx file is HTTP handler file used to process the submitted data. Let's look at it's ProcessRequest method.

   1:  private readonly string pdfFormFileName = "PDFForm_print.pdf";    
   2:   
   3:  public void ProcessRequest (HttpContext context) {
   4:      string contentType = context.Request.ContentType;
   5:   
   6:      if (String.Compare(contentType, "application/vnd.fdf", true) == 0)
   7:      {
   8:          ProcessFDFRequestFlattenPDF(context);
   9:      }
  10:      else
  11:      {
  12:          context.Response.ContentType = "text/plain";
  13:          context.Response.Write("Not a FDF request");
  14:      }
  15:  }
  16:   
  17:  private void ProcessFDFRequestFlattenPDF(HttpContext context)
  18:  {
  19:      MemoryStream pdfFlat = new MemoryStream();
  20:   
  21:      PdfReader pdfReader = new PdfReader(context.Request.MapPath(pdfFormFileName));
  22:      PdfStamper pdfStamper = new PdfStamper(pdfReader, pdfFlat);
  23:   
  24:      // bind fields from fdf...
  25:      FdfReader fdfReader = new FdfReader(context.Request.InputStream);
  26:   
  27:      AcroFields pdfForm = pdfStamper.AcroFields;
  28:      pdfForm.SetFields(fdfReader);
  29:      
  30:      pdfStamper.FormFlattening = true;
  31:      pdfStamper.Writer.CloseStream = false;
  32:      pdfStamper.Close();
  33:   
  34:      context.Response.ContentType = "application/pdf";
  35:      context.Response.AddHeader("Content-disposition", "attachment; filename=FlatPDFForm.pdf");
  36:      
  37:      pdfFlat.WriteTo(context.Response.OutputStream);
  38:      pdfFlat.Close();
  39:  }

The ProcessRequest method tests for correct submited content type and if it's set correctly to application/vnd.fdf processes this request. We just flatten this FDF stream with our PDF form file and send the flattened PDF file back to the client's browser. You may have noticed we are using another PDF form file here PDFForm_print.pdf. This file is basically the same as the one in previous step, only without TextBox black borders. The logic is quite straightforward, PdfReader is used to read PDF form file and PdfStamper created for this file. The stampers FormField collection is set to our submited FDF request and finally the PDF Form and FDF stream are merged together in one noneditable PDF file. This file is streamed back to the client's browser this time as an attachment.

As you can see this is quite easy. This technique works fine with Adobe Acrobat generated PDF Forms. There are some problems with iTextSharp and Adobe Distiler forms, because it generates some weird tags. This was only a demo, so please keep in mind real world application would need some additional erro checking and stuff like that.

7 Comments:

  • This is great. But as is the ouput pdf is a copy of the input pdf except flattened. 2 question:

    1. How can I generate a pdf that allows me to pass in some client details (held in variables)

    2. Then automatically generate a PDF (as a pre-designe certifcate with the clients details)

    Cheers

    Al

    By Blogger Al, at 10:56 PM  

  • Where can i find some documentation about this library?

    By Blogger Taras, at 1:33 PM  

  • This post has been removed by the author.

    By Blogger Azeem, at 8:39 PM  

  • Documentation is availabe at

    http://sourceforge.net/project/showfiles.php?group_id=72954

    By Blogger Azeem, at 8:39 PM  

  • Nice article... really help me in a project!

    Question: You said that there are some problems with iTextSharp and Adobe Distiller forms, because of some weird tags. What kind of additional error checking should be done? What kind of errors should I be looking for and where should I be looking?

    By Blogger Kyle, at 4:35 AM  

  • Really nice job, but I tried to do a PDF form with Acrobat Professional 7 and I can't fill it with this script!
    WHY?

    By Blogger Gianmario, at 9:37 PM  

  • I know this question comes a couple of years after the fact, but I'm hoping... I have downloaded your demo and it works great. I am integrating this design into my application and have hit a few roadblocks. One I was able to solve, two others not so much.

    1. I have a hyperlink on an .aspx page that takes me to a page whose Page_Load fills the form. Adobe kept throwing an error about the file not being found. Since your solution uses a button that performs a postback, I changed my code to match that. First hurdle hurdled! I'm not clear why that matters, but I can live with that.

    2. I can now process the form, and can call SetFieldAsName with no errors. The PDF is streamed to the browser, but there is no data in the fields I set. Any ideas?

    3. I can hit Submit button in form and get directed back to my HttpHandler, but the Request.ContentType is "application/pdf", not "...vnd.fdf". Did you create your document with some property that I haven't? What determines on the inbound trip that the form should be processed as an fdf not pdf? I'm working with a PDF form that was given to me, but I have Adobe Pro 9.0 and LC Designer.

    What's a good resource (even if I have to buy a book) for the ins-and-outs of FDF?

    Again, hoping, and many thanks for your example,
    Ray

    By Blogger hermeticpiper, at 4:39 PM  

Post a Comment

<< Home

Created dolly