81c19cc015a21398884a197277a3d08e6ef3e2e8
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / text-view-line-processor.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // FILE HEADER
19 #include <dali-toolkit/internal/controls/text-view/text-view-line-processor.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/internal/controls/text-view/text-view-word-processor.h>
23 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
24 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
25
26 namespace Dali
27 {
28
29 namespace Toolkit
30 {
31
32 namespace Internal
33 {
34
35 namespace TextViewProcessor
36 {
37
38 /////////////////////
39 // Layout info.
40 /////////////////////
41
42 LineLayoutInfo::LineLayoutInfo()
43 : mSize(),
44   mAscender( 0.f ),
45   mLineHeightOffset( 0.f ),
46   mWordsLayoutInfo(),
47   mNumberOfCharacters( 0 )
48 {
49 }
50
51 LineLayoutInfo::LineLayoutInfo( const LineLayoutInfo& line )
52 : mSize( line.mSize ),
53   mAscender( line.mAscender ),
54   mLineHeightOffset( line.mLineHeightOffset ),
55   mWordsLayoutInfo( line.mWordsLayoutInfo ),
56   mNumberOfCharacters( line.mNumberOfCharacters )
57 {
58 }
59
60 LineLayoutInfo& LineLayoutInfo::operator=( const LineLayoutInfo& line )
61 {
62   mSize = line.mSize;
63   mAscender = line.mAscender;
64   mLineHeightOffset = line.mLineHeightOffset;
65   mWordsLayoutInfo = line.mWordsLayoutInfo;
66   mNumberOfCharacters = line.mNumberOfCharacters;
67
68   return *this;
69 }
70
71 void UpdateLineLayoutInfo( LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset )
72 {
73   // Update layout info.
74   lineLayoutInfo.mSize = Size::ZERO;
75   lineLayoutInfo.mAscender = 0.f;
76   lineLayoutInfo.mNumberOfCharacters = 0u;
77   for( WordLayoutInfoContainer::iterator it = lineLayoutInfo.mWordsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordsLayoutInfo.end();
78        it != endIt;
79        ++it )
80   {
81     WordLayoutInfo& word( *it );
82
83     UpdateSize( lineLayoutInfo.mSize, word.mSize );
84     lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, word.mAscender );
85     lineLayoutInfo.mNumberOfCharacters += word.mCharactersLayoutInfo.size();
86   }
87
88   lineLayoutInfo.mSize.height += lineHeightOffset;
89   lineLayoutInfo.mLineHeightOffset = lineHeightOffset;
90 }
91
92 void CreateLineInfo( const MarkupProcessor::StyledTextArray& line,
93                      TextView::RelayoutData& relayoutData,
94                      LineLayoutInfo& lineLayoutInfo )
95 {
96   // Split the line in words.
97   // TODO: Proper RTL support.
98   MarkupProcessor::StyledTextArray convertedLine;
99   if( TextProcessor::ContainsRightToLeftCharacter( line ) )
100   {
101     // If the text is bidirectional, the characters will be converted and reordered
102     // as specified by the Unicode Bidirectional Algorithm.
103
104     // Reorders the line and converts arabic glyphs (if any).
105     TextProcessor::ConvertBidirectionalText( line,
106                                              convertedLine,
107                                              relayoutData.mCharacterLogicalToVisualMap,
108                                              relayoutData.mCharacterVisualToLogicalMap);
109   }
110   else
111   {
112     // No bidirectional text to process.
113     convertedLine = line;
114
115     // Create trivial bidirectional map tables.
116     std::size_t index = 0;
117     for( MarkupProcessor::StyledTextArray::const_iterator it = convertedLine.begin(), endIt = convertedLine.end(); it != endIt; ++it )
118     {
119       const MarkupProcessor::StyledText& styledText( *it );
120
121       for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i )
122       {
123         relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
124         relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
125         ++index;
126       }
127     }
128   }
129
130   // Split the line in words
131   std::vector<MarkupProcessor::StyledTextArray> words;
132   TextProcessor::SplitInWords( convertedLine, words );
133
134   // if last word has a new line separator, create a new word.
135   if( !words.empty() )
136   {
137     MarkupProcessor::StyledTextArray& word( *( words.end() - 1u ) );
138     if( word.size() > 1u )
139     {
140       // do nothing if the word has only one character.
141       MarkupProcessor::StyledText& styledText( *( word.end() - 1u ) );
142       if( !styledText.mText.IsEmpty() )
143       {
144         const std::size_t length = styledText.mText.GetLength();
145         if( styledText.mText[length-1u].IsNewLine() )
146         {
147           // Last character of this word is a new line character.
148
149           // Remove line separator character from current word.
150           styledText.mText.Remove( length - 1u, 1u );
151
152           // Create a new word with the line separator character.
153           MarkupProcessor::StyledText newLineText( Text( styledText.mText[length-1u] ), styledText.mStyle );
154
155           MarkupProcessor::StyledTextArray newLineWord;
156           newLineWord.push_back( newLineText );
157
158           words.push_back( newLineWord );
159         }
160       }
161     }
162   }
163
164   std::string lastCharacterFont; // Keeps the font used by the last character. It's used to set the font to a word separator.
165
166   // Traverse all words.
167   for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator wordIt = words.begin(), wordEndIt = words.end(); wordIt != wordEndIt; ++wordIt )
168   {
169     const MarkupProcessor::StyledTextArray& word( *wordIt );
170
171     // Data structures for the new word.
172     WordLayoutInfo wordLayoutInfo;
173
174     CreateWordTextInfo( word,
175                         wordLayoutInfo );
176
177     // 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
178     // avoid 'jumps' of characters when there is a switch between one text-actor per character and one text-actor per line and/or style.
179     if( WordSeparator == wordLayoutInfo.mType )
180     {
181       // If current word is a word separator (white space) then the font of the last character is set.
182       for( CharacterLayoutInfoContainer::iterator characterIt = wordLayoutInfo.mCharactersLayoutInfo.begin(), characterEndIt = wordLayoutInfo.mCharactersLayoutInfo.end();
183            characterIt != characterEndIt;
184            ++characterIt )
185       {
186         CharacterLayoutInfo& characterLayout( *characterIt );
187
188         characterLayout.mStyledText.mStyle.SetFontName( lastCharacterFont );
189       }
190     }
191     else
192     {
193       // kepps the font of the last character.
194       if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
195       {
196         lastCharacterFont = ( *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1 ) ).mStyledText.mStyle.GetFontName();
197       }
198     }
199
200     // Update the max word width figure.
201     relayoutData.mTextLayoutInfo.mMaxWordWidth = std::max( relayoutData.mTextLayoutInfo.mMaxWordWidth, wordLayoutInfo.mSize.width );
202
203     // Update layout info for the current line.
204     lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordLayoutInfo.mAscender );
205     lineLayoutInfo.mNumberOfCharacters += wordLayoutInfo.mCharactersLayoutInfo.size();
206     UpdateSize( lineLayoutInfo.mSize, wordLayoutInfo.mSize );
207
208     // Add the word to the current line.
209     lineLayoutInfo.mWordsLayoutInfo.push_back( wordLayoutInfo );
210   } // end of words
211 }
212
213 void RemoveWordsFromLine( std::size_t wordIndex,
214                           std::size_t numberOfWords,
215                           float lineHeightOffset,
216                           LineLayoutInfo& lineLayout )
217 {
218   // Removes words from a line.
219
220   // * 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.
221
222   // Remove words from layout info.
223   lineLayout.mWordsLayoutInfo.erase( lineLayout.mWordsLayoutInfo.begin() + wordIndex,
224                                      lineLayout.mWordsLayoutInfo.begin() + ( wordIndex + numberOfWords ) );
225
226   UpdateLineLayoutInfo( lineLayout, lineHeightOffset );
227 }
228
229 void RemoveCharactersFromLineInfo( TextView::RelayoutData& relayoutData,
230                                    const std::size_t numberOfCharacters,
231                                    bool& mergeWords,
232                                    bool& mergeLines,
233                                    TextInfoIndices& textInfoIndicesBegin,
234                                    TextInfoIndices& textInfoIndicesEnd,
235                                    TextInfoIndices& textInfoMergeIndicesBegin,
236                                    TextInfoIndices& textInfoMergeIndicesEnd,
237                                    LineLayoutInfo& lineLayout,
238                                    std::vector<TextActor>& removedTextActorsFromFirstWord,
239                                    std::vector<TextActor>& removedTextActorsFromLastWord )
240 {
241   const TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
242
243   if( textInfoIndicesBegin.mWordIndex < textInfoIndicesEnd.mWordIndex )
244   {
245     // Deleted text is from different words. The two different words may be merged.
246
247     // Get first word.
248     WordLayoutInfo& firstWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
249
250     // Get last word.
251     WordLayoutInfo& lastWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
252
253     // whether first or last word need to be split and merged.
254     bool mergeFromBegin = false;
255     bool mergeToEnd = false;
256
257     if( textInfoIndicesBegin.mCharacterIndex > 0u )
258     {
259       // First word is going to be split. It could be merged with the last word.
260       mergeFromBegin = true;
261       textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex;
262     }
263     else if( ( textInfoIndicesBegin.mCharacterIndex == 0u ) && ( textInfoIndicesBegin.mWordIndex > 0u ) )
264     {
265       // First word is going to be removed completely.
266       // Check if previous word could be merged.
267
268       // Get word before.
269       WordLayoutInfo& previousWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1u ) );
270       if( WordSeparator != previousWordLayout.mType )
271       {
272         // Previous word is not a word separator, so could be merged.
273         mergeFromBegin = true;
274         textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1u;
275       }
276     }
277
278     if( mergeFromBegin )
279     {
280       // First word (or previous one) could be merged. Check if last one could be merged as well.
281
282       if( textInfoIndicesEnd.mCharacterIndex + 1u < lastWordLayout.mCharactersLayoutInfo.size() )
283       {
284         // Last word is going to be split. It could be merged with the first word.
285         mergeToEnd = true;
286         textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex;
287       }
288       else if( ( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1u < lineLayout.mWordsLayoutInfo.size() ) )
289       {
290         // Last word is going to be removed completely.
291         // Check if the word after could be merged.
292
293         // Get word after.
294         WordLayoutInfo& afterWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex + 1u ) );
295         if( WordSeparator != afterWordLayout.mType )
296         {
297           // The word after is not a word separator, so could be merged.
298           mergeToEnd = true;
299           textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesEnd.mWordIndex + 1u;
300         }
301       }
302
303       // Merge words only if both words could be merged.
304       mergeWords = mergeFromBegin && mergeToEnd;
305     }
306
307     if( ( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() ) && ( textInfoIndicesEnd.mWordIndex + 1u == lineLayout.mWordsLayoutInfo.size() ) )
308     {
309       // Last word of the line is going to be removed completely.
310       // Check if it's a line separator.
311
312       if( LineSeparator == lastWordLayout.mType )
313       {
314         // The line separator is going to be removed.
315         if( textInfoIndicesBegin.mLineIndex + 1u < textLayoutInfo.mLinesLayoutInfo.size() )
316         {
317           //  Line need to be merged.
318           textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex;
319           textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesBegin.mLineIndex + 1u;
320           mergeLines= true;
321
322           ++textInfoIndicesBegin.mLineIndex; // increase both indices,
323           textInfoIndicesEnd.mLineIndex +=2u; // will delete last line.
324         }
325       }
326     }
327
328     if( textInfoIndicesBegin.mCharacterIndex > 0u )
329     {
330       // First word needs to be split.
331
332       // Store text-actors before removing them.
333       CollectTextActors( removedTextActorsFromFirstWord, firstWordLayout, textInfoIndicesBegin.mCharacterIndex, firstWordLayout.mCharactersLayoutInfo.size() );
334
335       RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
336                                 firstWordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
337                                 firstWordLayout );
338
339       ++textInfoIndicesBegin.mWordIndex; // will delete from the word after.
340     }
341
342     if( textInfoIndicesEnd.mCharacterIndex + 1u < lastWordLayout.mCharactersLayoutInfo.size() )
343     {
344       // Last word needs to be split.
345
346       // Store text-actors before removing them.
347       CollectTextActors( removedTextActorsFromLastWord, lastWordLayout, 0u, textInfoIndicesEnd.mCharacterIndex + 1u );
348
349       RemoveCharactersFromWord( 0u,
350                                 textInfoIndicesEnd.mCharacterIndex + 1u,
351                                 lastWordLayout );
352
353       if( mergeWords )
354       {
355         // This word is going to be merged, so is not needed.
356         ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
357       }
358     }
359     else if( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() )
360     {
361       // The whole last word is going to be removed.
362       ++textInfoIndicesEnd.mWordIndex; // will delete the last word.
363
364       if( ( WordSeparator == lastWordLayout.mType ) && mergeWords )
365       {
366         // The last word is a word separator and the word after is going to be merged so is not needed.
367         ++textInfoIndicesEnd.mWordIndex; // will delete the word after the last one.
368       }
369     }
370   }
371   else
372   {
373     // Chraracters to be removed are from the same word.
374
375     RemoveCharactersFromWordInfo( relayoutData,
376                                   numberOfCharacters,
377                                   mergeWords,
378                                   mergeLines,
379                                   textInfoIndicesBegin,
380                                   textInfoIndicesEnd,
381                                   textInfoMergeIndicesBegin,
382                                   textInfoMergeIndicesEnd,
383                                   lineLayout,
384                                   removedTextActorsFromFirstWord );
385   } // word indices
386 }
387
388 void SplitLine( const TextInfoIndices& indices,
389                 const PointSize& lineHeightOffset,
390                 LineLayoutInfo& firstLineLayoutInfo,
391                 LineLayoutInfo& lastLineLayoutInfo )
392 {
393   // Splits a line in two.
394   // A word may be split in two as well.
395
396   // * Split the word within the line.
397   // * Add last part of the word to the new line.
398   // * Add words from wordPosition + 1 to the end.
399   // * Update layout info of the last line.
400   // * Remove words added to the last part of the line from the first line.
401
402   // early returns!!
403   if( ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
404   {
405     // the whole line goes to the last part.
406     lastLineLayoutInfo = firstLineLayoutInfo;
407
408     firstLineLayoutInfo = LineLayoutInfo();
409
410     return;
411   }
412
413   if( !firstLineLayoutInfo.mWordsLayoutInfo.empty() )
414   {
415     const std::size_t numberOfWords = firstLineLayoutInfo.mWordsLayoutInfo.size();
416     if( indices.mWordIndex == numberOfWords - 1u )
417     {
418       const WordLayoutInfo& word( *( firstLineLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
419       if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
420       {
421         // the whole line goes to the first part.
422
423         // Just delete whatever there is in the last part of the line.
424         lastLineLayoutInfo = LineLayoutInfo();
425
426         return;
427       }
428     }
429   }
430
431   lastLineLayoutInfo = LineLayoutInfo();
432
433   // 1) Split the word whitin the line.
434   WordLayoutInfo& firstWordLayoutInfo( *( firstLineLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex ) );
435   WordLayoutInfo lastWordLayoutInfo;
436
437   SplitWord( indices.mCharacterIndex,
438              firstWordLayoutInfo,
439              lastWordLayoutInfo );
440
441   // 2) Add last part of the word to the new line.
442   if( !lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
443   {
444     lastLineLayoutInfo.mWordsLayoutInfo.push_back( lastWordLayoutInfo );
445   }
446
447   // 3) Add words from word-position + 1 to the end.
448   lastLineLayoutInfo.mWordsLayoutInfo.insert( lastLineLayoutInfo.mWordsLayoutInfo.end(),
449                                               firstLineLayoutInfo.mWordsLayoutInfo.begin() + indices.mWordIndex + 1u,
450                                               firstLineLayoutInfo.mWordsLayoutInfo.end() );
451
452   // 4) update layout info of the last line.
453   for( WordLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordsLayoutInfo.end();
454        it != endIt;
455        ++it )
456   {
457     WordLayoutInfo& layoutInfo( *it );
458
459     UpdateSize( lastLineLayoutInfo.mSize, layoutInfo.mSize );
460     lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mCharactersLayoutInfo.size();
461     lastLineLayoutInfo.mAscender = std::max( lastLineLayoutInfo.mAscender, layoutInfo.mAscender );
462   }
463   lastLineLayoutInfo.mSize.height += lineHeightOffset;
464   lastLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
465
466   // 5) Remove words added to the last part of the line from the first line.
467
468   // if the number of characters of the last word of the first line is zero, it should be removed.
469   const std::size_t index = ( firstWordLayoutInfo.mCharactersLayoutInfo.empty() ? indices.mWordIndex : indices.mWordIndex + 1u );
470
471   firstLineLayoutInfo.mWordsLayoutInfo.erase( firstLineLayoutInfo.mWordsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordsLayoutInfo.end() );
472
473   // 6) update layout info of the first line.
474   UpdateLineLayoutInfo( firstLineLayoutInfo, lineHeightOffset );
475 }
476
477 void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo,
478                 const LineLayoutInfo& lastLineLayoutInfo )
479 {
480   // Merges two given lines.
481   //
482   // Can't merge two lines if the last word of the first one is a line separator (new line character)
483
484   // Early returns.
485
486   if( lastLineLayoutInfo.mWordsLayoutInfo.empty() )
487   {
488     // Nothing to merge if last line is empty.
489     return;
490   }
491
492   if( firstLineLineLayoutInfo.mWordsLayoutInfo.empty() )
493   {
494     // If first line is empty, just copy the last line to the first one.
495     firstLineLineLayoutInfo = lastLineLayoutInfo;
496
497    return;
498   }
499
500   // Check the last word of the first line doesn't finish with a new line character.
501   WordLayoutInfo& lastWordLayout( *( firstLineLineLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
502   if( LineSeparator == lastWordLayout.mType )
503   {
504     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character." );
505   }
506
507   // 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.
508   // Otherwise both words need to be merged first.
509   const WordLayoutInfo& firstWordLayout( *lastLineLayoutInfo.mWordsLayoutInfo.begin() );
510
511   std::size_t index = 0u;
512   if( ( WordSeparator != lastWordLayout.mType ) && ( WordSeparator != firstWordLayout.mType ) && ( LineSeparator != firstWordLayout.mType ) )
513   {
514     // 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.
515     // Words need to be merged.
516
517     MergeWord( lastWordLayout,
518                firstWordLayout );
519
520     // After merging two words, the rest of the words need to be added.
521     ++index; // By increasing this index the word already merged won't be added again.
522   }
523
524   // Merge layout info
525
526   // Insert the layout of the words.
527   firstLineLineLayoutInfo.mWordsLayoutInfo.insert( firstLineLineLayoutInfo.mWordsLayoutInfo.end(),
528                                                    lastLineLayoutInfo.mWordsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordsLayoutInfo.end() );
529
530   // Update the size and other layout parameters.
531   UpdateSize( firstLineLineLayoutInfo.mSize, lastLineLayoutInfo.mSize );
532   firstLineLineLayoutInfo.mAscender = std::max( firstLineLineLayoutInfo.mAscender, lastLineLayoutInfo.mAscender );
533   firstLineLineLayoutInfo.mLineHeightOffset = std::max( firstLineLineLayoutInfo.mLineHeightOffset, lastLineLayoutInfo.mLineHeightOffset );
534   firstLineLineLayoutInfo.mNumberOfCharacters += lastLineLayoutInfo.mNumberOfCharacters;
535 }
536
537 WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
538 {
539   WordLayoutInfo layoutInfo;
540
541   if( !lineLayoutInfo.mWordsLayoutInfo.empty() )
542   {
543     layoutInfo = *( lineLayoutInfo.mWordsLayoutInfo.end() - 1u );
544   }
545
546   return layoutInfo;
547 }
548
549 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
550 {
551   CharacterLayoutInfo layoutInfo;
552
553   if( !lineLayoutInfo.mWordsLayoutInfo.empty() )
554   {
555     const WordLayoutInfo& wordInfo( *lineLayoutInfo.mWordsLayoutInfo.begin() );
556
557     layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
558   }
559
560   return layoutInfo;
561 }
562
563 CharacterLayoutInfo GetLastCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
564 {
565   const WordLayoutInfo wordInfo = GetLastWordLayoutInfo( lineLayoutInfo );
566
567   return GetLastCharacterLayoutInfo( wordInfo );
568 }
569
570 void CollectTextActorsFromLines( std::vector<TextActor>& textActors, const TextLayoutInfo& textLayoutInfo, const std::size_t lineIndexBegin, const std::size_t lineIndexEnd )
571 {
572   for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexBegin, lineEndIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexEnd;
573        lineIt != lineEndIt;
574        ++lineIt )
575   {
576     const LineLayoutInfo& line( *lineIt );
577
578     CollectTextActorsFromWords( textActors, line, 0u, line.mWordsLayoutInfo.size() );
579   }
580 }
581
582 } //namespace TextViewProcessor
583
584 } //namespace Internal
585
586 } //namespace Toolkit
587
588 } //namespace Dali