//////////////////////////////////////////////////////////////////////////////// // // 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.factory { import flash.display.Sprite; import flash.geom.Rectangle; import flash.text.engine.TextBlock; import flash.text.engine.TextLine; import flash.text.engine.TextLineValidity; import flashx.textLayout.compose.IFlowComposer; import flashx.textLayout.compose.ISWFContext; import flashx.textLayout.compose.SimpleCompose; import flashx.textLayout.compose.StandardFlowComposer; import flashx.textLayout.container.ContainerController; import flashx.textLayout.container.ScrollPolicy; import flashx.textLayout.debug.Debugging; import flashx.textLayout.debug.assert; import flashx.textLayout.elements.OverflowPolicy; import flashx.textLayout.elements.TextFlow; import flashx.textLayout.formats.BlockProgression; import flashx.textLayout.tlf_internal; use namespace tlf_internal; [Exclude(name="containerController",kind="property")] [Exclude(name="setContentBounds",kind="method")] [Exclude(name="callbackWithTextLines",kind="method")] [Exclude(name="doesComposedTextFit",kind="method")] [Exclude(name="getNextTruncationPosition",kind="method")] /** * The TextLineFactoryBase class serves as the base class for the Text Layout Framework text line factories. * *
Note: Application code does not typically need to create or use a TextLineFactoryBase object directly. * Use one of the derived text factory classes instead.
* * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see flashx.textLayout.elements.TextFlow */ public class TextLineFactoryBase { /** Requested logical bounds to wrap to */ private var _compositionBounds:Rectangle; /** Bounds of composition results - where the text landed */ private var _contentBounds:Rectangle; /** @private */ protected var _isTruncated:Boolean = false; private var _horizontalScrollPolicy:String; private var _verticalScrollPolicy:String; private var _truncationOptions:TruncationOptions; private var _containerController:ContainerController; static private var _tc:Sprite = new Sprite(); private var _swfContext:ISWFContext; /** @private */ static tlf_internal var _factoryComposer:SimpleCompose= new SimpleCompose(); /** @private */ static protected var _truncationLineIndex:int; // used during truncation /** @private */ static protected var _pass0Lines:Array; // used during truncation /** * Base-class constructor for text line factories. * *Note: Application code does not typically need to create or use a TextLineFactoryBase object directly. * Use one of the derived text factory classes instead.
* * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function TextLineFactoryBase():void { _containerController = new ContainerController(_tc); _horizontalScrollPolicy = _verticalScrollPolicy = String(ScrollPolicy.scrollPolicyPropertyDefinition.defaultValue); } /** * The rectangle within which text lines are created. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function get compositionBounds():Rectangle { return _compositionBounds; } public function set compositionBounds(value:Rectangle):void { _compositionBounds = value; } /** * The smallest rectangle in which the layed-out content fits. * *Note: Truncated lines are not included in the size calculation.
* * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function getContentBounds():Rectangle { return _contentBounds; } /** @private */ protected function setContentBounds(controllerBounds:Rectangle):void { _contentBounds = controllerBounds; _contentBounds.offset(compositionBounds.left, compositionBounds.top); } /** * The ISWFContext instance used to make FTE calls as needed. * *By default, the ISWFContext implementation is this FlowComposerBase object. * Applications can provide a custom implementation to use fonts * embedded in a different SWF file or to cache and reuse text lines.
* * @see flashx.textLayout.compose.ISWFContext * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function get swfContext():ISWFContext { return _swfContext; } public function set swfContext(value:ISWFContext):void { _swfContext = value; } /** * Specifies the options for truncating the text if it doesn't fit in the composition bounds. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function get truncationOptions():TruncationOptions { return _truncationOptions; } public function set truncationOptions(value:TruncationOptions):void { _truncationOptions = value; } /** * Indicates whether text was truncated when lines were last created. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function get isTruncated():Boolean { return _isTruncated; } /** * Specifies how lines are created when the composition bounds are not large enough. * *If set to ScrollPolicy.ON
or ScrollPolicy.AUTO
, all lines
* are created. It is the your responsibility to scroll lines in the viewable area (and to
* mask lines outside this area, if necessary). If set to ScrollPolicy.OFF
, then
* only lines that fit within the composition bounds are created.
If the truncationOptions
property is set, the scroll policy is ignored
* (and treated as ScrollPolicy.OFF
).
If set to ScrollPolicy.ON
or ScrollPolicy.AUTO
, all lines
* are created. It is the your responsibility to scroll lines in the viewable area (and to
* mask lines outside this area, if necessary). If set to ScrollPolicy.OFF
, then
* only lines that fit within the composition bounds are created.
If the truncationOptions
property is set, the scroll policy is ignored
* (and treated as ScrollPolicy.OFF
).
This method sets the x
and y
properties of the line.
true
if text has more than one paragraph.
*
* @returns the next candidate truncation position.
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
protected function getNextTruncationPosition(truncateAtCharPosition:int, multiPara:Boolean=false):int
{
// 1. Get the position of the last character of the preceding atom
truncateAtCharPosition--; // truncateAtCharPosition-1, because truncateAtCharPosition is an atom boundary
// Note: The current set of lines may not contain the next truncation position because the truncation indicator
// could combine with original content to form a word that does not afford a suitable break opportunity.
// The combined word would then move to the next line, which may not have been composed if the bounds were exceeded.
// Therefore, this function needs to use the original lines (from before truncation is attempted).
CONFIG::debug
{
assert(_pass0Lines != null, "getNextTruncationPosition called before saving off lines from the first pass at composition");
assert(_truncationLineIndex < _pass0Lines.length, "index out of range in getNextTruncationPosition");
}
// 2. Find the new target line (i.e., the line that has the new truncation position)
// If the last truncation position was at the beginning of the target line, the new position may have moved to a previous line
// In any case, the new truncation position lies in the vicinity of the previous target line, so a linear search suffices
var line:TextLine = _pass0Lines[_truncationLineIndex] as TextLine;
do
{
if (truncateAtCharPosition >= line.userData && truncateAtCharPosition < line.userData + line.rawTextLength)
break;
if (truncateAtCharPosition < line.userData)
line = _pass0Lines[--_truncationLineIndex] as TextLine;
else
{
CONFIG::debug { assert(false, "truncation position should decrease monotonically"); }
}
}
while (true);
var paraStart:int = multiPara ? line.userData - line.textBlockBeginIndex : 0;
// 3. Get the line atom index at this position
var atomIndex:int = line.getAtomIndexAtCharIndex(truncateAtCharPosition - paraStart);
// 4. Get the char index for this atom index
var nextTruncationPosition:int = line.getAtomTextBlockBeginIndex(atomIndex) + paraStart;
line.flushAtomData();
return nextTruncationPosition;
}
/** @private */
tlf_internal function createFlowComposer():IFlowComposer
{
return new FactoryDisplayComposer();
}
/** @private
* Calculates the last line that fits in the line count limit
* The result is stored in _truncationLineIndex
*
* Note: This code is only called when scrolling is OFF, so only lines that fit in bounds are generated
*/
tlf_internal function computeLastAllowedLineIndex (lineCountLimit:int):void
{
_truncationLineIndex = _factoryComposer._lines.length - 1;
// if line count limit is smaller, use that
if (lineCountLimit != TruncationOptions.NO_LINE_COUNT_LIMIT && lineCountLimit <= _truncationLineIndex)
_truncationLineIndex = lineCountLimit - 1;
}
}
} // end package
import flash.geom.Rectangle;
import flashx.textLayout.compose.StandardFlowComposer;
import flashx.textLayout.compose.SimpleCompose;
import flashx.textLayout.debug.assert;
import flashx.textLayout.elements.BackgroundManager;
import flashx.textLayout.elements.FlowLeafElement;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.factory.TextLineFactoryBase;
import flashx.textLayout.tlf_internal;
import flash.text.engine.TextLine;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.compose.TextFlowLine;
use namespace tlf_internal;
class FactoryDisplayComposer extends StandardFlowComposer
{
tlf_internal override function callTheComposer(absoluteEndPosition:int, controllerEndIndex:int):ContainerController
{
// always do a full compose
clearCompositionResults();
var state:SimpleCompose = TextLineFactoryBase._factoryComposer;
state.composeTextFlow(textFlow, -1, -1);
state.releaseAnyReferences()
return getControllerAt(0);
}
/** Returns true if composition is necessary, false otherwise */
protected override function preCompose():Boolean
{
return true;
}
/** @private */
public override function createBackgroundManager():BackgroundManager
{ return new FactoryBackgroundManager(); }
}
class FactoryBackgroundManager extends BackgroundManager
{
public override function finalizeLine(line:TextFlowLine):void
{
var textLine:TextLine = line.getTextLine();
var array:Array = lineDict[textLine];
if (array)
{
// attach the columnRect and the TextLine to the first object in the Array
var obj:Object = array[0];
if (obj) // check not needed?
obj.columnRect = line.controller.columnState.getColumnAt(line.columnIndex);
}
}
}