| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.compose |
| { |
| import flash.geom.Rectangle; |
| import flash.text.engine.TextBaseline; |
| import flash.text.engine.TextBlock; |
| import flash.text.engine.TextLine; |
| |
| import flashx.textLayout.*; |
| import flashx.textLayout.container.ContainerController; |
| import flashx.textLayout.debug.Debugging; |
| import flashx.textLayout.debug.assert; |
| import flashx.textLayout.elements.*; |
| import flashx.textLayout.formats.*; |
| import flashx.textLayout.utils.LocaleUtil; |
| |
| use namespace tlf_internal; |
| |
| |
| [ExcludeClass] |
| /** @private Common composer base class */ |
| public class BaseCompose |
| { |
| |
| public static function get globalSWFContext():ISWFContext |
| { |
| return GlobalSWFContext.globalSWFContext; |
| } |
| |
| protected var _parcelList:IParcelList; |
| |
| /** List of areas we're composing into, matches the container's bounding box */ |
| public function get parcelList():IParcelList |
| { return _parcelList; } |
| |
| /** Element of current location */ |
| protected var _curElement:FlowLeafElement; |
| /** Absolute start position of _curElement */ |
| protected var _curElementStart:int; |
| /** Offset from element start to current location */ |
| protected var _curElementOffset:int; |
| /** ParagraphElement that contains the current location */ |
| protected var _curParaElement:ParagraphElement; |
| protected var _curParaFormat:ITextLayoutFormat; |
| /** Absolute start position of _curParaElement */ |
| protected var _curParaStart:int; |
| /** leading direction for the current line's para (set when line is being composed and committed to _lastLineLeadingModel when line is finalized) */ |
| private var _curLineLeadingModel:String = ""; |
| /** leading amount for the current line (set when line is being composed and committed to _lastLineLeading when line is finalized) */ |
| private var _curLineLeading:Number; |
| /** leading direction for the last line's para */ |
| protected var _lastLineLeadingModel:String = ""; |
| /** leading amount for the last line */ |
| protected var _lastLineLeading:Number; |
| /** descent of the last line */ |
| protected var _lastLineDescent:Number; |
| /** Amount of spaceAfter added to the previous line */ |
| protected var _spaceCarried:Number; |
| /** BlockProgression - vertical horizontal etc. @see text.formats.BlockProgression */ |
| protected var _blockProgression:String; |
| |
| /** Minimum left edge coordinate across all the parcels in a controller */ |
| private var _controllerLeft:Number; |
| /** Minimum top edge across all the parcels in a controller */ |
| private var _controllerTop:Number; |
| /** Maximum right edge coordinate across all the parcels in a controller */ |
| private var _controllerRight:Number; |
| /** Maximum bottom edge coordinate across all the parcels in a controller */ |
| private var _controllerBottom:Number; |
| |
| /** Maximum horizontal extension from left/right edge of the parcel. Alignment width for the parcel. */ |
| protected var _contentLogicalExtent:Number; |
| /* Commited extent any lines needing additional alignment must update this number */ |
| protected var _contentCommittedExtent:Number; |
| |
| /** Minimum left edge coordinate across all the parcels in a controller */ |
| protected var _parcelLeft:Number; |
| /** Minimum top edge across all the parcels in a controller */ |
| protected var _parcelTop:Number; |
| /** Maximum right edge coordinate across all the parcels in a controller */ |
| protected var _parcelRight:Number; |
| /** Maximum bottom edge coordinate across all the parcels in a controller */ |
| protected var _parcelBottom:Number; |
| |
| /** owning textFlow of current compose */ |
| protected var _textFlow:TextFlow; |
| private var _releaseLineCreationData:Boolean; |
| /** flowComposer of current compose */ |
| protected var _flowComposer:StandardFlowComposer; |
| /** rootElement of current compose */ |
| protected var _rootElement:ContainerFormattedElement; |
| /** position to stop composing at */ |
| protected var _stopComposePos:int; |
| |
| /** First damaged controller to begin composing */ |
| protected var _startController:ContainerController; |
| /** Beginning composition position. Note this gets cleared once its been passed */ |
| protected var _startComposePosition:int; |
| |
| |
| // scratch line slugs |
| static protected var _candidateLineSlug:Rectangle = new Rectangle(); |
| static protected var _lineSlug:Rectangle = new Rectangle(); |
| |
| // scratch array for holding lines awaiting alignment |
| static private var _alignLines:Array; |
| |
| /** Parcel we are composing - used for keeping track of when it changes b/c parcelList.parcel may have advanced */ |
| protected var _curParcel:Parcel; |
| |
| /** Start position of _curParcel */ |
| protected var _curParcelStart:int; |
| |
| /** Constructor. */ |
| public function BaseCompose() |
| { |
| |
| } |
| |
| protected function createParcelList():IParcelList |
| { return null; } |
| protected function releaseParcelList(list:IParcelList):void |
| { } |
| |
| /** Starting controller for skipping ahead */ |
| public function get startController():ContainerController |
| { return _startController; } |
| |
| /** prevent any leaks. @private */ |
| tlf_internal function releaseAnyReferences():void |
| { |
| _curElement = null; |
| _curParaElement = null; |
| _curParaFormat = null; |
| _flowComposer = null; |
| _parcelList = null; |
| _rootElement = null; |
| _startController = null; |
| _textFlow = null; |
| } |
| |
| /** Initialize for a composition that will compose up through the controllerEndIndex, or all the way to the end of the flow |
| * @param composer |
| * @param composeToPosition -1 means not specified. 0 means request to compose nothing, >0 specifies a position to force compose to |
| * @param controllerEndIndex index of the last controller to compose for, or -1 to compose through all controllers |
| */ |
| protected function initializeForComposer(composer:IFlowComposer, composeToPosition:int, controllerEndIndex:int):void |
| { |
| _parcelList = createParcelList(); |
| _parcelList.notifyOnParcelChange = parcelHasChanged; |
| |
| _spaceCarried = 0; |
| // TODO: just use the rootElement for table cells |
| _blockProgression = composer.rootElement.computedFormat.blockProgression; |
| // for a non-specified compose position the ParcelList handles the bail out - just set to textLength |
| _stopComposePos = composeToPosition >= 0 ? Math.min(_textFlow.textLength,composeToPosition) : _textFlow.textLength; |
| |
| // this chains through the list - tell it if a "care about" comopseToPosition was specified |
| _parcelList.beginCompose(composer, controllerEndIndex, composeToPosition > 0); |
| |
| _contentLogicalExtent = 0; |
| _contentCommittedExtent = 0; |
| } |
| |
| /* |
| * Compose an inline-block element, used for tables or other inline-blocks. The |
| * element has a container associated with it, and the container is going to be placed |
| * after the current paragraph if it fits in the text container. |
| * |
| * @param composeFrame the text container we're composing into |
| */ |
| protected function composeFloat(elem:ContainerFormattedElement,composeFrame:ContainerController):void |
| { |
| // Should get handled in derived class |
| CONFIG::debug { assert(false, "Floats are not supported in ComposeState"); } |
| } |
| |
| /** Called when we are about to compose a line. Handler for derived classes to override default behavior. */ |
| protected function startLine():void |
| { |
| // does nothing |
| } |
| |
| /** Called when we are finished composing a line. Handler for derived classes to override default behavior. */ |
| protected function endLine():void |
| { |
| // does nothing |
| } |
| |
| private function composeBlockElement(elem:FlowGroupElement,absStart:int):Boolean |
| { |
| // Compose all the children, until all the containers are filled, or if we're on the last container, we've hit the stop compose text index |
| var idx:int; |
| if (_startComposePosition != 0) |
| { |
| idx = elem.findChildIndexAtPosition(_startComposePosition-absStart); |
| CONFIG::debug { assert(idx != -1,"Bad _startComposePosition to index in composeBlockElement"); } |
| absStart += elem.getChildAt(idx).parentRelativeStart; |
| } |
| else |
| idx = 0; |
| |
| for (; idx < elem.numChildren && (absStart <= _stopComposePos || ! parcelList.atLast()); idx++) |
| { |
| var child:FlowElement = elem.getChildAt(idx); |
| |
| var para:ParagraphElement = child as ParagraphElement; |
| if (para) |
| { |
| var rslt:Boolean = composeParagraphElement(para,absStart); |
| // we need to flush each TextBlock - this saves a lot of memory at the cost of performance during editing |
| // note that this is a nop on older players. only newer players implement flush |
| if (releaseLineCreationData) |
| para.releaseLineCreationData(); |
| if (!rslt) |
| return false; // done |
| } |
| else if (child.display == FlowElementDisplayType.FLOAT) |
| { |
| composeFloat(ContainerFormattedElement(child),_parcelList.controller); |
| if (_parcelList.atEnd()) |
| return false; |
| CONFIG::debug { assert(child.getAbsoluteStart() + child.textLength - _parcelList.controller.absoluteStart >= 0, "frame has negative composition"); } |
| } |
| else |
| { |
| if (!composeBlockElement(FlowGroupElement(child),absStart)) |
| return false; |
| } |
| |
| absStart += child.textLength; |
| } |
| return true; |
| } |
| |
| // TODO: move somewhere reasonable |
| |
| private static function doNothingOnParcelChange(newParcel:Parcel):void |
| { } |
| |
| |
| /** |
| * Compose the flow into the text container. Starts at the root element, |
| * and composes elements until either there are no more elements, or the |
| * text container is full. It will compose only the lines which are |
| * marked invalid, so that existing lines that are unchanged are not |
| * recomposed. |
| */ |
| public function composeTextFlow(textFlow:TextFlow, composeToPosition:int, controllerEndIndex:int):int |
| { |
| _textFlow = textFlow; |
| _releaseLineCreationData = textFlow.configuration.releaseLineCreationData && Configuration.playerEnablesArgoFeatures; |
| |
| // must setup _startController and _startComposePosition |
| initializeForComposer(textFlow.flowComposer, composeToPosition, controllerEndIndex); |
| |
| _flowComposer = _textFlow.flowComposer as StandardFlowComposer; |
| _rootElement = textFlow; |
| _curElementOffset = 0; |
| _curElement = _rootElement.getFirstLeaf(); |
| |
| _curElementStart = 0; // current position in the text (start of current line) |
| |
| _curParcel = null; |
| resetControllerBounds(); |
| |
| if (_startController != _flowComposer.getControllerAt(0)) |
| { |
| var cacheNotify:Function = _parcelList.notifyOnParcelChange; |
| _parcelList.notifyOnParcelChange = doNothingOnParcelChange; |
| // skip parcels until the first one in startController |
| while(_parcelList.currentParcel.controller != _startController) |
| _parcelList.next(); |
| _parcelList.notifyOnParcelChange = cacheNotify; |
| } |
| |
| parcelHasChanged(_parcelList.currentParcel); // force start of composition acccounting initialization |
| |
| composeInternal(_rootElement,0); |
| |
| for (;;) |
| { |
| if (parcelList.atEnd()) |
| { |
| parcelHasChanged(null); // force end of composition accounting for the parcel |
| break; |
| } |
| parcelList.next(); |
| } |
| |
| |
| releaseParcelList(_parcelList); |
| _parcelList = null; |
| |
| return _curElementStart + _curElementOffset; // Return last composed position |
| } |
| |
| private function resetControllerBounds():void |
| { |
| _controllerLeft = TextLine.MAX_LINE_WIDTH; |
| _controllerTop = TextLine.MAX_LINE_WIDTH; |
| _controllerRight = -TextLine.MAX_LINE_WIDTH; |
| _controllerBottom = -TextLine.MAX_LINE_WIDTH; |
| } |
| |
| /** Release line creation data during this compose */ |
| protected function get releaseLineCreationData():Boolean |
| { return _releaseLineCreationData; } |
| |
| // Create new lines through composition. lines, wrap, etc. |
| protected function composeInternal(composeRoot:FlowGroupElement,absStart:int):void |
| { |
| composeBlockElement(composeRoot,absStart); |
| } |
| |
| protected function composeParagraphElement(elem:ParagraphElement,absStart:int):Boolean |
| { |
| CONFIG::debug { assert(false,"MISSING OVERRIDE"); } |
| return false; |
| } |
| |
| protected function composeParagraphElementIntoLines():Boolean |
| { |
| var curLine:TextFlowLine; |
| |
| // loop creating lines |
| for (;;) |
| { |
| if (_parcelList.atEnd()) |
| return false; |
| |
| // Allow derived classes to do processing here |
| startLine(); |
| |
| // Get the next line |
| curLine = composeNextLine(); |
| if (curLine == null) |
| return false; |
| |
| var alignData:AlignData = calculateTextAlign(curLine, curLine.getTextLine()); |
| |
| /* { |
| for (var idx:int = 0; idx < curLine.textLine.atomCount; idx++) |
| { |
| trace(idx.toString()+": beginIndex: " + curLine.textLine.getAtomTextBlockBeginIndex(idx)+ " bidiLevel: "+ curLine.textLine.getAtomBidiLevel(idx) + " bounds: " + curLine.textLine.getAtomBounds(idx)); |
| } |
| } */ |
| |
| if ((curLine.spaceBefore != 0 || _spaceCarried != 0) && !_parcelList.isColumnStart()) |
| _parcelList.addTotalDepth(Math.max(curLine.spaceBefore, _spaceCarried)); |
| _spaceCarried = 0; |
| _parcelList.addTotalDepth(curLine.height); |
| _curElementOffset += curLine.textLength; |
| // textLength is the first character in the next line |
| |
| var textLine:TextLine = curLine.getTextLine(); |
| |
| var lineWidth:Number; |
| if (_parcelList.explicitLineBreaks) |
| { |
| var isRTL:Boolean = _curParaElement.computedFormat.direction == Direction.RTL; |
| textLine = curLine.getTextLine(true); |
| var lastAtom:int = textLine.atomCount - 1; |
| // If we're at the end of the paragraph, don't count the terminator |
| var endOfParagraph:Boolean = _curElementStart+_curElementOffset == _curParaStart + _curParaElement.textLength; |
| if (endOfParagraph && !isRTL) |
| --lastAtom; // can go negative if just the terminator. in that case use left/top of atom zero |
| var bounds:Rectangle = textLine.getAtomBounds(lastAtom >= 0 ? lastAtom : 0); // get rightmost atom bounds |
| lineWidth = (_blockProgression == BlockProgression.TB) |
| ? (lastAtom >= 0 ? bounds.right : bounds.left) |
| : (lastAtom >= 0 ? bounds.bottom : bounds.top); |
| if (isRTL) // in right to left, get leftmost atom bounds, that has trailing space |
| { |
| // in RTL strip the width of the paragraph terminator from the front |
| bounds = textLine.getAtomBounds(lastAtom != 0 && endOfParagraph ? 1 : 0); |
| lineWidth -= (_blockProgression == BlockProgression.TB) ? bounds.left : bounds.top; |
| } |
| textLine.flushAtomData(); |
| } |
| else |
| lineWidth = textLine.textWidth; |
| |
| var rightSidePadding:Number = _curParaFormat.direction == Direction.LTR ? _curParaFormat.paragraphEndIndent : _curParaFormat.paragraphStartIndent; |
| var textIndent:Number = 0; |
| var rightSideIndent:Number = 0; |
| var leftSideIndent:Number = 0; |
| if (_curParaFormat.direction == Direction.RTL && (curLine.location & TextFlowLineLocation.FIRST)) |
| { |
| // the textIndent isn't applied on left aligned paragraphs in measured RTL mode |
| // need to be careful because leftaligned paragraphs need to be exactly right coming out of this routine |
| if (alignData && (_blockProgression == BlockProgression.TB && !curLine.controller.measureWidth || _blockProgression == BlockProgression.RL && !curLine.controller.measureHeight)) |
| rightSideIndent = _curParaFormat.textIndent; |
| } |
| var leftSidePadding:Number = _curParaFormat.direction == Direction.LTR ? _curParaFormat.paragraphStartIndent : _curParaFormat.paragraphEndIndent; |
| if (_curParaFormat.direction == Direction.LTR && (curLine.location & TextFlowLineLocation.FIRST)) |
| { |
| // recording leftSideIndent is here because there is an extra alignment step for non-left aligned paragraphs |
| leftSideIndent = _curParaFormat.textIndent; |
| } |
| |
| if (alignData) |
| { |
| alignData.rightSidePadding = rightSidePadding; |
| alignData.leftSidePadding = leftSidePadding; |
| alignData.lineWidth = lineWidth; |
| alignData.rightSideIndent = rightSideIndent; |
| alignData.leftSideIndent = leftSideIndent; |
| |
| // trace("AlignData",alignData.leftSidePadding,alignData.rightSidePadding,alignData.lineWidth,alignData.leftSideIndent,alignData.rightSideIndent); |
| } |
| |
| // extent from the left margin |
| var lineExtent:Number = lineWidth + leftSidePadding+leftSideIndent + rightSidePadding+rightSideIndent; |
| _contentLogicalExtent = Math.max(_contentLogicalExtent, lineExtent); |
| if (!alignData) |
| _contentCommittedExtent = Math.max(_contentCommittedExtent, lineExtent); |
| |
| CONFIG::debug { assert(_parcelList.controller.textLength >= 0, "frame has negative composition"); } |
| |
| if (_parcelList.atEnd()) |
| return false; |
| |
| endLine(); |
| |
| // advance to the next element, using the rootElement of the container as a limitNode |
| // to prevent going past the content bound to this container |
| if (_curElementOffset >= _curElement.textLength) |
| { |
| // We may have composed ahead over several spans; skip until we match up |
| // Loop until we use catch up to where the line we just composed ended (pos). |
| // Stop if we run out of elements. Skip empty inline elements, and skip floats |
| // that came at the start of the line before any text -- they've already been |
| // processed. |
| do{ |
| _curElementOffset -= _curElement.textLength; |
| _curElementStart += _curElement.textLength; |
| if (_curElementStart == _curParaStart+_curParaElement.textLength) |
| { |
| _curElement = null; |
| break; |
| } |
| _curElement = _curElement.getNextLeaf(); |
| CONFIG::debug { assert(_curElement && _curElement.getParagraph() == _curParaElement,"composeParagraphElement: bad textLength in TextLine"); } |
| } while (_curElementOffset >= _curElement.textLength || _curElement.textLength == 0 ); |
| } |
| |
| _spaceCarried = curLine.spaceAfter; |
| |
| |
| if (_curElement == null) |
| break; |
| } |
| return true; |
| } |
| |
| protected function composeNextLine():TextFlowLine |
| { |
| CONFIG::debug { throw new Error("composeNextLine requires override"); } |
| return null; |
| } |
| |
| // fills in _lineSlug |
| protected function fitLineToParcel(curLine:TextFlowLine, isNewLine:Boolean):TextFlowLine |
| { |
| // Try to place the line in the current parcel. |
| // get a zero height parcel. place the line there and then test if it still fits. |
| // if it doesn't place it in the new result parcel |
| // still need to investigate because the height used on the 2nd getLineSlug call may be too big. |
| for (;;) |
| { |
| if (_parcelList.getLineSlug(_candidateLineSlug,0)) |
| break; |
| _parcelList.next(); |
| if (_parcelList.atEnd()) |
| return null; |
| _spaceCarried = 0; |
| } |
| |
| curLine.setController(_parcelList.controller,_parcelList.columnIndex); |
| |
| // If we are at the last parcel, we let text be clipped if that's specified in the configuration. At the point where no part of text can be accommodated, we go overset. |
| // If we are not at the last parcel, we let text flow to the next parcel instead of getting clipped. |
| var spaceBefore:Number = Math.max(curLine.spaceBefore, _spaceCarried); |
| for (;;) |
| { |
| finishComposeLine(curLine, _candidateLineSlug, isNewLine); |
| if (_parcelList.getLineSlug(_lineSlug, spaceBefore + (_parcelList.atLast() && _textFlow.configuration.overflowPolicy != OverflowPolicy.FIT_DESCENDERS ? curLine.height-curLine.ascent : curLine.height+curLine.descent))) |
| { |
| CONFIG::debug { assert(_parcelList.getComposeXCoord(_candidateLineSlug) == _parcelList.getComposeXCoord(_lineSlug) && _parcelList.getComposeYCoord(_candidateLineSlug) == _parcelList.getComposeYCoord(_lineSlug),"fitLineToParcel: slug mismatch"); } |
| break; |
| } |
| spaceBefore = curLine.spaceBefore; |
| for (;;) |
| { |
| _parcelList.next(); |
| if (_parcelList.atEnd()) |
| return null; |
| if (_parcelList.getLineSlug(_candidateLineSlug,0)) |
| break; |
| } |
| curLine.setController(_parcelList.controller,_parcelList.columnIndex); |
| } |
| |
| // check to see if we got a good line |
| return (_parcelList.getComposeWidth(_lineSlug) == curLine.outerTargetWidth) ? curLine : null; |
| } |
| |
| |
| protected function finishComposeLine(curLine:TextFlowLine, lineSlug:Rectangle, isNewLine:Boolean):void |
| { |
| var curTextLine:TextLine = curLine.getTextLine(); |
| var lineHeight:Number = 0; |
| //replace X and Y with rise and run. |
| // rise - the offset within a line relative to block progressiong. For RL this is X, for TB Y |
| // run - the indentation of the line. For RL this is Y, TB X |
| var rise:Number = _blockProgression != BlockProgression.RL ? parcelList.getComposeYCoord(lineSlug) : _parcelList.getComposeXCoord(lineSlug); |
| var run:Number = _blockProgression != BlockProgression.RL ? parcelList.getComposeXCoord(lineSlug) : _parcelList.getComposeYCoord(lineSlug); |
| |
| if (_curParaFormat.direction == Direction.LTR) |
| { |
| run += curLine.lineOffset; |
| } |
| else |
| { |
| run += curLine.outerTargetWidth-curLine.lineOffset-curLine.targetWidth; |
| |
| if (curLine.outerTargetWidth == TextLine.MAX_LINE_WIDTH && curLine.location&TextFlowLineLocation.FIRST) // doing measurement ignore |
| { |
| run += curLine.paragraph.computedFormat.textIndent; |
| } |
| } |
| |
| _curLineLeading = curLine.getLineLeading(_blockProgression,_curElement,_curElementStart); |
| _curLineLeadingModel = _curParaElement.getEffectiveLeadingModel(); |
| |
| var containerAttrs:ITextLayoutFormat = _parcelList.controller.computedFormat; |
| var baselineType:Object = BaselineOffset.LINE_HEIGHT; |
| if (_parcelList.isColumnStart()) |
| { |
| // If we're at the top of the column, we need to check the container properties to see |
| // what the firstBaselineOffset should be. This tells us how to treat the line. |
| // However, when vertical alignment is center or bottom, ignore the firstBaselineOffset setting |
| // and treat them as the BaselineOffset.AUTO case |
| if (containerAttrs.firstBaselineOffset != BaselineOffset.AUTO && containerAttrs.verticalAlign != VerticalAlign.BOTTOM && containerAttrs.verticalAlign != VerticalAlign.MIDDLE) |
| { |
| baselineType = containerAttrs.firstBaselineOffset; |
| // The first line's offset is specified relative firstBaselineOffsetBasis, which used to be, but no longer is, a container-level property |
| // Now it is implicitly deduced based on the container-level locale in the following manner: |
| // IDEOGRAPHIC_BOTTOM for ja and zh locales (this is the same locale set for which the default LeadingModel is IDEOGRAPHIC_TOP_DOWN) |
| // ROMAN for all other locales |
| var firstBaselineOffsetBasis:String = LocaleUtil.leadingModel(containerAttrs.locale) == LeadingModel.IDEOGRAPHIC_TOP_DOWN ? flash.text.engine.TextBaseline.IDEOGRAPHIC_BOTTOM : flash.text.engine.TextBaseline.ROMAN; |
| lineHeight -= curTextLine.getBaselinePosition(firstBaselineOffsetBasis); |
| } |
| else |
| { |
| if (_curLineLeadingModel == LeadingModel.APPROXIMATE_TEXT_FIELD) |
| { |
| // Reinterpret AUTO when APPROXIMATE_TEXT_FIELD leading model is used. |
| // Align the "enhanced ascent" (an approximation of TextField's notion of ascent baseline, |
| // which differs from FTEs notion of the same by an amount equal to the line's descent) with the container top inset |
| lineHeight += Math.round(curTextLine.descent) + Math.round(curTextLine.ascent) |
| |
| // Ensure Roman baseline will fall at an integer position. This is desirable for all leading models, |
| // but only APPROXIMATE_TEXT_FIELD requires it now. In a future release, this code can be moved below and lineX/lineY rounded off directly. |
| if (_blockProgression == BlockProgression.TB) |
| lineHeight = Math.round(rise + lineHeight) - rise; |
| else |
| lineHeight = rise - Math.round(rise - lineHeight); |
| |
| baselineType = 0; // No further adjustments |
| } |
| else |
| { |
| // The AUTO case requires aligning line top to container top inset. This efect can be achieved by using firstBaselineOffset=ASCENT |
| // and firstBaselineOffsetBasis=ROMAN |
| baselineType = BaselineOffset.ASCENT; |
| |
| if(curTextLine.hasGraphicElement) |
| { |
| var firstLineAdjustment:LeadingAdjustment = getLineAdjustmentForInline(curLine, _curLineLeadingModel, true); |
| if(firstLineAdjustment != null) |
| { |
| if(_blockProgression == BlockProgression.RL) |
| { |
| firstLineAdjustment.rise = -(firstLineAdjustment.rise); |
| } |
| _curLineLeading += firstLineAdjustment.leading; |
| rise += firstLineAdjustment.rise; |
| } |
| } |
| |
| lineHeight -= curTextLine.getBaselinePosition(flash.text.engine.TextBaseline.ROMAN); |
| } |
| } |
| } |
| else |
| { |
| // handle space before by adjusting y position of line |
| if (curLine.spaceBefore != 0 || _spaceCarried != 0) |
| { |
| var spaceAdjust:Number = Math.max(curLine.spaceBefore, _spaceCarried); |
| |
| rise += _blockProgression == BlockProgression.RL ? -spaceAdjust :spaceAdjust; |
| } |
| } |
| //getTextLineTypographicAscent |
| if (baselineType == BaselineOffset.ASCENT) |
| { |
| // CONFIG::debug { assert(_curElement == _textFlow.findLeaf(curLine.absoluteStart),"Bad _curElement"); } |
| CONFIG::debug { assert(_curElementStart == _textFlow.findLeaf(curLine.absoluteStart).getAbsoluteStart(), "Bad _curElementStart"); } |
| lineHeight += curLine.getLineTypographicAscent(_curElement,_curElementStart); |
| } |
| else |
| { |
| if (baselineType == BaselineOffset.LINE_HEIGHT) |
| { |
| if (_curLineLeadingModel == LeadingModel.APPROXIMATE_TEXT_FIELD) |
| { |
| // Position the "enhanced ascent" (see above) at a distance of leading from the previous line's descent |
| lineHeight += Math.round(_lastLineDescent) + Math.round(curTextLine.ascent) + Math.round(curTextLine.descent) + Math.round(_curLineLeading); |
| } |
| else if (_curLineLeadingModel == LeadingModel.ASCENT_DESCENT_UP) |
| { |
| lineHeight += _lastLineDescent + curTextLine.ascent + _curLineLeading; |
| } |
| else |
| { |
| // Leading direction is irrelevant for the first line. Treat it as (UP, UP) |
| // TODO-9/3/2008-It may be better to handle Middle/Last lines separately because we know that the previous line also belongs in the same para |
| var curLeadingDirectionUp:Boolean = _parcelList.isColumnStart() ? true : ParagraphElement.useUpLeadingDirection(_curLineLeadingModel); |
| |
| var prevLeadingDirectionUp:Boolean = _parcelList.isColumnStart() || _lastLineLeadingModel == "" ? true : |
| ParagraphElement.useUpLeadingDirection(_lastLineLeadingModel); |
| |
| var prevLineFirstElement:FlowLeafElement; |
| |
| if (curLeadingDirectionUp) |
| { |
| //TODO-9/12/2008-The above behavior is the InDesign behavior but raises some questions about selection shapes. |
| //Should selection code associate leading with the influencing line? That would be weird. InDesign only |
| //supports alternate leading directions in the J feature set, where leading is never included in selection, |
| //so this question does not arise. We take the unambiguous route: ignore leading DOWN at the end of a para |
| lineHeight += _curLineLeading; |
| } |
| else |
| { |
| if (!prevLeadingDirectionUp) |
| { |
| // Same leading directions; use previous line's leading setting. |
| lineHeight += _lastLineLeading; |
| } |
| else |
| { |
| // Make NO leading adjustments. Set lines solid. |
| lineHeight += _lastLineDescent + curTextLine.ascent; |
| } |
| } |
| } |
| } |
| else |
| lineHeight += Number(baselineType); // fixed offset |
| } |
| |
| //don't know why, but ascent only needs to be removed from horizontal text. Hmm, that seems |
| //odd to me - gak 12.15.09 |
| rise += _blockProgression == BlockProgression.RL ? -(lineHeight) : lineHeight - curTextLine.ascent; |
| |
| //baselineType will be BaselineOffset.ASCENT for fixed leading |
| if(curTextLine.hasGraphicElement && baselineType != BaselineOffset.ASCENT) |
| { |
| var adjustment:LeadingAdjustment = getLineAdjustmentForInline(curLine, _curLineLeadingModel, false); |
| if(adjustment != null) |
| { |
| if(_blockProgression == BlockProgression.RL) |
| { |
| adjustment.rise = -(adjustment.rise); |
| } |
| _curLineLeading += adjustment.leading; |
| rise += adjustment.rise; |
| } |
| } |
| |
| |
| if(_blockProgression == BlockProgression.TB) |
| curLine.setXYAndHeight(run,rise,lineHeight); |
| else |
| curLine.setXYAndHeight(rise,run,lineHeight); |
| |
| if(isNewLine) |
| curLine.createAdornments(_blockProgression,_curElement,_curElementStart); |
| } |
| |
| // Calculate the text alignment of the current line we're composing. If alignment is required, the adjustment will be made in |
| // applyTextAlign, after we've calculated the width of the parcel (it may be based on measurement). |
| // TODO: optimization possibility - do the alignment here when not doing measurement |
| private function calculateTextAlign(curLine:TextFlowLine, curTextLine:TextLine):AlignData |
| { |
| // Adjust the coordinates of the line for center/right. The line is always left aligned. TextBlock handles justified cases |
| // If we're on the last line of a justified paragraph, use the textAlignLast value |
| var textAlignment:String = _curParaFormat.textAlign; |
| if (textAlignment == TextAlign.JUSTIFY && _curParaFormat.textAlignLast != null) |
| { |
| var location:int = curLine.location; |
| if (location == TextFlowLineLocation.LAST || location == TextFlowLineLocation.ONLY) |
| textAlignment = _curParaFormat.textAlignLast; |
| } |
| switch(textAlignment) |
| { |
| case TextAlign.START: |
| textAlignment = (_curParaFormat.direction == Direction.LTR) ? TextAlign.LEFT : TextAlign.RIGHT; |
| break; |
| case TextAlign.END: |
| textAlignment = (_curParaFormat.direction == Direction.LTR) ? TextAlign.RIGHT : TextAlign.LEFT; |
| break; |
| } |
| |
| var createAlignData:Boolean = textAlignment == TextAlign.CENTER || textAlignment == TextAlign.RIGHT; |
| |
| // in argo lines that have tabs must be either START or JUSTIFY |
| if (Configuration.playerEnablesArgoFeatures) |
| { |
| if (curTextLine["hasTabs"]) |
| { |
| if (_curParaFormat.direction == Direction.LTR) |
| { |
| createAlignData = false; // don't align it - let it be left align |
| } |
| else |
| { |
| createAlignData = true; |
| textAlignment = TextAlign.RIGHT; |
| } |
| } |
| } |
| |
| |
| if (createAlignData) |
| { |
| var alignData:AlignData = new AlignData(); |
| alignData.textLine = curTextLine; |
| alignData.center = (textAlignment == TextAlign.CENTER); |
| if (!_alignLines) |
| _alignLines = []; |
| _alignLines.push(alignData); |
| return alignData; |
| } |
| return null; |
| } |
| |
| private function applyTextAlign(effectiveParcelWidth:Number):void |
| { |
| var textLine:TextLine; |
| var line:TextFlowLine; |
| var alignData:AlignData; |
| |
| var coord:Number; |
| var delta:Number; |
| var adjustedLogicalRight:Number; |
| var extraSpace:Number; |
| var leftSideGap:Number; |
| var rightSideGap:Number; |
| |
| if (_blockProgression == BlockProgression.TB) |
| { |
| for each (alignData in _alignLines) |
| { |
| textLine = alignData.textLine; |
| |
| rightSideGap = alignData.rightSidePadding; |
| leftSideGap = alignData.leftSidePadding; |
| leftSideGap += alignData.leftSideIndent; |
| rightSideGap += alignData.rightSideIndent; |
| |
| line = textLine.userData as TextFlowLine; |
| extraSpace = effectiveParcelWidth - leftSideGap - rightSideGap - textLine.textWidth; |
| delta = leftSideGap + (alignData.center ? extraSpace / 2 : extraSpace); |
| coord = _curParcel.left + delta; |
| if (line) |
| line.x = coord; |
| textLine.x = coord; |
| |
| adjustedLogicalRight = alignData.lineWidth + coord + Math.max(rightSideGap, 0); |
| _parcelRight = Math.max(adjustedLogicalRight , _parcelRight); |
| } |
| } |
| else |
| { |
| for each (alignData in _alignLines) |
| { |
| textLine = alignData.textLine; |
| |
| rightSideGap = alignData.rightSidePadding; |
| leftSideGap = alignData.leftSidePadding; |
| leftSideGap += alignData.leftSideIndent; |
| rightSideGap += alignData.rightSideIndent; |
| |
| line = textLine.userData as TextFlowLine; |
| extraSpace = effectiveParcelWidth - leftSideGap - rightSideGap - textLine.textWidth; |
| delta = leftSideGap + (alignData.center ? extraSpace / 2 : extraSpace); |
| coord = _curParcel.top + delta; |
| if (line) |
| line.y = coord; |
| textLine.y = coord; |
| |
| adjustedLogicalRight = alignData.lineWidth + coord + Math.max(rightSideGap, 0); |
| _parcelBottom = Math.max(adjustedLogicalRight,_parcelBottom); |
| } |
| } |
| _alignLines.length = 0; |
| } |
| |
| protected function commitLastLineState(curLine:TextFlowLine):void |
| { |
| // Remember leading-related state that may be used for laying out the next line |
| _lastLineDescent = curLine.descent; |
| _lastLineLeading = _curLineLeading; |
| _lastLineLeadingModel = _curLineLeadingModel; |
| } |
| |
| protected function doVerticalAlignment(canVerticalAlign:Boolean,nextParcel:Parcel):Boolean |
| { |
| // stub for required override |
| CONFIG::debug { assert(false, "override in derived class"); } |
| return false; |
| } |
| |
| protected function finalParcelAdjustment(controller:ContainerController):void |
| { |
| // stub for required override |
| CONFIG::debug { assert(false, "finalParcelAdjustment missing override in derived class"); } |
| } |
| |
| protected function finishParcel(controller:ContainerController,nextParcel:Parcel):Boolean |
| { |
| if (_curParcelStart == _curElementStart+_curElementOffset) // empty parcel -- nothing composed into it |
| { |
| CONFIG::debug { assert(_contentLogicalExtent == 0,"bad contentlogicalextent on empty container"); } |
| return false; |
| } |
| |
| // We're only going to align the lines in measurement mode if there's only one parcel |
| var doTextAlign:Boolean = (_alignLines && _alignLines.length > 0); |
| |
| // Figure out the contents bounds information for the parcel we just finished composing |
| |
| // Content logical height is parcel depth, plus descenders of last line |
| var totalDepth:Number = _parcelList.totalDepth; |
| if (_textFlow.configuration.overflowPolicy == OverflowPolicy.FIT_DESCENDERS && !isNaN(_lastLineDescent)) |
| totalDepth += _lastLineDescent; |
| |
| // Initialize the parcel bounds |
| // note we can later optimize away the adjustements |
| if (_blockProgression == BlockProgression.TB) |
| { |
| _parcelLeft = _curParcel.left; |
| _parcelTop = _curParcel.top; |
| _parcelRight = _contentCommittedExtent+_curParcel.left; |
| _parcelBottom = totalDepth+_curParcel.top; |
| } |
| else |
| { |
| // Push the values up to the controller running min/max, if they are bigger |
| _parcelLeft = _curParcel.right-totalDepth; |
| _parcelTop = _curParcel.top; |
| _parcelRight = _curParcel.right; |
| _parcelBottom = _contentCommittedExtent+_curParcel.top; |
| } |
| if (doTextAlign) |
| { |
| var effectiveParcelWidth:Number; |
| if (_blockProgression == BlockProgression.TB) |
| effectiveParcelWidth = controller.measureWidth ? _contentLogicalExtent : _curParcel.width; |
| else |
| effectiveParcelWidth = controller.measureHeight ? _contentLogicalExtent : _curParcel.height; |
| applyTextAlign(effectiveParcelWidth); |
| } |
| |
| // If we're measuring, then don't do vertical alignment |
| var canVerticalAlign:Boolean = false; |
| if (_blockProgression == BlockProgression.TB) |
| { |
| if (!controller.measureHeight && (!_curParcel.fitAny || _curElementStart + _curElementOffset >= _textFlow.textLength)) |
| canVerticalAlign = true; |
| } |
| else |
| { |
| if (!controller.measureWidth && (!_curParcel.fitAny || _curElementStart + _curElementOffset >= _textFlow.textLength)) |
| canVerticalAlign = true; |
| } |
| |
| // need to always call this function because internal variables may need resetting |
| if (doVerticalAlignment(canVerticalAlign,nextParcel)) |
| doTextAlign = true; |
| // This last adjustment is for two issues |
| // 1) inline graphics that extend above the top (any ILGS I expect) |
| // 2) negative first line indents (stil a worry here?) |
| // If neither of these are present it can be skipped - TODO optimization |
| // trace("BEF finalParcelAdjustment",_parcelLeft,_parcelRight,_parcelTop,_parcelBottom); |
| finalParcelAdjustment(controller); |
| // trace("AFT finalParcelAdjustment",_parcelLeft,_parcelRight,_parcelTop,_parcelBottom); |
| _contentLogicalExtent = 0; |
| _contentCommittedExtent = 0; |
| |
| return true; |
| } |
| |
| /** apply vj and adjust the parcel bounds */ |
| protected function applyVerticalAlignmentToColumn(controller:ContainerController,vjType:String,lines:Array,beginIndex:int,numLines:int):void |
| { |
| var firstLine:IVerticalJustificationLine = lines[beginIndex]; |
| var lastLine:IVerticalJustificationLine = lines[beginIndex+numLines-1] |
| var firstLineCoord:Number; |
| var lastLineCoord:Number |
| if (_blockProgression == BlockProgression.TB) |
| { |
| firstLineCoord = firstLine.y; |
| lastLineCoord = lastLine.y; |
| } |
| else |
| { |
| firstLineCoord = firstLine.x; |
| lastLineCoord = lastLine.x; |
| } |
| |
| VerticalJustifier.applyVerticalAlignmentToColumn(controller,vjType,lines,beginIndex,numLines); |
| |
| if (_blockProgression == BlockProgression.TB) |
| { |
| _parcelTop += firstLine.y-firstLineCoord; |
| _parcelBottom += lastLine.y-lastLineCoord; |
| } |
| else |
| { |
| _parcelRight += firstLine.x-firstLineCoord; |
| _parcelLeft += lastLine.x-lastLineCoord; |
| } |
| } |
| |
| private function finishController(controller:ContainerController):void |
| { |
| var controllerTextLength:int = _curElementStart + _curElementOffset - controller.absoluteStart; |
| |
| if (controllerTextLength != 0) |
| { |
| // Leave room for the padding. If the content overlaps the padding, don't count the padding twice. |
| var paddingLeft:Number = controller.effectivePaddingLeft; |
| var paddingTop:Number = controller.effectivePaddingTop; |
| var paddingRight:Number = controller.effectivePaddingRight; |
| var paddingBottom:Number = controller.effectivePaddingBottom; |
| if (_blockProgression == BlockProgression.TB) |
| { |
| if (_controllerLeft > 0) |
| { |
| if (_controllerLeft < paddingLeft) |
| _controllerLeft = 0; |
| else |
| _controllerLeft -= paddingLeft; |
| } |
| |
| if (_controllerTop > 0) |
| { |
| if (_controllerTop < paddingTop) |
| _controllerTop = 0; |
| else |
| _controllerTop -= paddingTop; |
| } |
| |
| if (isNaN(controller.compositionWidth)) |
| _controllerRight += paddingRight; |
| else if (_controllerRight < controller.compositionWidth) |
| { |
| if (_controllerRight > controller.compositionWidth - paddingRight) |
| _controllerRight = controller.compositionWidth; |
| else |
| _controllerRight += paddingRight; |
| } |
| _controllerBottom += paddingBottom; |
| } |
| else |
| { |
| _controllerLeft -= paddingLeft; |
| if (_controllerTop > 0) |
| { |
| if (_controllerTop < paddingTop) |
| _controllerTop = 0; |
| else |
| _controllerTop -= paddingTop; |
| } |
| if (_controllerRight < 0) |
| { |
| if (_controllerRight > -paddingRight) |
| { |
| _controllerRight = 0; |
| } |
| else |
| _controllerRight += paddingRight; |
| } |
| if (isNaN(controller.compositionHeight)) |
| _controllerBottom += paddingBottom; |
| else if (_controllerBottom < controller.compositionHeight) |
| { |
| if (_controllerBottom > controller.compositionHeight - paddingBottom) |
| _controllerBottom = controller.compositionHeight; |
| else |
| _controllerBottom += paddingBottom; |
| } |
| } |
| controller.setContentBounds(_controllerLeft, _controllerTop, _controllerRight-_controllerLeft, _controllerBottom-_controllerTop); |
| } |
| else |
| controller.setContentBounds(0,0,0,0); |
| |
| controller.setTextLength(controllerTextLength); |
| } |
| |
| private function clearControllers(oldController:ContainerController, newController:ContainerController):void |
| { |
| // any controller between oldController and up to and including newController gets cleared |
| var firstToClear:int = oldController ? _flowComposer.getControllerIndex(oldController)+1 : 0; |
| var lastToClear:int = newController ? _flowComposer.getControllerIndex(newController) : _flowComposer.numControllers-1; |
| while (firstToClear <= lastToClear) |
| { |
| var controllerToClear:ContainerController = ContainerController(_flowComposer.getControllerAt(firstToClear)); |
| controllerToClear.setContentBounds(0, 0, 0, 0); |
| controllerToClear.setTextLength(0); |
| firstToClear++; |
| } |
| } |
| |
| /** This is called when the parcel has changed |
| * @param oldParcel - the parcel we had before (you can get the new parcel from the parcel list) |
| */ |
| protected function parcelHasChanged(newParcel:Parcel):void |
| { |
| var oldController:ContainerController = _curParcel ? ContainerController(_curParcel.controller) : null; |
| var newController:ContainerController = newParcel ? ContainerController(newParcel.controller) : null; |
| |
| /* if (newParcel) |
| trace("parcelHasChanged newParcel: ",newParcel.clone().toString()); */ |
| |
| if (_curParcel != null) |
| { |
| if (finishParcel(oldController,newParcel)) |
| { |
| if (_parcelLeft < _controllerLeft) |
| _controllerLeft = _parcelLeft; |
| if (_parcelRight > _controllerRight) |
| _controllerRight = _parcelRight; |
| if (_parcelTop < _controllerTop) |
| _controllerTop = _parcelTop; |
| if (_parcelBottom > _controllerBottom) |
| _controllerBottom = _parcelBottom; |
| } |
| } |
| |
| // update parcel data |
| if (oldController != newController) // we're going on to the next controller in the chain |
| { |
| if (oldController) |
| finishController(oldController); |
| |
| resetControllerBounds(); |
| |
| if (_flowComposer.numControllers > 1) |
| { |
| if (oldController == null && _startController) |
| clearControllers(_startController, newController); |
| else |
| clearControllers(oldController, newController); |
| } |
| // Parcel list will set totalDepth to newController's paddingTop |
| } |
| _curParcel = newParcel; |
| _curParcelStart = _curElementStart; |
| } |
| /** @private */ |
| private function getLineAdjustmentForInline(curLine:TextFlowLine, curLeadingDir:String, isFirstLine:Boolean):LeadingAdjustment |
| { |
| var adjustment:LeadingAdjustment = null; |
| var curTextLine:TextLine = curLine.getTextLine(); |
| var para:ParagraphElement = curLine.paragraph; |
| var flowElem:FlowLeafElement = _curElement; //the first element included in this line |
| var curPos:int = flowElem.getAbsoluteStart(); |
| var largestPointSize:Number = flowElem.getEffectiveFontSize(); |
| var largestImg:Number = 0; |
| |
| //walk |
| while(flowElem && curPos < curLine.absoluteStart + curLine.textLength) |
| { |
| if(curPos >= curLine.absoluteStart || curPos + flowElem.textLength >= curLine.absoluteStart) |
| { |
| if(flowElem is InlineGraphicElement) |
| { |
| var inlineImg:InlineGraphicElement = flowElem as InlineGraphicElement; |
| //we can ignore TCY for leading adjustments |
| if(!(_blockProgression == BlockProgression.RL && (flowElem.parent is TCYElement))) |
| { |
| //if the largest found img is smaller than the current image, we need new data |
| if(largestImg < inlineImg.getEffectiveFontSize()) |
| { |
| largestImg = inlineImg.getEffectiveFontSize(); |
| //only get this if the img is as large or larger than the largest found text |
| if(largestImg >= largestPointSize) |
| { |
| largestImg = largestImg; |
| var domBaseline:String = flowElem.computedFormat.dominantBaseline; |
| if(domBaseline == FormatValue.AUTO) |
| domBaseline = LocaleUtil.dominantBaseline(para.computedFormat.locale); |
| |
| //we are only making the adjustment for ideo-center, all others are to be ignored... |
| if(domBaseline == TextBaseline.IDEOGRAPHIC_CENTER) |
| { |
| var elemLeading:Number = TextLayoutFormat.lineHeightProperty.computeActualPropertyValue(inlineImg.computedFormat.lineHeight, inlineImg.getEffectiveFontSize()); |
| var curAdjustment:LeadingAdjustment = calculateLinePlacementAdjustment(curTextLine, domBaseline, curLeadingDir, inlineImg, isFirstLine); |
| if(!adjustment || Math.abs(curAdjustment.rise) > Math.abs(adjustment.rise) || Math.abs(curAdjustment.leading) > Math.abs(adjustment.leading)) |
| { |
| if(adjustment) |
| { |
| adjustment.rise = curAdjustment.rise; |
| adjustment.leading = curAdjustment.leading; |
| } |
| else |
| adjustment = curAdjustment; |
| } |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| var tempSize:Number = flowElem.getEffectiveFontSize(); |
| if(largestPointSize <= tempSize) |
| { |
| largestPointSize = tempSize; |
| } |
| |
| //if the largest image is smaller than this element, zero out the adjustment |
| if(adjustment && largestImg < largestPointSize) |
| { |
| adjustment.leading = 0; |
| adjustment.rise = 0; |
| } |
| } |
| } |
| |
| //advance the position and get the next element |
| curPos += flowElem.textLength; |
| flowElem = flowElem.getNextLeaf(para); |
| } |
| return adjustment; |
| } |
| |
| |
| public function get swfContext():ISWFContext |
| { |
| var composerContext:ISWFContext = _flowComposer.swfContext; |
| return composerContext ? composerContext : GlobalSWFContext.globalSWFContext; |
| } |
| |
| /** @private */ |
| private function calculateLinePlacementAdjustment(curTextLine:TextLine, domBaseline:String, curLeadingDir:String, inlineImg:InlineGraphicElement, isFirstLine:Boolean):LeadingAdjustment |
| { |
| var curAdjustment:LeadingAdjustment = new LeadingAdjustment(); |
| //get the leading height for the img |
| var imgHeight:Number = TextLayoutFormat.lineHeightProperty.computeActualPropertyValue(inlineImg.computedFormat.lineHeight, inlineImg.getEffectiveFontSize()); |
| //get the leading as if the line contains no imgs. We'll need this to adjust the total adjustments |
| var lineLeading:Number = TextLayoutFormat.lineHeightProperty.computeActualPropertyValue(inlineImg.computedFormat.lineHeight, curTextLine.textHeight) |
| |
| //this is a redundant check, but will be needed in the future, so we're leaving it in. - gak 12.16.09 |
| if(domBaseline == TextBaseline.IDEOGRAPHIC_CENTER) |
| { |
| if(!isFirstLine) |
| { |
| //for non-first lines, we want to offset the rise of the line |
| curAdjustment.rise += (imgHeight - lineLeading)/2; |
| } |
| else |
| { |
| //for the first line, the offset will be right, but hte leading wrong. |
| curAdjustment.leading -= (imgHeight - lineLeading)/2; |
| } |
| } |
| |
| return curAdjustment; |
| } |
| } |
| } |
| |
| import flash.text.engine.TextLine; |
| import flashx.textLayout.compose.ISWFContext; |
| import flashx.textLayout.debug.Debugging; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| class AlignData |
| { |
| public var textLine:TextLine; |
| public var lineWidth:Number; |
| public var leftSidePadding:Number; |
| public var rightSidePadding:Number; |
| public var center:Boolean; |
| public var leftSideIndent:Number; |
| public var rightSideIndent:Number; |
| } |
| |
| |
| class GlobalSWFContext implements ISWFContext |
| { |
| static public const globalSWFContext:GlobalSWFContext = new GlobalSWFContext(); |
| |
| public function GlobalSWFContext() |
| { } |
| |
| public function callInContext(fn:Function, thisArg:Object, argsArray:Array, returns:Boolean=true):* |
| { |
| CONFIG::debug |
| { |
| var rslt:* |
| try |
| { |
| if (returns) |
| rslt = fn.apply(thisArg, argsArray); |
| |
| else |
| fn.apply(thisArg, argsArray); |
| |
| if (thisArg) |
| { |
| var traceArgs:Array; |
| // later make this table driven |
| if (thisArg.hasOwnProperty("createTextLine") && fn == thisArg["createTextLine"]) |
| { |
| traceArgs = [rslt,thisArg,"createTextLine"] |
| traceArgs.push.apply(traceArgs, argsArray); |
| Debugging.traceFTECall.apply(null,traceArgs); |
| } |
| else if (thisArg.hasOwnProperty("recreateTextLine") && fn == thisArg["recreateTextLine"]) |
| { |
| traceArgs = [rslt,thisArg,"recreateTextLine"] |
| traceArgs.push.apply(traceArgs, argsArray); |
| Debugging.traceFTECall.apply(null,traceArgs); |
| } |
| } |
| } |
| catch(e:Error) |
| { |
| // trace(e); |
| throw(e); |
| } |
| return rslt; |
| } |
| CONFIG::release |
| { |
| if (returns) |
| return fn.apply(thisArg, argsArray); |
| fn.apply(thisArg, argsArray); |
| } |
| } |
| } |
| |
| class LeadingAdjustment |
| { |
| public var rise:Number = 0; |
| public var leading:Number = 0; |
| public var lineHeight:Number = 0; |
| } |
| |
| |