External Code for PDF Forms: The Secret Revealed

To summarize where we are, we’ve been trying to find a way to store functions as

persistent globals, so that we can come back to them, from ANY future PDF form that our

reader downloads from our site, and use the code that’s stored there. It’s kind of like

being able to take a bathroom break during the Scholastic Aptitude Test, and having the

Cliffs Notes to Euclid’s Geometry already stored behind the toilet in the men’s room. We

want to be able to put whatever Cliffs Notes we want, behind whosoever’s toilet we want.

Recap

What we’ve figured out so far is that we can convert a function to a string with

JavaScript’s built-in toString() function. And we can convert strings to functions

with the built-in Function constructor. But in order to use the latter, we have to supply

the ‘guts’ of the function as a string, minus the declarative ‘function funcName(args)’

prefix. We developed a regex to clip that prefix off of function declarations yesterday.

But we also have another problem to deal with, namely the problem that toString()

introduces unwanted formatting into the strings it produces! Weirdly enough, it turns out

that this function inserts newlines and spaces willy-nilly, to format our code, whether

that’s what we wanted or not. The reason this is a problem for us is that when we put the

resulting string into a persistent global, and then that global gets read into memory again

later (at the start of another session), the newlines cause errors of the ‘unterminated

string literal’ kind.

The answer to the latter problem is another regex. All we have to do is pass the output

of toString() thru a regex that removes newlines. In JavaScript, we do:

str = str.replace(/n/g,'');

This regex-replace operation is applied globally (because of the lowercase ‘g’) to all

newlines. And since the replacement string is null, we end up deleting all newlines, plain

and simple. Problem solved.

The Magic Formula

So, in order to store a library function as a persistent global, here is what we have to

do:

  • Convert the function to a string.
    var str = myFunc().toString();

  • Take the declarative front end off.
    str = str.replace(/function [a-zA-Z_]+(S*)/g,'');

  • Take the newlines out.
    str = str.replace(/n/g,'');

  • Make it a global.
    global.f1 = str;

  • Make it persistent.
    global.setPersistent('f1',true);

To restore the function, we have to do this:

  • Read the global string into a variable (optional).
    var s = global.f1;

  • Construct a new function.
    var newFunc = new Function('a',s);

  • Use the new function just like you would the old one.
    x = newFunc( a );

The ‘a’ in the Function constructor is the argument that the old function took (if any).

Some functions have multiple args, some have none; obviously you’ll have to tailor this part

to the situation, although I hasten to add that as an exercise, you should be able to devise

a general solution (one set of code fitting all situations) using regexes, arrays, and/or

common sense. We’ve already complicated this discussion enough with regexes, so I won’t go

there any more just now. However, I do encourage you to play with the above recipes for

storing and reconstructing functions as persistent globals. Maybe you can find ways to

improve on my scheme through the use of arrays, concatenated strings (join/slice methods),

etc. In fact, I’m sure you can find many ways to improve on this scheme. All I was

trying to do is determine a way in which persistent storage of library functions

could be done. At all.

Checking to See If a Global Exists

Before I stop today, I want to give a tip for checking to see if a global already has

been defined or already ‘lives’ because of persistence. Sometimes in JavaScript it is not

easy to check a variable for validity. But in this case it turns out to be easy. You can

check a global this way:


if (global.myWhatever == 'undefined') { // doesn't exist }else
{ // exists}

An undefined variable, in PDF JavaScript, will evaluate to the string ‘undefined’. So

testing it is easy. Use this trick to test whether your library functions (above) are

present.

Another trick:

When you want to make a variable global, but not necessarily persistent, you do NOT have

to store it in the Global object explicitly. Just go up to the Tools:Forms:Document

JavaScripts menu command in Acrobat, and enter a global as you would a top-level function.

(Delete the function skeleton.) For example, enter gMyVariable = 0; and hit OK to

dismiss the dialog. (Don’t type a function.) From that point on, gMyVariable will be

globally available throughout your document, to any function that needs it. And, not only is

it available throguhout the current document… it is available to ALL open PDF documents

that have JavaScripts. As I said, it is a true global.

You May Also Like

About the Author: Kas Thomas

Leave a Reply