Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3352115
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类: 系统运维

2011-11-05 16:12:40

1 概述
       继承:Label ->TextBase ->UIComponent
       Label 是可以呈示一行或多行统一格式文本的低级 UIComponent。要显示的文本由从 TextBase 继承的 text 属性确定。文本格式由元素的 CSS 样式指定,例如 fontFamily 和 fontSize。

具体格式如下
  1. <s:Label
  2.     Properties
  3.     fontContext=""
  4.  
  5.     Styles
  6.     alignmentBaseline="baseline"
  7.     baselineShift="0"
  8.     cffHinting="0.0"
  9.     color="0x000000"
  10.     digitCase="default"
  11.     digitWidth="default"
  12.     direction="ltr"
  13.     dominantBaseline="auto"
  14.     fontFamily="Arial"
  15.     fontLookup="embeddedCFF"
  16.     fontSize="12"
  17.     fontStyle="normal"
  18.     fontWeight="normal"
  19.     justificationRule="auto"
  20.     justificationStyle="auto"
  21.     kerning="false"
  22.     ligatureLevel="common"
  23.     lineBreak="toFit"
  24.     lineHeight="120%"
  25.     lineThrough="false%"
  26.     locale="en"
  27.     paddingBottom="0"
  28.     paddingLeft="0"
  29.     paddingRight="0"
  30.     paddingTop="0"
  31.     renderingMode="cff"
  32.     textAlign="start"
  33.     textAlignLast="start"
  34.     textAlpha="1"
  35.     textDecoration="start"
  36.     textJustify="interWord"
  37.     trackingLeft="0"
  38.     trackingRight="00"
  39.     typographicCase="default"
  40.     verticalAlign="top"
  41.   />


2 常见属性和方法


3 源代码
  package spark.components {
  1. import flash.display.DisplayObject;
  2. import flash.display.DisplayObjectContainer;
  3. import flash.display.Graphics;
  4. import flash.display.Shape;
  5. import flash.geom.Rectangle;
  6. import flash.text.TextFormat;
  7. import flash.text.engine.EastAsianJustifier;
  8. import flash.text.engine.ElementFormat;
  9. import flash.text.engine.FontDescription;
  10. import flash.text.engine.FontLookup;
  11. import flash.text.engine.FontMetrics;
  12. import flash.text.engine.Kerning;
  13. import flash.text.engine.LineJustification;
  14. import flash.text.engine.SpaceJustifier;
  15. import flash.text.engine.TextBaseline;
  16. import flash.text.engine.TextBlock;
  17. import flash.text.engine.TextElement;
  18. import flash.text.engine.TextLine;
  19. import flash.text.engine.TypographicCase;

  20. import flashx.textLayout.compose.ISWFContext;
  21. import flashx.textLayout.compose.TextLineRecycler;
  22. import flashx.textLayout.formats.BaselineShift;
  23. import flashx.textLayout.formats.TLFTypographicCase;

  24. import mx.core.IEmbeddedFontRegistry;
  25. import mx.core.IFlexModuleFactory;
  26. import mx.core.IUIComponent;
  27. import mx.core.Singleton;  //详见《mx.core.Singleton源代码解析
  28. import mx.core.mx_internal;

  29. import spark.components.supportClasses.TextBase;
  30. import spark.utils.TextUtil;

  31. use namespace mx_internal;

  32. // Styles

  33. include "../styles/metadata/BasicInheritingTextStyles.as"
  34. include "../styles/metadata/BasicNonInheritingTextStyles.as"

  35. // Other metadata
  36. [DefaultProperty("text")]

  37. [IconFile("Label.png")]
  38. public class Label extends TextBase
  39. {
  40.     include "../core/Version.as";

  41.     // 类构造器
  42.     private static function initClass():void
  43.     {
  44.         staticTextBlock = new TextBlock();
  45.         staticTextElement = new TextElement();
  46.         staticSpaceJustifier = new SpaceJustifier();
  47.         staticEastAsianJustifier = new EastAsianJustifier();
  48.         
  49.         if ("recreateTextLine" in staticTextBlock)
  50.             recreateTextLine = staticTextBlock["recreateTextLine"];
  51.     }
  52.     
  53.     initClass();

  54.     // 类变量
  55.     private static var staticTextBlock:TextBlock;
  56.     private static var staticTextElement:TextElement;
  57.     private static var staticSpaceJustifier:SpaceJustifier;
  58.     private static var staticEastAsianJustifier:EastAsianJustifier;
  59.     
  60.     /**
  61.      * staticTextBlock类可函数recreateTextLine()的引用
  62.      * 如果该引用存在,则这个方法可以添加到player 10.1中
  63.      * 这个引用允许复用存在的TextLines,而不是重新创建一个,从而提高性能。
  64.      */
  65.     private static var recreateTextLine:Function;

  66.     // 类的属性
  67.     // 属性embeddedFontRegistry

  68.     /**
  69.      * 用来存储_embeddedFontRegistry
  70.      * 注意:第一次访问的时候初始化,如果这个类没有初始化,应确定单例的registry已初始化
  71.      */
  72.     private static var _embeddedFontRegistry:IEmbeddedFontRegistry;

  73.     /**
  74.      *  embedded font registry的引用.
  75.      *  详见《mx.core.Singleton源代码解析
  76.      */
  77.     private static function get embeddedFontRegistry():IEmbeddedFontRegistry
  78.     {
  79.         if (!_embeddedFontRegistry)
  80.         {
  81.             _embeddedFontRegistry = IEmbeddedFontRegistry(
  82.                 Singleton.getInstance("mx.core::IEmbeddedFontRegistry"));
  83.         }

  84.         return _embeddedFontRegistry;
  85.     }

  86.     // 类方法
  87.     //获取百分比对应的实际值
  88.     private static function getNumberOrPercentOf(value:Object, n:Number):Number
  89.     {
  90.         // 如果value是一个number值,比如10.5,则返回该值
  91.         if (value is Number)
  92.             return Number(value);

  93.         // 如果value是一个百分比值,比如 10.5%,则返回n*value的得到的实际值,
  94.         if (value is String){
  95.             var len:int = String(value).length;
  96.             if (len >= 1 && value.charAt(len - 1) == "%"){
  97.                 var percent:Number = Number(value.substring(0, len - 1));
  98.                 return percent / 100 * n;
  99.             }
  100.         }

  101.         // 否则返回 NaN.
  102.         return NaN;
  103.     }

  104.     // 构造函数
  105.     public function Label()
  106.     {
  107.         super();
  108.     }
  109.     

  110.     // 变量

  111.     /**
  112.      * 保存module工厂的最后记录,用于创建font
  113.      */
  114.     private var embeddedFontContext : IFlexModuleFactory;
  115.     
  116.     /**
  117.      * 当文本使用FTE格式显示时,这个对象format文本中的元素。
  118.      * 每次format相关样式改变后,这个对象将被销毁,仅只在渲染文本时使用
  119.      */
  120.     private var elementFormat:ElementFormat;

  121.     // 覆盖接口ISimpleStyleClient中方法
  122.     override public function stylesInitialized():void
  123.     {
  124.         super.stylesInitialized();
  125.         elementFormat = null;
  126.     }
  127.    
  128.     // 覆盖接口ISimpleStyleClient中方法
  129.     override public function styleChanged(styleProp:String):void
  130.     {
  131.         super.styleChanged(styleProp);
  132.         elementFormat = null;
  133.     }
  134.     
  135.     // 覆盖父类TextBase中的方法   
  136.     /**
  137.      * 这个函数由measure() 和 updateDisplayList()使用,
    使用了staticTextBlock 作为工厂,使用'width' 和 'height'参加定义了组合矩形的尺寸。如果所有的线组合完成,返回true,否则为false
  138.      */
  139.     override mx_internal function composeTextLines(width:Number = NaN,
  140.                                                    height:Number = NaN):Boolean
  141.     {
  142.         super.composeTextLines(width, height);
  143.         
  144.         if (!elementFormat)
  145.             elementFormat = createElementFormat();
  146.             
  147.         // 通过createTextLines()设置组合的bound.
  148.         // 如果width或 height为 NaN, 由这个方法进行计算
  149.         // addTextLines()决定 isOverset 标志
  150.         // 组合 bounds 由 measure() 方法给出。
  151.         bounds.x = 0;
  152.         bounds.y = 0;
  153.         bounds.width = width;
  154.         bounds.height = height;

  155.         // 在容器中删除所有的 TextLines
  156.         removeTextLines();
  157.         releaseTextLines();
  158.         
  159.         // 创建 TextLines.
  160.         var allLinesComposed:Boolean = createTextLines(elementFormat);
  161.         
  162.         // Need truncation if all the following are true
  163.         // - there is text (even if there is no text there is may be padding
  164.         // which may not fit and the text would be reported as truncated)
  165.         // - truncation options exist (0=no trunc, -1=fill up bounds then trunc,
  166.         // n=n lines then trunc)
  167.         // - compose width is specified
  168.         // - content doesn't fit
  169.         var lb:String = getStyle("lineBreak");
  170.         if (text != null && text.length > 0 &&
  171.             maxDisplayedLines &&
  172.             !doesComposedTextFit(height, width, allLinesComposed, maxDisplayedLines, lb))
  173.         {
  174.             truncateText(width, height, lb);
  175.         }
  176.         
  177.         // Detach the TextLines from the TextBlock that created them.
  178.         releaseLinesFromTextBlock();
  179.                                                        
  180.         // Add the new text lines to the container.
  181.         addTextLines();

  182.         // Figure out if a scroll rect is needed.
  183.         isOverset = isTextOverset(width, height);
  184.         
  185.         // Just recomposed so reset.
  186.         invalidateCompose = false;
  187.         
  188.         return allLinesComposed;
  189.     }

  190.     //--------------------------------------------------------------------------
  191.     //
  192.     // Methods
  193.     //
  194.     //--------------------------------------------------------------------------

  195.     /**
  196.      * @private
  197.      * Creates an ElementFormat (and its FontDescription)
  198.      * based on the Label's CSS styles.
  199.      * These must be recreated each time because FTE
  200.      * does not allow them to be reused.
  201.      * As a side effect, this method also sets embeddedFontContext
  202.      * so that we know which SWF should be used to create TextLines.
  203.      * (TextLines using an embedded font must be created in the SWF
  204.      * where the font is.)
  205.      */
  206.     private function createElementFormat():ElementFormat
  207.     {
  208.         // When you databind to a text formatting style on a Label,
  209.         // as in
  210.         // the databinding can cause the style to be set to null.
  211.         // Setting null values for properties in an FTE FontDescription
  212.         // or ElementFormat throw an error, so the following code does
  213.         // null-checking on the problematic properties.

  214.         var s:String;
  215.         
  216.         // If the CSS styles for this component specify an embedded font,
  217.         // embeddedFontContext will be set to the module factory that
  218.         // should create TextLines (since they must be created in the
  219.         // SWF where the embedded font is.)
  220.         // Otherwise, this will be null.
  221.         embeddedFontContext = getEmbeddedFontContext();

  222.         // Fill out a FontDescription based on the CSS styles.
  223.         
  224.         var fontDescription:FontDescription = new FontDescription();
  225.         
  226.         s = getStyle("cffHinting");
  227.         if (s != null)
  228.             fontDescription.cffHinting = s;
  229.         
  230.         s = getStyle("fontLookup");
  231.         if (s != null)
  232.         {
  233.             // FTE understands only "device" and "embeddedCFF"
  234.             // for fontLookup. But Flex allows this style to be
  235.             // set to "auto", in which case we automatically
  236.             // determine it based on whether the CSS styles
  237.             // specify an embedded font.
  238.             if (s == "auto")
  239.             {
  240.                 s = embeddedFontContext ?
  241.                     FontLookup.EMBEDDED_CFF :
  242.                     FontLookup.DEVICE;
  243.             }
  244.             else if (s == FontLookup.EMBEDDED_CFF && !embeddedFontContext)
  245.             {
  246.                 // If the embedded font isn't found, fall back to device font,
  247.                 // before falling back to the player's default font.
  248.                 s = FontLookup.DEVICE;
  249.             }
  250.             fontDescription.fontLookup = s;
  251.         }
  252.         
  253.         s = getStyle("fontFamily");
  254.         if (s != null)
  255.             fontDescription.fontName = s;
  256.         
  257.         s = getStyle("fontStyle");
  258.         if (s != null)
  259.             fontDescription.fontPosture = s;
  260.         
  261.         s = getStyle("fontWeight");
  262.         if (s != null)
  263.             fontDescription.fontWeight = s;
  264.             
  265.         s = getStyle("renderingMode");
  266.         if (s != null)
  267.             fontDescription.renderingMode = s;
  268.         
  269.         // Fill our an ElementFormat based on the CSS styles.
  270.         
  271.         var elementFormat:ElementFormat = new ElementFormat();
  272.         
  273.         // Out of order so it can be used by baselineShift.
  274.         elementFormat.fontSize = getStyle("fontSize");
  275.         
  276.         s = getStyle("alignmentBaseline");
  277.         if (s != null)
  278.             elementFormat.alignmentBaseline = s;
  279.             
  280.         elementFormat.alpha = getStyle("textAlpha");
  281.             
  282.         setBaselineShift(elementFormat);
  283.         
  284.         // Note: Label doesn't support a breakOpportunity style,
  285.         // so we leave elementFormat.breakOpportunity with its
  286.         // default value of "auto".
  287.             
  288.         elementFormat.color = getStyle("color");
  289.         
  290.         s = getStyle("digitCase");
  291.         if (s != null)
  292.             elementFormat.digitCase = s;
  293.             
  294.         s = getStyle("digitWidth");
  295.         if (s != null)
  296.             elementFormat.digitWidth = s;
  297.             
  298.         s = getStyle("dominantBaseline");
  299.         if (s != null)
  300.         {
  301.             // TLF adds the concept of a locale-based "auto" setting for
  302.             // dominantBaseline, so we support that in Label as well
  303.             // so that "auto" can be used in the global selector.
  304.             // TLF's rule is that "auto" means "ideographicCenter"
  305.             // for Japanese and Chinese locales and "roman" for other locales.
  306.             // (See TLF's LocaleUtil, which we avoid linking in here.)
  307.             if (s == "auto")
  308.             {
  309.                 s = TextBaseline.ROMAN;
  310.                 var locale:String = getStyle("locale");
  311.                 if (locale != null)
  312.                 {
  313.                     var lowercaseLocale:String = locale.toLowerCase();
  314.                     if (lowercaseLocale.indexOf("ja") == 0 ||
  315.                         lowercaseLocale.indexOf("zh") == 0)
  316.                     {
  317.                         s = TextBaseline.IDEOGRAPHIC_CENTER;
  318.                     }
  319.                 }
  320.             }
  321.             elementFormat.dominantBaseline = s;
  322.         }
  323.             
  324.         elementFormat.fontDescription = fontDescription;
  325.         
  326.         setKerning(elementFormat);

  327.         s = getStyle("ligatureLevel");
  328.         if (s != null)
  329.             elementFormat.ligatureLevel = s;
  330.         
  331.         s = getStyle("locale");
  332.         if (s != null)
  333.             elementFormat.locale = s;
  334.         
  335.         setTracking(elementFormat);

  336.         setTypographicCase(elementFormat);
  337.         
  338.         return elementFormat;
  339.     }

  340.     /**
  341.      * @private
  342.      */
  343.     private function setBaselineShift(elementFormat:ElementFormat):void
  344.     {
  345.         var baselineShift:* = getStyle("baselineShift");
  346.         var fontSize:Number = elementFormat.fontSize;
  347.         
  348.         if (baselineShift == BaselineShift.SUPERSCRIPT ||
  349.             baselineShift == BaselineShift.SUBSCRIPT)
  350.         {
  351.             var fontMetrics:FontMetrics;
  352.             if (embeddedFontContext)
  353.                 fontMetrics = embeddedFontContext.callInContext(elementFormat.getFontMetrics, elementFormat, null);
  354.             else
  355.                 fontMetrics = elementFormat.getFontMetrics();
  356.             if (baselineShift == BaselineShift.SUPERSCRIPT)
  357.             {
  358.                 elementFormat.baselineShift =
  359.                     fontMetrics.superscriptOffset * fontSize;
  360.                 elementFormat.fontSize = fontMetrics.superscriptScale * fontSize;
  361.             }
  362.             else // it's subscript
  363.             {
  364.                 elementFormat.baselineShift =
  365.                     fontMetrics.subscriptOffset * fontSize;
  366.                 elementFormat.fontSize = fontMetrics.subscriptScale * fontSize;
  367.             }
  368.         }            
  369.         else
  370.         {
  371.             // TLF will throw a range error if percentage not between
  372.             // -1000% and 1000%. Label does not.
  373.             baselineShift =
  374.                 getNumberOrPercentOf(baselineShift, fontSize);
  375.             if (!isNaN(baselineShift))
  376.                 elementFormat.baselineShift = -baselineShift;
  377.                     // Note: The negative sign is because, as in TLF,
  378.                     // we want a positive number to shift the baseline up,
  379.                     // whereas FTE does it the opposite way.
  380.                     // In FTE, a positive baselineShift increases
  381.                     // the y coordinate of the baseline, which is
  382.                     // mathematically appropriate, but unintuitive.
  383.         }
  384.     }
  385.     
  386.     /**
  387.      * @private
  388.      */
  389.     private function setKerning(elementFormat:ElementFormat):void
  390.     {
  391.         var kerning:Object = getStyle("kerning");
  392.         
  393.         // In Halo components based on TextField,
  394.         // kerning is supposed to be true or false.
  395.         // The default in TextField and Flex 3 is false
  396.         // because kerning doesn't work for device fonts
  397.         // and is slow for embedded fonts.
  398.         // In Spark components based on TLF and FTE,
  399.         // kerning is "auto", "on", or, "off".
  400.         // The default in TLF and FTE is "auto"
  401.         // (which means kern non-Asian characters)
  402.         // because kerning works even on device fonts
  403.         // and has miminal performance impact.
  404.         // Since a CSS selector or parent container
  405.         // can affect both Halo and Spark components,
  406.         // we need to map true to "on" and false to "off"
  407.         // here and in Label.
  408.         // For Halo components, UITextField and UIFTETextField
  409.         // do the opposite mapping
  410.         // of "auto" and "on" to true and "off" to false.
  411.         // We also support a value of "default"
  412.         // (which we set in the global selector)
  413.         // to mean "auto" for Spark and false for Halo
  414.         // to get the recommended behavior in both sets of components.
  415.         if (kerning === "default")
  416.             kerning = Kerning.AUTO;
  417.         else if (kerning === true)
  418.             kerning = Kerning.ON;
  419.         else if (kerning === false)
  420.             kerning = Kerning.OFF;
  421.         
  422.         if (kerning != null)
  423.            elementFormat.kerning = String(kerning);
  424.     }

  425.     /**
  426.      * @private
  427.      */
  428.     private function setTracking(elementFormat:ElementFormat):void
  429.     {
  430.         var trackingLeft:Object = getStyle("trackingLeft");
  431.         var trackingRight:Object = getStyle("trackingRight");
  432.         
  433.         var value:Number;
  434.         var fontSize:Number = elementFormat.fontSize;
  435.        
  436.         value = getNumberOrPercentOf(trackingLeft, fontSize);
  437.         if (!isNaN(value))
  438.             elementFormat.trackingLeft = value;

  439.         value = getNumberOrPercentOf(trackingRight, fontSize);
  440.         if (!isNaN(value))
  441.             elementFormat.trackingRight = value;
  442.     }

  443.     /**
  444.      * @private
  445.      */
  446.     private function setTypographicCase(elementFormat:ElementFormat):void
  447.     {
  448.         var s:String = getStyle("typographicCase");
  449.         if (s != null)
  450.         {
  451.             switch (s)
  452.             {
  453.                 case TLFTypographicCase.LOWERCASE_TO_SMALL_CAPS:
  454.                 {
  455.                     elementFormat.typographicCase =
  456.                         TypographicCase.CAPS_AND_SMALL_CAPS;
  457.                     break;
  458.                 }
  459.                 case TLFTypographicCase.CAPS_TO_SMALL_CAPS:
  460.                 {
  461.                     elementFormat.typographicCase = TypographicCase.SMALL_CAPS;
  462.                     break;
  463.                 }
  464.                 default:
  465.                 {
  466.                     // Others map directly so handle it in the default case.
  467.                     elementFormat.typographicCase = s;
  468.                     break;
  469.                 }
  470.             }
  471.         }
  472.     }

  473.     
  474.     /**
  475.      * @private
  476.      * Stuffs the specified text and formatting info into a TextBlock
  477.      * and uses it to create as many TextLines as fit into the bounds.
  478.      * Returns true if all the text was composed into textLines.
  479.      */
  480.     private function createTextLines(elementFormat:ElementFormat):Boolean
  481.     {
  482.         // Get CSS styles that affect a TextBlock and its justifier.
  483.         var direction:String = getStyle("direction");
  484.         var justificationRule:String = getStyle("justificationRule");
  485.         var justificationStyle:String = getStyle("justificationStyle");
  486.         var textAlign:String = getStyle("textAlign");
  487.         var textAlignLast:String = getStyle("textAlignLast");
  488.         var textJustify:String = getStyle("textJustify");

  489.         // TLF adds the concept of a locale-based "auto" setting for
  490.         // justificationRule and justificationStyle, so we support
  491.         // that in Label as well so that "auto" can be used
  492.         // in the global selector.
  493.         // TLF's rule is that "auto" for justificationRule means "eastAsian"
  494.         // for Japanese and Chinese locales and "space" for other locales,
  495.         // and that "auto" for justificationStyle (which only affects
  496.         // the EastAsianJustifier) always means "pushInKinsoku".
  497.         // (See TLF's LocaleUtil, which we avoid linking in here.)
  498.         if (justificationRule == "auto")
  499.         {
  500.             justificationRule = "space";
  501.             var locale:String = getStyle("locale");
  502.             if (locale != null)
  503.             {
  504.                 var lowercaseLocale:String = locale.toLowerCase();
  505.                 if (lowercaseLocale.indexOf("ja") == 0 ||
  506.                     lowercaseLocale.indexOf("zh") == 0)
  507.                 {
  508.                     justificationRule = "eastAsian";
  509.                 }
  510.             }
  511.         }
  512.         if (justificationStyle == "auto")
  513.             justificationStyle = "pushInKinsoku";

  514.         // Set the TextBlock's content.
  515.         // Note: If there is no text, we do what TLF does and compose
  516.         // a paragraph terminator character, so that a TextLine
  517.         // gets created and we can measure it.
  518.         // It will have a width of 0 but a height equal
  519.         // to the font's ascent plus descent.
  520.         staticTextElement.text = text != null && text.length > 0 ? text : "\u2029";
  521.         staticTextElement.elementFormat = elementFormat;
  522.         staticTextBlock.content = staticTextElement;

  523.         // And its bidiLevel.
  524.         staticTextBlock.bidiLevel = direction == "ltr" ? 0 : 1;

  525.         // And its justifier.
  526.         var lineJustification:String;
  527.         if (textAlign == "justify")
  528.         {
  529.             lineJustification = textAlignLast == "justify" ?
  530.                                 LineJustification.ALL_INCLUDING_LAST :
  531.                                 LineJustification.ALL_BUT_LAST;
  532.         }
  533.         else
  534.         {
  535.             lineJustification = LineJustification.UNJUSTIFIED;
  536.         }
  537.         if (justificationRule == "space")
  538.         {
  539.             staticSpaceJustifier.lineJustification = lineJustification;
  540.             staticSpaceJustifier.letterSpacing = textJustify == "distribute";
  541.             staticTextBlock.textJustifier = staticSpaceJustifier;
  542.         }
  543.         else
  544.         {
  545.             staticEastAsianJustifier.lineJustification = lineJustification;
  546.             staticEastAsianJustifier.justificationStyle = justificationStyle;
  547.             
  548.             staticTextBlock.textJustifier = staticEastAsianJustifier;
  549.         }
  550.                 
  551.         // Then create TextLines using this TextBlock.
  552.         return createTextLinesFromTextBlock(staticTextBlock, textLines, bounds);
  553.     }

  554.     /**
  555.      * @private
  556.      * Compose into textLines. bounds on input is size of composition
  557.      * area and on output is the size of the composed content.
  558.      * The caller must call releaseLinesFromTextBlock() to release the
  559.      * textLines from the TextBlock. This must be done after truncation
  560.      * so that the composed lines can be broken into atoms to figure out
  561.      * where the truncation indicator should be placed.
  562.      *
  563.      * Returns true if all the text was composed into textLines.
  564.      */
  565.     private function createTextLinesFromTextBlock(textBlock:TextBlock,
  566.                                                   textLines:Vector.<DisplayObject>,
  567.                                                   bounds:Rectangle):Boolean
  568.     {
  569.         // Start with 0 text lines.
  570.         releaseTextLines(textLines);
  571.            
  572.         // Get CSS styles for formats that we have to apply ourselves.
  573.         var direction:String = getStyle("direction");
  574.         var lineBreak:String = getStyle("lineBreak");
  575.         var lineHeight:Object = getStyle("lineHeight");
  576.         var lineThrough:Boolean = getStyle("lineThrough");
  577.         var paddingBottom:Number = getStyle("paddingBottom");
  578.         var paddingLeft:Number = getStyle("paddingLeft");
  579.         var paddingRight:Number = getStyle("paddingRight");
  580.         var paddingTop:Number = getStyle("paddingTop");
  581.         var textAlign:String = getStyle("textAlign");
  582.         var textAlignLast:String = getStyle("textAlignLast");
  583.         var textDecoration:String = getStyle("textDecoration");
  584.         var verticalAlign:String = getStyle("verticalAlign");

  585.         var innerWidth:Number = bounds.width - paddingLeft - paddingRight;
  586.         var innerHeight:Number = bounds.height - paddingTop - paddingBottom;
  587.                 
  588.         var measureWidth:Boolean = isNaN(innerWidth);
  589.         if (measureWidth)
  590.             innerWidth = maxWidth;
  591.         
  592.         var maxLineWidth:Number = lineBreak == "explicit" ?
  593.                                   TextLine.MAX_LINE_WIDTH :
  594.                                   innerWidth;
  595.         
  596.         if (innerWidth < 0 || innerHeight < 0 || !textBlock)
  597.         {
  598.             bounds.width = 0;
  599.             bounds.height = 0;
  600.             return false;
  601.         }

  602.         var fontSize:Number = staticTextElement.elementFormat.fontSize;
  603.         var actualLineHeight:Number;
  604.         if (lineHeight is Number)
  605.         {
  606.             actualLineHeight = Number(lineHeight);
  607.         }
  608.         else if (lineHeight is String)
  609.         {
  610.             var len:int = lineHeight.length;
  611.             var percent:Number =
  612.                 Number(String(lineHeight).substring(0, len - 1));
  613.             actualLineHeight = percent / 100 * fontSize;
  614.         }
  615.         if (isNaN(actualLineHeight))
  616.             actualLineHeight = 1.2 * fontSize;
  617.         
  618.         var maxTextWidth:Number = 0;
  619.         var totalTextHeight:Number = 0;
  620.         var n:int = 0;
  621.         var nextTextLine:TextLine;
  622.         var nextY:Number = 0;
  623.         var textLine:TextLine;
  624.         
  625.         var swfContext:ISWFContext = ISWFContext(embeddedFontContext);
  626.                     
  627.         // For truncation, need to know if all lines have been composed.
  628.         var createdAllLines:Boolean = false;
  629.         // sometimes we need to create an extra line in order to compute
  630.         // truncation
  631.         var extraLine:Boolean;
  632.         
  633.         // Generate TextLines, stopping when we run out of text
  634.         // or reach the bottom of the requested bounds.
  635.         // In this loop the lines are positioned within the rectangle
  636.         // (0, 0, innerWidth, innerHeight), with top-left alignment.
  637.         while (true)
  638.         {
  639.             var recycleLine:TextLine = TextLineRecycler.getLineForReuse();
  640.             if (recycleLine)
  641.             {
  642.                 if (swfContext)
  643.                 {
  644.                     nextTextLine = swfContext.callInContext(
  645.                         textBlock["recreateTextLine"], textBlock,
  646.                         [ recycleLine, textLine, maxLineWidth ]);
  647.                 }
  648.                 else
  649.                 {
  650.                     nextTextLine = recreateTextLine(
  651.                         recycleLine, textLine, maxLineWidth);
  652.                 }
  653.             }
  654.             else
  655.             {
  656.                 if (swfContext)
  657.                 {
  658.                     nextTextLine = swfContext.callInContext(
  659.                         textBlock.createTextLine, textBlock,
  660.                         [ textLine, maxLineWidth ]);
  661.                 }
  662.                 else
  663.                 {
  664.                     nextTextLine = textBlock.createTextLine(
  665.                         textLine, maxLineWidth);
  666.                 }
  667.             }
  668.             
  669.             if (!nextTextLine)
  670.             {
  671.                 createdAllLines = !extraLine;
  672.                 break;
  673.             }
  674.             
  675.             // Determine the natural baseline position for this line.
  676.             // Note: The y coordinate of a TextLine is the location
  677.             // of its baseline, not of its top.
  678.             nextY += (n == 0 ? nextTextLine.ascent : actualLineHeight);
  679.             
  680.             // If verticalAlign is top and the next line is completely outside
  681.             // the rectangle, we're done. If verticalAlign is middle or bottom
  682.             // then we need to compose all the lines so the alignment is done
  683.             // correctly.
  684.             if (verticalAlign == "top" &&
  685.                 nextY - nextTextLine.ascent > innerHeight)
  686.             {
  687.                 // make an extra line so we can compute truncation
  688.                 if (!extraLine)
  689.                     extraLine = true;
  690.                 else
  691.                     break;
  692.             }

  693.             // We'll keep this line. Put it into the textLines array.
  694.             textLine = nextTextLine;
  695.             textLines[n++] = textLine;

  696.             // Assign its location based on left/top alignment.
  697.             // Its x position is 0 by default.
  698.             textLine.y = nextY;
  699.             
  700.             // Keep track of the maximum textWidth
  701.             // and the accumulated textHeight of the TextLines.
  702.             maxTextWidth = Math.max(maxTextWidth, textLine.textWidth);
  703.             totalTextHeight += textLine.textHeight;

  704.             if (lineThrough || textDecoration == "underline")
  705.             {
  706.                 // FTE doesn't render strikethroughs or underlines,
  707.                 // but it can tell us where to draw them.
  708.                 // You can't draw in a TextLine but it can have children,
  709.                 // so we create a child Shape to draw them in.
  710.                 
  711.                 var elementFormat:ElementFormat =
  712.                     TextElement(textBlock.content).elementFormat;
  713.                 var fontMetrics:FontMetrics;
  714.                 if (embeddedFontContext)
  715.                     fontMetrics = embeddedFontContext.callInContext(elementFormat.getFontMetrics, elementFormat, null);
  716.                 else
  717.                     fontMetrics = elementFormat.getFontMetrics();
  718.                 
  719.                 var shape:Shape = new Shape();
  720.                 var g:Graphics = shape.graphics;
  721.                 if (lineThrough)
  722.                 {
  723.                     g.lineStyle(fontMetrics.strikethroughThickness,
  724.                                 elementFormat.color, elementFormat.alpha);
  725.                     g.moveTo(0, fontMetrics.strikethroughOffset);
  726.                     g.lineTo(textLine.textWidth, fontMetrics.strikethroughOffset);
  727.                 }
  728.                 if (textDecoration == "underline")
  729.                 {
  730.                     g.lineStyle(fontMetrics.underlineThickness,
  731.                                 elementFormat.color, elementFormat.alpha);
  732.                     g.moveTo(0, fontMetrics.underlineOffset);
  733.                     g.lineTo(textLine.textWidth, fontMetrics.underlineOffset);
  734.                 }
  735.                 
  736.                 textLine.addChild(shape);
  737.             }
  738.         }

  739.         // At this point, n is the number of lines that fit
  740.         // and textLine is the last line that fit.

  741.         if (n == 0)
  742.         {
  743.             bounds.width = paddingLeft + paddingRight;
  744.             bounds.height = paddingTop + paddingBottom;
  745.             return false;
  746.         }
  747.         
  748.         // If not measuring the width, innerWidth remains the same since
  749.         // alignment is done over the innerWidth not over the width of the
  750.         // text that was just composed.
  751.         if (measureWidth)
  752.             innerWidth = maxTextWidth;

  753.         if (isNaN(bounds.height))
  754.             innerHeight = textLine.y + textLine.descent;
  755.         
  756.         // Ensure we snap for consistent results.
  757.         innerWidth = Math.ceil(innerWidth);
  758.         innerHeight = Math.ceil(innerHeight);
  759.         
  760.         var leftAligned:Boolean =
  761.             textAlign == "start" && direction == "ltr" ||
  762.             textAlign == "end" && direction == "rtl" ||
  763.             textAlign == "left" ||
  764.             textAlign == "justify";
  765.         var centerAligned:Boolean = textAlign == "center";
  766.         var rightAligned:Boolean =
  767.             textAlign == "start" && direction == "rtl" ||
  768.             textAlign == "end" && direction == "ltr" ||
  769.             textAlign == "right";

  770.         // Calculate loop constants for horizontal alignment.
  771.         var leftOffset:Number = bounds.left + paddingLeft;
  772.         var centerOffset:Number = leftOffset + innerWidth / 2;
  773.         var rightOffset:Number = leftOffset + innerWidth;
  774.         
  775.         // Calculate loop constants for vertical alignment.
  776.         var topOffset:Number = bounds.top + paddingTop;
  777.         var bottomOffset:Number = innerHeight - (textLine.y + textLine.descent);
  778.         var middleOffset:Number = bottomOffset / 2;
  779.         bottomOffset += topOffset;
  780.         middleOffset += topOffset;
  781.         var leading:Number = (innerHeight - totalTextHeight) / (n - 1);
  782.         
  783.         var previousTextLine:TextLine;
  784.         var y:Number = 0;

  785.         var lastLineIsSpecial:Boolean =
  786.             textAlign == "justify" && createdAllLines;

  787.         var minX:Number = innerWidth;
  788.         var minY:Number = innerHeight;
  789.         var maxX:Number = 0;
  790.         
  791.         var clipping:Boolean = (n) ? (textLines[n - 1].y + TextLine(textLines[n - 1]).descent > innerHeight) : false;

  792.         // Reposition each line if necessary.
  793.         // based on the horizontal and vertical alignment.
  794.         for (var i:int = 0; i < n; i++)
  795.         {
  796.             textLine = TextLine(textLines[i]);

  797.             // If textAlign is "justify" and there is more than one line,
  798.             // the last one (if we created it) gets horizontal aligned
  799.             // according to textAlignLast.
  800.             if (lastLineIsSpecial && i == n - 1)
  801.             {
  802.                 leftAligned =
  803.                     textAlignLast == "start" && direction == "ltr" ||
  804.                     textAlignLast == "end" && direction == "rtl" ||
  805.                     textAlignLast == "left" ||
  806.                     textAlignLast == "justify";
  807.                 centerAligned = textAlignLast == "center";
  808.                 rightAligned =
  809.                     textAlignLast == "start" && direction == "rtl" ||
  810.                     textAlignLast == "end" && direction == "ltr" ||
  811.                     textAlignLast == "right";
  812.             }

  813.             if (leftAligned)
  814.                 textLine.x = leftOffset;
  815.             else if (centerAligned)
  816.                 textLine.x = centerOffset - textLine.textWidth / 2;
  817.             else if (rightAligned)
  818.                 textLine.x = rightOffset - textLine.textWidth;
  819.             
  820.             if (verticalAlign == "top" || !createdAllLines || clipping)
  821.             {
  822.                 textLine.y += topOffset;
  823.             }
  824.             else if (verticalAlign == "middle")
  825.             {
  826.                 textLine.y += middleOffset;
  827.             }
  828.             else if (verticalAlign == "bottom")
  829.             {
  830.                 textLine.y += bottomOffset;
  831.             }
  832.             else if (verticalAlign == "justify")
  833.             {
  834.                 // Determine the natural baseline position for this line.
  835.                 // Note: The y coordinate of a TextLine is the location
  836.                 // of its baseline, not of its top.
  837.                 y += i == 0 ?
  838.                      topOffset + textLine.ascent :
  839.                      previousTextLine.descent + leading + textLine.ascent;
  840.             
  841.                 textLine.y = y;
  842.                 previousTextLine = textLine;
  843.             }

  844.             // Upper left corner of bounding box may not be 0,0 after
  845.             // styles are applied or rounding error from minY calculation.
  846.             // y is one decimal place and ascent isn't rounded so minY can be
  847.             // slightly less than zero.
  848.             minX = Math.min(minX, textLine.x);
  849.             minY = Math.min(minY, textLine.y - textLine.ascent);
  850.             maxX = Math.max(maxX, textLine.x + textLine.textWidth);
  851.         }

  852.         bounds.x = minX - paddingLeft;
  853.         bounds.y = minY - paddingTop;
  854.         bounds.right = maxX + paddingRight;
  855.         bounds.bottom = textLine.y + textLine.descent + paddingBottom;
  856.         
  857.         return createdAllLines;
  858.     }
  859.  
  860.     /**
  861.      * @private
  862.      * Create textLine using paragraph terminator "\u2029" so it creates one
  863.      * text line that we can use to get the baseline. The height is
  864.      * important if the text is vertically aligned.
  865.      */
  866.     override mx_internal function createEmptyTextLine(height:Number=NaN):void
  867.     {
  868.         staticTextElement.text = "\u2029";
  869.         
  870.         bounds.width = NaN;
  871.         bounds.height = height;
  872.         
  873.         createTextLinesFromTextBlock(staticTextBlock, textLines, bounds);
  874.         
  875.         releaseLinesFromTextBlock();
  876.     }
  877.     
  878.     /**
  879.      * @private
  880.      * Determines if the composed text fits in the given height and
  881.      * line count limit.
  882.      */
  883.     private function doesComposedTextFit(height:Number, width:Number,
  884.                                          createdAllLines:Boolean,
  885.                                          lineCountLimit:int, lineBreak:String):Boolean
  886.     {
  887.         // Not all text composed because it didn't fit within bounds.
  888.         if (!createdAllLines)
  889.             return false;
  890.                     
  891.         // More text lines than allowed lines.
  892.         if (lineCountLimit != -1 && textLines.length > lineCountLimit)
  893.             return false;
  894.         
  895.         if (lineBreak == "explicit")
  896.         {
  897.             // if explicit, if the right edge of any lines go outside the
  898.             // desired width
  899.             if (bounds.right > width)
  900.                 return false;
  901.         }

  902.         // No lines or one line or no height restriction. We don't truncate away
  903.         // the one and only line just because height is too small. Clipping
  904.         // will take care of it later
  905.         if (textLines.length <= 1 || isNaN(height))
  906.             return true;
  907.                                              
  908.         // Does the bottom of the last line fall within the bounds?
  909.         var lastLine:TextLine = TextLine(textLines[textLines.length - 1]);
  910.         var lastLineExtent:Number = lastLine.y + lastLine.descent;
  911.         
  912.         return lastLineExtent <= height;
  913.     }

  914.     /**
  915.      * @private
  916.      * width and height are the ones used to do the compose, not the measured
  917.      * results resulting from the compose.
  918.      *
  919.      * Adapted from justification code in TLF's
  920.      * TextLineFactory.textLinesFromString().
  921.      */
  922.     private function truncateText(width:Number, height:Number, lineBreak:String):void
  923.     {
  924.         var lineCountLimit:int = maxDisplayedLines;
  925.         var somethingFit:Boolean = false;
  926.         var truncLineIndex:int = 0;

  927.         if (lineBreak == "explicit")
  928.         {
  929.             truncateExplicitLineBreakText(width, height);
  930.             return;
  931.         }

  932.         // Compute the truncation line.
  933.         truncLineIndex = computeLastAllowedLineIndex(height, lineCountLimit);
  934.         
  935.         // Add extra line in case we wordwrapped some characters
  936.         // onto extra lines. If we truncate in the middle of the last word
  937.         // it may then fit on the line above.
  938.         var extraLine:Boolean;
  939.         if (truncLineIndex + 1 < textLines.length)
  940.         {
  941.             truncLineIndex++;
  942.             extraLine = true;
  943.         }
  944.                                              
  945.         if (truncLineIndex >= 0)
  946.         {
  947.             // Estimate the initial truncation position using the following
  948.             // steps.
  949.             
  950.             // 1. Measure the space that the truncation indicator will take
  951.             // by composing the truncation resource using the same bounds
  952.             // and formats. The measured indicator lines could be cached but
  953.             // as well as being dependent on the indicator string, they are
  954.             // dependent on the given width.
  955.             staticTextElement.text = truncationIndicatorResource;
  956.             var indicatorLines:Vector.<DisplayObject> =
  957.                 new Vector.<DisplayObject>();
  958.             var indicatorBounds:Rectangle = new Rectangle(0, 0, width, NaN);
  959.     
  960.             var indicatorFits:Boolean = createTextLinesFromTextBlock(staticTextBlock,
  961.                                                                      indicatorLines,
  962.                                                                      indicatorBounds);
  963.                                                
  964.             releaseLinesFromTextBlock();
  965.                                                                                                          
  966.             // 2. Move target line for truncation higher by as many lines
  967.             // as the number of full lines taken by the truncation
  968.             // indicator. Indicator should also be able to fit.
  969.             truncLineIndex -= (indicatorLines.length - 1);
  970.             if (truncLineIndex >= 0 && indicatorFits)
  971.             {
  972.                 // 3. Calculate allowed width (width left over from the
  973.                 // last line of the truncation indicator).
  974.                 var measuredTextLine:TextLine =
  975.                     TextLine(indicatorLines[indicatorLines.length - 1]);
  976.                 var allowedWidth:Number =
  977.                     measuredTextLine.specifiedWidth -
  978.                     measuredTextLine.unjustifiedTextWidth;
  979.                                         
  980.                 measuredTextLine = null;
  981.                 releaseTextLines(indicatorLines);
  982.                                                         
  983.                 // 4. Get the initial truncation position on the target
  984.                 // line given this allowed width.
  985.                 var truncateAtCharPosition:int = getTruncationPosition(
  986.                     TextLine(textLines[truncLineIndex]), allowedWidth, extraLine);

  987.                 // The following loop executes repeatedly composing text until
  988.                 // it fits. In each iteration, an atoms's worth of characters
  989.                 // of original content is dropped
  990.                 do
  991.                 {
  992.                     // Replace all content starting at the inital truncation
  993.                     // position with the truncation indicator.
  994.                     var truncText:String = text.slice(0, truncateAtCharPosition) +
  995.                                            truncationIndicatorResource;

  996.                     // (Re)-initialize bounds for next compose.
  997.                     bounds.x = 0;
  998.                     bounds.y = 0;
  999.                     bounds.width = width;
  1000.                     bounds.height = height;
  1001.                                                                                     
  1002.                     staticTextElement.text = truncText;
  1003.                     
  1004.                     var createdAllLines:Boolean = createTextLinesFromTextBlock(
  1005.                         staticTextBlock, textLines, bounds);
  1006.         
  1007.                     if (doesComposedTextFit(height, width,
  1008.                                             createdAllLines,
  1009.                                             lineCountLimit, lineBreak))

  1010.                     {
  1011.                         somethingFit = true;
  1012.                         break;
  1013.                     }
  1014.                     
  1015.                      // No original content left to make room for
  1016.                      // truncation indicator.
  1017.                     if (truncateAtCharPosition == 0)
  1018.                         break;
  1019.                     
  1020.                     // Try again by truncating at the beginning of the
  1021.                     // preceding atom.
  1022.                     var oldCharPosition:int = truncateAtCharPosition;
  1023.                     truncateAtCharPosition = getNextTruncationPosition(
  1024.                         truncLineIndex, truncateAtCharPosition);
  1025.                     // check to see if we've run out of chars
  1026.                     if (oldCharPosition == truncateAtCharPosition)
  1027.                         break;
  1028.                 }
  1029.                 while (true);
  1030.             }
  1031.         }

  1032.         // If nothing fit, return no lines and bounds that just contains
  1033.         // padding.
  1034.         if (!somethingFit)
  1035.         {
  1036.             releaseTextLines();

  1037.             var paddingBottom:Number = getStyle("paddingBottom");
  1038.             var paddingLeft:Number = getStyle("paddingLeft");
  1039.             var paddingRight:Number = getStyle("paddingRight");
  1040.             var paddingTop:Number = getStyle("paddingTop");
  1041.             
  1042.             bounds.x = 0;
  1043.             bounds.y = 0;
  1044.             bounds.width = paddingLeft + paddingRight;
  1045.             bounds.height = paddingTop + paddingBottom;
  1046.         }
  1047.         
  1048.         // The text was truncated.
  1049.         setIsTruncated(true);
  1050.     }
  1051.         
  1052.     /**
  1053.      * @private
  1054.      * width and height are the ones used to do the compose, not the measured
  1055.      * results resulting from the compose.
  1056.      */
  1057.     private function truncateExplicitLineBreakText(width:Number, height:Number):void
  1058.     {
  1059.         // 1. Measure the space that the truncation indicator will take
  1060.         // by composing the truncation resource using the same bounds
  1061.         // and formats. The measured indicator lines could be cached but
  1062.         // as well as being dependent on the indicator string, they are
  1063.         // dependent on the given width.
  1064.         staticTextElement.text = truncationIndicatorResource;
  1065.         var indicatorLines:Vector.<DisplayObject> =
  1066.             new Vector.<DisplayObject>();
  1067.         var indicatorBounds:Rectangle = new Rectangle(0, 0, width, NaN);

  1068.         createTextLinesFromTextBlock(staticTextBlock,
  1069.                                      indicatorLines,
  1070.                                      indicatorBounds);
  1071.                                            
  1072.         releaseLinesFromTextBlock();

  1073.         // check each line to see if it needs truncation
  1074.         var n:int = textLines.length;
  1075.         for (var i:int = 0; i < n; i++)
  1076.         {
  1077.             var line:TextLine = textLines[i] as TextLine;
  1078.             // if the line is wider than bounds or off the left side
  1079.             // TODO (aharui): What if text runs off left side because of
  1080.             // alignment or direction?
  1081.             if ((line.x + line.width) > width)
  1082.             {
  1083.                 // clip this line
  1084.                 var lineLength:int = line.rawTextLength;
  1085.                 // start chopping from the end until it fits
  1086.                 while (--lineLength > 0)
  1087.                 {
  1088.                     var lineStr:String = text.substr(line.textBlockBeginIndex, lineLength);
  1089.                     lineStr += truncationIndicatorResource;
  1090.                     staticTextElement.text = lineStr;
  1091.                     var clippedLines:Vector.<DisplayObject> =
  1092.                         new Vector.<DisplayObject>();

  1093.                     createTextLinesFromTextBlock(staticTextBlock,
  1094.                                                  clippedLines,
  1095.                                                  indicatorBounds);
  1096.                                            
  1097.                     releaseLinesFromTextBlock();
  1098.                     if (clippedLines.length == 1 &&
  1099.                         (clippedLines[0].x + clippedLines[0].width) <= width)
  1100.                     {
  1101.                         // replace with the clipped line
  1102.                         clippedLines[0].x = line.x;
  1103.                         clippedLines[0].y = line.y;
  1104.                         textLines[i] = clippedLines[0];
  1105.                         break;
  1106.                     }
  1107.                     
  1108.                 }
  1109.             }
  1110.         }
  1111.     }

  1112.     /**
  1113.      * @private
  1114.      * Calculates the last line that fits in the given height and line count
  1115.      * limit.
  1116.      */
  1117.     private function computeLastAllowedLineIndex(height:Number,
  1118.                                                  lineCountLimit:int):int
  1119.     {
  1120.         var truncationLineIndex:int = textLines.length - 1;
  1121.         // return -1 if no textLines (usually because zero size)
  1122.         if (truncationLineIndex < 0)
  1123.             return truncationLineIndex;
  1124.         
  1125.         if (!isNaN(height))
  1126.         {
  1127.             // Search in reverse order since truncation near the end is the
  1128.             // more common use case.
  1129.             do
  1130.             {
  1131.                 var textLine:TextLine = TextLine(textLines[truncationLineIndex]);
  1132.                 if (textLine.y + textLine.descent <= height)
  1133.                     break;
  1134.                                 
  1135.                 truncationLineIndex--;
  1136.             }
  1137.             while (truncationLineIndex >= 0);
  1138.         }
  1139.     
  1140.         // if line count limit is smaller, use that
  1141.         if (lineCountLimit != -1 && lineCountLimit <= truncationLineIndex)
  1142.             truncationLineIndex = lineCountLimit - 1;
  1143.             
  1144.         return truncationLineIndex;
  1145.     }

  1146.     /**
  1147.      * @private
  1148.      * Gets the initial truncation position on a line.
  1149.      *
  1150.      * If there is an extra line, start at the first word boundary since
  1151.      * truncating characters in this word may make it fit on the line above.
  1152.      *
  1153.      * If there is not an extra line, start at the allowed width.
  1154.      *
  1155.      * - Must be at an atom boundary.
  1156.      * - Must scan the line for atoms in logical order, not physical position
  1157.      * order.
  1158.      * For example, given bi-di text AB讗讘CD
  1159.      * atoms must be scanned in this order:
  1160.      * A, B, 讗
  1161.      * 讙, C, D
  1162.      */
  1163.     private function getTruncationPosition(line:TextLine,
  1164.                                            allowedWidth:Number,
  1165.                                            extraLine:Boolean):int
  1166.     {
  1167.         var consumedWidth:Number = 0;
  1168.         var charPosition:int = line.textBlockBeginIndex;
  1169.         
  1170.         while (charPosition < line.textBlockBeginIndex + line.rawTextLength)
  1171.         {
  1172.             var atomIndex:int = line.getAtomIndexAtCharIndex(charPosition);
  1173.             if (extraLine)
  1174.             {
  1175.                 // Skip the initial word boundary.
  1176.                 if (charPosition != line.textBlockBeginIndex &&
  1177.                     line.getAtomWordBoundaryOnLeft(atomIndex))
  1178.                 {
  1179.                     break;
  1180.                 }
  1181.             }
  1182.             else
  1183.             {
  1184.                 var atomBounds:Rectangle = line.getAtomBounds(atomIndex);
  1185.                 consumedWidth += atomBounds.width;
  1186.                 if (consumedWidth > allowedWidth)
  1187.                     break;
  1188.             }

  1189.             charPosition = line.getAtomTextBlockEndIndex(atomIndex);
  1190.         }
  1191.                 
  1192.         return charPosition;
  1193.     }
  1194.         
  1195.     /**
  1196.      * @private
  1197.      * Gets the next truncation position by shedding an atom's worth of
  1198.      * characters.
  1199.      */
  1200.     private function getNextTruncationPosition(truncationLineIndex:int,
  1201.                                                truncateAtCharPosition:int):int
  1202.     {
  1203.         // 1. Get the position of the last character of the preceding atom
  1204.         // truncateAtCharPosition-1, because truncateAtCharPosition is an
  1205.         // atom boundary.
  1206.         truncateAtCharPosition--;
  1207.         
  1208.         // 2. Find the new target line (i.e., the line that has the new
  1209.         // truncation position). If the last truncation position was at the
  1210.         // beginning of the target line, the new position may have moved to a
  1211.         // previous line. It is also possible for this position to be found
  1212.         // in the next line because the truncation indicator may have combined
  1213.         // with original content to form a word that may not have afforded a
  1214.         // suitable break opportunity. In any case, the new truncation
  1215.         // position lies in the vicinity of the previous target line, so a
  1216.         // linear search suffices.
  1217.         var line:TextLine = TextLine(textLines[truncationLineIndex]);
  1218.         do
  1219.         {
  1220.             if (truncateAtCharPosition >= line.textBlockBeginIndex &&
  1221.                 truncateAtCharPosition < line.textBlockBeginIndex + line.rawTextLength)
  1222.             {
  1223.                 break;
  1224.             }
  1225.             
  1226.             if (truncateAtCharPosition < line.textBlockBeginIndex)
  1227.             {
  1228.                 truncationLineIndex--;
  1229.                 // if we run out of chars, just return the same
  1230.                 // position to warn the caller to stop
  1231.                 if (truncationLineIndex < 0)
  1232.                     return truncateAtCharPosition;
  1233.             }
  1234.             else
  1235.             {
  1236.                 truncationLineIndex++;
  1237.                 // if we run out of chars, just return the same
  1238.                 // position to warn the caller to stop
  1239.                 if (truncationLineIndex >= textLines.length)
  1240.                     return truncateAtCharPosition;
  1241.             }
  1242.                 
  1243.             line = TextLine(textLines[truncationLineIndex]);
  1244.         }
  1245.         while (true);

  1246.         // 3. Get the line atom index at this position
  1247.         var atomIndex:int =
  1248.                         line.getAtomIndexAtCharIndex(truncateAtCharPosition);
  1249.         
  1250.         // 4. Get the char index for this atom index
  1251.         var nextTruncationPosition:int =
  1252.                         line.getAtomTextBlockBeginIndex(atomIndex);
  1253.                 
  1254.         return nextTruncationPosition;
  1255.     }
  1256.     
  1257.     /**
  1258.      * @private
  1259.      * Cleans up and sets the validity of the lines associated
  1260.      * with the TextBlock to TextLineValidity.INVALID.
  1261.      */
  1262.     private function releaseLinesFromTextBlock():void
  1263.     {
  1264.         var firstLine:TextLine = staticTextBlock.firstLine;
  1265.         var lastLine:TextLine = staticTextBlock.lastLine;
  1266.         
  1267.         if (firstLine)
  1268.             staticTextBlock.releaseLines(firstLine, lastLine);
  1269.      }
  1270. }

  1271. }


参考文献
1.http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/frameworks/projects/spark/src/spark/components/Label.as
2.http://www.fising.cn/docs/AS3_Reference/spark/components/Label.html#commonStyleSummary
阅读(3595) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~