Recently in layout Category

Editable Floating Fields V2

| No Comments

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

| 4 Comments

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

| No Comments

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

| No Comments

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

| 8 Comments

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. 

About this Archive

This page is an archive of recent entries in the layout category.

Exclusion Group is the previous category.

mandatory is the next category.

Find recent content on the main index or look in the archives to find all content.