Friday, July 15, 2011

Sending an FDF response back to a PDF document

One way of getting values out of a form-fillable PDF is to use the 'submitForm' javascript method from within the PDF document, like so:

function saveValues(){
    var arr = [];
    //Gather values into an array
    for( var i = 0 ; i < this.numFields; i++){
        arr[i] = this.getNthFieldName(i);
    }
    this.submitForm({
      cURL: "http://foo.com/saveValues#FDF",
      aFields: arr,
      bEmpty: "true",
      cSubmitAs: "HTML"});
}
Note the '#FDF' on the end of the url. If the PDF is viewed in a browser, this tells the browser that it should expect an FDF file as the response. Without this, the browser will treat the response as a regular web page. In this example, 'submitForm' function sends the values in the array 'arr' to 'http://foo.com/saveValues' as HTML key-value pairs. The function also supports a few other options that you can look up later. Perhaps you are wondering why we want an FDF response instead of some other type of response. The answer, in my case, is that it gives the PDF document in the browser a response without having the browser go to a different page. For example, if you want to save some values that the user has entered in the fields of a PDF and send them to a server, you don't want the browser to go to a different page because the user may still be filling in the fields. An FDF response allows you to save without leaving the page, and you can call a javascript function on the return trip. As an example, here is about the simplest FDF you might need:
%FDF-1.2
1 0 obj
<<
/FDF
<<
/JavaScript << /After (app.alert("Data Imported Successfully");) >>
>>
>>
endobj
trailer
<<
/Root 1 0 R
>>
%%EOF
I found that useful snippet here:

http://acrobatusers.com/forum/javascript/submitform-and-server-response-http-post

The important part to note is the 'app.alert'. On the return trip, this will cause the PDF to display 'Data Imported Successfully' in an alert box. However, notice that this is just a javascript call to the app.alert function. Replacing this with a function that exists in the PDF will allow you to call that function (or functions) on the return, allowing custom functionality:

Javascript in the PDF document:
function saveValues(){
    var arr = [];
    //Gather values into an array
    for( var i = 0 ; i < this.numFields; i++){
        arr[i] = this.getNthFieldName(i);
    }
    this.submitForm({
      cURL: "http://foo.com/saveValues#FDF",
      aFields: arr,
      bEmpty: "true",
      cSubmitAs: "HTML"});
}
function valuesSaved(){
    app.alert("Values were saved");
}
function doSomethingElse(text_1, text_2){
    //Do stuff here
}

FDF text to be returned:
%FDF-1.2
1 0 obj
<<
/FDF
<<
/JavaScript << /After (valuesSaved(); doSomethingElse('customText','moreText');) >>
>>
>>
endobj
trailer
<<
/Root 1 0 R
>>
%%EOF

Cache Problems:
One thing to be aware of is that browsers like to cache all files they download. So, if you are trying to send back dynamically generated FDF files, you should probably tell the browser not to cache it. One option is to add a random number on the end of the url you are sending the values to. For example, adding '?number=12345' to the url in the example could help, where '12345' is a random number that changes for each call to submitForm. Sending headers similar to the following might also help:
headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
headers["Pragma"] = "no-cache"
headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"

7 comments:

  1. Hi. Thanks for the tutorial. I have a small problem, im using a php page instead of the FDF, and I have fdf data in the php file. Everything works fine. The only issue is when I try to submit the data, adobe says 'Some features have been disabled to avoid potential security risks. Only enable these features if you trust this document' and I have to click 'Options' and select 'Trust this document' . Is there a way around this?

    ReplyDelete
    Replies
    1. Hey Udi,

      At the time of my original post, that security warning wasn't there. However, the security settings for Reader are different depending on whether the PDF is in the browser or not. If the PDF is open in Reader (not in the browser), the security settings are more strict. If the PDF is in the browser, the warning doesn't show up for me. However, I would imagine that if you have the PDF in the browser from one domain (foo.com) and you are sending off values to another domain (bar.com), you might get the warning, but I haven't tried it. So, one work-around is to show the PDF in the browser. If there is a work-around for when the PDF is only in Reader, I haven't yet found it.

      Delete
    2. Hi Lee. Thank you very much for the reply. The reason I don't want to open the PDF in the browser, because on mac chrome, when it opens Form PDFs on the browser, the input fields are gone altogether. And the submit button doesn't work either.

      If I use the following script on the submit button. How can I see the data I receive on the FormSubmit.php?

      function saveValues(){
      var arr = [];
      //Gather values into an array
      for( var i = 0 ; i < this.numFields; i++){
      arr[i] = this.getNthFieldName(i);
      }
      this.submitForm({
      cURL: "http://www.website.com/FormSubmit.php",
      aFields: arr,
      bEmpty: "true",
      cSubmitAs: "HTML"});
      }

      Delete
    3. Hey Udi,

      I believe that the issue with Chrome is that it is using a native PDF viewer. It doesn't always work with form elements. However, you can disable it and use the Adobe Reader, instead. If you do end up putting it in the browser, remember to add #FDF on the end of the URL it posts to.

      In answer to your question about the data, the snippet of code you are using sends the data to your PHP script as HTML form fields, which PHP should be able to handle easily. It will appear the same way that fields submitted from an HTML form would appear.
      Example: $_POST["field_name"];

      Delete
    4. Hi Lee, This works fine if I open the PDF on the browser. But on mac, for some reason, the pdf doesnt work on the browser. Mozilla MAC, forces pdf to download, Chrome MAC, throws an error, Safari MAC doesnt do anything when I click 'Submit Form Data' button.

      Delete
    5. I don't have a Mac to test it on, so I can't be of much help there. I can only guess at the reasons why it doesn't work:

      Mozilla forcing a download could be a setting. Try going to the following menus:
      Tools (or the Firefox button) > Options > Applications
      Then look for 'Adobe Acrobat Forms Document' and see what the default action is for it. Change it if needed.

      The issue with Chrome, as outlined in a previous comment, could be that it is trying to render it using the built-in viewer. The built-in viewer can't render all PDFs, and it usually throws an error or doesn't work on PDFs that contain form fields. Switching it to use the Adobe Reader could solve that problem. If switching it to use the Adobe Reader doesn't work, then you'll have to try something else. Perhaps updating Adobe Reader, if you haven't already done that.

      I have no idea what is wrong with Safari.

      Hopefully, something mentioned above helps.

      Delete
  2. Thank you very much for the article. I was after the return values and "FDF text to be returned:" worked perfectly. I didnt use javascript though. Just a text value.

    ReplyDelete