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