Posts in Category "layout"

Tables with Variable Numbers of Columns

Support for tables in Designer assumes you know in advance how many columns there will be in your table. Occasionally this will not be the case.  You might want a table that adapts to the number of columns that occur in your data.  In other words, you want a table with rows where a cell is represented by a repeating subform.  And while Designer doesn’t support creating such a table via the table UI, it is possible to create one using a bit of script

The technique involves leverages the fact that tables and rows are really just subforms with special attributes.  So when designing the form, we create:

  • A subform to represent the table.  Make it a subform where content flows top-to-bottom.
  • Inside the table subform create one or more row subforms.  Make the row subforms flow left-to-right.
  • Inside each row subform, create combinations of fields, text and subforms that will be treated as columns.
  • It’s best to make the cells growable in width and allow the layout engine to determine best fit.  If you want control over width, leave the columns growable and set the columnWidths attribute on the table subform (see script below)

And then to turn this into a table, add this initialization script to the table subform:

// mark this subform as a table
this.layout = "table";

/** 
* if needed, we could explicitly set the column widths * If left alone, the columns will be auto-sized */ //this.columnWidths = "1in 2in 2in 2in 1in";

And then for each row in the table, add this initialization script:

// mark this subform as a row
this.layout = "row";
if (this.instanceIndex === 0) {
    // mark the first row as a header
    this.assist.role="TH";
} else {
    // mark each subsequent row as a regular table row
    this.assist.role="TR";
}

Note that setting the role attribute is very important for accessibility — it allows the screen reader to use the table header cells to describe the contents of the table body cells. Here is a sample form that implements this technique — along with sample data for previewing.

Fix Layout Problems

This is the fourth of four exercises that were part of the LiveCycle best practices lab at MAX.

Fix Layout Problems using XFADebugger

The purpose of this exercise is to fix layout problems by analyzing their PDF with XFADebugger.pdf.

In Designer, open: EX4 LayoutProblem.pdf (with preview data file: afiList.xml)
In preview, notice that :

  • Content was pushed off the first page
  • The second page is truncated

Open XFADebugger.pdf in Acrobat. Use the “Load XFA/PDF file” to open EX4 LayoutProblem.pdf.

  • Explore the content in the debugger view. Expand the trees. Click on nodes.
  • Click the “Find Warnings” button several times to see various warnings reported.
  • Fix the warnings in Designer

Here is the file with the layout problems fixed: EX4 LayoutProblem (Solution).pdf

Border Control

In these times. many countries are interested in border control.  Today I’m interested in controlling field borders. Did you know that fields have two borders? There is a border around the entire field (including the caption) and there’s a border around just the content part — excluding the caption. These are edited in two different places in the Designer UI.  Field border is specified under the border tab:

The border around the content portion is specified in the "Appearance" selection of object/field:

Both of these borders can be manipulated in script.  Have a look at this sample form to see what I mean. 

You might notice that the scripts in the sample form do not use the field.borderColor or field.fillColor properties. These are shortcuts — convenience syntax that simplifies the underlying property structure. And while they’re convenient, they don’t give you full control. Most notably, they control only the outer border, do not give access to the widget border. 

As you look at the script in the sample form, you will notice some interesting things:

1) script reflects syntax. 

The (simplified) XML definition of a border looks like this:

<border presence="visible | hidden">

    <edge presence="visible | hidden" thickness="measurement">
        <color value="r,g,b"/>
    </edge>  <!-- [0..4] -->

    <fill presence="visible | hidden">
      <color value="r,g,b"/>
    </fill>
</border>
            

A script that changes a border fill color (border.fill.color.value = "255,0,0";) is simply traversing the hierarchy of elements in the grammar.

2) Edge access is tricky

The syntax to get at the four edges uses the "getElement()" method, whose second parameter is the occurence number of the edge.  Note that the sample always sets all 4 edges.  This is because edge definitions are inherited.  e.g. if only one edge is specified, then modifying the single edge property impacts all four edges.  The problem is that you don’t always know how many edges have been specified, so it’s safest to set all four explicitly.

3) Show/hide with the presence property. 

You’re likely accustomed to using the presence property of fields. That same attribute applies on the components of a border:

border.presence = "hidden";      // hides entire border — all edges and fill
border.fill.presence = "hidden"; // makes the border fill transparentborder.getElement("edge", 0).presence = "hidden"; // hides the first edge

4) There’s more.

There is more to the border definitions than I’ve shown you here. Using script you an control the four corners, fill patterns, edge thickness, border margins and more.

5) There are other borders

Subforms, rectangles, draw elements and exclusion groups all have border definitions.

Debug merge and layout: Updated

Almost two years ago I published a blog post with a tool that I developed to help debug data merge and layout problems: http://blogs.adobe.com/formfeed/2009/05/debug_merge_and_layout.html

I continue to use this sample pdf on a regular basis.  It has a reserved location among my desktop icons for quick access.  But occasionally I’d run into PDF forms that cause it to hang.  So finally I took the time to find and fix the problem.  While I was at it, I took care of a couple other minor issues.  The updated form is available for download here.  I hope you find it useful.

Disable your form in older versions of Reader

When designing forms that require newer versions of Reader, you may want to be intentional about the experience users get when they open the form in older versions of Reader.  Today if you target a form for
Reader X and open it in Reader 9, you get this dialog:

After you’ve dismissed the message, Reader goes on to display the form as best it can.

The problem is that "as best it can" could lead to a very poor experience.  If you have functionality in your form that relies on a more recent Reader, then in all likelihood, your form is broken.  This could manifest itself in any number of ways.  e.g. Validations not firing correctly or the form not displaying correctly. 

As a form author, you’d prefer to prevent the user with an old version of Reader from having any interaction with the form.  No experience is better than a bad experience.  The topic for today is a design pattern to author a tailored experience for the user with an older Reader.

Design Pattern

There are probably several ways this can be done. I’ve chosen to add a version-checking subform below the root.  This version-checking subform has the following properties:

  • Contains content that warns the user that their version of Reader is not new enough
  • Points the user to the reader update page
  • Has a form field with the required Reader version number
  • Has an initialization script that compares the Reader version to the required version
  • If the Reader version is not new enough, the initialization script will remove instances of other subforms below the root and make sure the version checking subform is visible.
  • If the Reader version is new enough, the initialization script will add instances of other subforms below the root and hide the version checking subform

For this script to be effective, the other subforms below the root must be defined with occurence settings where the minimum is zero and the initial number is also zero:

I have attached a sample form that uses this technique. The form is targeted for Reader 10.  If you open it in version 9, you will get the version check warning experience.

Merging text to a caption

In your form design you have two choices when it comes to labelling your fields.  You can either place the descriptive label text in a field caption or you can create a separate text object with the label text. 

There are pros and cons to each, but today I am going to try and convince you to use captions — and give you a tool to make it easier to create captions.

Whether your descriptive text is stored as a separate object or whether it is specified as a caption, in most cases we can achieve the same visual effect with either. Admittedly there are some layout conditions where a caption cannot be used.  e.g. where the label extends both above and to the left of the field.  But this is the exception.

The advantages of using separate objects:

  1. Absolute control over label positioning
  2. Can be less effort to author in this mode

The advantages of using captions:

  1. Far better accessibility experience
  2. Results in smaller form templates with better performance
  3. Lower form maintenance

Accessibility

Field captions can be used to generate a good accessible experience.  The caption text can be used as the text that the screen reader uses to describe the field. Obviously when it is stored as a separate object we do not have this correlation. When the label is a separate object, we have extra “noise” as the screen reader traverses the read order of the document. The read order will encounter the label and the field as objects that will be read separately.  If they’re not consecutive in the read order, there is potential for even more confusion.

Form Size

As I’ve mentioned before, form size matters. If your form is delivered with rights enabled and/or with certification, the time to open the form is directly impacted by the size of the form template.  This is because both RE and certification use encryption and we need to validate that the form has not been tampered with at open time.  To make a long story short, the validation involves loading the form template more than once.  If there are fewer objects and less syntax, the form will load faster and open time will be reduced.

Lower form maintenance

When a caption is combined with a field, then all operations treat the field and caption atomically: 

  • A script to make a field invisible will also hide the caption. 
  • A page break will never separate a field from its caption, but could separate a field from label text. 
  • Moving or deleting a field in Designer automatically does the same for the caption. 

The Form Conversion Problem

My suspicion is that very few form authors consciously choose to separate the label from the field.  In most cases this is a byproduct of a conversion process from some other format to XDP.  The form author’s starting point is a form design where all fields are separated from their caption.  The question becomes: is it worth the time and effort for an author to combine these objects?  Is the net gain worth the investment?  The answer is dependent on your own scenario — and dependent on the amount of effort to fix the form.

Designer has a built-in function that will combine text and field into a combined field with caption.  You select the text and the field and choose “Layout/Merge as Caption”.  The problem you’ll find is that while designer does combine the objects, it does not preserve the layout, positioning, font and paragraph properties of the label when converted to a caption.  I think you will see this function improve in future versions of Designer.  But in the mean time, this is also a function that can be customized using a designer macro.  Here is a designer macro you can install that will combine labels with fields when the label is positioned above the field. 

You should find that the macro works fairly hard to give you a finished result with the same visual appearance as the original.  Here is a sample PDF with the separated label/text on the left and the combined result on the right. 

As I was writing the macro, I realized there are cases where the end-user might want a different behaviour than I’ve provided.  For example, when the caption is wider than the field, should we expand the field to match the width of the caption? That was the choice I made. If you prefer a different result, you can tailor the macro to your own needs.

Enjoy.  Hopefully there’s enough of a starting point here that someone could expand the macro to also handle captions placement to the left/right/bottom of fields.

Nested Master Pages

Fragments and Master Pages

If you are designing your forms using fragments, you will likely have done some work to get consistent master page definitions in your final document. The strategy most form authors follow is to define a set of re-usable master pages and include them in their host template.  All included fragment subfoms will then make use of the master page definitions found in the host template.

But what many form authors do not realize is that it is possible for the fragment to bring its own set of master pages into the host document.  The strategy is to define the collection of master pages as a child of the fragment being included.  I have created a sample XDP to illustrate this.  Note in the hierarchy that there are three sets of master pages: one for the root subform, and one each for the group1 and group2 subforms:

In this example, the group1 subform will look up its master page definitions in its nested master page collection. Same for group2.  And if group1 or group2 were to be included as a fragment, their master page definitions would be automatically included, since they’re part of the subform.  If group1 and group2 used the master page definitions defined under form1 and were included as a fragment, they would then use the master page definitions found in their host document.

Referencing Master Pages

Assume we have a master page that is defined with this syntax:

<pageArea name=”Page1″ id=”Page1_ID”>

In Designer, when the pagination option is: ‘ Place: On Page: “Page1″ ‘, the syntax generated by Designer will reference this page by name:


<subform name=”usePage1″>
    <breakBefore target=”Page1″ targetType=”pageArea”/>   

However, we can also reference by id:


<subform name=”usePage1″>
    <breakBefore target=”#Page1_ID” targetType=”pageArea”/>   

Referencing pages by name is the best strategy when you are sharing a global set of master pages. As long as you are consistent in your naming scheme, the “Page1″ referenced by your fragment will be replaced by “Page1″ found in the host template.

However, if your master pages are embedded in your fragments, then you don’t want to share page definitions. In this case there is less ambiguity if we use ID references instead of name references.

In fact, there is a bug in the current shipping products where if the top-level “Page1″ is in use when group1 is added to the layout, then “Page1″ will continue to be used for “group1″ — but it is the wrong “Page1″.  Here is a sample XDP to illustrate the bug.  The workaround is to either use unique names for your nested master pages, or switch to use id references.

Using unique names is easier. Using id references means either hand-editing the XML source or writing a designer macro to change the syntax.

Display an XML Spreadsheet

Today’s blog is on a topic that I’ve intended to tackle for a long time. I finally got the opportunity to work on this after a customer contact.  The end goal is to add spreadsheet data to a PDF form.  Copy and paste aren’t enough — we need to preserve the formatting of the cell data.

The solution involves converting the spreadsheet to an xml format and then populating the form with the cell data from a nice friendly xml grammar.

To get consumable XML from within Excel, choose save-as “XML Spreadsheet 2003″.
You can read about this format at:

http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats

and

http://msdn.microsoft.com/en-us/library/aa140066%28office.10%29.aspx

The spreadsheet grammar has all the cell and row properties needed for rendering: formatting, styling, widths, heights etc.  The only problem is that the format does not lend itself to populating a form via data binding. There are too many special cases to construct sensible binding expressions.  Instead, I’ve taken the approach of using JavaScript to parse the spreadsheet format and to turn it into corresponding formatted XFA fields.

Before I go further, I should mention that my implementation took a few shortcuts.  I didn’t implement the full XML Spreadsheet definition.  Some parts of it I implemented fairly poorly.  My intention wasn’t to provide a finished product, but rather to give you a sample you could build on.

Here is the sample form: spreadsheet.pdf.  I trolled around looking for interesting spreadsheets to test with and found the financial statement for Colgate.

To use the form, follow these steps (from Acrobat — not Reader):

  1. Take an excel spreadsheet, save-as “XML Spreadsheet 2003″.
  2. Click “Load XML Spreadsheet” to embed it as an attachment
  3. Select the region to display
  4. Click “Display”
  5. Repeat steps 4,5 with other regions.  Or go back to step 2 with a different spreadsheet.

Note that Acrobat won’t allow you to do step 2 while the spreadsheet is open in Excel. And if you’ve picked a big spreadsheet it will be slow.  Go ahead and try it with a couple of the spreadsheets you have on your hard drive.

The structure of the form is simple:

  • A Table subform that gets repeated for every worksheet. It has a conditional break so that every worksheet after the first one occurs on a new page.
  • A repeating Row Subform that corresponds to a spreadsheet row
  • A repeating Cell Subform with a Data field that repeats for each column

The only really tricky part of the layout is that the cells are explicitly positioned — not flowed left-to-right.  The reason for that is so that I could immitate the Z order of Excel.  When cells overflow they overwrite cells to their right. To make this work I needed the cells to be added in order from right-to-left so that the left-most cells are highest in Z-order.  I needed them to be positioned so that when a cell overflows it doesn’t push its neighbors over.

The script is another matter. There’s over 700 lines of script that are fairly complex.

It starts by prompting the user to add the spreadsheet as an attachment (with the doc.importDataObject() method).  Once the spreadsheet is stored as an attachment it extracts the contents into an E4X XML object.  Then most of the work is simply parsing the style and size information from the spreadsheet and applying those to the cells.

Note that the row and cell columns are unbound.  That means if you looked in the form data for the spreadsheet information, you wouldn’t find it.  But not to worry, the data is close by — available in the attached spreadsheet.

 

Duplicating subform structures

This week I was asked to look at a problem where the customer wanted to have two sets of repeating subforms reference the same data.  With normal data binding techniques this doesn’t work. Data may be bound to only one subform. The solution in this case was to bind one set of subforms to the data, and synchronize a second set of subforms to the data using JavaScript.

In the attached sample, the first set of subforms is used for data collection, the second set is used to present the data as prose. In other words, we have two views of the same data: one for editing, one for final presentation. Of course, in many scenarios this would be done as two separate forms.  But we can make it work as a single form.

In the sample, the subform named "Me" is a hierarchy of subforms that binds to data that looks like:

<Me>
    <Fname />
    <LName />
    <BirthDate/>
    <Gender/>
    <Spouse>
        <Fname/>
        <LName/>
        <BirthDate/>
    </Spouse>
    <Dependent> <!– repeats –>
        <Fname/>
        <BirthDate/>
        <Gender/>
        <Hobby> <!– repeats –>
            <HobbyName/>
        </Hobby>
        <Pet> <!– repeats –>
            <PetType/>
            <PetName/>
        </Pet>
    </Dependent>
</Me>

The sample includes a set of repeating subforms that use "normal" (named-based) binding to connect to the data.  The second set of subforms and fields ("Summary") are declared with no data binding.  The Summary subform has a calculation script that synchronizes its descendent objects to the data. 

The calculation script iterates through the data under "Me", looking for fields and subforms that have the same name as the data. 

  • Where there’s a name match to a field, we assign the value. 
  • When there’s a name match on a subfom we recurse one level deeper into the hierarchy. 
  • When there’s a grouping element in the data (a dataGroup) and no corresponding subform, we look for an instance manager so that we can create a subform instance to match to the data. 
  • When there are optional subforms left over that don’t match data, we remove them. 
  • We keep track of which subforms and fields have been "bound’ to data so that the same objects don’t bind twice.
  • Because the logic is in a calculation, it will re-fire whenever the referenced data changes (due to dependency tracking)

There are other ways we could have synchronized data to a set of subforms, but this technique has some advantages:

  1. The logic is generic and can be applied to any form.
  2. We can add and remove fields/subforms without worrying about any bookeeping to match the objects to data.  The logic is centralized and will work as long as the form author uses names that match data.

The only caveat is that this solution won’t scale all that well to very large forms. The re-binding script runs every time a data value changes, and is looping through all the data nodes and form objects.  As the data set grows large, this logic will begin to slow down.

Updated Form Debugger

Last year I posted a sample form that was useful for debugging the internals of dynamic forms: Debug merge and layout.  In the mean time, I’ve discovered a couple bugs.  Time for a maintenance release.  The main changes are:

  • Does a better job finding the coordinates of objects — anything inside a subformSet, fields inside exclusion groups.
  • Reports the breakBefore and breakAfter properties correctly
  • General code cleanup and a bit of optimization

As I’ve mentioned before — this tool is useful not only as a way to debug your merge/layout, but also a good way to visualize what’s going on inside the the XFA processor.

Here’s the updated debugger form.