+ // Set the alternative cursor position.
+ if( cursorInfo.isSecondaryCursor )
+ {
+ // Convert the cursor position into the glyph position.
+ const CharacterIndex previousCharacterIndex = ( ( isRightToLeftParagraph != isCurrentRightToLeft ) ? logical : previousLogical );
+ const GlyphIndex previousGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + previousCharacterIndex );
+ const Length numberOfGlyphs = *( mVisualModel->mGlyphsPerCharacter.Begin() + previousCharacterIndex );
+
+ // Get the glyph position.
+ const Vector2& previousPosition = *( mVisualModel->mGlyphPositions.Begin() + previousGlyphIndex );
+
+ // Get the metrics for the group of glyphs.
+ GlyphMetrics glyphMetrics;
+ GetGlyphsMetrics( previousGlyphIndex,
+ numberOfGlyphs,
+ glyphMetrics );
+
+ // Set the cursor position and height.
+ cursorInfo.secondaryPosition.x = -glyphMetrics.xBearing + previousPosition.x + ( ( ( isLastPosition && !isCurrentRightToLeft ) ||
+ ( !isLastPosition && isCurrentRightToLeft ) ) ? glyphMetrics.advance : 0.f );
+
+ cursorInfo.secondaryCursorHeight = 0.5f * glyphMetrics.fontHeight;
+
+ cursorInfo.secondaryPosition.y = cursorInfo.lineHeight - cursorInfo.secondaryCursorHeight - line.descender - ( glyphMetrics.fontHeight - glyphMetrics.ascender );
+
+ // Update the primary cursor height as well.
+ cursorInfo.primaryCursorHeight *= 0.5f;
+ }
+ }
+
+ /**
+ * @brief Get some glyph's metrics of a group of glyphs formed as a result of shaping one character.
+ *
+ * @param[in] glyphIndex The index to the first glyph.
+ * @param[in] numberOfGlyphs The number of glyphs.
+ * @param[out] glyphMetrics Some glyph metrics (font height, advance, ascender and x bearing).
+ */
+ void GetGlyphsMetrics( GlyphIndex glyphIndex,
+ Length numberOfGlyphs,
+ GlyphMetrics& glyphMetrics ) const
+ {
+ const GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin();
+
+ const GlyphInfo& firstGlyph = *( glyphsBuffer + glyphIndex );
+
+ Text::FontMetrics fontMetrics;
+ mFontClient.GetFontMetrics( firstGlyph.fontId, fontMetrics );
+
+ glyphMetrics.fontHeight = fontMetrics.height;
+ glyphMetrics.advance = firstGlyph.advance;
+ glyphMetrics.ascender = fontMetrics.ascender;
+ glyphMetrics.xBearing = firstGlyph.xBearing;
+
+ for( unsigned int i = 1u; i < numberOfGlyphs; ++i )
+ {
+ const GlyphInfo& glyphInfo = *( glyphsBuffer + glyphIndex + i );
+
+ glyphMetrics.advance += glyphInfo.advance;
+ }
+ }
+
+ /**
+ * @brief Calculates the new cursor index.
+ *
+ * It takes into account that in some scripts multiple characters can form a glyph and all of them
+ * need to be jumped with one key event.
+ *
+ * @param[in] index The initial new index.
+ *
+ * @return The new cursor index.
+ */
+ CharacterIndex CalculateNewCursorIndex( CharacterIndex index ) const
+ {
+ CharacterIndex cursorIndex = mPrimaryCursorPosition;
+
+ const Script script = mLogicalModel->GetScript( index );
+ const GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+ const Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin();
+
+ Length numberOfCharacters = 0u;
+ if( TextAbstraction::LATIN == script )
+ {
+ // Prevents to jump the whole Latin ligatures like fi, ff, ...
+ numberOfCharacters = 1u;
+ }
+ else
+ {
+ GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index );
+ numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
+
+ while( 0u == numberOfCharacters )
+ {
+ numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex );
+ ++glyphIndex;
+ }
+ }
+
+ if( index < mPrimaryCursorPosition )
+ {
+ cursorIndex -= numberOfCharacters;
+ }
+ else
+ {
+ cursorIndex += numberOfCharacters;
+ }
+
+ return cursorIndex;