Learnosity Logo
Learnosity Banner Image

ExtendoText project on google code

We've been busy squirrelling away on many projects and they are beginning to come to light:

One which is coming to light very early is a new text framework for actionscript which leverages flash 10 text engine.

It's very early alpha but is a good proof of concept and already solves some problems for us - namely the ability to simply create rich text area's that have superscript and subscript text.

It uses XML and CSS and completely seperates the formatting from the content.

Screenshot of extendotext 0.1

It can now:

  • Render a simple XML document
  • Apply styles including color, font and opacity
  • Plug in custom code for rendering images, video or any other XML tag you want to create.

The screenshot highlights a number of these features:

  • Bold, italic, super and subscript text
  • Text wrapping around custom display classes (Red and Green boxes)
  • Opacity applied to display objects (green box)

There is still a lot more to be done - including editing of text which is the next hard piece to knock over.

You can get it from the google code site

Keen to hear any feedback.

Cheers, Mark

More of AIR Gzip compression

Further to Marko's blog entry on implementing Gzip compression in AIR applications - turns out we were impressively hoodwinked.

By adding the 'Accept-Encoding':'gzip' header we were telling our apache server to serve up the compressed version of the content and we could see the content was compressed coming through Charles Proxy, and our application was working perfectly.

However, I started working on the exact same code the next day and straight away got an error - the application was erroring because it was getting binary data where it expected the decompressed XML data.

I turned Charles back on to see what was going on and it started working again. With my best sherlock holmes head on I rapidly deduced that Charles as well as proxying the request was also decompressing it helpfully for me.

Luckily I'd already come across the solution to the problem courtesy of Anirudh Sasikumar (GzipHTTPService) and Paul Robertson (Gzip Encoder).

I made some minor modifications to Anirudh's Gzip HTTP Service to do two things.

  • Always send the header 'Accept-Encoding':'gzip' if running a Desktop (Aka AIR) app.
  • Handle the case where the Gzip response that comes back is not gzip compressed. I.e. Charles already decrypted it or the server doesn't support gzip compression.
  • Change it to use version 0.2.0 of the gzip encoder

With these changes it is a drop in replacement for the <mx:HTTPService/> tag.

The changes are as follows:

...snip....
public function GzipHTTPService(rootURL:String=null, destination:String=null)
{
super(rootURL, destination);
         //If using AIR then set accept GZIP          if(Capabilities.playerType == "Desktop")
         {
            this.headers = {'Accept-Encoding':'gzip'};
         }
}

...snip....
else if ( body is ByteArray )
{
var barr:ByteArray = body as ByteArray;

try{
   var encoder:GZIPBytesEncoder = new GZIPBytesEncoder();
   /* decode the gzip encoded result */
   message.body = encoder.uncompressToByteArray(barr).toString();
}catch(error:IllegalOperationError){
   //Not gzip compressed - assume utf8    barr.position = 0;//reset to start of bytearray    message.body = barr.readUTFBytes(barr.length);
}
/* pass it on to HTTPService */
return super.processResult(message, token);            
}

...snip...

For convenience you can download the modified files from here.

Hope it helps.

Cheers, Mark

GZIP encoding in Adobe AIR/Flex - HOWTO

If you use HTTPService component in Flex or AIR to retrieve data from a server, you might want to consider gzipping the response. This can significantly reduce the packet size you get from the server, which results in a much better application performance.

This means that response packet is compressed by Apache web server and your client will need to decompress it. Fortunately, both Flex and AIR 1.5.1 natively support GZIP compression. However there are a couple of things you need to watch out for.

Firstly, you need to enable Apache Module mod_deflate in your virtual host.

In your AIR application, you need to tell HTTPService component to accept gzip encoding and AIR will take care of the rest:

<mx:HTTPService id="service" resultFormat="e4x" />
this.service.headers = {'Accept-Encoding':'gzip'};

If you are in a Flex project, specifying gzip encoding will throw the following error:

ArgumentError: Error #2096: The HTTP request header Accept-Encoding cannot be set via ActionScript.

This is because Flex apps run in a browser and the browser is responsible for determining encoding type and decompression (if required). Therefore, you cannot specify encoding type in Flex apps.

Our problem is that we use one core library for AIR and Flex applications. So the issue above can be avoided by conditionally specifying encoding type depending on the framework used. This can be done in 2 lines of code:

if(Capabilities.playerType == "Desktop")
this.service.headers = {'Accept-Encoding':'gzip'};

Note: this is not the complete solution - check out this entry for the rest of it :-)

Cheers Marko

AS3 BulkLoader example

I've been using AS3 BulkLoader for a while, which is great for loading multiple assets such as images and swfs into your application. Typical usage would look something like this:

instantiate BulkLoader object:

private var assetLoader:BulkLoader = new BulkLoader("instanceName");

add assets into the queue:

this.assetLoader.add("assets/myImage.jpg", {id:"key1"});
this.assetLoader.add("assets/myMovie.swf", {id:"key2"});
where id is the key, which is used to identify which asset to retrieve from the queue once it's been loaded.

To start loading assets you simply call the start() method:

this.assetLoader.start();

To get asset content, you would normally call getContent() method, which would return Bitmap for images or AVM1Movie for swf files. You could also call methods like getBitmap() and getAVM1Movie() to target specific file types.

The problem I encountered was that assets I was loading were encrypted. Therefore, I needed to read them in as ByteArray type and then decrypt them. Fortunately the latest version of BulkLoader has getBinary() method, but there is a trick to it. You need to tell BulkLoader to load assets in binary format when adding items into the queue:

this.assetLoader.add("assets/myImage.jpg", {id:"key1", type:BulkLoader.TYPE_BINARY});
this.assetLoader.add("assets/myMovie.swf", {id:"key2", type:BulkLoader.TYPE_BINARY});
And after all assets have been loaded, you get binary content:
var assetBinary1:ByteArray = this.assetLoader.getBinary("key1");
var assetBinary2:ByteArray = this.assetLoader.getBinary("key2");
Without specifying the type, BulkLoader will try to load data in text format which will result in the following error:
[BulkLoader] Failed to get content with url: 'file:///myFile.jpg'as type: [class ByteArray]

Setting the type to binary did the trick for me, so I thought I'd share it in case someone else falls into the same trap.

Oh, and I should thank Mark Lynch for helping me figure this out :-)

Marko

Can't Launch Air Apps after upgrading Flex Builder to 3.0.2

I recently upgraded flex builder to 3.0.2 to play with the new Flash 10 goodness and came across this little problem.

Whenever I clicked run it would just sit there for ages and never launch the app. If I clicked debug I got the following error:

Process terminated without establishing connection to debugger.

Command:

"C:\Program Files\Adobe\Flex Builder 3\sdks\3.2.0\bin\adl.exe" "C:\Documents and Settings\markl\My Documents\Flex Builder 3\SampleApp\bin-debug\SampleApp-app.xml" "C:\Documents and Settings\markl\My Documents\Flex Builder 3\DexterAir\bin-debug"

Output from command:

error while loading initial content

Turns out the solution is easy when you know how, thanks to this post.

The solution is a simple as changing the namespace in the app.xml file from:

to

Which is fine if you want to upgrade you app to 1.5 - not sure if there is still a way to compile apps for AIR 1.0 though.

Cheers, Mark Lynch

Flash Text Layout Framework - Getting Started

View Demo
Download Code

I finally got some time to play with Flash 10 and the new text functionality including the Text Layout Framework Beta

It gives very powerful control of the text layout but with this power come a few gotcha's which can trip you up.

The basic process for using the TextFlow for advanced text rendering is as follows:

  • Create a sprite container to hold the text
  • Import the text into a TextFlow object
  • Attach the container to the textflow object
  • Call updateAllContainers() on the textflow object
  • We also need to add event handlers for resize and graphics loading if we are using these features.

Create a sprite

_container = new Sprite();
this.textArea.rawChildren.addChild(_container);
this.textArea.addEventListener(ResizeEvent.RESIZE,resizeEvent);
Create a new sprite which get's attached to the canvas we've already created.
<mx:Canvas id="textArea" top="10" left="10" right="10" bottom="10"/>

Import the text into a TextFlow object

XML.ignoreWhitespace = false;         

var markup:XML = <TextFlow fontSize="30" fontFamily="Arial" whiteSpaceCollapse="preserve">
   <p fontSize="50" textAlign="center">Text Layout Framework Demo</p>
   <p>Text is now easy to control in Flash</p>
   <p><span>It can be formatted easily to do</span><span fontWeight="bold" whiteSpaceCollapse="preserve"> Bold</span><span>, </span><span fontStyle="italic">Italic</span><span>, </span><span textDecoration="underline">Underline</span><span> and </span><span lineThrough="true"> Strikethrough</span></p>
   <p>It can also finally do <span fontSize="20" alignmentBaseline="ideographicTop">Superscript</span> and <span fontSize="20" alignmentBaseline="ideographicBottom">Subscript</span></p>
   <p><a href='http://www.learnosity.com/techblog/'>Links</a> and inline images <img align="right" source="http://www.adobe.com/shockwave/download/images/flashplayer_100x100.jpg" /> are also available</p>
</TextFlow>;

_textFlow = TextFilter.importToFlow(markup,TextFilter.TEXT_LAYOUT_FORMAT);

This loads the XML and stores it in the _textFlow object.

Note: the XML.ignoreWhitespace is important otherwise it all the whitespace between tags will get stripped out and any formatted text will lose it's spacing.

Attach the container to the textflow object

_textFlow.flowComposer.addController(new DisplayObjectContainerController(_container,textArea.width,textArea.height));

Call updateAllContainers() on the textflow object

_textFlow.flowComposer.updateAllContainers();

That's it.

For bonus points you can add the event handlers to handle graphics loading and resize etc.

this.textArea.addEventListener(ResizeEvent.RESIZE,resizeEvent);
_textFlow.addEventListener(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGED,graphicStatusChangeEvent);
And the functions they call look like this:
public function graphicStatusChangeEvent(evt:Event):void
      {
         // the graphic has loaded (or failed to load) update the display          _textFlow.flowComposer.updateAllContainers();
      }   
      public function resizeEvent(evt:Event):void
      {
         //Resize event - recreate the flowcompser with the correct width and height          _textFlow.flowComposer.removeAllControllers();   
         _textFlow.flowComposer.addController(new DisplayObjectContainerController(_container,textArea.width,textArea.height));
         _textFlow.flowComposer.updateAllContainers();
      }

Make it selectable

To make the text selectable we just need to add an interaction manager. As easy as this:
//To make selectable
_textFlow.interactionManager = new SelectionManager();

I'm looking forward to implementing the rest of this in our projects that use flash text. It's so much simpler and cleaner.

Cheers, Mark

Extracting from sound from Flash (aka NellyMoser)

A current project that we are working on requires us to be able to record sound via a flash plugin. Initially I thought it would be nice and easy as I've seen demos of both video and audio broadcasting - however, the one big problem is that the current flash client only allows you to record video to a netstream, you can't get any access to it in the flash player.

So you need something like Flash Media Server or Red5 to record it.

However, once you record it to the server it is an flv and the audio codec is stored as a NellyMoser encoded audio portion. This codec is not supported by many applications and after a quick google found the nelly2pcm project on google code.

This will convert a flv sound file to a raw pcm file - which you can then do useful stuff with. So here's how to do it on a Ubuntu machine.

$ tar -xjvf nelly2pcm.tar.bz2
$ cd nelly2pcm
$ make
cc -Wall -c -o nelly2pcm.o nelly2pcm.c
cc -Wall -c -o nelly.o nelly.c
cc -Wall -c -o nelly_tables.o nelly_tables.c
cc -Wall nelly2pcm.o nelly.o nelly_tables.o -lm -o nelly2pcm
You should now have a nelly2pcm executable file in this directory.

You can run it as follows:

$ ./nelly2pcm test.flv > test.raw
mono Nellymoser stream with 16-bit samples at 44kHz

This will create a raw sound file with no headers and output the line above which you'll need for the next part.

To play this file back you can use sox (apt-get install sox)

$ play -r 44100 -c 1 -2 -s test.raw

If you've got all the options correct then this will play the file back. Once you got it correct you can then use sox to create a wav file which is essentially the same except that it has a header which contains all the settings (eg bitrate etc)

$ sox -r 44100 -c 1 -2 -s test.raw test.wav

From a wav you can convert to whatever you like. I'm looking forward to the Flash Player 10 release as this messing will no longer be necessary as it will support encoding with the free Speex codec.

Cheers, Mark

Update:

In the 12 hours since I wrote this post it appears that a DMCA takedown notice has been served on the nelly2pcm site and google have taken it down. I've no idea if it's related to this post or not and no details are available yet on the chillingeffects site but hopefully it will be updated shortly. As I mentioned earlier, I can't wait for flash 10 with the speex codec so we don't have to use Nellymoser, well done Adobe for including it.

"Logical AND" with flex binding in mxml

While making a small change to a view in one of our flex app's I came across the following little problem.

Initially there was a single parameter binding which set the button enabled, eg:

<mx:Button enabled="{model.user.allowSomething}" label="Do Something">

The business requirements changed and we need to have a global setting as well as a user setting, so now the setting had to be globally enabled and the user had to have access to it.

My 1st attempt was this:

<mx:Button enabled="{model.settings.allowSomething && model.user.allowSomething}" label="Do Something">

However, this gave the following error:

The entity name must immediately follow the '&' in the entity reference.

This you may recognise as an XML formatting error. After a quick google I came across a bug in the adobe bug tracker related to this (BTW I love that this is now open and searchable) which explained the workaround. XML escape the characters - which feels ugly when writing code, but it does make sense.

So the code now becomes:

<mx:Button enabled="{model.settings.allowSomething &amp;&amp; model.user.allowSomething}" label="Do Something">

I also came across another way of handling it using the ternary operator which is very clever but I think it is even less readable than the escaped XML characters.

Cheers, Mark

Flex/AIR embedded fonts - Exception during Transcoding - Solved

I've come across this error a number of times:

exception during transcoding: Unexpected exception encountered while reading font file '/C:/Documents and Settings/markl/My Documents/Flex Builder 3/DexterNorthCode/src/assets/DejaVuSans-Bold.ttf'

It can be very frustrating as there is not much information to be gleaned from the error messages and the whole font area is very murky and complicated.

There are a number for reasons that this error will occur

  • If you reference the wrong file
  • If you specify the fontStyle or fontWeight wrong
  • or most frustratingly if the font manager can't interpret the font correctly.

After grappling with this problem for I while and after much reading I realised that there are 3 different "font managers" that transcode fonts in AS3. These are:

  • flash.fonts.JREFontManager
  • flash.fonts.AFEFontManager
  • flash.fonts.BatikFontManager

The different font managers have different capabilities and should be automatically selected to choose the best one. However, this is not always the case.

You can force the compiler to use a specific manager by appending the following to the compiler arguments.

Project->Properties->Flex Compiler->Additional compiler arguments:

-managers=flash.fonts.AFEFontManager

This forces the use of the AFEFontManager which seems to have the best support for fonts and is the one being most actively developed.

Rich formatting for PDF's directly from Actionscript

I've blogged previously about the AlivePDF libraries which allow you to create PDF's directly from Actionscript.

I've been using them for a project and all was going well until we hit a limitation in the libraries. We can do rich formatting and we can do automatic line wrapping and pagination, but we can't do the both together.

Screenshot of generated pdf

This would have been an enormous problem if this was a piece of commercial software as we would be completely at the mercy of the creators. However, as it is Open Source we have access to the code and we can extend it as we see fit.

And that's exactly what we did. I've added a new version of the writeText() method called very imaginatively writeText2().

It takes the same parameters but instead of taking a standard string it will render a string that has html markup. I.e. you can put a <b> tag in and your text will be bold. Eg:

// This will write the following text with appropriate styling and word wrapping
pdf.writeText2 ( 15, 'A long <u>piece</u> of <b>text</b> in <center>1111 center 1111 2222 center 2222 3333 center 3333 <b>BOLD TEXT</b> 4444 center 4444</center>standard format that should wrap by the time I finish this. AAAAAAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCAAA <i>Italic text</i>. <b>Some bold text</b>. And now for some <b><i>Bold and Italic Text</i></b>. And back to normal. Then a line break<br/> That\'s All.'   );

For the curious you can download a sample document generated by it.

It currently supports the following tags:

  • <b> Make it bold
  • <u> Underline it
  • <i> Make it italic
  • <center> Center it
  • <br /> Add a line break

The code still needs a some cleanup and more testing but it seems to be reliable now.

The two files that were changed are available for download. Or alternatively you can download the full version with changes included. Give it a try and let me know what you think.

Cheers, Mark Lynch

More Entries