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