+ if(RTL == bidirectionalLineInfo.direction)
+ {
+ // If there are characters in the second half of Line.
+ if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
+ {
+ // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
+ while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
+ {
+ const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
+
+ const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
+ whiteSpaceLengthEndOfLine += calculatedAdvance;
+
+ ++characterLogicalIndex;
+ characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
+ }
+ }
+
+ // If all characters in the second half of Line are WhiteSpaces.
+ // then continue adding the WhiteSpaces from the first hel of Line.
+ // Also this is valid when the line was not splitted.
+ if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
+ {
+ extendedToSecondHalf = true; // Whether the logical index is extended to second half
+ characterLogicalIndex = 0u;
+ characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
+
+ // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
+ while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
+ {
+ const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
+
+ const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
+ whiteSpaceLengthEndOfLine += calculatedAdvance;
+
+ ++characterLogicalIndex;
+ characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
+ }
+ }
+ }
+
+ // Here's the first index of character is not WhiteSpace
+ const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
+
+ // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
+ const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
+ lastGlyphOfParagraphPlusOne,
+ charactersPerGlyphBuffer);
+
+ GlyphMetrics glyphMetrics;
+ const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
+ GetGlyphsMetrics(glyphIndex,
+ numberOfGLyphsInGroup,
+ glyphMetrics,
+ glyphsBuffer,
+ mMetrics,
+ calculatedAdvance);
+
+ float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
+
+ // Traverses the characters of the right to left paragraph.
+ // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
+ if(!extendedToSecondHalf &&
+ bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
+ {
+ for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
+ {
+ // Convert the character in the logical order into the character in the visual order.
+ const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
+ const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
+
+ const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
+
+ // Check whether this glyph comes from a character that is shaped in multiple glyphs.
+ const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
+ lastGlyphOfParagraphPlusOne,
+ charactersPerGlyphBuffer);
+
+ characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
+
+ GlyphMetrics glyphMetrics;
+ const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
+ GetGlyphsMetrics(glyphIndex,
+ numberOfGLyphsInGroup,
+ glyphMetrics,
+ glyphsBuffer,
+ mMetrics,
+ calculatedAdvance);
+
+ if(isWhiteSpace)
+ {
+ // If glyph is WhiteSpace then:
+ // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
+ // the endOfLine in RTL was the headOfLine for layouting.
+ // But for LTR added it to the endOfLine and use "advance" to accumulate length.
+ if(RTL == bidirectionalLineInfo.direction)
+ {
+ length += glyphMetrics.advance;
+ }
+ else
+ {
+ whiteSpaceLengthEndOfLine += glyphMetrics.advance;
+ }
+ penX += glyphMetrics.advance;
+ }
+ else
+ {
+ // If glyph is not whiteSpace then:
+ // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
+ // Use "advance" and "interGlyphExtraAdvance" to shift penX.
+ // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
+ // Otherwise the current length is maximum.
+ if(LTR == bidirectionalLineInfo.direction)
+ {
+ whiteSpaceLengthEndOfLine = 0.f;
+ }
+ length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
+ penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
+ }
+ }
+ }
+
+ // Continue traversing in the first half of line or in the whole line.
+ // If the second half of line was extended then continue from logical index in the first half of line
+ // Also this is valid when the line was not splitted and there were WhiteSpace.
+ // Otherwise start from first logical index in line.
+ characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
+ for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
+ {
+ // Convert the character in the logical order into the character in the visual order.
+ const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
+ const bool isWhiteSpace = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
+
+ const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
+
+ // Check whether this glyph comes from a character that is shaped in multiple glyphs.
+ const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
+ lastGlyphOfParagraphPlusOne,
+ charactersPerGlyphBuffer);
+
+ characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
+
+ GlyphMetrics glyphMetrics;
+ const float characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
+ GetGlyphsMetrics(glyphIndex,
+ numberOfGLyphsInGroup,
+ glyphMetrics,
+ glyphsBuffer,
+ mMetrics,
+ calculatedAdvance);
+
+ if(isWhiteSpace)
+ {
+ // If glyph is WhiteSpace then:
+ // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
+ // the endOfLine in RTL was the headOfLine for layouting.
+ // But for LTR added it to the endOfLine and use "advance" to accumulate length.
+ if(RTL == bidirectionalLineInfo.direction)
+ {
+ length += glyphMetrics.advance;
+ }
+ else
+ {
+ whiteSpaceLengthEndOfLine += glyphMetrics.advance;
+ }
+ penX += glyphMetrics.advance;
+ }
+ else
+ {
+ // If glyph is not whiteSpace then:
+ // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
+ // Use "advance" and "interGlyphExtraAdvance" to shift penX.
+ // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
+ // Otherwise the current length is maximum.
+ if(LTR == bidirectionalLineInfo.direction)
+ {
+ whiteSpaceLengthEndOfLine = 0.f;
+ }
+ length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
+ penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
+ }
+ }
+ }
+
+ void ReorderBiDiLayout(const Parameters& parameters,
+ LayoutBidiParameters& bidiParameters,
+ const LineLayout& currentLineLayout,
+ LineLayout& lineLayout,
+ bool breakInCharacters,
+ bool enforceEllipsisInSingleLine)
+ {
+ const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
+
+ // The last glyph to be laid-out.
+ const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
+
+ const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
+
+ const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
+ if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
+ (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
+ {
+ Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
+
+ // Sets the visual to logical map tables needed to reorder the text.
+ ReorderLine(bidirectionalParagraphInfo,
+ bidirectionalLinesInfo,
+ bidiParameters.bidiLineIndex,
+ lineLayout.characterIndex,
+ lineLayout.numberOfCharacters,
+ lineLayout.characterIndexInSecondHalfLine,
+ lineLayout.numberOfCharactersInSecondHalfLine,
+ bidiParameters.paragraphDirection);
+
+ // Recalculate the length of the line and update the layout.
+ const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
+
+ if(!bidirectionalLineInfo.isIdentity)
+ {
+ float length = 0.f;
+ float whiteSpaceLengthEndOfLine = 0.f;
+ LayoutRightToLeft(parameters,
+ bidirectionalLineInfo,
+ length,
+ whiteSpaceLengthEndOfLine);
+
+ lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
+ if(!Equals(length, lineLayout.length))
+ {
+ const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
+
+ if(isMultiline && (length > parameters.boundingBox.width))
+ {
+ if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
+ {
+ // The word doesn't fit in one line. It has to be split by character.
+
+ // Remove the last laid out glyph(s) as they doesn't fit.
+ for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
+ {
+ const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
+ lastGlyphOfParagraphPlusOne,
+ charactersPerGlyphBuffer);
+
+ const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
+
+ lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
+ lineLayout.numberOfCharacters -= numberOfCharacters;
+
+ AdjustLayout(parameters,
+ bidiParameters,
+ bidirectionalParagraphInfo,
+ lineLayout);
+
+ if(lineLayout.length < parameters.boundingBox.width)
+ {
+ break;
+ }
+
+ if(glyphIndex < numberOfGLyphsInGroup)
+ {
+ // avoids go under zero for an unsigned int.
+ break;
+ }
+
+ glyphIndex -= numberOfGLyphsInGroup;
+ }
+ }
+ else
+ {
+ lineLayout = currentLineLayout;
+
+ AdjustLayout(parameters,
+ bidiParameters,
+ bidirectionalParagraphInfo,
+ lineLayout);
+ }
+ }
+ else
+ {
+ lineLayout.length = std::max(length, lineLayout.length);
+ }
+ }
+ }
+ }
+ }
+
+ void AdjustLayout(const Parameters& parameters,
+ LayoutBidiParameters& bidiParameters,
+ const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
+ LineLayout& lineLayout)
+ {
+ Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
+
+ // Remove current reordered line.
+ bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
+
+ // Re-build the conversion table without the removed glyphs.
+ ReorderLine(bidirectionalParagraphInfo,
+ bidirectionalLinesInfo,
+ bidiParameters.bidiLineIndex,
+ lineLayout.characterIndex,
+ lineLayout.numberOfCharacters,
+ lineLayout.characterIndexInSecondHalfLine,
+ lineLayout.numberOfCharactersInSecondHalfLine,
+ bidiParameters.paragraphDirection);
+
+ const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
+
+ float length = 0.f;
+ float whiteSpaceLengthEndOfLine = 0.f;
+ LayoutRightToLeft(parameters,
+ bidirectionalLineInfo,
+ length,
+ whiteSpaceLengthEndOfLine);
+
+ lineLayout.length = length;
+ lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;