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"

Fixing a warning: deprecated conversion from string constant to 'char*'

When using C++, you may get the warning "deprecated conversion from string constant to 'char*'" when compiling something similar to the following:

void foo(char *){}
...
foo("hi");

I have found three options to overcome this problem:

1. Add the 'const' keyword to the function definition:
void foo(const char *){}

2. Cast the string when calling the function:
foo((char *)"hi");

3. Create a char array to pass to the function:
char message[] = "hi";
foo(message);

Each might work better than the other 2 for a given situation. I leave it up to you to figure out which is best for your situation.

Running a Qt application without the IDE on Windows

When writing a Qt application, you may find yourself at the point where you need to run the program without the IDE. If you have the paid-for version, that isn't so hard, as everything should be compiled into the executable. But if you are using the LGPL version, your executable needs to have the Qt dlls close at hand, either in the same folder or in your path. I found this forum to be useful:

http://www.qtforum.org/article/24337/error-running-qt-application.html

To make things complicated, there are actually 2 sets of dlls with the same name. tymek's post was what helped me: you have to get the dlls from the \qt\bin folder, not the \bin folder. I believe one set is for debug versions, and one set is for release versions of your program, but I could be wrong.