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