Add MIN_LINE_SIZE property
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2020 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 // CLASS HEADER
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
20
21 // EXTERNAL INCLUDES
22 #include <limits>
23 #include <cmath>
24 #include <dali/integration-api/debug.h>
25 #include <dali/devel-api/text-abstraction/font-client.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Text
40 {
41
42 namespace Layout
43 {
44
45 namespace
46 {
47
48 #if defined(DEBUG_ENABLED)
49   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
50 #endif
51
52 const float MAX_FLOAT = std::numeric_limits<float>::max();
53 const CharacterDirection LTR = false;
54 const CharacterDirection RTL = !LTR;
55 const float LINE_SPACING = 0.f;
56 const float MIN_LINE_SIZE = 0.f;
57
58 inline bool isEmptyLineAtLast( const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line )
59 {
60   return ( (*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End() );
61 }
62
63 } //namespace
64
65 /**
66  * @brief Stores temporary layout info of the line.
67  */
68 struct LineLayout
69 {
70   LineLayout()
71   : glyphIndex{ 0u },
72     characterIndex{ 0u },
73     numberOfGlyphs{ 0u },
74     numberOfCharacters{ 0u },
75     ascender{ -MAX_FLOAT },
76     descender{ MAX_FLOAT },
77     lineSpacing{ 0.f },
78     penX{ 0.f },
79     previousAdvance{ 0.f },
80     length{ 0.f },
81     whiteSpaceLengthEndOfLine{ 0.f },
82     direction{ LTR }
83   {}
84
85   ~LineLayout()
86   {}
87
88   void Clear()
89   {
90     glyphIndex = 0u;
91     characterIndex = 0u;
92     numberOfGlyphs = 0u;
93     numberOfCharacters = 0u;
94     ascender = -MAX_FLOAT;
95     descender = MAX_FLOAT;
96     direction = LTR;
97   }
98
99   GlyphIndex         glyphIndex;                ///< Index of the first glyph to be laid-out.
100   CharacterIndex     characterIndex;            ///< Index of the first character to be laid-out.
101   Length             numberOfGlyphs;            ///< The number of glyph which fit in one line.
102   Length             numberOfCharacters;        ///< The number of characters which fit in one line.
103   float              ascender;                  ///< The maximum ascender of all fonts in the line.
104   float              descender;                 ///< The minimum descender of all fonts in the line.
105   float              lineSpacing;               ///< The line spacing
106   float              penX;                      ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
107   float              previousAdvance;           ///< The advance of the previous glyph.
108   float              length;                    ///< The current length of the line.
109   float              whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
110   CharacterDirection direction;
111 };
112
113 struct LayoutBidiParameters
114 {
115   void Clear()
116   {
117     paragraphDirection = LTR;
118     bidiParagraphIndex = 0u;
119     bidiLineIndex = 0u;
120     isBidirectional = false;
121   }
122
123   CharacterDirection paragraphDirection = LTR;   ///< The paragraph's direction.
124   BidirectionalRunIndex bidiParagraphIndex = 0u; ///< Index to the paragraph's bidi info.
125   BidirectionalLineRunIndex bidiLineIndex = 0u;  ///< Index where to insert the next bidi line info.
126   bool isBidirectional = false;                  ///< Whether the text is bidirectional.
127 };
128
129 struct Engine::Impl
130 {
131   Impl()
132   : mLayout{ Layout::Engine::SINGLE_LINE_BOX },
133     mCursorWidth{ 0.f },
134     mDefaultLineSpacing{ LINE_SPACING },
135     mDefaultLineSize{ MIN_LINE_SIZE }
136   {
137   }
138
139   /**
140    * @brief Updates the line ascender and descender with the metrics of a new font.
141    *
142    * @param[in] glyphMetrics The metrics of the new font.
143    * @param[in,out] lineLayout The line layout.
144    */
145   void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout )
146   {
147     Text::FontMetrics fontMetrics;
148     if( 0u != glyphMetrics.fontId )
149     {
150       mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics );
151     }
152     else
153     {
154       fontMetrics.ascender = glyphMetrics.fontHeight;
155       fontMetrics.descender = 0.f;
156       fontMetrics.height = fontMetrics.ascender;
157       fontMetrics.underlinePosition = 0.f;
158       fontMetrics.underlineThickness = 1.f;
159     }
160
161     // Sets the maximum ascender.
162     lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender );
163
164     // Sets the minimum descender.
165     lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
166
167     // Sets the line size
168     lineLayout.lineSpacing = mDefaultLineSize - ( lineLayout.ascender + -lineLayout.descender );
169     lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
170
171     // Add the line spacing
172     lineLayout.lineSpacing += mDefaultLineSpacing;
173   }
174
175   /**
176    * @brief Merges a temporary line layout into the line layout.
177    *
178    * @param[in,out] lineLayout The line layout.
179    * @param[in] tmpLineLayout A temporary line layout.
180    */
181   void MergeLineLayout( LineLayout& lineLayout,
182                         const LineLayout& tmpLineLayout )
183   {
184     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
185     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
186
187     lineLayout.penX = tmpLineLayout.penX;
188     lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
189
190     lineLayout.length = tmpLineLayout.length;
191     lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
192
193     // Sets the maximum ascender.
194     lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender );
195
196     // Sets the minimum descender.
197     lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender );
198   }
199
200   void LayoutRightToLeft( const Parameters& parameters,
201                           const BidirectionalLineInfoRun& bidirectionalLineInfo,
202                           float& length,
203                           float& whiteSpaceLengthEndOfLine )
204   {
205     const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
206     const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
207     const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
208     const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
209
210     const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
211     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
212
213     CharacterIndex characterLogicalIndex = 0u;
214     CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
215
216     if( RTL == bidirectionalLineInfo.direction )
217     {
218       while( TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) ) )
219       {
220         const GlyphInfo& glyphInfo = *( glyphsBuffer + *( charactersToGlyphsBuffer + characterVisualIndex ) );
221
222         whiteSpaceLengthEndOfLine += glyphInfo.advance;
223
224         ++characterLogicalIndex;
225         characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
226       }
227     }
228
229     const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
230
231     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
232     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
233                                                                    lastGlyphOfParagraphPlusOne,
234                                                                    charactersPerGlyphBuffer );
235
236     GlyphMetrics glyphMetrics;
237     GetGlyphsMetrics( glyphIndex,
238                       numberOfGLyphsInGroup,
239                       glyphMetrics,
240                       glyphsBuffer,
241                       mMetrics );
242
243     float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
244
245     // Traverses the characters of the right to left paragraph.
246     for( ; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters; )
247     {
248       // Convert the character in the logical order into the character in the visual order.
249       const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *( bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex );
250       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) );
251
252       const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
253
254       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
255       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
256                                                                      lastGlyphOfParagraphPlusOne,
257                                                                      charactersPerGlyphBuffer );
258
259       characterLogicalIndex += *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
260
261       GlyphMetrics glyphMetrics;
262       GetGlyphsMetrics( glyphIndex,
263                         numberOfGLyphsInGroup,
264                         glyphMetrics,
265                         glyphsBuffer,
266                         mMetrics );
267
268       if( isWhiteSpace )
269       {
270         if( RTL == bidirectionalLineInfo.direction )
271         {
272           length += glyphMetrics.advance;
273         }
274         else
275         {
276           whiteSpaceLengthEndOfLine += glyphMetrics.advance;
277         }
278         penX += glyphMetrics.advance;
279       }
280       else
281       {
282         if( LTR == bidirectionalLineInfo.direction )
283         {
284           whiteSpaceLengthEndOfLine = 0.f;
285         }
286         length = std::max( length, penX + glyphMetrics.xBearing + glyphMetrics.width );
287         penX += ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
288       }
289     }
290   }
291
292   void ReorderBiDiLayout( const Parameters& parameters,
293                           LayoutBidiParameters& bidiParameters,
294                           const LineLayout& currentLineLayout,
295                           LineLayout& lineLayout,
296                           bool breakInCharacters )
297   {
298     const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
299
300     // The last glyph to be laid-out.
301     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
302
303     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
304
305     const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
306     if( ( lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex ) &&
307         ( lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters ) )
308     {
309       Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
310
311       // Sets the visual to logical map tables needed to reorder the text.
312       ReorderLine( bidirectionalParagraphInfo,
313                    bidirectionalLinesInfo,
314                    bidiParameters.bidiLineIndex,
315                    lineLayout.characterIndex,
316                    lineLayout.numberOfCharacters,
317                    bidiParameters.paragraphDirection );
318
319       // Recalculate the length of the line and update the layout.
320       const BidirectionalLineInfoRun& bidirectionalLineInfo = *( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
321
322       if( !bidirectionalLineInfo.isIdentity )
323       {
324         float length = 0.f;
325         float whiteSpaceLengthEndOfLine = 0.f;
326         LayoutRightToLeft( parameters,
327                            bidirectionalLineInfo,
328                            length,
329                            whiteSpaceLengthEndOfLine );
330
331         lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
332         if( !Equals( length, lineLayout.length ) )
333         {
334           const bool isMultiline = mLayout == MULTI_LINE_BOX;
335
336           if( isMultiline && ( length > parameters.boundingBox.width ) )
337           {
338             if( breakInCharacters || ( isMultiline && ( 0u == currentLineLayout.numberOfGlyphs ) ) )
339             {
340               // The word doesn't fit in one line. It has to be split by character.
341
342               // Remove the last laid out glyph(s) as they doesn't fit.
343               for( GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex; )
344               {
345                 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
346                                                                                lastGlyphOfParagraphPlusOne,
347                                                                                charactersPerGlyphBuffer );
348
349                 const Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
350
351                 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
352                 lineLayout.numberOfCharacters -= numberOfCharacters;
353
354                 AdjustLayout( parameters,
355                               bidiParameters,
356                               bidirectionalParagraphInfo,
357                               lineLayout );
358
359                 if( lineLayout.length < parameters.boundingBox.width )
360                 {
361                   break;
362                 }
363
364                 if( glyphIndex < numberOfGLyphsInGroup )
365                 {
366                   // avoids go under zero for an unsigned int.
367                   break;
368                 }
369
370                 glyphIndex -= numberOfGLyphsInGroup;
371               }
372             }
373             else
374             {
375               lineLayout  = currentLineLayout;
376
377               AdjustLayout( parameters,
378                             bidiParameters,
379                             bidirectionalParagraphInfo,
380                             lineLayout );
381             }
382           }
383           else
384           {
385             lineLayout.length = std::max( length, lineLayout.length );
386           }
387         }
388       }
389     }
390   }
391
392   void AdjustLayout( const Parameters& parameters,
393                      LayoutBidiParameters& bidiParameters,
394                      const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
395                      LineLayout& lineLayout )
396   {
397     Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
398
399     // Remove current reordered line.
400     bidirectionalLinesInfo.Erase( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
401
402     // Re-build the conversion table without the removed glyphs.
403     ReorderLine( bidirectionalParagraphInfo,
404                  bidirectionalLinesInfo,
405                  bidiParameters.bidiLineIndex,
406                  lineLayout.characterIndex,
407                  lineLayout.numberOfCharacters,
408                  bidiParameters.paragraphDirection );
409
410     const BidirectionalLineInfoRun& bidirectionalLineInfo = *( bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex );
411
412     float length = 0.f;
413     float whiteSpaceLengthEndOfLine = 0.f;
414     LayoutRightToLeft( parameters,
415                        bidirectionalLineInfo,
416                        length,
417                        whiteSpaceLengthEndOfLine );
418
419     lineLayout.length = length;
420     lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
421   }
422
423   /**
424    * Retrieves the line layout for a given box width.
425    *
426    * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
427    *       of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
428    *       to reorder the line and recalculate its length.
429    *
430
431    * @param[in] parameters The layout parameters.
432    * @param[] bidiParameters Bidirectional info for the current line.
433    * @param[out] lineLayout The line layout.
434    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
435    */
436   void GetLineLayoutForBox( const Parameters& parameters,
437                             LayoutBidiParameters& bidiParameters,
438                             LineLayout& lineLayout,
439                             bool completelyFill )
440   {
441     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
442     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex );
443
444     const Character* const textBuffer = parameters.textModel->mLogicalModel->mText.Begin();
445     const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
446     const GlyphInfo* const glyphsBuffer = parameters.textModel->mVisualModel->mGlyphs.Begin();
447     const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
448     const LineBreakInfo* const lineBreakInfoBuffer = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
449
450     const float outlineWidth = static_cast<float>( parameters.textModel->GetOutlineWidth() );
451     const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
452
453     const bool isMultiline = mLayout == MULTI_LINE_BOX;
454     const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD;
455
456     // The last glyph to be laid-out.
457     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
458
459     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
460     // In the case the line starts with a right to left character, if the width is longer than the advance,
461     // the difference needs to be added to the line length.
462
463     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
464     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
465                                                                    lastGlyphOfParagraphPlusOne,
466                                                                    charactersPerGlyphBuffer );
467
468     GlyphMetrics glyphMetrics;
469     GetGlyphsMetrics( lineLayout.glyphIndex,
470                       numberOfGLyphsInGroup,
471                       glyphMetrics,
472                       glyphsBuffer,
473                       mMetrics );
474
475     // Set the direction of the first character of the line.
476     lineLayout.characterIndex = *( glyphsToCharactersBuffer + lineLayout.glyphIndex );
477
478     // Stores temporary line layout which has not been added to the final line layout.
479     LineLayout tmpLineLayout;
480
481     // Initialize the start point.
482
483     // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
484     // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
485     // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
486     tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
487
488     // Calculate the line height if there is no characters.
489     FontId lastFontId = glyphMetrics.fontId;
490     UpdateLineHeight( glyphMetrics, tmpLineLayout );
491
492     bool oneWordLaidOut = false;
493
494     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
495          glyphIndex < lastGlyphOfParagraphPlusOne; )
496     {
497       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex );
498
499       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
500       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
501                                                                      lastGlyphOfParagraphPlusOne,
502                                                                      charactersPerGlyphBuffer );
503
504       GlyphMetrics glyphMetrics;
505       GetGlyphsMetrics( glyphIndex,
506                         numberOfGLyphsInGroup,
507                         glyphMetrics,
508                         glyphsBuffer,
509                         mMetrics );
510
511       const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup  == totalNumberOfGlyphs;
512
513       // Check if the font of the current glyph is the same of the previous one.
514       // If it's different the ascender and descender need to be updated.
515       if( lastFontId != glyphMetrics.fontId )
516       {
517         UpdateLineHeight( glyphMetrics, tmpLineLayout );
518         lastFontId = glyphMetrics.fontId;
519       }
520
521       // Get the character indices for the current glyph. The last character index is needed
522       // because there are glyphs formed by more than one character but their break info is
523       // given only for the last character.
524       const Length charactersPerGlyph = *( charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
525       const bool hasCharacters = charactersPerGlyph > 0u;
526       const CharacterIndex characterFirstIndex = *( glyphsToCharactersBuffer + glyphIndex );
527       const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
528
529       // Get the line break info for the current character.
530       const LineBreakInfo lineBreakInfo = hasCharacters ? *( lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
531
532       // Increase the number of characters.
533       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
534
535       // Increase the number of glyphs.
536       tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
537
538       // Check whether is a white space.
539       const Character character = *( textBuffer + characterFirstIndex );
540       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
541
542       // Calculate the length of the line.
543
544       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
545       const float previousTmpPenX = tmpLineLayout.penX;
546       const float previousTmpAdvance = tmpLineLayout.previousAdvance;
547       const float previousTmpLength = tmpLineLayout.length;
548       const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
549
550       if( isWhiteSpace )
551       {
552         // Add the length to the length of white spaces at the end of the line.
553         tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
554       }
555       else
556       {
557         tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
558         tmpLineLayout.previousAdvance = ( glyphMetrics.advance + parameters.interGlyphExtraAdvance );
559
560         tmpLineLayout.length = std::max( tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width );
561
562         // Clear the white space length at the end of the line.
563         tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
564       }
565
566       // Check if the accumulated length fits in the width of the box.
567       if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
568           ( tmpLineLayout.length > parameters.boundingBox.width ) )
569       {
570         // Current word does not fit in the box's width.
571         if( !oneWordLaidOut || completelyFill )
572         {
573           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Break the word by character\n" );
574
575           // The word doesn't fit in the control's width. It needs to be split by character.
576           if( tmpLineLayout.numberOfGlyphs > 0u )
577           {
578             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
579             tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
580
581             tmpLineLayout.penX = previousTmpPenX;
582             tmpLineLayout.previousAdvance = previousTmpAdvance;
583             tmpLineLayout.length = previousTmpLength;
584             tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
585           }
586
587           // Add part of the word to the line layout.
588           MergeLineLayout( lineLayout, tmpLineLayout );
589         }
590         else
591         {
592           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Current word does not fit.\n" );
593         }
594
595         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
596
597         // Reorder the RTL line.
598         if( bidiParameters.isBidirectional )
599         {
600           ReorderBiDiLayout( parameters,
601                              bidiParameters,
602                              lineLayout,
603                              lineLayout,
604                              true );
605         }
606
607         return;
608       }
609
610       if( ( isMultiline || isLastGlyph ) &&
611           ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
612       {
613         LineLayout currentLineLayout = lineLayout;
614
615         // Must break the line. Update the line layout and return.
616         MergeLineLayout( lineLayout, tmpLineLayout );
617
618        // Reorder the RTL line.
619         if( bidiParameters.isBidirectional )
620         {
621           ReorderBiDiLayout( parameters,
622                              bidiParameters,
623                              currentLineLayout,
624                              lineLayout,
625                              false );
626         }
627
628         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Must break\n" );
629         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
630
631         return;
632       }
633
634       if( isMultiline &&
635           ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
636       {
637         oneWordLaidOut = isWordLaidOut;
638         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid-out\n" );
639
640         // Current glyph is the last one of the current word.
641         // Add the temporal layout to the current one.
642         MergeLineLayout( lineLayout, tmpLineLayout );
643
644         tmpLineLayout.Clear();
645       }
646
647       glyphIndex += numberOfGLyphsInGroup;
648     }
649
650     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
651   }
652
653   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
654                           Length numberOfGlyphs,
655                           float outlineWidth,
656                           float interGlyphExtraAdvance,
657                           Vector2* glyphPositionsBuffer )
658   {
659     // Traverse the glyphs and set the positions.
660
661     // Check if the x bearing of the first character is negative.
662     // If it has a negative x bearing, it will exceed the boundaries of the actor,
663     // so the penX position needs to be moved to the right.
664
665     const GlyphInfo& glyph = *glyphsBuffer;
666     float penX = -glyph.xBearing + mCursorWidth + outlineWidth;
667
668     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
669     {
670       const GlyphInfo& glyph = *( glyphsBuffer + i );
671       Vector2& position = *( glyphPositionsBuffer + i );
672
673       position.x = std::roundf( penX + glyph.xBearing );
674       position.y = -glyph.yBearing;
675
676       penX += ( glyph.advance + interGlyphExtraAdvance );
677     }
678   }
679
680   void SetGlyphPositions( const Parameters& layoutParameters,
681                           Vector2* glyphPositionsBuffer,
682                           LayoutBidiParameters& layoutBidiParameters,
683                           const LineLayout& layout )
684   {
685     const Character* const textBuffer = layoutParameters.textModel->mLogicalModel->mText.Begin();
686     const BidirectionalLineInfoRun& bidiLine = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
687     const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
688     const GlyphIndex* const charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
689     const Length* const glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
690
691     CharacterIndex characterLogicalIndex = 0u;
692     CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
693
694     float penX = 0.f;
695     while( TextAbstraction::IsWhiteSpace( *( textBuffer + characterVisualIndex ) ) )
696     {
697       const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
698       const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
699
700       Vector2& position = *( glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex );
701       position.x = penX;
702       position.y = -glyph.yBearing;
703
704       penX += glyph.advance;
705
706       ++characterLogicalIndex;
707       characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
708     }
709
710     const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex );
711     const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
712
713     penX += -glyph.xBearing;
714
715     // Traverses the characters of the right to left paragraph.
716     for( ; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
717          ++characterLogicalIndex )
718     {
719       // Convert the character in the logical order into the character in the visual order.
720       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
721
722       // Get the number of glyphs of the character.
723       const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + characterVisualIndex );
724
725       for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
726       {
727         // Convert the character in the visual order into the glyph in the visual order.
728         const GlyphIndex glyphIndex = *( charactersToGlyphsBuffer + characterVisualIndex ) + index;
729
730         DALI_ASSERT_DEBUG( glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count() );
731
732         const GlyphInfo& glyph = *( glyphsBuffer + glyphIndex );
733         Vector2& position = *( glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex );
734
735         position.x = std::round( penX + glyph.xBearing );
736         position.y = -glyph.yBearing;
737
738        penX += ( glyph.advance + layoutParameters.interGlyphExtraAdvance );
739       }
740     }
741   }
742
743   /**
744    * @brief Resizes the line buffer.
745    *
746    * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
747    * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
748    * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
749    * @param[in] updateCurrentBuffer Whether the layout is updated.
750    *
751    * @return Pointer to either lines or newLines.
752    */
753   LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
754                               Vector<LineRun>& newLines,
755                               Length& linesCapacity,
756                               bool updateCurrentBuffer )
757   {
758     LineRun* linesBuffer = nullptr;
759     // Reserve more space for the next lines.
760     linesCapacity *= 2u;
761     if( updateCurrentBuffer )
762     {
763       newLines.Resize( linesCapacity );
764       linesBuffer = newLines.Begin();
765     }
766     else
767     {
768       lines.Resize( linesCapacity );
769       linesBuffer = lines.Begin();
770     }
771
772     return linesBuffer;
773   }
774
775   /**
776    * Ellipsis a line if it exceeds the width's of the bounding box.
777    *
778    * @param[in] layoutParameters The parameters needed to layout the text.
779    * @param[in] layout The line layout.
780    * @param[in,out] layoutSize The text's layout size.
781    * @param[in,out] linesBuffer Pointer to the line's buffer.
782    * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
783    * @param[in,out] numberOfLines The number of laid-out lines.
784    * @param[in] penY The vertical layout position.
785    * @param[in] currentParagraphDirection The current paragraph's direction.
786    * @param[in,out] isAutoScrollEnabled If the isAutoScrollEnabled is true and the height of the text exceeds the boundaries of the control the text is elided and the isAutoScrollEnabled is set to false to disable the autoscroll
787    *
788    * return Whether the line is ellipsized.
789    */
790   bool EllipsisLine( const Parameters& layoutParameters,
791                      LayoutBidiParameters& layoutBidiParameters,
792                      const LineLayout& layout,
793                      Size& layoutSize,
794                      LineRun* linesBuffer,
795                      Vector2* glyphPositionsBuffer,
796                      Length& numberOfLines,
797                      float penY,
798                      bool& isAutoScrollEnabled )
799   {
800     const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
801                                                 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
802                                                   ( ( mLayout == SINGLE_LINE_BOX ) &&
803                                                     ( layout.length > layoutParameters.boundingBox.width ) ) );
804
805     if( ellipsis )
806     {
807       isAutoScrollEnabled = false;
808       // Do not layout more lines if ellipsis is enabled.
809
810       // The last line needs to be completely filled with characters.
811       // Part of a word may be used.
812
813       LineRun* lineRun = nullptr;
814       LineLayout ellipsisLayout;
815       if( 0u != numberOfLines )
816       {
817         // Get the last line and layout it again with the 'completelyFill' flag to true.
818         lineRun = linesBuffer + ( numberOfLines - 1u );
819
820         penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
821
822         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
823       }
824       else
825       {
826         // At least there is space reserved for one line.
827         lineRun = linesBuffer;
828
829         lineRun->glyphRun.glyphIndex = 0u;
830         ellipsisLayout.glyphIndex = 0u;
831
832         ++numberOfLines;
833       }
834
835       GetLineLayoutForBox( layoutParameters,
836                            layoutBidiParameters,
837                            ellipsisLayout,
838                            true );
839
840       lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
841       lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
842       lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
843       lineRun->width = ellipsisLayout.length;
844       lineRun->extraLength = std::ceil( ellipsisLayout.whiteSpaceLengthEndOfLine );
845       lineRun->ascender = ellipsisLayout.ascender;
846       lineRun->descender = ellipsisLayout.descender;
847       lineRun->ellipsis = true;
848
849       layoutSize.width = layoutParameters.boundingBox.width;
850       if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
851       {
852         layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
853       }
854
855       const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
856       const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
857
858       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
859
860       if( layoutBidiParameters.isBidirectional )
861       {
862         layoutBidiParameters.bidiLineIndex = 0u;
863         for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
864                endIt = bidirectionalLinesInfo.End();
865              it != endIt;
866              ++it, ++layoutBidiParameters.bidiLineIndex )
867         {
868           const BidirectionalLineInfoRun& run = *it;
869
870           if( ellipsisLayout.characterIndex == run.characterRun.characterIndex )
871           {
872             // Found where to insert the bidi line info.
873             break;
874           }
875         }
876       }
877
878       const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
879
880       if( ( nullptr != bidirectionalLineInfo ) &&
881           !bidirectionalLineInfo->isIdentity &&
882           ( ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
883       {
884         lineRun->direction = RTL;
885         SetGlyphPositions( layoutParameters,
886                            glyphPositionsBuffer,
887                            layoutBidiParameters,
888                            ellipsisLayout );
889       }
890       else
891       {
892         lineRun->direction = LTR;
893         SetGlyphPositions( glyphsBuffer + lineRun->glyphRun.glyphIndex,
894                            ellipsisLayout.numberOfGlyphs,
895                            outlineWidth,
896                            layoutParameters.interGlyphExtraAdvance,
897                            glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
898       }
899     }
900
901     return ellipsis;
902   }
903
904   /**
905    * @brief Updates the text layout with a new laid-out line.
906    *
907    * @param[in] layoutParameters The parameters needed to layout the text.
908    * @param[in] layout The line layout.
909    * @param[in,out] layoutSize The text's layout size.
910    * @param[in,out] linesBuffer Pointer to the line's buffer.
911    * @param[in] index Index to the vector of glyphs.
912    * @param[in,out] numberOfLines The number of laid-out lines.
913    * @param[in] isLastLine Whether the laid-out line is the last one.
914    */
915   void UpdateTextLayout( const Parameters& layoutParameters,
916                          const LineLayout& layout,
917                          Size& layoutSize,
918                          LineRun* linesBuffer,
919                          GlyphIndex index,
920                          Length& numberOfLines,
921                          bool isLastLine )
922   {
923     LineRun& lineRun = *( linesBuffer + numberOfLines );
924     ++numberOfLines;
925
926     lineRun.glyphRun.glyphIndex = index;
927     lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
928     lineRun.characterRun.characterIndex = layout.characterIndex;
929     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
930     lineRun.width = layout.length;
931     lineRun.extraLength = std::ceil( layout.whiteSpaceLengthEndOfLine );
932
933
934     // Rounds upward to avoid a non integer size.
935     lineRun.width = std::ceil( lineRun.width );
936
937     lineRun.ascender = layout.ascender;
938     lineRun.descender = layout.descender;
939     lineRun.direction = layout.direction;
940     lineRun.ellipsis = false;
941
942     lineRun.lineSpacing = mDefaultLineSize - ( lineRun.ascender + -lineRun.descender );
943     lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
944
945     lineRun.lineSpacing += mDefaultLineSpacing;
946
947
948     // Update the actual size.
949     if( lineRun.width > layoutSize.width )
950     {
951       layoutSize.width = lineRun.width;
952     }
953
954     layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
955   }
956
957   /**
958    * @brief Updates the text layout with the last laid-out line.
959    *
960    * @param[in] layoutParameters The parameters needed to layout the text.
961    * @param[in] characterIndex The character index of the line.
962    * @param[in] glyphIndex The glyph index of the line.
963    * @param[in,out] layoutSize The text's layout size.
964    * @param[in,out] linesBuffer Pointer to the line's buffer.
965    * @param[in,out] numberOfLines The number of laid-out lines.
966    */
967   void UpdateTextLayout( const Parameters& layoutParameters,
968                          CharacterIndex characterIndex,
969                          GlyphIndex glyphIndex,
970                          Size& layoutSize,
971                          LineRun* linesBuffer,
972                          Length& numberOfLines )
973   {
974     const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
975
976     // Need to add a new line with no characters but with height to increase the layoutSize.height
977     const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
978
979     Text::FontMetrics fontMetrics;
980     if( 0u != glyphInfo.fontId )
981     {
982       mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
983     }
984
985     LineRun& lineRun = *( linesBuffer + numberOfLines );
986     ++numberOfLines;
987
988     lineRun.glyphRun.glyphIndex = glyphIndex;
989     lineRun.glyphRun.numberOfGlyphs = 0u;
990     lineRun.characterRun.characterIndex = characterIndex;
991     lineRun.characterRun.numberOfCharacters = 0u;
992     lineRun.width = 0.f;
993     lineRun.ascender = fontMetrics.ascender;
994     lineRun.descender = fontMetrics.descender;
995     lineRun.extraLength = 0.f;
996     lineRun.alignmentOffset = 0.f;
997     lineRun.direction = LTR;
998     lineRun.ellipsis = false;
999
1000     lineRun.lineSpacing = mDefaultLineSize - ( lineRun.ascender + -lineRun.descender );
1001     lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
1002
1003     lineRun.lineSpacing += mDefaultLineSpacing;
1004
1005     layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
1006   }
1007
1008   /**
1009    * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1010    *
1011    * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1012    * @param[in,out] layoutSize The text's layout size.
1013    */
1014   void UpdateLayoutSize( const Vector<LineRun>& lines,
1015                          Size& layoutSize )
1016   {
1017     for( Vector<LineRun>::ConstIterator it = lines.Begin(),
1018            endIt = lines.End();
1019          it != endIt;
1020          ++it )
1021     {
1022       const LineRun& line = *it;
1023
1024       if( line.width > layoutSize.width )
1025       {
1026         layoutSize.width = line.width;
1027       }
1028
1029       layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
1030     }
1031   }
1032
1033   /**
1034    * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1035    *
1036    * @param[in] layoutParameters The parameters needed to layout the text.
1037    * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1038    * @param[in] characterOffset The offset to be added to the runs of characters.
1039    * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1040    */
1041   void UpdateLineIndexOffsets( const Parameters& layoutParameters,
1042                                Vector<LineRun>& lines,
1043                                Length characterOffset,
1044                                Length glyphOffset )
1045   {
1046     // Update the glyph and character runs.
1047     for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
1048            endIt = lines.End();
1049          it != endIt;
1050          ++it )
1051     {
1052       LineRun& line = *it;
1053
1054       line.glyphRun.glyphIndex = glyphOffset;
1055       line.characterRun.characterIndex = characterOffset;
1056
1057       glyphOffset += line.glyphRun.numberOfGlyphs;
1058       characterOffset += line.characterRun.numberOfCharacters;
1059     }
1060   }
1061
1062   bool LayoutText( Parameters& layoutParameters,
1063                    Size& layoutSize,
1064                    bool elideTextEnabled,
1065                    bool& isAutoScrollEnabled )
1066   {
1067     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
1068     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
1069
1070     Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1071
1072     if( 0u == layoutParameters.numberOfGlyphs )
1073     {
1074       // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1075       if( layoutParameters.isLastNewParagraph )
1076       {
1077         Length numberOfLines = lines.Count();
1078         if( 0u != numberOfLines )
1079         {
1080           const LineRun& lastLine = *( lines.End() - 1u );
1081
1082           if( 0u != lastLine.characterRun.numberOfCharacters )
1083           {
1084             // Need to add a new line with no characters but with height to increase the layoutSize.height
1085             LineRun newLine;
1086             Initialize( newLine );
1087             lines.PushBack( newLine );
1088
1089             UpdateTextLayout( layoutParameters,
1090                               lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1091                               lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1092                               layoutSize,
1093                               lines.Begin(),
1094                               numberOfLines );
1095           }
1096         }
1097       }
1098
1099       // Calculates the layout size.
1100       UpdateLayoutSize( lines,
1101                         layoutSize );
1102
1103       // Rounds upward to avoid a non integer size.
1104       layoutSize.height = std::ceil( layoutSize.height );
1105
1106       // Nothing else do if there are no glyphs to layout.
1107       return false;
1108     }
1109
1110     const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1111     const Length totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1112     Vector<Vector2>& glyphPositions = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1113
1114     // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1115     // This extra line needs to be removed.
1116     if( 0u != lines.Count() )
1117     {
1118       Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1119
1120       if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
1121           ( lastGlyphPlusOne == totalNumberOfGlyphs ) )
1122       {
1123         lines.Remove( lastLine );
1124       }
1125     }
1126
1127     // Retrieve BiDi info.
1128     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1129
1130     const CharacterIndex* const glyphsToCharactersBuffer = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1131     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1132     const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1133
1134     // Set the layout bidirectional paramters.
1135     LayoutBidiParameters layoutBidiParameters;
1136
1137     // Whether the layout is being updated or set from scratch.
1138     const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1139
1140     Vector2* glyphPositionsBuffer = nullptr;
1141     Vector<Vector2> newGlyphPositions;
1142
1143     LineRun* linesBuffer = nullptr;
1144     Vector<LineRun> newLines;
1145
1146     // Estimate the number of lines.
1147     Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
1148     Length numberOfLines = 0u;
1149
1150     if( updateCurrentBuffer )
1151     {
1152       newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
1153       glyphPositionsBuffer = newGlyphPositions.Begin();
1154
1155       newLines.Resize( linesCapacity );
1156       linesBuffer = newLines.Begin();
1157     }
1158     else
1159     {
1160       glyphPositionsBuffer = glyphPositions.Begin();
1161
1162       lines.Resize( linesCapacity );
1163       linesBuffer = lines.Begin();
1164     }
1165
1166     float penY = CalculateLineOffset( lines,
1167                                       layoutParameters.startLineIndex );
1168     for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
1169     {
1170       layoutBidiParameters.Clear();
1171
1172       if( hasBidiParagraphs )
1173       {
1174         const CharacterIndex startCharacterIndex = *( glyphsToCharactersBuffer + index );
1175
1176         for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalParagraphsInfo.Begin(),
1177                endIt = bidirectionalParagraphsInfo.End();
1178              it != endIt;
1179              ++it, ++layoutBidiParameters.bidiParagraphIndex )
1180         {
1181           const BidirectionalParagraphInfoRun& run = *it;
1182
1183           const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1184
1185           if( lastCharacterIndex <= startCharacterIndex )
1186           {
1187             // Do not process, the paragraph has already been processed.
1188             continue;
1189           }
1190
1191           if( startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex )
1192           {
1193             layoutBidiParameters.paragraphDirection = run.direction;
1194             layoutBidiParameters.isBidirectional = true;
1195           }
1196
1197           // Has already been found.
1198           break;
1199         }
1200
1201         if( layoutBidiParameters.isBidirectional )
1202         {
1203           for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLinesInfo.Begin(),
1204                  endIt = bidirectionalLinesInfo.End();
1205                it != endIt;
1206                ++it, ++layoutBidiParameters.bidiLineIndex )
1207           {
1208             const BidirectionalLineInfoRun& run = *it;
1209
1210             const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1211
1212             if( lastCharacterIndex <= startCharacterIndex )
1213             {
1214               // skip
1215               continue;
1216             }
1217
1218             if( startCharacterIndex < lastCharacterIndex )
1219             {
1220               // Found where to insert the bidi line info.
1221               break;
1222             }
1223           }
1224         }
1225       }
1226
1227       CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1228
1229       // Get the layout for the line.
1230       LineLayout layout;
1231       layout.direction = layoutBidiParameters.paragraphDirection;
1232       layout.glyphIndex = index;
1233       GetLineLayoutForBox( layoutParameters,
1234                            layoutBidiParameters,
1235                            layout,
1236                            false );
1237
1238       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex );
1239       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex );
1240       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs );
1241       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters );
1242       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                length %f\n", layout.length );
1243
1244       if( 0u == layout.numberOfGlyphs )
1245       {
1246         // The width is too small and no characters are laid-out.
1247         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
1248
1249         lines.Resize( numberOfLines );
1250
1251         // Rounds upward to avoid a non integer size.
1252         layoutSize.height = std::ceil( layoutSize.height );
1253
1254         return false;
1255       }
1256
1257       // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
1258       // of the box.
1259       penY += layout.ascender;
1260
1261       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  pen y %f\n", penY );
1262
1263       bool ellipsis = false;
1264       if( elideTextEnabled )
1265       {
1266         layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1267
1268         // Does the ellipsis of the last line.
1269         ellipsis = EllipsisLine( layoutParameters,
1270                                  layoutBidiParameters,
1271                                  layout,
1272                                  layoutSize,
1273                                  linesBuffer,
1274                                  glyphPositionsBuffer,
1275                                  numberOfLines,
1276                                  penY,
1277                                  isAutoScrollEnabled );
1278       }
1279
1280       if( ellipsis )
1281       {
1282         // No more lines to layout.
1283         break;
1284       }
1285       else
1286       {
1287         // Whether the last line has been laid-out.
1288         const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1289
1290         if( numberOfLines == linesCapacity )
1291         {
1292
1293           // Reserve more space for the next lines.
1294           linesBuffer = ResizeLinesBuffer( lines,
1295                                            newLines,
1296                                            linesCapacity,
1297                                            updateCurrentBuffer );
1298         }
1299
1300         // Updates the current text's layout with the line's layout.
1301         UpdateTextLayout( layoutParameters,
1302                           layout,
1303                           layoutSize,
1304                           linesBuffer,
1305                           index,
1306                           numberOfLines,
1307                           isLastLine );
1308
1309         const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1310
1311         if( ( nextIndex == totalNumberOfGlyphs ) &&
1312             layoutParameters.isLastNewParagraph &&
1313             ( mLayout == MULTI_LINE_BOX ) )
1314         {
1315           // The last character of the text is a new paragraph character.
1316           // An extra line with no characters is added to increase the text's height
1317           // in order to place the cursor.
1318
1319           if( numberOfLines == linesCapacity )
1320           {
1321             // Reserve more space for the next lines.
1322             linesBuffer = ResizeLinesBuffer( lines,
1323                                              newLines,
1324                                              linesCapacity,
1325                                              updateCurrentBuffer );
1326           }
1327
1328           UpdateTextLayout( layoutParameters,
1329                             layout.characterIndex + layout.numberOfCharacters,
1330                             index + layout.numberOfGlyphs,
1331                             layoutSize,
1332                             linesBuffer,
1333                             numberOfLines );
1334         } // whether to add a last line.
1335
1336         const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1337         const float outlineWidth = static_cast<float>( layoutParameters.textModel->GetOutlineWidth() );
1338
1339         const BidirectionalLineInfoRun* const bidirectionalLineInfo = ( layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty() ) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1340
1341         if( ( nullptr != bidirectionalLineInfo ) &&
1342             !bidirectionalLineInfo->isIdentity &&
1343             ( layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex ) )
1344         {
1345           SetGlyphPositions( layoutParameters,
1346                              glyphPositionsBuffer,
1347                              layoutBidiParameters,
1348                              layout );
1349         }
1350         else
1351         {
1352
1353           // Sets the positions of the glyphs.
1354           SetGlyphPositions( glyphsBuffer + index,
1355                              layout.numberOfGlyphs,
1356                              outlineWidth,
1357                              layoutParameters.interGlyphExtraAdvance,
1358                              glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
1359         }
1360
1361         // Updates the vertical pen's position.
1362         penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1363
1364         // Increase the glyph index.
1365         index = nextIndex;
1366       } // no ellipsis
1367     } // end for() traversing glyphs.
1368
1369     if( updateCurrentBuffer )
1370     {
1371       glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1372                              newGlyphPositions.Begin(),
1373                              newGlyphPositions.End() );
1374       glyphPositions.Resize( totalNumberOfGlyphs );
1375
1376       newLines.Resize( numberOfLines );
1377
1378       // Current text's layout size adds only the newly laid-out lines.
1379       // Updates the layout size with the previously laid-out lines.
1380       UpdateLayoutSize( lines,
1381                         layoutSize );
1382
1383       if( 0u != newLines.Count() )
1384       {
1385         const LineRun& lastLine = *( newLines.End() - 1u );
1386
1387         const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1388         const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1389
1390         // Update the indices of the runs before the new laid-out lines are inserted.
1391         UpdateLineIndexOffsets( layoutParameters,
1392                                 lines,
1393                                 characterOffset,
1394                                 glyphOffset );
1395
1396         // Insert the lines.
1397         lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1398                       newLines.Begin(),
1399                       newLines.End() );
1400       }
1401     }
1402     else
1403     {
1404       lines.Resize( numberOfLines );
1405     }
1406
1407     // Rounds upward to avoid a non integer size.
1408     layoutSize.height = std::ceil( layoutSize.height );
1409
1410     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1411
1412     return true;
1413   }
1414
1415   void Align( const Size& size,
1416               CharacterIndex startIndex,
1417               Length numberOfCharacters,
1418               Text::HorizontalAlignment::Type horizontalAlignment,
1419               Vector<LineRun>& lines,
1420               float& alignmentOffset,
1421               Dali::LayoutDirection::Type layoutDirection,
1422               bool matchSystemLanguageDirection )
1423   {
1424     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1425
1426     alignmentOffset = MAX_FLOAT;
1427     // Traverse all lines and align the glyphs.
1428     for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1429          it != endIt;
1430          ++it )
1431     {
1432       LineRun& line = *it;
1433
1434       if( line.characterRun.characterIndex < startIndex )
1435       {
1436         // Do not align lines which have already been aligned.
1437         continue;
1438       }
1439
1440       if( line.characterRun.characterIndex > lastCharacterPlusOne )
1441       {
1442         // Do not align lines beyond the last laid-out character.
1443         break;
1444       }
1445
1446       if( line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast( lines, it ) )
1447       {
1448         // Do not align lines beyond the last laid-out character unless the line is last and empty.
1449         break;
1450       }
1451
1452       // Calculate the line's alignment offset accordingly with the align option,
1453       // the box width, line length, and the paragraph's direction.
1454       CalculateHorizontalAlignment( size.width,
1455                                     horizontalAlignment,
1456                                     line,
1457                                     layoutDirection,
1458                                     matchSystemLanguageDirection );
1459
1460       // Updates the alignment offset.
1461       alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1462     }
1463   }
1464
1465   void CalculateHorizontalAlignment( float boxWidth,
1466                                      HorizontalAlignment::Type horizontalAlignment,
1467                                      LineRun& line,
1468                                      Dali::LayoutDirection::Type layoutDirection,
1469                                      bool matchSystemLanguageDirection )
1470   {
1471     line.alignmentOffset = 0.f;
1472     const bool isLineRTL = RTL == line.direction;
1473
1474     // Whether to swap the alignment.
1475     // Swap if the line is RTL and is not required to match the direction of the system's language or if it's required to match the direction of the system's language and it's RTL.
1476     bool isLayoutRTL = isLineRTL;
1477     float lineLength = line.width;
1478
1479     // match align for system language direction
1480     if( matchSystemLanguageDirection )
1481     {
1482       // Swap the alignment type if the line is right to left.
1483       isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1484     }
1485     // Calculate the horizontal line offset.
1486     switch( horizontalAlignment )
1487     {
1488       case HorizontalAlignment::BEGIN:
1489       {
1490         if( isLayoutRTL )
1491         {
1492           if( isLineRTL )
1493           {
1494             lineLength += line.extraLength;
1495           }
1496
1497           line.alignmentOffset = boxWidth - lineLength;
1498         }
1499         else
1500         {
1501           line.alignmentOffset = 0.f;
1502
1503           if( isLineRTL )
1504           {
1505             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1506             line.alignmentOffset -= line.extraLength;
1507           }
1508         }
1509         break;
1510       }
1511       case HorizontalAlignment::CENTER:
1512       {
1513         line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1514
1515         if( isLineRTL )
1516         {
1517           line.alignmentOffset -= line.extraLength;
1518         }
1519
1520         line.alignmentOffset = std::floor( line.alignmentOffset ); // floor() avoids pixel alignment issues.
1521         break;
1522       }
1523       case HorizontalAlignment::END:
1524       {
1525         if( isLayoutRTL )
1526         {
1527           line.alignmentOffset = 0.f;
1528
1529           if( isLineRTL )
1530           {
1531             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1532             line.alignmentOffset -= line.extraLength;
1533           }
1534         }
1535         else
1536         {
1537           if( isLineRTL )
1538           {
1539             lineLength += line.extraLength;
1540           }
1541
1542           line.alignmentOffset = boxWidth - lineLength;
1543         }
1544         break;
1545       }
1546     }
1547   }
1548
1549   void Initialize( LineRun& line )
1550   {
1551     line.glyphRun.glyphIndex = 0u;
1552     line.glyphRun.numberOfGlyphs = 0u;
1553     line.characterRun.characterIndex = 0u;
1554     line.characterRun.numberOfCharacters = 0u;
1555     line.width = 0.f;
1556     line.ascender = 0.f;
1557     line.descender = 0.f;
1558     line.extraLength = 0.f;
1559     line.alignmentOffset = 0.f;
1560     line.direction = LTR;
1561     line.ellipsis = false;
1562     line.lineSpacing = mDefaultLineSpacing;
1563   }
1564
1565   Type mLayout;
1566   float mCursorWidth;
1567   float mDefaultLineSpacing;
1568   float mDefaultLineSize;
1569
1570   IntrusivePtr<Metrics> mMetrics;
1571 };
1572
1573 Engine::Engine()
1574 : mImpl{ nullptr }
1575 {
1576   mImpl = new Engine::Impl();
1577 }
1578
1579 Engine::~Engine()
1580 {
1581   delete mImpl;
1582 }
1583
1584 void Engine::SetMetrics( MetricsPtr& metrics )
1585 {
1586   mImpl->mMetrics = metrics;
1587 }
1588
1589 void Engine::SetLayout( Type layout )
1590 {
1591   mImpl->mLayout = layout;
1592 }
1593
1594 Engine::Type Engine::GetLayout() const
1595 {
1596   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1597   return mImpl->mLayout;
1598 }
1599
1600 void Engine::SetCursorWidth( int width )
1601 {
1602   mImpl->mCursorWidth = static_cast<float>( width );
1603 }
1604
1605 int Engine::GetCursorWidth() const
1606 {
1607   return static_cast<int>( mImpl->mCursorWidth );
1608 }
1609
1610 bool Engine::LayoutText( Parameters& layoutParameters,
1611                          Size& layoutSize,
1612                          bool elideTextEnabled,
1613                          bool& isAutoScrollEnabled )
1614 {
1615   return mImpl->LayoutText( layoutParameters,
1616                             layoutSize,
1617                             elideTextEnabled,
1618                             isAutoScrollEnabled );
1619 }
1620
1621 void Engine::Align( const Size& size,
1622                     CharacterIndex startIndex,
1623                     Length numberOfCharacters,
1624                     Text::HorizontalAlignment::Type horizontalAlignment,
1625                     Vector<LineRun>& lines,
1626                     float& alignmentOffset,
1627                     Dali::LayoutDirection::Type layoutDirection,
1628                     bool matchSystemLanguageDirection )
1629 {
1630   mImpl->Align( size,
1631                 startIndex,
1632                 numberOfCharacters,
1633                 horizontalAlignment,
1634                 lines,
1635                 alignmentOffset,
1636                 layoutDirection,
1637                 matchSystemLanguageDirection );
1638 }
1639
1640 void Engine::SetDefaultLineSpacing( float lineSpacing )
1641 {
1642   mImpl->mDefaultLineSpacing = lineSpacing;
1643 }
1644
1645 float Engine::GetDefaultLineSpacing() const
1646 {
1647   return mImpl->mDefaultLineSpacing;
1648 }
1649
1650 void Engine::SetDefaultLineSize( float lineSize )
1651 {
1652   mImpl->mDefaultLineSize = lineSize;
1653 }
1654
1655 float Engine::GetDefaultLineSize() const
1656 {
1657   return mImpl->mDefaultLineSize;
1658 }
1659
1660 } // namespace Layout
1661
1662 } // namespace Text
1663
1664 } // namespace Toolkit
1665
1666 } // namespace Dali