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