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