//////////////////////////////////////////////////////////////////////////////// // // 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.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 * */ static public function applyVerticalAlignmentToColumn(cont:ContainerController, verticalAlignAttr:String, lines:Array, startIndex:int, numLines:int):void { // TODO: This function doesn't work if there are any tables. var helper:IVerticalAdjustmentHelper; if (cont.rootElement.computedFormat.blockProgression == BlockProgression.RL) helper = new RL_VJHelper(ContainerController(cont)); else helper = new TB_VJHelper(ContainerController(cont)); CONFIG::debug { assert(startIndex + numLines <= lines.length && numLines > 0,"applyVerticalAlignmentToColumn: bad indices"); } var i:int; switch(verticalAlignAttr) { case VerticalAlign.MIDDLE: helper.computeMiddleAdjustment(lines[(startIndex + numLines) - 1]); for (i = startIndex; i < startIndex + numLines; i++) helper.applyMiddleAdjustment(lines[i]); break; case VerticalAlign.BOTTOM: helper.computeBottomAdjustment(lines[(startIndex + numLines) - 1]); for (i = startIndex; i < startIndex + numLines; i++) helper.applyBottomAdjustment(lines[i]); break; case VerticalAlign.JUSTIFY: 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); } } } import flash.text.engine.TextLine; import flashx.textLayout.compose.IVerticalJustificationLine; import flashx.textLayout.container.ContainerController; import flashx.textLayout.compose.TextFlowLine; import flashx.textLayout.compose.TextFlowLine; interface IVerticalAdjustmentHelper { function computeMiddleAdjustment(lastLine:IVerticalJustificationLine):void; function applyMiddleAdjustment(line:IVerticalJustificationLine):void; function computeBottomAdjustment(lastLine:IVerticalJustificationLine):void; function applyBottomAdjustment(line:IVerticalJustificationLine):void; function computeJustifyAdjustment(lineArray:Array, firstLine:int, numLines:int):void; 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; } private 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(lastLine:IVerticalJustificationLine):void { var frameBottom:Number = _textFrame.compositionHeight - Number(_textFrame.effectivePaddingBottom); adj = (frameBottom - getBottomOfLine(lastLine)) / 2; if (adj < 0) adj = 0; // no space available to redistribute } public function applyMiddleAdjustment(line:IVerticalJustificationLine):void { line.y = line.y + adj; } // the baseline of the last line should be at the bottom of the frame - paddingBottom. public function computeBottomAdjustment(lastLine:IVerticalJustificationLine):void { var frameBottom:Number = _textFrame.compositionHeight - Number(_textFrame.effectivePaddingBottom); adj = frameBottom - getBottomOfLine(lastLine); if (adj < 0) adj = 0; // no space available to redistribute } public function applyBottomAdjustment(line:IVerticalJustificationLine):void { line.y = line.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):void { adj = 0; if (numLines == 1) return; // 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.effectivePaddingBottom); var allowance:Number = frameBottom - getBottomOfLine(lastLine); if (allowance < 0) return; // 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 } [ 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; } // half of the available adjustment added to each x to shift the lines half way left across the frame public function computeMiddleAdjustment(lastTextLine:IVerticalJustificationLine):void { // ignore paddingRight its already offset var frameWidth:Number = _textFrame.compositionWidth-Number(_textFrame.effectivePaddingLeft); adj = (frameWidth + lastTextLine.x - lastTextLine.descent) / 2; if (adj < 0) adj = 0; // no space available to redistribute } public function applyMiddleAdjustment(line:IVerticalJustificationLine):void { line.x = (line.x - adj); } // the baseline of the last line should be at the bottom of the frame - paddingLeft. public function computeBottomAdjustment(lastTextLine:IVerticalJustificationLine):void { var frameWidth:Number = _textFrame.compositionWidth-Number(_textFrame.effectivePaddingLeft); adj = frameWidth + lastTextLine.x - lastTextLine.descent; if (adj < 0) adj = 0; // no space available to redistribute } public function applyBottomAdjustment(line:IVerticalJustificationLine):void { line.x = (line.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):void { adj = 0; if (numLines == 1) return; // 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.effectivePaddingLeft) - _textFrame.compositionWidth; var allowance:Number = (lastLine.x - lastLine.descent) - frameLeft; if (allowance < 0) return; // 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 } [ 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; } } }