[dali_2.1.26] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/layouts/layout-engine.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/integration-api/debug.h>
24 #include <cmath>
25 #include <limits>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
31 #include <dali-toolkit/internal/text/layouts/layout-engine-helper-functions.h>
32 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
33 #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                     bool                              isAutoScrollMaxTextureExceeded,
1264                     DevelText::EllipsisPosition::Type ellipsisPosition,
1265                     bool                              enforceEllipsisInSingleLine)
1266   {
1267     const bool ellipsis    = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? isAutoScrollMaxTextureExceeded : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
1268     const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
1269     if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
1270     {
1271       if(penY - layout.descender > layoutParameters.boundingBox.height)
1272       {
1273         // Even if auto scroll is enabled and text is bigger than max texture size,
1274         // if the the height is small, auto scroll should not work.
1275         isAutoScrollEnabled = false;
1276       }
1277
1278       // Do not layout more lines if ellipsis is enabled.
1279       // The last line needs to be completely filled with characters.
1280       // Part of a word may be used.
1281
1282       LineRun*   lineRun = nullptr;
1283       LineLayout ellipsisLayout;
1284
1285       ellipsisLayout.relativeLineSize = layout.relativeLineSize;
1286
1287       if(0u != numberOfLines)
1288       {
1289         // Get the last line and layout it again with the 'completelyFill' flag to true.
1290         lineRun = linesBuffer + (numberOfLines - 1u);
1291         penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
1292
1293         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
1294       }
1295       else
1296       {
1297         // At least there is space reserved for one line.
1298         lineRun = linesBuffer;
1299
1300         lineRun->glyphRun.glyphIndex = 0u;
1301         ellipsisLayout.glyphIndex    = 0u;
1302         lineRun->isSplitToTwoHalves  = false;
1303
1304         ++numberOfLines;
1305       }
1306
1307       GetLineLayoutForBox(layoutParameters,
1308                           layoutBidiParameters,
1309                           ellipsisLayout,
1310                           true,
1311                           ellipsisPosition,
1312                           enforceEllipsisInSingleLine,
1313                           true);
1314
1315       if(ellipsisPosition == DevelText::EllipsisPosition::START && !isMultiline)
1316       {
1317         lineRun->glyphRun.glyphIndex = ellipsisLayout.glyphIndex;
1318       }
1319
1320       lineRun->glyphRun.numberOfGlyphs         = ellipsisLayout.numberOfGlyphs;
1321       lineRun->characterRun.characterIndex     = ellipsisLayout.characterIndex;
1322       lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
1323       lineRun->width                           = ellipsisLayout.length;
1324       lineRun->extraLength                     = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
1325       lineRun->ascender                        = ellipsisLayout.ascender;
1326       lineRun->descender                       = ellipsisLayout.descender;
1327       lineRun->ellipsis                        = true;
1328
1329       lineRun->isSplitToTwoHalves                               = ellipsisLayout.isSplitToTwoHalves;
1330       lineRun->glyphRunSecondHalf.glyphIndex                    = ellipsisLayout.glyphIndexInSecondHalfLine;
1331       lineRun->glyphRunSecondHalf.numberOfGlyphs                = ellipsisLayout.numberOfGlyphsInSecondHalfLine;
1332       lineRun->characterRunForSecondHalfLine.characterIndex     = ellipsisLayout.characterIndexInSecondHalfLine;
1333       lineRun->characterRunForSecondHalfLine.numberOfCharacters = ellipsisLayout.numberOfCharactersInSecondHalfLine;
1334
1335       layoutSize.width = layoutParameters.boundingBox.width;
1336       if(layoutSize.height < Math::MACHINE_EPSILON_1000)
1337       {
1338         layoutSize.height += GetLineHeight(*lineRun, true);
1339       }
1340       else
1341       {
1342         //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
1343         //usually we don't includ it in normal cases using GetLineHeight()
1344         if(lineRun->lineSpacing < 0)
1345         {
1346           layoutSize.height -= lineRun->lineSpacing;
1347         }
1348       }
1349
1350       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1351
1352       if(layoutBidiParameters.isBidirectional)
1353       {
1354         layoutBidiParameters.bidiLineIndex = 0u;
1355         for(Vector<BidirectionalLineInfoRun>::ConstIterator it    = bidirectionalLinesInfo.Begin(),
1356                                                             endIt = bidirectionalLinesInfo.End();
1357             it != endIt;
1358             ++it, ++layoutBidiParameters.bidiLineIndex)
1359         {
1360           const BidirectionalLineInfoRun& run = *it;
1361           //To handle case when the laid characters exist in next line.
1362           //More than one BidirectionalLineInfoRun could start with same character.
1363           //When need to check also numberOfCharacters in line.
1364           //Note: This fixed the incorrect view of extra spaces of RTL as in Arabic then view ellipsis glyph
1365           if(ellipsisLayout.characterIndex == run.characterRun.characterIndex &&
1366              ellipsisLayout.numberOfCharacters == run.characterRun.numberOfCharacters &&
1367              ellipsisLayout.characterIndexInSecondHalfLine == run.characterRunForSecondHalfLine.characterIndex &&
1368              ellipsisLayout.numberOfCharactersInSecondHalfLine == run.characterRunForSecondHalfLine.numberOfCharacters)
1369           {
1370             // Found where to insert the bidi line info.
1371             break;
1372           }
1373         }
1374       }
1375
1376       const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1377
1378       if((nullptr != bidirectionalLineInfo) &&
1379          !bidirectionalLineInfo->isIdentity &&
1380          (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1381       {
1382         lineRun->direction = RTL;
1383         SetGlyphPositions(layoutParameters,
1384                           glyphPositionsBuffer,
1385                           layoutBidiParameters,
1386                           ellipsisLayout);
1387       }
1388       else
1389       {
1390         lineRun->direction = LTR;
1391         SetGlyphPositions(layoutParameters,
1392                           glyphPositionsBuffer,
1393                           ellipsisLayout);
1394       }
1395     }
1396
1397     return ellipsis;
1398   }
1399
1400   /**
1401    * @brief Updates the text layout with a new laid-out line.
1402    *
1403    * @param[in] layoutParameters The parameters needed to layout the text.
1404    * @param[in] layout The line layout.
1405    * @param[in,out] layoutSize The text's layout size.
1406    * @param[in,out] linesBuffer Pointer to the line's buffer.
1407    * @param[in] index Index to the vector of glyphs.
1408    * @param[in,out] numberOfLines The number of laid-out lines.
1409    * @param[in] isLastLine Whether the laid-out line is the last one.
1410    */
1411   void UpdateTextLayout(const Parameters& layoutParameters,
1412                         const LineLayout& layout,
1413                         Size&             layoutSize,
1414                         LineRun*          linesBuffer,
1415                         GlyphIndex        index,
1416                         Length&           numberOfLines,
1417                         bool              isLastLine)
1418   {
1419     LineRun& lineRun = *(linesBuffer + numberOfLines);
1420     ++numberOfLines;
1421
1422     lineRun.glyphRun.glyphIndex             = index;
1423     lineRun.glyphRun.numberOfGlyphs         = layout.numberOfGlyphs;
1424     lineRun.characterRun.characterIndex     = layout.characterIndex;
1425     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
1426     lineRun.width                           = layout.length;
1427     lineRun.extraLength                     = std::ceil(layout.whiteSpaceLengthEndOfLine);
1428
1429     lineRun.isSplitToTwoHalves                               = layout.isSplitToTwoHalves;
1430     lineRun.glyphRunSecondHalf.glyphIndex                    = layout.glyphIndexInSecondHalfLine;
1431     lineRun.glyphRunSecondHalf.numberOfGlyphs                = layout.numberOfGlyphsInSecondHalfLine;
1432     lineRun.characterRunForSecondHalfLine.characterIndex     = layout.characterIndexInSecondHalfLine;
1433     lineRun.characterRunForSecondHalfLine.numberOfCharacters = layout.numberOfCharactersInSecondHalfLine;
1434
1435     // Rounds upward to avoid a non integer size.
1436     lineRun.width = std::ceil(lineRun.width);
1437
1438     lineRun.ascender  = layout.ascender;
1439     lineRun.descender = layout.descender;
1440     lineRun.direction = layout.direction;
1441     lineRun.ellipsis  = false;
1442
1443     lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
1444
1445     // Update the actual size.
1446     if(lineRun.width > layoutSize.width)
1447     {
1448       layoutSize.width = lineRun.width;
1449     }
1450
1451     layoutSize.height += GetLineHeight(lineRun, isLastLine);
1452   }
1453
1454   /**
1455    * @brief Updates the text layout with the last laid-out line.
1456    *
1457    * @param[in] layoutParameters The parameters needed to layout the text.
1458    * @param[in] characterIndex The character index of the line.
1459    * @param[in] glyphIndex The glyph index of the line.
1460    * @param[in,out] layoutSize The text's layout size.
1461    * @param[in,out] linesBuffer Pointer to the line's buffer.
1462    * @param[in,out] numberOfLines The number of laid-out lines.
1463    */
1464   void UpdateTextLayout(const Parameters& layoutParameters,
1465                         CharacterIndex    characterIndex,
1466                         GlyphIndex        glyphIndex,
1467                         Size&             layoutSize,
1468                         LineRun*          linesBuffer,
1469                         Length&           numberOfLines)
1470   {
1471     const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
1472
1473     // Need to add a new line with no characters but with height to increase the layoutSize.height
1474     const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
1475
1476     Text::FontMetrics fontMetrics;
1477     if(0u != glyphInfo.fontId)
1478     {
1479       mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
1480     }
1481
1482     LineRun& lineRun = *(linesBuffer + numberOfLines);
1483     ++numberOfLines;
1484
1485     lineRun.glyphRun.glyphIndex             = glyphIndex;
1486     lineRun.glyphRun.numberOfGlyphs         = 0u;
1487     lineRun.characterRun.characterIndex     = characterIndex;
1488     lineRun.characterRun.numberOfCharacters = 0u;
1489     lineRun.width                           = 0.f;
1490     lineRun.ascender                        = fontMetrics.ascender;
1491     lineRun.descender                       = fontMetrics.descender;
1492     lineRun.extraLength                     = 0.f;
1493     lineRun.alignmentOffset                 = 0.f;
1494     lineRun.direction                       = LTR;
1495     lineRun.ellipsis                        = false;
1496
1497     BoundedParagraphRun currentParagraphRun;
1498     LineLayout          tempLineLayout;
1499     (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
1500
1501     lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
1502
1503     layoutSize.height += GetLineHeight(lineRun, true);
1504   }
1505
1506   /**
1507    * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1508    *
1509    * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1510    * @param[in,out] layoutSize The text's layout size.
1511    */
1512   void UpdateLayoutSize(const Vector<LineRun>& lines,
1513                         Size&                  layoutSize)
1514   {
1515     for(Vector<LineRun>::ConstIterator it    = lines.Begin(),
1516                                        endIt = lines.End();
1517         it != endIt;
1518         ++it)
1519     {
1520       const LineRun& line       = *it;
1521       bool           isLastLine = (it + 1 == endIt);
1522
1523       if(line.width > layoutSize.width)
1524       {
1525         layoutSize.width = line.width;
1526       }
1527
1528       layoutSize.height += GetLineHeight(line, isLastLine);
1529     }
1530   }
1531
1532   /**
1533    * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1534    *
1535    * @param[in] layoutParameters The parameters needed to layout the text.
1536    * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1537    * @param[in] characterOffset The offset to be added to the runs of characters.
1538    * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1539    */
1540   void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1541                               Vector<LineRun>&  lines,
1542                               Length            characterOffset,
1543                               Length            glyphOffset)
1544   {
1545     // Update the glyph and character runs.
1546     for(Vector<LineRun>::Iterator it    = lines.Begin() + layoutParameters.startLineIndex,
1547                                   endIt = lines.End();
1548         it != endIt;
1549         ++it)
1550     {
1551       LineRun& line = *it;
1552
1553       line.glyphRun.glyphIndex         = glyphOffset;
1554       line.characterRun.characterIndex = characterOffset;
1555
1556       glyphOffset += line.glyphRun.numberOfGlyphs;
1557       characterOffset += line.characterRun.numberOfCharacters;
1558     }
1559   }
1560
1561   /**
1562    * @brief Sets the relative line size for the LineLayout
1563    *
1564    * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
1565    * @param[in,out] lineLayout The line layout to be updated.
1566    */
1567   void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
1568   {
1569     lineLayout.relativeLineSize = mRelativeLineSize;
1570
1571     if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
1572     {
1573       lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
1574     }
1575   }
1576
1577   /**
1578    * @brief Get the bounded paragraph for the characterIndex if exists.
1579    *
1580    * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
1581    * @param[in] characterIndex The character index to get bounded paragraph for.
1582    * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
1583    *
1584    * @return returns true if a bounded paragraph was found.
1585    */
1586   bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
1587   {
1588     for(Vector<BoundedParagraphRun>::Iterator it    = boundedParagraphRuns.Begin(),
1589                                               endIt = boundedParagraphRuns.End();
1590         it != endIt;
1591         ++it)
1592     {
1593       BoundedParagraphRun& tempParagraphRun = *it;
1594
1595       if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
1596          characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
1597       {
1598         currentParagraphRun = tempParagraphRun;
1599         return true;
1600       }
1601     }
1602
1603     return false;
1604   }
1605
1606   bool LayoutText(Parameters&                       layoutParameters,
1607                   Size&                             layoutSize,
1608                   bool                              elideTextEnabled,
1609                   bool&                             isAutoScrollEnabled,
1610                   bool                              isAutoScrollMaxTextureExceeded,
1611                   DevelText::EllipsisPosition::Type ellipsisPosition)
1612   {
1613     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1614     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1615
1616     layoutParameters.textModel->mVisualModel->mHyphen.glyph.Clear();
1617     layoutParameters.textModel->mVisualModel->mHyphen.index.Clear();
1618
1619     //Reset indices of ElidedGlyphs
1620     layoutParameters.textModel->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
1621     layoutParameters.textModel->mVisualModel->SetEndIndexOfElidedGlyphs(layoutParameters.textModel->GetNumberOfGlyphs() - 1u);
1622     layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
1623     layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
1624
1625     Vector<LineRun>&                   lines                = layoutParameters.textModel->mVisualModel->mLines;
1626     const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
1627
1628     if(0u == layoutParameters.numberOfGlyphs)
1629     {
1630       // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1631       if(layoutParameters.isLastNewParagraph)
1632       {
1633         Length numberOfLines = lines.Count();
1634         if(0u != numberOfLines)
1635         {
1636           const LineRun& lastLine = *(lines.End() - 1u);
1637
1638           if(0u != lastLine.characterRun.numberOfCharacters)
1639           {
1640             // Need to add a new line with no characters but with height to increase the layoutSize.height
1641             LineRun newLine;
1642             Initialize(newLine);
1643             lines.PushBack(newLine);
1644
1645             UpdateTextLayout(layoutParameters,
1646                              lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1647                              lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1648                              layoutSize,
1649                              lines.Begin(),
1650                              numberOfLines);
1651           }
1652         }
1653       }
1654
1655       // Calculates the layout size.
1656       UpdateLayoutSize(lines,
1657                        layoutSize);
1658
1659       // Rounds upward to avoid a non integer size.
1660       layoutSize.height = std::ceil(layoutSize.height);
1661
1662       // Nothing else do if there are no glyphs to layout.
1663       return false;
1664     }
1665
1666     const GlyphIndex lastGlyphPlusOne    = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1667     const Length     totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1668     Vector<Vector2>& glyphPositions      = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1669
1670     // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1671     // This extra line needs to be removed.
1672     if(0u != lines.Count())
1673     {
1674       Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1675
1676       if((0u == lastLine->characterRun.numberOfCharacters) &&
1677          (lastGlyphPlusOne == totalNumberOfGlyphs))
1678       {
1679         lines.Remove(lastLine);
1680       }
1681     }
1682
1683     // Retrieve BiDi info.
1684     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1685
1686     const CharacterIndex* const                  glyphsToCharactersBuffer    = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
1687     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1688     const Vector<BidirectionalLineInfoRun>&      bidirectionalLinesInfo      = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1689
1690     // Set the layout bidirectional paramters.
1691     LayoutBidiParameters layoutBidiParameters;
1692
1693     // Whether the layout is being updated or set from scratch.
1694     const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1695
1696     Vector2*        glyphPositionsBuffer = nullptr;
1697     Vector<Vector2> newGlyphPositions;
1698
1699     LineRun*        linesBuffer = nullptr;
1700     Vector<LineRun> newLines;
1701
1702     // Estimate the number of lines.
1703     Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1704     Length numberOfLines = 0u;
1705
1706     if(updateCurrentBuffer)
1707     {
1708       newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1709       glyphPositionsBuffer = newGlyphPositions.Begin();
1710
1711       newLines.Resize(linesCapacity);
1712       linesBuffer = newLines.Begin();
1713     }
1714     else
1715     {
1716       glyphPositionsBuffer = glyphPositions.Begin();
1717
1718       lines.Resize(linesCapacity);
1719       linesBuffer = lines.Begin();
1720     }
1721
1722     float penY            = CalculateLineOffset(lines,
1723                                      layoutParameters.startLineIndex);
1724     bool  anyLineIsEliped = false;
1725     for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1726     {
1727       layoutBidiParameters.Clear();
1728
1729       if(hasBidiParagraphs)
1730       {
1731         const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1732
1733         for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalParagraphsInfo.Begin(),
1734                                                                  endIt = bidirectionalParagraphsInfo.End();
1735             it != endIt;
1736             ++it, ++layoutBidiParameters.bidiParagraphIndex)
1737         {
1738           const BidirectionalParagraphInfoRun& run = *it;
1739
1740           const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1741
1742           if(lastCharacterIndex <= startCharacterIndex)
1743           {
1744             // Do not process, the paragraph has already been processed.
1745             continue;
1746           }
1747
1748           if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1749           {
1750             layoutBidiParameters.paragraphDirection = run.direction;
1751             layoutBidiParameters.isBidirectional    = true;
1752           }
1753
1754           // Has already been found.
1755           break;
1756         }
1757
1758         if(layoutBidiParameters.isBidirectional)
1759         {
1760           for(Vector<BidirectionalLineInfoRun>::ConstIterator it    = bidirectionalLinesInfo.Begin(),
1761                                                               endIt = bidirectionalLinesInfo.End();
1762               it != endIt;
1763               ++it, ++layoutBidiParameters.bidiLineIndex)
1764           {
1765             const BidirectionalLineInfoRun& run = *it;
1766
1767             const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1768
1769             if(lastCharacterIndex <= startCharacterIndex)
1770             {
1771               // skip
1772               continue;
1773             }
1774
1775             if(startCharacterIndex < lastCharacterIndex)
1776             {
1777               // Found where to insert the bidi line info.
1778               break;
1779             }
1780           }
1781         }
1782       }
1783
1784       CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1785
1786       // Get the layout for the line.
1787       LineLayout layout;
1788       layout.direction  = layoutBidiParameters.paragraphDirection;
1789       layout.glyphIndex = index;
1790
1791       BoundedParagraphRun currentParagraphRun;
1792       (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
1793
1794       GetLineLayoutForBox(layoutParameters,
1795                           layoutBidiParameters,
1796                           layout,
1797                           false,
1798                           ellipsisPosition,
1799                           false,
1800                           elideTextEnabled);
1801
1802       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex);
1803       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex);
1804       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs);
1805       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters);
1806       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                length %f\n", layout.length);
1807
1808       CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
1809
1810       //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)
1811       if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex + layout.numberOfCharacters)
1812       {
1813         layout.relativeLineSize = mRelativeLineSize;
1814       }
1815
1816       if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
1817       {
1818         // The width is too small and no characters are laid-out.
1819         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1820
1821         lines.Resize(numberOfLines);
1822
1823         // Rounds upward to avoid a non integer size.
1824         layoutSize.height = std::ceil(layoutSize.height);
1825
1826         return false;
1827       }
1828
1829       // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1830       // of the box.
1831       penY += layout.ascender;
1832
1833       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  pen y %f\n", penY);
1834
1835       bool ellipsis = false;
1836       if(elideTextEnabled)
1837       {
1838         layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1839
1840         // Does the ellipsis of the last line.
1841         ellipsis = EllipsisLine(layoutParameters,
1842                                 layoutBidiParameters,
1843                                 layout,
1844                                 layoutSize,
1845                                 linesBuffer,
1846                                 glyphPositionsBuffer,
1847                                 numberOfLines,
1848                                 penY,
1849                                 isAutoScrollEnabled,
1850                                 isAutoScrollMaxTextureExceeded,
1851                                 ellipsisPosition,
1852                                 false);
1853       }
1854
1855       if(ellipsis && ((ellipsisPosition == DevelText::EllipsisPosition::END) || (numberOfLines == 1u)))
1856       {
1857         const bool isMultiline = mLayout == MULTI_LINE_BOX;
1858         if(isMultiline && ellipsisPosition != DevelText::EllipsisPosition::END)
1859         {
1860           ellipsis = EllipsisLine(layoutParameters,
1861                                   layoutBidiParameters,
1862                                   layout,
1863                                   layoutSize,
1864                                   linesBuffer,
1865                                   glyphPositionsBuffer,
1866                                   numberOfLines,
1867                                   penY,
1868                                   isAutoScrollEnabled,
1869                                   isAutoScrollMaxTextureExceeded,
1870                                   ellipsisPosition,
1871                                   true);
1872         }
1873
1874         //clear hyphen from ellipsis line
1875         const Length* hyphenIndices = layoutParameters.textModel->mVisualModel->mHyphen.index.Begin();
1876         Length        hyphensCount  = layoutParameters.textModel->mVisualModel->mHyphen.glyph.Size();
1877
1878         while(hyphenIndices && hyphensCount > 0 && hyphenIndices[hyphensCount - 1] >= layout.glyphIndex)
1879         {
1880           layoutParameters.textModel->mVisualModel->mHyphen.index.Remove(layoutParameters.textModel->mVisualModel->mHyphen.index.Begin() + hyphensCount - 1);
1881           layoutParameters.textModel->mVisualModel->mHyphen.glyph.Remove(layoutParameters.textModel->mVisualModel->mHyphen.glyph.Begin() + hyphensCount - 1);
1882           hyphensCount--;
1883         }
1884
1885         // No more lines to layout.
1886         break;
1887       }
1888       else
1889       {
1890         //In START location of ellipsis whether to shift lines or not.
1891         anyLineIsEliped |= ellipsis;
1892
1893         // Whether the last line has been laid-out.
1894         const bool isLastLine = index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine) == totalNumberOfGlyphs;
1895
1896         if(numberOfLines == linesCapacity)
1897         {
1898           // Reserve more space for the next lines.
1899           linesBuffer = ResizeLinesBuffer(lines,
1900                                           newLines,
1901                                           linesCapacity,
1902                                           updateCurrentBuffer);
1903         }
1904
1905         // Updates the current text's layout with the line's layout.
1906         UpdateTextLayout(layoutParameters,
1907                          layout,
1908                          layoutSize,
1909                          linesBuffer,
1910                          index,
1911                          numberOfLines,
1912                          isLastLine);
1913
1914         const GlyphIndex nextIndex = index + layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine;
1915
1916         if((nextIndex == totalNumberOfGlyphs) &&
1917            layoutParameters.isLastNewParagraph &&
1918            (mLayout == MULTI_LINE_BOX))
1919         {
1920           // The last character of the text is a new paragraph character.
1921           // An extra line with no characters is added to increase the text's height
1922           // in order to place the cursor.
1923
1924           if(numberOfLines == linesCapacity)
1925           {
1926             // Reserve more space for the next lines.
1927             linesBuffer = ResizeLinesBuffer(lines,
1928                                             newLines,
1929                                             linesCapacity,
1930                                             updateCurrentBuffer);
1931           }
1932
1933           UpdateTextLayout(layoutParameters,
1934                            layout.characterIndex + (layout.numberOfCharacters + layout.numberOfCharactersInSecondHalfLine),
1935                            index + (layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine),
1936                            layoutSize,
1937                            linesBuffer,
1938                            numberOfLines);
1939         } // whether to add a last line.
1940
1941         const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1942
1943         if((nullptr != bidirectionalLineInfo) &&
1944            !bidirectionalLineInfo->isIdentity &&
1945            (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1946         {
1947           SetGlyphPositions(layoutParameters,
1948                             glyphPositionsBuffer,
1949                             layoutBidiParameters,
1950                             layout);
1951         }
1952         else
1953         {
1954           // Sets the positions of the glyphs.
1955           SetGlyphPositions(layoutParameters,
1956                             glyphPositionsBuffer,
1957                             layout);
1958         }
1959
1960         // Updates the vertical pen's position.
1961         penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
1962
1963         // Increase the glyph index.
1964         index = nextIndex;
1965       } // no ellipsis
1966     }   // end for() traversing glyphs.
1967
1968     //Shift lines to up if ellipsis and multilines and set ellipsis of first line to true
1969     if(anyLineIsEliped && numberOfLines > 1u)
1970     {
1971       if(ellipsisPosition == DevelText::EllipsisPosition::START)
1972       {
1973         Length lineIndex = 0;
1974         while(lineIndex < numberOfLines && layoutParameters.boundingBox.height < layoutSize.height)
1975         {
1976           LineRun& delLine = linesBuffer[lineIndex];
1977           delLine.ellipsis = true;
1978
1979           layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1980           for(Length lineIndex = 0; lineIndex < numberOfLines - 1; lineIndex++)
1981           {
1982             linesBuffer[lineIndex]          = linesBuffer[lineIndex + 1];
1983             linesBuffer[lineIndex].ellipsis = false;
1984           }
1985           numberOfLines--;
1986         }
1987         linesBuffer[0u].ellipsis = true;
1988       }
1989       else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
1990       {
1991         Length middleLineIndex   = (numberOfLines) / 2u;
1992         Length ellipsisLineIndex = 0u;
1993         while(1u < numberOfLines && 0u < middleLineIndex && layoutParameters.boundingBox.height < layoutSize.height)
1994         {
1995           LineRun& delLine = linesBuffer[middleLineIndex];
1996           delLine.ellipsis = true;
1997
1998           layoutSize.height -= (delLine.ascender + -delLine.descender) + delLine.lineSpacing;
1999           for(Length lineIndex = middleLineIndex; lineIndex < numberOfLines - 1; lineIndex++)
2000           {
2001             linesBuffer[lineIndex]          = linesBuffer[lineIndex + 1];
2002             linesBuffer[lineIndex].ellipsis = false;
2003           }
2004           numberOfLines--;
2005           ellipsisLineIndex = middleLineIndex - 1u;
2006           middleLineIndex   = (numberOfLines) / 2u;
2007         }
2008
2009         linesBuffer[ellipsisLineIndex].ellipsis = true;
2010       }
2011     }
2012
2013     if(updateCurrentBuffer)
2014     {
2015       glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
2016                             newGlyphPositions.Begin(),
2017                             newGlyphPositions.End());
2018       glyphPositions.Resize(totalNumberOfGlyphs);
2019
2020       newLines.Resize(numberOfLines);
2021
2022       // Current text's layout size adds only the newly laid-out lines.
2023       // Updates the layout size with the previously laid-out lines.
2024       UpdateLayoutSize(lines,
2025                        layoutSize);
2026
2027       if(0u != newLines.Count())
2028       {
2029         const LineRun& lastLine = *(newLines.End() - 1u);
2030
2031         const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
2032         const Length glyphOffset     = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
2033
2034         // Update the indices of the runs before the new laid-out lines are inserted.
2035         UpdateLineIndexOffsets(layoutParameters,
2036                                lines,
2037                                characterOffset,
2038                                glyphOffset);
2039
2040         // Insert the lines.
2041         lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
2042                      newLines.Begin(),
2043                      newLines.End());
2044       }
2045     }
2046     else
2047     {
2048       lines.Resize(numberOfLines);
2049     }
2050
2051     // Rounds upward to avoid a non integer size.
2052     layoutSize.height = std::ceil(layoutSize.height);
2053
2054     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
2055
2056     return true;
2057   }
2058
2059   void Align(const Size&                     size,
2060              CharacterIndex                  startIndex,
2061              Length                          numberOfCharacters,
2062              Text::HorizontalAlignment::Type horizontalAlignment,
2063              Vector<LineRun>&                lines,
2064              float&                          alignmentOffset,
2065              Dali::LayoutDirection::Type     layoutDirection,
2066              bool                            matchLayoutDirection)
2067   {
2068     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
2069
2070     alignmentOffset = MAX_FLOAT;
2071     // Traverse all lines and align the glyphs.
2072     for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
2073         it != endIt;
2074         ++it)
2075     {
2076       LineRun& line = *it;
2077
2078       if(line.characterRun.characterIndex < startIndex)
2079       {
2080         // Do not align lines which have already been aligned.
2081         continue;
2082       }
2083
2084       if(line.characterRun.characterIndex > lastCharacterPlusOne)
2085       {
2086         // Do not align lines beyond the last laid-out character.
2087         break;
2088       }
2089
2090       if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
2091       {
2092         // Do not align lines beyond the last laid-out character unless the line is last and empty.
2093         break;
2094       }
2095
2096       // Calculate the line's alignment offset accordingly with the align option,
2097       // the box width, line length, and the paragraph's direction.
2098       CalculateHorizontalAlignment(size.width,
2099                                    horizontalAlignment,
2100                                    line,
2101                                    layoutDirection,
2102                                    matchLayoutDirection);
2103
2104       // Updates the alignment offset.
2105       alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
2106     }
2107   }
2108
2109   void CalculateHorizontalAlignment(float                       boxWidth,
2110                                     HorizontalAlignment::Type   horizontalAlignment,
2111                                     LineRun&                    line,
2112                                     Dali::LayoutDirection::Type layoutDirection,
2113                                     bool                        matchLayoutDirection)
2114   {
2115     line.alignmentOffset = 0.f;
2116     const bool isLineRTL = RTL == line.direction;
2117
2118     // Whether to swap the alignment.
2119     // 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.
2120     bool  isLayoutRTL = isLineRTL;
2121     float lineLength  = line.width;
2122
2123     // match align for system language direction
2124     if(matchLayoutDirection)
2125     {
2126       // Swap the alignment type if the line is right to left.
2127       isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
2128     }
2129     // Calculate the horizontal line offset.
2130     switch(horizontalAlignment)
2131     {
2132       case HorizontalAlignment::BEGIN:
2133       {
2134         if(isLayoutRTL)
2135         {
2136           if(isLineRTL)
2137           {
2138             lineLength += line.extraLength;
2139           }
2140
2141           line.alignmentOffset = boxWidth - lineLength;
2142         }
2143         else
2144         {
2145           line.alignmentOffset = 0.f;
2146
2147           if(isLineRTL)
2148           {
2149             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2150             line.alignmentOffset -= line.extraLength;
2151           }
2152         }
2153         break;
2154       }
2155       case HorizontalAlignment::CENTER:
2156       {
2157         line.alignmentOffset = 0.5f * (boxWidth - lineLength);
2158
2159         if(isLineRTL)
2160         {
2161           line.alignmentOffset -= line.extraLength;
2162         }
2163
2164         line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
2165         break;
2166       }
2167       case HorizontalAlignment::END:
2168       {
2169         if(isLayoutRTL)
2170         {
2171           line.alignmentOffset = 0.f;
2172
2173           if(isLineRTL)
2174           {
2175             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
2176             line.alignmentOffset -= line.extraLength;
2177           }
2178         }
2179         else
2180         {
2181           if(isLineRTL)
2182           {
2183             lineLength += line.extraLength;
2184           }
2185
2186           line.alignmentOffset = boxWidth - lineLength;
2187         }
2188         break;
2189       }
2190     }
2191   }
2192
2193   void Initialize(LineRun& line)
2194   {
2195     line.glyphRun.glyphIndex                              = 0u;
2196     line.glyphRun.numberOfGlyphs                          = 0u;
2197     line.characterRun.characterIndex                      = 0u;
2198     line.characterRun.numberOfCharacters                  = 0u;
2199     line.width                                            = 0.f;
2200     line.ascender                                         = 0.f;
2201     line.descender                                        = 0.f;
2202     line.extraLength                                      = 0.f;
2203     line.alignmentOffset                                  = 0.f;
2204     line.direction                                        = LTR;
2205     line.ellipsis                                         = false;
2206     line.lineSpacing                                      = mDefaultLineSpacing;
2207     line.isSplitToTwoHalves                               = false;
2208     line.glyphRunSecondHalf.glyphIndex                    = 0u;
2209     line.glyphRunSecondHalf.numberOfGlyphs                = 0u;
2210     line.characterRunForSecondHalfLine.characterIndex     = 0u;
2211     line.characterRunForSecondHalfLine.numberOfCharacters = 0u;
2212   }
2213
2214   Type  mLayout;
2215   float mCursorWidth;
2216   float mDefaultLineSpacing;
2217   float mDefaultLineSize;
2218
2219   IntrusivePtr<Metrics> mMetrics;
2220   float                 mRelativeLineSize;
2221 };
2222
2223 Engine::Engine()
2224 : mImpl{nullptr}
2225 {
2226   mImpl = new Engine::Impl();
2227 }
2228
2229 Engine::~Engine()
2230 {
2231   delete mImpl;
2232 }
2233
2234 void Engine::SetMetrics(MetricsPtr& metrics)
2235 {
2236   mImpl->mMetrics = metrics;
2237 }
2238
2239 void Engine::SetLayout(Type layout)
2240 {
2241   mImpl->mLayout = layout;
2242 }
2243
2244 Engine::Type Engine::GetLayout() const
2245 {
2246   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
2247   return mImpl->mLayout;
2248 }
2249
2250 void Engine::SetCursorWidth(int width)
2251 {
2252   mImpl->mCursorWidth = static_cast<float>(width);
2253 }
2254
2255 int Engine::GetCursorWidth() const
2256 {
2257   return static_cast<int>(mImpl->mCursorWidth);
2258 }
2259
2260 bool Engine::LayoutText(Parameters&                       layoutParameters,
2261                         Size&                             layoutSize,
2262                         bool                              elideTextEnabled,
2263                         bool&                             isAutoScrollEnabled,
2264                         bool                              isAutoScrollMaxTextureExceeded,
2265                         DevelText::EllipsisPosition::Type ellipsisPosition)
2266 {
2267   return mImpl->LayoutText(layoutParameters,
2268                            layoutSize,
2269                            elideTextEnabled,
2270                            isAutoScrollEnabled,
2271                            isAutoScrollMaxTextureExceeded,
2272                            ellipsisPosition);
2273 }
2274
2275 void Engine::Align(const Size&                     size,
2276                    CharacterIndex                  startIndex,
2277                    Length                          numberOfCharacters,
2278                    Text::HorizontalAlignment::Type horizontalAlignment,
2279                    Vector<LineRun>&                lines,
2280                    float&                          alignmentOffset,
2281                    Dali::LayoutDirection::Type     layoutDirection,
2282                    bool                            matchLayoutDirection)
2283 {
2284   mImpl->Align(size,
2285                startIndex,
2286                numberOfCharacters,
2287                horizontalAlignment,
2288                lines,
2289                alignmentOffset,
2290                layoutDirection,
2291                matchLayoutDirection);
2292 }
2293
2294 void Engine::SetDefaultLineSpacing(float lineSpacing)
2295 {
2296   mImpl->mDefaultLineSpacing = lineSpacing;
2297 }
2298
2299 float Engine::GetDefaultLineSpacing() const
2300 {
2301   return mImpl->mDefaultLineSpacing;
2302 }
2303
2304 void Engine::SetDefaultLineSize(float lineSize)
2305 {
2306   mImpl->mDefaultLineSize = lineSize;
2307 }
2308
2309 float Engine::GetDefaultLineSize() const
2310 {
2311   return mImpl->mDefaultLineSize;
2312 }
2313
2314 void Engine::SetRelativeLineSize(float relativeLineSize)
2315 {
2316   mImpl->mRelativeLineSize = relativeLineSize;
2317 }
2318
2319 float Engine::GetRelativeLineSize() const
2320 {
2321   return mImpl->mRelativeLineSize;
2322 }
2323
2324 } // namespace Layout
2325
2326 } // namespace Text
2327
2328 } // namespace Toolkit
2329
2330 } // namespace Dali