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