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