Stefan Cameron on Forms
Building intelligent forms using Adobe LiveCycle Designer

'Acrobat' Category Archive

Bug: Images aren't always embedded into PDFs

Description

Image objects (images and image fields) have a property which you can set to specify whether the image should be embedded or linked (the "Embed Image Data" check box on the Object palette’s Field tab).

When the property is checked, the result is that the image file loaded into the image object is embedded into the XFA Data that’s stored in the form. When the property is unchecked, only the URI (file path or URL) is stored in the XFA Data.

I put emphasis on XFA Data above because it’s important to understand the difference between the XFA layer and the PDF layer of a PDF form in order to understand what this bug is all about: Essentially, an XFA form saved as a PDF file results in a PDF container with the XFA inside (as opposed to saving the form as an XDP where the XFA form is at the top layer). When the PDF form is subsequently filled and saved within Acrobat (Standard or Pro), the image loaded into an image field object is supposed to be saved on one layer or the other depending on the setting of the "Embed Image Data" property on the image field.

When you save a PDF form in Acrobat, what gets saved in the XFA Data (in the XFA layer) depends on the value of the "Embed Image Data" property (which maps to the //field/ui/imageEdit@data attribute). If it’s set to "link" (unchecked), then only the original path to the image file is saved. If it’s set to "embed", then the image file is text-encoded and saved in the data instead of the original file path. In either case, however, the image should always be embedded into the PDF layer (don’t worry, it doesn’t actually get embedded twice, once on each layer, I’m just simplifying things a little here) such that the PDF is self-contained and can be re-distributed without having to ensure that the linked image file remains accessible from any location.

Unfortunately, there are cases when the image file doesn’t get embedded into the PDF layer when it should be. For example, if you have an image field object which is set to link to its image file and you load the image by importing data that contains the file path then save the PDF, the image won’t be embedded into the PDF as expected.

Workaround

At this time, I’m not aware of any workarounds to the specific issue I stated above (when importing the image’s file path).

The only way to consistently get image files to be embedded into the PDF as they should be — regardless of the image field object’s setting to link or embed its data — is to manually click on the image field object in Acrobat and pick the image file.

Fix

Please refer to the Bug List for updated information on the version(s) affected by this bug as well as if and when it was/will be fixed.

Update (May 22, 2008)

This bug was addressed in Acrobat/Reader 8.1 (XFA 2.6, PDF 1.7) in a way that addresses both security and PDF self-containment issues.

For XFA 2.6+ forms (forms authored in Designer 8.1+), images that are linked (referenced) rather than being embedded are now stored (the bits are embedded) in an array in the PDF layer when the PDF form is saved. At runtime, if an image is linked (referenced) rather than embedded, Acrobat/Reader will look for the associated image bits in the PDF layer and, if a match is found, will load the image. This means that Acrobat/Reader no longer goes outside of the PDF to fetch images unless explicitly directed by the user (e.g. the user clicks on an image field and chooses an image on their own) which addresses security issues. The fact that linked images are now stored in the PDF layer also addresses self-containment issues where the nature of a PDF document is that it is a self-contained entity (e.g. when you email a PDF, the recipient can always open it and see its content even if they don’t have access to referenced content — the exception being if the form makes an external data connection to a database or web service or needs an XML data file to be imported).

Image fields with content embedded at design time are still stored in the XFA data as they were before. You may still load an image dynamically from data provided that the data is the actual image bits or, if the data is a link, that there are associated image bits already in the PDF layer (which implies that the image was linked from the form at design-time — for example, you could have 5 hidden image objects each linking to different images and then use a link in the data to control which of those images show up in an image field on your form). In either case (whether the image is loaded via embedded bits or a link), the image bits always end-up in the data submitted from the form. The original link is not included in the submitted data nor is it available at runtime via scripting (unless imported in data via a data connection or XML data file) because it could contain sensitive information (e.g. your username).


Posted by Stefan Cameron on January 23rd, 2007
Filed under Acrobat,Bugs

Better Form Design with XFA 2.5

You may have noticed that the new version of Designer and Acrobat that Adobe recently released uses a new version of XFA (2.5 to be exact). While the language has many new features in and of its own, the version of the language (2.5 vs 2.4 or older) also dictates how your forms will behave in Acrobat 8+ with respect to certification (digital signatures) and ubiquitization (Reader-enablement).

Background

Since this new behaviour, which is geared to encourage better form design going forward, will ultimately change the way you write certain scripts, I believe it’s important to start with some basic knowledge of XFA forms, see what happens when they get certified/ubiquitized, examine how each used to behave in Acrobat 7 and then how they’ll behave in Acrobat 8+.

XFA Primer

Simply said, an XFA form is described inside an XDP file. When it’s saved as a PDF file, the XDP is actually embedded into the PDF so that it can be processed by Acrobat.

If you look at the XDP (via Designer’s XML Source view), you’ll see that it’s simply a collection of packets that describe the form’s various components. For example, <template> describes the form’s layout and behaviour while <sourceSet> contains a collection of data connection descriptions you’ve defined using the Data View palette. When the form is loaded as a PDF into Acrobat, each packet is loaded into its own in-memory (temporary) model and that’s what you actually reference in your scripts. For example, you access the sourceSet packet via the sourceSet model with “xfa.sourceSet“. Changes to these models are reflected directly into their pertaining packets.

Form Certification/Ubiquitization

Without going into more details than are necessary, once a form is either certified and/or ubiquitized, modifications to any of the protected content (the various models) should be prevented. Modifying the form’s protected content post-certification/ubiquitization would invalidate the certification/ubiquitization status. Such modifications mean that the document is no longer in the state in which it was when it was digitally signed and therefore can no longer be trusted by the recipient as being authentic. Even though the modification may have been caused by authored script in the form, no distinction is made between those kinds of modifications and malicious attacks by an unknown party to, for instance, cause form data to be submitted to an alternate server.

Acrobat 7 and XFA 2.4

Acrobat 7 supported XFA forms up to XFA 2.4 (which Designer 7.1 would author). Once an XFA 2.4 form would be either certified and/or ubiquitized, Acrobat 7 would detect modifications to the form’s protected content and would invalidate its certification/ubiquitization status if such modifications occurred. It didn’t, however, prevent the sourceSet model from being modified post-certification/ubiquitization even though the <sourceSet> packet was included as part of the certification/ubiquitization process.

The inherent danger in this was that while any form that would do common things like display all the records in a database or select specific records in a database (filter records) — which required the modification of data connection nodes contained within the sourceSet model — would function happily at first, problems would ensue later on when a form’s certification/ubiquitization status would be inadvertently invalidated, for example, because of an unauthorized modification to a certified/ubiquitized model (sourceSet).

Acrobat 8 and XFA 2.4

In order to address the problem of inadvertent modifications (by form scripts) to certified/ubiquitized XFA 2.4 forms, Acrobat 8 was designed to prevent the sourceSet model (and any other certified and/or ubiquitized content) from being modified post-certification/ubiquitization. This means that if an XFA 2.4 form, loaded in Acrobat 8+, becomes certified and/or ubiquitized, any attempt by a form script to modify the sourceSet model (as in the two examples I mentioned earlier) will result in a security exception:

GeneralError: Operation failed.
XFAObject.setAttribute:25:XFA:form1[0]:initialize
This operation violates your permissions configuration.

One could argue, of course, that this change effectively “breaks” XFA 2.4 forms in Acrobat 8+ but in the end, inadvertently invalidating a form’s certification/ubiquitization status is likely just as bad as a security failure in a form’s script (because it attempted to modify a now-protected model) from a user experience point-of-view. As I mentioned earlier, Acrobat makes no distinction between authored scripts and malicious attacks when certified/ubiquitized content is modified — and neither does the user (in their minds, the content simply can’t be trusted any longer)!

Acrobat 8 and XFA 2.5

With a new version of Acrobat and XFA, there was an opportunity to further improve on the user experience of both certified/ubiquitized and non-certified/ubiquitized forms going forward. It was done simply by ensuring that modifications to any model that becomes protected post-certification/ubiquitization are now prevented from the start (whether the form is certified and/or ubiquitized or not) and by using XFA 2.5 as Acrobat 8+’s “trigger” for imposing the new behaviour.

The result is that we’re now forced to think about security from the very beginning of the form design process by opting to work with copies of the in-memory models (which is achieved by cloning models) rather than with the base models such that our forms don’t fail regardless of their certified/ubiquitized state. With XFA 2.5’s support for “on-the-fly” certification/ubiquitization, a form may become secured and locked-down at any point in its “live cycle” which makes it imperative to use scripting techniques which won’t fail post-certification/ubiquitization.

Legacy Mode

New forms authored in Designer 8.0 will be XFA 2.5 forms by default and you’ll need to use the new cloning technique described later in this article. That being said, if you need things to be back the way they were, there is a way that you can still use Designer 8.0 to design XFA 2.4 forms and that’s by using what’s called the Legacy Mode processing instruction.

Put simply, switch to the XML Source view for an XFA 2.5 form in Designer 8.0 and insert the following processing instruction under the <template> node (as a child element):

<?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.4/?>

The result will be that Acrobat 8.0 will run your form as though it was an XFA 2.4 form — but be aware that this will also prevent you from using any of the new language extensions and APIs that come with XFA 2.5 (more on those in later posts).

(By the way, when you load an older form — earlier than XFA 2.5 — into Designer 8.0, even though the form’s version is upgraded to XFA 2.5, the Legacy Mode processing instruction specifying the form’s original XFA version is automatically added so that your form continues to work properly with respect to the XFA version is was originally designed for.)

Modifying sourceSet in XFA 2.5+ Forms

In order to avoid unexpected security exceptions in your forms after they get certified and/or ubiquitized and to handle the fact that you may not necessarily know for sure at which point in the form’s workflow that it’ll happen (if ever), you need to make sure that when you’re working with the sourceSet model, you’re actually using a cloned in-memory copy of the original sourceSet model rather than using the original sourceSet model directly.

Cloning Form Nodes

Don’t worry: You don’t have to be a scientist to use this simple technique. Using the

clone(deep)

method on the node that defines the particular data connection you’re wanting to modify within the SourceSet model and making sure your script keeps using the clone instead of the actual definition will do the trick. This method accepts a boolean parameter which, when set to 1 (or true), will clone the node and all its children (which is definitely what you want to do or else you will only get a shell instead of the full data connection) and return a reference to the in-memory copy.

As an example, let’s consider the following script taken from the Data Drop Down List object (found in the Library palette’s Custom tab):

...
var oDB = xfa.sourceSet.nodes.item(nIndex);
...
// Search node with the class name "command"
var nDBIndex = 0;
while(oDB.nodes.item(nDBIndex).className != "command")
  nDBIndex++;

oDB.nodes.item(nDBIndex).query.recordSet.setAttribute("stayBOF", "bofAction");
oDB.nodes.item(nDBIndex).query.recordSet.setAttribute("stayEOF", "eofAction");

Notice that the script first obtains a reference to a data connection node found within the original sourceSet model and then goes on to modify some of its properties. In an XFA 2.4 form loaded in Acrobat 8+, prior to certification/ubiquitization, this will function properly although it’ll stop functioning if the form ever gets certified/ubiquitized. In an XFA 2.5 form, however, it’ll immediately fail with a security exception simply because Acrobat 8+ determines that the sourceSet model may eventually become protected and protects it from the start.

Applying the cloning technique to this script is trivial. All you need to do is change the line which accesses the sourceSet model to this:

// JavaScript:
var oDB = xfa.sourceSet.nodes.item(nIndex).clone(1);
// FormCalc:
var oDB = Ref(xfa.sourceSet.nodes.item(nIndex).clone(1))

Notice the clone(1) method appended to the end of the statement. At that point, “oDB” now receives a reference to a copy of the original sourceSet model which it’s free to modify regardless of the form’s certification/ubiquitization status. (Also note that in FormCalc, you have to wrap the statement in a call to the Ref() function which will ensure you get a reference to the cloned object.) The rest of the script doesn’t need to be modified at all!

Note that you could just as easily store the cloned data connection node into a Form Variable or a variable defined in a Script Object in order to reference it again at a later time if you make modifications to it that you would like to persist while the form is running in Acrobat.

Updated Library Objects

If you had already installed Designer 8.0 and tried using the Data List Box and Data Drop Down List objects under the Custom tab in the Library palette, you more than likely ran into the security exception I described earlier. That’s because those custom objects managed to miss the ever so important update which they required in order to function properly in XFA 2.5+ forms with Acrobat 8+ (as we saw in the previous section).

For your convenience, I’ve posted updated versions of both the Data Drop Down List and Data List Box custom Library objects which you can save to your local system and add to your personal (or shared) Library in Designer 8.0.

Updated: December 11, 2006
Updated: March 10, 2010 — Added pointer on using Ref() function in FormCalc to get a reference to the cloned data connection.


Posted by Stefan Cameron on December 7th, 2006
Filed under Acrobat,Data Binding,Designer,Scripting,Tutorials

Scripting Table Columns

A few days ago, Sergio, one of my regular commenters, posted a question about programmatically adding new columns to a table. My reply to his comment quickly turned into something that I thought should be promoted to a blog post so here it is.

This question required some investigation because it led me to the discovery of a bug related to adding/removing instances of a table column in a form viewed in a version of Acrobat prior to 8.0. More on that later in this post.

The short answer to Sergio’s question is that yes, in fact, you can modify the set of columns in a table programmatically at runtime. You can do this by either using the presence attribute — although this isn’t recommended because it can lead to data merging problems — or you can use Instance Managers to do it, which is the recommended method to use.

Here’s a sample form that contains a table with a “repeatable column”. Using the add and remove buttons that are provided, you can add and remove instances of the 3rd column.

Download Sample [pdf]

Minimum Requirements: Designer 7.1, Acrobat 7.0.5.

Table Structure

To better understand what’s going on here, I’ll start by explaining how tables are represented in XFA. In reality, there are no <table>, <tr> or <td> elements in XFA. What happens is that <subform layout=”table|row”> elements get used. When a subform represents a table, it’s layout attribute is set to table and when it represents a row, it’s set to row.

Table Subforms

If you read about using a subform’s Instance Manager in order to add/remove instances, you should know that a subform only becomes dynamic or repeatable once it’s placed in a flowed container and its set as repeatable using the Object palette’s Binding tab. By default, a <subform layout=”table”> element is flowed from top to bottom. Since the table subform contains row subforms which, in turn contain the cells, that means that every row in a table could easily be set as repeatable and then get an Instance Manager that you could use to add/remove instances of a particular row.

Row Subforms

Just like the table subform, a <subform layout=”row”> is flowed. Its flow direction, however, isn’t top to bottom, it’s right to left. This means that its contents — the cells — are flowed.

Cells and Columns

Table cells are little different. Essentially, since they reside in a row subform which is flowed from right to left, there’s no specific definition for what is a table cell. It’s simply any type of object (text, numeric field, subform, button, etc.) that’s placed within a row subform. The number of objects in the row determines the number of columns there are. Therefore, to have a 3×3 table, you would need 3 row subforms each containing 3 objects.

There’s no actual definition for a table column either. Table columns are inferred by the cells (put simply, without going into details about column spanning). This means that a column consists of objects in separate row subforms which are above and below each other (e.g. the first object on every row make-up the first column in a table).

Adding/Removing Columns

Given the explanations in my little “table primer” above, we now know that table cells are objects which are flowed from right to left within row subforms. Since row subforms are flowed by nature, it means that if a cell were a subform itself, it could be made repeatable and it would then get its own Instance Manager for free! And once we have Instance Managers, we can start adding and removing instances of those cells.

So the trick here lies in converting every cell which constitutes a column into a subform which then contains the type of object you would normally use to display information in that cell (e.g. a text field, a check box, etc.).

Now if you’ve never noticed the Type property on the Field tab in the Object palette before, you’ll want to check it out because it’s about to come in real handy for setting-up a dynamic table column which consists of cells which are all subforms. This property is used to change the type of the selected object(s). For instance, if you put a text field on a form and decide that it should have been a numeric field, you can change it’s type from text field to numeric field simply by using this property. Typically, you cannot change anything into a subform but when you select a table cell which is a text object (the default cell object type when you insert a new table into a form using the Insert Table menu command or the Table object in the Library’s Standard tab), you can, in fact, change it into a subform. So just select each cell in the column and change its type to a subform using this property. The result will be a cell which is a subform that contains a text object. Then you can change the text object into some other field type which better represents the data which will go into the cell.

Making Cell Subforms Repeatable

Unfortunately, this is one case where you’ll have to use the XML Source tab because the repeatable property isn’t available for cell subforms on the Object palette’s Binding tab. Since it’s a valid XFA setting, you can set this yourself using the XML Source.

Switch to the XML Source window by selecting “XML Source” from the top-level View menu. Then, insert the following XML inside each subform which defines a cell:

<occur max="-1"/>

This means that the cell subform goes from looking like this:

<subform layout="tb" name="Col3SF">
  <field name="Cell3" w="29.9999mm" h="10mm">

to looking like this:

<subform layout="tb" name="Col3SF">
  <occur max="-1"/>
  <field name="Cell3" w="29.9999mm" h="10mm">

and signifies that the cell will have one initial and minimum instance and can have as many additional instances as you need (no maximum).

Last but not least, the script!

After all that, the script is actually quite straight-forward: To add an instance of a column just add an instance of every repeatable subform cell that constitute a column. In my sample, I have a 3×3 table and each subform cell is named “Col3SF”. That means that the script looks like this (in JavaScript):

Table1.HeaderRow._Col3SF.addInstance(0);
Table1.Row1._Col3SF.addInstance(0);
Table1.Row2._Col3SF.addInstance(0);
Table1.Row3._Col3SF.addInstance(0);

Then, to remove an instance of the column, just do the reverse: Use the Instance Manager’s removeInstance method to remove an instance of each cell subform.

Bug when adding/removing table cell instances

Of course, everything was going great until now… Unfortunately, I discovered a little snag in playing with table column instances while making this sample. Fortunately, the bug has already been fixed in Acrobat 8.

Description

The first manifests itself when the second new instance of a column is added (by adding an instance of a column, I mean adding an instance of each cell subform in each row which collectively constitute a table column — as in my sample script above earlier). As of the second new instance, the cell in the header row will stop appearing in all subsequent instances.

There’s also an issue with removing column instances (by removing an instance of each cell subform). In this case, all new instances are removed until what was originally the first instance to be added is removed, leaving only the original, initial instance. What happens is that part of the last instance to be removed remains rendered on the form which doesn’t really look nice because it looks like the instance is still there (even though you can’t click on the cells anymore).

The problem is that the form’s layout isn’t properly updated after columns are added or removed.

Workaround

Luckily, there’s the xfa.layout object which gives us a solution to this problem when version of Acrobat prior to 8.0 are used to render the form. More specifically, versions 7.0.5+ since table support didn’t appear until then, when Designer 7.1 was released.

Using the xfa.layout object, you can tell Acrobat to re-render the form at your command. This effectively repaints the entire form and gets rid of any artifacts (those being the incorrectly rendered column instances). So, after adding or removing column instances, just place the following command:

xfa.layout.relayout();

Please use this with caution, however, since it may adversely affect the performance of your form (since this statement will essentially cause the entire form to be re-rendered every time a column is added/removed). That’s why I check the version of Acrobat that’s rendering the form in my sample so that I know whether I need to apply the workaround or not.

The other workaround is simply to use Acrobat 8.0 to render the form (save is as an “Acrobat 7.0.5 Dynamic PDF Form” but open it in Acrobat 8.0). Acrobat 8.0 now properly renders all instances as they get added or removed.

Fix

Please refer to the Bug List for updated information on the version(s) affected by this bug as well as if and when it was/will be fixed.


Posted by Stefan Cameron on October 28th, 2006
Filed under Acrobat,Bugs,Instance Manager,Scripting,Tables,Tutorials

Image Field Z-Order Problems

Description

If you’ve ever tried to use an image field’s contents (an image) as the background for a form and wanted to let the user change it at run-time, you’ve probably run into problems if you had placed objects on top of this image field. The most common result is the image field’s contents always appears on top of all other fields which are technically higher in z-order (“in front” in the hierarchy) than the image field.

While this may seem like buggy behaviour, it’s not technically a bug and I’ll try to explain why it isn’t.

The behavior occurs because of the way image fields are translated from XFA objects to AcroForm objects when your form is saved as a PDF and rendered in Acrobat.

The PDF format has a definition for what is called an Annotation object which represents essentially all the “extra” stuff you can add to PDF documents such as comments, stamps, highlighting, etc. When it comes to image fields, their contents is actually an Annotation in the PDF layer as opposed to contents in the XFA layer. Since the PDF layer is above the XFA layer, Annotations can’t be placed behind objects on the XFA layer — hence why the contents of an image field is always displayed above any objects that may be “in front” of it in the XFA hierarchy (z-order).

Workaround

Unfortunately, there aren’t any workarounds to this problem if you want users to pick the image field’s contents at run-time (in Acrobat) or if you specifically need to use an image field object.

If you are able to restrict the possibilities to a known set of images that you can store in the form at design-time (in Designer), however, then you could embed all the images in separate read-only image objects (note that this is a static image object — not an image field object) and then change their presence attribute (make one visible and all the others invisible) when the user performs an action that would’ve let them change the background image.

Since static image objects (as opposed to image field objects) render their contents in the XFA layer at the moment the form is rendered in Acrobat, their contents appears “behind” all the other objects, as is desired in this case.

Fix

Please refer to the Bug List for updated information on the version(s) affected by this bug as well as if and when it was/will be fixed.


Posted by Stefan Cameron on October 4th, 2006
Filed under Acrobat,Bugs

Bug: Cannot Override Calculations

Description

The XFA language supports fields which are calculated yet still overridable by the person filling the form. For instance, you may have an invoice on which you calculate the tax associated with a purchase using a calculated field yet, for customers from other countries, you would like to allow them to override the calculated tax amount and enter the correct amount themselves.

To do this, you would use the Object palette’s Value tab and set the field’s Type property to Calculated – User Can Override. You could even go as far as specifying an Override Message which would appear if the user attempted to override the field’s calculated value.

In theory, when the user attempts to override the field’s calculated value, either the Override Message or, if none was specified, an Acrobat default message appears warning that the field is calculated and gives the user the option to continue with the override or to cancel. If the user proceeds with the override, subsequent calculations use the override value instead of the original calculated value regardless of subsequent changes to other fields which the overridden field’s calculations were dependent on.

Unfortunately, there’s a bug in Acrobat that causes the overridden value to be discarded. It manifests itself in a couple of different ways:

  1. Dynamic PDF: Not only will the value specified by the user (the override) not be retained but the Override Message (if specified) won’t be displayed. The behavior is simply to let the user type-in a value but then throw it away and run the calculation again without any warning.
  2. Static PDF: In this case, the Override Message (if specified) is displayed and the user has the option to discard their change or proceed with the override but the specified value is discarded regardless of what they choose to do and the calculation is run again.

Workaround

You could use two fields and a check box where the first field is simply “Calculated – Read Only” and the second field is the override value and is invisible until the check box is checked.

All calculations which depend on the calculated field in question would need to verify the value of the “override” check box: If it’s unchecked, calculations would use the value from the first field which itself may be calculated. If it’s checked, they would use the value from the second field which contains the override value.

Checking the check box would cause the first (calculated) field to become invisible and the second (override) field to become visible. You could even display an Override Message when the check box gets checked and use the

xfa.host.response("Question", "Title", "Default Value")

statement to give the user the option to cancel the override (the return value is null if the user picked the Cancel button).

Fix

Please refer to the Bug List for updated information on the version(s) affected by this bug as well as if and when it was/will be fixed.


Posted by Stefan Cameron on August 29th, 2006
Filed under Acrobat,Bugs