Search This Blog

Wednesday 23 April 2014

Adobe LiveCycle Designer Tip #4 - Setting Breakpoints

Debugging a Designer form can be frustrating for people used to other environments.  With a Designer form you often have to add console.println(...) calls to your code so you can work out what is going on.  However, you can do most of the things you expect with a debugger, but there are a few tricks.

First you can only debug JavaScript, there is no support for debugging FormCalc scripts.  Second, you can only set breakpoints in event handlers, you can step into a script object but you can't set a breakpoint in one and the JavaScript debugger will not show you the code being debugged.  It will at least show you which line number you are on and the JavaScript debugger console will allow you to display the values of form object properties and variables.

To set a breakpoint you must open the form in Acrobat, the Preview PDF within Designer does not seem to support all the debugging functionality available.  The simplest way to do this is to perform a Preview PDF, Designer will generate a temporary file in your temp directory with a random name.  So in Adobe Acrobat you can go File ... Open ... then type "%temp%" (without the quotes) in the File name field click the open button and find the most recent PDF file with a randomly generated name something like "_165o26cev28fbb2ut.pdf".

Once open in Acrobat the JavaScript debugger will now display the Hierarchy of the form, you will be able the expand the XFA node and navigate to a piece of event code and set a breakpoint, like this one on line 2 of the validate event of TextField1, just click where the red dot is.




















To see all the breakpoints you have set select BreakPoints in the Inspect: drop down (which will have defaulted to Local Variables), this will display all the current breakpoints, in this case we only have one so it looks like;


















If you want to make the breakpoint conditional, select the breakpoint and click the edit button;



This will bring up a "change the breakpoint condition to:" dialog, like;











The default condition is true (so always breaks) but you can enter any JavaScript expression that resolves to true or false.  So in this example we could type this.rawValue.length == 3 and the breakpoint will only be hit when we validate the field and it has a three character long value.

You can also list all the breakpoints using the JavaScript console.  In the View drop down select console and type the following (to execute select the snippet and use the Ctrl-Enter on the numeric keypad);

for ( var i = 0; i < dbg.bps.length; i++ )
{
    console.println(dbg.bps[i].toSource());
}

This will display something like;

({fileName:"XFA:form1[0]:Page1[0]:Field[0]:TextField1[0]:validate", lineNum:2, condition:"this.rawValue.length == 3"})

Then you can use the following to clear the breakpoint

dbg.cb({fileName:"XFA:form1[0]:Page1[0]:Field[0]:TextField1[0]:validate", lineNum:2})

And to set the breakpoint again you can use;

dbg.sb({fileName:"XFA:form1[0]:Page1[0]:Field[0]:TextField1[0]:validate", lineNum:2, condition:"this.rawValue.length == 3"})

The dbg object also has methods for step into, step over, step out, etc but it is generally easier to use the toolbar of the JavaScript Debugger.

Watches

In the Inspect drop down there is also a Watches option this will allow you to enter any form object property or variable and watch the value change as you step thought your code.

 



Saturday 12 April 2014

Programmatically updating Rich Text (or xHTML)

In a previous sample I gave an example of updating the rich text of a draw object using E4X.  In this sample I want to show an alternative using the Span objects from the Adobe Reader API. 

The Span object does not cater for all the attributes of rich text that are supported in an XFA form but sometimes can be easier than using E4X.

So an example, the rich text "Please click the red button" is generated by the xHTML;

<body xmlns="http://www.w3.org/1999/xhtml" xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
    <p style="letter-spacing:0in">
        Please click the<span style="color:#ff0000">red</span>button
    </p>
</body>

As an array of Spans objects this is represented as

({text:"Please click the "})
({text:"red", textColor:["RGB", 1, 0, 0]})
({text:" button"})


Then the code to change all the red text to green would be;

var spans = util.xmlToSpans(Text1.value.exData["##xHTML"].saveXML());
for (var i = 0; i < spans.length; i++)
{

    if (spans[i].textColor && color.equal(spans[i].textColor, color.red))
    {

        spans[i].textColor = color.green;
        spans[i].text = "green";
    }
}

var xHTML = util.spansToXML(spans);
Text1.value.exData.loadXML(xHTML, falsetrue);

The color object used in this code is also from the Adobe Reader API, it defines some common colors  (that is black, white, dkGray, gray, ltGray, red, green, blue, cyan, magenta and yellow).  As well as the color.equal() method it also has a color.convert() method so to change all the colors to a gray scale we could;

var spans = util.xmlToSpans(Text1.value.exData["##xHTML"].saveXML());
for (var i = 0; i < spans.length; i++)
{

    if (spans[i].textColor)
    {
        spans[i].textColor = color.convert(spans[i].textColor, "G");
    }

}
var xHTML = util.spansToXML(spans);
Text1.value.exData.loadXML(xHTML, falsetrue);

If anyone still needs to have a color form for the screen and a black and white version for printing and uses rich text then this code could go in the prePrint event and then in the postPrint event you could revert to the original formatting with;

Text1.nodes.remove(Text1.value);
Removing the changes made in the Form DOM resets the properties to their initial values as defined in the Template DOM.

To build a rich text value from scratch we could;

var spans = [];

spans.push({text:"Hello "});

spans.push({text:NameField.rawValue, fontWeight:700, fontStyle:"italic"});
spans.push({text:", I hope you like this sample"});

var xHTML = util.spansToXML(spans);

Text1.value.exData.loadXML(xHTML, false, true);

If you need to change a plain-text Text field into a rich-text one then you can use the following code;

if (Text1.value.oneOfChild.className === "text")
{
    Text1.value.nodes.remove(Text1.value.text);
    var exDataNode = xfa.form.createNode("exData");
    Text1.value.nodes.append(exDataNode);
}

The sample SpanDemos.pdf includes a couple of other simple examples, as well as a more complicated example that will take the body element of the rich text then format and colorize it.  This sample also shows a workaround for the lack of support for the xfa-spacerun:yes style attribute in the Span objects.


background-color style attribute

This attribute is not mentioned in the XFA spec and there is no support for it with the Designer UI but Reader will render the background color, you just need to enter it in the XML Source view.  The trick with using background-color is that all elements following the one the style is applied to will inherit the color.  In an HTML page only the descendent elements would inherit the color.  This just means you need a second background-color style attribute to reset the color.  Designer doesn't play well with this attribute as every time you edit the text using the Design view you will have to add the second background-color style attribute back.

The color can be specified in hexadecimal or decimal;

background-color:#C4C4C4     or     background-color:rgb(196,196,196)

You can see an example of the background-color style attribute in this sample. 

I haven't gone though all the style attributes to see which ones are supported, but I did try adding a border and that didn't work for me.