AcroForm Objects
Today I thought I would tackle the subject of AcroForm Objects — objects available via scripting in the Acrobat Form Object Model — because they offer unique possibilities for your forms when they’re running in Acrobat in the PDF format.
Just to be clear, AcroForms are specific to Acrobat and therefore this functionality doesn’t apply when rendering your forms to a target other than PDF (e.g. when using LiveCycle Forms to render your XFA Forms as HTML).
First, let’s explain what XFA (XML Forms Architecture — a W3C Submission) does: It lets you describe a form, using a defined set of rules that govern an XML structure, which can target many different clients (e.g. PDF, HTML, etc.) — as long these clients support the XFA format. Today, the Adobe LiveCycle Designer targets PDF out-of-the-box and, along with LiveCycle Forms, targets HTML.
The fact that XFA is always translated into a format which can be understood by a client with which a user interacts in order to fill a form and possibly submit its data to a receiver means that the scripts you write in your XFA forms get executed in the target client application (such as Acrobat or a web browser). If the target client also contains a Scripting Object Model — like Acrobat does — there may be ways that you can take advantage of specific functionality exposed by the client which is hosting your XFA forms.
This brings us to the topic at hand: Acrobat’s Form (AcroForm) Object Scripting Model. If you’re designing your form only to target PDF (or you add code to your form to detect when your form is being hosted by Acrobat using xfa.host.name, for example), you can get access to the Acrobat app, Document and Field objects, amongst others, and do some really cool things like have a field with invalid data start flashing red when the user attempts to submit the form’s data.
xfa.host
When writing scripts in an XFA form, you have access to the special xfa.host object. This object gives you access to methods which are specific to the application hosting your form (such as Acrobat, a form server or a web browser). For example, the
xfa.host.name
property tells you the name of the application hosting your form at the time the script is interpreted. If your form is being viewed/hosted in Acrobat, this property will return “Acrobat” while it’ll return the name of the browser if it has been served to a web browser. Furthermore, it’ll return “Presentation Agent” if the script was flagged to run on the server and the server application serving the form to PDF or HTML is Adobe’s LiveCycle Forms product.
xfa.host also gives you access to other properties and functions such as
xfa.host.messageBox // displays a dialog box on the screen
xfa.host.validationsEnabled // turns the form's validations on/off with a single call
but take note that not all functions and properties are available on all hosts (e.g. since a server host can’t display a dialog which requires user input, the xfa.host.messageBox function is only supported on client hosts like Acrobat and web browsers).
You can obtain more information on xfa.host on page 185 of the Adobe XML Form Object Model Reference.
Acrobat app Object
Since the scripts you write in functions and events within an XFA form are interpreted by and executed within the context of Acrobat’s Scripting Engine, you have access to a special object called app. This object gives you access to the collection of active documents and plugins, amongst other things, and lets you display alert messages
app.alert("Hello world!");
and even set Acrobat into full screen mode with a red background!
app.fs.backgroundColor = color.red;
app.fs.isFullScreen = true;
Note that while color.red isn’t an object provided by the XFA Scripting Object Model, it still exists within the context of your scripts because the scripts are ultimately interpreted and executed within Acrobat. You can get more information on the app object in the Acrobat JavaScript Scripting Reference.
xfa.event
This is a special object which exists only via the XFA Plugin which executes XFA scripts on XFA object events inside Acrobat. Whenever an event occurs (such as the click of a button or a field gaining input focus), your script has access to the xfa.event object which gives lots of important information about the event.
For example, if you want to know the value that was selected in a list box or a drop down list in order to change the state of another object on your form, you would script against the list box’s or drop down list’s Change event. If you used the following code to get the value of the item that the user selected:
this.rawValue
you would get the previously-selected value because the object’s rawValue property isn’t updated until after the Change event has occurred. In order to get the information you need, you must use the following code:
xfa.event.newText
which will give you the value of the item the user just selected.
xfa.event also gives you access to a very useful property called target: In Acrobat, this property specifies the Acrobat Document object (which contains the Acrobat Field object which wraps the XFA object whose event is being scripted). This means that you can get at the Acrobat Document object for the “active document” just by using:
event.target
(Note that you don’t need — and shouldn’t use — the “xfa” prefix when accessing the “event.target” property — I don’t know why yet but you’ll have trouble using it if you use the “xfa.event.target” syntax.)
Using this information, you can:
event.target.zoom *= 2; // increase the zoom level two-fold
event.target.zoomType = zoomtype.fitW; // zoom to page-width level
event.target.zoomType = zoomtype.fitH; // zoom to page-height level
or you can use the getField method to get an Acrobat Field object and do some more interesting things.
You can get more information on the Document and Field objects in the Acrobat JavaScript Scripting Reference.
Putting it all Into Perspective
To tie this all together, I’ve drawn-up a simplified version of the Scripting Object Model the way I picture it in my head:
This image illustrates how things work from the perspective of a script running within an XFA Event or an XFA Script Object Function (note that you don’t have access to the xfa.event object there unless you pass it into the function by calling it from an XFA Event). You can see how, from the XFA object, you can get to the:
- Acrobat app object (directly);
- Acrobat Document object (via event.target or the app object);
- Acrobat Field object (via the Document object);
- xfa.host object (directly).
Hopefully this post will have given you a general idea of the Acrobat-specific tools at your disposal when you’re writing XFA scripts using the JavaScript language. Please note, however, that changes may occur to the way XFA Form Objects are hosted within Acrobat in future releases and therefore using the AcroForm Object Model should be a last resort if an XFA equivalent simply isn’t available.
Updated: August 30, 2006
Posted by Stefan Cameron on June 10th, 2006
Filed under AcroForm Objects,Scripting
Both comments and pings are currently closed.
Hi Stefan,
Another great article! Just one quick correction. XFA was a W3C submission but is *not* a W3C standard. A standard is a document that has gone through a rigorous review process by multiple independent parties. A W3C submission is done unilaterally by a party or parties interested in having the W3C take the submission into consideration during the standards process.
Just because something appears on the W3C site, doesn’t make it a W3C standard, but it does mean that the parties involved are participating in the W3C process.
I should know because my name is on the submission. 🙂
Regards,
Rob
Rob,
Thanks for correcting me on that one! I see now that the W3C document states that it’s a submission and not a standard. I’ve made the correction in my post.
Hi,
Thank you for your article.
I would like to make one question that I think is related to this article:
When opening a pdf file using a xdp file, I realize that at least some events get fired twice. Can you explian that?
Thank you
smvo,
Based on the Adobe ML Form Object Model Reference, opening a dynamic PDF form will result in the pre-render sequence of events to fire twice completely simply because of the way the form is loaded into Acrobat.
Also, you’ll get multiple Initialize event triggers depending on whether you’re merging data into your form: If you don’t merge data, you should only get it once for every object; otherwise, you’ll get it once prior to data merge and another time after the data merge has completed.
Does that clarify things for you?
Stefan,
I would like to make another question about a subject in your tutorial.
You said this: “…your XFA forms get executed in the target client application (such as Acrobat or a web browser)”.
My question is, how can I have a brwoser as a host/target application? I ask this, because, even when acrobat open inside a web browser, the host is still Acrobat, right?
So How can I open a XFA form in a browser?
Thank you again.
smvo,
You can have your forms hosted by (executed within a) browser by deploying them to HTML via LiveCycle Forms — an Adobe LiveCycle solution which lets you securely capture data from and deploy your forms to a number of different devices from desktops to Pocket PCs without users having to install any extra software.
Hello again Stefan,
I have two more questions that I hope to be related with your topic. Any help is appreciated.
1) When opening a xdp file referencing a PDF file, for instance in a browser IFrame, I realize the xdp file by itself opens inside the IFrame, and then it browses to the PDF and this way the PDF will no longer be opened inside the IFrame (so Im´not able to hide it from the user).
Is there any way to control this beahaviour/event? For instance, is there any way to instruct acrobat to open the referencing xdp file in a predefined window´s frame? (self, top, etc)
2) Because I couldn´t figured out a way to accomplish the previous goal, I tried to open a new hidden browser window where the pdf would be rendered, and automate the initialize event of the XFA PDF calling a button click event to print the document:
xfa.host.print(0, “0”, (xfa.host.numPages -1).toString(), 0, 0, 0, 0, 0);
The goal was to create a new hidden window, where the pdf would be rendered.
This way the initialize event would be automatically call, and also the click event of a print button inside de pdf. The PDf would print and the browser window close.
However there is one problem in this scenario: the print method of the xfa.host will be called, but it will soon stop sending to the printer. I can see the pdf being send to the printer spool at start but if the pdf is a bit longer the send to the spool will stop and the spool became clean without printing. It seems the print method is asynconous or something like this.
Thank you
smvo,
I’m afraid it’s “0 for 2” in this case:
1. The only thing I know of that you can do when opening a PDF is to tell it either to open in the current window or a new one — you can’t specify which frame it should open in.
2. For security reasons, you can’t (or at least you shouldn’t be able to) silently (without user interaction) execute a print command neither by using xfa.host.print nor by using event.target.print. Therefore, attempting to execute a print command in an Initialize event shouldn’t result in anything.
Hi Stefan,
Here it goes 2 more questions about your article. Any help is really appreciate.
I´m writing code in an application/folder level javascript file. From there, I would like to get the value of some fields palced in my XFA forms… Is this possible? At what level can we interact with xfa forms using application level javascript? For instace, can I pass an object as a function parameter to the application level javascript and then change some properties?
Thank you!
Sergio,
I don’t see why you couldn’t pass an XFA Object reference to an Acrobat application-level script. Also, from your script, if you get an Acrobat Document object, you can get the XFA Form Object Model from there by using the “xfa.form” object within the current context (the specific Acrobat Document object). That should let you do things like
in order to get a list of page-level subforms (assuming the current form’s root subform is named form1).
I’m not promising that this works but I would hope that it does. In the end, however, it’s best to use application-level scripts only if absolutely necessary. Access to certain Acrobat Object Model objects in an XFA-PDF form may change in future releases and this could cause your scripts to break.
Stefan,
At first I tried to get the XFA object from an application/folder lever javascript function using “this.xfa”. The result was always “undefined”. Using a direct call to “xfa” was undefined too. However, using “event.target.xfa” works very well. All the properties/methods of the xfa object could be used this way.
I think this is an important point to your great figure in this article: If I´m not wrong, using “this” in a folder level script return an Acrobat Document object, while event.target will return an xfa form object.
So, maybe you could show that in your figure as it mainly shows the direction form xfa to acrobat and not the opposite with the exception of the get field function. I don´t know if I make my self clear about this… I´m only trying to figured out all these ways in my mind using your figure.
In what relates to code breacking, I understand your point, however, I rather change the code in a javascript file than change it in about 400 documents… That is why I´m trying to put my code inside an external javascript file.
Stefan, thank you for your help.
Regards,
Sérgio
Sergio,
Thanks for your input.
I see your point about code breakage and not wanting to update 400 forms to fix one little typo in a script. Hopefully you’ll like what a future version of Designer has to offer in order to address this sort of problem.
With respect to the xfa object, I’d like to clarify that the reason why “event.target.xfa…” works is because “event.target” represents the active Acrobat Document Object and within that object lives an xfa object (as long as you’re dealing with an XFA-PDF form). From there, you can access the XFA Form Object Model by doing things like
in order to assign the value “test” to a field named “myField” on the first page of a form named “form1”.
As far as the “this” keyword in Acrobat application level scripts goes, while I’m no Acrobat scripting expert, I’m quite certain that “this” usually refers to the app object (Acrobat Application) instead of an Acrobat Document Object. You would have to get the list of opened documents and then test each one for the existence of the xfa object. For example, you could do something like this (please note that I haven’t tested this Acrobat script):
Stefan,
What do you mean about this:
“Hopefully you’ll like what the next version of Designer has to offer in order to address this sort of problem.”
What version of Designer are we talking about? I believe it will be a new release right?
Thank you!
Sergio,
Curious, are we? 😉
I’m talking about a new release of Designer. Unfortunately, that’s about all I can say at this point.
Ok,
Another question less curious, about the new table object of Designer 7.1.
Is it possible to add new columns programmatically or at least, hide columns already in the table?
Thank you!
Sergio,
In response to your question on table columns, check-out my post on Scripting Table Columns.
I hope it answers your questions.
Hello Stefan,
Here I am again, now that I´m trying to use LiveCycle Forms.
Well, after all the code created in LiveCycle Designer, and because I used some acrobat objects and application level code, I am now stuck in some issues:
1 – I need to use something like a xml string reader. So I used XMLData object in Livecycle Designer. In LC Forms I only get this: Object302394736
Does this menas that there is not a way to work with xml strings using xfa (to be compatible with Livecycle Forms)? This is really bad, because I will need many hidden fields to accomplish the sabe functionality…
2 – I use a lot of application script because I don´t want to get big PDF files. However, I guess this will not work in LC Forms, right? Is there any sort of code container to LC Forms similar to the acrobat install script Directory where we put javascript files? So that when the script is not on the form, LC Forms will search somewhere else?
Thank you.
smvo,
I’m afraid it’s 0 for 2 on this one.
Since the XMLData object is an AcroForm object, it isn’t available to the scripting model when the form is running on Form Server.
As for your question about a central place to put global JavaScript scripts on the server, this isn’t possible either.
I’m afraid you’re best bet here is to write a little XML parser in JavaScript (I know, that’s really not what you wanted to hear and it’s not what I wanted to be able to tell you either) and to place all scripts in the form itself.
Stefan,
I decided to use arrays.
Tell me, is there a way to get the full path to an object? For instance, in an object event: this.some_propertie_to_get_the_full_path_of_it?
Thank you
ok, I had figured out: this.somExpression
Thank you
hi Guys,
I have form which is built in Adobe LifeCycle version 8. of SP2. I am looking for a way to tell whether my form is open within an IE browser or normal Acrobat reader.
I try to use the method of target.event.internal or this.external, however it returns undefined.
Can you shed some lights on this question ?
wnged,
That’s an interesting question! There are a couple of things you try: First, if Acrobat/Reader is loaded in-place within a browser, the
property will be defined. It provides a reference to the browser, if I’m not mistaken. If the form is loaded in Acrobat/Reader standalone, the property will not be defined. Note that this property isn’t supported on the Mac OS so if your form is loaded within the Mac OS, you wouldn’t be able to tell either way.
If that doesn’t work-out, then you could try parsing the
property which is the document’s URL. If the form is loaded locally, the URL will start with “file://” and this would indicate a pretty good change that it’s loaded in Acrobat/Reader standalone (but this isn’t 100% accurate since the user could’ve loaded your form within their browser from a local web server). Otherwise, the URL will likely be “http://” which means the form is likely loaded in Acrobat/Reader which is, in turn, loaded in a browser.
Stefan,
Thank for your suggestion. The target.hostContainer did not work, it returns undefined in either Reader Standalone or Browser running in window OS.
The event.target.URL works as it returns the file or http. My problem is that I have used event.target.URL in my form. however
the new version of Reader 8.1 or above displays a security warning when you use the target.URL. This annoys user to click OK each time openning the form.
I try to avoid to execute the event.target.URL by trying detecting whether the form is opened in browser or stand-alone. if it opens in stand-alone, it can by-pass the code and
avoid the warning message. sofar I cannot find otherway to detect this, apart using the event.target.URL.
I am keeping looking..
Hi, try with this, it worked to me 😉
var oParent = event.target;
if ( oParent.external )
{
// Viewing from a browser
console.println(“Viewing from a browser”);
}
else
{
// Viewing in the Acrobat application.
console.println(“Viewing in the Acrobat application.”);
}
In looking for some help, I found this post. Maybe you can answer this question: I am using Pro 8 and am having an issue with newly created forms. We use an application to import the form and map the fieldnames to our application for printing purposes, however by default, new forms are created with Designer in the new format. This new format does not work with our application, and I need to create new forms in the “old” acroforms method.
I can open these new forms in Pro, but under Forms toolbar, can only edit the form in Designer. Old forms will allow me to edit in Acrobat. I can work around this problem by opening an old document, saving as a new doc and adding my new pages and deleting the old. I can then edit the form in Acrobat instead of Designer, but this is cumbersome, and I have to be able to train new users to do this. I would be much more efficient to be able to select which way the new forms are created, but I cant find an option that allows that. Any ideas?
Thanks
Hi Stefan,
I am desperate to get a solution to my problem with average calculation using checkboxes. I hope you can help me. I am a first time user of the designer but was successful on completing my form. The problem came up when a numeric field to calculate average using checkbox values was not giving me the correct number. My form has a section with 7 rows or questions. I use 4 checkboxes to select answer. Boxes are set to global so only one check for each row. All 4 boxes in a row have the same fieldname. To calculate the average: sum of the values of the checboxes (1-4) / count of the checkboxes which is 7. The problem came up when only 5 rows are checked. How do I make the denominator a variable so that it will only count the checkboxes that are checked???
Please help. Thanks in advance.
Cheska
Cheska,
I’m not quite sure I understand the way in which you’ve setup your checkboxes. On one hand, it sounds like you want them to be mutually-exclusive (i.e. users can only check one out of the set like radio buttons) however you also mention being able to select multiple checkboxes…
Mutual-exclusivity aside, let’s say you had 7 checkboxes, each with an “on” value of 1 to 7 and an “off” value of 0, and you wanted to calculate the average based on the number of checkboxes checked. The numeric field that displays the average would have a Calculate script like this (with the language set to JavaScript):
This isn’t the simplest script you could write but I think it makes it easier to grasp the concept this way.
Hi Stefan,
I’ve been looking around for a while for an example that would show me how to create a popup whereby I can control the font size and color. I’ve had a look at the adobe javascript dialog reference and the xfa.host.messagebox api, but haven’t had much luck. Do you have any idea on how I can achieve this.
Regards
Justin
justin poyser,
Unfortunately, these dialogs are created by Acrobat/Reader and can’t be modified.
Stefan,
I’m using Adobe LiveCycle Designer ES 8.2. I’ve successfully connected to a database and can populate the form by making selections from 2 drop downs. This works great thanks to the great posts shared in the forums.
What I need to do next is to dynamically save the form to a file location based on text in the form. I’ve read all the blogs on this subject and my code error in the JavaScript console reads:
doc is not defined.
I’ve created and recreated many versions of the trusted function (folder level Javascripts – saved in the C:\Program Files\Adobe\Acrobat 9.0\Acrobat\Javascripts.
Please, how do I get the doc to be recognized by the Adobe LiveCycle form? I’ve restarted Acrobat, etc. I’ve made all of the gotcha settings like enabling menu item JavaScript execution.
Here’s the folder level JS function:
// application folder function
mySaveAs = app.trustPropagatorFunction(function(oDoc, cPath, cFlName)
{
app.beginPriv();
oDoc.saveAs(oDoc, cPath, cFlName);
app.endPriv();
})
myTrustedSpecialTaskFunc = app.trustedFunction(function(oDoc, cPath, cFlName)
{
// Check name of the current form
var aDocumentFileName = this.documentFileName;
// If name of the current form is … save it to the following target
if (aDocumentFileName == “BOEForm.pdf”)
// Privileged and/or non-privileged code above
app.beginPriv();
mySaveAs(oDoc, cPath, cFlName);
app.endPriv();
// Privileged and/or non-privileged code below
});
Here’s the mouseup event code in the Adobe LiveCycle Form-
topmostSubform.Form1.Page1.Button2::mouseUp – (JavaScript, client)
aDocumentFileName = this.documentFileName;
var doc = “BOEForm.pdf”
event.target.myTrustedSpecialTaskFunc(event.target, “/c/BOEForm.pdf”, “mySavedDoc.pdf”); //this should execute the save as js file
And the JS Console Error Reports:
doc is not defined
9:XFA:topmostSubform[0]:Form1[0]:Page1[0]:Button2[0]:mouseUp
I appreciate your help. I’m hoping to solve this so I can go on with life….
Thank you!
Hello, Dear Sir Stef!
Great area, your works!
I´m searching for examples of the openList-method,
because FormCalc,
xfa.host.openlist(Formular1.#subform[0].DropdownListe1)
doesn´t work;
at your hint you use “@object” and “@string”, I´m sorry,
what is some explicit syntax?!
it´s great for user when they enter with tab or mouse
a DateTime-Field and nothing more move to the little black
triangle to click…..!
please, what to do?!
the code is so cool and so much cool because shortend!
ThankYou Very Much! Yours Kurt Lukas.
Back, please, backward my question!!
it works in any event of another control, I have uesed it errorly
in the enter-event of the control itself;
what is th ecorrect event to fire this nice little utility?!
ok, it works in MouseEnter-Event, but not in Enter-Event….what´s wrong, please?!
@DebbieF,
Could this simply be that you’re missing a semi-colon on the following line in your MouseUp event?
I don’t see one in the code you provided… Otherwise, I don’t see what would be going wrong.
@Kurt Lukas, Europe, Germany,
“@object” was meant to be replaced by a reference to the drop down list object and “@string” was meant to be replaced by the drop down list’s SOM expression.
The object reference variation is only supported in later versions. Have you tried this?
(also note the capital “L” in “openList”).