fix issue in negative line spacing with key arrow down
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2022 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 <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
24 #include <cmath>
25 #include <limits>
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-engine-helper-functions.h>
32 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
33
34 namespace Dali
35 {
36 namespace Toolkit
37 {
38 namespace Text
39 {
40 float GetLineHeight(const LineRun lineRun, bool isLastLine)
41 {
42   // The line height is the addition of the line ascender, the line descender and the line spacing.
43   // However, the line descender has a negative value, hence the subtraction.
44   // In case this is the only/last line then line spacing should be ignored.
45   float lineHeight = lineRun.ascender - lineRun.descender;
46
47   if(!isLastLine || lineRun.lineSpacing > 0)
48   {
49     lineHeight += lineRun.lineSpacing;
50   }
51   return lineHeight;
52 }
53
54 namespace Layout
55 {
56 namespace
57 {
58 #if defined(DEBUG_ENABLED)
59 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
60 #endif
61
62 const float              MAX_FLOAT          = std::numeric_limits<float>::max();
63 const CharacterDirection LTR                = false;
64 const CharacterDirection RTL                = !LTR;
65 const float              LINE_SPACING       = 0.f;
66 const float              MIN_LINE_SIZE      = 0.f;
67 const Character          HYPHEN_UNICODE     = 0x002D;
68 const float              RELATIVE_LINE_SIZE = 1.f;
69
70 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
71 {
72   return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
73 }
74
75 } //namespace
76
77 /**
78  * @brief Stores temporary layout info of the line.
79  */
80 struct LineLayout
81 {
82   LineLayout()
83   : glyphIndex{0u},
84     characterIndex{0u},
85     numberOfGlyphs{0u},
86     numberOfCharacters{0u},
87     ascender{-MAX_FLOAT},
88     descender{MAX_FLOAT},
89     lineSpacing{0.f},
90     penX{0.f},
91     previousAdvance{0.f},
92     length{0.f},
93     whiteSpaceLengthEndOfLine{0.f},
94     direction{LTR},
95     isSplitToTwoHalves(false),
96     glyphIndexInSecondHalfLine{0u},
97     characterIndexInSecondHalfLine{0u},
98     numberOfGlyphsInSecondHalfLine{0u},
99     numberOfCharactersInSecondHalfLine{0u}
100
101   {
102   }
103
104   ~LineLayout()
105   {
106   }
107
108   void Clear()
109   {
110     glyphIndex                         = 0u;
111     characterIndex                     = 0u;
112     numberOfGlyphs                     = 0u;
113     numberOfCharacters                 = 0u;
114     ascender                           = -MAX_FLOAT;
115     descender                          = MAX_FLOAT;
116     direction                          = LTR;
117     isSplitToTwoHalves                 = false;
118     glyphIndexInSecondHalfLine         = 0u;
119     characterIndexInSecondHalfLine     = 0u;
120     numberOfGlyphsInSecondHalfLine     = 0u;
121     numberOfCharactersInSecondHalfLine = 0u;
122   }
123
124   GlyphIndex         glyphIndex;                ///< Index of the first glyph to be laid-out.
125   CharacterIndex     characterIndex;            ///< Index of the first character to be laid-out.
126   Length             numberOfGlyphs;            ///< The number of glyph which fit in one line.
127   Length             numberOfCharacters;        ///< The number of characters which fit in one line.
128   float              ascender;                  ///< The maximum ascender of all fonts in the line.
129   float              descender;                 ///< The minimum descender of all fonts in the line.
130   float              lineSpacing;               ///< The line spacing
131   float              penX;                      ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
132   float              previousAdvance;           ///< The advance of the previous glyph.
133   float              length;                    ///< The current length of the line.
134   float              whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
135   CharacterDirection direction;
136
137   bool           isSplitToTwoHalves;                 ///< Whether the second half is defined.
138   GlyphIndex     glyphIndexInSecondHalfLine;         ///< Index of the first glyph to be laid-out for the second half of line.
139   CharacterIndex characterIndexInSecondHalfLine;     ///< Index of the first character to be laid-out for the second half of line.
140   Length         numberOfGlyphsInSecondHalfLine;     ///< The number of glyph which fit in one line for the second half of line.
141   Length         numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
142 };
143
144 struct LayoutBidiParameters
145 {
146   void Clear()
147   {
148     paragraphDirection = LTR;
149     bidiParagraphIndex = 0u;
150     bidiLineIndex      = 0u;
151     isBidirectional    = false;
152   }
153
154   CharacterDirection        paragraphDirection = LTR;   ///< The paragraph's direction.
155   BidirectionalRunIndex     bidiParagraphIndex = 0u;    ///< Index to the paragraph's bidi info.
156   BidirectionalLineRunIndex bidiLineIndex      = 0u;    ///< Index where to insert the next bidi line info.
157   bool                      isBidirectional    = false; ///< Whether the text is bidirectional.
158 };
159
160 struct Engine::Impl
161 {
162   Impl()
163   : mLayout{Layout::Engine::SINGLE_LINE_BOX},
164     mCursorWidth{0.f},
165     mDefaultLineSpacing{LINE_SPACING},
166     mDefaultLineSize{MIN_LINE_SIZE},
167     mRelativeLineSize{RELATIVE_LINE_SIZE}
168   {
169   }
170
171   /**
172    * @brief get the line spacing.
173    *
174    * @param[in] textSize The text size.
175    * @return the line spacing value.
176    */
177   float GetLineSpacing(float textSize)
178   {
179     float lineSpacing;
180     float relTextSize;
181
182     // Sets the line size
183     lineSpacing = mDefaultLineSize - textSize;
184     lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
185
186     // Add the line spacing
187     lineSpacing += mDefaultLineSpacing;
188
189     //subtract line spcaing if relativeLineSize < 1 & larger than min height
190     relTextSize = textSize * mRelativeLineSize;
191     if(relTextSize > mDefaultLineSize)
192     {
193       if(mRelativeLineSize < 1)
194       {
195         //subtract the difference (always will be positive)
196         lineSpacing -= (textSize - relTextSize);
197       }
198       else
199       {
200         //reverse the addition in the top.
201         if(mDefaultLineSize > textSize)
202         {
203           lineSpacing -= mDefaultLineSize - textSize;
204         }
205
206         //add difference instead
207         lineSpacing += relTextSize - textSize;
208       }
209     }
210
211     return lineSpacing;
212   }
213
214   /**
215    * @brief Updates the line ascender and descender with the metrics of a new font.
216    *
217    * @param[in] glyphMetrics The metrics of the new font.
218    * @param[in,out] lineLayout The line layout.
219    */
220   void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
221   {
222     Text::FontMetrics fontMetrics;
223     if(0u != glyphMetrics.fontId)
224     {
225       mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
226     }
227     else
228     {
229       fontMetrics.ascender           = glyphMetrics.fontHeight;
230       fontMetrics.descender          = 0.f;
231       fontMetrics.height             = fontMetrics.ascender;
232       fontMetrics.underlinePosition  = 0.f;
233       fontMetrics.underlineThickness = 1.f;
234     }
235
236     // Sets the maximum ascender.
237     lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
238
239     // Sets the minimum descender.
240     lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
241
242     lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender);
243   }
244
245   /**
246    * @brief Merges a temporary line layout into the line layout.
247    *
248    * @param[in,out] lineLayout The line layout.
249    * @param[in] tmpLineLayout A temporary line layout.
250    * @param[in] isShifted Whether to shift first glyph and character indices.
251    */
252   void MergeLineLayout(LineLayout&       lineLayout,
253                        const LineLayout& tmpLineLayout,
254                        bool              isShifted)
255   {
256     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
257     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
258
259     lineLayout.penX            = tmpLineLayout.penX;
260     lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
261
262     lineLayout.length                    = tmpLineLayout.length;
263     lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
264
265     // Sets the maximum ascender.
266     lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
267
268     // Sets the minimum descender.
269     lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
270
271     // To handle cases START in ellipsis position when want to shift first glyph to let width fit.
272     if(isShifted)
273     {
274       lineLayout.glyphIndex     = tmpLineLayout.glyphIndex;
275       lineLayout.characterIndex = tmpLineLayout.characterIndex;
276     }
277
278     lineLayout.isSplitToTwoHalves                 = tmpLineLayout.isSplitToTwoHalves;
279     lineLayout.glyphIndexInSecondHalfLine         = tmpLineLayout.glyphIndexInSecondHalfLine;
280     lineLayout.characterIndexInSecondHalfLine     = tmpLineLayout.characterIndexInSecondHalfLine;
281     lineLayout.numberOfGlyphsInSecondHalfLine     = tmpLineLayout.numberOfGlyphsInSecondHalfLine;
282     lineLayout.numberOfCharactersInSecondHalfLine = tmpLineLayout.numberOfCharactersInSecondHalfLine;
283   }
284
285   void LayoutRightToLeft(const Parameters&               parameters,
286                          const BidirectionalLineInfoRun& bidirectionalLineInfo,
287                          float&                          length,
288                          float&                          whiteSpaceLengthEndOfLine)
289   {
290     // Travers characters in line then draw it form right to left by mapping index using visualToLogicalMap.
291     // When the line is spllited by MIDDLE ellipsis then travers the second half of line "characterRunForSecondHalfLine"
292     // then the first half of line "characterRun",
293     // Otherwise travers whole characters in"characterRun".
294
295     const Character* const  textBuffer               = parameters.textModel->mLogicalModel->mText.Begin();
296     const Length* const     charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
297     const GlyphInfo* const  glyphsBuffer             = parameters.textModel->mVisualModel->mGlyphs.Begin();
298     const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
299
300     const float      outlineWidth                = static_cast<float>(parameters.textModel->GetOutlineWidth());
301     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
302     const float      characterSpacing            = parameters.textModel->mVisualModel->GetCharacterSpacing();
303
304     CharacterIndex characterLogicalIndex = 0u;
305     CharacterIndex characterVisualIndex  = 0u;
306
307     float calculatedAdvance = 0.f;
308
309     // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
310     // Otherwise maps the first visual index from visualToLogicalMap.
311     // This is to initialize the first visual index.
312     if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
313     {
314       characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
315     }
316     else
317     {
318       characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
319     }
320
321     bool extendedToSecondHalf = false; // Whether the logical index is extended to second half
322
323     if(RTL == bidirectionalLineInfo.direction)
324     {
325       // If there are characters in the second half of Line.
326       if(bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
327       {
328         // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
329         while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
330         {
331           const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
332
333           calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
334           whiteSpaceLengthEndOfLine += calculatedAdvance;
335
336           ++characterLogicalIndex;
337           characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
338         }
339       }
340
341       // If all characters in the second half of Line are WhiteSpaces.
342       // then continue adding the WhiteSpaces from the first hel of Line.
343       // Also this is valid when the line was not splitted.
344       if(characterLogicalIndex == bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters)
345       {
346         extendedToSecondHalf  = true; // Whether the logical index is extended to second half
347         characterLogicalIndex = 0u;
348         characterVisualIndex  = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
349
350         // Keep adding the WhiteSpaces to the whiteSpaceLengthEndOfLine
351         while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
352         {
353           const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
354
355           calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
356           whiteSpaceLengthEndOfLine += calculatedAdvance;
357
358           ++characterLogicalIndex;
359           characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
360         }
361       }
362     }
363
364     // Here's the first index of character is not WhiteSpace
365     const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
366
367     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
368     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
369                                                                   lastGlyphOfParagraphPlusOne,
370                                                                   charactersPerGlyphBuffer);
371
372     GlyphMetrics glyphMetrics;
373     calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
374     GetGlyphsMetrics(glyphIndex,
375                      numberOfGLyphsInGroup,
376                      glyphMetrics,
377                      glyphsBuffer,
378                      mMetrics,
379                      calculatedAdvance);
380
381     float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
382
383     // Traverses the characters of the right to left paragraph.
384     // Continue in the second half of line, because in it the first index of character that is not WhiteSpace.
385     if(!extendedToSecondHalf &&
386        bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters > 0u)
387     {
388       for(; characterLogicalIndex < bidirectionalLineInfo.characterRunForSecondHalfLine.numberOfCharacters;)
389       {
390         // Convert the character in the logical order into the character in the visual order.
391         const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
392         const bool           isWhiteSpace         = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
393
394         const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
395
396         // Check whether this glyph comes from a character that is shaped in multiple glyphs.
397         const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
398                                                                       lastGlyphOfParagraphPlusOne,
399                                                                       charactersPerGlyphBuffer);
400
401         characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
402
403         GlyphMetrics glyphMetrics;
404         calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
405         GetGlyphsMetrics(glyphIndex,
406                          numberOfGLyphsInGroup,
407                          glyphMetrics,
408                          glyphsBuffer,
409                          mMetrics,
410                          calculatedAdvance);
411
412         if(isWhiteSpace)
413         {
414           // If glyph is WhiteSpace then:
415           // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
416           // the endOfLine in RTL was the headOfLine for layouting.
417           // But for LTR added it to the endOfLine and use "advance" to accumulate length.
418           if(RTL == bidirectionalLineInfo.direction)
419           {
420             length += glyphMetrics.advance;
421           }
422           else
423           {
424             whiteSpaceLengthEndOfLine += glyphMetrics.advance;
425           }
426           penX += glyphMetrics.advance;
427         }
428         else
429         {
430           // If glyph is not whiteSpace then:
431           // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
432           // Use "advance" and "interGlyphExtraAdvance" to shift penX.
433           // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
434           // Otherwise the current length is maximum.
435           if(LTR == bidirectionalLineInfo.direction)
436           {
437             whiteSpaceLengthEndOfLine = 0.f;
438           }
439           length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
440           penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
441         }
442       }
443     }
444
445     // Continue traversing in the first half of line or in the whole line.
446     // If the second half of line was extended then continue from logical index in the first half of line
447     // Also this is valid when the line was not splitted and there were WhiteSpace.
448     // Otherwise start from first logical index in line.
449     characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
450     for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
451     {
452       // Convert the character in the logical order into the character in the visual order.
453       const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
454       const bool           isWhiteSpace         = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
455
456       const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
457
458       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
459       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
460                                                                     lastGlyphOfParagraphPlusOne,
461                                                                     charactersPerGlyphBuffer);
462
463       characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
464
465       GlyphMetrics glyphMetrics;
466       calculatedAdvance = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
467       GetGlyphsMetrics(glyphIndex,
468                        numberOfGLyphsInGroup,
469                        glyphMetrics,
470                        glyphsBuffer,
471                        mMetrics,
472                        calculatedAdvance);
473
474       if(isWhiteSpace)
475       {
476         // If glyph is WhiteSpace then:
477         // For RTL it is whitespace but not at endOfLine. Use "advance" to accumulate length and shift penX.
478         // the endOfLine in RTL was the headOfLine for layouting.
479         // But for LTR added it to the endOfLine and use "advance" to accumulate length.
480         if(RTL == bidirectionalLineInfo.direction)
481         {
482           length += glyphMetrics.advance;
483         }
484         else
485         {
486           whiteSpaceLengthEndOfLine += glyphMetrics.advance;
487         }
488         penX += glyphMetrics.advance;
489       }
490       else
491       {
492         // If glyph is not whiteSpace then:
493         // Reset endOfLine for LTR because there is a non-whiteSpace so the tail of line is not whiteSpaces
494         // Use "advance" and "interGlyphExtraAdvance" to shift penX.
495         // Set length to the maximum possible length, of the current glyph "xBearing" and "width" are shifted penX to length greater than current lenght.
496         // Otherwise the current length is maximum.
497         if(LTR == bidirectionalLineInfo.direction)
498         {
499           whiteSpaceLengthEndOfLine = 0.f;
500         }
501         length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
502         penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
503       }
504     }
505   }
506
507   void ReorderBiDiLayout(const Parameters&     parameters,
508                          LayoutBidiParameters& bidiParameters,
509                          const LineLayout&     currentLineLayout,
510                          LineLayout&           lineLayout,
511                          bool                  breakInCharacters,
512                          bool                  enforceEllipsisInSingleLine)
513   {
514     const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
515
516     // The last glyph to be laid-out.
517     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
518
519     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
520
521     const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
522     if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
523        (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
524     {
525       Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
526
527       // Sets the visual to logical map tables needed to reorder the text.
528       ReorderLine(bidirectionalParagraphInfo,
529                   bidirectionalLinesInfo,
530                   bidiParameters.bidiLineIndex,
531                   lineLayout.characterIndex,
532                   lineLayout.numberOfCharacters,
533                   lineLayout.characterIndexInSecondHalfLine,
534                   lineLayout.numberOfCharactersInSecondHalfLine,
535                   bidiParameters.paragraphDirection);
536
537       // Recalculate the length of the line and update the layout.
538       const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
539
540       if(!bidirectionalLineInfo.isIdentity)
541       {
542         float length                    = 0.f;
543         float whiteSpaceLengthEndOfLine = 0.f;
544         LayoutRightToLeft(parameters,
545                           bidirectionalLineInfo,
546                           length,
547                           whiteSpaceLengthEndOfLine);
548
549         lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
550         if(!Equals(length, lineLayout.length))
551         {
552           const bool isMultiline = (!enforceEllipsisInSingleLine) && (mLayout == MULTI_LINE_BOX);
553
554           if(isMultiline && (length > parameters.boundingBox.width))
555           {
556             if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
557             {
558               // The word doesn't fit in one line. It has to be split by character.
559
560               // Remove the last laid out glyph(s) as they doesn't fit.
561               for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
562               {
563                 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
564                                                                               lastGlyphOfParagraphPlusOne,
565                                                                               charactersPerGlyphBuffer);
566
567                 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
568
569                 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
570                 lineLayout.numberOfCharacters -= numberOfCharacters;
571
572                 AdjustLayout(parameters,
573                              bidiParameters,
574                              bidirectionalParagraphInfo,
575                              lineLayout);
576
577                 if(lineLayout.length < parameters.boundingBox.width)
578                 {
579                   break;
580                 }
581
582                 if(glyphIndex < numberOfGLyphsInGroup)
583                 {
584                   // avoids go under zero for an unsigned int.
585                   break;
586                 }
587
588                 glyphIndex -= numberOfGLyphsInGroup;
589               }
590             }
591             else
592             {
593               lineLayout = currentLineLayout;
594
595               AdjustLayout(parameters,
596                            bidiParameters,
597                            bidirectionalParagraphInfo,
598                            lineLayout);
599             }
600           }
601           else
602           {
603             lineLayout.length = std::max(length, lineLayout.length);
604           }
605         }
606       }
607     }
608   }
609
610   void AdjustLayout(const Parameters&                    parameters,
611                     LayoutBidiParameters&                bidiParameters,
612                     const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
613                     LineLayout&                          lineLayout)
614   {
615     Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
616
617     // Remove current reordered line.
618     bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
619
620     // Re-build the conversion table without the removed glyphs.
621     ReorderLine(bidirectionalParagraphInfo,
622                 bidirectionalLinesInfo,
623                 bidiParameters.bidiLineIndex,
624                 lineLayout.characterIndex,
625                 lineLayout.numberOfCharacters,
626                 lineLayout.characterIndexInSecondHalfLine,
627                 lineLayout.numberOfCharactersInSecondHalfLine,
628                 bidiParameters.paragraphDirection);
629
630     const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
631
632     float length                    = 0.f;
633     float whiteSpaceLengthEndOfLine = 0.f;
634     LayoutRightToLeft(parameters,
635                       bidirectionalLineInfo,
636                       length,
637                       whiteSpaceLengthEndOfLine);
638
639     lineLayout.length                    = length;
640     lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
641   }
642
643   /**
644    * Retrieves the line layout for a given box width.
645    *
646    * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
647    *       of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
648    *       to reorder the line and recalculate its length.
649    *
650
651    * @param[in] parameters The layout parameters.
652    * @param[] bidiParameters Bidirectional info for the current line.
653    * @param[out] lineLayout The line layout.
654    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
655    * @param[in] ellipsisPosition Where is the location the text elide
656    */
657   void GetLineLayoutForBox(const Parameters&                 parameters,
658                            LayoutBidiParameters&             bidiParameters,
659                            LineLayout&                       lineLayout,
660                            bool                              completelyFill,
661                            DevelText::EllipsisPosition::Type ellipsisPosition,
662                            bool                              enforceEllipsisInSingleLine,
663                            bool                              elideTextEnabled)
664   {
665     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
666     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex);
667
668     const Character* const      textBuffer               = parameters.textModel->mLogicalModel->mText.Begin();
669     const Length* const         charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
670     const GlyphInfo* const      glyphsBuffer             = parameters.textModel->mVisualModel->mGlyphs.Begin();
671     const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
672     const LineBreakInfo* const  lineBreakInfoBuffer      = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
673
674     const float  outlineWidth        = static_cast<float>(parameters.textModel->GetOutlineWidth());
675     const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
676
677     const bool isMultiline   = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
678     const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD ||
679                                (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
680                                (parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED);
681     const bool isHyphenMode = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION;
682     const bool isMixedMode  = parameters.textModel->mLineWrapMode == (Text::LineWrap::Mode)DevelText::LineWrap::MIXED;
683
684     const bool isSplitToTwoHalves = elideTextEnabled && !isMultiline && ellipsisPosition == DevelText::EllipsisPosition::MIDDLE;
685
686     // The last glyph to be laid-out.
687     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
688
689     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
690     // In the case the line starts with a right to left character, if the width is longer than the advance,
691     // the difference needs to be added to the line length.
692
693     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
694     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
695                                                                   lastGlyphOfParagraphPlusOne,
696                                                                   charactersPerGlyphBuffer);
697
698     float targetWidth    = parameters.boundingBox.width;
699     float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
700
701     bool isSecondHalf = false;
702     // Character Spacing
703     const float             characterSpacing          = parameters.textModel->mVisualModel->GetCharacterSpacing();
704     float                   calculatedAdvance         = 0.f;
705     Vector<CharacterIndex>& glyphToCharacterMap       = parameters.textModel->mVisualModel->mGlyphsToCharacters;
706     const CharacterIndex*   glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
707
708     GlyphMetrics glyphMetrics;
709     calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
710     GetGlyphsMetrics(lineLayout.glyphIndex,
711                      numberOfGLyphsInGroup,
712                      glyphMetrics,
713                      glyphsBuffer,
714                      mMetrics,
715                      calculatedAdvance);
716
717     // Set the direction of the first character of the line.
718     lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
719
720     // Stores temporary line layout which has not been added to the final line layout.
721     LineLayout tmpLineLayout;
722
723     // Initialize the start point.
724
725     // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
726     // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
727     // 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.
728     tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
729
730     // Calculate the line height if there is no characters.
731     FontId lastFontId = glyphMetrics.fontId;
732     UpdateLineHeight(glyphMetrics, tmpLineLayout);
733
734     bool       oneWordLaidOut   = false;
735     bool       oneHyphenLaidOut = false;
736     GlyphIndex hyphenIndex      = 0;
737     GlyphInfo  hyphenGlyph;
738
739     for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
740         glyphIndex < lastGlyphOfParagraphPlusOne;)
741     {
742       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex);
743
744       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
745       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
746                                                                     lastGlyphOfParagraphPlusOne,
747                                                                     charactersPerGlyphBuffer);
748
749       GlyphMetrics glyphMetrics;
750       calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
751       GetGlyphsMetrics(glyphIndex,
752                        numberOfGLyphsInGroup,
753                        glyphMetrics,
754                        glyphsBuffer,
755                        mMetrics,
756                        calculatedAdvance);
757
758       const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
759
760       // Check if the font of the current glyph is the same of the previous one.
761       // If it's different the ascender and descender need to be updated.
762       if(lastFontId != glyphMetrics.fontId)
763       {
764         UpdateLineHeight(glyphMetrics, tmpLineLayout);
765         lastFontId = glyphMetrics.fontId;
766       }
767
768       // Get the character indices for the current glyph. The last character index is needed
769       // because there are glyphs formed by more than one character but their break info is
770       // given only for the last character.
771       const Length         charactersPerGlyph  = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
772       const bool           hasCharacters       = charactersPerGlyph > 0u;
773       const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
774       const CharacterIndex characterLastIndex  = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
775
776       // Get the line break info for the current character.
777       const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
778
779       if(isSecondHalf)
780       {
781         // Increase the number of characters.
782         tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
783
784         // Increase the number of glyphs.
785         tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
786       }
787       else
788       {
789         // Increase the number of characters.
790         tmpLineLayout.numberOfCharacters += charactersPerGlyph;
791
792         // Increase the number of glyphs.
793         tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
794       }
795
796       // Check whether is a white space.
797       const Character character    = *(textBuffer + characterFirstIndex);
798       const bool      isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
799
800       // Calculate the length of the line.
801
802       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
803       const float previousTmpPenX                      = tmpLineLayout.penX;
804       const float previousTmpAdvance                   = tmpLineLayout.previousAdvance;
805       const float previousTmpLength                    = tmpLineLayout.length;
806       const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
807
808       if(isWhiteSpace)
809       {
810         // Add the length to the length of white spaces at the end of the line.
811         tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
812         // The advance is used as the width is always zero for the white spaces.
813       }
814       else
815       {
816         tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
817         tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
818
819         tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
820
821         // Clear the white space length at the end of the line.
822         tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
823       }
824
825       if(isSplitToTwoHalves && (!isSecondHalf) &&
826          (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > widthFirstHalf))
827       {
828         tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
829         tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
830
831         tmpLineLayout.numberOfCharactersInSecondHalfLine += charactersPerGlyph;
832         tmpLineLayout.numberOfGlyphsInSecondHalfLine += numberOfGLyphsInGroup;
833
834         tmpLineLayout.glyphIndexInSecondHalfLine     = tmpLineLayout.glyphIndex + tmpLineLayout.numberOfGlyphs;
835         tmpLineLayout.characterIndexInSecondHalfLine = tmpLineLayout.characterIndex + tmpLineLayout.numberOfCharacters;
836
837         tmpLineLayout.isSplitToTwoHalves = isSecondHalf = true;
838       }
839       // Check if the accumulated length fits in the width of the box.
840       if((ellipsisPosition == DevelText::EllipsisPosition::START ||
841           (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && isSecondHalf)) &&
842          completelyFill && !isMultiline &&
843          (tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth))
844       {
845         GlyphIndex glyphIndexToRemove = isSecondHalf ? tmpLineLayout.glyphIndexInSecondHalfLine : tmpLineLayout.glyphIndex;
846
847         while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
848         {
849           GlyphMetrics glyphMetrics;
850           calculatedAdvance = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
851           GetGlyphsMetrics(glyphIndexToRemove,
852                            numberOfGLyphsInGroup,
853                            glyphMetrics,
854                            glyphsBuffer,
855                            mMetrics,
856                            calculatedAdvance);
857
858           const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
859                                                                         lastGlyphOfParagraphPlusOne,
860                                                                         charactersPerGlyphBuffer);
861
862           const Length         charactersPerGlyph  = *(charactersPerGlyphBuffer + glyphIndexToRemove + numberOfGLyphsInGroup - 1u);
863           const bool           hasCharacters       = charactersPerGlyph > 0u;
864           const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndexToRemove);
865           const CharacterIndex characterLastIndex  = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
866
867           // Check whether is a white space.
868           const Character character                = *(textBuffer + characterFirstIndex);
869           const bool      isRemovedGlyphWhiteSpace = TextAbstraction::IsWhiteSpace(character);
870
871           if(isSecondHalf)
872           {
873             // Decrease the number of characters for SecondHalf.
874             tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
875
876             // Decrease the number of glyphs for SecondHalf.
877             tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
878           }
879           else
880           {
881             // Decrease the number of characters.
882             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
883
884             // Decrease the number of glyphs.
885             tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
886           }
887
888           if(isRemovedGlyphWhiteSpace)
889           {
890             tmpLineLayout.penX -= glyphMetrics.advance;
891             tmpLineLayout.length -= glyphMetrics.advance;
892           }
893           else
894           {
895             tmpLineLayout.penX -= (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
896             tmpLineLayout.length -= (std::min(glyphMetrics.advance + parameters.interGlyphExtraAdvance, glyphMetrics.xBearing + glyphMetrics.width));
897           }
898
899           if(isSecondHalf)
900           {
901             tmpLineLayout.glyphIndexInSecondHalfLine += numberOfGLyphsInGroup;
902             tmpLineLayout.characterIndexInSecondHalfLine = characterLastIndex + 1u;
903             glyphIndexToRemove                           = tmpLineLayout.glyphIndexInSecondHalfLine;
904           }
905           else
906           {
907             tmpLineLayout.glyphIndex += numberOfGLyphsInGroup;
908             tmpLineLayout.characterIndex = characterLastIndex + 1u;
909             glyphIndexToRemove           = tmpLineLayout.glyphIndex;
910           }
911         }
912       }
913       else if((completelyFill || isMultiline) &&
914               (tmpLineLayout.length > targetWidth))
915       {
916         // Current word does not fit in the box's width.
917         if(((oneHyphenLaidOut && isHyphenMode) ||
918             (!oneWordLaidOut && isMixedMode && oneHyphenLaidOut)) &&
919            !completelyFill)
920         {
921           parameters.textModel->mVisualModel->mHyphen.glyph.PushBack(hyphenGlyph);
922           parameters.textModel->mVisualModel->mHyphen.index.PushBack(hyphenIndex + 1);
923         }
924
925         if((!oneWordLaidOut && !oneHyphenLaidOut) || completelyFill)
926         {
927           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  Break the word by character\n");
928
929           // The word doesn't fit in the control's width. It needs to be split by character.
930           if(tmpLineLayout.numberOfGlyphs + tmpLineLayout.numberOfGlyphsInSecondHalfLine > 0u)
931           {
932             if(isSecondHalf)
933             {
934               tmpLineLayout.numberOfCharactersInSecondHalfLine -= charactersPerGlyph;
935               tmpLineLayout.numberOfGlyphsInSecondHalfLine -= numberOfGLyphsInGroup;
936             }
937             else
938             {
939               tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
940               tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
941             }
942
943             tmpLineLayout.penX                      = previousTmpPenX;
944             tmpLineLayout.previousAdvance           = previousTmpAdvance;
945             tmpLineLayout.length                    = previousTmpLength;
946             tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
947           }
948
949           if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
950           {
951             // Add part of the word to the line layout and shift the first glyph.
952             MergeLineLayout(lineLayout, tmpLineLayout, true);
953           }
954           else if(ellipsisPosition != DevelText::EllipsisPosition::START ||
955                   (ellipsisPosition == DevelText::EllipsisPosition::START && (!completelyFill)))
956           {
957             // Add part of the word to the line layout.
958             MergeLineLayout(lineLayout, tmpLineLayout, false);
959           }
960         }
961         else
962         {
963           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  Current word does not fit.\n");
964         }
965
966         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
967
968         // Reorder the RTL line.
969         if(bidiParameters.isBidirectional)
970         {
971           ReorderBiDiLayout(parameters,
972                             bidiParameters,
973                             lineLayout,
974                             lineLayout,
975                             true,
976                             enforceEllipsisInSingleLine);
977         }
978
979         return;
980       }
981
982       if((isMultiline || isLastGlyph) &&
983          (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
984       {
985         LineLayout currentLineLayout = lineLayout;
986         oneHyphenLaidOut             = false;
987
988         if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
989         {
990           // Must break the line. Update the line layout, shift the first glyph and return.
991           MergeLineLayout(lineLayout, tmpLineLayout, true);
992         }
993         else
994         {
995           // Must break the line. Update the line layout and return.
996           MergeLineLayout(lineLayout, tmpLineLayout, false);
997         }
998
999         // Reorder the RTL line.
1000         if(bidiParameters.isBidirectional)
1001         {
1002           ReorderBiDiLayout(parameters,
1003                             bidiParameters,
1004                             currentLineLayout,
1005                             lineLayout,
1006                             false,
1007                             enforceEllipsisInSingleLine);
1008         }
1009
1010         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  Must break\n");
1011         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1012
1013         return;
1014       }
1015
1016       if(isMultiline &&
1017          (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
1018       {
1019         oneHyphenLaidOut = false;
1020         oneWordLaidOut   = isWordLaidOut;
1021         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  One word laid-out\n");
1022
1023         // Current glyph is the last one of the current word.
1024         // Add the temporal layout to the current one.
1025         MergeLineLayout(lineLayout, tmpLineLayout, false);
1026
1027         tmpLineLayout.Clear();
1028       }
1029
1030       if(isMultiline &&
1031          ((isHyphenMode || (!oneWordLaidOut && isMixedMode))) &&
1032          (TextAbstraction::LINE_HYPHENATION_BREAK == lineBreakInfo))
1033       {
1034         hyphenGlyph        = GlyphInfo();
1035         hyphenGlyph.fontId = glyphsBuffer[glyphIndex].fontId;
1036
1037         TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
1038         hyphenGlyph.index                      = fontClient.GetGlyphIndex(hyphenGlyph.fontId, HYPHEN_UNICODE);
1039
1040         mMetrics->GetGlyphMetrics(&hyphenGlyph, 1);
1041
1042         if((tmpLineLayout.length + hyphenGlyph.width) <= targetWidth)
1043         {
1044           hyphenIndex      = glyphIndex;
1045           oneHyphenLaidOut = true;
1046
1047           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  One hyphen laid-out\n");
1048
1049           // Current glyph is the last one of the current word hyphen.
1050           // Add the temporal layout to the current one.
1051           MergeLineLayout(lineLayout, tmpLineLayout, false);
1052
1053           tmpLineLayout.Clear();
1054         }
1055       }
1056
1057       glyphIndex += numberOfGLyphsInGroup;
1058     }
1059
1060     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
1061   }
1062
1063   void SetGlyphPositions(const Parameters& layoutParameters,
1064                          Vector2*          glyphPositionsBuffer,
1065                          const LineLayout& layout)
1066   {
1067     // Traverse the glyphs and set the positions.
1068
1069     const GlyphInfo* const glyphsBuffer           = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1070     const float            outlineWidth           = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1071     const Length           numberOfGlyphs         = layout.numberOfGlyphs;
1072     const float            interGlyphExtraAdvance = layoutParameters.interGlyphExtraAdvance;
1073
1074     const GlyphIndex startIndexForGlyph          = layout.glyphIndex;
1075     const GlyphIndex startIndexForGlyphPositions = startIndexForGlyph - layoutParameters.startGlyphIndex;
1076
1077     // Check if the x bearing of the first character is negative.
1078     // If it has a negative x bearing, it will exceed the boundaries of the actor,
1079     // so the penX position needs to be moved to the right.
1080     const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
1081     float            penX  = -glyph.xBearing + mCursorWidth + outlineWidth; //
1082
1083     CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1084                                layoutParameters.textModel->mLogicalModel,
1085                                interGlyphExtraAdvance,
1086                                numberOfGlyphs,
1087                                startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
1088                                startIndexForGlyphPositions,
1089                                glyphPositionsBuffer,
1090                                penX);
1091
1092     if(layout.isSplitToTwoHalves)
1093     {
1094       const GlyphIndex startIndexForGlyphInSecondHalf         = layout.glyphIndexInSecondHalfLine;
1095       const Length     numberOfGlyphsInSecondHalfLine         = layout.numberOfGlyphsInSecondHalfLine;
1096       const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
1097
1098       CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
1099                                  layoutParameters.textModel->mLogicalModel,
1100                                  interGlyphExtraAdvance,
1101                                  numberOfGlyphsInSecondHalfLine,
1102                                  startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
1103                                  startIndexForGlyphPositionsnSecondHalf,
1104                                  glyphPositionsBuffer,
1105                                  penX);
1106     }
1107   }
1108
1109   void SetGlyphPositions(const Parameters&     layoutParameters,
1110                          Vector2*              glyphPositionsBuffer,
1111                          LayoutBidiParameters& layoutBidiParameters,
1112                          const LineLayout&     layout)
1113   {
1114     const BidirectionalLineInfoRun& bidiLine                 = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
1115     const GlyphInfo* const          glyphsBuffer             = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1116     const GlyphIndex* const         charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
1117
1118     CharacterIndex characterLogicalIndex = 0u;
1119     CharacterIndex characterVisualIndex  = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
1120     bool           extendedToSecondHalf  = false; // Whether the logical index is extended to second half
1121
1122     float penX = 0.f;
1123
1124     if(layout.isSplitToTwoHalves)
1125     {
1126       CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1127                                  layoutParameters.textModel->mLogicalModel,
1128                                  layoutBidiParameters.bidiLineIndex,
1129                                  layoutParameters.startGlyphIndex,
1130                                  glyphPositionsBuffer,
1131                                  characterVisualIndex,
1132                                  characterLogicalIndex,
1133                                  penX);
1134     }
1135
1136     if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
1137     {
1138       extendedToSecondHalf  = true;
1139       characterLogicalIndex = 0u;
1140       characterVisualIndex  = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
1141
1142       CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1143                                  layoutParameters.textModel->mLogicalModel,
1144                                  layoutBidiParameters.bidiLineIndex,
1145                                  layoutParameters.startGlyphIndex,
1146                                  glyphPositionsBuffer,
1147                                  characterVisualIndex,
1148                                  characterLogicalIndex,
1149                                  penX);
1150     }
1151
1152     const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
1153     const GlyphInfo& glyph      = *(glyphsBuffer + glyphIndex);
1154
1155     penX += -glyph.xBearing;
1156
1157     // Traverses the characters of the right to left paragraph.
1158     if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
1159     {
1160       TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1161                                               layoutParameters.textModel->mLogicalModel->mText.Begin(),
1162                                               layoutParameters.startGlyphIndex,
1163                                               layoutParameters.interGlyphExtraAdvance,
1164                                               bidiLine.characterRunForSecondHalfLine,
1165                                               bidiLine.visualToLogicalMapSecondHalf,
1166                                               glyphPositionsBuffer,
1167                                               characterLogicalIndex,
1168                                               penX);
1169     }
1170
1171     characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
1172
1173     TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
1174                                             layoutParameters.textModel->mLogicalModel->mText.Begin(),
1175                                             layoutParameters.startGlyphIndex,
1176                                             layoutParameters.interGlyphExtraAdvance,
1177                                             bidiLine.characterRun,
1178                                             bidiLine.visualToLogicalMap,
1179                                             glyphPositionsBuffer,
1180                                             characterLogicalIndex,
1181                                             penX);
1182   }
1183
1184   /**
1185    * @brief Resizes the line buffer.
1186    *
1187    * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
1188    * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
1189    * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
1190    * @param[in] updateCurrentBuffer Whether the layout is updated.
1191    *
1192    * @return Pointer to either lines or newLines.
1193    */
1194   LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
1195                              Vector<LineRun>& newLines,
1196                              Length&          linesCapacity,
1197                              bool             updateCurrentBuffer)
1198   {
1199     LineRun* linesBuffer = nullptr;
1200     // Reserve more space for the next lines.
1201     linesCapacity *= 2u;
1202     if(updateCurrentBuffer)
1203     {
1204       newLines.Resize(linesCapacity);
1205       linesBuffer = newLines.Begin();
1206     }
1207     else
1208     {
1209       lines.Resize(linesCapacity);
1210       linesBuffer = lines.Begin();
1211     }
1212
1213     return linesBuffer;
1214   }
1215
1216   /**
1217    * Ellipsis a line if it exceeds the width's of the bounding box.
1218    *
1219    * @param[in] layoutParameters The parameters needed to layout the text.
1220    * @param[in] layout The line layout.
1221    * @param[in,out] layoutSize The text's layout size.
1222    * @param[in,out] linesBuffer Pointer to the line's buffer.
1223    * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
1224    * @param[in,out] numberOfLines The number of laid-out lines.
1225    * @param[in] penY The vertical layout position.
1226    * @param[in] currentParagraphDirection The current paragraph's direction.
1227    * @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
1228    * @param[in] ellipsisPosition Where is the location the text elide
1229    *
1230    * return Whether the line is ellipsized.
1231    */
1232   bool EllipsisLine(const Parameters&                 layoutParameters,
1233                     LayoutBidiParameters&             layoutBidiParameters,
1234                     const LineLayout&                 layout,
1235                     Size&                             layoutSize,
1236                     LineRun*                          linesBuffer,
1237                     Vector2*                          glyphPositionsBuffer,
1238                     Length&                           numberOfLines,
1239                     float                             penY,
1240                     bool&                             isAutoScrollEnabled,
1241                     DevelText::EllipsisPosition::Type ellipsisPosition,
1242                     bool                              enforceEllipsisInSingleLine)
1243   {
1244     const bool ellipsis    = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? (penY - layout.descender > layoutParameters.boundingBox.height) : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1245     const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1246     if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1247     {
1248       isAutoScrollEnabled = false;
1249       // Do not layout more lines if ellipsis is enabled.
1250
1251       // The last line needs to be completely filled with characters.
1252       // Part of a word may be used.
1253
1254       LineRun*   lineRun = nullptr;
1255       LineLayout ellipsisLayout;
1256       if(0u != numberOfLines)
1257       {
1258         // Get the last line and layout it again with the 'completelyFill' flag to true.
1259         lineRun = linesBuffer + (numberOfLines - 1u);
1260         penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1261
1262         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1263       }
1264       else
1265       {
1266         // At least there is space reserved for one line.
1267         lineRun = linesBuffer;
1268
1269         lineRun->glyphRun.glyphIndex = 0u;
1270         ellipsisLayout.glyphIndex    = 0u;
1271         lineRun->isSplitToTwoHalves  = false;
1272
1273         ++numberOfLines;
1274       }
1275
1276       GetLineLayoutForBox(layoutParameters,
1277                           layoutBidiParameters,
1278                           ellipsisLayout,
1279                           true,
1280                           ellipsisPosition,
1281                           enforceEllipsisInSingleLine,
1282                           true);
1283
1284       if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1285       {
1286         lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1287       }
1288
1289       lineRun->glyphRun.numberOfGlyphs         = ellipsisLayout.numberOfGlyphs;
1290       lineRun->characterRun.characterIndex     = ellipsisLayout.characterIndex;
1291       lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1292       lineRun->width                           = ellipsisLayout.length;
1293       lineRun->extraLength                     = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1294       lineRun->ascender                        = ellipsisLayout.ascender;
1295       lineRun->descender                       = ellipsisLayout.descender;
1296       lineRun->ellipsis                        = true;
1297
1298       lineRun->isSplitToTwoHalves                               = ellipsisLayout.isSplitToTwoHalves;
1299       lineRun->glyphRunSecondHalf.glyphIndex                    = ellipsisLayout.glyphIndexInSecondHalfLine;
1300       lineRun->glyphRunSecondHalf.numberOfGlyphs                = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1301       lineRun->characterRunForSecondHalfLine.characterIndex     = ellipsisLayout.characterIndexInSecondHalfLine;
1302       lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1303
1304       layoutSize.width = layoutParameters.boundingBox.width;
1305       if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1306       {
1307         layoutSize.height += GetLineHeight(*lineRun, true);
1308       }
1309       else
1310       {
1311         //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1312         //usually we don't includ it in normal cases using GetLineHeight()
1313         if(lineRun->lineSpacing < 0)
1314         {
1315           layoutSize.height -= lineRun->lineSpacing;
1316         }
1317       }
1318
1319       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1320
1321       if(layoutBidiParameters.isBidirectional)
1322       {
1323         layoutBidiParameters.bidiLineIndex = 0u;
1324         for(Vector<BidirectionalLineInfoRun>::ConstIterator it    = bidirectionalLinesInfo.Begin(),
1325                                                             endIt = bidirectionalLinesInfo.End();
1326             it != endIt;
1327             ++it, ++layoutBidiParameters.bidiLineIndex)
1328         {
1329           const BidirectionalLineInfoRun& run = *it;
1330           //To handle case when the laid characters exist in next line.
1331           //More than one BidirectionalLineInfoRun could start with same character.
1332           //When need to check also numberOfCharacters in line.
1333           //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1334           if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1335              ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1336              ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1337              ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1338           {
1339             // Found where to insert the bidi line info.
1340             break;
1341           }
1342         }
1343       }
1344
1345       const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1346
1347       if((nullptr != bidirectionalLineInfo) &&
1348          !bidirectionalLineInfo->isIdentity &&
1349          (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1350       {
1351         lineRun->direction = RTL;
1352         SetGlyphPositions(layoutParameters,
1353                           glyphPositionsBuffer,
1354                           layoutBidiParameters,
1355                           ellipsisLayout);
1356       }
1357       else
1358       {
1359         lineRun->direction = LTR;
1360         SetGlyphPositions(layoutParameters,
1361                           glyphPositionsBuffer,
1362                           ellipsisLayout);
1363       }
1364     }
1365
1366     return ellipsis;
1367   }
1368
1369   /**
1370    * @brief Updates the text layout with a new laid-out line.
1371    *
1372    * @param[in] layoutParameters The parameters needed to layout the text.
1373    * @param[in] layout The line layout.
1374    * @param[in,out] layoutSize The text's layout size.
1375    * @param[in,out] linesBuffer Pointer to the line's buffer.
1376    * @param[in] index Index to the vector of glyphs.
1377    * @param[in,out] numberOfLines The number of laid-out lines.
1378    * @param[in] isLastLine Whether the laid-out line is the last one.
1379    */
1380   void UpdateTextLayout(const Parameters& layoutParameters,
1381                         const LineLayout& layout,
1382                         Size&             layoutSize,
1383                         LineRun*          linesBuffer,
1384                         GlyphIndex        index,
1385                         Length&           numberOfLines,
1386                         bool              isLastLine)
1387   {
1388     LineRun& lineRun = *(linesBuffer + numberOfLines);
1389     ++numberOfLines;
1390
1391     lineRun.glyphRun.glyphIndex             = index;
1392     lineRun.glyphRun.numberOfGlyphs         = layout.numberOfGlyphs;
1393     lineRun.characterRun.characterIndex     = layout.characterIndex;
1394     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1395     lineRun.width                           = layout.length;
1396     lineRun.extraLength                     = std::ceil(layout.whiteSpaceLengthEndOfLine);
1397
1398     lineRun.isSplitToTwoHalves                               = layout.isSplitToTwoHalves;
1399     lineRun.glyphRunSecondHalf.glyphIndex                    = layout.glyphIndexInSecondHalfLine;
1400     lineRun.glyphRunSecondHalf.numberOfGlyphs                = layout.numberOfGlyphsInSecondHalfLine;
1401     lineRun.characterRunForSecondHalfLine.characterIndex     = layout.characterIndexInSecondHalfLine;
1402     lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1403
1404     // Rounds upward to avoid a non integer size.
1405     lineRun.width = std::ceil(lineRun.width);
1406
1407     lineRun.ascender  = layout.ascender;
1408     lineRun.descender = layout.descender;
1409     lineRun.direction = layout.direction;
1410     lineRun.ellipsis  = false;
1411
1412     lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1413
1414     // Update the actual size.
1415     if(lineRun.width > layoutSize.width)
1416     {
1417       layoutSize.width = lineRun.width;
1418     }
1419
1420     layoutSize.height += GetLineHeight(lineRun, isLastLine);
1421   }
1422
1423   /**
1424    * @brief Updates the text layout with the last laid-out line.
1425    *
1426    * @param[in] layoutParameters The parameters needed to layout the text.
1427    * @param[in] characterIndex The character index of the line.
1428    * @param[in] glyphIndex The glyph index of the line.
1429    * @param[in,out] layoutSize The text's layout size.
1430    * @param[in,out] linesBuffer Pointer to the line's buffer.
1431    * @param[in,out] numberOfLines The number of laid-out lines.
1432    */
1433   void UpdateTextLayout(const Parameters& layoutParameters,
1434                         CharacterIndex    characterIndex,
1435                         GlyphIndex        glyphIndex,
1436                         Size&             layoutSize,
1437                         LineRun*          linesBuffer,
1438                         Length&           numberOfLines)
1439   {
1440     const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1441
1442     // Need to add a new line with no characters but with height to increase the layoutSize.height
1443     const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1444
1445     Text::FontMetrics fontMetrics;
1446     if(0u != glyphInfo.fontId)
1447     {
1448       mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1449     }
1450
1451     LineRun& lineRun = *(linesBuffer + numberOfLines);
1452     ++numberOfLines;
1453
1454     lineRun.glyphRun.glyphIndex             = glyphIndex;
1455     lineRun.glyphRun.numberOfGlyphs         = 0u;
1456     lineRun.characterRun.characterIndex     = characterIndex;
1457     lineRun.characterRun.numberOfCharacters = 0u;
1458     lineRun.width                           = 0.f;
1459     lineRun.ascender                        = fontMetrics.ascender;
1460     lineRun.descender                       = fontMetrics.descender;
1461     lineRun.extraLength                     = 0.f;
1462     lineRun.alignmentOffset                 = 0.f;
1463     lineRun.direction                       = LTR;
1464     lineRun.ellipsis                        = false;
1465
1466     lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
1467
1468     layoutSize.height += GetLineHeight(lineRun, true);
1469   }
1470
1471   /**
1472    * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1473    *
1474    * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1475    * @param[in,out] layoutSize The text's layout size.
1476    */
1477   void UpdateLayoutSize(const Vector<LineRun>& lines,
1478                         Size&                  layoutSize)
1479   {
1480     for(Vector<LineRun>::ConstIterator it    = lines.Begin(),
1481                                        endIt = lines.End();
1482         it != endIt;
1483         ++it)
1484     {
1485       const LineRun& line       = *it;
1486       bool           isLastLine = (it + 1 == endIt);
1487
1488       if(line.width > layoutSize.width)
1489       {
1490         layoutSize.width = line.width;
1491       }
1492
1493       layoutSize.height += GetLineHeight(line, isLastLine);
1494     }
1495   }
1496
1497   /**
1498    * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1499    *
1500    * @param[in] layoutParameters The parameters needed to layout the text.
1501    * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1502    * @param[in] characterOffset The offset to be added to the runs of characters.
1503    * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1504    */
1505   void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1506                               Vector<LineRun>&  lines,
1507                               Length            characterOffset,
1508                               Length            glyphOffset)
1509   {
1510     // Update the glyph and character runs.
1511     for(Vector<LineRun>::Iterator it    = lines.Begin() + layoutParameters.startLineIndex,
1512                                   endIt = lines.End();
1513         it != endIt;
1514         ++it)
1515     {
1516       LineRun& line = *it;
1517
1518       line.glyphRun.glyphIndex         = glyphOffset;
1519       line.characterRun.characterIndex = characterOffset;
1520
1521       glyphOffset += line.glyphRun.numberOfGlyphs;
1522       characterOffset += line.characterRun.numberOfCharacters;
1523     }
1524   }
1525
1526   bool LayoutText(Parameters&                       layoutParameters,
1527                   Size&                             layoutSize,
1528                   bool                              elideTextEnabled,
1529                   bool&                             isAutoScrollEnabled,
1530                   DevelText::EllipsisPosition::Type ellipsisPosition)
1531   {
1532     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1533     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1534
1535     layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1536     layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1537
1538     //Reset indices of ElidedGlyphs
1539     layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1540     layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1541     layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1542     layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1543
1544     Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1545
1546     if(0u == layoutParameters.numberOfGlyphs)
1547     {
1548       // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1549       if(layoutParameters.isLastNewParagraph)
1550       {
1551         Length numberOfLines = lines.Count();
1552         if(0u != numberOfLines)
1553         {
1554           const LineRun& lastLine = *(lines.End() - 1u);
1555
1556           if(0u != lastLine.characterRun.numberOfCharacters)
1557           {
1558             // Need to add a new line with no characters but with height to increase the layoutSize.height
1559             LineRun newLine;
1560             Initialize(newLine);
1561             lines.PushBack(newLine);
1562
1563             UpdateTextLayout(layoutParameters,
1564                              lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1565                              lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1566                              layoutSize,
1567                              lines.Begin(),
1568                              numberOfLines);
1569           }
1570         }
1571       }
1572
1573       // Calculates the layout size.
1574       UpdateLayoutSize(lines,
1575                        layoutSize);
1576
1577       // Rounds upward to avoid a non integer size.
1578       layoutSize.height = std::ceil(layoutSize.height);
1579
1580       // Nothing else do if there are no glyphs to layout.
1581       return false;
1582     }
1583
1584     const GlyphIndex lastGlyphPlusOne    = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1585     const Length     totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1586     Vector<Vector2>& glyphPositions      = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1587
1588     // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1589     // This extra line needs to be removed.
1590     if(0u != lines.Count())
1591     {
1592       Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1593
1594       if((0u == lastLine->characterRun.numberOfCharacters) &&
1595          (lastGlyphPlusOne == totalNumberOfGlyphs))
1596       {
1597         lines.Remove(lastLine);
1598       }
1599     }
1600
1601     // Retrieve BiDi info.
1602     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1603
1604     const CharacterIndex* const                  glyphsToCharactersBuffer    = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1605     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1606     const Vector<BidirectionalLineInfoRun>&      bidirectionalLinesInfo      = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1607
1608     // Set the layout bidirectional paramters.
1609     LayoutBidiParameters layoutBidiParameters;
1610
1611     // Whether the layout is being updated or set from scratch.
1612     const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1613
1614     Vector2*        glyphPositionsBuffer = nullptr;
1615     Vector<Vector2> newGlyphPositions;
1616
1617     LineRun*        linesBuffer = nullptr;
1618     Vector<LineRun> newLines;
1619
1620     // Estimate the number of lines.
1621     Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1622     Length numberOfLines = 0u;
1623
1624     if(updateCurrentBuffer)
1625     {
1626       newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1627       glyphPositionsBuffer = newGlyphPositions.Begin();
1628
1629       newLines.Resize(linesCapacity);
1630       linesBuffer = newLines.Begin();
1631     }
1632     else
1633     {
1634       glyphPositionsBuffer = glyphPositions.Begin();
1635
1636       lines.Resize(linesCapacity);
1637       linesBuffer = lines.Begin();
1638     }
1639
1640     float penY            = CalculateLineOffset(lines,
1641                                      layoutParameters.startLineIndex);
1642     bool  anyLineIsEliped = false;
1643     for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1644     {
1645       layoutBidiParameters.Clear();
1646
1647       if(hasBidiParagraphs)
1648       {
1649         const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1650
1651         for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalParagraphsInfo.Begin(),
1652                                                                  endIt = bidirectionalParagraphsInfo.End();
1653             it != endIt;
1654             ++it, ++layoutBidiParameters.bidiParagraphIndex)
1655         {
1656           const BidirectionalParagraphInfoRun& run = *it;
1657
1658           const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1659
1660           if(lastCharacterIndex <= startCharacterIndex)
1661           {
1662             // Do not process, the paragraph has already been processed.
1663             continue;
1664           }
1665
1666           if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1667           {
1668             layoutBidiParameters.paragraphDirection = run.direction;
1669             layoutBidiParameters.isBidirectional    = true;
1670           }
1671
1672           // Has already been found.
1673           break;
1674         }
1675
1676         if(layoutBidiParameters.isBidirectional)
1677         {
1678           for(Vector<BidirectionalLineInfoRun>::ConstIterator it    = bidirectionalLinesInfo.Begin(),
1679                                                               endIt = bidirectionalLinesInfo.End();
1680               it != endIt;
1681               ++it, ++layoutBidiParameters.bidiLineIndex)
1682           {
1683             const BidirectionalLineInfoRun& run = *it;
1684
1685             const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1686
1687             if(lastCharacterIndex <= startCharacterIndex)
1688             {
1689               // skip
1690               continue;
1691             }
1692
1693             if(startCharacterIndex < lastCharacterIndex)
1694             {
1695               // Found where to insert the bidi line info.
1696               break;
1697             }
1698           }
1699         }
1700       }
1701
1702       CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1703
1704       // Get the layout for the line.
1705       LineLayout layout;
1706       layout.direction  = layoutBidiParameters.paragraphDirection;
1707       layout.glyphIndex = index;
1708       GetLineLayoutForBox(layoutParameters,
1709                           layoutBidiParameters,
1710                           layout,
1711                           false,
1712                           ellipsisPosition,
1713                           false,
1714                           elideTextEnabled);
1715
1716       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex);
1717       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex);
1718       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs);
1719       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters);
1720       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                length %f\n", layout.length);
1721
1722       if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1723       {
1724         // The width is too small and no characters are laid-out.
1725         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1726
1727         lines.Resize(numberOfLines);
1728
1729         // Rounds upward to avoid a non integer size.
1730         layoutSize.height = std::ceil(layoutSize.height);
1731
1732         return false;
1733       }
1734
1735       // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1736       // of the box.
1737       penY += layout.ascender;
1738
1739       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  pen y %f\n", penY);
1740
1741       bool ellipsis = false;
1742       if(elideTextEnabled)
1743       {
1744         layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1745
1746         // Does the ellipsis of the last line.
1747         ellipsis = EllipsisLine(layoutParameters,
1748                                 layoutBidiParameters,
1749                                 layout,
1750                                 layoutSize,
1751                                 linesBuffer,
1752                                 glyphPositionsBuffer,
1753                                 numberOfLines,
1754                                 penY,
1755                                 isAutoScrollEnabled,
1756                                 ellipsisPosition,
1757                                 false);
1758       }
1759
1760       if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1761       {
1762         const bool isMultiline = mLayout == MULTI_LINE_BOX;
1763         if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1764         {
1765           ellipsis = EllipsisLine(layoutParameters,
1766                                   layoutBidiParameters,
1767                                   layout,
1768                                   layoutSize,
1769                                   linesBuffer,
1770                                   glyphPositionsBuffer,
1771                                   numberOfLines,
1772                                   penY,
1773                                   isAutoScrollEnabled,
1774                                   ellipsisPosition,
1775                                   true);
1776         }
1777
1778         //clear hyphen from ellipsis line
1779         const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1780         Length        hyphensCount  = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1781
1782         while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1783         {
1784           layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1785           layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1786           hyphensCount--;
1787         }
1788
1789         // No more lines to layout.
1790         break;
1791       }
1792       else
1793       {
1794         //In START location of ellipsis whether to shift lines or not.
1795         anyLineIsEliped |= ellipsis;
1796
1797         // Whether the last line has been laid-out.
1798         const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1799
1800         if(numberOfLines == linesCapacity)
1801         {
1802           // Reserve more space for the next lines.
1803           linesBuffer = ResizeLinesBuffer(lines,
1804                                           newLines,
1805                                           linesCapacity,
1806                                           updateCurrentBuffer);
1807         }
1808
1809         // Updates the current text's layout with the line's layout.
1810         UpdateTextLayout(layoutParameters,
1811                          layout,
1812                          layoutSize,
1813                          linesBuffer,
1814                          index,
1815                          numberOfLines,
1816                          isLastLine);
1817
1818         const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1819
1820         if((nextIndex == totalNumberOfGlyphs) &&
1821            layoutParameters.isLastNewParagraph &&
1822            (mLayout == MULTI_LINE_BOX))
1823         {
1824           // The last character of the text is a new paragraph character.
1825           // An extra line with no characters is added to increase the text's height
1826           // in order to place the cursor.
1827
1828           if(numberOfLines == linesCapacity)
1829           {
1830             // Reserve more space for the next lines.
1831             linesBuffer = ResizeLinesBuffer(lines,
1832                                             newLines,
1833                                             linesCapacity,
1834                                             updateCurrentBuffer);
1835           }
1836
1837           UpdateTextLayout(layoutParameters,
1838                            layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1839                            index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1840                            layoutSize,
1841                            linesBuffer,
1842                            numberOfLines);
1843         } // whether to add a last line.
1844
1845         const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1846
1847         if((nullptr != bidirectionalLineInfo) &&
1848            !bidirectionalLineInfo->isIdentity &&
1849            (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1850         {
1851           SetGlyphPositions(layoutParameters,
1852                             glyphPositionsBuffer,
1853                             layoutBidiParameters,
1854                             layout);
1855         }
1856         else
1857         {
1858           // Sets the positions of the glyphs.
1859           SetGlyphPositions(layoutParameters,
1860                             glyphPositionsBuffer,
1861                             layout);
1862         }
1863
1864         // Updates the vertical pen's position.
1865         penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
1866
1867         // Increase the glyph index.
1868         index = nextIndex;
1869       } // no ellipsis
1870     }   // end for() traversing glyphs.
1871
1872     //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1873     if(anyLineIsEliped && numberOfLines > 1u)
1874     {
1875       if(ellipsisPosition == DevelText::EllipsisPosition::START)
1876       {
1877         Length lineIndex = 0;
1878         while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1879         {
1880           LineRun& delLine = linesBuffer[lineIndex];
1881           delLine.ellipsis = true;
1882
1883           layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1884           for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1885           {
1886             linesBuffer[lineIndex]          = linesBuffer[lineIndex + 1];
1887             linesBuffer[lineIndex].ellipsis = false;
1888           }
1889           numberOfLines--;
1890         }
1891         linesBuffer[0u].ellipsis = true;
1892       }
1893       else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1894       {
1895         Length middleLineIndex   = (numberOfLines) / 2u;
1896         Length ellipsisLineIndex = 0u;
1897         while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1898         {
1899           LineRun& delLine = linesBuffer[middleLineIndex];
1900           delLine.ellipsis = true;
1901
1902           layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1903           for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
1904           {
1905             linesBuffer[lineIndex]          = linesBuffer[lineIndex + 1];
1906             linesBuffer[lineIndex].ellipsis = false;
1907           }
1908           numberOfLines--;
1909           ellipsisLineIndex = middleLineIndex - 1u;
1910           middleLineIndex   = (numberOfLines) / 2u;
1911         }
1912
1913         linesBuffer[ellipsisLineIndex].ellipsis = true;
1914       }
1915     }
1916
1917     if(updateCurrentBuffer)
1918     {
1919       glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1920                             newGlyphPositions.Begin(),
1921                             newGlyphPositions.End());
1922       glyphPositions.Resize(totalNumberOfGlyphs);
1923
1924       newLines.Resize(numberOfLines);
1925
1926       // Current text's layout size adds only the newly laid-out lines.
1927       // Updates the layout size with the previously laid-out lines.
1928       UpdateLayoutSize(lines,
1929                        layoutSize);
1930
1931       if(0u != newLines.Count())
1932       {
1933         const LineRun& lastLine = *(newLines.End() - 1u);
1934
1935         const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1936         const Length glyphOffset     = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1937
1938         // Update the indices of the runs before the new laid-out lines are inserted.
1939         UpdateLineIndexOffsets(layoutParameters,
1940                                lines,
1941                                characterOffset,
1942                                glyphOffset);
1943
1944         // Insert the lines.
1945         lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1946                      newLines.Begin(),
1947                      newLines.End());
1948       }
1949     }
1950     else
1951     {
1952       lines.Resize(numberOfLines);
1953     }
1954
1955     // Rounds upward to avoid a non integer size.
1956     layoutSize.height = std::ceil(layoutSize.height);
1957
1958     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1959
1960     return true;
1961   }
1962
1963   void Align(const Size&                     size,
1964              CharacterIndex                  startIndex,
1965              Length                          numberOfCharacters,
1966              Text::HorizontalAlignment::Type horizontalAlignment,
1967              Vector<LineRun>&                lines,
1968              float&                          alignmentOffset,
1969              Dali::LayoutDirection::Type     layoutDirection,
1970              bool                            matchLayoutDirection)
1971   {
1972     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1973
1974     alignmentOffset = MAX_FLOAT;
1975     // Traverse all lines and align the glyphs.
1976     for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1977         it != endIt;
1978         ++it)
1979     {
1980       LineRun& line = *it;
1981
1982       if(line.characterRun.characterIndex < startIndex)
1983       {
1984         // Do not align lines which have already been aligned.
1985         continue;
1986       }
1987
1988       if(line.characterRun.characterIndex > lastCharacterPlusOne)
1989       {
1990         // Do not align lines beyond the last laid-out character.
1991         break;
1992       }
1993
1994       if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1995       {
1996         // Do not align lines beyond the last laid-out character unless the line is last and empty.
1997         break;
1998       }
1999
2000       // Calculate the line's alignment offset accordingly with the align option,
2001       // the box width, line length, and the paragraph's direction.
2002       CalculateHorizontalAlignment(size.width,
2003                                    horizontalAlignment,
2004                                    line,
2005                                    layoutDirection,
2006                                    matchLayoutDirection);
2007
2008       // Updates the alignment offset.
2009       alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2010     }
2011   }
2012
2013   void CalculateHorizontalAlignment(float                       boxWidth,
2014                                     HorizontalAlignment::Type   horizontalAlignment,
2015                                     LineRun&                    line,
2016                                     Dali::LayoutDirection::Type layoutDirection,
2017                                     bool                        matchLayoutDirection)
2018   {
2019     line.alignmentOffset = 0.f;
2020     const bool isLineRTL = RTL == line.direction;
2021
2022     // Whether to swap the alignment.
2023     // 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.
2024     bool  isLayoutRTL = isLineRTL;
2025     float lineLength  = line.width;
2026
2027     // match align for system language direction
2028     if(matchLayoutDirection)
2029     {
2030       // Swap the alignment type if the line is right to left.
2031       isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2032     }
2033     // Calculate the horizontal line offset.
2034     switch(horizontalAlignment)
2035     {
2036       case HorizontalAlignment::BEGIN:
2037       {
2038         if(isLayoutRTL)
2039         {
2040           if(isLineRTL)
2041           {
2042             lineLength += line.extraLength;
2043           }
2044
2045           line.alignmentOffset = boxWidth - lineLength;
2046         }
2047         else
2048         {
2049           line.alignmentOffset = 0.f;
2050
2051           if(isLineRTL)
2052           {
2053             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2054             line.alignmentOffset -= line.extraLength;
2055           }
2056         }
2057         break;
2058       }
2059       case HorizontalAlignment::CENTER:
2060       {
2061         line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2062
2063         if(isLineRTL)
2064         {
2065           line.alignmentOffset -= line.extraLength;
2066         }
2067
2068         line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2069         break;
2070       }
2071       case HorizontalAlignment::END:
2072       {
2073         if(isLayoutRTL)
2074         {
2075           line.alignmentOffset = 0.f;
2076
2077           if(isLineRTL)
2078           {
2079             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2080             line.alignmentOffset -= line.extraLength;
2081           }
2082         }
2083         else
2084         {
2085           if(isLineRTL)
2086           {
2087             lineLength += line.extraLength;
2088           }
2089
2090           line.alignmentOffset = boxWidth - lineLength;
2091         }
2092         break;
2093       }
2094     }
2095   }
2096
2097   void Initialize(LineRun& line)
2098   {
2099     line.glyphRun.glyphIndex                              = 0u;
2100     line.glyphRun.numberOfGlyphs                          = 0u;
2101     line.characterRun.characterIndex                      = 0u;
2102     line.characterRun.numberOfCharacters                  = 0u;
2103     line.width                                            = 0.f;
2104     line.ascender                                         = 0.f;
2105     line.descender                                        = 0.f;
2106     line.extraLength                                      = 0.f;
2107     line.alignmentOffset                                  = 0.f;
2108     line.direction                                        = LTR;
2109     line.ellipsis                                         = false;
2110     line.lineSpacing                                      = mDefaultLineSpacing;
2111     line.isSplitToTwoHalves                               = false;
2112     line.glyphRunSecondHalf.glyphIndex                    = 0u;
2113     line.glyphRunSecondHalf.numberOfGlyphs                = 0u;
2114     line.characterRunForSecondHalfLine.characterIndex     = 0u;
2115     line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2116   }
2117
2118   Type  mLayout;
2119   float mCursorWidth;
2120   float mDefaultLineSpacing;
2121   float mDefaultLineSize;
2122
2123   IntrusivePtr<Metrics> mMetrics;
2124   float                 mRelativeLineSize;
2125 };
2126
2127 Engine::Engine()
2128 : mImpl{nullptr}
2129 {
2130   mImpl = new Engine::Impl();
2131 }
2132
2133 Engine::~Engine()
2134 {
2135   delete mImpl;
2136 }
2137
2138 void Engine::SetMetrics(MetricsPtr& metrics)
2139 {
2140   mImpl->mMetrics = metrics;
2141 }
2142
2143 void Engine::SetLayout(Type layout)
2144 {
2145   mImpl->mLayout = layout;
2146 }
2147
2148 Engine::Type Engine::GetLayout() const
2149 {
2150   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2151   return mImpl->mLayout;
2152 }
2153
2154 void Engine::SetCursorWidth(int width)
2155 {
2156   mImpl->mCursorWidth = static_cast<float>(width);
2157 }
2158
2159 int Engine::GetCursorWidth() const
2160 {
2161   return static_cast<int>(mImpl->mCursorWidth);
2162 }
2163
2164 bool Engine::LayoutText(Parameters&                       layoutParameters,
2165                         Size&                             layoutSize,
2166                         bool                              elideTextEnabled,
2167                         bool&                             isAutoScrollEnabled,
2168                         DevelText::EllipsisPosition::Type ellipsisPosition)
2169 {
2170   return mImpl->LayoutText(layoutParameters,
2171                            layoutSize,
2172                            elideTextEnabled,
2173                            isAutoScrollEnabled,
2174                            ellipsisPosition);
2175 }
2176
2177 void Engine::Align(const Size&                     size,
2178                    CharacterIndex                  startIndex,
2179                    Length                          numberOfCharacters,
2180                    Text::HorizontalAlignment::Type horizontalAlignment,
2181                    Vector<LineRun>&                lines,
2182                    float&                          alignmentOffset,
2183                    Dali::LayoutDirection::Type     layoutDirection,
2184                    bool                            matchLayoutDirection)
2185 {
2186   mImpl->Align(size,
2187                startIndex,
2188                numberOfCharacters,
2189                horizontalAlignment,
2190                lines,
2191                alignmentOffset,
2192                layoutDirection,
2193                matchLayoutDirection);
2194 }
2195
2196 void Engine::SetDefaultLineSpacing(float lineSpacing)
2197 {
2198   mImpl->mDefaultLineSpacing = lineSpacing;
2199 }
2200
2201 float Engine::GetDefaultLineSpacing() const
2202 {
2203   return mImpl->mDefaultLineSpacing;
2204 }
2205
2206 void Engine::SetDefaultLineSize(float lineSize)
2207 {
2208   mImpl->mDefaultLineSize = lineSize;
2209 }
2210
2211 float Engine::GetDefaultLineSize() const
2212 {
2213   return mImpl->mDefaultLineSize;
2214 }
2215
2216 void Engine::SetRelativeLineSize(float relativeLineSize)
2217 {
2218   mImpl->mRelativeLineSize = relativeLineSize;
2219 }
2220
2221 float Engine::GetRelativeLineSize() const
2222 {
2223   return mImpl->mRelativeLineSize;
2224 }
2225
2226 } // namespace Layout
2227
2228 } // namespace Text
2229
2230 } // namespace Toolkit
2231
2232 } // namespace Dali