Archive for May, 2011

Recover an Embedded Image

By now I’ve mentioned a bunch of times that it is better to link your images than to embed them.  Most notably in this blog entry.  Just to review the facts one more time:

  • Linked/embedded applies only to the form definition (the template). By the time you generate a PDF, the image is always embedded in the PDF.  But the mechanics of how it is embedded in the PDF differs from embedded to linked.
  • Images embedded in the template are stored in base64 format.  Linked images are stored in binary form.  Base64 is 4/3 bigger than binary
  • Embedded images cause very large form templates. Linked images are stored outside the form template.
  • Embedded images force Designer and Reader to keep the image in DOM memory, resulting in slower open times, higher memory footprint, slower performance in Reader and Designer
  • Same embedded image used <n> times is stored <n> times.  Same linked image used <n> times is stored once

Now, just in case all of that rationale was a bit too much tech-speak (what’s a DOM anyway?) let me simplify the message: Do not embed images in your templates. When you see this pallette, do not check the box.

Lost Images

I predict some of you will look at your forms and realize that you no longer have the original image that you embedded in your template.  It is possible to recover these images and extract them to an external file.  The process is a bit messy, but if you really need it, I’m sure you can figure it out.  Here are the steps:

  1. Find a web site that supports base64 decoding.  A google search for "base64 decode" led me to this site: http://webnet77.com/cgi-bin/helpers/base-64.pl
  2. In Designer, select the image object and then switch to XML source view.  Make note of the contentType.  In the example below, it is image/JPG.  This will tell you what file suffix to use when you download the file.
  3. Copy the base64 image data into your paste buffer.  i.e. select the base64 data and right-click to choose "Copy"
  4. Go to the web site and paste into the "Base64 to Decode" field.  Click the "Process now" button.
  5. Now click the "download binary file now" button.  Save the file using a suffix according to the content type — in this case, with a .jpg extention.

Once you have the image extracted, go back to your form design, re-specify the url to the new image and de-select the embed option.  This would be a great tool to develop into a Designer macro.  Especially given that mx.utils has a Base64Decoder class.

Optional Sections of Forms

When Adobe first released Reader 9.1 I wrote a series of blog entries describing the new forms features.  But of course, at that time it would have been easy to overlook them because you didn’t have a designer that could target 9.1.  Not to mention that your user base would take some time to upgrade to Reader 9.1.

But now some time has passed and it’s a good idea to revisit some of the 9.1 features.  Today we’ll look at field.presence = “inactive”.  For background, you may want to re-read the original post here

The sample for today shows a scenario where the form requests a payment type.  If the payment type is “credit card”, we need to expose the credit card fields to fill.  Not only that, but the credit card fields need to be mandatory. With the inactive setting, this becomes a very simple script:

if (this.rawValue === "Credit Card") {
    CreditCardDetails.presence = "visible";
} else { 
    CreditCardDetails.presence = "inactive";
}
            

If you wanted to do the same without the inactive setting, the script would look something like:

if (this.rawValue === "Credit Card") {
	CreditCardDetails.presence = "visible";
	CreditCardDetails.cardExpiry.mandatory = "error";
	CreditCardDetails.cardNumber.mandatory = "error";
	CreditCardDetails.cardType.mandatory = "error";

} else { 
	CreditCardDetails.presence = "hidden";
	CreditCardDetails.cardExpiry.mandatory = "disabled";
	CreditCardDetails.cardNumber.mandatory = "disabled";
	CreditCardDetails.cardType.mandatory = "disabled";
}

And this is with a very simple scenario where the optional section consists of three fields.  Imagine if the optional section had dozens of fields with mandatory settings, validation scripts etc.

The presence=”inactive” setting was added to help reduce the amount and complexity of code authors needed to write to author forms.  If you make use of it, you should find your forms easier to code and less difficult to maintain.

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.