Archive for May, 2009

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. 

Tracing Function Arguments

Given that we don’t have a script debugger, I make extensive use of the Acrobat console object to dump script context information.  I’ve discovered that getting the right information out is a bit of an art form.  When you add a call to console.println() you want to make sure you include enough information so that you know the context of the output.  Your output needs to handle conditions such as empty strings, null values, variable numbers of parameters…  But who wants to spend time coding a really clever call to console.println() when you know full well that you’re just going to delete it five minutes later?

Given my lazy nature I went searching for an easier way.  The place where I most often add calls to console.println() is at the beginning of a function call.  For example, here’s how I might debug a method to convert a measurement to inches:

/**
* Convert a measurement to inches.
* @param measurement – a measurement property
*                 e.g. "49.3mm".  Input units can be mm, cm, pt, in
* @param digitsPecision – (optional) how many digits to preserve
*                          after the decimal place. Default is 3.
* @return the corresponding number of inches.
*/
function getInches(measurement, digitsPrecision)
{
  console.println("getInches: " + measurement + " " +
     
  ((digitsPrecision) ?  digitsPrecision : "undefined"));
  …
}

That console.println() method call didn’t exactly roll off the fingertips.  And no, I didn’t get it right first try. In my test form, the output dumped to the console was:

getInches: 9.525mm 4
getInches: 60.325mm undefined

Then I started looking at what I might be able to get for free by extracting information from the "arguments" object.  (If you’re not familiar with the arguments object, read chapter 8.2 of O’Reilly JavaScript The Definitive Guide.)

I wrote a utility function that dumps the contents of the arguments object.  My debug statement now looks like:

function getInches(measurement, digitsPrecision)

  dbg.trace(arguments);
  …
}

Far easier to code.  The output produced by the dbg.trace() call:

function getInches() :
  measurement: [9.525mm] (string)
  digitsPrecision: [4] (number)

function getInches() :
  measurement: [60.325mm] (string)
  digitsPrecision: undefined

You can look in the sample form to see the details on how the script extracts all this information.  The tricky part was extracting the function name and parameter names.  The script uses arguments.callee.toString() to get a string representation of the function syntax and then uses a regular expression to extract the function name and parameter names.

Other features of the dbg.trace() method:

  • handles variable numbers of parameters
  • If a parameter is an object, it will attempt to extract the className, name and rawValue properties (if defined).
  • If the form is running on the server, the trace output is dumped to the log object instead of the console.
  • There are a couple of global variables in the dbg script object to control whether trace output is enabled and to control the maximum number characters of data to dump for parameter values.

Have a look.  I’m sure you’ll find ways to customize the trace to suit your specific needs.

August 14 Update

As explained here, the trace function has been updated to handle parameters.  It is now also available as a downloadable fragment.

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"

Sort Subforms

You probably already knew it was possible to re-order (and hence sort) a group of subforms.  But in case you didn’t — or in case you needed a sample, here you go.

In the sample, the column headings are button fields.  Their click event sorts the rows by the values in that column.  If you click again it will sort in descending order.

I centralized the sort methods into a script object that ought to be re-usable in other contexts:

/**
* Sort a group of subforms.  Calling this the first time will sort
* ascending. Calling it again will toggle to descending.
* @param vButton the button field used to initiate the sort. 
*  We store the current sort order in the button rawValue property.
* @param vInstanceManager the instance manager of the subforms to
*  be sorted
* @param vSortFieldName the name of the field in the subforms used
*  to sort.  Null values always sort to the beginning.
*/
function sort(vButtonField, vInstanceManager, vSortFieldName)

/**
* Sort a group of subforms.  Calling this the first time will sort
* ascending. Calling it again will toggle to descending.
* @param vSubformInstance one instance of a subform in the set of
*  subforms to be sorted
* @param vFieldName the name of the field used to sort
* @param bAscending boolean: if true, sort in ascending order,
*  otherwise descending.
*/
function performSort(vSubformInstance, vFieldName, bAscending)

Note that sorting subforms will also cause their underlying data to also be re-ordered.

Notes

Rather than embarrass myself by implementing a rookie bubble sort, I used the JavaScript Array.sort() method.  Of course, this meant figuring out how to set up a sorting function and letting it have access to the sort parameters (sort field and sort order).  But figuring out how to store these under <extras> was easier than trying to impress you with a fancy heapsort algorithm.

I generated the sample data at this very clever website: http://www.generatedata.com/ 

Deep End

If you sort a very large set of subforms, you might run into performance issues.  If you need to improve the performance, try sorting the underlying data DOM using the remove/insert methods on the nodelist.  After re-ordering the data you would do an xfa.form.remerge() and the subforms should show up nicely sorted.

Document Your Form Template

Many of us are creating forms with lots of script and complex logic.  The good side of this is that we are able to generate some pretty cool form fill experiences.  But how maintainable are the forms we create?  If another form developer inherited my form and was asked to make a few changes, would they be able to?  Or would they end up staring at a mass of objects and scripts in Designer and give up?  Unless your objective is to create built-in job security it would seem like a good idea to make your forms as maintainable as possible.  Toward this end, I have two suggestions: a) build good frameworks and b) (the topic of this post) provide great documentation.

Create Frameworks

If you are developing a series of forms and have logic that is re-usable, encapsulate that code in script objects.  Turn these script objects into fragments and reference them from your forms.  There are hopefully two outcomes from this exercise:

  • The framework itself can be heavily documented to facilitate easy usage and also proper maintenance
  • The amount of script logic that lives outside the framework will be minimized

If these are true, then editing forms becomes reasonable because the average form author does not need to know the internals of the framework — they just need to know how to use the framework.  They can modify forms with confidence because there isn’t much script and the script is easy to understand.

Document your Script

This area is harder.  The problem with documenting script is that it is fragmented throughout your form.  An individual script may be well documented, but where does it fit in the overall context?  When you open an unfamiliar form, there’s bound to be lots of routine, innocuous script, but then there are likely a small number of scripts that are key to how the form operates.  There is no easy way to get the big picture of how logic flows in your form.

My project for the last few days has been trying to figure out how we could make use of JavaDoc comments to build a documentation map for a form.   There are two sides to this problem. One is figuring out conventions for adding the comments.  The other aspect is figuring out how to harvest them and generate documentation. 

Not surprisingly, my first crack at a harvesting tool is… a form.  This sample form will load an XFA/PDF and generate a report from the script it finds inside (Be patient — loading a large form takes a long time).  Here is a sample report generated from the survey form I created for a previous blog entry.  (and here is the survey form with updated/improved internal documentation).  Going through the exercise of documenting a form, harvesting the documentation and generating a report forced me to deal with some issues that are unique to this environment.

Commenting on scripts vs. commenting on objects

Normally JavaDoc comments are used to describe classes, interfaces, functions and variables.  This makes sense for our script objects.  e.g. this  script fragment should work nicely with a harvesting tool:

/**
* function handleError(err) — return an error message and if
* in Acrobat, raise the console window with the error
* @param err – the error object thrown by the script engine
* @return – a human readable message extracted from the error object
* @author John Brinkman
http://blogs.adobe.com/formfeed/
*/

function handleError(err)
{

}

For this case I was able to fairly easily extract the comment and the function definition.  Just scan past the end of the comment up to the first "{" or ";" characters. (now you understand my fascination with complex regular expressions).  A good harvester tool could extract the function declaration even without the structured comment — just locate the function(){} syntax.

But how do we document a calculation? or a click event?  There’s no function/object that is the target for the documentation fragment.  There are also no parameters or return values to describe.  In this case, I chose to support a single JavaDoc comment for the script, and for the syntax portion emit the first 5 lines of script that follow the comment.  Instead of supporting @param, I added a custom tag called @reference where the script author can describe dependencies for a particular script. e.g.

Survey.DefineSurvey.Sections::initialize – (JavaScript, client)
/**
* Sections is the container subform that holds a series of survey
* section definitions. The initialization script makes this subform
* initially hidden.  It will be made visible as the user reaches
* this stage of survey definition.
* @reference _Section.count — If there are already section
* definitions, make this subform visible.
*/
this.presence = _Section.count == 0 ? "hidden" : "visible";

Harvesting Information outside the script

In the case of documenting forms, there is lots of information outside the actual JavaDoc that is relevant to the report. In our case, information about the context of the script is very important.   What form object is hosting the script? What event? Is it FormCalc?  All these pieces and more need to be pulled into the report.

Order of Report

When generating the report, I had to choose between two ways to order the report:

  1. Order the report by object (in document order).  E.g. list all the scripts for the first object encountered and then search for the next object with script.
  2. Order the report according to the (approximate) order in which script will execute.  Then a field’s initialize event will appear early in the report and its layout:ready event will appear much later in the report.

I chose to go with option 2.  My guess is that this order will be best for trying to decipher form logic.

Forms with no structured comments

You’re not likely to uncover many forms that have structured comments in them.  Yet we still ought to be able to generate a decent report.  We  harvest all the script context information and the report will contain the first 5 lines of each script.  For each script summary you can click on the "+" button to see the entire script.

Noise

There are many scripts that you do not want included in your report.  e.g. if your form has enter/exit scripts on every field, and the scripts are the same each time, we do not need to report on each and every script.  It just clutters the report.  I added an @ignore directive that allows the form author to exclude these scripts.

FormCalc

Unfortunately, structured comments are not legal syntax in FormCalc. For FC, the harvester looks for blocks of 2 or more lines that start with "///".  E.g.

Survey.DefineSurvey.Sections.Section.Questions.Question.QuestionNumber::calculate – (FormCalc, client)

///
/// Assign Question number based on subform index
///
Question.index+1

JavaDoc Completeness

The harvester supports only a fraction of the JavaDoc directives: @param, @return, @reference, @ignore, @author.  In the descriptive text you can use <br> and <p> elements to force line/paragraph breaks.

Resources

If you are new to the idea of JavaDocs, check out the JavaDoc tool home page.  I noticed that there is also a Google project to support JavaDocs for JavaScript here.

Futures

Unfortunately, documenting script is not really enough to make complete sense of the way a form works.  There are many other aspects that you might want to describe.  Maybe there is a clever data binding strategy.  Maybe you’d want to describe various inputs — the use of certain datasets.   Maybe there is complex binding that modifies form behaviour.  Maybe the form makes use of WSDL definitions.  To begin to describe all these things we need to be able to place comments in more places in the template.  One obvious place would be under the <desc> element of both <field> and <subform>.  But then we need a way in Designer for adding those comments.

Feedback

I’d be interested in hearing from people whether template documentation is a problem that interests them.  Is there more you’d like to do?  Ideas to build on what I’ve described here?  Let me know please.

Template Transformation

Today I am disclosing a trick that will allow you to apply a transformation to your template.  But first a disclaimer: this technique is not a product feature.  It is just a trick that takes advantage of some side effect behaviours.  Ok, ok, it’s a hack.  But it’s potentially a very useful hack.

Motivation

Have you ever wanted to do apply a global change to your template?  Change the fonts of your fields.  Set a colour scheme.  Add a script to all your fields.  You know by now that it can be very tedious to do these things. 

Transformation Techniques

What options do you have today?  For very simple changes, some people will go to their XML Source tab and do a global search/replace.  That’s a bit scary.  I do it when I rename anything that is referenced in script.  But it’s hard to use when you want to be selective in your changes — e.g. apply a font change to all numeric fields.  There is also XSLT.  We have some very technical customers who will save their form as an XDP and apply an XSLT script to make global changes.  These customers have my very humble respect.  XSLT is a very powerful tool, but a very difficult technology to master.

Script

There are a couple of ways I’d like to transform a template.  One choice would be some form of style sheet definition.  My other choice (today’s topic) would be to write JavaScript that updates the template.  We write tons of script that modifies the form DOM at runtime.  Wouldn’t it be great if we could write script that modified the template at design time?  Well, turns out you can.  But as I think I may have mentioned — it’s a bit of a hack.

I’ll explain how it works, but first some background to help you understand the technique.

PDF Generation Process

When you edit a dynamic template and save as a PDF, Designer gives you the illusion that PDF is the native format for the template.  In reality, the native format is XDP.  When Designer opens a dynamic PDF, it extracts the XDP stream stored inside and discards the PDF container.  Then when you save, Designer re-generates the PDF from the XDP.  That would explain the phrasing of the messages you see in the log tab of Designer when you save:

Generating PDF Document…
PDF generated successfully.

0 warnings/errors reported.

The PDF generation process that Designer goes through during save is almost exactly the same process LiveCycle Forms goes through when it generates a PDF from an XDP on the server.  Among the steps during PDF generation:

  1. Embed all required fonts
  2. Embed referenced images
  3. Generate XMP metadata
  4. Run server-side scripts

Hopefully #4 caught your attention.  When Designer saves a dynamic PDF, one of the things it does is execute script. 

Scripting the Template

When any script runs in an XFA form, the default context is the Form DOM.  The Form DOM is a transient DOM that gets created when you open the form and goes away when you close the form.  You can script against the Form DOM as much as you like.  However, in Reader you cannot modify the template DOM.  The template DOM is the source code.  It is what gets saved back in the XDP stream in the <template> element.   For security reasons, we don’t allow forms in Reader to modify their template.  But, this is a Reader-only restriction.  Forms running in the context of the server can modify their template.  This means that script run by Designer when it saves your dynamic PDF has full write-access to the template and any changes the script makes will be preserved in the generated PDF.

Setting up a save-time script

Take a simple example.  This script will update the author information metadata in my template (note that the script is set to run at server):

form1.#subform[0].updateMeta::initialize – (JavaScript, server)
updateMetaData("creator", "John Brinkman");
updateMetaData("contact", "
http://blogs.adobe.com/formfeed/");
updateMetaData("usage",   "No rights reserved");

function updateMetaData(vName, vValue)
{
    var vDesc = xfa.template.["#subform"].desc;
    if (vDesc.nodes.namedItem(vName) == null)
    {
        vDesc.nodes.append(xfa.template.createNode("text", vName));
        vDesc[vName].value = vValue;
    }
}

Now when I save the form and go look at the file/Form Properties/Info I’ll see… no change.  Oops.  The problem is that once Designer saves, it doesn’t re-open the PDF it saved.  It continues operating on the XDP it has in memory.  To see the change you need to save *and* close.  Then when you re-open and look at your file properties you will see the author and contact information updated. (You won’t see the "usage" element in this screen, but it does get embedded in the PDF metadata).

Debugging

For those of you who get their JavaScript right the first time, read no further.  Those like me who need to liberally sprinkle their code with console.println() messages will discover that the console API doesn’t work.  That’s because console is an Acrobat-only object. You can’t code against it on the server.  Instead, you will want to familiarize yourself with the xfa.log object.  To dump a message to the log file, you can code:

xfa.log.message(0, "your message here");

This message will show up in the log tab of designer.  A couple things you’ll want to know about using this command effectively:

  • The log file suppresses messages that have the same text as the previous message
  • For a message to appear with proper line breaks in the log tab, you need to insert carriage return/line feed combinations in your string.  i.e. instead of "line1\nline2" you need to code: "line1\r\nline2".
  • Before saving you should clear the log.

Cleanup

Once your transformation script has run, you don’t need it anymore, and it would be bad form to leave it around.  Add a couple lines to your script so that after it has done its work it will remove itself.

With a log message and cleanup, the sample above becomes:

updateMetaData("creator", "John Brinkman");
updateMetaData("contact", "
http://blogs.adobe.com/formfeed/");
updateMetaData("usage",   "No rights reserved");

// Insert a message in the Designer log tab
xfa.log.message(0, "updated form metadata");

// remove this script once it has run on-save
var vNode = xfa.template.resolveNode("$.." + this.name);
vNode.parent.nodes.remove(vNode);

function updateMetaData(vName, vValue)
{
    var vDesc = xfa.template.["#subform"].desc;
    if (vDesc.nodes.namedItem(vName) == null)
    {
        vDesc.nodes.append(xfa.template.createNode("text", vName));
        vDesc[vName].value = vValue;
    }
}

Of course, you’ll probably want to leave the cleanup code commented out until you have fully debugged your script :-).  It would be a good idea to make sure your imported subform has a visual appearance so that you won’t forget to deal with it.

Once it is working, the updateMeta subform can be added to your fragment library.  When you want to update your metadata, you follow these steps:

  • import the updateMeta subform fragment
  • save
  • close
  • re-open

And if that’s less effort than re-typing the metadata, then you’re in a happy space.

I have attached a sample that does a couple of interesting things:

  • Update all the field widget borders to be .01in thick, solid and blue.
  • Add enter and exit scripts to each field to highlight the border while the field is active.

To use the sample:

  • Open the form in Designer
  • Modify the first line of the script and change it from if (false) to if (true)
  • Save
  • Close
  • Open

When re-opened you’ll see that the subform hosting the update script is gone.  The borders are now solid blue and when you preview and tab through the fields you’ll see that the border changes colour when the field is active.  You’ll also notice that in the sample I use a slightly updated version of the scMessage scripts to display the server message — fixing the carriage return/line feed issue.

Futures

I have been saying that this technique applies to Dynamic XFA-based PDFs.  In fact it works with static as well.  But it does not work for artwork PDFs.  It also does not work when you save as .XDP.  For those environments there is no solution yet.

The technique described here is admittedly awkward, but it does point toward some exciting possible enhancement areas for Designer.  Notably style sheets and Design-time macros.  Nobody is making any commitments, but these are areas of interest to us as we move forward.

JavaScript Regular Expression Tester

I have been working on a couple samples recently where I have become obsessed with the power of regular expressions.  There are plenty of areas in forms development where regular expressions can come in handy.  Maybe you want to validate that the user entered a syntactically-correct email address.  Maybe you want to change "LastName, FirstName" into "FirstName LastName".  The more I look at regular expressions, the more uses I find for them.  However, there are a couple of problems with my obsession (besides the danger that I’m on a path to OCD):

  1. Regular expressions easily become complex.  You start out doing a simple search/replace and it seems seductively easy.  A basic regular expression used in String.replace() means you can do in one line of code what would have otherwise required 20 lines of fragile script.  Then next thing you know you you’re processing HTML blocks.  You’re struggling to capture a repeating group — it *almost* works — "just another five minutes and I’ll get it"…
  2. Good news: There is plenty of online documentation and plenty of online tools to test regular expressions and feed your habit.  There are also plenty of forums populated with questions from regular expression addicts.  Bad News: There is a frustrating lack of uniformity in the regular expression functionality in various development environments — whether it’s Perl, JavaScript, Java, grep, Python, Flex …

Eventually my frustration/obsession grew to the point where I built my own RegEx testing form.  That way I could be certain that the expressions I tested would work with the version of JavaScript found in the version of Reader I was targeting.

The form tests regular expressions in two contexts: RegExp.exec() and String.replace().

I won’t try to explain the usefulness of both these methods, since any modest JavaScript reference would do a much better job.  The test form has an input fields where you enter the source string, regular expression and (optional) replacement string.  There are check boxes to control the RegExp attributes.  When you click the "find" button, the expression is evaluated (RexExp.exec()) and the results are dumped into a series of repeating subforms.  If you choose the replace option, the script uses the regular expression with String.replace().

Enjoy.  But proceed with caution.  I’m not responsible for any social disorders you develop along the way.  However, I might be forming a support group…