Lately I’ve been thinking on what to blog and either haven’t found anything particular to talk about or just don’t have the will to do it. I have a bunch of unpublished posts on hold due to lack of time to finish the code samples and what not, so I decided to post a bit on what I have my hands on as of today. Perhaps later I’ll try to just finish what’s left undone, perhaps not.
Anyway, these days I’ve been working on a crazy PDF – Flash integration and have been getting my hands dirty on Acrobat Javascript and figuring out what would be the best way to make a SWF file talk to a PDF document and vice verse — I have to accept though that I was not even aware that a PDF document could be scripted, WTF!, and further on do all sorts of crazy sh!t like accessing databases, consume web services, etc.
Given that the integration had to happen inside the web browser, using Adobe Air was not an option, so the first item on the TODO list was to be able to “render” a PDF document within a flash movie. I quoted render because the flash player does not support it so the approach (or hack if you will) consisted on placing the PDF object on top of the SWF giving the fake impression that the PDF is contained within the SWF application. Once I was able to place my PDF doc on top of my SWF, I had to figure out how to make both of them talk to each other. To my surprise, it turned out to be very simple.
To keep things simple, the sole requirement is to make our SWF movie to respond-or-act depending on the interaction that our user has on a PDF form. Let see what it takes to accomplish this task.

(Click here to see the example, tested in Firefox and Chrome, and the code, right-click view source enabled. Obviously it is required to have a Adobe Reader installed)
First, we’ll create our flash application. I’m using Flex 3 by the way, but you are completely free to do it in whatever version you like
Basically this “app” will have a button to get our PDF document, and the necessary visual elements to display whenever the user does something within the interactive PDF form. And a special container that will act as our PDF placeholder and also take care of all of its position and resizing actions.
The most important aspect of our Flex application is the canvas control, as it will serve as our PDF document placeholder. Obviously the PDF document won’t be loaded within the canvas control, because is not possible, but what it’ll do instead is determine the position and size of our PDF object that will get loaded and displayed within our HTML page. This PDF placeholder, which I repeat is a Canvas, has the following code that is used to load the PDF object (by the way is a total rip-off, plus a couple of other things I needed, of the IFrame component that can be found over here, thanks guys):
public function set source(source: String): void
{
if (source)
{
if (! ExternalInterface.available)
{
throw new Error("ExternalInterface is not available in this container. Internet Explorer ActiveX, Firefox, Mozilla 1.7.5 and greater, or other browsers that support NPRuntime are required.");
}
__source = source;
ExternalInterface.call("loadPDFObject", source);
movePDFObject();
}
}
, basically what the code on the PDFContainer class does is invoke some javascript functions, through ExternalInterface, to load the PDF document and place it within a previously define HTML DIV. Also, has some other methods to handle resizing and positioning in case we want to re-size or reposition our PDF placeholder canvas within our flex application. For this particular example I won’t make use of any reisizing and reposition stuff, other than the initial.
Ok, I will move on to the PDF part now, and we’ll get back to our Flex app later. As I mentioned at the beginning of this post, you can do all kinds of things within a PDF document – you can add some Javascript to it in order for it to communicate with databases, web services, etc. For our example we are only going to add the necessary stuff to move around data between our PDF document and our Flex application. Acrobat Javascript is basically a framework built on top javascript, so you pretty much can do all the things you’d normally do with javascript, plus a lot of other cool stuff related to the PDF themselves.
It is worth mentioning that all javascript within a PDF happens within its contextt, it is isolated from the external container in case that the PDF is embedded within an HTML page. That being said some of the JS functions that you’d normally use on an HTML container don’t have the same effect within the PDF document, for example, to display an alert on a HTML page you would do:
If you script that on your PDF document nothing will happen, no alert will pop out. In order to display an alert within the PDF document you’ll do instead
The “app” object bascailly represents the Acrobat application and provides you access to a bunch functions and routines that are specific to Acrobat. On the other hand there is the “Document” object, which is the most important as it enables the communication between the PDF document itself, the one that is in the viewer, and the Javascript interpreter. Through that object, which usually you refer to it using “this”, you can use some handy methods and properties for accessing the PDF document. For example, let say you have a form that contains a text field named “MySuperCoolTxtField” (pretty lame name I know), you could get its value like:
this.getField("MySuperCollTxtField").value
, it sorts of reminds me to the getElementById method available in JS. Doesn’t it ? Anyway, this is not intended to be an Acrobat JS tutorial nor I do have the expertise to elaborate on one, so if you are interested on going furhter with Arobat JS here are some helpful guides.
Moving on to you initial purpose … where was I ?
Ok, now we are going to create a simple PDF form (here you can take a look at it) to make our little test. Well I actually have it ready, so I’ll just mention pretty much the gist. Here is the final PDF form. It is worth mentioning that you’ll need Acrobat Pro in order to be able to create interactive forms and add JS to your documents and stuff. Basically what the form will do is to trigger events whenever one of its controls got/loses focus, which will be hopefully catched by our HTML JS.
If you take a look at the form you’ll see that we have 4 controls, 3 textfields and one button. We have NameTxt, LocationTxt, and EmailTxt, and the SaveBtn. Each of the text controls have two actions attached. An action represents an function that you would like tog get executed as a response of an event triggered by the control. For example for this text controls Acrobat allows you to add actions for the events MouseUp, MouseDown, MouseExit, OnBlur, OnFocus, and MouseEnter. For this example I just added actions for the OnBlur and OnFocus events of all of the three text controls. See screenshot below.

On the OnFocus action we have this.sendOnFocusMessage("NameTxt");, and on the OnBlur action we have this.sendOnBlurMessage("NameTxt"); . If we click on Edit with a particular action selected an editor will pop out to display the current code associated to that action, if any. We have the same function for all of the other controls with the difference that we send its correspoing name as the function argument (“LocationTxt” and “EmailTxt” respectively).
If you recall, “this” refers to the Document it self, so we are calling a function that exists within the document scope. You can add as many code to your document as you wish, if you happen to have your Acrobat ready you’ll need to go to Main Menu -> Advanced -> Document Processing -> Document Javascripts, which will open a window that will list all of the code/functions associated with your Document.

This is the code that we have on those functions,

function sendOnBlurMessage(fieldName)
{
if(!this.hostContainer)
app.alert("No hostContainer object found");
else
{
try
{
var fieldValue = this.getField(fieldName).value;
if( fieldValue.length == 0 ) fieldValue = " ";
this.hostContainer.postMessage(["OnBlur", fieldName, fieldValue]);
}
catch(e)
{
//Display some error
}
}
}
function sendOnFocusMessage(fieldName)
{
if(!this.hostContainer)
app.alert("No hostContainer object found");
else
{
try
{
this.hostContainer.postMessage(["OnFocus", fieldName]);
}
catch(e)
{
//Display some error, this alert will be displayed within the PDF document
}
}
}
Ok, before I go any further let me point out a couple of things … If you look at the above code, they pretty much looks normal, however there is a dude called hostContainer. WTF? Ok, hostContainer is an object that your document has access to in order to communicate with its host container, DUH!!!! that sounded stupid, for example an HTML page; hostContainer will be null if you open your document on your PDF viewer application, otherwise, when opened inside an HTML, this object will hold a reference to it. Look at hostContainer as something along the lines of ExternalInterface. The hostContainer basically uses the postMessage function to send messages, and also can have event handlers registered in order to respond to specific actions. Notice that the argument that postMessage expects, which is mandatory, is an array of strings.
As to what the functions do, well, basically whenever a control looses focus they send a message to tell the container was it, and when they get focus also pass the data entered by the user. I guess we have everything we need on our PDF document, so let’s move on to our actual HTML JS. That was simple!
In our HTML javascript we need to do a couple of things – first register message handlers to our PDF object, and create our actual handlers. So, we ended up with something like the following:
function registerPDFMessageHandlers()
{
try
{
var PDFObject = document.getElementById("PDFObjectID");
PDFObject.messageHandler = {
onMessage:onMessageHandler,
onError:onErrorHandler
};
}
catch(e)
{
alert("Error: " + e);
}
}
function onMessageHandler(params)
{
// params - Array
// 0: Action triggered
// 1: FieldName
// 2: FieldValue
var action = params[0];
var fieldName = params[1];
var fieldValue = params[2];
var swf = getMovie("PDFSample");
if( swf )
{
switch( action )
{
case Action.onFocus:
swf.sendOnFocus(fieldName);
break;
case Action.onBlur:
swf.sendOnBlur(fieldName, trim(fieldValue));
break;
}
}
else
{
alert( "SWF Object not found" );
}
}
function onErrorHandler(error, message)
{
alert("Error: " + error);
}
The registerPDFMessageHandlers register the proper message handlers on our PDF object, which means that whenever the postMessage method gets invoked (insider our PDF object) the event handlers will get called. I forgot to mention earlier, but our hostContainer acts on 3 differente evets, onMessage, invoked at a successfully postMessage action, onError and onDisclose. You can take a look at the documentation for further information. registerPDFMessageHandlers is called right after our PDF object is loaded, remeber that the loadPDFObject function is called by our Flex application through ExternalInterface.
The function defined as our onMessage event handler, onMessageHandler, is the one that handles the message data that is passed from our PDF document. What it does is very simple stupid, it basically see whether is recieving an OnBlur or OnFocus action and calls the appropiate SWF functions, previously discussed, that have been exposed by ExternalInterface. It is pretty much self explanatory all of it.
Now, going back to our Flex application…. man! this has been a long post! if you are still reading all of this crap I applaud you, seriously!!! you are the man!!
The last part that I want to go over briefly is the code that handles the javascript calls made after our HTML container recieves PDF news, thus make in it look as if our Flex application is instantly and directly communicating with our PDF document. We have two methods exposed by ExternalInteface, one to handle OnFocus message, and the other one to handle OnBlur messages. We could have on sole method to handle all of the messaging however just for the sake of this example I decided two have one for each of the events happening within our PDF form.
private function _onFocusSendCommand(fieldName:Object):void
{
if( this.alertWrapper != null )
{
this.floatingAlertSpanshot = new UIComponent();
snapshotData = new BitmapData(this.floatingAlertSprite.width, this.floatingAlertSprite.height, true);
snapshotData.draw(this.floatingAlertSprite, new Matrix());
snapshotBmp = new Bitmap(snapshotData);
this.floatingAlertSpanshot.x = this.alertWrapper.x;
this.floatingAlertSpanshot.y = this.alertWrapper.y;
this.floatingAlertSpanshot.addChild(snapshotBmp);
this.addChild(this.floatingAlertSpanshot);
this.alertWrapper.alpha = 0;
this.alertWrapper.removeChild(this.floatingAlertSprite);
this.floatingAlertSprite = null;
var t:GTween = new GTween(this.floatingAlertSpanshot, 0.2, {alpha:0});
}
else
{
this.alertWrapper = new UIComponent();
this.alertWrapper.alpha = 0;
this.addChild(this.alertWrapper);
}
var alertMsg:String;
var alertPos:Point = new Point();
alertPos.x = this.pdfContainer.x + this.pdfContainer.width + 20;
switch( fieldName )
{
case Constants.PDF_NAME_FIELD:
alertMsg = Constants.ENTER_NAME_MESAGE;
alertPos.y = this.pdfContainer.y + 190;
break;
case Constants.PDF_LOCATION_FIELD:
alertMsg = Constants.ENTER_LOCATION_MESSAGE;
alertPos.y = this.pdfContainer.y + 230;
break;
case Constants.PDF_EMAIL_FIELD:
alertMsg = Constants.ENTER_EMAIL_MESSAGE;
alertPos.y = this.pdfContainer.y + 270;
break;
}
this.floatingAlertSprite = new FloatingAlert(alertMsg, "Important Tip");
alertWrapper.addChild(this.floatingAlertSprite);
alertWrapper.x = alertPos.x;
alertWrapper.y = alertPos.y;
var gt:GTween = new GTween(alertWrapper, 0.2, {alpha:1});
}
private function _onBlurSendCommand(fieldName:Object, fieldValue:Object):void
{
var fieldValueStr:String = fieldValue as String;
this[fieldName] = (fieldValueStr.length != 0 && fieldValueStr != "") ? fieldValueStr : null;
var infoStr:String = "";
infoStr += (this.NameTxt != null) ? "Your name is '" + this.NameTxt + "'. " : "";
infoStr += ( this.LocationTxt != null ) ? "You are located at '" + this.LocationTxt + "'. " : "";
infoStr += ( this.EmailTxt != null ) ? "Ohh, and BTW, this email address '" + this.EmailTxt + "' will get lots of spam from me." : "";
if( infoStr == "" ) infoStr = "Please fill out the form on the left";
this.dataBoard.message = infoStr;
}
private function _loadPDF(e:Event):void
{
this.loadBtn.enabled = false;
this.pdfContainer.source = "form.pdf";
var tw:GTween = new GTween(this.dataBoardWrapper, 0.25, {x:this.pdfContainer.x + this.pdfContainer.width + 20});
}
So, what the code above does ? Well nothing cool in particular, the _onFocusSendCommand method is called whenever a control in our PDF gets focus, and what the Flex application does is to display an alert message to let the user know the type of data that is expecting for that component. On the other hand, the _onBlurSendCommand gets executed whenever a control in our PDF document has lost its focus and perhaps has some info in it. So whenever that method is executed a cool alert is displayed to tell the user which information has been entered so far in the PDF form. Lastly, the _loadPDF method, well as it name implies LOADS THE DAMN PDF!!!
it just sets the PDFContainer’s source property to whatever is the PDF URL and invokes the javascript function that loads and displays our PDF object within our HTML page.
To make our PDF document recieve messages from our HTML container is just a matter of adding the proper message handlers, as we did in HTML JS, inside our PDF document javascript code, and call PDFObject.postMessage(whatever) within our javascript. If we set up the proper onMessage handler in our PDF it will be invoked whenever a postMessage is executed in our HTML container. And to add our SWF into the equation is just a matter of adding the proper ExternalInterface callbacks to do. Probably a topic worth a following post, I’ll just need to clean up somethings up and I’ll post it some day.
I hope, if you made it this far, that you liked what you read and found it useful as I did
There are a lot of cool stuff that could be accomplish way beyond of what was explained here. This is not intended to be a thorough guide on how to interact with your PDFs but to serve as a starting point and give you an overall idea of how you can start to do so. There are a lot of other features that can be pulled off with a Acrobat javascript, again, if you are on to this here you can find interesting documentation.
Thanks for stopping by, your comments, and cheers, now is time for me to grab a beer!