Chapter 6. PDF Support

Detecting Whether PDF Support Is Available

Problem

You want to detect whether the user is able to display PDF content in your AIR application.

Solution

Read the value of the HTMLLoader.pdfCapability property to check that the user has a version of Adobe Reader or Adobe Acrobat 8.1 (or newer) installed.

Discussion

The HTMLLoader.pdfCapability property is set to one of the following constants of the HTMLPDFCapability class:

  • HTMLPDFCapability.STATUS_OK

  • HTMLPDFCapability.ERROR_INSTALLED_READER_NOT_FOUND

  • HTMLPDFCapability.ERROR_INSTALLED_READER_TOO_OLD

  • HTMLPDFCapability.ERROR_PREFERRED_READER_TOO_OLD

If the property has the value of the constant HTMLPDFCapability.STATUS_OK, then you are sure that PDF content can be loaded into the HTMLLoader object. It also can happen that the user does not have the right version of Adobe Reader installed. If the installed Adobe Reader version is too old, then the constant ERROR_INSTALLED_READER_TOO_OLD is returned when you call the HTMLLoader.pdfCapability property. If the user has different versions of Adobe Reader installed but the one that is set up to handle PDF content is older than Adobe Reader 8.1, the constant ERROR_PREFERRED_READER_TOO_OLD is returned. If the user has no version of Adobe Reader installed at all, then the HTMLLoader object will not be able to display any PDF content.

Working with PDF content on Windows involves one other important caveat. If Adobe Acrobat or Adobe Reader version 7.x (or newer) is opened on the user’s system, AIR references that specific opened version even if another version (8.1 or newer, for example) is installed on that system also. In this case, your PDF content will not load in your AIR application. When your PDF content is not loading within an acceptable time frame, it’s a best practice to inform the user that Adobe Acrobat must be closed while running your application.

ActionScript/Flex/Flash

The following code detects whether a user can display PDF content in an AIR application. If the STATUS_OK code is returned, then the application can display the content; if it isn’t, then you must trace the error code that corresponds to the HTMLPDFCapability error object.

if(HTMLLoader.pdfCapability == HTMLPDFCapability.STATUS_OK)
{
    trace("The user is able to display PDF content !");
}
else
{
   trace("PDF cannot be displayed. Error code:", HTMLLoader.pdfCapability);
}

JavaScript

The following code detects whether a user can display PDF content in an AIR application. If the STATUS_OK code is returned, then the application can display the content; if it isn’t, then you need to trace the error code that corresponds to the HTMLPDFCapability error object.

if(air.HTMLLoader.pdfCapability == air.HTMLPDFCapability.STATUS_OK)
{
    air.trace("The user is able to display PDF content ");
}
else
{
    air.trace("PDF cannot be displayed. Error code:", HTMLLoader.pdfCapability);
}

Loading a PDF Document

Problem

You want to load a PDF document in an AIR application.

Solution

Create an HTMLLoader instance, set the dimensions, and load the path of a PDF.

Discussion

Loading and rendering PDF content in your AIR application are identical to loading and rendering HTML. All you need to do is specify the path to the actual PDF when requesting the content to load into the HTMLLoader instance.

ActionScript/Flex

The following code snippet loads a PDF document using the HTMLLoader class. The HTMLLoader class loads a URLRequest object that points to the actual PDF document.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
   height="900" width="700" horizontalAlign="left">
   <mx:Script>
      <![CDATA[
         private function loadPDF():void
         {
            var request:URLRequest = new URLRequest("test.pdf");
            var pdf:HTMLLoader = new HTMLLoader();

            //set the dimensions
            pdf.height = 800;
            pdf.width = 600;

            //call the load method
            pdf.load(request);

            //add the HTMLLoader object to the display list to make it visible
            container.addChild(pdf);
         }
      ]]>
   </mx:Script>

   <mx:VBox>
      <mx:Label text="Dipsplay 'test.pdf'" />
      <mx:Button label="Load" click="loadPDF()" />
   </mx:VBox>
   <mx:Spacer id="container" />
</mx:WindowedApplication>

JavaScript/HTML

If you want to load a PDF in your AIR application using HTML, you follow similar steps for loading a PDF document into a browser. For example, you can load a PDF into the top-level HTML content of a window, into an object tag, into a frame, or into an iframe.

This is the HTML code for loading a PDF into an iframe:

<html>
    <body>
        <h1>PDF test</h1>
        <iframe id="pdfFrame"
            width="100%"
            height="100%"
            src="test.pdf"/>
    </body>
</html>

This is the HTML code for loading a PDF into an object tag:

<object id="PDFObj" data="test.pdf" type="application/pdf" width="100%" 
height="100%"/>
Communication flow between an AIR application and PDF
Figure 6-1. Communication flow between an AIR application and PDF

Communicating from AIR to PDF

Problem

You want to communicate with a PDF in your AIR application.

Solution

In your AIR application, load an HTML file (wrapper) that holds your PDF. Use JavaScript in your wrapper to communicate in both directions with the embedded JavaScript in your PDF file.

Discussion

Communicating with the PDF has many advantages. If a PDF document has, for example, a form, you can simply execute a JavaScript function when a user clicks a button in the form. You also can control page navigation with custom buttons in your PDF or can control the magnification of the document, to name only a few possibilities.

JavaScript handles the communication between your AIR application and the PDF document. Your AIR application loads an HTML file, and that HTML file loads the PDF file. In this way, you can easily send messages to the PDF or receive messages from the PDF in an AIR application. Figure 6-1 diagrams a simple communication flow.

As you can see, the AIR application first loads an HTML file (as you did in Loading a PDF Document), and that HTML file loads the actual PDF document. If you want to communicate with the loaded PDF, however, you need to set up some JavaScript in the PDF document. This is possible through the JavaScript extensions for Adobe Acrobat. (You can find full details on the JavaScript extensions for Adobe Acrobat in the Acrobat Developer Center at http://www.adobe.com/devnet/acrobat/javascript.html.)

If you want to send messages to the PDF (for example, “Next Page”), then you have to make sure the PDF can react to these messages. You need Adobe Acrobat Professional to add JavaScript to the PDF that can react to the messages you send. This kind of JavaScript is called document-level JavaScript.

Every PDF document has a HostContainer object. This object manages the communication between a PDF document and a corresponding host container that the document is contained within, such as an HTML page. You specify the host container for a document using the document’s hostContainer property.

The HostContainer object has a messageHandler property that is invoked when JavaScript in the host container calls the postMessage method. This property should reference a notification object that has three methods:

  • onMessage: A method that is called in response to postMessage

  • onError: A method that is called in response to an error

  • onDisclose: A method that is called to determine whether the host application is permitted to send messages to the document

The following code is a typical host container message handler:

this.hostContainer.messageHandler =
{
    onMessage: function(aMessage)
    {
          for(var i = 0; i < aMessage.length; i++)
          console.println(aMessage[i]);
    },
    onError: function(error, aMessage){ } ,

    onDisclose: function (cURL,cDocumentURL){return true;}
};

The this keyword in the first line of the code points to the Doc object of the document. The Doc object provides the interface between a PDF document and the JavaScript interpreter. It provides methods and properties for accessing a PDF document. For this example, use the pageNum property of the Doc object to jump from one page to another.

To add the JavaScript to the PDF, you need Adobe Acrobat. Create a new PDF document, and choose Advanced→Document Processing→Document JavaScripts. In the JavaScript Functions dialog box, type onMessage as the script name, and then click the Add button.

In the JavaScript Editor window, type the following code:

function myOnMessage(aMessage)
{
    if (aMessage.length==1) {
        switch(aMessage[0])
        {
            case "PageUp":
                pageNum--;
                break;
            case "PageDn":
                pageNum++;
                break;
            default:
                app.alert("Unknown message: " + aMessage[0]);
        }
    }
    else
    {
        app.alert("Message from hostContainer: 
" + aMessage);
    }
}

function myOnDisclose(cURL,cDocumentURL)
{
    return true;
}

function myOnError(error, aMessage)
{
    app.alert(error);
}

var msgHandlerObject = new Object();
msgHandlerObject.onMessage = myOnMessage;
msgHandlerObject.onError = myOnError;
msgHandlerObject.onDisclose = myOnDisclose;

this.hostContainer.messageHandler = msgHandlerObject;

When you save the PDF file, be sure to hide the menu bar, toolbars, and window controls. By doing so, you restrict the user’s navigation options to the controls in your Flex interface. You can set these values in the Document Properties dialog box.

Now you are ready to load the PDF in an AIR application. Because AIR applications can show HTML pages, embed the PDF in an HTML wrapper:

<html>
    <body>
        <object id="PDFObj"
            data="PDF_COM_with_AIR.pdf"
            type="application/pdf"
            width="100%"
            height="100%"/>
    </body>
</html>

You can now load the HTML file with the Flex HTML component:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
 width="900" height="700" layout="vertical">

   <mx:HTML id="pdfHtml" location="PDF_COM_with_AIR.html" width="100%" 
height="100%" />

</mx:WindowedApplication>

If you run your application now, you will see your PDF document rendered in the AIR application.

If you add, for example, two buttons in the application, you can control the paging by sending the right message String to the PDF document:

<mx:Button id="btn3" label="&lt;" click="sendMessage('PageUp')" width="35" 
enabled="false" />

<mx:Button id="btn4" label="&gt;" click="sendMessage('PageDn')" width="35" 
enabled="false" />

When the user clicks the buttons, the sendMessage function sends commands to the PDF object using the postMessage method of the PDF object. The sendMessage function in your script block looks like this:

private function sendMessage(message:String):void
{
    var pdfObj:Object = 
pdfHtml.htmlLoader.window.document.getElementById("PDFObj");

    pdfObj.postMessage([message]);

}

The document-level JavaScript in the PDF file has a hostContainer.messageHandler object defined, and that object knows how to handle the incoming messages. In this way, you can communicate with a PDF document from inside an AIR application.

You’ll now learn how you can communicate from inside the PDF to the AIR application. It is actually a similar technique, as you will see in the next example.

Communicating from PDF to AIR

Problem

You want to communicate from a loaded PDF document to your AIR application.

Solution

In your AIR application, load an HTML file (wrapper) that holds your PDF. Use JavaScript in your wrapper to communicate in both directions with the embedded JavaScript in your PDF file.

Discussion

A simple form-processing example demonstrates communication from a PDF document to its hosting AIR environment. This example loads a PDF document, which includes a form and a submit button, into an AIR application. When the user fills in the form and clicks the submit button, the form data is sent to the hosting AIR application, and a customized “thank you” message is displayed to the user.

ActionScript code in an AIR application cannot directly communicate with JavaScript in the PDF. ActionScript can communicate with the JavaScript in the HTML page, however, and that JavaScript code can communicate with the JavaScript in the loaded PDF file.

Figure 6-2 shows the simple PDF contact form.

A PDF contact form
Figure 6-2. A PDF contact form

When the user clicks the button, the JavaScript code gets the values from the respective fields in the PDF form and stores them in an Array called arrMsg. By calling the postMessage method, the data is sent to the hostContainer, which is the JavaScript in the HTML page that loads the PDF:

this.hostContainer.postMessage(arrMsg);

The complete JavaScript code on the click action of the submit button in the PDF looks like this:

var strName = this.getField("nameField").value.toString();

    var strSurname = this.getField("surnameField").value.toString();
    var strNumber = this.getField("numberField").value.toString();
    var strStreet = this.getField("streetField").value.toString();
    var strCity = this.getField("cityField").value.toString();
    var strPostalcode = this.getField("postalcodeField").value.toString();
    var strCountry = this.getField("countryField").value.toString();
    var strEmailAddress = this.getField("EmailAddressField").value.toString();
    var boNewsLetter = this.getField("newsletterCheck").value;



      var arrMsg = null;
      try{
        arrMsg = new Array();
        arrMsg=[strName,strSurname,strNumber,strStreet,strCity,strPostalcode,strCountry,str
EmailAddress,boNewsLetter];

      }catch(e){
        arrMsg = ["Error"];
      }
      console.println("Msg: " + arrMsg.length + " : " + arrMsg);

      try{
        this.hostContainer.postMessage(arrMsg);
      }catch(e){

      }

To receive the data in the HTML page, use the same messageHandler system as you did in Communicating from AIR to PDF. The complete HTML page code looks like this:

<html>
    <head>
        <title>Communicate from PDF to AIR</title>
        <script type="text/javascript" >

            function initialize()
            {
                 var PDFObject = document.getElementById("PDF");
                PDFObject.messageHandler =
                {
                  onMessage: function(aMessage)
                  {
                      alert(aMessage);
                      return true;
                  },
                  onError: function(error, aMessage)
                  {
                    alert("!error!");
                  }
                };
            }
        </script>
    </head>
    <body onLoad="initialize()">
        <object id="PDF" height=90% width=90% type="application/pdf" 
data="ContactForm.pdf"></object>
    </div>
    </body>
</html>

When you are developing AIR applications with HTML and JavaScript/Ajax, you are done with the communication. In the previous JavaScript block, the aMessage parameter passed to the methods in the messageHandler is an Array containing all the data from the PDF form you might need to access.

If you are developing with Flex (ActionScript), you need to go one step further. Figure 6-3 shows the application’s layout.

When the user clicks the Load PDF button, the HTML page is loaded into the HTML control; this happens by executing the following function:

private function loadPdf(path:String):void{
   var url:URLRequest = new URLRequest(path);
   pdfContainer.addEventListener(Event.COMPLETE,completeHandler);
   pdfContainer.htmlLoader.load(url);
}

In the previous code, the pdfContainer is a reference to the HTML control hosting the PDF document, and the completeHandler is where the application will talk with the global JavaScript object for the content loaded into the HTML control. You can get a reference to that JavaScript object by using the htmlLoader.window property of the HTML control (here called pdf). When you have the reference, you have a good level of control; for example, you can create a new function at runtime in the JavaScript of your HTML page and set that function to “point to” a function defined in ActionScript. So, the completeHandler looks like this:

private function completeHandler(event:Event):void{
   pdf.htmlLoader.window.storeValues = storeValues;
}

Because you couple the storeValues JavaScript function to the storeValues ActionScript function, when you define a function call for the storeValues function in the JavaScript of the HTML page, the ActionScript version of the function will be used.

The application layout of the example
Figure 6-3. The application layout of the example

The final JavaScript function in the HTML page looks like this:

<script type="text/javascript" >

            function initialize()
            {
                 var PDFObject = document.getElementById("PDF");
                PDFObject.messageHandler =
                {
                  onMessage: function(aMessage)
                  {
                      storeValues(aMessage);
                      return true;
                  },
                  onError: function(error, aMessage)
                  {
                    alert("!error!");
                  }
                };
            }
        </script>

Now you can also write the storeValues function in ActionScript:

private function storeValues(fieldArray:Object):void{
    //get the values from the sent object
    var thanksStr:String = "THANKS FOR FILLING IN THE FORM " + fieldArray[0] +", " 
+fieldArray[1];
    currentState='thanks';
    thanksLabel.text = thanksStr;
}

This function switches application states and shows a customized “thank you” message to the user. The fieldArray contains all the data from the contact form. As you can see, the fieldArray is typed as a simple Object. This is done because the fieldArray has a special type that you can easily check as follows:

trace(flash.utils.getQualifiedClassName(fieldArray));

This trace statement returns the following result:

flash.html::__HTMLScriptArray

In ActionScript you are not able to define this type, so you must type the incoming Array as an Object or eventually type it as an asterisk (fieldArray: *).

The complete Flex code for this example looks like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"

    layout="absolute"

    >
    <mx:states>
        <mx:State name="thanks">
            <mx:RemoveChild target="{pdf}"/>
            <mx:RemoveChild target="{hbox1}"/>
            <mx:RemoveChild target="{vbox1}"/>
            <mx:AddChild position="lastChild">
                <mx:Label textAlign="center" id="thanksLabel" fontSize="18"  
width="100%" x="0" y="233"/>
            </mx:AddChild>
        </mx:State>
    </mx:states>
    <mx:Script>
        <![CDATA[

            private function loadPdf(path:String):void{
                var url:URLRequest = new URLRequest(path);
                pdfContainer.addEventListener(Event.COMPLETE,completeHandler);
                pdfContainer.htmlLoader.load(url);
            }
            private function completeHandler(event:Event):void{
                pdfContainer.htmlLoader.window.storeValues = storeValues;
            }
            private function storeValues(fieldArray:Object):void{
                //get the values from the sent object
                var thanksStr:String = "THANKS FOR FILLING IN THE FORM " + 
fieldArray[0] +", " +fieldArray[1];
                currentState='thanks';
                thanksLabel.text = thanksStr;
            }
        ]]>
    </mx:Script>
        <mx:VBox width="100%" height="100%" id="vbox1">
        <mx:HBox width="100%" height="50" id="hbox1" horizontalAlign="center">
            <mx:Button label="Load PDF" click="loadPdf('DocRight.htm')" width="80" 
height="50"/>
        </mx:HBox>
        <mx:HTML id="pdfContainer" width="100%" height="75%" />
    </mx:VBox>
</mx:WindowedApplication>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.5.68