//////////////////////////////////////////////////////////////////////////////// // // 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.Shape; 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.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.FlowGroupElement; import flashx.textLayout.elements.FlowLeafElement; import flashx.textLayout.elements.OverflowPolicy; import flashx.textLayout.elements.ParagraphElement; import flashx.textLayout.elements.SpanElement; import flashx.textLayout.elements.TextFlow; import flashx.textLayout.compose.TextFlowLine; import flashx.textLayout.formats.BlockProgression; import flashx.textLayout.formats.ITextLayoutFormat; import flashx.textLayout.formats.LineBreak; import flashx.textLayout.formats.TextLayoutFormat; import flashx.textLayout.tlf_internal; use namespace tlf_internal; /** * The TextFlowTextLineFactory class provides a simple way to create TextLines for displaying text from a text flow. * *

The text lines are static and created fit in a single bounding rectangle, but can have multiple paragraphs and formats as well as * inline graphics. To create TextLine objects directly from a string, use StringTextLineFactory.

* *

Note: When using inline graphics, the source property of the InlineGraphicElement object * must either be an instance of a DisplayObject or a Class object representing an embedded asset. * URLRequest objects cannot be used. The width and height of the inline graphic at the time the line * is created is used to compose the flow.

* * @includeExample examples\TextFlowTextLineFactory_example.as * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * * @see flashx.textLayout.elements.TextFlow TextFlow * @see flashx.textLayout.factory.StringTextLineFactory StringTextLineFactory */ public final class TextFlowTextLineFactory extends TextLineFactoryBase { /** * Creates a TextFlowTextLineFactory object. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function TextFlowTextLineFactory():void { super(); } /** * Creates TextLine objects from the specified text flow. * *

The text lines are composed to fit the bounds assigned to the compositionBounds property. * As each line is created, the factory calls the function specified in the * callback parameter. This function is passed the TextLine object and * is responsible for displaying the line. If a line has a background color, the factory also calls the * callback function with a Shape object containing a rectangle of the background color.

* * @param callback function to call with each generated TextLine object. * The callback will be called with a Shape object representing any background color (if present), * and with TextLine objects for the text. * @param textFlow The TextFlow from which the lines are created. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 * */ public function createTextLines(callback:Function,textFlow:TextFlow):void { var measureWidth:Boolean = isNaN(compositionBounds.width); var bp:String = textFlow.computedFormat.blockProgression; var helper:IFlowComposer = createFlowComposer(); helper.swfContext = swfContext; helper.addController(containerController); textFlow.flowComposer = helper; textFlow.backgroundManager = null; _isTruncated = false; // compose containerController.setCompositionSize(compositionBounds.width, compositionBounds.height); containerController.verticalScrollPolicy = truncationOptions ? ScrollPolicy.OFF : verticalScrollPolicy; containerController.horizontalScrollPolicy = truncationOptions ? ScrollPolicy.OFF : horizontalScrollPolicy; textFlow.normalize(); textFlow.applyUpdateElements(true); helper.compose(); // Need truncation if all the following are true // - truncation options exist // - content doesn't fit if (truncationOptions && !doesComposedTextFit(truncationOptions.lineCountLimit, textFlow.textLength, bp)) { _isTruncated = true; var somethingFit:Boolean = false; // were we able to fit something? computeLastAllowedLineIndex (truncationOptions.lineCountLimit); if (_truncationLineIndex >= 0) { // Create a span for the truncation indicator var truncationIndicatorSpan:SpanElement = new SpanElement(); truncationIndicatorSpan.text = truncationOptions.truncationIndicator; truncationIndicatorSpan.id = "truncationIndicator"; // prevents merging with other spans if (truncationOptions.truncationIndicatorFormat) truncationIndicatorSpan.format = truncationOptions.truncationIndicatorFormat; var hostFormat:ITextLayoutFormat = textFlow.hostFormat; // Initial truncation position: end of the last allowed line var line:TextLine = _factoryComposer._lines[_truncationLineIndex] as TextLine; var truncateAtCharPosition:int = line.userData + line.rawTextLength; // Save off the original lines: used in getNextTruncationPosition // Note that for the original lines to be valid when used, the containing text block should not be modified // Cloning the text flow before modifying it ensures that if (!_pass0Lines) _pass0Lines = new Array(); _pass0Lines = _factoryComposer.swapLines(_pass0Lines); // The following loop executes repeatedly composing text until it fits // In each iteration, an atoms's worth of characters of original content is dropped do { // Clone the part of the flow before the truncation position textFlow = textFlow.deepCopy(0, truncateAtCharPosition) as TextFlow; // TODO-2/18/2009-deepCopy does not copy hostTextLayoutFormat if (hostFormat) textFlow.hostFormat = hostFormat; // Find a parent for the truncation span var parent:FlowGroupElement; var lastLeaf:FlowLeafElement = textFlow.getLastLeaf(); if (lastLeaf) { parent = lastLeaf.parent; // Set format to match the leaf if none specified if (!truncationOptions.truncationIndicatorFormat) truncationIndicatorSpan.format = lastLeaf.format; } else { parent = new ParagraphElement(); textFlow.addChild(parent); } // Append the truncation span (after severing it from the previous flow) if (truncationIndicatorSpan.parent) truncationIndicatorSpan.parent.removeChild(truncationIndicatorSpan); parent.addChild(truncationIndicatorSpan); textFlow.flowComposer = helper; textFlow.normalize(); helper.compose(); if (doesComposedTextFit(truncationOptions.lineCountLimit, textFlow.textLength, bp)) { somethingFit = true; break; } if (truncateAtCharPosition == 0) break; // no original content left to make room for truncation indicator // Try again by truncating at the beginning of the preceding atom truncateAtCharPosition = getNextTruncationPosition(truncateAtCharPosition, true); } while (true); } if (_truncatedTextFlowCallback != null) _truncatedTextFlowCallback (somethingFit ? textFlow : null); if (!somethingFit) _factoryComposer._lines.splice(0); // return no lines } var xadjust:Number = compositionBounds.x; // toptobottom sets zero to the right edge - adjust the locations var controllerBounds:Rectangle = containerController.getContentBounds(); if (bp == BlockProgression.RL) xadjust += (measureWidth ? controllerBounds.width : compositionBounds.width); // apply x and y adjustment to the bounds controllerBounds.left += xadjust; controllerBounds.right += xadjust; controllerBounds.top += compositionBounds.y; controllerBounds.bottom += compositionBounds.y; if (textFlow.backgroundManager) { var bgShape:Shape = new Shape(); textFlow.backgroundManager.drawAllRects(bgShape,containerController); bgShape.x = xadjust; bgShape.y = compositionBounds.y; callback(bgShape); textFlow.backgroundManager = null; } callbackWithTextLines(callback,xadjust,compositionBounds.y); setContentBounds(controllerBounds); textFlow.changeFlowComposer(null,false); // clear out lines in the FactoryComposer _factoryComposer._lines.splice(0); if (_pass0Lines) _pass0Lines.splice(0); } /** @private * Test hook: Sets a callback function for getting the truncated text flow. * This function is only called if truncation is performed. It takes a single parameter which will have the following value: * null, if nothing fits * A text flow representing the truncated text (containing inital text from the original text flow followed by the truncation indicator), otherwise */ tlf_internal function set truncatedTextFlowCallback(val:Function):void { _truncatedTextFlowCallback = val; } private var _truncatedTextFlowCallback:Function; } }