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