+ if(hasEllipsis)
+ {
+ textElided = true;
+ numberOfLaidOutGlyphs = numberOfGlyphs;
+
+ switch(ellipsisPosition)
+ {
+ case DevelText::EllipsisPosition::START:
+ {
+ numberOfActualLaidOutGlyphs = numberOfGlyphs - ellipsisLine->glyphRun.glyphIndex;
+ break;
+ }
+ case DevelText::EllipsisPosition::MIDDLE:
+ {
+ numberOfActualLaidOutGlyphs = 0u;
+ for(Length lineIndex = 0u ; lineIndex < numberOfLines ; lineIndex++)
+ {
+ numberOfActualLaidOutGlyphs += lines[lineIndex].glyphRun.numberOfGlyphs + lines[lineIndex].glyphRunSecondHalf.numberOfGlyphs;
+ }
+ break;
+ }
+ case DevelText::EllipsisPosition::END:
+ {
+ numberOfActualLaidOutGlyphs = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs;
+ break;
+ }
+ }
+ }
+ else
+ {
+ numberOfActualLaidOutGlyphs = numberOfLaidOutGlyphs = numberOfGlyphs;
+ }
+}
+
+void InsertEllipsisGlyph(
+ GlyphInfo*& glyphs,
+ GlyphIndex& indexOfEllipsis,
+ Length& numberOfRemovedGlyphs,
+ Vector2*& glyphPositions,
+ TextAbstraction::FontClient& fontClient,
+ const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns,
+ const float& modelCharacterSpacing,
+ float& calculatedAdvance,
+ const Character*& textBuffer,
+ const CharacterIndex*& glyphToCharacterMapBuffer,
+ const Length& numberOfGlyphs,
+ const bool isTailMode,
+ const LineRun*& ellipsisLine,
+ const Length& numberOfLaidOutGlyphs)
+{
+ // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
+ float firstPenX = 0.f; // Used if rtl text is elided.
+ float penY = 0.f;
+ bool firstPenSet = false;
+ bool inserted = false;
+
+ float removedGlyphsWidth = 0.f;
+
+ while(!inserted)
+ {
+ const GlyphInfo& glyphToRemove = *(glyphs + indexOfEllipsis);
+
+ if(0u != glyphToRemove.fontId)
+ {
+ // i.e. The font id of the glyph shaped from the '\n' character is zero.
+
+ // Need to reshape the glyph as the font may be different in size.
+ const GlyphInfo& ellipsisGlyph = fontClient.GetEllipsisGlyph(fontClient.GetPointSize(glyphToRemove.fontId));
+
+ if(!firstPenSet)
+ {
+ const Vector2& position = *(glyphPositions + indexOfEllipsis);
+
+ // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
+ penY = position.y + glyphToRemove.yBearing;
+
+ // Calculates the first penX which will be used if rtl text is elided.
+ firstPenX = position.x - glyphToRemove.xBearing;
+ if(firstPenX < -ellipsisGlyph.xBearing)
+ {
+ // Avoids to exceed the bounding box when rtl text is elided.
+ firstPenX = -ellipsisGlyph.xBearing;
+ }
+
+ removedGlyphsWidth = -ellipsisGlyph.xBearing;
+
+ firstPenSet = true;
+ }
+
+ const float characterSpacing = GetGlyphCharacterSpacing(indexOfEllipsis, characterSpacingGlyphRuns, modelCharacterSpacing);
+ calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + indexOfEllipsis))), characterSpacing, glyphToRemove.advance);
+ removedGlyphsWidth += std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width));
+
+ // Calculate the width of the ellipsis glyph and check if it fits.
+ const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
+ if((ellipsisGlyphWidth < removedGlyphsWidth) || (isTailMode ? (indexOfEllipsis == 0u) : (indexOfEllipsis == numberOfGlyphs - 1u)))
+ {
+ GlyphInfo& glyphInfo = *(glyphs + indexOfEllipsis);
+ Vector2& position = *(glyphPositions + indexOfEllipsis);
+ position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f;
+
+ // Replace the glyph by the ellipsis glyph.
+ glyphInfo = ellipsisGlyph;
+
+ // Change the 'x' and 'y' position of the ellipsis glyph.
+ if(position.x > firstPenX)
+ {
+ if(isTailMode)
+ {
+ // To handle case of the mixed languages (LTR then RTL) with
+ // EllipsisPosition::END and the LayoutDirection::RIGHT_TO_LEFT
+ float nextXPositions = ellipsisLine->width;
+ if(indexOfEllipsis + 1u < numberOfGlyphs)
+ {
+ Vector2& positionOfNextGlyph = *(glyphPositions + indexOfEllipsis + 1u);
+ nextXPositions = positionOfNextGlyph.x;
+ }
+
+ if(position.x > nextXPositions) // RTL language
+ {
+ if((indexOfEllipsis > 0u) && ((position.x - nextXPositions) > removedGlyphsWidth))
+ {
+ // To handle mixed directions
+ // Re-calculates the first penX which will be used if rtl text is elided.
+ firstPenX = position.x - glyphToRemove.xBearing;
+ if(firstPenX < -ellipsisGlyph.xBearing)
+ {
+ // Avoids to exceed the bounding box when rtl text is elided.
+ firstPenX = -ellipsisGlyph.xBearing;
+ }
+ //Reset the width of removed glyphs
+ removedGlyphsWidth = std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width)) - ellipsisGlyph.xBearing;
+
+ --indexOfEllipsis;
+ continue;
+ }
+ else
+ {
+ // To handle the case of RTL language with EllipsisPosition::END
+ position.x = firstPenX + removedGlyphsWidth - ellipsisGlyphWidth;
+ }
+ }
+ }
+ else
+ {
+ // To handle the case of LTR language with EllipsisPosition::START
+ position.x = firstPenX + removedGlyphsWidth - ellipsisGlyphWidth;
+ }
+ }
+ else
+ {
+ if(!isTailMode)
+ {
+ // To handle case of the mixed languages (RTL then LTR) with
+ // EllipsisPosition::START and the LayoutDirection::RIGHT_TO_LEFT
+ float nextXPositions = ellipsisLine->width;
+ if(indexOfEllipsis + 1u < numberOfGlyphs)
+ {
+ Vector2& positionOfNextGlyph = *(glyphPositions + indexOfEllipsis + 1u);
+ nextXPositions = positionOfNextGlyph.x;
+ }
+
+ if(position.x < nextXPositions) // LTR language
+ {
+ position.x = firstPenX + removedGlyphsWidth - ellipsisGlyphWidth;
+
+ if((position.x + ellipsisGlyphWidth + ellipsisGlyph.xBearing) > nextXPositions)
+ {
+ position.x -= (position.x + ellipsisGlyphWidth + ellipsisGlyph.xBearing) - nextXPositions;
+ }
+ }
+ }
+ }
+
+ position.x += ellipsisGlyph.xBearing;
+ position.y = penY - ellipsisGlyph.yBearing;
+
+ inserted = true;
+ }
+ }
+
+ if(!inserted)
+ {
+ if(isTailMode && indexOfEllipsis > 0u)
+ {
+ // Tail Mode: remove glyphs from startIndexOfEllipsis then decrement indexOfEllipsis, until arrive to index zero.
+ --indexOfEllipsis;
+ }
+ else if(!isTailMode && indexOfEllipsis < numberOfLaidOutGlyphs - 1u)
+ {
+ // Not Tail Mode: remove glyphs from startIndexOfEllipsis then increase indexOfEllipsis, until arrive to last index (numberOfGlyphs - 1u).
+ ++indexOfEllipsis;
+ }
+ else
+ {
+ // No space for the ellipsis.
+ inserted = true;
+ }
+ ++numberOfRemovedGlyphs;
+ }
+ }
+}
+
+/// 'Removes' all the glyphs after the ellipsis glyph.
+void RemoveAllGlyphsAfterEllipsisGlyph(
+ const DevelText::EllipsisPosition::Type& ellipsisPosition,
+ Length& numberOfLaidOutGlyphs,
+ const Length& numberOfActualLaidOutGlyphs,
+ const Length& numberOfRemovedGlyphs,
+ const bool isTailMode,
+ const GlyphIndex& indexOfEllipsis,
+ const LineRun*& ellipsisNextLine,
+ const LineRun*& ellipsisLine,
+ GlyphInfo*& glyphs,
+ Vector2*& glyphPositions,
+ const Length& numberOfGlyphs,
+ const GlyphIndex& startIndexOfEllipsis,
+ VisualModelPtr& visualModel)