blob: 3ed1baab36ff76d4be802a0ab4b20d617dab0b26 [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 flashx.textLayout.container.ContainerController;
import flashx.textLayout.debug.assert;
import flashx.textLayout.formats.BlockProgression;
import flashx.textLayout.formats.Float;
import flashx.textLayout.formats.VerticalAlign;
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
/** @private
* Adjust line positions according to verticalAlign settings. Vertical alignment is perpendicular
* to the linebreak direction.
*/
public final class VerticalJustifier
{
[ ArrayElementType("text.IVerticalJustificationLine") ]
/** Vertical justify the subset of lines from startIndext to startIndex to numLines according to the rule specified by verticalAlignAttr.
* The assumption is that they are all the lines in a single column of cont.
* @see text.formats.VerticalAlign
* @return adjustment applied to the first line in the column.
* */
static public function applyVerticalAlignmentToColumn(controller:ContainerController, verticalAlignAttr:String, lines:Array, startIndex:int, numLines:int,beginFloatIndex:int,endFloatIndex:int):Number
{
var helper:IVerticalAdjustmentHelper;
if (controller.rootElement.computedFormat.blockProgression == BlockProgression.RL)
helper = new RL_VJHelper(controller);
else
helper = new TB_VJHelper(controller);
CONFIG::debug { assert(startIndex + numLines <= lines.length && numLines > 0,"applyVerticalAlignmentToColumn: bad indices"); }
var i:int;
var rslt:Number;
switch(verticalAlignAttr)
{
case VerticalAlign.MIDDLE:
case VerticalAlign.BOTTOM:
var lastLine:IVerticalJustificationLine = lines[(startIndex + numLines) - 1];
var bottom:Number = helper.getBottom(lastLine, controller, beginFloatIndex, endFloatIndex);
rslt = (verticalAlignAttr == VerticalAlign.MIDDLE) ? helper.computeMiddleAdjustment(bottom) : helper.computeBottomAdjustment(bottom);
for (i = startIndex; i < startIndex + numLines; i++) // Adjust line positions
helper.applyAdjustment(lines[i]);
for (var floatIndex:int = beginFloatIndex; floatIndex < endFloatIndex; floatIndex++) // Adjust float positions
{
var floatInfo:FloatCompositionData = controller.getFloatAt(floatIndex);
if (floatInfo.floatType != Float.NONE)
helper.applyAdjustmentToFloat(floatInfo);
}
break;
case VerticalAlign.JUSTIFY:
rslt = helper.computeJustifyAdjustment(lines, startIndex, numLines);
helper.applyJustifyAdjustment(lines, startIndex, numLines);
break;
}
//for (i = startIndex; i < startIndex + numLines; i++)
// trace("x=", sc[i].x, "y=", sc[i].y, "yAdj=", yAdj);
return rslt;
}
}
}
import flash.geom.Rectangle;
import flash.text.engine.TextLine;
import flashx.textLayout.compose.FloatCompositionData;
import flashx.textLayout.compose.IVerticalJustificationLine;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.elements.InlineGraphicElement;
import flashx.textLayout.formats.Float;
interface IVerticalAdjustmentHelper
{
function getBottom(line:IVerticalJustificationLine, controller:ContainerController, beginFloat:int, endFloat:int):Number;
function computeMiddleAdjustment(bottom:Number):Number;
function applyAdjustment(line:IVerticalJustificationLine):void;
function applyAdjustmentToFloat(floatInfo:FloatCompositionData):void;
function computeBottomAdjustment(bottom:Number):Number;
function computeJustifyAdjustment(lineArray:Array, firstLine:int, numLines:int):Number;
function applyJustifyAdjustment(lineArray:Array, firstLine:int, numLines:int):void;
}
class TB_VJHelper implements IVerticalAdjustmentHelper
{
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
private var _textFrame:ContainerController;
private var adj:Number;
public function TB_VJHelper(tf:ContainerController):void
{
_textFrame = tf;
}
public function getBottom(line:IVerticalJustificationLine, controller:ContainerController, beginFloat:int, endFloat:int):Number
{
var maxBottom:Number = getBaseline(line) + line.descent;
for (var i:int = beginFloat; i < endFloat; ++i)
{
var floatInfo:FloatCompositionData = controller.getFloatAt(i);
if (floatInfo.floatType != Float.NONE)
{
var ilg:InlineGraphicElement = controller.rootElement.findLeaf(floatInfo.absolutePosition) as InlineGraphicElement;
maxBottom = Math.max(maxBottom, floatInfo.y + ilg.elementHeightWithMarginsAndPadding());
}
}
return maxBottom;
}
public function getBottomOfLine(line:IVerticalJustificationLine):Number
{
return getBaseline(line) + line.descent;
}
private function getBaseline(line:IVerticalJustificationLine):Number
{
if (line is TextFlowLine)
return line.y + line.ascent;
else
return line.y;
}
private function setBaseline(line:IVerticalJustificationLine, pos:Number):void
{
if (line is TextFlowLine)
line.y = pos - line.ascent;
else
line.y = pos;
}
// half of the available adjustment added to each y to shift the lines half way down the frame
public function computeMiddleAdjustment(contentBottom:Number):Number
{
var frameBottom:Number = _textFrame.compositionHeight - Number(_textFrame.getTotalPaddingBottom());
adj = (frameBottom - contentBottom) / 2;
if (adj < 0)
adj = 0; // no space available to redistribute
return adj;
}
// the baseline of the last line should be at the bottom of the frame - paddingBottom.
public function computeBottomAdjustment(contentBottom:Number):Number
{
var frameBottom:Number = _textFrame.compositionHeight - Number(_textFrame.getTotalPaddingBottom());
adj = frameBottom - contentBottom;
if (adj < 0)
adj = 0; // no space available to redistribute
return adj;
}
public function applyAdjustment(line:IVerticalJustificationLine):void
{
line.y = line.y + adj;
}
public function applyAdjustmentToFloat(floatInfo:FloatCompositionData):void
{
floatInfo.y += adj;
}
// one line: untouched, two lines: first line untouched and descent of last line at the bottom of the frame,
// and more than two lines: line spacing increased proportionally, with first line untouched and descent of last line at the bottom of the frame
[ ArrayElementType("text.compose.IVerticalJustificationLine") ]
public function computeJustifyAdjustment(lineArray:Array, firstLineIndex:int, numLines:int):Number
{
adj = 0;
if (numLines == 1)
{
return 0; // do nothing
}
// first line unchanged
var firstLine:IVerticalJustificationLine = lineArray[firstLineIndex];
var firstBaseLine:Number = getBaseline(firstLine);
// descent of the last line on the bottom of the frame
var lastLine:IVerticalJustificationLine = lineArray[firstLineIndex + numLines - 1];
var frameBottom:Number = _textFrame.compositionHeight - Number(_textFrame.getTotalPaddingBottom());
var allowance:Number = frameBottom - getBottomOfLine(lastLine);
if (allowance < 0)
{
return 0; // Some text scrolled out; don't justify
}
var lastBaseLine:Number = getBaseline(lastLine);
adj = allowance/(lastBaseLine - firstBaseLine); // multiplicative factor by which the space between consecutive lines is increased
return adj;
}
[ ArrayElementType("text.compose.IVerticalJustificationLine") ]
public function applyJustifyAdjustment(lineArray:Array, firstLineIndex:int, numLines:int):void
{
if (numLines == 1 || adj == 0)
return; // do nothing
var firstLine:IVerticalJustificationLine = lineArray[firstLineIndex];
var prevBaseLine:Number = getBaseline(firstLine);
var prevBaseLineUnjustified:Number = prevBaseLine;
var line:IVerticalJustificationLine;
var currBaseLine:Number;
var currBaseLineUnjustified:Number;
for (var i:int = 1; i < numLines; i++)
{
line = lineArray[i + firstLineIndex];
currBaseLineUnjustified = getBaseline(line);
// Space between consecutive baselines scaled up by the calculated factor
currBaseLine = prevBaseLine + (currBaseLineUnjustified - prevBaseLineUnjustified)*(1 + adj);
setBaseline(line, currBaseLine);
prevBaseLineUnjustified = currBaseLineUnjustified;
prevBaseLine = currBaseLine;
}
}
}
class RL_VJHelper implements IVerticalAdjustmentHelper
{
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
private var _textFrame:ContainerController;
private var adj:Number = 0;
public function RL_VJHelper(tf:ContainerController):void
{
_textFrame = tf;
}
public function getBottom(lastLine:IVerticalJustificationLine, controller:ContainerController, beginFloat:int, endFloat:int):Number
{
var frameWidth:Number = _textFrame.compositionWidth - Number(_textFrame.getTotalPaddingLeft());
var maxLeft:Number = (frameWidth + lastLine.x - lastLine.descent);
for (var i:int = beginFloat; i < endFloat; ++i)
{
var floatInfo:FloatCompositionData = controller.getFloatAt(i);
if (floatInfo.floatType != Float.NONE)
maxLeft = Math.min(maxLeft, floatInfo.x + frameWidth);
}
return maxLeft;
}
// half of the available adjustment added to each x to shift the lines half way left across the frame
public function computeMiddleAdjustment(contentLeft:Number):Number
{
adj = contentLeft / 2;
if (adj < 0)
adj = 0; // no space available to redistribute
return -adj;
}
// the baseline of the last line should be at the bottom of the frame - paddingLeft.
public function computeBottomAdjustment(contentLeft:Number):Number
{
adj = contentLeft;
if (adj < 0)
adj = 0; // no space available to redistribute
return -adj;
}
public function applyAdjustment(line:IVerticalJustificationLine):void
{
line.x = (line.x - adj);
}
public function applyAdjustmentToFloat(floatInfo:FloatCompositionData):void
{
floatInfo.x -= adj;
}
// one line: untouched, two lines: first line untouched and descent of last line at the bottom of the frame,
// and more than two lines: line spacing increased proportionally, with first line untouched and descent of last line at the bottom of the frame
[ ArrayElementType("text.compose.IVerticalJustificationLine") ]
public function computeJustifyAdjustment(lineArray:Array, firstLineIndex:int, numLines:int):Number
{
adj = 0;
if (numLines == 1)
return 0; // do nothing
// first line unchanged
var firstLine:IVerticalJustificationLine = lineArray[firstLineIndex];
var firstBaseLine:Number = firstLine.x;
// descent of the last line on the left of the frame
var lastLine:IVerticalJustificationLine = lineArray[firstLineIndex + numLines - 1];
var frameLeft:Number = Number(_textFrame.getTotalPaddingLeft()) - _textFrame.compositionWidth;
var allowance:Number = (lastLine.x - lastLine.descent) - frameLeft;
if (allowance < 0)
{
return 0; // Some text scrolled out; don't justify
}
var lastBaseLine:Number = lastLine.x;
adj = allowance/(firstBaseLine - lastBaseLine); // multiplicative factor by which the space between consecutive lines is increased
return -adj;
}
[ ArrayElementType("text.IVerticalJustificationLine") ]
public function applyJustifyAdjustment(lineArray:Array, firstLineIndex:int, numLines:int):void
{
if (numLines == 1 || adj == 0)
return; // do nothing
var firstLine:IVerticalJustificationLine = lineArray[firstLineIndex];
var prevBaseLine:Number = firstLine.x;
var prevBaseLineUnjustified:Number = prevBaseLine;
var line:IVerticalJustificationLine;
var currBaseLine:Number;
var currBaseLineUnjustified:Number;
for (var i:int = 1; i < numLines; i++)
{
line = lineArray[i + firstLineIndex];
currBaseLineUnjustified = line.x;
// Space between consecutive baselines scaled up by the calculated factor
currBaseLine = prevBaseLine - (prevBaseLineUnjustified - currBaseLineUnjustified)*(1 + adj);
line.x = currBaseLine;
prevBaseLineUnjustified = currBaseLineUnjustified;
prevBaseLine = currBaseLine;
}
}
}