Posts in Category "Macros"

More Macro Goodies

It’s been quiet here.  I have been mostly heads-down preparing for MAX.  I hope I’ll see many of you there.  If not, the content I’m working on will eventually find its way to this blog.

But today the focus is on some things to get you further ahead with macros.

Updated Samples

I’ve included a zip file with the following:

  • updated lint check macro
  • updated accessibility checker macro
  • updated form report macro
  • sample macro to use as a ‘starter’ for new macros

You can simply unzip this file under your Designer install directory and they should immediately work.

If you’re writing new macros, you should appreciate sample.js.  It includes:

  • A convenience method for emiting messages to Designer’s log panel
  • A convenience method for recursively iterating over the contents of your form
  • A ‘catch’ block that does a good job of reporting script errors in your macro.

Have a peek at the comments in the macro for more details.

In full disclosure, I admit that one of the reasons for updating these macros is that I found a couple issues where old macros didn’t work in the new version of Designer.  But not to worry, there are workarounds available and the new versions of these macros work even better than the old ones.

Dreamweaver

Not surprisingly, I use Dreamweaver to edit my macro javascript files. It is possible to extend the object assist/hinting mechanism of Dreamweaver.  Take this designer.xml and place it under the Dreamweaver/configuration/CodeHints directory.  Then, next time you start DW, you will have code hinting for the designer object.

I’d like to extend this to more of the XFA object model, but that’s a project for another day.

Macros in ADEP Designer 10.0

Well, here’s a poorly kept secret now made public.  The macro feature that we previewed in the ES2 Designer is now an officially supported feature.

You can find the online documentation here.

The good news is that the macros you wrote for ES2 should continue to work in the new Designer.  The only changes is that we’ve changed the directory name where they’re stored (now under "macros").  As well, each macro is described by a macro.xml file.

There are a couple items I should hightlight:

There is a method: designer.callExternalFunction() that allows you to call an arbitrary method in a .DLL. Here is your escape key for every integration problem you might conceive.

designer.filterNodeTree() is added to enhance performance.  Traversing the entire DOM searching for specific elements can ge slow on large forms.  For example,
designer.filterNodeTree(xfa.template, "className", "font")
will return all the <font> elements in the form. 
designer.filterNodeTree(xfa.template, "name", "price")
will return all the objects named "price".

If you produce .chm help files to document your macro, you can invoke them with designer.showHelp()

And, just to give you an excuse to try this out, I’ve written a new macro.  This one will append a floating field reference for an existing field to a text object.  Here are the macro.xml and javascript files. Usage: select the text and field objects, then invoke the macro.

 

Propagating Events

Back to another of the 9.1 enhancements intended to help you write less code: propagating events. See here for an overview.  Again, now that we have a Design tool capable of authoring these and now that the 9.1 Reader is more common, we should have a closer look. 

For starters, here is the sample form I’ll be referencing.  As you try the form you will notice a couple of behaviors:

  • as you move your mouse over fields, they are highlighted
  • invalid fields get a thick red border

The punchline here is that there are 14 text fields on the form, but only one each of a mouseEnter, mouseExit and validationState event scripts.  These events are defined on the root subform and apply to all descendent objects.  This is a great technique for minimizing code in your form.  A highly recommended form design technique. 

But now the bad news… So far, this functionality has not been easy to get at through Designer.  In the ES2 designer there was a check box to make an event propagate.  But the script dialog didn’t allow field events to be defined in the context of a subform.  Then because of the confusion this functionality caused, the checkbox was removed from the Desginer that shipped with Acrobat 10.  This functionality should re-emerge in a future version of Designer. 

In order to make it easier to get at this functionality, I have written a macro that will add propagating events to subforms.  The UI appears like this:

After you run the macro, the event and script will be added to your subform. Now, the tricky part — to edit the script you need to do two things: 1) close/re-open the form. Yeah, macros are still beta, and Designer doesn’t pick up on the changes made by the macro. 2) on the script dialog for the subform, choose to show: "events with scripts".

One interesting thing to note is that it is valid to have two or more of the same event defined on a subform. e.g. you could have both a propagating enter event as well as a non-propagating enter event.

The Deep End

When changing the display of an object to show an error state, it’s a bit of a challenge to revert the object back to its original state.  e.g. If you change the border color or border thickness you need to know the original color and thickness in order to restore the original state of the field.

Well, not really.  You’ll see the validationState script in the sample uses a different technique to restore the original display.  The script removes the border and assist elements from the field.  To understand why this works you need to understand the relationship between the form and the template.  The template is the definition of a form you see in Designer.  At runtime, we merge the template objects with data and create form objects.  A form field is then a sparsely defined object that has a pointer back to a template field. 

When we execute script that changes the color of a border, we modify the form definition of the border and override the default border properties found in the template. Removing the border from the form field means that we have removed the override and the field then reverts to the definition found in the template.

 

Use the change event to filter keystrokes

If you’re wondering why a sudden flurry of blog posts, it’s because I spoke at the Ottawa Enterprise Developer User Group meeting last week.  I prepared a bunch of material for that presentation, and now I need to make that material generally available.

I spoke a lot about validation techniques in form design.  Today I’ll focus on using the change event to validate user input. 

User input should be validated as early as possible.  Compare the experience between:

  1. Wait until the user submits the form — then highlight all the validation errors
  2. Validate as the user exits the field
  3. Validate input as the user types into the field

The earlier the validation happens, the better the user experience. Let’s look at what is involved in validating input as the user types into the field.

The change event fires every time the user enters data into a field.  The change is normally a keystroke, but could also be a delete or a paste operation. When the change event fires, there is lots of useful information available in the xfa.event object.  I’ll describe the properties that are relevant for today’s discussion:

xfa.event.change: the contents of the data being entered.  Normally this is the keystroke.  But it could also be the contents of the paste buffer.  Or in the case where the user hits the delete or backspace keys, it is an empty string. You can modify the value of xfa.event.change in the change event.

xfa.event.selStart, xfa.event.selEnd: Tells us where the change event will happen. "sel" is short for "selection".  selStart and selEnd are character positions.  When the user has selected text, they describe the range of selected text.  When no text is selected, selEnd will be the same as selStart and text will be inserted at that position.  You can change the values of selStart and selEnd in the change event.

xfa.event.prevText: The contents of the field before the change is applied

xfa.event.fullText: What the contents of the field will be after the change is applied.

Now, some practical examples of what you can do in the change event.  Here is a sample PDF containing all the examples.

Force upper case

If you want to make sure that the contents of your field will be upper case, then modify xfa.event.change like this:

xfa.event.change = xfa.event.change.toUpperCase();

Allow only numeric characters

If you create a field that is a numeric type, then Reader/Acrobat will automatically restrict users to valid numeric input.  But suppose you’re gathering a telephone number or a credit card number. These are normally text fields that hold numbers. In this case you want to "swallow" any changes that insert non-numeric characters. This script uses a regular expression to test the change contents and cancel if necessary:

if (xfa.event.change.match(/[^0-9]/) !== null) {
    // swallow the change
    xfa.event.change = "";
    // if the user has selected a range of characters,
    // then leave the range intact by re-setting the start/end
    xfa.event.selStart = 0;
    xfa.event.selEnd = 0;
}

Visual Feedback

I once designed a form with a telephone number that accepted only digits.  I swallowed spaces, brackets, dashes and other formatting characters that the user entered.  Then I found out that a couple of the people filling in the form abandoned it because they couldn’t enter data in that field.  They needed some feedback that their keystrokes weren’t valid.  This next example temporarily sets the field border red and thick when the user enters an invalid key.  <deepEnd>The script uses the app.setTimeOut() method.  Notice that I call it from a script object.  If the return value of setTimeOut() gets garbage collected, the event will cancel. Variables declared outside script objects will be garbage collected.</deepEnd>

// If the user has entered invalid data, cancel the event and give some visual feedback
if (xfa.event.change.match(/[^0-9]/) !== null || xfa.event.fullText.length > 10) {
    // cancel the change
    xfa.event.change = "";
    xfa.event.selStart = xfa.event.selEnd = 0;
	
    // turn the border red and thick
    this.borderColor = "255,0,0";
    this.borderWidth = ".04in";
    // Turn the border back to black after one second
    var sRevert = 
        "var This = xfa.resolveNode('" + this.somExpression + "');\
        This.borderColor = '0,0,0';\
        This.borderWidth = '.02in';";
		
    helper.timer(sRevert, 1000);
}

Fonts are Big

It strikes me that we don’t complain about big email attachments as much as we used to. We don’t mind as much when aunt Grace sends us a batch of 2MB jpg files straight from her camera.  The photo quality is the same as always (sigh), but at least it doesn’t bring our internet connection and email client to it’s knees anymore.  Heck, even Dad and Mom now have a broadband connection.

But just because we *can* send big attachments doesn’t mean we should.  Let’s have some professional pride in making sure that our files are as lean as possible.  Today’s topic is about managing your fonts so that your PDF forms are tidy and small. 

I’m sure you already knew this, but I’ll repeat the basics.  PDF files can embed font definitions.  The advantage of embedding a font is that it guarantees your PDF will look exactly the same no matter where it’s opened — you don’t have to worry whether the user opening your form has copies of the fonts you used or not. If you don’t embed fonts, and the user doesn’t have
"Charlemagne Std" on their system, Reader will display the PDF with a substitute font.  It won’t look the same.

Of course, the disadvantage of embedding fonts is that they’re big.  They bump up the size of your PDF in a big hurry. Often around 200K per font.

Here are some notes to remember about font usage:

Note #1: If you’re using common fonts, and especially if you can tolerate some variance in your page display, then don’t embed fonts.  Designer embeds all fonts by default:

Designer Form Properties dialog showing that fonts are embedded by default.

Note #2: If you’re embedding fonts, use as few fonts as possible in your form design.

There was a reason I just finished updated the form reporter.  It will tell you what fonts you have used and how many instances of each. I recently reviewed a form that showed this in the report:

All fonts were embeded. The form had one object using Times New Roman.  That one instance bloated the PDF by over 200K.  After I consolidated all font instances to Myriad Pro, the form was a total of 600K smaller.

Note #3: Not all fonts are equal in size

I haven’t done an extensive accounting, but it appears that Myriad Pro is smaller than most. A small form with Myriad Pro embedded is 77K.  While the not-embedded version is 13K.  Why does embedding Myriad Pro cost only 64K while Times New Roman was 200K?  I’m told that the embedded Myriad Pro excludes character sets that are not in use e.g. Cyrillic.

Note #4: Reader installs fonts

On my system, Reader X installed Minion Pro, Myriad Pro and Courier Std. There are asian font packs for Reader available for download. I’d like to think that for most users having Adobe Reader installed fonts would mean they don’t need to be embedded in your PDFs.

Note #5: Fonts can be subset

If you use a font in an interactive form field, you need to have the entire font embeded.  But if the font is used only in boilerplate text, then you only need to embed the definitions of the characters that are found in the PDF.  In this case we can reduce the size by embedding only a subset of the font.  Options to embed subset fonts are not exposed in Designer.  This is server-side processing.  And as long as you’re mucking in that area, you can also explicitly choose on a font-by-font basis which are embedded and which are never embedded.

Edit Fonts in Designer

Here’s the problem: The form report shows you have one instance of Times New Roman in your form design.  Now find that one instance among the 300 fields on your form and change it. If it were me, I’d probably switch over to source view.  But that’s not very user friendly.

This becomes yet another case where Designer macros can be very helpful. Here is a zip file that contains a Designer macro to perform global font substitutions. When you run it, you’ll get a dialog like this:

The macro will replace the font references it finds in <font> elements, as well as the font references it finds inside rich text values.  Just be sure to type the names of the fonts correctly.  If you mis-spell the replacement font, the macro will happily give you a form full of "Myirod Pro" references. When the macro completes, look in Designer’s log display for a summary of the changes.

 

Updated Form Report Tool

Has it been two years already?  Seems like just the other day I published a tool for summarizing form content.

I’m sure it’s been sitting on your desktop for regular use.  In my day job I’m, often asked to look at customer forms.  Or I sometimes need to go back and review one of my own form designs.  The first thing I always want to know is the "big picture" of what’s inside this form.  That’s what the summary tool is all about. You’d be surprised what you can learn from a report. I’ll hopefully point out a thing or two in follow-up blog posts.

Now that we have a macro capability in Designer (yes, yes, still in beta) it’s time to migrate this functionality from being an external tool to a Designer macro.

Here are the files you need: FormReport.pdf and FormReport.txt (rename to FormReport.js).  If you need a reminder about how to install them with Designer, then read this blog entry.

 

Multiple Top Level Subforms

I’m willing to bet that all the XFA form definitions you’ve looked at all have one top level subform.  Not surprising, because that’s what Designer
allows you to author.  But according to the XFA specification, there can be any number of top-level subforms below the <template> root.

Today I’ll briefly describe the processing rules for top-level subforms, provide a simple example where you might find them useful, and give you a couple of macros to make these easier to work with in Designer.

The Processing Rules

The rules are simple:

  • When there are multiple top-level subforms, only one is rendered.
  • We choose which subform to render based on which one best matches the data.  i.e. the template behaves like a choice subformSet: <subformSet relation="choice">. 
  • If no data is supplied, we render the first subform below the <template> element

A Sample

Suppose I want different variations of my form for printing and for interactive.  I construct my template so that it has top level subforms for both print and interactive.  Since the binding is all "by name", the form will render according to the name of the top level node in the data.  Try previewing the sample with these data files: interactive.xml, print.xml — or import these data files in Acrobat, and you’ll see the form change according to the data provided.  However, controlling print behavior with different data files is more applicable on the server than on the client. 

In order to force the form to use the print subform when printing from Acrobat/Reader, I added a pre-print event to the interactive root subform.  The script renames the top level node in the data, and forces a remerge.  This will cause the print operation to use the print subform.

interactive::prePrint – (JavaScript, client)
xfa.record.name = "print";
xfa.form.remerge();

Then to restore to the interactive view, I added a postPrint event to the print subform:

print::postPrint – (JavaScript, client)
xfa.record.name = "interactive";
xfa.form.remerge();

Design Experience

I’ve already hinted that Designer doesn’t really support multiple top-level subforms. But with a couple of handy macros, we can get by.

First problem is that there’s no easy way to create a new top level subform.  Here’s a macro to proide that functionality.  You’ll notice when you run the macro that there’s a problem.  The changes made by the macro don’t show up in the hierarchy.  This is a bug that will be fixed next release.  Meanwhile, closing and re-opening the form will workaround the issue.  Note that the macro to add the new top level subform also inserts a default master page (<pageSet>). Designer and the XFA runtime are generally very unhappy if they don’t have a set of master pages to work with.

Next problem is to figure out how to edit the new top level subform in Designer.  Here’s a macro to solve that problem. Since Designer always renders the first subform, this macro re-orders the top-level subforms so that you get a different top-level subform. Specifically, the macro re-orders the first subform to be last.  Just make sure that when you save the form that your preferred default subform is first in order.

Now, for the specific example of print and interactive variations, I could have implemented the solution in a number of different ways.  e.g. with a choice subformSet or with optional subforms at the second level in the hierarchy.  But the point was to bring to light some functionality that you might not otherwise been aware of.

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.

JavaScript Lint check in Designer

Some time ago I distributed a sample form that used Douglas Crockford’s excellent JSLint script to check form JavaScript for common coding errors.  You can read about it here. The logical next step is to use macros to integrate this capability into Designer (macros introduced here).

Here is everything you need to install the macro.

One improvement I’ve made over the previous version is to automatically populate the list of global objects with all the subform and field names in the form.  Now it will flag far fewer global objects — and if it does flag a global, you really want to pay attention, since it is very likely an un-declared variable.

I really strongly recommend this tool to you. When you run this against your script you will very likely be surprised at some of the coding errors you’ve made.  I know I was.

XDP Size Matters

I have recently been involved in a couple of contexts where customers have worked hard to reduce the size of their XDP and resulting PDF files.

We know that size matters.  The smaller the better.  We want small PDF files for downloading.  We know that encryption-based technologies such as certificates and rights enabling perform faster when the form definition is smaller.  Today’s discussion is a couple of tips for reducing the size of your XDP and PDF.

When concerned about size, the first two places to look are fonts and images.  I’ve covered images a couple times.  Check out: Linked vs Embedded Images and also the sample in Parameterize your SOAP address.

Fonts are a simple discussion.  Your PDFs are much smaller when the fonts are not embedded. If you need to embed fonts, use as few as possible.  To see which fonts are referenced by you form, you can check out the form summary tool here.

But the main topic for today is syntax cleanup.  The elements and attributes in the XFA grammar all have reasonable default values.  We save lots of space by not writing out the syntax when the value corresponds to the default.  For example, the default value for an ordinate is "0". If your field is at (0,0), we will not write out the x and y attributes. 

However, there are a couple of places where the syntax cleanup could use some help. Specifically: borders (edges and corners) and margins. There are scenarios where these elements can be safely deleted from your form.

Today’s sample is a Designer macro (with the sample form) for eliminating extra syntax. If you don’t want to understand the gory details, you can just download and install the Designer macro.  (If you haven’t already, please read this regarding Designer macros).  After running the macro, look in Designer’s log tab for a summary of the cleanup.  Then double check the rendering of your form and make sure it hasn’t changed.

If you want to understand a bit more, I’ll explain what is going on.  There are three instances of syntax bloat that the macro will clean up:

Extra corners

In the attached form there are two fields: border1 and border2. 

The border definition for border1 looks like:

<border>
  <edge thickness="0.882mm"/>
  <corner thickness="0.882mm"/>
  <edge thickness="0.3528mm"/>
  <edge thickness="0.3528mm"/>
  <edge thickness="0.3528mm"/>
  <corner thickness="0.353mm"/>
  <corner thickness="0.353mm"/>
  <corner thickness="0.353mm"/>
</border>

When you modify edge properties, designer will inject <corner> definitions. However, the only time a corner definition has any impact on the rendering is when the radius attribute is non-zero.  In this example, the <corner> elements may all be safely removed without changing the rendering of the form.

Hidden Borders

The border definition for border2 looks like:

<border>
  <edge thickness="0.882mm" presence="hidden"/>
  <corner thickness="0.882mm" presence="hidden"/>
  <edge thickness="0.3528mm" presence="hidden"/>
  <edge thickness="0.3528mm" presence="hidden"/>
  <edge thickness="0.3528mm" presence="hidden"/>
  <corner thickness="0.353mm" presence="hidden"/>
  <corner thickness="0.353mm" presence="hidden"/>
  <corner thickness="0.353mm" presence="hidden"/>
</border>

I’m not sure how I ended up with this configuration, but as you can see, all the edges and corners are hidden.This would be be represented more efficiently as <border presence="hidden"/> or better yet, no border element at all. But before cleaning up this syntax, bear in mind that there can be a useful purpose here.  If you have script that toggles the presence of border edges, it is useful to have the edge properties (e.g. thickness, color) defined in the markup.  If you remove the markup, you will need to set those properties via script.

Zero Margins

If you have edited object margins, you could end up in a situation where your margin element looks like:

<margin topInset="0mm" bottomInset="0mm" leftInset="0mm" rightInset="0mm"/>

Since the default margin insets are all zero, this element can be safely removed.

How frequently this syntax bloat occurs in your forms depends on your editing patterns. In my testing against several customer forms, I saw size reductions of 10 – 15% by removing these elements.