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