Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

Archive for January, 2009

Submit or Execute on Other Events

I discovered something interesting the other day: Just because Designer wants you to type script for a field’s Full event doesn’t mean you have to execute script when the Full event is triggered.

The XFA 2.8 specification specifies the <event> node’s content as being one of the following nodes: <script>, <execute> (executes a web service data connection), <submit> (causes the form to be submitted via email or HTTP) or <signData> (causes the form to be signed). Only one may be specified and whatever content is there will be executed when the event is triggered.

This means that you could, for example, cause a web service to be executed whenever a repeatable subform’s index changes (i.e. whenever you add/remove instances).

Is this useful? Well, perhaps not, but I thought it was interesting! Who knows what you might think of doing with this…

The only catch to all this is that Designer only lets you enter script (JavaScript or FormCalc) into an event, thereby specifying an event’s content as <script> as opposed to <submit> or <execute>. If you want to, say, cause a form submit when a field’s Full event is triggered, you’ll have to go to the XML Source and set the event’s content to a <submit> node yourself. An easy way to do this is first to add an Email/HTTP Submit Button to the form, set the field’s Full event to a placeholder script, go to the XML Source view, find the button using the Hierarchy palette, copy its Click event <submit> node and paste it into the field’s Full event so it looks like what’s below and delete the button:

<event activity="full" name="event__full">
    <submit format="xml" textEncoding="UTF-8" target="mailto:"/>
</event>

Sample Form

Check-out my little sample form that causes the form to be submitted via email when you fill the text field at the top and where a web service data connection is executed whenever you add an instance of Subform2 using the button at the bottom (below the web service import/export fields).

Download Sample [pdf]

Sample Minimum Requirements: Designer and Acrobat Standard/Pro 8.0 (XFA 2.5)


Posted by Stefan Cameron on January 30th, 2009
Filed under Events,Scripting,Tutorials,XFA

Fragment Library File Management

Did you know that the Fragment Library palette in LiveCycle Designer 8.2 now lets you edit fragment titles and descriptions, even fragment files themselves, without having to first open the files that contain them?

Prior to Designer 8.2 it was possible to see the title and description associated with a fragment by selecting the “Fragment Info” command from a fragment listed in the Fragment Library palette. This dialog, however, didn’t let you edit that metadata unless you were actually editing the file (XDP) that contained the fragment at the same time. That is, in order to edit a fragment’s title and description, you would have to edit it Designer first, then change its information.

Note that the Fragment Library palette is only available when using Designer outside of Workbench.

As of Designer 8.2, you can that directly from the Fragment Info dialog without having to open the file that contains it and you can even delete a fragment. The Fragment Library palette will even handle the case where there are multiple fragments in the same file (e.g. you have an XDP that contains a single paragraph of text translated into multiple languages, each being a fragment that can be referenced individually by a localized form) when you decide to delete an existing fragment.

Editing Title and Description

Say you have 3 fragments: Fragment_1, Fragment_2a and Fragment_2b. Fragment_1 is in fragment1.xdp and the other two are in fragment2.xdp and both files are in the same folder, “My Fragments”. They would look like this in the Fragment Library palette:

In the form that references them, they would look like this:

Without any of the fragment files being opened in Designer, you can edit the description of Fragment_2a to be “My description.” and see the new description appear immediately in the Fragment Library palette:


—Editing the description.


—New description in the Fragment Library palette after clicking “OK” in the Fragment Info dialog.

You can do the same for the title.

Deleting Fragments

Now say you wanted to delete Fragment_2b. Instead of having to open fragment2.xdp, select the Fragment_2b subform, delete it and then save your changes, you can just right-click on the “Fragment 2b” item in the Fragment Library palette and choose the “Delete Fragment” command.

You will be presented with a confirmation dialog stating that deleting the fragment will cause broken fragment references in all forms that might be using it. You can opt not to have this dialog displayed again.

If you choose to proceed, the fragment is removed from the file that contained it (fragment2.xdp in this case) and references in currently-open forms are updated:


—Result of deleting Fragment_2b when our form was still referencing it (it was replaced with a broken fragment reference).

Additionally, if there are no longer any fragments defined in the file which contained the fragment you just deleted, you’ll get another prompt asking if you would like to also delete the file that contained the fragment (which you can also opt not to be displayed in the future). If you accept, the file that contained the fragment would also be deleted. For example, if you opted to always remove the fragment and always remove an empty fragment file and deleted Fragment_1 from Fragment Library palette, the fragment and its file would be deleted instantly and all references would be updated, showing broken fragment references in its place.


Posted by Stefan Cameron on January 26th, 2009
Filed under Designer,Form Fragments,Tutorials

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

Adobe Reader Survey

Here’s your chance to make your voice heard with the Adobe Reader Team: They’re looking for your feedback on what you want most in future versions. Would you like Reader to be a smaller download? Should it be easier to use? Does it need a specific feature?

Please take 5 minutes to complete the survey by January 31, 2009, and let the team know what your thoughts are.


Posted by Stefan Cameron on January 12th, 2009
Filed under Acrobat

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