Designing Custom External Document Viewer

Designing an external document viewer requires Flash and
ActionScript 3 programming skills. For developing of your viewer you may use
Adobe Flash CS Professional or Adobe Flex.
Print2Flash SDK contains a sample of a simple external document viewer
developed in Adobe Flash CS3 Professional. It can be imported into Adobe Flash
CS4 or CS5 Professional formats as well. We recommend using this sample as a starting
point when developing your own viewer. The description below deals with this
sample code so refer to it for full details. If you want to develop a viewer
using Flex, you may use code scraps from the sample and from below in this help topic.
Document representation
To use an external document viewer, you need to generate documents in
"External Viewer" format. Documents in this format are usually
represented as a single SWF file. The responsibility of your viewer is to load
that document file, retrieve document pages and settings from it and display the
document in your custom way. In rare cases document cannot fit a single file
due to Flash format limitations. In this case Print2Flash
splits a document into several parts and stores it as a set of SWF files. To
account for such cases your viewer must also be able to load and process each
part of the document. Additional parts are named similarly to the main SWF file
name but with _partXXX appended to the file name. Here XXX is an
ordinal part number which starts from 2. For example, if a document with main
output file name synopsis.swf can fit into a single file, it is named
synopsis.swf. However, if it has to be split into, say, three parts, three
files are created:
- synopsis.swf;
- synopsis_part2.swf;
- synopsis_part3.swf.
Loading the main document file
The document loading starts from the loading of the first part of the
document: the main document file. It contains document settings as well as the
starting (or all) document pages. You need to communicate somehow to your viewer
the URL of the main document file. For example, you may pass it from a HTML page
embedding your viewer using FlashVars parameter, you may pass it from another
Flash movie, etc. To load the main document file once its URL is known and put
into url variable, you may use this code:
var loader:Loader=new Loader();
loader.contentLoaderInfo.addEventListener("init", onDocInit);
loader.visible=false;
addChild(loader);
loader.load(new URLRequest(url), new LoaderContext(false,new ApplicationDomain(null)));
|
When the document file starts loading and document settings become available,
init event is fired on the loader object. You need to process this
event in order to retrieve settings from it and start loading additional
document parts (if any). In the sample above we specified the onDocInit
function as a listener of init event (discussed below).
Retrieving document settings
Document may have a number of settings stored in the document SWF file. These
settings help the viewer in document rendering. If the document is split into
several parts, settings are written into the first main document file only.
The list of common available
parameters is contained in Custom Document Viewer Settings help topic. To read these settings you need to
declare and execute this code:
var settings:XML;
function AttachBinData(id):ByteArray {
var ba:ByteArray
try {
var AssetClass:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(id) as Class;
ba = new AssetClass();
}
catch(e) {
}
return ba
}
function GetSetting(name){
var val=settings.attribute(name);
return val;
}
|
Then you may use declared GetSetting function to retrieve a setting value
passing it the setting name as a parameter. However, before this you need to
load settings into settings variable as described
below. For example, to get the
resolution of the document, you may use this code:
var resolution:Number=parseInt(GetSetting("Resolution"));
|
Note that all values are returned by GetSetting function as strings so you may need to convert them
to other types as demonstrated in the previous sample with standard parseInt
function.
ChunkPageNums parameter
The most important parameter available in documents generated in "External
viewer" format is ChunkPageNums parameter. It stores a comma-separated list of
the number of pages contained in each document part. Using this parameter you
may determine:
- the number of document parts (files);
- the number of pages in each document part;
- the total number of pages.
For example, if a document has been split into three parts, the ChunkPageNums
parameter may contain, for example, this value: 102,98,40. It means that:
- the document contains three parts;
- the number of pages in the first part is 102, in the second - 98 and in
the third - 40;
- the total number of pages in the document is 102+98+40=240 pages.
Processing document settings
Once the document settings are available, init event is fired on the
loader object and onDocInit function is invoked because we declared it
earlier as this event listener. This function should retrieve settings from the
main document file and start loading of additional document parts (if any). To
retrieve document settings, you need to execute this code:
var settingsArr:ByteArray=AttachBinData("Settings");
settings=new XML(settingsArr.readUTFBytes(settingsArr.length));
|
Then to retrieve information on document parts, you need to use ChunkPageNums
parameter like this:
var pageChunkLimits=GetSetting("ChunkPageNums").split(/,/);
|
It will create an array of values representing a number of pages in each
document part. The number of values in this array correspond to the number of
parts in the document. For example, for the sample document mentioned above, the
array will contain three values: 102, 98 and 40.
Then you may load additional document parts (if necessary):
var ChunkLoaders:Array=new Array();
var totalPages:Number=pageChunkLimits[0];
for (var i:int=1;i<pageChunkLimits.length;i++) {
var loader:Loader=new Loader();
ChunkLoaders.push(loader);
loader.visible=false;
addChild(loader);
var url:String= ... // Determine the URL of the current document part file here
loader.load(new URLRequest(url), new LoaderContext(false,new ApplicationDomain(null)));
totalPages+=parseInt(pageChunkLimits[i])
}
|
You need to store each created loader object in an array (ChunkLoaders array
in this example) for later usage for attachment of page
movies. Also, you need to determine each part URL by appending
_partXXX string to the main document URL. You
may also calculate the total number of pages in the document by summing the
values of the pageChunkLimits array and put the result in a totalPages
variable which may appear useful later. See the SDK sample
for full details.
Attaching page movies
Each page movie is represented by an ActionScript class inherited from MovieClip
class associated with a symbol of the library located in the respective document
part file. To create a page movie instance, you may use
this function:
function AttachPageMovie(loader:Loader, pageno:Number, parent:DisplayObjectContainer) {
var id="Page"+pageno
var mc
try {
var AssetClass:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(id) as Class;
mc = new AssetClass();
}
catch(e:Error) {
}
if (mc) parent.addChild(mc);
return mc;
}
|
The function accepts three parameters:
- loader - an instance of Loader object which represents the document part
in which the page is located;
- pageno - the number of page to create (starts from 1);
- parent - the parent movie to which to attach the created page movie as a
child.
The function returns a reference to the created MovieClip instance of the
page movie. If a movie cannot be created, null is returned.
Print2Flash appends page movies to the document part files in such a way so that
document can be opened and viewed by users before Flash files are fully
loaded. This makes user interface more responsive and allows users to start
viewing the document sooner without having to wait until all document files
are
loaded in full. As a result of this, pages may not be available yet as soon as
you started downloading document part files. They become available
later as pages code is being loaded by the Flash Player. Because of this we
recommend to start a timer and attach pages in timer function until all pages
are loaded as demonstrated in this code:
var myTimer:Timer = new Timer(100);
myTimer.addEventListener("timer", CheckLoadedPages);
var loadedPages=0;
myTimer.start();
function CheckLoadedPages(event:TimerEvent):void {
ScanPages(loadedPages+1);
if (loadedPages>=totalPages) {
myTimer.stop();
myTimer=null;
}
}
function ScanPages(startpage) {
var pageno=startpage;
do {
var loader:Loader= ... // Determine the relevant Loader object for page #pageno
var movie=AttachPageMovie(loader,pageno,parent);
var validpage = movie!=null;
if (validpage) {
// Process page here: scale, position, etc.
loadedPages=pageno;
}
pageno++;
}
while(validpage);
}
|
This code assumes that parent variable is set to a reference to the
parent movie. After successful attachment you may process the page instance:
scale, position it, etc. as indicated in the code comment. See
the SDK sample for full details.
|