//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package flashx.textLayout.elements { import flash.display.Shape; import flash.text.engine.ContentElement; import flash.text.engine.EastAsianJustifier; import flash.text.engine.GroupElement; import flash.text.engine.LineJustification; import flash.text.engine.SpaceJustifier; import flash.text.engine.TabStop; import flash.text.engine.TextBaseline; import flash.text.engine.TextBlock; import flash.text.engine.TextLine; import flash.text.engine.TextLineValidity; import flash.text.engine.TextRotation; import flash.utils.getQualifiedClassName; import flashx.textLayout.compose.TextFlowLine; import flashx.textLayout.debug.Debugging; import flashx.textLayout.debug.assert; import flashx.textLayout.formats.BlockProgression; import flashx.textLayout.formats.Direction; import flashx.textLayout.formats.FormatValue; import flashx.textLayout.formats.ITextLayoutFormat; import flashx.textLayout.formats.JustificationRule; import flashx.textLayout.formats.LeadingModel; import flashx.textLayout.formats.LineBreak; import flashx.textLayout.formats.TabStopFormat; import flashx.textLayout.formats.TextAlign; import flashx.textLayout.formats.TextJustify; import flashx.textLayout.formats.TextLayoutFormat; import flashx.textLayout.tlf_internal; import flashx.textLayout.utils.CharacterUtil; import flashx.textLayout.utils.LocaleUtil; use namespace tlf_internal; /** * The ParagraphElement class represents a paragraph in the text flow hierarchy. Its parent * is a ParagraphFormattedElement, and its children can include spans (SpanElement), images * (inLineGraphicElement), links (LinkElement) and TCY (Tatechuuyoko - ta-tae-chu-yo-ko) elements (TCYElement). The * paragraph text is stored in one or more SpanElement objects, which define ranges of text that share the same attributes. * A TCYElement object defines a small run of Japanese text that runs perpendicular to the line, as in a horizontal run of text in a * vertical line. A TCYElement can also contain multiple spans. * * @includeExample examples\ParagraphElementExample.as -noswf * @includeExample examples\ParagraphElementExample2.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see InlineGraphicElement * @see LinkElement * @see SpanElement * @see TCYElement * @see TextFlow */ public final class ParagraphElement extends ParagraphFormattedElement { private var _textBlock:TextBlock; /** Constructor - represents a paragraph in a text flow. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function ParagraphElement() { super(); } /** @private */ public override function shallowCopy(startPos:int = 0, endPos:int = -1):FlowElement { var retFlow:ParagraphElement = super.shallowCopy(startPos, endPos) as ParagraphElement; if (_textBlock) retFlow.createTextBlock(); return retFlow; } /** @private */ tlf_internal function createTextBlock():void { CONFIG::debug { assert(_textBlock == null,"createTextBlock called when there is already a textblock"); } _textBlock = new TextBlock(); CONFIG::debug { Debugging.traceFTECall(_textBlock,null,"new TextBlock()"); } for (var i:int = 0; i < numChildren; i++) { var child:FlowElement = getChildAt(i); child.createContentElement(); } updateTextBlock(); } /** @private */ tlf_internal function releaseTextBlock():void { if (!_textBlock) return; // Preflight children to see if any are in use for (var i:int = 0; i < numChildren; i++) { var child:FlowElement = getChildAt(i); if (!child.canReleaseContentElement()) return; } if (_textBlock.firstLine) // A TextBlock may have no firstLine if it has previously been released. { for (var textLineTest:TextLine = _textBlock.firstLine; textLineTest != null; textLineTest = textLineTest.nextLine) { if(textLineTest.numChildren != 0) { //if the number of adornments added does not match the number of children on the textLine //then a third party has added adornments. Don't recycle the line or the adornment will be //lost. var tfl:TextFlowLine = textLineTest.userData as TextFlowLine; if(tfl.adornCount != textLineTest.numChildren) return; } } for (var textLine:TextLine = _textBlock.firstLine; textLine != null; textLine = textLine.nextLine) TextFlowLine(textLine.userData).markReleased(); CONFIG::debug { Debugging.traceFTECall(null,_textBlock,"releaseLines",_textBlock.firstLine, _textBlock.lastLine); } _textBlock.releaseLines(_textBlock.firstLine, _textBlock.lastLine); } _textBlock.content = null; for (i = 0; i < numChildren; i++) { child = getChildAt(i); child.releaseContentElement(); } _textBlock = null; } /** TextBlock where the text of the paragraph is kept. @private */ tlf_internal function getTextBlock():TextBlock { if (!_textBlock) createTextBlock(); return _textBlock; } /** @private */ tlf_internal function releaseLineCreationData():void { CONFIG::debug { assert(Configuration.playerEnablesArgoFeatures,"bad call to releaseLineCreationData"); } if (_textBlock) _textBlock["releaseLineCreationData"](); } /** @private */ tlf_internal override function createContentAsGroup():GroupElement { var group:GroupElement = _textBlock.content as GroupElement; if (!group) { var originalContent:ContentElement = _textBlock.content; group = new GroupElement(); CONFIG::debug { Debugging.traceFTECall(group,null,"new GroupElement()"); } _textBlock.content = group; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",group); } if (originalContent) { var gc:Vector. = new Vector.(); CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.()") } gc.push(originalContent); CONFIG::debug { Debugging.traceFTECall(null,gc,"push",originalContent); } group.replaceElements(0,0,gc); CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",0,0,gc); } } // Now we've got to force damage the entire paragraph, because we restructured it in FTE. if (_textBlock.firstLine && textLength) { var textFlow:TextFlow = getTextFlow(); if (textFlow) textFlow.damage(getAbsoluteStart(), textLength, TextLineValidity.INVALID, false); } } return group; } /** @private */ tlf_internal override function removeBlockElement(child:FlowElement, block:ContentElement):void { if (numChildren == 1) { if (block is GroupElement) { // see insertBlockElement CONFIG::debug { assert(_textBlock.content != block,"removeBlockElement: bad call to removeBlockElement"); } CONFIG::debug { assert(_textBlock.content is GroupElement,"removeBlockElement: bad content"); } CONFIG::debug { assert(GroupElement(_textBlock.content).elementCount == 1,"removeBlockElement: bad element count"); } CONFIG::debug { assert(GroupElement(_textBlock.content).getElementAt(0) == block,"removeBlockElement: bad group content"); } GroupElement(_textBlock.content).replaceElements(0,1,null); CONFIG::debug { Debugging.traceFTECall(null,_textBlock.content,"replaceElements",0,1,null); } } _textBlock.content = null; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",null); } } else { var idx:int = this.getChildIndex(child); var group:GroupElement = GroupElement(_textBlock.content); CONFIG::debug { assert(group.elementCount == numChildren,"Mismatched group and elementCount"); } group.replaceElements(idx,idx+1,null); CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",idx,idx+1,null); } if (numChildren == 2) // its going to be one so ungroup { // ungroup - need to take it out first as inlinelements can only have one parent var elem:ContentElement = group.getElementAt(0); CONFIG::debug { Debugging.traceFTECall(elem,group,"getElementAt",0); } if (!(elem is GroupElement)) { group.replaceElements(0,1,null); CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",0,1,null); } _textBlock.content = elem; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",elem); } } } } } /** @private */ tlf_internal override function hasBlockElement():Boolean { return _textBlock != null; } /** @private */ override tlf_internal function createContentElement():void { createTextBlock(); } /** @private */ tlf_internal override function insertBlockElement(child:FlowElement, block:ContentElement):void { if (_textBlock == null) { child.releaseContentElement(); createTextBlock(); // does the whole tree return; } var gc:Vector.; // scratch var var group:GroupElement; // scratch if (numChildren == 1) { if (block is GroupElement) { // this case forces the Group to be in a Group so that following FlowLeafElements aren't in a SubParagraphGroupElement's group gc = new Vector.(); CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.()") } gc.push(block); CONFIG::debug { Debugging.traceFTECall(null,gc,"push",block); } group = new GroupElement(gc); CONFIG::debug { Debugging.traceFTECall(group,null,"new GroupElement",gc); } _textBlock.content = group; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",group); } } else { _textBlock.content = block; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"content",block); } } } else { group = createContentAsGroup(); var idx:int = this.getChildIndex(child); gc = new Vector.(); CONFIG::debug { Debugging.traceFTECall(gc,null,"new Vector.") } gc.push(block); CONFIG::debug { Debugging.traceFTECall(null,gc,"push",block); } group.replaceElements(idx,idx,gc); CONFIG::debug { Debugging.traceFTECall(null,group,"replaceElements",idx,idx,gc); } } } /** @private */ override protected function get abstract():Boolean { return false; } /** @private */ public override function replaceChildren(beginChildIndex:int,endChildIndex:int,...rest):void { // are we replacing the last element? var oldLastLeaf:FlowLeafElement = getLastLeaf(); CONFIG::debug { if (oldLastLeaf && (oldLastLeaf is SpanElement)) SpanElement(oldLastLeaf).verifyParagraphTerminator(); } var applyParams:Array; // makes a measurable difference - rest.length zero and one are the common cases if (rest.length == 1) applyParams = [beginChildIndex, endChildIndex, rest[0]]; else { applyParams = [beginChildIndex, endChildIndex]; if (rest.length != 0) applyParams = applyParams.concat.apply(applyParams, rest); } super.replaceChildren.apply(this, applyParams); ensureTerminatorAfterReplace(oldLastLeaf); } /** @private */ tlf_internal function ensureTerminatorAfterReplace(oldLastLeaf:FlowLeafElement):void { var newLastLeaf:FlowLeafElement = getLastLeaf(); if (oldLastLeaf != newLastLeaf) { if (oldLastLeaf && oldLastLeaf is SpanElement) oldLastLeaf.removeParaTerminator(); if (newLastLeaf) { if (newLastLeaf is SpanElement) newLastLeaf.addParaTerminator(); else { var s:SpanElement = new SpanElement(); super.replaceChildren(numChildren,numChildren,s); s.format = newLastLeaf.format; s.addParaTerminator(); } } } } [RichTextContent] /** @private NOTE: all FlowElement implementers and overrides of mxmlChildren must specify [RichTextContent] metadata */ public override function set mxmlChildren(array:Array):void { // remove all existing children replaceChildren(0,numChildren); for each (var child:Object in array) { if (child is FlowElement) { if ((child is SpanElement) || (child is SubParagraphGroupElement)) child.bindableElement = true; // Note: calling super.replaceChildren because we don't want to transfer para terminator each time super.replaceChildren(numChildren, numChildren, child as FlowElement); } else if (child is String) { var s:SpanElement = new SpanElement(); s.text = String(child); s.bindableElement = true; // Note: calling super.replaceChildren because we don't want to transfer para terminator each time super.replaceChildren(numChildren, numChildren, s); } else if (child != null) throw new TypeError(GlobalSettings.resourceStringFunction("badMXMLChildrenArgument",[ getQualifiedClassName(child) ])); } // Now ensure para terminator ensureTerminatorAfterReplace(null); } /** @private */ public override function getText(relativeStart:int=0, relativeEnd:int=-1, paragraphSeparator:String="\n"):String { // Optimization for getting text of the entire paragraph if (relativeStart == 0 && (relativeEnd == -1 || relativeEnd >= textLength-1) && _textBlock) { if (_textBlock.content) { var text:String = _textBlock.content.rawText; return text.substring(0, text.length - 1); } return ""; // content is null } return super.getText(relativeStart, relativeEnd, paragraphSeparator); } /** Returns the paragraph that follows this one, or null if there are no more paragraphs. * * @return the next paragraph or null if there are no more paragraphs. * * @includeExample examples\ParagraphElement_getNextParagraph.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see #getPreviousParagraph() */ public function getNextParagraph():ParagraphElement { var nextLeaf:FlowLeafElement = getLastLeaf().getNextLeaf(); return nextLeaf ? nextLeaf.getParagraph() : null; } /** Returns the paragraph that precedes this one, or null, if this paragraph is the first one * in the TextFlow. * * @includeExample examples\ParagraphElement_getPreviousParagraph.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see #getNextParagraph() */ public function getPreviousParagraph():ParagraphElement { var previousLeaf:FlowLeafElement = getFirstLeaf().getPreviousLeaf(); return previousLeaf ? previousLeaf.getParagraph() : null; } /** * Scans backward from the supplied position to find the location * in the text of the previous atom and returns the index. The term atom refers to * both graphic elements and characters (including groups of combining characters), the * indivisible entities that make up a text line. * * @param relativePosition position in the text to start from, counting from 0 * @return index in the text of the previous cluster * * @includeExample examples\ParagraphElement_findPreviousAtomBoundary.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see flash.text.engine.TextLine */ public function findPreviousAtomBoundary(relativePosition:int):int { return getTextBlock().findPreviousAtomBoundary(relativePosition); } /** * Scans ahead from the supplied position to find the location * in the text of the next atom and returns the index. The term atom refers to * both graphic elements and characters (including groups of combining characters), the * indivisible entities that make up a text line. * * @param relativePosition position in the text to start from, counting from 0 * @return index in the text of the following atom * * @includeExample examples\ParagraphElement_findNextAtomBoundary.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see flash.text.engine.TextLine */ public function findNextAtomBoundary(relativePosition:int):int { return getTextBlock().findNextAtomBoundary(relativePosition); } /** @private */ public override function getCharAtPosition(relativePosition:int):String { return getTextBlock().content.rawText.charAt(relativePosition); } /** * Returns the index of the previous word boundary in the text. * *

Scans backward from the supplied position to find the previous position * in the text that starts or ends a word.

* * @param relativePosition position in the text to start from, counting from 0 * @return index in the text of the previous word boundary * * @includeExample examples\ParagraphElement_findPreviousWordBoundary.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function findPreviousWordBoundary(relativePosition:int):int { if (relativePosition == 0) return 0; var prevCharCode:int = getCharCodeAtPosition(relativePosition - 1); if (CharacterUtil.isWhitespace(prevCharCode)) { while (CharacterUtil.isWhitespace(prevCharCode) && ((relativePosition - 1) > 0)) { relativePosition--; prevCharCode = getCharCodeAtPosition(relativePosition - 1); } return relativePosition; } return getTextBlock().findPreviousWordBoundary(relativePosition); } /** * Returns the index of the next word boundary in the text. * *

Scans ahead from the supplied position to find the next position * in the text that starts or ends a word.

* * @param relativePosition position in the text to start from, counting from 0 * @return index in the text of the next word boundary * * @includeExample examples\ParagraphElement_findNextWordBoundary.as -noswf * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function findNextWordBoundary(relativePosition:int):int { if (relativePosition == textLength) return textLength; var curCharCode:int = getCharCodeAtPosition(relativePosition); if (CharacterUtil.isWhitespace(curCharCode)) { while (CharacterUtil.isWhitespace(curCharCode) && relativePosition < (textLength - 1)) { relativePosition++; curCharCode = getCharCodeAtPosition(relativePosition); } return relativePosition; } return getTextBlock().findNextWordBoundary(relativePosition); } private static var _defaultTabStops:Vector.; private const defaultTabWidth:int = 48; // matches default tabs setting in Argo private const defaultTabCount:int = 20; private function initializeDefaultTabStops():void { var lastTabPos:int = defaultTabWidth * defaultTabCount; _defaultTabStops = new Vector.(defaultTabCount, true); for (var i:int = 0; i < defaultTabCount; ++i) _defaultTabStops[i] = new TabStop(TextAlign.START, defaultTabWidth * i); } private function updateTextBlock():void { // find the ancestor with a container and use its format for various settings var containerElement:ContainerFormattedElement = getAncestorWithContainer(); if (!containerElement) return; var containerElementFormat:ITextLayoutFormat = containerElement ? containerElement.computedFormat : TextLayoutFormat.defaultFormat; var lineJust:String; if (computedFormat.textAlign == TextAlign.JUSTIFY) { lineJust = (_computedFormat.textAlignLast == TextAlign.JUSTIFY) ? LineJustification.ALL_INCLUDING_LAST : LineJustification.ALL_BUT_LAST; // We don't allow explicit line breaks and justification together because it results in very strange (invisible) lines if (containerElementFormat.lineBreak == LineBreak.EXPLICIT) lineJust = LineJustification.UNJUSTIFIED; } else lineJust = LineJustification.UNJUSTIFIED; var makeJustRuleStyle:String = this.getEffectiveJustificationStyle(); var justRule:String = this.getEffectiveJustificationRule(); // set the justifier in the TextBlock if (justRule == JustificationRule.SPACE) { var spaceJustifier:SpaceJustifier = new SpaceJustifier(_computedFormat.locale,lineJust,false); spaceJustifier.letterSpacing = _computedFormat.textJustify == TextJustify.DISTRIBUTE ? true : false; CONFIG::debug { Debugging.traceFTECall(spaceJustifier,null,"new SpaceJustifier",_computedFormat.locale,lineJust,false); } _textBlock.textJustifier = spaceJustifier; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"textJustifier",spaceJustifier); } _textBlock.baselineZero = getLeadingBasis(this.getEffectiveLeadingModel()); CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"baselineZero",_textBlock.baselineZero); } } else { var eastAsianJustifier:EastAsianJustifier = new EastAsianJustifier(_computedFormat.locale,lineJust, makeJustRuleStyle); CONFIG::debug { Debugging.traceFTECall(eastAsianJustifier,null,"new EastAsianJustifier",_computedFormat.locale,lineJust,makeJustRuleStyle); } _textBlock.textJustifier = eastAsianJustifier; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"textJustifier",eastAsianJustifier); } _textBlock.baselineZero = getLeadingBasis(this.getEffectiveLeadingModel()); CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"baselineZero",_textBlock.baselineZero); } } _textBlock.bidiLevel = _computedFormat.direction == Direction.LTR ? 0 : 1; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"bidiLevel",_textBlock.bidiLevel); } _textBlock.lineRotation = containerElementFormat.blockProgression == BlockProgression.RL ? TextRotation.ROTATE_90 : TextRotation.ROTATE_0; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"lineRotation",_textBlock.lineRotation); } if (_computedFormat.tabStops && _computedFormat.tabStops.length != 0) { //create a vector of TabStops and assign it to tabStops in _textBlock var tabStops:Vector. = new Vector.(); CONFIG::debug { Debugging.traceFTECall(tabStops,null,"new Vector.()"); } for each(var tsa:TabStopFormat in _computedFormat.tabStops) { var token:String = tsa.decimalAlignmentToken==null ? "" : tsa.decimalAlignmentToken; var tabStop:TabStop = new TabStop(tsa.alignment,Number(tsa.position),token); // this next line when uncommented works around bug 1912782 if (tsa.decimalAlignmentToken != null) var garbage:String = "x" + tabStop.decimalAlignmentToken; CONFIG::debug { Debugging.traceFTECall(tabStop,null,"new TabStop",tabStop.alignment,tabStop.position,tabStop.decimalAlignmentToken); } tabStops.push(tabStop); CONFIG::debug { Debugging.traceFTECall(null,tabStops,"push",tabStop); } } _textBlock.tabStops = tabStops; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"tabStops",tabStops); } } else if (GlobalSettings.enableDefaultTabStops && !Configuration.playerEnablesArgoFeatures) { // Player versions prior to 10.1 do not set up any default tabStops. As a workaround, if enableDefaultTabs // is true, TLF will set up default tabStops in the case where there are no tabs defined. if (_defaultTabStops == null) initializeDefaultTabStops(); _textBlock.tabStops = _defaultTabStops; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"tabStops",_defaultTabStops); } } else { _textBlock.tabStops = null; CONFIG::debug { Debugging.traceFTEAssign(_textBlock,"tabStops",null); } } } /** @private */ public override function get computedFormat():ITextLayoutFormat { if (!_computedFormat) { super.computedFormat; if (_textBlock) updateTextBlock(); } return _computedFormat; } /** @private */ tlf_internal override function canOwnFlowElement(elem:FlowElement):Boolean { return elem is FlowLeafElement || elem is SubParagraphGroupElement; } /** @private */ tlf_internal override function normalizeRange(normalizeStart:uint,normalizeEnd:uint):void { var idx:int = findChildIndexAtPosition(normalizeStart); if (idx != -1 && idx < numChildren) { var child:FlowElement = getChildAt(idx); normalizeStart = normalizeStart-child.parentRelativeStart; CONFIG::debug { assert(normalizeStart >= 0, "bad normalizeStart in normalizeRange"); } for (;;) { // watch out for changes in the length of the child var origChildEnd:int = child.parentRelativeStart+child.textLength; child.normalizeRange(normalizeStart,normalizeEnd-child.parentRelativeStart); var newChildEnd:int = child.parentRelativeStart+child.textLength; normalizeEnd += newChildEnd-origChildEnd; // adjust // no zero length children if (child.textLength == 0 && !child.bindableElement) replaceChildren(idx,idx+1); else if (child.mergeToPreviousIfPossible()) { var prevElement:FlowElement = this.getChildAt(idx-1); // possibly optimize the start to the length of prevelement before the merge prevElement.normalizeRange(0,prevElement.textLength); } else idx++; if (idx == numChildren) { // check if last child is an empty SubPargraphBlock and remove it if (idx != 0) { var lastChild:FlowElement = this.getChildAt(idx-1); if (lastChild is SubParagraphGroupElement && lastChild.textLength == 1 && !lastChild.bindableElement) replaceChildren(idx-1,idx); } break; } // next child child = getChildAt(idx); if (child.parentRelativeStart > normalizeEnd) break; normalizeStart = 0; // for the next child } } // empty paragraphs not allowed after normalize if (numChildren == 0 || textLength == 0) { var s:SpanElement = new SpanElement(); replaceChildren(0,0,s); s.normalizeRange(0,s.textLength); } } /** @private */ tlf_internal function getEffectiveLeadingModel():String { return computedFormat.leadingModel == LeadingModel.AUTO ? LocaleUtil.leadingModel(computedFormat.locale) : computedFormat.leadingModel; } /** @private */ tlf_internal function getEffectiveDominantBaseline():String { return computedFormat.dominantBaseline == FormatValue.AUTO ? LocaleUtil.dominantBaseline(computedFormat.locale) : computedFormat.dominantBaseline; } /** @private */ tlf_internal function getEffectiveJustificationRule():String { return computedFormat.justificationRule == FormatValue.AUTO ? LocaleUtil.justificationRule(computedFormat.locale) : computedFormat.justificationRule; } /** @private */ tlf_internal function getEffectiveJustificationStyle():String { return computedFormat.justificationStyle == FormatValue.AUTO ? LocaleUtil.justificationStyle(computedFormat.locale) : computedFormat.justificationStyle; } /** @private */ CONFIG::debug public override function debugCheckFlowElement(depth:int = 0, extraData:String = ""):int { var rslt:int = super.debugCheckFlowElement(depth," fte:"+getDebugIdentity(_textBlock)+" "+extraData); // now check the character count and then the last character if (_textBlock) { var contentLength:int = _textBlock.content && _textBlock.content.rawText ? _textBlock.content.rawText.length : 0; rslt += assert(contentLength == textLength,"Bad paragraph length mode:"+textLength.toString()+" _textBlock:" + contentLength.toString()); var groupElement:GroupElement = _textBlock.content as GroupElement; if (groupElement) assert(groupElement.elementCount == numChildren,"Mismatched group and elementCount"); else if (_textBlock.content) assert(1 == numChildren,"Mismatched group and elementCount"); else assert(0 == numChildren,"Mismatched group and elementCount"); } rslt += assert(numChildren == 0 || textLength > 0,"Para must have at least one text char"); return rslt; } /** @private */ tlf_internal static function getLeadingBasis (leadingModel:String):String { switch (leadingModel) { default: CONFIG::debug { assert(false,"Unsupported parameter to ParagraphElement.getLeadingBasis"); } // In particular, AUTO is not supported by this method. Must be mapped to one of the above case LeadingModel.ASCENT_DESCENT_UP: case LeadingModel.APPROXIMATE_TEXT_FIELD: case LeadingModel.ROMAN_UP: return flash.text.engine.TextBaseline.ROMAN; case LeadingModel.IDEOGRAPHIC_TOP_UP: case LeadingModel.IDEOGRAPHIC_TOP_DOWN: return flash.text.engine.TextBaseline.IDEOGRAPHIC_TOP; case LeadingModel.IDEOGRAPHIC_CENTER_UP: case LeadingModel.IDEOGRAPHIC_CENTER_DOWN: return flash.text.engine.TextBaseline.IDEOGRAPHIC_CENTER; } } /** @private */ tlf_internal static function useUpLeadingDirection (leadingModel:String):Boolean { switch (leadingModel) { default: CONFIG::debug { assert(false,"Unsupported parameter to ParagraphElement.useUpLeadingDirection"); } // In particular, AUTO is not supported by this method. Must be mapped to one of the above case LeadingModel.ASCENT_DESCENT_UP: case LeadingModel.APPROXIMATE_TEXT_FIELD: case LeadingModel.ROMAN_UP: case LeadingModel.IDEOGRAPHIC_TOP_UP: case LeadingModel.IDEOGRAPHIC_CENTER_UP: return true; case LeadingModel.IDEOGRAPHIC_TOP_DOWN: case LeadingModel.IDEOGRAPHIC_CENTER_DOWN: return false; } } } }