Monday, June 22, 2009

Default stylesheet in an SWC (Flex Library Project), and embedded font rotation

For ages now I've been trying to figure out how I can use a StyleSheet from inside my Flex Library Project in ActionScript. I kept reading that it is very resource intensive to be calling UIComponent.setStyle(...) at runtime, so I wanted to set all my styles using a StyleSheet. The LiveDocs on the subject seem to say that there are two ways to load a StyleSheet:
  1. From inside MXML in your Application using the <Style source="assets/styles.css"/> tag
  2. By loading an external stylesheet SWF file (compiled from a CSS file) using the
    StyleManager. loadStyleDeclarations(url)

Then I finally found the solution in this article (right near the bottom of the page in the comment by Henk). And that pointed me to this LiveDoc - Applying styles to your custom component which solved the problem. Scroll most of the way down until you get to the part called "Applying styles from a defaults.css file".

If you don't want to read the articles above, here is my brief summary.

Using a StyleSheet in your FlexLibrary Project:
  1. create a StyleSheet in the src directory of your project, and call it defaults.css
  2. open the project Properties > Flex Library Build Path and check the src/defaults.css file under the Assets tab
  3. define your styles inside the defaults.css StyleSheet
Restrictions:
  • You can include only a single style sheet in the SWC file
  • The file must be named defaults.css
  • The file must be in the top-most directory of the SWC file
*Note: it appears that having a defaults.css file in your Flex Application project (swf) also works without having to specify the stylesheet in the mxml (e.g. <mx:Style source="defaults.css"/> is not needed).


Using Embedded Fonts inside Flex Library Project/SWC:
If you want to use an embedded font inside your library project you have two ways of doing this:
  1. Embed the font inside an ActionScript file like this (the font is inside the project src/assets directory):
    [Embed(source='/assets/verdana.ttf'fontName='localVerdana'
           mimeType='application/x-font')]
    public var verdanaFont:Class;
    ...
    setStyle("fontFamily""localVerdana");
  2. Or put your embedded ttf font in your project's src directory and embed the fond using the defaults.css file (the font must be in the src directory for this to work - see this Adobe Bug for more details):
    defaults.css:
    @font-face {
      font-family: localVerdana;
      font-weight: normal;
      src: url("verdana.ttf");
    }
    .label {
      /* must use embedded font for label rotation to work */
      font-family: localVerdana;
      /* must specifically set the font weight */
      font-weight: normal;
    }
Note that if you want to set the DisplayObject.rotation property then you have to use an embedded font otherwise it doesn't work.

Here is a simple example of font/label rotation (right click "View Source" for the code).
In this example I actually included three fonts - Verdana plain, Verdana bold, and Comic Sans.

* Note that fonts can greatly increase the size of your SWF. The three fonts that I used above are all about 150 KB each. One way to reduce the size of your SWF is to restrict the unicode character range (meaning that only part of the font set is included in your SWF). Here is the LiveDoc on Using Fonts and Setting Character Ranges.

Ben Stucki's blog helped me figure out how to embed a bold and plain font in CSS.

7 comments:

Mark said...

Hey man,

Thanks for the super useful post about putting a default stylesheet in a swc. However...

it doesn't work. Well, let me clarify: your steps do work, but if you are using the default components (like customizing the look of a TabNavigator for example) and you don't use it in a particular swf, (because the swc library is shared across multiple swfs) it gives you a compiler warning and still embeds the graphics into your swf! Totally stupid, and defeats the entire point of placing your files in a swc.

Chris Callendar said...

I think I've noticed your problem too. I guess it's no surprise why the the compiler takes so long building SWFs when it is trying to figure out which assets and classes to include!

Anonymous said...

Yo thanks for ur post , although it is like Mark said :). guess the best way is to make a font.css and (set compile to swf) it to swf and load it at run time with the stylemanager class, i used to have a as2 class that could read a css file and embeded the files needed at run time , this is more what i would like, also the option if a font is installed, say you want three huge fonts with all the glyphs visitors who have these fonts shouldnt have to load the swf.... lots of things to think about, is there a way to have air or something compile these files withouth having flex builder installed ??.. thanks again...
Greetz

Chris Callendar said...

I don't know a way to have Air compile the files, I only use Flex Builder. But I agree that it would be nice if you didn't have to load the SWF if the font was already installed on the client's os. I doubt that is possible though.

Unknown said...

I had done this exact thing in a SWC build in Flex 3.4 SDK. I can NOT get it to work with Flex 4! I end up having to place the fonts in the application itself which uses the SWC just to get it to build. Have you been able to do this with Flex 4 yet? I did not get a response on the Adobe forum. Thanks!

Chris Callendar said...

Hi there,
I haven't tried this out in Flex 4 yet. If I find anything out I'll be sure to post it here.
Thanks for the comment.
Chris

Chris Callendar said...

I still haven't gotten it to work in Flex 4 using the defaults.css file.

I have succeeded in getting it to work using ActionScript Embed tag like this:

[Embed(source='/verdana.ttf', fontName='localVerdana', mimeType='application/x-font-truetype', embedAsCFF='false', unicodeRange='U+0041-U+005A, U+0061-U+007A, U+0030-U+0039, U+0020-U+002F, U+003A-U+0040, U+005B-U+0060, U+007B-U+007E')]
public static var verdanaFont:Class;

[Embed(source='/verdanab.ttf', fontName='localVerdana', fontWeight='bold', mimeType='application/x-font-truetype', embedAsCFF='false', unicodeRange='U+0041-U+005A, U+0061-U+007A, U+0030-U+0039, U+0020-U+002F, U+003A-U+0040, U+005B-U+0060, U+007B-U+007E')]
public static var verdanaBoldFont:Class;

And then in the class you would set the style:
setStyle("fontFamily", "localVerdana");

The unicodeRange values are optional, they simply restrict to english letters, numbers, and punctuation to keep the SWF file size down.