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