blob: de979a8369573cb4f7f3d491e521dbd5625664ab [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.events.Event;
import flash.geom.Rectangle;
import flash.text.engine.TextBlock;
import flash.text.engine.TextLine;
import flash.text.engine.TextLineValidity;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.debug.Debugging;
import flashx.textLayout.debug.assert;
import flashx.textLayout.elements.ContainerFormattedElement;
import flashx.textLayout.elements.FlowElement;
import flashx.textLayout.elements.FlowGroupElement;
import flashx.textLayout.elements.FlowLeafElement;
import flashx.textLayout.elements.OverflowPolicy;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.elements.SubParagraphGroupElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.compose.TextFlowLineLocation;
import flashx.textLayout.formats.BaselineOffset;
import flashx.textLayout.formats.BlockProgression;
import flashx.textLayout.formats.Direction;
import flashx.textLayout.formats.ITextLayoutFormat;
import flashx.textLayout.formats.TextAlign;
import flashx.textLayout.formats.VerticalAlign;
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
[ExcludeClass]
/** Keeps track of internal state during composition.
*
* This is the simpler version, used when there are no floats, no wraps, no columns.
* @private
*/
public class SimpleCompose extends BaseCompose
{
// reusable scratch TextFlowLine
protected var workingLine:TextFlowLine = new TextFlowLine(null, null);
// resulting TextLines
public var _lines:Array;
// scratch aligns for VJ
private var _vjLines:Array;
// for figuring out when to do VJ
private var vjBeginLineIndex:int = 0;
private var vjDisableThisParcel:Boolean = false;
private var vjParcel:Parcel;
private var vjType:String;
// accumulator for absolute start computation to support truncation
private var _totalLength:Number;
/** Constructor. */
public function SimpleCompose()
{
super();
_lines = new Array();
_vjLines = new Array();
}
/** @private */
protected override function createParcelList():IParcelList
{
return ParcelList.getParcelList();
}
/** @private */
protected override function releaseParcelList(list:IParcelList):void
{
ParcelList.releaseParcelList(list);
}
protected override function initializeForComposer(composer:IFlowComposer, composeToPosition:int, controllerEndIndex:int):void
{
super.initializeForComposer(composer, composeToPosition, controllerEndIndex);
// vj support
_vjLines.splice(0);
vjBeginLineIndex = 0;
vjParcel = parcelList.currentParcel;
vjDisableThisParcel = false;
vjType = vjParcel ? vjParcel.controller.computedFormat.verticalAlign : VerticalAlign.TOP;
_startController = composer.getControllerAt(0);
_startComposePosition = 0;
}
/** @private */
public override function composeTextFlow(textFlow:TextFlow, composeToPosition:int, controllerEndIndex:int):int
{
_flowComposer = textFlow.flowComposer as StandardFlowComposer;
// empty out lines array
_lines.splice(0);
// accumulator initialization
_totalLength = 0;
return super.composeTextFlow(textFlow, composeToPosition, controllerEndIndex);
}
override protected function doVerticalAlignment(canVerticalAlign:Boolean,nextParcel:Parcel):Boolean
{
var result:Boolean = false;
if (canVerticalAlign && vjType != VerticalAlign.TOP && vjBeginLineIndex != _lines.length && !vjDisableThisParcel && vjParcel.columnCoverage == Parcel.FULL_COLUMN)
{
applyVerticalAlignmentToColumn(vjParcel.controller,vjType,_vjLines,0,_vjLines.length);
result = true; // lines were moved
}
_vjLines.splice(0);
vjBeginLineIndex = _lines.length;
vjParcel = nextParcel; // next parcel
vjDisableThisParcel = false;
if (nextParcel)
vjType = vjParcel.controller.computedFormat.verticalAlign;
return result;
}
private function finalizeLine(curLine:TextFlowLine):void
{
var line:TextLine = curLine.createShape(_blockProgression);
if (textFlow.backgroundManager)
textFlow.backgroundManager.finalizeLine(curLine);
line.userData = _totalLength; // store absolute start position in the userData field
_totalLength += line.rawTextLength; // update length accumulator
_lines.push(line);
if (vjType != VerticalAlign.TOP)
_vjLines.push(new VJHelper(line,curLine.height));
commitLastLineState (curLine);
}
public function get textFlow():TextFlow
{
return _textFlow;
}
/** @private */
protected override function composeParagraphElement(elem:ParagraphElement, absStart:int):Boolean
{
_curParaElement = elem;
_curParaStart = absStart;
_curParaFormat = elem.computedFormat;
CONFIG::debug { assert(_curParaStart == elem.getAbsoluteStart(),"composeParagraphElement: bad start"); }
_curElement = elem.getFirstLeaf();
_curElementStart = _curParaStart;
return composeParagraphElementIntoLines();
}
/** @private */
protected override function composeNextLine():TextFlowLine
{
// Check to see if there's an existing line that is composed up-to-date
var startCompose:int = _curElementStart + _curElementOffset - _curParaStart;
var prevLine:TextLine = startCompose != 0 ? workingLine.getTextLine() : null;
CONFIG::debug { assert(!prevLine || prevLine.validity == "valid","Bad prevline: "+Debugging.getIdentity(prevLine)); }
var finishLineSlug:Rectangle = _parcelList.currentParcel;
var curLine:TextFlowLine;
for (;;)
{
for (;;)
{
// generate new line
CONFIG::debug { assert(!_parcelList.atEnd(), "failing to stop"); }
CONFIG::debug { assert(_curElement is FlowLeafElement, "element must be leaf before calling composeLine"); }
curLine = createTextLine(prevLine, startCompose, _parcelList.getComposeXCoord(finishLineSlug), _parcelList.getComposeYCoord(finishLineSlug), _parcelList.getComposeWidth(finishLineSlug));
if (curLine != null)
break;
// force advance to the next parcel
if (!_parcelList.next())
return null;
}
// updates _lineSlug
curLine = fitLineToParcel(curLine, true);
if (curLine)
break;
if (_parcelList.atEnd())
return null;
finishLineSlug = _lineSlug;
}
finalizeLine(curLine);
CONFIG::debug { assert(curLine != null, "curLine != null"); }
return curLine;
}
/** @private */
protected function createTextLine(prevLine:TextLine, // previous line
lineStart:int, // text index of position to start from, relative to start of paragraph
x:Number, // left edge of the line
y:Number, // top of the line
targetWidth:Number // target width we're composing into
):TextFlowLine
{
// adjust target width for text indent, start and end indent
var lineOffset:Number = Number(_curParaFormat.paragraphStartIndent); // indent to "beginning" of the line. Direction dependent (as is paragraphStartIndent)
if (prevLine == null) // first line indent
lineOffset += Number(_curParaFormat.textIndent);
var outerTargetWidth:Number = targetWidth;
targetWidth -= (Number(_curParaFormat.paragraphEndIndent) + lineOffset); // make room for offset and end indent
targetWidth = (targetWidth < 0) ? 0 : targetWidth; // no negative targetwidth allowed
if (targetWidth > TextLine.MAX_LINE_WIDTH)
targetWidth = TextLine.MAX_LINE_WIDTH;
//var textLine:TextLine = _flowComposer.textLineCreator.createTextLine(_curParaElement.getTextBlock(), prevLine, targetWidth, lineOffset, true);
var textLine:TextLine = TextLineRecycler.getLineForReuse();
var textBlock:TextBlock = _curParaElement.getTextBlock();
if (textLine)
{
CONFIG::debug { assert(_textFlow.backgroundManager == null || _textFlow.backgroundManager.lineDict[textLine] === undefined,"Bad TextLine in recycler cache"); }
textLine = swfContext.callInContext(textBlock["recreateTextLine"], textBlock, [ textLine, prevLine, targetWidth, lineOffset, true ]);
}
else
{
textLine = swfContext.callInContext(textBlock.createTextLine, textBlock, [prevLine, targetWidth, lineOffset, true ]);
}
// Unable to fit a new line
if (textLine == null)
return null;
CONFIG::debug { assert(_curParaStart == _curParaElement.getAbsoluteStart(),"bad _curParaStart"); }
workingLine.initialize(_curParaElement, outerTargetWidth, lineOffset, lineStart + _curParaStart, textLine.rawTextLength, textLine);
CONFIG::debug { assert(workingLine.targetWidth == targetWidth,"Bad targetWidth"); }
// update spaceBefore & spaceAfter
CONFIG::debug
{
var linePos:uint = workingLine.location;
workingLine.setSpaceBefore((linePos & TextFlowLineLocation.FIRST) ? Number(_curParaFormat.paragraphSpaceBefore) : 0);
workingLine.setSpaceAfter((linePos & TextFlowLineLocation.LAST) ? Number(_curParaFormat.paragraphSpaceAfter) : 0);
}
return workingLine;
}
/** @private */
tlf_internal function swapLines(lines:Array):Array
{
var current:Array = _lines;
_lines = lines;
return current;
}
/** Final adjustment on the content bounds. */
override protected function finalParcelAdjustment(controller:ContainerController):void
{
var minX:Number = TextLine.MAX_LINE_WIDTH;
var minY:Number = TextLine.MAX_LINE_WIDTH;
var maxX:Number = -TextLine.MAX_LINE_WIDTH;
var maxY:Number = -TextLine.MAX_LINE_WIDTH;
var textLine:TextLine;
var verticalText:Boolean = _blockProgression == BlockProgression.RL;
var startPos:int = controller.absoluteStart;
for each (textLine in _lines)
{
var leaf:FlowLeafElement = controller.textFlow.findLeaf(startPos);
var para:ParagraphElement = leaf.getParagraph();
// Check the logical vertical dimension first
// If the lines have children, they may be inlines. The origin of the TextLine is the baseline,
// which does not include the ascent of the inlines or the text. So we have to factor that in.
// var verticalAdjust:Number = verticalText ? textLine.descent : textLine.ascent;
var inlineAscent:Number = 0;
if (textLine.numChildren > 0) // adjustjust logical vertical coord to take into account inlines
{
var leafStart:int = leaf.getAbsoluteStart();
inlineAscent = TextFlowLine.getTextLineTypographicAscent(textLine, leaf, leafStart, startPos + textLine.rawTextLength, para);
}
// Figure out the logical horizontal adjustment
var edgeAdjust:Number = 0;
var curParaFormat:ITextLayoutFormat = para.computedFormat;
if (curParaFormat.direction == Direction.LTR)
edgeAdjust = curParaFormat.paragraphStartIndent + Math.max(curParaFormat.textIndent, 0);
else
edgeAdjust = curParaFormat.paragraphEndIndent;
if (verticalText)
{
minX = Math.min(textLine.x - textLine.descent, minX);
maxX = Math.max(textLine.x + Math.max(inlineAscent,textLine.ascent), maxX);
minY = Math.min(textLine.y - edgeAdjust, minY);
}
else
{
if (inlineAscent < textLine.ascent)
inlineAscent = textLine.ascent;
minX = Math.min(textLine.x - edgeAdjust, minX);
minY = Math.min(textLine.y - inlineAscent, minY);
}
startPos += textLine.rawTextLength;
}
// Don't make adjustments for tiny fractional values.
if (minX != TextLine.MAX_LINE_WIDTH && Math.abs(minX-_parcelLeft) >= 1)
_parcelLeft = minX;
if (maxX != -TextLine.MAX_LINE_WIDTH && Math.abs(maxX-_parcelRight) >= 1)
_parcelRight = maxX;
if (minY != TextLine.MAX_LINE_WIDTH && Math.abs(minY-_parcelTop) >= 1)
_parcelTop = minY;
if (maxY != -TextLine.MAX_LINE_WIDTH && Math.abs(maxY-_parcelBottom) >= 1)
_parcelBottom = maxY;
}
tlf_internal override function releaseAnyReferences():void
{
super.releaseAnyReferences();
workingLine.initialize(null,0,0,0,0,null);
// parcelList.releaseAnyReferences();
}
}
}
import flash.text.engine.TextLine;
import flashx.textLayout.compose.IVerticalJustificationLine;
import flash.text.engine.TextLineCreationResult;
class VJHelper implements IVerticalJustificationLine
{
private var _line:TextLine;
private var _height:Number;
public function VJHelper(line:TextLine,h:Number)
{
_line = line;
_height = h;
}
public function get x():Number
{ return _line.x; }
public function set x(val:Number):void
{ _line.x = val; }
public function get y():Number
{ return _line.y; }
public function set y(val:Number):void
{ _line.y = val; }
public function get ascent():Number
{ return _line.ascent; }
public function get descent():Number
{ return _line.descent; }
public function get height():Number
{ return _height; }
}