/customers/iconara.net/iconara.net/httpd.www/blog/wp-content/plugins/wp-super-cache/wp-cache-phase1.php Iconara » Creating specialized compilers with the Flex Compiler API

Creating specialized compilers with the Flex Compiler API

Included with Flex 3 is the “Adobe Flex 3 Compiler API”, a little-talked-about piece of code that lets developers create custom Flex compilers, embed the Flex compiler into other applications or just about anything imaginable. In this post I’m going to show how to use the compiler API to create a specialized Flex compiler that can be used to compile font packages for runtime loading of fonts.

Background

The application I’m working on at the moment needs to be able to load fonts at runtime. To do this it loads a SWF-file which has fonts embedded in it, extracts those fonts and registers them using Font.registerFont. This solution works fine, but the font SWF has to be created by hand by writing a class containing the font embeds and it then has to be compiled. Both these tasks require that you know how to develop Flex applications and the problem is that the administrators of the application may not be Flex programmers, and definitely don’t want to be bothered with the details of how a font embed should be written or which arguments that needs to be used with the mxmlc compiler. The font packaging needs to be automated somehow.

One solution to this is to create some elaborate shell scripts which generate the necessary ActionScript code and passes it on to mxmlc using the right arguments. It’s a completely usable solution in this case, as I can safely assume that the administrators will at least be proficient in the art of the terminal. It would probably be the solution I would use if I hadn’t discovered the Flex Compiler API. Instead of writing shell scripts that just automate the steps that you otherwise would do by hand I can create an application that can use the compiler directly using an interface much more appropriate to the problem. Generating command line argument strings is a pain, and as you will see, working with the compiler in Java code is much easier.

What I ended up with is a command-line application that can be used like this

fontc -o MyriadWebPro.swf \
      -font MyriadWebPro.ttf "Myriad Web Pro" \
      -font MyriadWebPro-Bold.ttf "Myriad Web Pro" bold \
      -font MyriadWebPro-Italic.ttf "Myriad Web Pro" italic

“fontc” is actually a shell script that sets up the class path, which makes invocation a little less verbose, more or less the equivalent of the mxmlc shell script.

In the arguments to the application -o should be followed by the name of the output SWF and -font should be followed by the path to a font file (TrueType of OpenType) then the font name (quoted if it contains spaces) and optionally “bold”, “italic” or both.

The goal was that the above invocation should generate the following ActionScript code behind the scenes, pass it on to the compiler and output it in a file called “MyriadWebPro.swf”:

package {

import flash.display.Sprite;

public class FontLibrary extends Sprite {

[Embed(source="MyriadWebPro.ttf",
     fontName="Myriad Web Pro")]
public var MyriadWebPro : Class;

[Embed(source="MyriadWebPro-Bold.ttf",
     fontName="Myriad Web Pro",
   fontWeight="bold")]
public var MyriadWebPro_bold : Class;

[Embed(source="MyriadWebPro-Italic.ttf",
     fontName="Myriad Web Pro",
    fontStyle="italic")]
public var MyriadWebPro_italic : Class;

}

}

As you can see, each -font ... becomes an instance variable with an embed statement with the corresponding source path, font name, fontWeight and fontStyle. The names of the instance variables are generated from the font name, weight and style, but they are not actually important, they just have to be unique.

When compiled, the class above becomes the document class for the resulting SWF and an instance of it can be retrieved using the content property of the Loader instance that is used to load it. For reference, the following code shows how I do that:

function loadFonts( ) : void {
  var loader : Loader = new Loader();

loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onFontsLoaded);

loader.load(new URLRequest("MyriadWebPro.swf")); }

function onFontsLoaded( event : Event ) : void { var fontLib : Object = Loader(event.currentTarget).content;

var declaredFonts : XMLList = describeType(fontLib).variable.(@type == "Class").@name;

for each ( var propertyName : String in declaredFonts ) { Font.registerFont(fontLib[propertyName]); } }

The important part is how the fonts are discovered: the instance of the loaded font library class is searched for all it’s instance variables of the type Class using describeType and some E4X. The way I created the font library class all instance variables types as Class are embedded fonts, so if we find any, these can then safely be passed to Font.registerFont which will make them usable throughout our application.

How it is done

The guts of the compiler is nothing more than the following code, which is more or less taken directly from the examples in the Flex Compiler API documentation.

The compiler can work with real files (represented by java.io.File objects) or with virtual files that only exist in memory. In this example I want to generate the code on the fly so a virtual file is exactly what I want. I generate the ActionScript code (not shown here, but it should be easy to see how it could be done, and if you really need to see how it’s done you can mail me and ask for the full code) and put it in a VirtualLocalFile, then create an Application with that virtual file as source. The Application class represents a SWF application, and there is a corresponding class called Library that can be used to create SWC files.

The code new Application(file).build(false) is more or less equivalent to mxmlc File.as (the parameter to build is the same as the mxmlc parameter -incremental).

File output = ...the output file...;

String source = ...create the source ActionScript...;

File parent = new File(".").getCanonicalFile();

VirtualLocalFileSystem fs = new VirtualLocalFileSystem();

VirtualLocalFile lf = fs.create( new File(parent, "FontLibrary.as").getCanonicalPath(), source, parent, System.currentTimeMillis() );

Application app = new Application(lf);

Configuration config = app.getDefaultConfiguration();

config.optimize(true);

app.setConfiguration(config); app.setOutput(output);

app.build(true);

A complete application would need some command line argument parsing and other tediousness, but I leave that up to you (again, if you really need an example mail me and ask for the code).

I decided on making it a command line application, because that way it will integrate well with other tools used by the administrators of the Flex application I’m working on. However, you could use exactly the same code to create a GUI application which could support drag-and-drop of font files and even auto-discovery of the names, weights and styles of the fonts, or you could make it into a Ant Task in order to use it in your Ant build scripts.

Using the Flex Compiler API you can create all kinds of specialized compilers that either work on existing ActionScript code or generates it on the fly. You can make all sorts of pre-processors and code translators, or integrate the compiler into other applications. At this point you can probably imagine how mxmlc works with MXML files, or how it deals with metadata like [Bindable] and [Embed].

There is one caveat, however: to use your custom compiler the end user needs to have the Flex SDK installed. The reason for this is that the Flex Compiler API depends on almost all of the other JAR files in the SDK’s lib directory (actually, it depends on mxmlc.jar, which depends on almost all the rest). Even if the SDK is free, it’s unlikely that you are allowed to distribute applications that contain it (but now that it is open source that may be possible, it’s worth digging into). On the other hand, most applications that use the Flex Compiler API will probably be targeted at Flex developers anyway.

Using custom metadata
– just for the sake of it

The solution above works fine as it is, but the way it discovers embedded fonts is a little crude. Looking for instance variables that are typed as Class doesn’t actually tell us that they are fonts, they could be other embedded assets. In this case that’s not really an issue since I’m the one creating the code and I’m only interested in loading fonts. I can, however, imagine cases where I would like to embed other types of assets in the same package, and then I would need a way to tell them apart. My solution to this would be to use custom metadata tags.

In my oppinion, custom metadata tags is a powerful feature looking for a problem to solve. Most problems can be solved without them, but sometimes they make for a nice and clean solution. This is one such case (although I can think of a few other ways to do it as well, such as prefixing the names of the variables with “font”).

As it is, the code that loads the font package assumes that all instance variables of the loaded object that are of the type Class are fonts. By adding a new metadata tag to the font variables, let’s say [Font]:

[Embed(source=...)]
[Font]
public var MyGreatFont : Class;

we can then change the E4X statement that retrieves the names of the font variables in the font loading code to

describeType(fontLib).variable.(metadata.(@name=="Font")).@name

and in the compiler code we have to add (just before or after config.optimize(true))

config.addActionScriptMetadata(new String[] {"Font"});

to make sure that the metadata is not thrown away.

The new E4X retrieves the names of all variables having a “metadata” node which has the name “Font”. If you embed nothing but fonts this will make no difference to how it worked before, but it will allow you to embed other things besides fonts in the same SWF.

This is just one example of how custom metadata can be used to enabled advanced introspection of an application at runtime. You can probably think of other examples where it can be used to make code easier to read and introspection that relies less on how methods and variables are named. One obvious example would be a unit testing framework that, instead of assuming all method with names beginning with “test” were tests, looked for methods with the metadata [Test].

15 Responses to “Creating specialized compilers with the Flex Compiler API”

  1. ethan Says:

    Do you think that this could be used for user defined css? I’m thinking of an app allowing the user to select colors and other stuff to style a flex runtime swf. I was stumped on how to take the css code and convert it to an swf that could be targeted by the flex runtime swf without having me converting each one for my users.

  2. Jun Heider Says:

    Theo,

    This is an intensely rocking article! Other than the very good information on custom compiler api, you put in one of the best explainations of custom metadata and it’s benefits that I’ve seen.

    Thanks!

  3. Bjorn Schultheiss Says:

    Champion article..

    One of the best i’ve read in a while.

    I wonder if you could bundle the compiler in an air application. Coupled with File system access you could probably have an app where you select a few ttf files and can generate the final swf?

  4. Theo Says:

    I’ve thought about doing something like that. I was going to go for a Java app because I’d like to automate the naming of the fonts and so on and I think there are libraries for doing that in Java (after all the Flex compiler does it). Then there is the issue that the compiler has to be a Java app and AIR applications can’t call external applications so that seems to make Java the only choice. I’d rather do it in AIR, of course.

  5. TK Says:

    This is absolutely brilliant. I’ve been wanting to crack open the compiler and change some stuff, and this is exactly what I need to get started. Thanks so much!

    • TK
  6. TK Says:

    Theo, how would I create my own implementation of Bindable in the flex compiler API? I’m kind of confused by the whole metadata thing.

  7. Theo Says:

    @TK

    Don’t expect a full code example =)

    If I remember correctly the compiler API doesn’t have callbacks for the code parser (or for any part of the compilation for that matter, it’s just a front to the compiler), so you can’t just plug in and replace metadata tags on the fly while the compiler is parsing the code. Instead you will have to implement your own preprocessor that just scans the code for your metadata tags, and replace these with your generated code.

    The nice thing about the compiler API is that you don’t have to save the preprocessed code, you can use it from memory via “virtual files” (as I seem to remember the name was, you have to look it up in the documentation).

  8. TK Says:

    Theo, thanks for getting back to me. So it’s pretty safe to say that the compiler API only supports creating simple “marker” metadata tags, like [Font] or [Test] as you mentioned in your post. Dang, I was actually thinking I could finally create my own system of data binding and build my own AS3 annotation implementation … :(

    Is there any way to crack into the actual compiler? I’ve really got the itch to do some crazy stuff with metadata and AS3 :)

  9. Theo Says:

    Not unless you really know what you’re doing. But the whole thing is open source now so there’s nothing stopping you in that respect.

  10. TK Says:

    Awesome. Well that’s great to know. Thanks for the great post, Theo!

  11. Rajiv Says:

    Any thoughts on memory requirement by this API.

    We want to use this in an application where mxml is generated at runtime and compiled thru this API to SWF.

    Will it be able to support medium to large number of concurrent requests ?

    Thanks Rajiv

  12. Theo Says:

    Sorry, I have no idea. It won’t be faster or less memory-intensive than running mxmlc on the command line. If your app is written in Java it will at least be faster than exec’ing to call mxmlc.

  13. Lance Says:

    Hey,

    I’m not sure if you are talking about as3 code generation here, but do you know how to customize the compiler-generated as3 code at all? I would like to customize the [Bindable] tag parsing, and some stuff with the new State syntax. Any ideas how to get in there and make changes without modifying the core compiler code?

    Thanks Theo, Lance

  14. Theo Says:

    There is currently no way to hook into the Flex compiler’s own code generation phase without rewriting the compiler itself. There compiler probably doesn’t even generate code as such, more likely it modifies the syntax tree after the code has been parsed and there is no way to make it run arbitrary code at that point. The compiler API is strictly a way to run the parser programmatically.

    I know this is a feature that the compiler team has received requests for, and I haven’t looked at the compiler API in Flex 4, so perhaps it’s something we will get in the future.

  15. Samuel Asher Rivello Says:

    Hi,

    Great information. I created a simple, powerful demo of how to use Custom Compiler Arguments in Flex (via Flash Builder or other IDE).

    See that and other demos here;

    http://www.blog.rivellomultimediaconsulting.com/posts/flash-builder-custom-compiler-arguments

    -Samuel Asher Rivello RivelloMultimediaConsulting.com

Leave a Reply