无痕客

落花无情,流水无痕……

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
SWFTools (pdf2swf) to properly work with Flex

It’s hype, there’s no word to describe how to involve and open-source command-line tool to transcode from pdf to swf file and get it right work with Flex.
So, I googled a lot yesterday and today morning, and didn’t find any relevance information on this field. I just saw a collection of developers getting problem to put it to work. For sure it’s a little hard work to put it in right to work.
The problem took me attention because we got a job from a start-up company that does document of any type and convert into a swf. Of course that you should put more effort to accomplish bunch things like Scribd does.
To help theses unlucky devs out there follow the steps of this tutorial bellow and 20% of your problem will solve, bet if solves 100% isn’t?

Get Start

  • Download the last version of SWFTools and put in the root of your HD access mine was (C:\swftools)
  • Grab some pdf you want to convert into swf and start transcode.
  • Open up your Prompt command and goes to the path where you put your SWFTools

To run the pdf to swf converter you just type :

C:\SWFTools\pdf2swf originalfile.pdf -o newfile.swf

This is automatically converts your pdf into something easy format like swf and ready to Flash Player.

The pdf2swf has many options see them bellow:

  • −h, −−help Print short help message and exit
  • −V, −−version Print version info and exit
  • −o, −−output file.swf will Go into a seperate file.
  • −p, −−pages range Ex:3-5,10-12
  • −P, −−password password Use password for deciphering the pdf.
  • −v, −−verbose Be verbose. Use more than one -v for greater effect.
  • −z, −−zlib The resulting SWF will not be playable in browsers with Flash Plugins 5 and below!
  • −i, −−ignore SWF files a little bit smaller, but it may also cause the images in the pdf to look funny.
  • −j, −−jpegquality quality Set quality of embedded jpeg pictures to quality. 0 is worst (small), 100 is best (big). (default:85)
  • −s, −−set param=value Set a SWF encoder specific parameter. See pdf2swf -s help for more information.
  • −w, −−samewindow When clicked on, the page they point to will be opened in the window the SWF is displayed.
  • −t, −−stop The resulting SWF file will not turn pages automatically.
  • −T, −−flashversion num Set Flash Version in the SWF header to num.
  • −F, −−fontdir directory Add directory to the font search path.
  • −b, −−defaultviewer Therefore the swf file will be “browseable”, i.e. display some buttons for turning pages.
    The viewer swf to be used is determined by a symlink named “default_viewer.swf” in
    the swftools data directory.
  • −l, −−defaultloader The loader swf to be used is determined by a symlink named “default_loader.swf” in
    the swftools data directory.
  • −B, −−viewer filename See http://www.quiss.org/swftools/pdf2swf_usage.html for information on how to create your own viewers.
  • −L, −−preloader filename filename is an arbitrary swf animation.
  • −q, −−quiet Suppress normal messages. Use -qq to suppress warnings, also.
  • −S, −−shapes Don’t use SWF Fonts, but store everything as shape.
  • −f, −−fonts Store full fonts in SWF. (Don’t reduce to used characters).
  • −G, −−flatten This usually makes the file faster to render and also usually smaller, but will increase
    conversion time.
  • −I, −−info Don’t do actual conversion, just display a list of all pages in the PDF.
  • −Q, −−maxtime n Abort conversion after n seconds. Only available on Unix.

Converting in the right way

The creators of SWFTools claim that you can export in AVM2, but in fact the Flash Player only recognize the header in the swf, not the document structure, which for Flex DOM is fundamental to understand.

Ok, How I convert mine with options?

I used ANT to do stuff things for conventions since I don’t want to re-type always I have a new PDF to convert in house. But simplifying it worked for me this command line argument.

C:\SWFTOOLS>pdf2swf -z -t sourceFile.pdf -o OutputFile.swf

That’s right, you have your swf file converted and running, What was done? I insert a stop action in each frame because this cause the swf to play each frame. Also I compacted the file using gzip to decrease the size, same as Scribd does.

Flex side

Create a new Flex project, name it as you wish. After that you might be think it’s simple we can load it using SWFLoader, but you will have problems to control and Flex will understand that this file is just as asset not as a component. To work with that, you have various options like, Loader, ByteLoader, ModuleLoader and BulkLoader. To load into Flex and given you a certain to control it.

My solution was simple, as I known that pdf2swf just give me swf in head with 8 version, I can manage a instance and place where I wish. That was easy, but what about Flex to control from Flash 5,6,7,8, both have their own way of render and work. To accomplished that I googled a little, because I knew it that some one should have fight this problem before and for lucky I found this class that kick ass problem. See class bellow:

  1. package  
  2. {   
  3.     import flash.display.Loader;   
  4.     import flash.net.URLRequest;   
  5.     import flash.net.URLStream;   
  6.     import flash.events.IOErrorEvent;   
  7.     import flash.events.SecurityErrorEvent;   
  8.     import flash.events.Event;   
  9.     import flash.utils.ByteArray;   
  10.     import flash.utils.Endian;   
  11.     import flash.errors.EOFError;    
  12.   
  13.     public class ForcibleLoader   
  14.     {   
  15.         public function ForcibleLoader(loader:Loader)   
  16.         {   
  17.             this.loader = loader;   
  18.             _stream = new URLStream();   
  19.             _stream.addEventListener(Event.COMPLETE, completeHandler);   
  20.             _stream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);   
  21.             _stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);   
  22.         }   
  23.         private var _loader:Loader;   
  24.         public var _stream:URLStream;   
  25.         public function get loader():Loader   
  26.         {   
  27.             return _loader;   
  28.         }   
  29.         public function set loader(value:Loader):void  
  30.         {   
  31.             _loader = value;   
  32.         }   
  33.         public function load(request:URLRequest):void  
  34.         {   
  35.             _stream.load(request);   
  36.         }   
  37.         private function completeHandler(event:Event):void  
  38.         {   
  39.             var inputBytes:ByteArray = new ByteArray();   
  40.             _stream.readBytes(inputBytes);   
  41.             _stream.close();   
  42.             inputBytes.endian = Endian.LITTLE_ENDIAN;   
  43.             if (isCompressed(inputBytes)) {   
  44.                 uncompress(inputBytes);   
  45.             }   
  46.             var version:uint = uint(inputBytes[3]);   
  47.             if (version <= 10) {   
  48.                 if (version == 8 || version == 9 || version == 10){   
  49.                     flagSWF9Bit(inputBytes);   
  50.                 }   
  51.                 else if (version <= 7) {   
  52.                     insertFileAttributesTag(inputBytes);   
  53.                 }   
  54.                 updateVersion(inputBytes, 9);   
  55.             }   
  56.             loader.loadBytes(inputBytes);   
  57.         }   
  58.         private function isCompressed(bytes:ByteArray):Boolean   
  59.         {   
  60.             return bytes[0] == 0x43;   
  61.         }   
  62.         private function uncompress(bytes:ByteArray):void  
  63.         {   
  64.             var cBytes:ByteArray = new ByteArray();   
  65.             cBytes.writeBytes(bytes, 8);   
  66.             bytes.length = 8;   
  67.             bytes.position = 8;   
  68.             cBytes.uncompress();   
  69.             bytes.writeBytes(cBytes);   
  70.             bytes[0] = 0x46;   
  71.             cBytes.length = 0;   
  72.         }   
  73.         private function getBodyPosition(bytes:ByteArray):uint   
  74.         {   
  75.             var result:uint = 0;   
  76.             result += 3// FWS/CWS   
  77.             result += 1// version(byte)   
  78.             result += 4// length(32bit-uint)   
  79.             var rectNBits:uint = bytes[result] >>> 3;   
  80.             result += (5 + rectNBits * 4) / 8// stage(rect)   
  81.             result += 2;   
  82.             result += 1// frameRate(byte)   
  83.             result += 2// totalFrames(16bit-uint)   
  84.             return result;   
  85.         }   
  86.         private function findFileAttributesPosition(offset:uint, bytes:ByteArray):uint   
  87.         {   
  88.             bytes.position = offset;   
  89.             try {   
  90.                 for (;;) {   
  91.                     var byte:uint = bytes.readShort();   
  92.                     var tag:uint = byte >>> 6;   
  93.                     if (tag == 69) {   
  94.                         return bytes.position - 2;   
  95.                     }   
  96.                     var length:uint = byte & 0x3f;   
  97.                     if (length == 0x3f) {   
  98.                         length = bytes.readInt();   
  99.                     }   
  100.                     bytes.position += length;   
  101.                 }   
  102.             }   
  103.             catch (e:EOFError) {   
  104.             }   
  105.             return NaN;   
  106.         }   
  107.         private function flagSWF9Bit(bytes:ByteArray):void  
  108.         {   
  109.             var pos:uint = findFileAttributesPosition(getBodyPosition(bytes), bytes);   
  110.             if (!isNaN(pos)) {   
  111.                 bytes[pos + 2] |= 0x08;   
  112.             }   
  113.         }   
  114.         private function insertFileAttributesTag(bytes:ByteArray):void  
  115.         {   
  116.             var pos:uint = getBodyPosition(bytes);   
  117.             var afterBytes:ByteArray = new ByteArray();   
  118.             afterBytes.writeBytes(bytes, pos);   
  119.             bytes.length = pos;   
  120.             bytes.position = pos;   
  121.             bytes.writeByte(0x44);   
  122.             bytes.writeByte(0x11);   
  123.             bytes.writeByte(0x08);   
  124.             bytes.writeByte(0x00);   
  125.             bytes.writeByte(0x00);   
  126.             bytes.writeByte(0x00);   
  127.             bytes.writeBytes(afterBytes);   
  128.             afterBytes.length = 0;   
  129.         }   
  130.         private function updateVersion(bytes:ByteArray, version:uint):void  
  131.         {   
  132.             bytes[3] = version;   
  133.         }   
  134.         private function ioErrorHandler(event:IOErrorEvent):void  
  135.         {   
  136.             loader.contentLoaderInfo.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));   
  137.         }   
  138.         private function securityErrorHandler(event:SecurityErrorEvent):void  
  139.         {   
  140.             loader.contentLoaderInfo.dispatchEvent(new SecurityErrorEvent(SecurityErrorEvent.SECURITY_ERROR));   
  141.         }   
  142.     }   
  143. }  

With this face off, you can handle quickly in Flex, I’ve built an application example. And for your own risk you can implement, share more functionalities, more controls, anyaway, you’re free for it.

Here’s the Main application.

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <MX:APPLICATION xmlns:views="org.igorcosta.views.*" layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()">  
  3.   
  4. <MX:METADATA>  
  5.         [Event(name="PDFComplete",type="flash.events.Event")]   
  6. </MX:METADATA>  
  7.   
  8.     <MX:SCRIPT>  
  9.         <![CDATA[ 
  10.                private var swfURL:String = "test/outra.swf"; 
  11.                public var libMC:MovieClip = new MovieClip(); 
  12.                private function init():void{ 
  13.                 var loader:Loader = new Loader(); 
  14.                 loader.contentLoaderInfo.addEventListener(Event.COMPLETE, swfComplete); 
  15.                 var fLoader:ForcibleLoader = new ForcibleLoader(loader); 
  16.                 fLoader.load(new URLRequest(swfURL)); 
  17.                 swfContainer.addChild(loader); 
  18.               } 
  19.               private function swfComplete(event:Event):void{ 
  20.                libMC = event.currentTarget.content as MovieClip; 
  21.                libMC.gotoAndStop(1); 
  22.               dispatchEvent(new Event("PDFComplete")); 
  23.               controls.target = libMC; 
  24.               controls.currentPage = libMC.currentFrame; 
  25.               controls.Pages = libMC.totalFrames; 
  26.               } 
  27.  
  28.         ]]>  
  29.     </mx:Script>  
  30.     <VIEWS:CONTROL id=controls />  
  31.     <MX:HDIVIDEDBOX height="100%" width="100%" top="40">  
  32.             <VIEWS:PAGES id=indexPage height="100%" width="200">  
  33.             </VIEWS:PAGES>  
  34.             <MX:CANVAS height="100%" width="100%" backgroundColor="#222222">  
  35.                 <MX:UICOMPONENT id=swfContainer height="1100" width="850" />  
  36.             </MX:CANVAS>  
  37.             </MX:HDIVIDEDBOX>  
  38. </MX:APPLICATION>  
You see, I didn’t used the SWFLoader of Flex, as explained before, I prefer to handle in diffrent way.
This application is simple, has some buggies (littles), but in general, just figure you out how to do such thing.

Missing things you could implement, TextSearch, Thumbnail generation from loaded swf to pages.

Download the FULL source code of this application here. Wanna see example of running, you have to download and it’s included. Because I didn’t put effort in secury sandbox levels in it.

Links of my researches:

Share on Twitter
posted on 2009-11-12 10:04  无痕客  阅读(1973)  评论(0编辑  收藏  举报