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