Archive for April, 2010

Flex 4 and the Text Layout Framework

Posted in Flash, Flex 4, Flex/AIR, Text Layout Framework with tags , , , , on April 26, 2010 by devgirl

One of my favorite things about Flex 4 is that it now includes the Text Layout Framework (TLF) as the basis of many Spark text components such as RichText, RichEditableText, TextArea, and TextInput as well as any other components that utilize any of these as part of another component, such as the ComboBox, which includes a TextInput control for example. You now have fine-grained control over text and text flow and can do some really amazing things with your UIs. This framework is extensive and I recently took some time to go through it all while building samples for Tour de Flex. I’d like to share some information I gathered while doing so.

The Text Layout Framework is included in Flex 4 via the textLayout.swc. This swc contains three combined SWC’s including:
• textLayout_core
• textLayout_conversion
• textLayout_edit

The textLayout_core component is the main component for the framework and handles storage and display of text.
The textLayout_conversion component is used for import/export of text and is necessary if you use text that is not compiled directly into the SWF.
The textLayout_edit component provides the editing libraries needed for selection, editing (cut/copy/paste) and undo capabilities.

This framework can be understood better when looking at it from an MVC standpoint. The packages that make up each part are listed below:

Model

  • flashx.textLayout.elements.* (classes for data structure definition)
  • flashx.textLayout.formats.* (classes for formatting properties)
  • flashx.textLayout.conversion.* (classes for import/export)

View

  • flashx.textLayout.factory.*
  • flashx.textLayout.container.*
  • flashx.textLayout.compose.*

Controller

  • flashx.textLayout.edit.*
  • flashx.textLayout.operations.*

Importing Text
In many cases you will need to import text into the framework. This can be especially useful for importing dynamic text that is returned from a remote service or HTTPService at runtime for instance. You can choose from three different conversion options for import/export of text in general. They are:

TextConverter.TEXT_LAYOUT_FORMAT – convert to/from the text layout markup format
TextConverter.PLAIN_TEXT_FORMAT – convert to/from a plain text string
TextConverter.TEXT_FIELD_HTML_FORMAT – convert to/from HTML (subset of HTML supported – see here for details)

The following example shows how to use the TLF markup language itself within some text and then import it into the framework for managing the display:

private static const textInput:XML = <TextFlow xmlns="http://ns.adobe.com/textLayout/2008">
	<div>
	<p color="0x336699"><span>The Text Layout Framework is an extensible library, built on the new text engine in Adobe Flash Player 10, which delivers advanced, easy-to-integrate typographic and text layout features for rich, sophisticated and innovative typography on the web. 
	</span></p>
	</div>
	</TextFlow>;
			
private var _textFlow:TextFlow;
var importer:ITextImporter = TextConverter.getImporter(TextConverter.TEXT_LAYOUT_FORMAT);
textFlow = importer.importToFlow(textInput);

You could also do something like the following with plain text. Note that this example specifies the format directly on the import method, whereas the code above retrieved an import filter first with a call to getImporter(). If you want to catch errors on the conversion then you would use the one that gets the import filter first such as above.

private static var myText:String = "Hello World";			
private var _textFlow:TextFlow;
textFlow = importer.importToFlow(myText,TextConverter.PLAIN_TEXT_FORMAT);

You can export your text to those same formats. An example of exporting text to the Text Layout Framework markup is shown here:

TextConverter.export(customEditor.editor.textFlow,
TextConverter.TEXT_LAYOUT_FORMAT,
 ConversionType.STRING_TYPE);

Applying this to Flex 4 controls, you could use any of the TLF-based controls’ textFlow property and set that value to the imported String result. The following code snippet shows how this is done:

	<fx:Declarations> 
		<!-- Define a String to use with HTML and plain text format. --> 
		<fx:String id="htmlTxt"><![CDATA[<p>Text containing <b>HTML</b> markup</p>]]></fx:String> 
		
		<!-- Define an XML object to use with TLF format. --> 
		<fx:XML id="tfTxt"> 
			<TextFlow xmlns="http://ns.adobe.com/textLayout/2008"> 
				<p>Text Using  <span fontWeight="bold">Text Layout Framework</span> Markup</p> 
			</TextFlow> 
		</fx:XML> 
	</fx:Declarations> 
	<s:TextArea id="txt1" width="200" height="50"
						textFlow="{TextConverter.importToFlow(htmlText, TextConverter.TEXT_FIELD_HTML_FORMAT)}" 
						horizontalCenter="0" verticalCenter="0" /> 
	<s:RichText id="txt3" width="200" height="50"
						textFlow="{TextConverter.importToFlow(tfText, TextConverter.TEXT_LAYOUT_FORMAT)}" 
						horizontalCenter="0" verticalCenter="0" /> 

You can also use the TextFlowUtil class to import a String into a Flex 4 TLF-based component as in the following code snippet:

		var markup:String = "<TextFlow xmlns='http://ns.adobe.com/textLayout/2008'><p fontFamily='Arial'>This is TLF markup with paragraphs.</p><p color='0x663399'>The root TextFlow tag is included.</p></TextFlow>"; 
		rt1.textFlow = TextFlowUtil.importFromString(markup);
			
		<s:TextArea id="rt1" width="300" height="50"/>

Note that you can even omit the TextFlow root and namespace and simply start with the paragraph tag and the TextFlow root and namespace will be added for you; for example:

		var markup:String = "<p color='0xCE267D'>This is TLF markup with paragraphs.</p><p fontSize='10' fontWeight='bold' fontFamily='Arial'>The root TextFlow tag is omitted and therefore created automatically.</p>"; 
		rt1.textFlow = TextFlowUtil.importFromString(markup);
		
                <s:RichText id="rt1" width="200"/>

There are some important classes that are key to the framework:

TextFlow
Represents the text itself and can contain either ParagraphElement (p) or DivElement (div). DivElement can be a group of ParagraphElements. ParagraphElement (p) can contain span, inline image, link or tcy elements (for Japanese text handling). The following picture is from the Adobe SDK docs and helps explain the relationship hierarchy further:

The TextFlow can contain multiple containers and each container is associated with a ContainerController.

ContainerController
A controller is associated with each container that is being used to display text (manages a Sprite for instance). The controller is used to control how the text is going to flow between containers. Here is another image from the Flex 4 SDK docs that I believe helps explain this nicely:

IFlowComposer
The IFlowComposer interface is implemented by the StandardFlowComposer and manages the conversion of the text into TextLine objects and placement of the text in the containers. The updateAllControllers() net must be called on this object to lay out the text and add update the display when any settings have been changed.

For instance:

textFlow.fontSize = 11;
flow.flowComposer = new StandardFlowComposer();
textFlow.flowComposer.updateAllControllers();

TextLayoutFormat
Formatting of text can be done in multiple ways, either at the container, paragraph, or character level. It’s done by using either a TextLayoutFormat object, which contains all of the properties that can be set, or by setting the specific properties on the TextFlow object. If you use the TextLayoutFormat object, you then assign it to the format property of the TextFlow object:

var tlf:TextLayoutFormat = new TextLayoutFormat();
tlf.fontSize =11;
tlf.direction = Direction.RTL;
tlf.columnCount = 3;
tlf.columnGap = 15;
textFlow.format = tlf;				

If you are only changing one attribute, you can do so on the TextFlow object directly as well:

textFlow.fontSize = 11;

The following sample code snippets show some specific techniques you might want to use with this framework:

Directional Text

textFlow.direction = Direction.RTL; // right-to-left text

Inline Images
You can display an inline image in your text with something like the following:

[Embed(source="adobe_air_logo.png")]
[Bindable]
static public var imgClass:Class;

var p:ParagraphElement = new ParagraphElement();
var inlineGraphicElement:InlineGraphicElement = new InlineGraphicElement();
inlineGraphicElement.source = imgClass;
inlineGraphicElement.width=32;
inlineGraphicElement.height=32;
p.addChild(inlineGraphicElement);
textFlow.addChild(p);

Selecting/Editing Text
You can create an EditManager with an UndoManager if you would like to use selection, editing, and undo capabilities. If you only need to support selection, you can use the base SelectionManager class. Below is code that shows how to set up the code to handle editing/undo. See the FlowOperation class docs for more information on the types of operations that can be handled. The new Tour de Flex sample shows how to do selection, undo, and redo of text so check that out for more information.

textFlow.interactionManager = new EditManager(new UndoManager());		

A great example of the Text Layout Framework in use is the New York Times Adobe AIR application. If you haven’t downloaded this app yet, I highly recommend it, and it’s FREE!

And finally some links for further information:
Tour de Flex TLF Samples
Text Layout Framework Team Blog – great examples here too!
InsideRIA article by Elad Elrom on MXML and TLF
Text Layout Frameworks on Adobe Labs
Try out TLF with an online editor

Advertisements

Framework RSL’s in Flex Builder vs Flash Builder, Performance and Important Info

Posted in Flash Builder, Flex 4, Flex Builder, Flex/AIR with tags , , , , , , , on April 8, 2010 by devgirl

If you are running multiple Flex applications on a server you should seriously consider using Flex Framework RSLs (runtime shared libraries) to reduce redundancy by eliminating the loading of the same core framework libraries used by all applications. The result could be a reduction in the size of your application SWF file to 1/10th of the original size! I recently implemented this approach for Tour de Flex since each of our hosted samples is its own application and would ultimately contain a large amount of code shared by all the other samples. I saw the size of the swf’s for the samples go from 572k to 57k just by making one small change, removing the core Flex Framework RSL’s from the resulting swf.

The good news is, in Flex 4 previously optimized core framework RSL’s are enabled by default and dynamically linked at runtime (versus compiled into the code via the ‘merged into code’ option). This option is specified in the project properties under Flex Build Path. Under the Library Path tab there you will see a Framework linkage selection that should look like the screenshot below and use runtime shared libraries by default. If it does not show that as the default, you can also select Runtime Shared Libraries from the drop-down list to ensure they are used.

Using Flex Builder you will see that the default option is set to Merged into Code, such as the following:

It’s important to understand what is happening here whether you are using Flex Builder or Flash Builder. Obviously with Flex Builder you will need to modify that option if you want to take advantage of the performance gain of having those libraries externalized. However even with Flash Builder and using Flex 4 it’s important to note what is happening, because though the RSL’s are enabled by default, the default configuration settings currently create a copy of the RSL locally for each of the different SWZ files (the extension of the core framework RSL). This means that when you export your release build for your application (File -> Export -> Release Build) you will have a copy of the each of those 6 .swz files created ranging from 60kb to 620kb. The 6 .swz files (release build number will vary) are:

  • framework_4.0.0.14159.swz
  • osmf_flex.4.0.0.13495.swz
  • rpc_4.0.0.14159.swz
  • spark_4.0.0.14159.swz
  • sparkskins_4.0.0.14159.swz
  • textLayout_1.0.0.595.swz

This may not be a big deal, but in the case of Tour de Flex samples there was no reason to have a copy of these files for every single sample on our server. I thought it would be useful to share this in case others were faced with this same issue or wondering what the heck all those .swz files were in their release directory. If you want to make a change to how this works, you can change your Flex configuration file to point to a different location for the runtime shared libraries other than creating them locally. There are two XML tags that specify an RSL location in the configuration file for each of the shared libraries. The first one points to the official Adobe path where they are located and you should leave that one as is. The 2nd one specifies the failover URL that you would want to change to point to a single location on a server somewhere for instance. For Tour de Flex I modified each of the failover URL paths in the configuration file to point to our Tour de Flex server that had all of those SWZ files (RSL’s) in one location. Then when I compiled each sample (or any application for that matter once these settings are changed, so be careful) it would no longer generate those local .swz files.

Here’s an example of one of the six default RSL path settings from the flex-config.xml file that comes with the Flex 4 SDK. The flex-config.xml file is the Flex configuration file and located on Mac for instance at /Applications/Adobe Flash Builder 4/sdks/4.0.0/frameworks/flex-config.xml:

	 <!-- Spark SWC-->
   	<runtime-shared-library-path>
		<path-element>libs/spark.swc</path-element>
		<rsl-url>http://fpdownload.adobe.com/pub/swz/flex/4.0.0.14159/spark_4.0.0.14159.swz</rsl-url>
		<policy-file-url>http://fpdownload.adobe.com/pub/swz/crossdomain.xml</policy-file-url>
		<rsl-url>spark_4.0.0.14159.swz</rsl-url>
		<policy-file-url></policy-file-url>
	</runtime-shared-library-path>

with these settings you will see the RSL files generated in the local bin-release directory when the application is built for release.

In the case of Tour de Flex, I changed that 2nd tag to point to our TDF Server location. For example:

        <!-- Spark SWC-->
   	<runtime-shared-library-path>
		<path-element>libs/spark.swc</path-element>
		<rsl-url>http://fpdownload.adobe.com/pub/swz/flex/4.0.0.14159/spark_4.0.0.14159.swz</rsl-url>
		<policy-file-url>http://fpdownload.adobe.com/pub/swz/crossdomain.xml</policy-file-url>
		<rsl-url>http://tdfserver/RSL/spark_4.0.0.14159.swz</rsl-url>
		<policy-file-url></policy-file-url>
	</runtime-shared-library-path>

When Flash Builder was then restarted with these settings and a sample recompiled, the local SWZ files were no longer output in the local release directory. IMPORTANT NOTE: if you keep Flash Builder open while you modify your flex-config.xml file you must restart it to pick up the changes. You also may need to delete the bin-release folder of your project first before seeing the change from recompiling.

There is also an option in Flash Builder that will create local copies of the runtime shared libraries in your bin-debug folder by default. If you look at the same Build Path -> Library Path settings in the Flash Builder screenshot above (1st screenshot), notice the checkbox for ‘Use local runtime shared libraries when debugging’, you will see that same set of files (but .swf) generated in your bin-debug output. When you uncheck this box they will not be generated. I thought it would be useful to point this out as well.

The last thing I want to note is that you can also use command line compile to change the option of where to load the RSL file from. Loads of information on how to do that, as well as more information on Framework RSL’s in general can be found here.