Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

'AcroForm Objects' Category Archive

XFA and AcroForm Event Object Access

XFA forms are event-driven. Pretty much every action triggers an event and in that event (e.g. Initialize, Change, Click, etc.), you always have access to an xfa.event object (see the “eventPseudoModel” topic on page 63 of the Designer 8.2 Scripting Reference) and, depending on whether your form is running in Acrobat/Reader, you will also have access to an AcroForm event object.

I have discovered that in that event isn’t quite accurate. It should rather be stated as within the context of that event. That’s because any function calls that you make from the event handler (for instance, into a script object that contains other functions — maybe your event handler simply passes execution to a re-usable event handler function that you have defined in this script object) will have access to these very useful objects.

In essence, xfa.event and event (if defined) are, for lack of a better term, contextual objects that exist — and are valid — as long as you are within the context (where context is different from usual definition of scope) of an event handler.

I had a case, just recently, where I needed to access the event.target property (that is, the AcroForm Doc object provided by Acrobat when it’s the host) in a function located inside a script object which I was calling from a subform’s Initialize event. At first, I thought I needed to pass it in as a parameter:

// Subform Initialize Event handler code
utils.InitSubform(this, xfa.host.name == "Acrobat" ? event : null);

-----
utils Script Object
-----

function InitSubform(sf, hostEvent)
{
    ...
    if (hostEvent != null)
    {
        // access AcroForm Doc object from parameter
        var pdfDoc = hostEvent.target;
        ...
    }
    ...
}

Unfortunately, this would’ve required updates to many forms since the script object was being used as a script object fragment and many forms were already using that function with only the first parameter.

Since the event object is actually available at that point in time, given that the handler is executing in the context of the subform’s Initialize event, I didn’t have to pass it in as a parameter and so I didn’t have to update a bunch of forms:

// Subform Initialize Event handler code
utils.InitSubform(this);

-----
utils Script Object
-----

function InitSubform(sf)
{
    ...
    if (xfa.host.name == "Acrobat")
    {
        // access AcroForm Doc object from in-context event object
        var pdfDoc = event.target
        ...
    }
    ...
}

Just remember that the xfa.event and event objects are only valid for the duration of the event which was triggered (so you shouldn’t hold on to them for future use) and that the event object is only available to you if the host is Acrobat/Reader (testing for xfa.host.name == “Acrobat” works for both Acrobat and Reader).

Tested with Adobe Acrobat and Reader 9.0.


Posted by Stefan Cameron on January 14th, 2009
Filed under AcroForm Objects,Events,Scripting

Call Script Object Functions Later

I hope you all had a great holiday! There’s suddenly lots of activity on my blog today so I assume most of you are back at it too.

To kick things off in 2009, I thought I would start with a short but significant post on calling script object functions at a later time using Acrobat’s app.setTimeOut JavaScript API. (app.setInterval is similar but requires a little more handling, to ensure you don’t leave the timer running when the form is closed, so I won’t cover that here. Check-out this sample if you’re curious.)

I had long thought this wasn’t possible but recently discovered that it was, while helping-out one of my readers, in Acrobat/Reader 9.

Theory

The app.setTimeOut API requires that the first parameter be a string which is the script to be evaluated and executed when the timer expires. The second parameter is the number of milliseconds the host application (Acrobat) should wait before interpreting the script in the context of the document which set the timer. This means that the “this” object is an AcroForm Doc object.

Given that an AcroForm Doc object has an xfa property (“defined only if the document is an XML form, that is, if the document was created in LiveCycle Designer“) which is “the root node of the underlying XFA model,” it follows that you can access properties of the XFA form contained within the PDF document from the AcroForm API. After all, that’s how I managed to get invalid fields to flash red in a previous sample.

By the way, if you’re writing AcroForm scripts and you want to verify whether the current document is an XFA-PDF (a PDF that contains an XFA form) or just a straight PDF document, you can simply test for (typeof pdfDoc.xfa == “undefined”). If it’s true, it means you just have a plain PDF.

When you put all of this together, it follows that you can effectively execute a script object function “later” as long as you address it properly using normal SOM expression rules. Since script objects are loaded into the Form DOM at runtime, you can access them via the xfa.form property. Once you’ve accessed the Form DOM, the first object you’ll need to reference is your root subform (the object at the very top of the tree, usually named “form1”, in Designer’s Hierarchy palette) and from there, your script object.

Note that the AcroForm app object is only available when the host application running the PDF is Acrobat/Reader so this won’t work if you render your form as HTML using LiveCycle Forms ES, for example.

Sample

If you had a function foo() in a script object “MyScripts” defined on the root subform “form1”, you would access it like this:

xfa.form.form1.MyScripts.foo();

If you didn’t know (or didn’t want to know) the name of the root subform ahead of time (say this function is accessed from a fragment which is used in many different forms), you could refer to it like this:

xfa.form.resolveNode("#subform[0]").MyScripts.foo();

Finally, to call it 1 second later, you would simply do this:

app.setTimeOut('xfa.form.resolveNode("#subform[0]").MyScripts.foo();', 1000);

Minimum Requirements: I’ve only tried this with Acrobat/Reader 9 however the JavaScript for Acrobat 9 Reference claims that the xfa property is available since Acrobat/Reader 6.0.2. I’ll leave it up to you to try it out in previous versions. Please report back if you find that it works prior to Acrobat/Reader 9.

Updated: January 5, 2009


Posted by Stefan Cameron on January 5th, 2009
Filed under AcroForm Objects,Scripting

No JavaScript for Acrobat 9 PDF Document

For those of you still waiting for a PDF version of the JavaScript for Acrobat 9 API documentation, I have some unfortunate news: I have learned that there will be no PDF version issued. The only documentation available will be the LiveDocs version which I reported on earlier.

I think that’s really unfortunate since PDFs (and CHMs) tend to be faster (no need to wait for page loads every time you select a topic), better indexed, easier to search and available offline. What’s your preference?


Posted by Stefan Cameron on December 8th, 2008
Filed under Acrobat,AcroForm Objects,Scripting

LiveDocs JavaScript API for Acrobat 9

If you’re like me, you’re still waiting for the PDF version to be posted since Acrobat 9 shipped last Summer (seems strange to say “last Summer” already!). In the mean time, I found the LiveDocs version online (once the page loads, click on “JavaScript > JavaScript for Acrobat API Reference > JavaScript API” in the table of contents on the left hand side). It’s not as “rich” as the PDF version but it’ll do for now.


Posted by Stefan Cameron on September 29th, 2008
Filed under Acrobat,AcroForm Objects,Scripting

Using URL Requests in PDF Forms

Here’s a sample in response to Ernest’s question on passing values to PDF forms via URL. His intent is to use it to provide a key to a PDF form such that it can be used to filter records from an ODBC Data Connection in order to pre-populate the form with data.

I love these kinds of questions because they challenge me to find answers!

Of course, this is quite specific but there are many other uses for this. In fact, you could have a whole lot of fun with it too! You could even use this to alter the appearance of your form: Say you had one form that you were using for multiple departments in your company and every department had its own header. You could place each header in a subform, make them all hidden and then, based on the URL request which would include a department code, decide which one to show — no XML Data file, database connection or web service needed!

Beware, however, that URL request strings are not secure because they get posted in plain text for anyone to read so be careful of the information you pass-in to your form this way.

This sample form looks for a “message” and a “color” key in the URL request in order to show a message in a text field and change the text field content area’s color (an RGB value). For example:

Download Sample [pdf]

Minimum Requirements: Designer 7.0, Acrobat 7.0.

The trick to all this is obtaining the URL that was used to access the PDF form. This can easily be achieved by accessing the Acrobat Document Object associated with the form and then obtaining the value of its URL property. This is done in JavaScript via the “Message” text field’s Initialize event:

event.target.URL

This property will give you the following string (from the second sample URL I provided earlier):

//URLRequests.pdf?message=Think%20of%20the%20possibilities...&color=0%2C255%2C0 

From there, it’s just simple JavaScript code to extract the request

?message=Think%20of%20the%20possibilities...&color=0%2C255%2C0

and then parse the key/value pairs

message=Think%20of%20the%20possibilities...
color=0%2C255%2C0

Finally, since we’re passing-in a string that may contain spaces which will have been URI-encoded (that’s use of “%20” instead of spaces and “%2C” instead of commas in the request string), we need to decode it back to a normal string using JavaScript’s built-in “decodeURI” and “decodeURIComponent” functions:

decodeURI(decodeURIComponent(string))

If you want to have fun submitting different strings to show in the message box, I found a little tool online which converts regular strings into URI-encoded strings.


Posted by Stefan Cameron on October 20th, 2006
Filed under AcroForm Objects,Scripting,Tutorials