TextView - Remove groups of words.
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / text-view-line-processor.cpp
index 18f670f..81c19cc 100644 (file)
@@ -20,7 +20,6 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/controls/text-view/text-view-word-processor.h>
-#include <dali-toolkit/internal/controls/text-view/text-view-word-group-processor.h>
 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
 
@@ -44,7 +43,7 @@ LineLayoutInfo::LineLayoutInfo()
 : mSize(),
   mAscender( 0.f ),
   mLineHeightOffset( 0.f ),
-  mWordGroupsLayoutInfo(),
+  mWordsLayoutInfo(),
   mNumberOfCharacters( 0 )
 {
 }
@@ -53,7 +52,7 @@ LineLayoutInfo::LineLayoutInfo( const LineLayoutInfo& line )
 : mSize( line.mSize ),
   mAscender( line.mAscender ),
   mLineHeightOffset( line.mLineHeightOffset ),
-  mWordGroupsLayoutInfo( line.mWordGroupsLayoutInfo ),
+  mWordsLayoutInfo( line.mWordsLayoutInfo ),
   mNumberOfCharacters( line.mNumberOfCharacters )
 {
 }
@@ -63,130 +62,327 @@ LineLayoutInfo& LineLayoutInfo::operator=( const LineLayoutInfo& line )
   mSize = line.mSize;
   mAscender = line.mAscender;
   mLineHeightOffset = line.mLineHeightOffset;
-  mWordGroupsLayoutInfo = line.mWordGroupsLayoutInfo;
+  mWordsLayoutInfo = line.mWordsLayoutInfo;
   mNumberOfCharacters = line.mNumberOfCharacters;
 
   return *this;
 }
 
-void UpdateLineLayoutInfo( TextViewProcessor::LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset )
+void UpdateLineLayoutInfo( LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset )
 {
-  lineLayoutInfo.mSize = Size();
-
-  for( WordGroupLayoutInfoContainer::iterator it = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
+  // Update layout info.
+  lineLayoutInfo.mSize = Size::ZERO;
+  lineLayoutInfo.mAscender = 0.f;
+  lineLayoutInfo.mNumberOfCharacters = 0u;
+  for( WordLayoutInfoContainer::iterator it = lineLayoutInfo.mWordsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordsLayoutInfo.end();
        it != endIt;
        ++it )
   {
-    WordGroupLayoutInfo& layoutInfo( *it );
+    WordLayoutInfo& word( *it );
 
-    UpdateSize( lineLayoutInfo.mSize, layoutInfo.mSize );
+    UpdateSize( lineLayoutInfo.mSize, word.mSize );
+    lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, word.mAscender );
+    lineLayoutInfo.mNumberOfCharacters += word.mCharactersLayoutInfo.size();
   }
+
   lineLayoutInfo.mSize.height += lineHeightOffset;
+  lineLayoutInfo.mLineHeightOffset = lineHeightOffset;
 }
 
 void CreateLineInfo( const MarkupProcessor::StyledTextArray& line,
                      TextView::RelayoutData& relayoutData,
-                     TextViewProcessor::LineLayoutInfo& lineLayoutInfo )
+                     LineLayoutInfo& lineLayoutInfo )
 {
-  // TODO: Split the line in group of words. Each group of words has only left to right characters or right to left characters but not a mix of both.
-  // TODO: set the wordgroup direction (LTR or RTL)
-  std::vector<MarkupProcessor::StyledTextArray> wordGroups;
+  // Split the line in words.
+  // TODO: Proper RTL support.
+  MarkupProcessor::StyledTextArray convertedLine;
   if( TextProcessor::ContainsRightToLeftCharacter( line ) )
   {
     // If the text is bidirectional, the characters will be converted and reordered
     // as specified by the Unicode Bidirectional Algorithm.
 
     // Reorders the line and converts arabic glyphs (if any).
-    // It also split words in different groups if there are a mix of left to right
-    // and right to left text.
-    // If the whole line is left to right or right to left all words are grouped in the same group.
     TextProcessor::ConvertBidirectionalText( line,
-                                             wordGroups,
+                                             convertedLine,
                                              relayoutData.mCharacterLogicalToVisualMap,
                                              relayoutData.mCharacterVisualToLogicalMap);
   }
   else
   {
     // No bidirectional text to process.
+    convertedLine = line;
 
-    if( !line.empty() )
+    // Create trivial bidirectional map tables.
+    std::size_t index = 0;
+    for( MarkupProcessor::StyledTextArray::const_iterator it = convertedLine.begin(), endIt = convertedLine.end(); it != endIt; ++it )
     {
-      // Add all words in a group.
-      wordGroups.push_back( line );
+      const MarkupProcessor::StyledText& styledText( *it );
 
-      // Create trivial bidirectional map tables.
-      std::size_t index = 0;
-      for( MarkupProcessor::StyledTextArray::const_iterator it = line.begin(), endIt = line.end(); it != endIt; ++it )
+      for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i )
       {
-        const MarkupProcessor::StyledText& styledText( *it );
+        relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
+        relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
+        ++index;
+      }
+    }
+  }
+
+  // Split the line in words
+  std::vector<MarkupProcessor::StyledTextArray> words;
+  TextProcessor::SplitInWords( convertedLine, words );
 
-        for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i )
+  // if last word has a new line separator, create a new word.
+  if( !words.empty() )
+  {
+    MarkupProcessor::StyledTextArray& word( *( words.end() - 1u ) );
+    if( word.size() > 1u )
+    {
+      // do nothing if the word has only one character.
+      MarkupProcessor::StyledText& styledText( *( word.end() - 1u ) );
+      if( !styledText.mText.IsEmpty() )
+      {
+        const std::size_t length = styledText.mText.GetLength();
+        if( styledText.mText[length-1u].IsNewLine() )
         {
-          relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
-          relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
-          ++index;
+          // Last character of this word is a new line character.
+
+          // Remove line separator character from current word.
+          styledText.mText.Remove( length - 1u, 1u );
+
+          // Create a new word with the line separator character.
+          MarkupProcessor::StyledText newLineText( Text( styledText.mText[length-1u] ), styledText.mStyle );
+
+          MarkupProcessor::StyledTextArray newLineWord;
+          newLineWord.push_back( newLineText );
+
+          words.push_back( newLineWord );
         }
       }
     }
   }
 
-  // Traverse all group of words.
-  for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator groupIt = wordGroups.begin(), groupEndIt = wordGroups.end(); groupIt != groupEndIt; ++groupIt )
+  std::string lastCharacterFont; // Keeps the font used by the last character. It's used to set the font to a word separator.
+
+  // Traverse all words.
+  for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator wordIt = words.begin(), wordEndIt = words.end(); wordIt != wordEndIt; ++wordIt )
   {
-    const MarkupProcessor::StyledTextArray& wordGroup( *groupIt );
+    const MarkupProcessor::StyledTextArray& word( *wordIt );
+
+    // Data structures for the new word.
+    WordLayoutInfo wordLayoutInfo;
 
-    // Data structures for the new group of words.
-    WordGroupLayoutInfo wordGroupLayoutInfo;
+    CreateWordTextInfo( word,
+                        wordLayoutInfo );
+
+    // White space's size could be different depending on the type of font. It's important to use the same font than the previous character to
+    // avoid 'jumps' of characters when there is a switch between one text-actor per character and one text-actor per line and/or style.
+    if( WordSeparator == wordLayoutInfo.mType )
+    {
+      // If current word is a word separator (white space) then the font of the last character is set.
+      for( CharacterLayoutInfoContainer::iterator characterIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), characterEndIt = wordLayoutInfo.mCharactersLayoutInfo.end();
+           characterIt != characterEndIt;
+           ++characterIt )
+      {
+        CharacterLayoutInfo& characterLayout( *characterIt );
+
+        characterLayout.mStyledText.mStyle.SetFontName( lastCharacterFont );
+      }
+    }
+    else
+    {
+      // kepps the font of the last character.
+      if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
+      {
+        lastCharacterFont = ( *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1 ) ).mStyledText.mStyle.GetFontName();
+      }
+    }
 
-    CreateWordGroupInfo( wordGroup,
-                         relayoutData.mTextLayoutInfo,
-                         wordGroupLayoutInfo );
+    // Update the max word width figure.
+    relayoutData.mTextLayoutInfo.mMaxWordWidth = std::max( relayoutData.mTextLayoutInfo.mMaxWordWidth, wordLayoutInfo.mSize.width );
 
     // Update layout info for the current line.
-    lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordGroupLayoutInfo.mAscender );
-    lineLayoutInfo.mNumberOfCharacters += wordGroupLayoutInfo.mNumberOfCharacters;
-    UpdateSize( lineLayoutInfo.mSize, wordGroupLayoutInfo.mSize );
+    lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordLayoutInfo.mAscender );
+    lineLayoutInfo.mNumberOfCharacters += wordLayoutInfo.mCharactersLayoutInfo.size();
+    UpdateSize( lineLayoutInfo.mSize, wordLayoutInfo.mSize );
 
-    // Add the group of words to the current line.
-    lineLayoutInfo.mWordGroupsLayoutInfo.push_back( wordGroupLayoutInfo );
-  } // end of group of words
+    // Add the word to the current line.
+    lineLayoutInfo.mWordsLayoutInfo.push_back( wordLayoutInfo );
+  } // end of words
 }
 
-void RemoveWordGroupsFromLine( const std::size_t groupIndex,
-                               const std::size_t numberOfGroups,
-                               const PointSize& lineHeightOffset,
-                               LineLayoutInfo& lineLayout )
+void RemoveWordsFromLine( std::size_t wordIndex,
+                          std::size_t numberOfWords,
+                          float lineHeightOffset,
+                          LineLayoutInfo& lineLayout )
 {
-  // Removes groups of words from a line.
+  // Removes words from a line.
 
-  // * Check if words or lines can be merged after removing a group of words or a line separator have to be done outside this method.
+  // * Check if words or lines can be merged after removing a number of words or a line separator needs to be done outside this method.
 
-  // * Note: Currently it's only used to remove a number of groups of words from the beginning, or
-  //         from groupIndex index to the end. This function doesn't merge groups of words (if a whole group is removed)
-  //         TODO: merge groups of words if required.
+  // Remove words from layout info.
+  lineLayout.mWordsLayoutInfo.erase( lineLayout.mWordsLayoutInfo.begin() + wordIndex,
+                                     lineLayout.mWordsLayoutInfo.begin() + ( wordIndex + numberOfWords ) );
 
-  const std::size_t groupEndIndex = groupIndex + numberOfGroups;
+  UpdateLineLayoutInfo( lineLayout, lineHeightOffset );
+}
 
-  // Remove word groups from layout info.
-  lineLayout.mWordGroupsLayoutInfo.erase( lineLayout.mWordGroupsLayoutInfo.begin() + groupIndex,
-                                          lineLayout.mWordGroupsLayoutInfo.begin() + groupEndIndex );
+void RemoveCharactersFromLineInfo( TextView::RelayoutData& relayoutData,
+                                   const std::size_t numberOfCharacters,
+                                   bool& mergeWords,
+                                   bool& mergeLines,
+                                   TextInfoIndices& textInfoIndicesBegin,
+                                   TextInfoIndices& textInfoIndicesEnd,
+                                   TextInfoIndices& textInfoMergeIndicesBegin,
+                                   TextInfoIndices& textInfoMergeIndicesEnd,
+                                   LineLayoutInfo& lineLayout,
+                                   std::vector<TextActor>& removedTextActorsFromFirstWord,
+                                   std::vector<TextActor>& removedTextActorsFromLastWord )
+{
+  const TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
 
-  // Update layout info.
-  lineLayout.mSize = Size();
-  lineLayout.mAscender = 0.f;
-  lineLayout.mNumberOfCharacters = 0;
-  for( WordGroupLayoutInfoContainer::const_iterator it = lineLayout.mWordGroupsLayoutInfo.begin(), endIt = lineLayout.mWordGroupsLayoutInfo.end();
-       it != endIt;
-       ++it )
+  if( textInfoIndicesBegin.mWordIndex < textInfoIndicesEnd.mWordIndex )
   {
-    const WordGroupLayoutInfo& group( *it );
+    // Deleted text is from different words. The two different words may be merged.
+
+    // Get first word.
+    WordLayoutInfo& firstWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
+
+    // Get last word.
+    WordLayoutInfo& lastWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
+
+    // whether first or last word need to be split and merged.
+    bool mergeFromBegin = false;
+    bool mergeToEnd = false;
+
+    if( textInfoIndicesBegin.mCharacterIndex > 0u )
+    {
+      // First word is going to be split. It could be merged with the last word.
+      mergeFromBegin = true;
+      textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex;
+    }
+    else if( ( textInfoIndicesBegin.mCharacterIndex == 0u ) && ( textInfoIndicesBegin.mWordIndex > 0u ) )
+    {
+      // First word is going to be removed completely.
+      // Check if previous word could be merged.
+
+      // Get word before.
+      WordLayoutInfo& previousWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1u ) );
+      if( WordSeparator != previousWordLayout.mType )
+      {
+        // Previous word is not a word separator, so could be merged.
+        mergeFromBegin = true;
+        textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1u;
+      }
+    }
 
-    UpdateSize( lineLayout.mSize, group.mSize );
-    lineLayout.mAscender = std::max( lineLayout.mAscender, group.mAscender );
-    lineLayout.mNumberOfCharacters += group.mNumberOfCharacters;
+    if( mergeFromBegin )
+    {
+      // First word (or previous one) could be merged. Check if last one could be merged as well.
+
+      if( textInfoIndicesEnd.mCharacterIndex + 1u < lastWordLayout.mCharactersLayoutInfo.size() )
+      {
+        // Last word is going to be split. It could be merged with the first word.
+        mergeToEnd = true;
+        textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex;
+      }
+      else if( ( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1u < lineLayout.mWordsLayoutInfo.size() ) )
+      {
+        // Last word is going to be removed completely.
+        // Check if the word after could be merged.
+
+        // Get word after.
+        WordLayoutInfo& afterWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex + 1u ) );
+        if( WordSeparator != afterWordLayout.mType )
+        {
+          // The word after is not a word separator, so could be merged.
+          mergeToEnd = true;
+          textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex + 1u;
+        }
+      }
+
+      // Merge words only if both words could be merged.
+      mergeWords = mergeFromBegin && mergeToEnd;
+    }
+
+    if( ( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1u == lineLayout.mWordsLayoutInfo.size() ) )
+    {
+      // Last word of the line is going to be removed completely.
+      // Check if it's a line separator.
+
+      if( LineSeparator == lastWordLayout.mType )
+      {
+        // The line separator is going to be removed.
+        if( textInfoIndicesBegin.mLineIndex + 1u < textLayoutInfo.mLinesLayoutInfo.size() )
+        {
+          //  Line need to be merged.
+          textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex;
+          textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesBegin.mLineIndex + 1u;
+          mergeLines= true;
+
+          ++textInfoIndicesBegin.mLineIndex; // increase both indices,
+          textInfoIndicesEnd.mLineIndex +=2u; // will delete last line.
+        }
+      }
+    }
+
+    if( textInfoIndicesBegin.mCharacterIndex > 0u )
+    {
+      // First word needs to be split.
+
+      // Store text-actors before removing them.
+      CollectTextActors( removedTextActorsFromFirstWord, firstWordLayout, textInfoIndicesBegin.mCharacterIndex, firstWordLayout.mCharactersLayoutInfo.size() );
+
+      RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
+                                firstWordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
+                                firstWordLayout );
+
+      ++textInfoIndicesBegin.mWordIndex; // will delete from the word after.
+    }
+
+    if( textInfoIndicesEnd.mCharacterIndex + 1u < lastWordLayout.mCharactersLayoutInfo.size() )
+    {
+      // Last word needs to be split.
+
+      // Store text-actors before removing them.
+      CollectTextActors( removedTextActorsFromLastWord, lastWordLayout, 0u, textInfoIndicesEnd.mCharacterIndex + 1u );
+
+      RemoveCharactersFromWord( 0u,
+                                textInfoIndicesEnd.mCharacterIndex + 1u,
+                                lastWordLayout );
+
+      if( mergeWords )
+      {
+        // This word is going to be merged, so is not needed.
+        ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
+      }
+    }
+    else if( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() )
+    {
+      // The whole last word is going to be removed.
+      ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
+
+      if( ( WordSeparator == lastWordLayout.mType ) && mergeWords )
+      {
+        // The last word is a word separator and the word after is going to be merged so is not needed.
+        ++textInfoIndicesEnd.mWordIndex; // will delete the word after the last one.
+      }
+    }
   }
-  lineLayout.mSize.height += lineHeightOffset;
-  lineLayout.mLineHeightOffset = lineHeightOffset;
+  else
+  {
+    // Chraracters to be removed are from the same word.
+
+    RemoveCharactersFromWordInfo( relayoutData,
+                                  numberOfCharacters,
+                                  mergeWords,
+                                  mergeLines,
+                                  textInfoIndicesBegin,
+                                  textInfoIndicesEnd,
+                                  textInfoMergeIndicesBegin,
+                                  textInfoMergeIndicesEnd,
+                                  lineLayout,
+                                  removedTextActorsFromFirstWord );
+  } // word indices
 }
 
 void SplitLine( const TextInfoIndices& indices,
@@ -195,16 +391,16 @@ void SplitLine( const TextInfoIndices& indices,
                 LineLayoutInfo& lastLineLayoutInfo )
 {
   // Splits a line in two.
-  // A group of words and a word may be split in two as well.
+  // A word may be split in two as well.
 
-  // * Split the group of words within the line.
-  // * Add last part of the group of words to the new line.
-  // * Add groups of words from groupPosition + 1 to the end.
+  // * Split the word within the line.
+  // * Add last part of the word to the new line.
+  // * Add words from wordPosition + 1 to the end.
   // * Update layout info of the last line.
-  // * Remove groups of words added to the last part of the line from the first line.
+  // * Remove words added to the last part of the line from the first line.
 
   // early returns!!
-  if( ( 0 == indices.mGroupIndex ) && ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
+  if( ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
   {
     // the whole line goes to the last part.
     lastLineLayoutInfo = firstLineLayoutInfo;
@@ -214,87 +410,68 @@ void SplitLine( const TextInfoIndices& indices,
     return;
   }
 
-  if( !firstLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
+  if( !firstLineLayoutInfo.mWordsLayoutInfo.empty() )
   {
-    const std::size_t numberOfGroups = firstLineLayoutInfo.mWordGroupsLayoutInfo.size();
-    if( indices.mGroupIndex == numberOfGroups - 1 )
+    const std::size_t numberOfWords = firstLineLayoutInfo.mWordsLayoutInfo.size();
+    if( indices.mWordIndex == numberOfWords - 1u )
     {
-      const WordGroupLayoutInfo& group( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
-
-      const std::size_t numberOfWords = group.mWordsLayoutInfo.size();
-      if( indices.mWordIndex == numberOfWords - 1 )
+      const WordLayoutInfo& word( *( firstLineLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
+      if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
       {
-        const WordLayoutInfo& word( *( group.mWordsLayoutInfo.end() - 1 ) );
-        if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
-        {
-          // the whole line goes to the first part.
+        // the whole line goes to the first part.
 
-          // Just delete whatever there is in the last part of the line.
-          lastLineLayoutInfo = LineLayoutInfo();
+        // Just delete whatever there is in the last part of the line.
+        lastLineLayoutInfo = LineLayoutInfo();
 
-          return;
-        }
+        return;
       }
     }
   }
 
   lastLineLayoutInfo = LineLayoutInfo();
 
-  // 1) Split the group of words whitin the line.
-  WordGroupLayoutInfo& firstWordGroupLayoutInfo( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex ) );
-  WordGroupLayoutInfo lastWordGroupLayoutInfo;
+  // 1) Split the word whitin the line.
+  WordLayoutInfo& firstWordLayoutInfo( *( firstLineLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex ) );
+  WordLayoutInfo lastWordLayoutInfo;
 
-  SplitWordGroup( indices,
-                  firstWordGroupLayoutInfo,
-                  lastWordGroupLayoutInfo );
+  SplitWord( indices.mCharacterIndex,
+             firstWordLayoutInfo,
+             lastWordLayoutInfo );
 
-  // 2) Add last part of the group of words to the new line.
-  if( !lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
+  // 2) Add last part of the word to the new line.
+  if( !lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
   {
-    lastLineLayoutInfo.mWordGroupsLayoutInfo.push_back( lastWordGroupLayoutInfo );
+    lastLineLayoutInfo.mWordsLayoutInfo.push_back( lastWordLayoutInfo );
   }
 
-  // 3) Add groups from group-position + 1 to the end.
-  lastLineLayoutInfo.mWordGroupsLayoutInfo.insert( lastLineLayoutInfo.mWordGroupsLayoutInfo.end(),
-                                                   firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex + 1, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
+  // 3) Add words from word-position + 1 to the end.
+  lastLineLayoutInfo.mWordsLayoutInfo.insert( lastLineLayoutInfo.mWordsLayoutInfo.end(),
+                                              firstLineLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex + 1u,
+                                              firstLineLayoutInfo.mWordsLayoutInfo.end() );
 
   // 4) update layout info of the last line.
-  for( WordGroupLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordGroupsLayoutInfo.end();
+  for( WordLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordsLayoutInfo.end();
        it != endIt;
        ++it )
   {
-    WordGroupLayoutInfo& layoutInfo( *it );
+    WordLayoutInfo& layoutInfo( *it );
 
-    lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
     UpdateSize( lastLineLayoutInfo.mSize, layoutInfo.mSize );
+    lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size();
     lastLineLayoutInfo.mAscender = std::max( lastLineLayoutInfo.mAscender, layoutInfo.mAscender );
   }
   lastLineLayoutInfo.mSize.height += lineHeightOffset;
   lastLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
 
-  // 5) Remove groups of words added to the last part of the line from the first line.
+  // 5) Remove words added to the last part of the line from the first line.
 
-  // if the number of characters of the last group of words of the first line is zero, it should be removed.
-  const std::size_t index = ( 0 == firstWordGroupLayoutInfo.mNumberOfCharacters ? indices.mGroupIndex : indices.mGroupIndex + 1 );
+  // if the number of characters of the last word of the first line is zero, it should be removed.
+  const std::size_t index = ( firstWordLayoutInfo.mCharactersLayoutInfo.empty() ? indices.mWordIndex : indices.mWordIndex + 1u );
 
-  firstLineLayoutInfo.mWordGroupsLayoutInfo.erase( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
+  firstLineLayoutInfo.mWordsLayoutInfo.erase( firstLineLayoutInfo.mWordsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordsLayoutInfo.end() );
 
   // 6) update layout info of the first line.
-  firstLineLayoutInfo.mNumberOfCharacters = 0;
-  firstLineLayoutInfo.mSize = Size();
-  firstLineLayoutInfo.mAscender = 0.f;
-  for( WordGroupLayoutInfoContainer::iterator it = firstLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = firstLineLayoutInfo.mWordGroupsLayoutInfo.end();
-       it != endIt;
-       ++it )
-  {
-    WordGroupLayoutInfo& layoutInfo( *it );
-
-    firstLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
-    UpdateSize( firstLineLayoutInfo.mSize, layoutInfo.mSize );
-    firstLineLayoutInfo.mAscender = std::max( firstLineLayoutInfo.mAscender, layoutInfo.mAscender );
-  }
-  firstLineLayoutInfo.mSize.height += lineHeightOffset;
-  firstLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
+  UpdateLineLayoutInfo( firstLineLayoutInfo, lineHeightOffset );
 }
 
 void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo,
@@ -306,78 +483,64 @@ void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo,
 
   // Early returns.
 
-  if( lastLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
+  if( lastLineLayoutInfo.mWordsLayoutInfo.empty() )
   {
     // Nothing to merge if last line is empty.
     return;
   }
 
-  if( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
+  if( firstLineLineLayoutInfo.mWordsLayoutInfo.empty() )
   {
     // If first line is empty, just copy the last line to the first one.
     firstLineLineLayoutInfo = lastLineLayoutInfo;
 
-    return;
+   return;
   }
 
-  if( 1 == firstLineLineLayoutInfo.mWordGroupsLayoutInfo.size() )
-  {
-    WordGroupLayoutInfo& wordGroupLayout( *firstLineLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
-
-    if( wordGroupLayout.mWordsLayoutInfo.empty() )
-    {
-      // If first line is empty, just copy the last line to the first one.
-      firstLineLineLayoutInfo = lastLineLayoutInfo;
-
-      return;
-    }
-  }
-
-  // Check the last word of the last group of the first line doesn't finish with a new line character.
-  WordGroupLayoutInfo& lastWordGroupLayout( *( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
-  WordLayoutInfo& lastWordLayout( *( lastWordGroupLayout.mWordsLayoutInfo.end() - 1 ) );
+  // Check the last word of the first line doesn't finish with a new line character.
+  WordLayoutInfo& lastWordLayout( *( firstLineLineLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
   if( LineSeparator == lastWordLayout.mType )
   {
     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character." );
   }
 
-  // If the last group of the first line has the same direction than the first group of the last line, both lines can be concatenated.
-  // Otherwise, both groups have to be merged first.
-  const WordGroupLayoutInfo& firstWordGroupLayout( *lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
+  // If the las word of the first line or the first word of the last line is a white space, both lines can be concatenated.
+  // Otherwise both words need to be merged first.
+  const WordLayoutInfo& firstWordLayout( *lastLineLayoutInfo.mWordsLayoutInfo.begin() );
 
-  std::size_t index = 0;
-  if( lastWordGroupLayout.mDirection == firstWordGroupLayout.mDirection )
+  std::size_t index = 0u;
+  if( ( WordSeparator != lastWordLayout.mType ) && ( WordSeparator != firstWordLayout.mType ) && ( LineSeparator != firstWordLayout.mType ) )
   {
-    // Both groups of words have the same direction. They need to be merged.
-    MergeWordGroup( lastWordGroupLayout,
-                    firstWordGroupLayout );
+    // Last word of the first line is not a word separator and first word of the last line is not a word or line separator.
+    // Words need to be merged.
+
+    MergeWord( lastWordLayout,
+               firstWordLayout );
 
-    // After merging two groups of words, the rest of groups need to be added.
-    ++index; // By increasing this index the group of words already merged won't be added again.
+    // After merging two words, the rest of the words need to be added.
+    ++index; // By increasing this index the word already merged won't be added again.
   }
 
   // Merge layout info
-  firstLineLineLayoutInfo.mWordGroupsLayoutInfo.insert( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end(),
-                                                        lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordGroupsLayoutInfo.end() );
+
+  // Insert the layout of the words.
+  firstLineLineLayoutInfo.mWordsLayoutInfo.insert( firstLineLineLayoutInfo.mWordsLayoutInfo.end(),
+                                                   lastLineLayoutInfo.mWordsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordsLayoutInfo.end() );
+
+  // Update the size and other layout parameters.
   UpdateSize( firstLineLineLayoutInfo.mSize, lastLineLayoutInfo.mSize );
   firstLineLineLayoutInfo.mAscender = std::max( firstLineLineLayoutInfo.mAscender, lastLineLayoutInfo.mAscender );
   firstLineLineLayoutInfo.mLineHeightOffset = std::max( firstLineLineLayoutInfo.mLineHeightOffset, lastLineLayoutInfo.mLineHeightOffset );
   firstLineLineLayoutInfo.mNumberOfCharacters += lastLineLayoutInfo.mNumberOfCharacters;
-
 }
 
 WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
 {
   WordLayoutInfo layoutInfo;
 
-  if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
+  if( !lineLayoutInfo.mWordsLayoutInfo.empty() )
   {
-    const WordGroupLayoutInfo& groupInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
-
-    if( !groupInfo.mWordsLayoutInfo.empty() )
-    {
-      layoutInfo = *( groupInfo.mWordsLayoutInfo.end() - 1 );
-    }
+    layoutInfo = *( lineLayoutInfo.mWordsLayoutInfo.end() - 1u );
   }
 
   return layoutInfo;
@@ -387,16 +550,11 @@ CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayou
 {
   CharacterLayoutInfo layoutInfo;
 
-  if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
+  if( !lineLayoutInfo.mWordsLayoutInfo.empty() )
   {
-    const WordGroupLayoutInfo& groupInfo( *lineLayoutInfo.mWordGroupsLayoutInfo.begin() );
+    const WordLayoutInfo& wordInfo( *lineLayoutInfo.mWordsLayoutInfo.begin() );
 
-    if( !groupInfo.mWordsLayoutInfo.empty() )
-    {
-      const WordLayoutInfo& wordInfo( *groupInfo.mWordsLayoutInfo.begin() );
-
-      layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
-    }
+    layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
   }
 
   return layoutInfo;
@@ -417,7 +575,7 @@ void CollectTextActorsFromLines( std::vector<TextActor>& textActors, const TextL
   {
     const LineLayoutInfo& line( *lineIt );
 
-    CollectTextActorsFromGroups( textActors, line, 0, line.mWordGroupsLayoutInfo.size() );
+    CollectTextActorsFromWords( textActors, line, 0u, line.mWordsLayoutInfo.size() );
   }
 }