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