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