Posts in Category "layout"

Editable Floating Fields V2

This is a follow-up to a previous blog entry that you probably should read first.

After doing the first version of the floating field editor, I tackled some issues/enhancements:

  1. A bug in the script where if you tabbed out and tabbed back in, the editor stopped working.
  2. Enforce constraints associated with the referenced fields
  3. When the editor does not have focus, display the field values using the formatted values of the referenced fields
  4. When the editor has focus, display the field values using the edit values of the referenced fields

I have updated the previous sample form — as well as the Editor fragment.

Enforcing Field Constraints

Since the floating fields are all presented inside a single text field, there was originally no constraints on any of the user input.  Now the form will look at the referenced fields and will restrict user input:

  • Respect the max chars constraint of text fields (in the sample, they’re all limited to 10 characters)
  • For numeric fields, limit input to valid numeric characters
  • For choice list fields, limit input to the set of valid choices

Locale-sensitive Numeric Fields

When restricting the set of valid characters for numeric input, it is tempting to just go with the obvious set:
[0-9\-\.]  However for many locales, the radix (decimal) and minus symbols will be different.  In order to know which symbols to use, the form queries the locale definition.  You XML source peepers will be aware of the <localeSet> packet in your XDP files.  This has all the data for the locales that are explicitly referenced on the form. 

The symbols are stored in a format that looks like:

<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.6/">
   <locale name="de_DE" desc="German (Germany)">
      <calendarSymbols name="gregorian"> … </calendarSymbols>
      <datePatterns> … </datePatterns>
      <timePatterns> … </timePatterns>
      <dateTimeSymbols>GjMtkHmsSEDFwWahKzZ</dateTimeSymbols>
      <numberPatterns> … </numberPatterns>
      <numberSymbols>
         <numberSymbol name="decimal">,</numberSymbol>
         <numberSymbol name="grouping">.</numberSymbol>
         <numberSymbol name="percent">%</numberSymbol>
         <numberSymbol name="minus">-</numberSymbol>
         <numberSymbol name="zero">0</numberSymbol>
      </numberSymbols>
      <currencySymbols> … </currencySymbols>
      <typefaces> … </typefaces>
    </locale>
    <locale> … </locale>
</localeSet>

I was able to extract the number symbols with this function:

function findLocaleNumberSymbols(vRefField) {
    var oSymbols = {decimal: ".",
                    minus: "-"
                   };
    var vLocale = localeSet[vRefField.locale];
    if (typeof(vLocale) !== "undefined") {
        var vNumberSymbols = vLocale["#numberSymbols"].nodes;

        for (var i = 0; i < vNumberSymbols.length; i++) {
            oSymbols[vNumberSymbols.item(i).name] =
                                     vNumberSymbols.item(i).value;
        }
    }
    return oSymbols;
}

Using the numeric symbols the form is able to more accurately restrict input for numeric fields.

Choice List Fields

The last field in the sample is a reference to a choice list with the American states.  Try out the editing experience here.  It’s pretty cool:

  • Input characters are limited to the set of valid choices
  • As soon as you type enough characters to uniquely identify a state, the rest of the input is completed automatically

Use Formatted and Edit Values

In the updated sample. the editing field now behaves like any other widget.  When you tab in, referenced field values display in their edit format.  When you tab out, the referenced fields display their formatted value.  In the sample you will notice that the currency and date values change when you tab in/out.  This is functionality that happens automatically on normal fields but had to be emulated in script for this sample.

You don’t have to read through all the script to figure out how it works, but it is worth noting that you can access a field value in three different ways:

  • field.rawValue — the canonical value as it is stored in the data
  • field.formattedValue — the value with the display pattern applied
  • field.editValue — the value with the edit pattern applied

Note that if a format or edit picture/mask is not supplied, there are default patterns for numeric and date values.

Hook up via the enter event

Previously, the editor field tapped into the script object by delegating its initialize, change and exit events.  it now also needs to delegate the enter event:

form1.LetterEdit::change – (JavaScript, client)
scEditFF.handleChangeEvent();

form1.LetterEdit::enter – (JavaScript, client)
scEditFF.handleEnter();

form1.LetterEdit::exit – (JavaScript, client)
scEditFF.handleExit();

form1.LetterEdit::initialize – (JavaScript, client)
scEditFF.initialize(this, Letter, "#c0c0c0", 10);

Futures

There are more constraints that could be enforced, e.g. digits before/after decimal but those

Editable Floating Fields

Floating fields have always been a bit tantalizing because while they provide you with the output you want, many users want to be able to edit directly in the floating fields.  They want the field values nested inside the static text to be actual widgets.  Unfortunately, this is one of those enhancements that never seems to percolate to the top of the priority list (but don’t let that stop you from requesting this feature!).

In the mean time, I’ve worked up a sample that simulates the interactive behaviour.  Have a look here.  The way the sample works, it that it has two objects: the text object with the embedded fields and an interactive field that’s the same size, placed over top of the text object.  When the form initializes, it populates the interactive field with the content of the text element.  Then using the change event, it prevents the user from typing into the ‘boilerplate’ parts of the text but allows them to type into the field portions.

But before talking too much about the mechanics, I’ll describe how to re-use this code in your own form:

How to add the floating field editor to a form

1. Add the scEditFF and scXML fragments to your form.  These script objects hold all the logic needed to make the sample work. 

2. Add your draw element with floating fields as you normally would. 

3. Add a text field that allows multiple lines and also enables rich text.

4. Add initialize, change and exit events to your field:

form1.#subform[0].LetterEdit::initialize – (JavaScript, client)
scEditFF.initialize(this, Letter, "#c0c0c0", 10);

form1.#subform[0].LetterEdit::change – (JavaScript, client)
scEditFF.handleChangeEvent();

form1.#subform[0].LetterEdit::exit – (JavaScript, client)
scEditFF.handleExit();

The parameters to initialize are:

/**
* initialize a field to be a floating field editor for a given
* text object
* @param vField – the field that is displaying/editing the floating
* field content
* @param vText – the text object holding the floating fields.
* @param sBGColor – the color to shade the field areas.
* Specify as a CSS color value. e.g. #101010
* @param nEmptySpace – the number of spaces to use to
* render an empty field
*/

Behaviours

Some things you will notice when using this sample:

  • When the floating field editor initializes, it makes itself visible and hides the text object
  • You can’t tab between the floating fields.  Since all the fields are housed in the same widget, it means you tab in and out of the entire group.
  • Reserve space.  Notice when a field is empty and you type in, the space collapses to the character(s) you’ve typed.  Similarly, once you empty the field, it expands back to the reserved size.  Notice also that you cannot delete the reserved spaces.
  • Extra leading space.  There’s a (non-breaking) space at the beginning of each floating field.  This is needed to ensure that the user input is always rendered with the shaded field background.  When you type into rich text, the typed text always inherits the format of the character preceding the caret.  In order to make sure that content inserted at the beginning of a floating field gets shaded, I needed to add an extra (un-editable) space character.  I initially tried using an invisible character such as a zero width space, but found that in Acrobat 9 these special Unicode spaces are made visible when they’re part of field content.  I suspect that was done for security reasons.
  • Field Updates.  When you exit the widget holding all the floating fields, the exit event fires and will update all the fields that correspond to the editable areas.
  • The script turns off Acrobat’s field highlighting.  It just looks a bit weird when the entire widget holding the floating fields is highlighted.
  • Versioning.  Notice the sample has a version checking button.  It would not be surprising if I needed to update the script for bug fixes or small enhancements.

How it Works

Floating fields are represented by some custom XHTML syntax:

<span xfa:embedType="uri" xfa:embedMode="raw" xfa:embed="#FF011680"/>

The concept is fairly simple.  The form parses the XHTML, finding all the embedded fields.  It then changes the syntax to:

<span style="background-color:#101010">referenced field value</span>

Meanwhile the form also collects the plain text from the XHTML and determines the start/end offsets of the field data.  When the user types into the field, we use the change event to ensure that their edits are within the range of one of the referenced fields.  If the edit is not in range, the form ‘swallows’ the event.

If the field has no value, we replace the field content in the display with a string of non-breaking spaces.

When exiting the field, we gather all the ranges of text that correspond to fields and push them back out to the field objects.

It’s all fairly simple in concept, but the code ended up being fairly complex.  One of the hardest parts was handling undo/redo.  Undo and redo will modify a field without firing a change event. This means that the cached offsets become stale.  There is code that detects if the field value has been modified since the last change event.  If so, the offsets are re-calculated.

Some Possible Enhancements

It would be nice if we could limit keystrokes to what is allowed by the referenced field.  e.g. if the referenced field is numeric, allow only numeric text to be entered for that floating field

Currently if the floating fields are updated externally from the floating field editor, the changes are not detected.

We could update the referenced fields on every keystroke rather than only on exit.

The sample does not handle xfa:embedMode="formatted". If this were supported then when editing the values you would see the edit format of the field and when you exit you would see the formatted value.

Managing Tab Stops in Fields

When I was working on my previous blog entry, I struggled a bit with rendering JavaScript source code in fields.  The issue was how to accurately display text with the tab stops that are embedded in the code.  The goal was to have the JavaScript code appear with the same spacing you would see in the source editor.  For this to work, the tab spacing needed to be exactly 4 characters.  The solution involved:

  • Use a non-proportional font to display the text (courier new)
  • Figure out the width of 4 characters: a 10pt character in courier new has a width of 6 points.  4 characters is 24 points
  • Set the default tab for the field.  Since Designer doesn’t expose tab properties for fields, the form uses an initialization script to set the default tab: this.para.tabDefault = "24pt";

Tab Leaders

Having accomplished that much, it seems worthwhile to spend some time showing some of the other cool things you can do with tab settings in fields.  In Acrobat/Reader 9 we introduced tab leaders.  To see how this feature works with static text, check out Stephanie Legault’s tutorial.

That’s fine for static text, but what about fields?  If you want to use tab leaders in your form fields, you need to understand the field properties that control tab behaviours.  One way to get there is to read the relevant portions in the XFA specification.  Tab settings are paragraph properties.  A field’s paragraph properties are defined in the <para> element (Part 2/Template Specification/the para element).  Now, you could fill your head with all that book knowledge or you could code from examples

The first page of the sample shows some variations on tab settings.  Each sample field has an initialization script to set the tab properties.  In each case there is a corresponding field that displays the resulting <para> element syntax.

tabDefault

This property on the <para> element controls the default tab setting.  If you want a tab stop every half inch, this would look like:

<field>
    <para tabDefault="0.5in"/>
</field>

To set this via script:

form1.tabExample1.sample::initialize – (JavaScript, client)
this.para.tabDefault = "0.5in";

tab-stops

The general format of a tab leader is: "[alignment] [leader] location ".  You can string a bunch of these together to specify multiple tabs.  If you look at the sample form you will see some variations.  The alignment ( left (before) / right (after) / center / decimal ) determines how the text is aligned at the tab location.  The leader parameter determines how to fill the space leading up to the tab position — space, dots, rule, characters.  The 3rd parameter is the actual tab position.   Hopefully it’s all self-explanatory from the samples.

Tabs and Data Entry

So far, this discussion has been assuming that we’re rendering data that has embedded tabs.  Do tab settings work with interactive fields?  Well, yes and no.  The tab settings work fine.  The problem is, how exactly do you enter a tab character into an interactive field?  The tab key moves you to the next field.  The workaround the example uses is to define a key sequence that will insert a tab character.  Pressing "shift" + "space" will insert a tab character.  The way this works is that the field has a change event to translate this sequence:

form1.#subform[0].tabExample1.sample::change – (JavaScript, client)
if (xfa.event.change === " " && xfa.event.shift) {
    xfa.event.change = "\t";
}

Tabs and Rich Text

Rich text introduces a  couple of wrinkles wrt tabs. 

The tab character is not handled by XHTML.  XHTML processing rules tell us to collapse all white space down to a space character.  To get past this, we define a special style attribute to specify a tab:

<span style=’xfa-tab-count:1′/>

The attributes that exist on the <para> element are also available as style properties: tab-interval and tab-stop.  With a plain text field, the paragraph properties are constant for the whole field.  With rich text, the paragraph properties can be re-specified with each <p> element.

The second page of the sample form has an example where a table of values is formatted into a plain text and a rich text field.  To see the code, look at the form:ready event of each field.  The script for the plain text variation is much simpler, but the script for the rich text field does some more elaborate formatting.  But back up a moment.  Likely the idea of constructing rich text is new to some of you.  The technique is fairly straightforward — build a string representing the XHTML content and then use the loadXML() method to apply the text to the field.  Using XHTML allows the second field to enhance the output:

  • The tab settings for the first line (paragraph) are different from the rest of the field (center aligned with a space leader)
  • The cents portion of the prices are displayed in a superscript
  • The tab leader is styled — the baseline is raised so the dots appear in the middle of the line rather than the bottom.

The overall effect makes the second menu field a fair bit nicer.  Imagine what someone with artistic skills could do.

Aligned Dots

Did you notice?  The dots in the plain text menu line up with the dots used in the rich text menu.  By default, the dot leaders are aligned between different objects on the page. i.e. the dots are aligned on a grid defined at the page level.

The Deep End

Ambient Attributes

When the rich text is rendered, the display properties are determined by combining the field properties with the properties specified in the XHTML.  An example would help:

<field>
  <font typeface="Myriad Pro"/>
  <value>
    <exData contentType="text/html">
      <body>
        <p>Text 1<span style="font-family:Arial">Text 2</span></p>
      </body>
    </exData>
  </value>
</field>

In this example, the XHTML has not specified a font for the string "Text 1".  Whereas the string "Text 2" is explicitly styled as Arial.  This means that the font for "Text 1" is the ambient font — the font inherited from the field definition — in this case Myriad Pro.  Same applies to
tab settings.  By default the XHTML will inherit the tab settings from the field.  If you want to deviate from the default, then specify it inside a <p> element in the rich text.  That explains why in my rich text menu example I explicitly styled the first paragraph, but allowed the remaining text to inherit the field paragraph properties.

Building Big Strings

XHTML strings can grow very large.  You need to be careful how you build the strings.  Using the " += " operator can lead to performance issues.  Appending to a large string is expensive.  It breaks down to these set of operations:

  1. allocate new storage for the larger string
  2. copy the old string to the new storage
  3. append the new string to the new storage

The larger the string and the more append operations, the more expensive this becomes.  A more efficient alternative is to build an array of small strings and at the end, use the join() method to generate one large string.  Here are two examples for comparison:;

var S = "hello world";
S += " more text";
S += " even more text";

In comparison:

var StringArray = [];
StringArray.push("hello world");
StringArray.push(" more text");
StringArray.push(" even more text");
var S = StringArray.join("");

Of course, in this specific example, the strings are so small that the performance difference would not be noticeable. But when the number of append operations grow, the array technique will become much faster.

Displaying Parallel Columns of Text

There is a common form design challenge where we want to place parallel columns of text side-by-side on a page.  We want the text in each column to grow vertically and to split over to the next page(s) as needed.

One Growable, Splittable Field

First the basics:  Before tackling side-by-side fields, how to make a single field growable and splittable:

  1. Make sure you have saved your form as an "Adobe XML Dynamic Form" or as an "Adobe XML Form".
  2. Drag a text field on to your canvas. 
  3. On the Object/Field tab select: "Allow Multiple Lines"
  4. On the layout tab select: "Expand to Fit" under the Y: measurement.
  5. For all subforms above this field in the form hierarchy select "Auto fit" under the layout tab
  6. For all subforms above this field in the form hierarchy select "Allow Page Breaks Within Content" under the Object/subform tab.

If you fill that field with enough data, it will grow to the bottom of the page.  Fill it with more data and it will cause an overflow.  A new page will be added and the field will split between pages.

Two Growable, Splitable, Positioned Fields

The next part of the challenge is to make this work for growable fields situated side-by-side.  If the parent subform has positioned layout, then it’s fairly easy.  Just place the two fields side-by side.

Two Growable, Splitable, Flowed Fields

If the parent is flowed left-to-right (Western text) then you won’t get the effect you want.  If the left-most field has enough data to overflow the page, then the result is that the other growable field doesn’t appear until the second page.  This is because in a flowable environment, the layout algorithm will finish layout for the first field before starting the second.  Once we have spilled over on to a new page we don’t go back and fill in more data on a previous page.

To make parallel columns work in a flowed environment, add the two fields to a table.  Make them sibling cells in the same row.  The layout algorithm for table rows will negotiate the row height between all the cells, and will make sure they start and end together.

Splitting Problems

When the layout algorithm wants to split content, between pages, there’s a step where it negotiates a split point.  When there are side-by-side objects, they need to be splittable at the same point in order to successfully split.  Some objects are not splittable: e.g. rectangles, images.  If they are side-by side with a splittable object, the form will not split. 

Multi-line fields are splittable by default.  However, for side-by-side multi-line fields to split, we need to find a common baseline.  i.e. we cannot split in the middle of a line of text.  We either split above or below a line.  Side-by-side text fields need to have lines that end at the same point in order to be splittable.  There are a large number of properties that can cause lines to be offset from one another — including: top margin, paragraph space before/space after, line spacing, vertical justification, typeface, font size, vertical scale. For example, if two side-by-side fields have top margins that differ by 3mm, then they will not share any common baselines — and we will not find a split point.  We are working on changes to the split point calculation that will be more tolerant of baseline mismatches.  But until then, you need to be very careful to make the properties of your fields exactly the same.

Blank Pages in Output

Once you have a form where content is not splittable and is too big for a page, you will often see blank pages in your output.  The reason for this is because of the way the layout algorithm fills pages.  It works something like this:

  1. Attempt to place an object on a page
  2. If the object is too large for the remaining space on a page, attempt to split the object to fit on the page
  3. If the object does not have a split point in the available space, start a new page
  4. If the object does not fit on the new page, attempt to split it.  If it does not have a split point in the available space, start another new page.
  5. If the object still does not fit, then place the object on a page and truncate the content

Newer versions of the XFA processor have managed to reduce the number of blank pages we see.  In a sense, I regret that we "fixed" those conditions because they are usually a warning sign that there is a layout problem that needs to be fixed.

Sample

A common use for side-by-side growable fields is to show text side-by-side with a translation.  The attached sample shows the preamble to the Declaration of Human Rights in French and English.  It shows them a second time with the fields offset slightly so that they couldn’t split.

Debugger Support

The debugger has been updated to include a new warning for splittable table rows that have more than one multi-line field.  Eventually the same warning should appear for positioned content.

BTW if you’re using the debugger, you’ll want to monitor the post where it was introduced for occasional updates.

DIY Column Chart

If you think about it, the XFA grammar has all the graphics primitives you’d need to generate a basic column or bar chart.  It has lines, rectangles and text.  It has repeating elements (subforms).  Ok, so it doesn’t have a shadow effect.  And 3D bars are off the table.  But theoretically, all that stands between us and a simple chart is a bit of JavaScript.

By searching the web you can find samples of packages that render charts in browsers using various JavaScript frameworks.  So why not do the same in an XFA form?

Have a look at this sample PDF (here’s the data).  In particular, try modifying a value in the table and watch the bars update.  Try modifying a value to $200,000 so that the Y-axis needs to be re-scaled.  Cool.

To use one of these charts in your PDF form, open this sample in Designer and turn the chart subform into a fragment that you’ll be able to bring into other forms.  When you open in Designer you’ll see some instructions that get hidden at runtime.  The components within the chart can be styled and customized as required.  The aspects that need not be set at design time are: the positions of the dynamic elements (labels, tics, bar), the contents of the labels and the size of the bars.

Options

Once the chart is sized, positioned and styled as you like, then look at the calculate script on the chart  subform to set the rest of the parameters.  At the top of the script you will see variables for controlling the rest of the chart behaviour:

  • Bar colours
  • grid line control (short tics vs. lines that extend across the chart)
  • x label staggering (prevent overlapping labels)
  • define the gap size between bars
  • Set the maximum number of labels on the Y axis
  • Column stacking (on or off)
  • SOM expressions defining the data series

In all it was around 450 lines of generously commented JavaScript. 

Oh, and lest you think this was all original, I did borrow some Java code (converted to JavaScript) to help with the Y axis label generation.

Performance Considerations

The form makes use of dynamic subforms to draw the variable elements of the chart — X and Y labels and bars. Once the subforms were created, the script resizes and positions them correctly.  A more efficient way to draw the chart would be to pre-create a fixed number of labels and bars at design time — not wrapped in subforms.  Drawing the chart would simply use the pre-created objects and hide the ones not need.  The problem with this approach is that it is harder to author — the author is responsible for making sure there are enough labels and bars.  My version of the form opts for easier design and a slightly less efficient runtime.  But either way, this approach of rendering graphics won’t scale well to larger more complex implementations. 

Debug merge and layout

It has been quiet for a while. That is because I took on a more ambitious task over the last couple weeks.  The result is a new XFA/PDF debugger tool.

In the past I’ve posted samples (tools) that help users debug their merge (form dom) and view their form layout. The new tool consolidates and extends those capabilities and implements the debugger in flash — a SWF embedded in a PDF. 

This tool will be useful to anyone having problems designing dynamic forms:

  • Subforms aren’t being created from data where you expected they would
  • Layout has a mysterious blank page
  • Garbled or overlapping layout
  • Layout did not appear in the order you expected
  • leaders and trailers aren’t being created
  • leaders and trailers are created too often
  • Content seems to be missing

If you’re encountering any of these symptoms then this tool could help.

Usage

You need to be running Acrobat (not Reader) to use the tool.  And you need to be using version 9. 

To start a debug session, populate your PDF form with data and save it.  Then open the PDF from the XFADebugger.pdf tool.  You will see a snapshot of the form represented in three vertical boxes:

  • Form DOM tree
  • Data DOM tree
  • Layout — page display (with content areas rendered as grey boxes)

From there on it is hopefully self-explanatory.  You can expand/collapse the trees.  Nodes in the trees are colour coded:

  • grey — node is not bound
  • black — node is bound once
  • blue — data node is bound to more than one form node
  • green — represents a subform leader/trailer that has been added by the layout process

Selecting a node in the form tree will:

  • if bound, highlight the corresponding node in the data tree
  • if not hidden, highlight the area on the page display where that node is rendered.
  • Display any interesting attributes/properties that impact merge and layout

Selecting a node in the data tree will (if bound)

  • select all the form node(s) that are bound to this data
  • display the attributes and highlight the corresponding form node(s)

Warnings

The tool reports on suspicious conditions on the form that could impact merge or layout.  Clicking on the "Find Warnings" button will cycle through any warnings found in the form.  For each warning, the corresponding nodes are highlighted and the the warning text appears in red.

These are the warning conditions detected:

Object extends outside its parent container
Just what it says.  When the offending form node is highlighted on the page layout in dark blue, it’s parent node is highlighted in pale blue.

leader/trailer subforms must not be flowed
Leader and trailer subforms must always have positioned content.  As you might imagine having a variable-sized leader/trailer would make it pretty difficult for the layout algorithm to reserve space for the leader/trailer subform.

Subform is splittable but cannot split because the parent is not splittable
Designer also warns about this condition.

Object is growable, but not-splittable.  With enough data, it could grow too large for its content area
If an object can grow vertically without any upper limit, it could eventually grow too big to be rendered inside a content area.

Object is growable but its parent is not.  With enough data, it could grow too large for its parent
If you place a growable object inside a subform with a fixed layout size, you could end up with an object that’s too big for its container.

Keep with previous’ conflicts with ‘break after’ on previous element
Conflicting break/keep directives. (By the way, the keep will trump the break — but this is condition is a leading cause of mysterious blank pages in your output)

‘Break before’ conflicts with ‘keep with next’ on previous element
Same as previous except the other way around.

Repeating subforms should not specify keep with next or keep with previous
Having a keep on a repeating subform will result in the entire group of subforms being un-splittable.

Subforms with repeating children should be splittable
If a subform has a repeating child, it is likely to require a split.

Multiple repeating form nodes are bound to the same data. This might cause a different merge result when the form is re-opened
This takes more explanation.  Consider this template definition:

<subform name="S0"><occur min=1 max=10/><bind ref="S[*]"/></subform>
<subform name="S1"><occur min=1 max=1/><bind match=once/></subform>

When this form is first opened without any data, we will create two instances of <S> in the data — one for S0 and one for S1. Then when we save/close/reopen, subform S0 will bind to both instances of <S> and subform S1 will create a new instance of <S>. i.e. after save/close/reopen there is one more subform than there was before.  This is a form design issue that crops up occasionally and can be very confusing for novice form authors.

Rows with more than one multi-line field might have difficulty splitting

If you have a splittable table row with more than one multi-line field, you might find that it does not split.  The algorithm for splitting rows requires finding a common font baseline between rows on the sibling cells.  For current shipping product, the check for the baseline is very exact.  If there is any difference between the fields that can cause the lines to be offset slightly, then the split algorithm will not find a split point.  Some of the attributes that affect the position of the baselines include: top margin, paragraph space before/space after, line spacing, vertical justification, typeface, font size, vertical scale… and probably a couple more I haven’t thought of.

 

As is the nature of warnings, not all warnings are problems that need to be fixed.  Your form might report warnings that are innocuous. 

Here is a sample of a very badly designed form that manages to have (at least) one instance of each warning.

How the Tool Works

The sample has two parts.  There is a base PDF with a document-level JavaScript defining:
function PDFLoader() that will:

  • Select and open an XFA-based PDF
  • Extract an XML snapshot representing the state of the form after it has opened

The form has a page-sized embedded SWF which holds the implementation of the debugger.  The SWF has a button that calls the document-level JavaScript using a call to ExternalInterface.call("PDFLoader").

Once the SWF has the XML snapshot of the form, it renders it and doesn’t communicate with the base PDF anymore.

Other Uses

Educational

Loading up a form and
seeing the form/data/layout graphically displayed can help to get insight on how the merge and layout processes work.

Quality Assurance

There are two ways that this tool can be used or adapted to maintain quality in your forms. 

1) loading and viewing your dynamic form in the debugger lets you verify that merge and layout are happening as designed.  Just because your form looks ok on screen doesn’t necessarily mean that your data merged correctly or that your layout is behaving as planned.  You might be surprised by what you see.  You should make it a habit to check for warnings.

2) Adapt the XML snapshot to produce ‘gold data’ for your form.  When you are satisfied that your form is working correctly, produce a snapshot of the form that you can save as a baseline.  Then if your form gets modified — perhaps some cosmetic changes — you can compare the new snapshot to the baseline and confirm that any changes are as expected.

Futures

There are undoubtedly more form design problems that could be flagged by this tool. If you have suggestions for other conditions to detect, please let me know.

The form DOM could include more objects — instance managers and draw elements.  For now I’ve left them out because they clutter the form tree too much.

Updates

June 1, 2009

  • Fixed bug where field splittable status was reported incorrectly
  • Increased the tolerance when checking for objects outside their extent
  • Added a new warning: "Rows with more than one multi-line field might have difficulty splitting"

Debug Your Form Layout

Today I want to share with you a work-in-progress of a tool that you can use to debug your form layout.  The sample builds on some concepts from previous blog entries:

  • The form dom debugger that used script access to graphically display the form and data doms
  • The reporter form that loaded/opened a PDF and generated a summary of the contents.
  • The form demonstrating how to calculate layout positions

The motivation for the debugger form is simply that once you step outside the simple layout scenarios, form layout can be a complex area to debug.

The layout debugger form loads and opens an XFA/PDF and will graphically display the form dom and the page layout.  To get a feel for how it works, save this sample form to disk and then open it from the layout debugger form.  Some notes to guide you:

  • Before you opened the form there was a checkbox: "show boilerplate".  This allows you to include/exclude the static text/images/lines/rectangles etc. that occur on your form.  Often these have no impact on layout problems and there is less noise if we do not include them.
  • The left hand side of the page displays the form dom in much the same way that the form dom debugger did.  As before, you can expand/collapse the hierarchy and use the arrow buttons to scroll up or down half a page at a time.
  • The right hand side of the form shows a graphical display of the pages.  Each page is numbered and scaled proportionately.  Each page has its content area(s) drawn in a dashed line.
  • When you mouse into a field in the hierarchy, the pages are highlighted with the extent(s) of the corresponding object.  If the object has split across multiple content areas, it will display as multiple rectangular areas.  You’ll note that when you select the root of the form dom that it spans all pages.
  • There are a set of XFA properties that impact layout.  Things like: presence, layout, break, keep, overflow, etc. Each object in the form dom hierarchy displays the names of any layout properties that do not correspond to their default values.  When you mouse into the hierarchy field, the expanded property values are displayed in the top right corner of the page.
  • The hierarchy is colour coded.  Grey entries are not-splittable.  Green entries are splittable, yellow entries are overflow leader/trailer subforms that were added  by the layout process.  If you scroll to the bottom of the form hierarchy you will see a couple of "hdr" subforms that layout added.  Note that near the top of the hierarchy the first occurrence  of "hdr" appears because of its place in the form dom — not because layout added it.
  • While you are scrolled to the bottom, click on the "Date" field under page 1.  You will see it appear with a yellow warning icon.  This is because it was positioned off the page.  Adding warnings is an area where I need to do much more work.  It should be possible to detect more conditions that frequently cause layout problems.
  • When you click on a hierarchy object that is hidden, nothing in the page display gets highlighted.  That is because hidden objects are excluded from layout.

As I said, this is a work in progress.  But I felt it was far enough along to share.  I have more plans for it.  If you have specific ideas of how to improve it, please let me know.

June Update

There is a follow-up debugger effort that supercedes the sample in this entry. Please have a look here.

Layout Methods to Find Page Positions

This post will be in the deep end for many of you.  But for those who design forms that extract position information from the xfa.layout object, you might well find some answers to longstanding problems — especially if you make use of multiple content areas on your master pages.

This blog entry deals specifically with the functions:
xfa.layout.x(), xfa.layout.y(), xfa.layout.h(), xfa.layout.w().

Return Value

The description for layout.x() says: "Determines the x coordinate of a given form design object".  This is true.  Incomplete, but true.  In fact, the x() and y() methods return a coordinate relative to the parent of the object.  The offset of the root subform is relative to the content area.  To find an absolute page coordinate, you could (in theory) traverse up the hierarchy and add the x coordinate of each ancestor plus the x coordinate of the content area.  But the APIs don’t make this easy.  First of all, it’s not obvious which content area contains your object.  And if your ancestor subform originated on a different page, it will have a different offset parameter.  Finding an absolute page position is complicated.  But not impossible.

Parameters

The layout position methods all take the same set of parameters.
e.g. xfa.layout.x(<object>, <units>, <offset>);

object is self evident.  You pass in a reference to some container (field, subform, draw etc).

units is also easy. "in" will cause the function to return a numeric value representing inches.

offset is … complicated.  The documentation for xfa.layout.x() says:

"An integer representing the number of pages to offset the x coordinate of the object, beginning with the first page the object occurs on. If left blank, the default value is 0."

This is partially true.  It is true only in the case where there is one content area per page.  The full truth is more complicated.  The documentation for xfa.layout.h() says:

"An integer representing the amount to offset the height value of a form design object, beginning with the first page the object occurs on. If left blank, the default value is 0."

Hmm.  Not true at all.  It’s not an offset of the height value.  Someone forgot to copy/paste the part of the description that says "number of pages".  We’ll work toward a more accurate description in the next section…

Multiple Content Areas

For forms with multiple content areas, here is the actual behaviour of the offset parameter:

The offset parameter indicates which relative content area the object appears in.  e.g. If a subform spans 2 pages and 5 content areas, you can call xfa.layout.h with offset values of 0, 1, 2, 3, 4 to return the the height of the object in each of the 5 content areas. 

This is great but, now the problem becomes: how do we find out which actual content area the object is in?  You can’t assume that the object appears in each content area on a page.  A subform with an explicit overflow target might skip a content area on any given page.

Fortunately, there is a heuristic that allows us to infer the content area in which an object appears. 
When you make a call to: xfa.layout.pageContent(0, "", false); you get a list of all objects on a page — including the content area objects.  If a field or subform appears in more than one content area on a page, that object will appear multiple times in the returned list.  The list appears in layout-order. Content area objects appear before the objects that have been placed in that content area.  To find out which content area an object appears in, look back up the list to find the most recent content area.

Finding Absolute Coordinates

Finding the x coordinate of an object involves adding the x coordinates of all the ancestors plus the x coordinate of the content area.  But, as mentioned above, the ancestor subforms may have originated in a different content area.  In order to add up the x coordinates, you have to find out the offset value of your parent object in this content area.  The script code to figure this all out is pretty complex.  I’ve put all the complexity under a script object that you can call:

// This function returns an array of extents for a given object
// There will be one entry in the array for each content area
// an object appears in.
// Each entry is an object with these properties:
//   Extent.page
//   Extent.contentArea  (SOM expression)
//   Extent.x            (absolute page position)
//   Extent.y
//   Extent.w
//   Extent.h  
// All measurements are inches.
function getExtents(vObject)

I have attached a sample form with multiple pages, multiple content areas, and which displays the results of querying layout positions.

Application

Once you’re able to find page positions, how can you use that information? Placing transpromo content is the best example.  I have also developed another sample where I used a different kind of field highlighting.  For each field error, the form places a "highlighter" subform on the master page (the highlighter is an arrow).  When you open the form, click on the "highlight errors" button.

I have another purpose in mind, but that’s the topic for a future blog post…

XFA 3.0: presence="inactive"

This is the first in a short series of posts to describe the new functionality found in XFA 3.0.

If you have developed a form with even modest complexity, you have almost certainly manipulated the presence property of fields, subforms and exclusion groups.  With XFA 3.0 we have extended the possible values of this property to include "inactive".

The existing presence attribute can be one of these properties:

  • visible – (default) Object is visible
  • invisible – Object is invisible, but still participates in layout and event processing.  i.e. space is allocated in the layout to display this object, its calculations and validations still fire.
  • hidden – Object is invisible and excluded from layout.  No space is reserved for this object. Its calculations and validations still fire.

In XFA 3.0 we now add:

  • inactive – Object is hidden and excluded from event processing.  Calculations, validations and other events do not fire.

Scenario

A mortgage application form is sent to a client. The form has an optional section where the user may apply for mortgage life insurance.  The life insurance application section is pre-populated with client information from the server.  If the user chooses not to apply for life insurance:

  • the optional section will remain hidden
  • The calculation and validation scripts in the optional section will not fire
  • Other event scripts such as layout:ready, form:ready, prePrint, preSubmit etc. do not fire
  • We do not check for mandatory fields

All these behaviours will be true as long as the subform that defines the life insurance application is set to presence="inactive".

Once the user checks the box to apply for insurance, we toggle the setting to presence="visible" and then that section of the form becomes fully functional.

Containers, Properties

The new behaviour for presence applies only to these container objects: subform, field, exclGroup.  The presence attribute is also found on draw, fill, items, edge, border, caption and corner — however in these contexts it will not introduce any new behaviour.  It will behave the same as "hidden".

The Deep End

In a previous post, I described the various stages of processing we go through when we open a form:

  1. Loading content
  2. Merging data with template (create Form DOM)
  3. Executing calculations and validations
  4. Layout (pagination)
  5. Render

The enumeration of the presence attribute determines which of these stages a form object will participate in:

  1. Loading content
  2. Merging data with template (create Form DOM) (visible, invisible, hidden, inactive)
  3. Executing calculations and validations (visible, invisible, hidden)
  4. Layout (pagination) (visible, invisible)
  5. Render (visible)

Working with Data Fragments

Many of you will be familiar with the idea of constructing a form using template fragments. Template fragments are a powerful way to construct a form experience with modular parts. But there are some workflows where template fragments do not (yet) have all the functionality we might like. In cases where the content of the form is determined at runtime, template fragments might not have the flexibility you need. Take the transpromo examples from earlier posts(here and here). In those samples, the advertisement was baked into he template.  But in real life the actual ad that gets inserted will change. On starting a new marketing campaign, a company may want to issue a new set of ads to embed in their output. The actual ad chosen will vary depending on data in the form. Is the client single? Insert the sports car ad. Do they have a new baby? Insert the minivan ad. Some of our customers are building these kinds of applications using what we call “stitching solutions”. They have written Java libraries that assemble XFA templates on-demand. Writing a Java solution is fine for some, but eventually we have to make this easier.

One of the solutions available using today’s LiveCycle products leverages the notion of data fragments (instead of template fragments). I use the term “data fragments” to refer to the idea of embedding rich content in data. You might be surprised at how much you can customize the look of your document via XML instance data. You can add images, rich text, hyperlinks, positioned text and even floating fields.

A solution that uses data fragments to place ads in statements might look like this:

  1. An application for authoring data fragments representing the advertisements
  2. An application for adding metadata to the ads and storing them in a repository
  3. A rules engine for selecting ads from the repository based on correlating transaction data with ad metadata
  4. Print engine to render the statements with the ads

I can’t give you the whole solution, but can offer a sample that would help you get started with parts 1 and 4:

  • AdGenerator.pdf: This is a PDF for generating a data fragment and adding it to statement data. (Ideally we’d define something fancier for designing data fragments – maybe a slick flash app side-by-side with the PDF.)
  • statement.xdp: A sample credit card statement that includes a placeholder subform to render the ad data fragment.

Here is a copy of AdGenerator.pdf, populated and ready to export data. (Please give special notice to the image artwork that I worked so hard on.)

Here is a copy of the resulting statement generated with the ad.

How AdGenerator.pdf works

The advertisement is a growable subform that holds:

  • An image field
  • A repeating subform housing rich text

The form has various field and button controls to add the image and to add and position the rich text.  To understand how to use the form, read the instructions on the form itself. The scripts are also well documented and a good source for discovering the techniques used.

Dynamic properties

The (x,y) placement of rich text and the size of the ad subform are controlled by dynamic properties. Authoring these means going into XML Source view and adding the appropriate <setProperty> elements. E.g.:

<field name="Image" w="203.2mm" h="25.4mm">
   
<setProperty target="h" ref="height"/>
   
<setProperty target="w" ref="width"/>

Repeating, positioned text

Another case where we had to use XML source mode: Having added the TextData subform as a positioned child of advertisement, add an <occur/> element to allow it to repeat:

<occur min="0" max="-1"/>

The buttons and fields that position the text will update both the text coordinates as well as the data that the text coordinates are dynamically bound to.

Data References

You can personalize the text on the ad by injecting data values inline with the text.

On loading transaction data, we populate a listbox with all the data values found in the instance data. Adding the data reference uses the same mechanism as floating fields in Designer. We inject a special <span/> element into the xhtml with an embedded data reference. E.g.:

<span xfa:embed="$data.Statement[0].AccountNumber[0]" />

Styling Rich Text

In order to style your rich text in Acrobat, you need to bring up the properties editor (ctl + e). To add a hyperlink, select some text and choose “Hyperlink…” from the context menu. (By the way, there seems to be a bug here.  Rich text editing never works for me in Designer preview.  I use standalone Acrobat — with Designer shut down).