There’s this new feature in AIR 3 – native extensions, and there’s that ever-existing problem, how to render PDF documents in flash. In web player that’s still not possible (well, unless you sacrifice one year for writing PDF renderer) but it’s possible in AIR, for example using native extensions.
MacOS-X has the Cocoa library(framework) which performs great in rendering PDF files so I’ve written an extension which uses this lib to send BitmapData object containing rendered PDF to flash. Of course this is possible in windows and linux too, using XPDF library for example. AIR native extensions are easily extendable and altering several things in files that I’ve provided should make that extension work on windows, linux and even mobile systems.
RenderPDF extension contents:
There are many files in the archive, but all of them were used to make an extension.
PDFExt_framework contains compiled OS-X framework.
PDFExt_XCode contains an objective-c (objective-c is needed for Cocoa library) framework project.
PDFExtTest is a test app using an extension. In sample_commands folder you have bash commands to compile and package the test app. Test app displays a pdf, allows page changing and saving current page as PNG image. Test app illustrates usage of RenderPDF extension methods:
get pageCount():int,
RenderPDF.renderPage( pageNum, dpi ):BitmapData ,
loadPDF( source:String ):Boolean
PDFExtTest app screenshot:
PDFExtAS is the source code for AS3 interface for an extension.
compiled – this folder contains compiled swc, extension, extension xml descriptor and sample commands for compiling and packagind everything into an .ane file.
Several remarks:
Before compiling the framework, you must add the Cocoa framework and Air framework to the project, and then exclude them from linking, as described on “Extending AIR” article. In Info.plist file you must add the DYLD_FORCE_FLAT_NAMESPACE env variable and in project settings you modify the “Runpath search path”.
If you’ll take a look into PDFExtM.m source file, you will see that returning a BitmapData object from native function involves quite a few steps.
Also, flash BitmapData sometimes contains some extra bytes at every scanline, so memcpy from native uint array to copy the bitmap won’t work, you must iterate every pixel and skip the extra bytes at the end of a scanline (maybe there’s some super clever low-level-memory-hacking method to do it faster).
RenderPDF+sources
Tags: air, flash, native extensions, pdf
Posted in flash experiments | Comments (9)
I wanted to make some kind of loader, which loads swf file, and accompanying asset files ( like for example dynamically loaded images or xml configuration files ) from one zip archive. Then we could have only 2 files – loader file and an archive with an application to load, instead of bunch of folders and files.
I wanted to make it cross-platform, so I’ve tried to do it in Adobe AIR ( standard flash player won’t allow us to create any files or folders ).
It would also serve as a Flex to AIR converter, becasue you add your Flex application’s files to data.zip archive, launch our AIR loader, and you have your Flex application running in AIR, without need to change xml files and so on.
We have AIR’s File class, which allows us to create folder and files.
So the basic idea is:
- unpack data.zip into some temporary directory ( File.applicationStorageDirectory is recommended by Adobe lang reference… )
- run some .swf file found in unpacked files.
Yes… No. That’s not possible. You cannot run files from appStorageDir. Stupid sandbox violation errors will appear. SandBox in standalone applications… gr8 idea.
You will think of a hack: Let’s load this swf into ByteArray, and then execute this ByteArray using Loader class!!!
for example:
var ldr:Loader = new Loader();
var fstream:FileStream = new FileStream();
fstream.open( YOUR_SWF_TO_LOAD , FileMode.READ );
ldr.load( fstream.readBytes() );
… Ok, it will run, but it won’t see any other files you’ve unpacked. Suppose we have config.xml in applicationStorageDirectory. It won’t see this config.xml, it will only see files in applicationDirectory – directory where our loader resides.
Ok: so let’s abandon this useless applicationStorageDirectory, and unpack everything to applicationDirectory. You think they will let you? Ha! You ingenuous little developer. They of course knows better what’s good for you. You can’t write to applicationDirectory. They say “it’s a bad practice”, and they also say “better use applicationStorageDirectory” They also say “make sandbox bridges to run active content from applicationStorageDirectory”. Sandbox bridges? This just some trivial feature in HTMLLoader class. SWF which runs in HTMLLoader is rendered by built-in flash AIR browser, and it runs 10x slower than it could in pure standalone flash player.
Ok, so we are still stuck with the problem – how to run this swf in AIR – and another problem – we can’t write to applicationDirectory. But wait… we can write to any other directory… So we can write to applicationDirectory too.
This is so stupid … Look, this causes security exception:
var new_directory:File = File.applicationDirectory.resolvePath( "some_new_directory" ); // create path to "some_new_directory" in our loader's directory
new_directory.createDirectory(); // <---- ERROR
And this doesn’t:
var new_directory:File = new File( File.applicationDirectory.resolvePath( "some_new_directory" ).nativePath );
new_directory.createDirectory();
We basically create new File object, with path equal to File.applicationDirectory/some_new_directory, but flash doesn’t know it’s our apps dir, so it doesn’t throw an exception. The rest is obvious – we execute .swf using File.applicationDirectory, because all files we need are actually in our application’s directory. And this is the simplest possible detour for these unnecessary sandbox “feature”.
And finally I’ve made this AIR swf loader, but… It appears, that files you load into AIR application has fewer fps, than they could have, when run stand-alone.
These are the mxml sources, along with some additional dependencies, in case you’d ever want to load some swf’s dynamically from AIR.
It depends on “newzip”, nochump’s zip loading library, which I’ve modified slightly, to support asynchrounous loading ( then if you’ll have 30MB zip, your flash won’t hang due to timeout exception ). Just build .air package along with your data.zip archive ( the .swf files to launch ) and you’re ready to go. Sample data.zip included.
sources
Tags: air, applicationStorageDirectory, as3, sandbox violation
Posted in flash experiments | Comments (0)