| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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; |
| } |
| } |
| |
| } |