I recently embarked on the journey towards tinytlf v2.0, a shiny new version replete with glossy sheen, twinkling diamond star, and cash-machine *jing* sound effect.
First, I gotta apologize for pushing v1 out into the world. It was crap. I’ve changed almost everything in v2, making it smaller, faster, and more feature rich. Crazy you say? Here’s a few of the additions and changes that make version 2 smarter than version 1:
- Using Dependency Injection instead of the Facade pattern
- Consolidating all the maps into a single implementation
- Integrating HTML and CSS into the core
- Rewriting the paragraph and line renderer that handles all variations of layout [PDF], floats, and virtualization (I’m proud of my work in this area)
- Alongside the renderer, adding a parser for content blocks, which is a better abstraction for implementing percent sizing, margins, padding, lists, tables, and floats than the way I did it in v1
- A rewrite of the interaction model, implementing gestures using raix, a port of Microsoft’s incredible Rx.NET framework. (events are fun again!)
- Because of the addition of raix, I can do away with the Flash Text Engine’s EventMirrors. I’ve implemented a (faster, smaller) replacement. You can see this action in AnchorMirror.as
Dependency Injection
Instead of wiring everything up through the combination of a Facade and maps, I’ve switched to SwiftSuspenders for Dependency Injection. Using a formal IoC framework makes tinytlf infinitely more extensible, rids the framework of ceremony for the sake of it, and gives us sweet [Inject] syntax to boot. And SwiftSuspenders is only 15K; fucking awesome.
Maps maps maps
I’ve written a generic mapping class, implemented a few interfaces for the sake of IoC mappings (and to reduce map collisions), and stuck them all in the IoC container. This reduces file size and cyclomatic complexity, as each map implementation is the same. That’s not to say you can’t extend it; feel free to extend the map and replace it in the IoC container if required.
HTML and CSS
The only way anybody is going to work with text is with markup and CSS. So I wrote a small CSS parser to inject and lookup styles, and the model is a DOM built from XML.
New Rendering Algorithm
I completely rewrote the render and layout algorithms. The new algorithm is referentially transparent, with zero side effects (aside from a bit of TextLine caching for performance’ sake).
What does that mean to you? There should be no weird bugs or race conditions caused by variables you can’t replicate in testing.
The previous rendering algorithm was shit. It’s the primary reason I never implemented editing. Virtualization was hacked in. It suffered from race conditions and required property synchronization, due to an architecture that maintained state and only worked if started in the correct configuration.
I’ve rewritten the layout algorithm from the ground up, solved all these problems, and exposed more points of extension.
If you want to see the speed of the new layout algorithm, scroll through this as fast as you can (looks cool with OS X Lion’s inertial scrolling). I already know of many places it should be improved, but it’s a hell of a lot better than what it was!
New interactions via Reactive eXtensions
Reactive Programming is already a wave breaking over the .NET (and maybe JS?) world, but unfortunately ActionScript developers have been largely left behind by this revolution in interactive programming, and have suffered for it.
Big thanks to Richard Szalay for raix, his port of Rx.NET to AS3.
I wrote a class called Observables which creates and manages IObservable streams for various interaction
events, some of which (like drag, doubleDown, doubleDrag, tripleDown, and tripleDrag) are compositions of event streams.
Observables.as also allows you to register IEventDispatchers to be included in master IObservable streams. A developer can subscribe to the master streams if he’s interested in receiving events from all IEventDispatchers registered with Observables.
HTML Demo
For my demos, I’ve chosen to work with the unaltered HTML from a few posts on idlewords.com, specifically Why Arabic is Terrific, and the July 2010 index page. The text has many features I wish to support: malformed XHTML, CSS, inline styles, tables, lists, anchors, bidirectional text, and floats. Here’s the demo source, HTML, and CSS. Click here to open the SWF in a fullscreen window.
Where does that leave things?
I wrote this post to show people that the framework is back in active development. There are already many places I know where performance can be improved. I don’t have a TextField component yet, decorations aren’t done, and most HTML tags (and their layout configurations) haven’t been implemented. Editing is back on the roadmap, but not done either.
Like always, feel free to email me with questions or feature requests, and I’m always monitoring github for forks and pull requests. Happy coding!
Tags: tinytlf



Great work! Also, a lot of references to other technologies worth studying. Thanks a lot!
Though the HTML demo crashes with RTE:
TypeError: Error #1085: “p” should end with “”.
at org.tinytlf.util::TagSoup$/toXML()
at Main()
Can’t see anything in the demo – just a white list (fp 11,0,1,98 win)
Just tried it with fp 10,3,183,7 – text is visible now, but it seems not selectable (the cursor looks as selectable though)
Ah yeah, while I have the interaction logic, I haven’t implemented the selection decorations again yet.
What XML format are you planning to support?
meaning:
xmlns=”http://ns.adobe.com/fxg/2008″
xmlns=”http://ns.adobe.com/textLayout/2008″
Vanilla HTML/CSS (XHTML5)
TextField HTML
Adobe has importers and exporters for a couple of different formats. This one has FXG and TextLayout:
http://labs.adobe.com/technologies/textlayout/demos/
The 3.0 archive here has TextLayout and TextField HTML (in the flex folder):
http://sourceforge.net/projects/tlf.adobe/files/3.0/current/
Just curious.
The parser still parses any valid XML, though this time I’ve broken it up between content and layout parsing.
Each tag can affect the ContentElements that ultimately get created, or they can represent “components” in the text layout. You can still externally modify the way elements are parsed: 952e34ccf225bb4afb9fa66131ba3d2de8bc7d4b TextEngineInjector.as#L82.
Line 87 says to the parser “whenever you see a <br/> node, create a GroupElement with two invisible GraphicElement children and a breakOpportunity of ‘all’.” This trick forces the TextBlock rendering algorithm to break the line right after the first invisible Graphic, and start the next line with the second invisible Graphic (http://guyinthechair.com/2011/06/fte-layout-tricks-with-breakopportunity-all/).
This works for <br/> tags inside paragraphs, but HTML also allows <br/> tags outside paragraphs. Tinytlf’s renderer no longer treats everything as “inside a paragraph”, so it’s important to define a layout item for <br/> tags too: 952e34ccf225bb4afb9fa66131ba3d2de8bc7d4b TextEngineInjector.as#L140. All this says is that whenever the renderer encounters a <br/> tag outside a paragraph, insert a rectangle that’s as tall as one line of text.
I expect to handle most cases with one or both of these techniques.
[...] Taylor released tinytlf 2.0 beta which is a lightweight text layout framework for ActionScript. He says that version 2.0 includes [...]
[...] I’ve given several Freehand drawing demos on my iPad-2 here at MAX, but one of the things I’ve failed to mention is the impending release of TinyTLF V2.0. You can read about the new features and implementation details here. [...]
Hi..
Are there any example demo files for Flash (not Flex) anywhere? I’m having a hard time getting anything to actually work. i downloaded from gethub, and tried working with that.. but I can’t seem to get an example going. there’s not a .fla file to reference.
Thanks,
TypeError: Error #1006: soup is not a function.
at org.tinytlf.util::TagSoup$/soup()[D:\EverBox\我的实验室\tinytlf\src\org\tinytlf\util\TagSoup.as:125]
at org.tinytlf.util::TagSoup$/slurp()[D:\EverBox\我的实验室\tinytlf\src\org\tinytlf\util\TagSoup.as:74]
at org.tinytlf.util::TagSoup$/toXML()[D:\EverBox\我的实验室\tinytlf\src\org\tinytlf\util\TagSoup.as:49]
at Main()[D:\EverBox\我的实验室\tinytlf\src\Main.as:60]
This is why?