Updated all cpp files to new format
[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
53 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
54 {
55   return ((*line).characterRun.numberOfCharacters == 0 && line + 1u == lines.End());
56 }
57
58 } //namespace
59
60 /**
61  * @brief Stores temporary layout info of the line.
62  */
63 struct LineLayout
64 {
65   LineLayout()
66   : glyphIndex{0u},
67     characterIndex{0u},
68     numberOfGlyphs{0u},
69     numberOfCharacters{0u},
70     ascender{-MAX_FLOAT},
71     descender{MAX_FLOAT},
72     lineSpacing{0.f},
73     penX{0.f},
74     previousAdvance{0.f},
75     length{0.f},
76     whiteSpaceLengthEndOfLine{0.f},
77     direction{LTR}
78   {
79   }
80
81   ~LineLayout()
82   {
83   }
84
85   void Clear()
86   {
87     glyphIndex         = 0u;
88     characterIndex     = 0u;
89     numberOfGlyphs     = 0u;
90     numberOfCharacters = 0u;
91     ascender           = -MAX_FLOAT;
92     descender          = MAX_FLOAT;
93     direction          = LTR;
94   }
95
96   GlyphIndex         glyphIndex;                ///< Index of the first glyph to be laid-out.
97   CharacterIndex     characterIndex;            ///< Index of the first character to be laid-out.
98   Length             numberOfGlyphs;            ///< The number of glyph which fit in one line.
99   Length             numberOfCharacters;        ///< The number of characters which fit in one line.
100   float              ascender;                  ///< The maximum ascender of all fonts in the line.
101   float              descender;                 ///< The minimum descender of all fonts in the line.
102   float              lineSpacing;               ///< The line spacing
103   float              penX;                      ///< The origin of the current glyph ( is the start point plus the accumulation of all advances ).
104   float              previousAdvance;           ///< The advance of the previous glyph.
105   float              length;                    ///< The current length of the line.
106   float              whiteSpaceLengthEndOfLine; ///< The length of the white spaces at the end of the line.
107   CharacterDirection direction;
108 };
109
110 struct LayoutBidiParameters
111 {
112   void Clear()
113   {
114     paragraphDirection = LTR;
115     bidiParagraphIndex = 0u;
116     bidiLineIndex      = 0u;
117     isBidirectional    = false;
118   }
119
120   CharacterDirection        paragraphDirection = LTR;   ///< The paragraph's direction.
121   BidirectionalRunIndex     bidiParagraphIndex = 0u;    ///< Index to the paragraph's bidi info.
122   BidirectionalLineRunIndex bidiLineIndex      = 0u;    ///< Index where to insert the next bidi line info.
123   bool                      isBidirectional    = false; ///< Whether the text is bidirectional.
124 };
125
126 struct Engine::Impl
127 {
128   Impl()
129   : mLayout{Layout::Engine::SINGLE_LINE_BOX},
130     mCursorWidth{0.f},
131     mDefaultLineSpacing{LINE_SPACING},
132     mDefaultLineSize{MIN_LINE_SIZE}
133   {
134   }
135
136   /**
137    * @brief Updates the line ascender and descender with the metrics of a new font.
138    *
139    * @param[in] glyphMetrics The metrics of the new font.
140    * @param[in,out] lineLayout The line layout.
141    */
142   void UpdateLineHeight(const GlyphMetrics& glyphMetrics, LineLayout& lineLayout)
143   {
144     Text::FontMetrics fontMetrics;
145     if(0u != glyphMetrics.fontId)
146     {
147       mMetrics->GetFontMetrics(glyphMetrics.fontId, fontMetrics);
148     }
149     else
150     {
151       fontMetrics.ascender           = glyphMetrics.fontHeight;
152       fontMetrics.descender          = 0.f;
153       fontMetrics.height             = fontMetrics.ascender;
154       fontMetrics.underlinePosition  = 0.f;
155       fontMetrics.underlineThickness = 1.f;
156     }
157
158     // Sets the maximum ascender.
159     lineLayout.ascender = std::max(lineLayout.ascender, fontMetrics.ascender);
160
161     // Sets the minimum descender.
162     lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
163
164     // Sets the line size
165     lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
166     lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
167
168     // Add the line spacing
169     lineLayout.lineSpacing += mDefaultLineSpacing;
170   }
171
172   /**
173    * @brief Merges a temporary line layout into the line layout.
174    *
175    * @param[in,out] lineLayout The line layout.
176    * @param[in] tmpLineLayout A temporary line layout.
177    */
178   void MergeLineLayout(LineLayout&       lineLayout,
179                        const LineLayout& tmpLineLayout)
180   {
181     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
182     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
183
184     lineLayout.penX            = tmpLineLayout.penX;
185     lineLayout.previousAdvance = tmpLineLayout.previousAdvance;
186
187     lineLayout.length                    = tmpLineLayout.length;
188     lineLayout.whiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
189
190     // Sets the maximum ascender.
191     lineLayout.ascender = std::max(lineLayout.ascender, tmpLineLayout.ascender);
192
193     // Sets the minimum descender.
194     lineLayout.descender = std::min(lineLayout.descender, tmpLineLayout.descender);
195   }
196
197   void LayoutRightToLeft(const Parameters&               parameters,
198                          const BidirectionalLineInfoRun& bidirectionalLineInfo,
199                          float&                          length,
200                          float&                          whiteSpaceLengthEndOfLine)
201   {
202     const Character* const  textBuffer               = parameters.textModel->mLogicalModel->mText.Begin();
203     const Length* const     charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
204     const GlyphInfo* const  glyphsBuffer             = parameters.textModel->mVisualModel->mGlyphs.Begin();
205     const GlyphIndex* const charactersToGlyphsBuffer = parameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
206
207     const float      outlineWidth                = static_cast<float>(parameters.textModel->GetOutlineWidth());
208     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
209
210     CharacterIndex characterLogicalIndex = 0u;
211     CharacterIndex characterVisualIndex  = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
212
213     if(RTL == bidirectionalLineInfo.direction)
214     {
215       while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
216       {
217         const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
218
219         whiteSpaceLengthEndOfLine += glyphInfo.advance;
220
221         ++characterLogicalIndex;
222         characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
223       }
224     }
225
226     const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
227
228     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
229     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
230                                                                   lastGlyphOfParagraphPlusOne,
231                                                                   charactersPerGlyphBuffer);
232
233     GlyphMetrics glyphMetrics;
234     GetGlyphsMetrics(glyphIndex,
235                      numberOfGLyphsInGroup,
236                      glyphMetrics,
237                      glyphsBuffer,
238                      mMetrics);
239
240     float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
241
242     // Traverses the characters of the right to left paragraph.
243     for(; characterLogicalIndex < bidirectionalLineInfo.characterRun.numberOfCharacters;)
244     {
245       // Convert the character in the logical order into the character in the visual order.
246       const CharacterIndex characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
247       const bool           isWhiteSpace         = TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex));
248
249       const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
250
251       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
252       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
253                                                                     lastGlyphOfParagraphPlusOne,
254                                                                     charactersPerGlyphBuffer);
255
256       characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
257
258       GlyphMetrics glyphMetrics;
259       GetGlyphsMetrics(glyphIndex,
260                        numberOfGLyphsInGroup,
261                        glyphMetrics,
262                        glyphsBuffer,
263                        mMetrics);
264
265       if(isWhiteSpace)
266       {
267         if(RTL == bidirectionalLineInfo.direction)
268         {
269           length += glyphMetrics.advance;
270         }
271         else
272         {
273           whiteSpaceLengthEndOfLine += glyphMetrics.advance;
274         }
275         penX += glyphMetrics.advance;
276       }
277       else
278       {
279         if(LTR == bidirectionalLineInfo.direction)
280         {
281           whiteSpaceLengthEndOfLine = 0.f;
282         }
283         length = std::max(length, penX + glyphMetrics.xBearing + glyphMetrics.width);
284         penX += (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
285       }
286     }
287   }
288
289   void ReorderBiDiLayout(const Parameters&     parameters,
290                          LayoutBidiParameters& bidiParameters,
291                          const LineLayout&     currentLineLayout,
292                          LineLayout&           lineLayout,
293                          bool                  breakInCharacters)
294   {
295     const Length* const charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
296
297     // The last glyph to be laid-out.
298     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
299
300     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = parameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
301
302     const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo = bidirectionalParagraphsInfo[bidiParameters.bidiParagraphIndex];
303     if((lineLayout.characterIndex >= bidirectionalParagraphInfo.characterRun.characterIndex) &&
304        (lineLayout.characterIndex < bidirectionalParagraphInfo.characterRun.characterIndex + bidirectionalParagraphInfo.characterRun.numberOfCharacters))
305     {
306       Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
307
308       // Sets the visual to logical map tables needed to reorder the text.
309       ReorderLine(bidirectionalParagraphInfo,
310                   bidirectionalLinesInfo,
311                   bidiParameters.bidiLineIndex,
312                   lineLayout.characterIndex,
313                   lineLayout.numberOfCharacters,
314                   bidiParameters.paragraphDirection);
315
316       // Recalculate the length of the line and update the layout.
317       const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
318
319       if(!bidirectionalLineInfo.isIdentity)
320       {
321         float length                    = 0.f;
322         float whiteSpaceLengthEndOfLine = 0.f;
323         LayoutRightToLeft(parameters,
324                           bidirectionalLineInfo,
325                           length,
326                           whiteSpaceLengthEndOfLine);
327
328         lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
329         if(!Equals(length, lineLayout.length))
330         {
331           const bool isMultiline = mLayout == MULTI_LINE_BOX;
332
333           if(isMultiline && (length > parameters.boundingBox.width))
334           {
335             if(breakInCharacters || (isMultiline && (0u == currentLineLayout.numberOfGlyphs)))
336             {
337               // The word doesn't fit in one line. It has to be split by character.
338
339               // Remove the last laid out glyph(s) as they doesn't fit.
340               for(GlyphIndex glyphIndex = lineLayout.glyphIndex + lineLayout.numberOfGlyphs - 1u; glyphIndex >= lineLayout.glyphIndex;)
341               {
342                 const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
343                                                                               lastGlyphOfParagraphPlusOne,
344                                                                               charactersPerGlyphBuffer);
345
346                 const Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
347
348                 lineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
349                 lineLayout.numberOfCharacters -= numberOfCharacters;
350
351                 AdjustLayout(parameters,
352                              bidiParameters,
353                              bidirectionalParagraphInfo,
354                              lineLayout);
355
356                 if(lineLayout.length < parameters.boundingBox.width)
357                 {
358                   break;
359                 }
360
361                 if(glyphIndex < numberOfGLyphsInGroup)
362                 {
363                   // avoids go under zero for an unsigned int.
364                   break;
365                 }
366
367                 glyphIndex -= numberOfGLyphsInGroup;
368               }
369             }
370             else
371             {
372               lineLayout = currentLineLayout;
373
374               AdjustLayout(parameters,
375                            bidiParameters,
376                            bidirectionalParagraphInfo,
377                            lineLayout);
378             }
379           }
380           else
381           {
382             lineLayout.length = std::max(length, lineLayout.length);
383           }
384         }
385       }
386     }
387   }
388
389   void AdjustLayout(const Parameters&                    parameters,
390                     LayoutBidiParameters&                bidiParameters,
391                     const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
392                     LineLayout&                          lineLayout)
393   {
394     Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = parameters.textModel->mLogicalModel->mBidirectionalLineInfo;
395
396     // Remove current reordered line.
397     bidirectionalLinesInfo.Erase(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
398
399     // Re-build the conversion table without the removed glyphs.
400     ReorderLine(bidirectionalParagraphInfo,
401                 bidirectionalLinesInfo,
402                 bidiParameters.bidiLineIndex,
403                 lineLayout.characterIndex,
404                 lineLayout.numberOfCharacters,
405                 bidiParameters.paragraphDirection);
406
407     const BidirectionalLineInfoRun& bidirectionalLineInfo = *(bidirectionalLinesInfo.Begin() + bidiParameters.bidiLineIndex);
408
409     float length                    = 0.f;
410     float whiteSpaceLengthEndOfLine = 0.f;
411     LayoutRightToLeft(parameters,
412                       bidirectionalLineInfo,
413                       length,
414                       whiteSpaceLengthEndOfLine);
415
416     lineLayout.length                    = length;
417     lineLayout.whiteSpaceLengthEndOfLine = whiteSpaceLengthEndOfLine;
418   }
419
420   /**
421    * Retrieves the line layout for a given box width.
422    *
423    * @note This method starts to layout text as if it was left to right. However, it might be differences in the length
424    *       of the line if it's a bidirectional one. If the paragraph is bidirectional, this method will call a function
425    *       to reorder the line and recalculate its length.
426    *
427
428    * @param[in] parameters The layout parameters.
429    * @param[] bidiParameters Bidirectional info for the current line.
430    * @param[out] lineLayout The line layout.
431    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
432    */
433   void GetLineLayoutForBox(const Parameters&     parameters,
434                            LayoutBidiParameters& bidiParameters,
435                            LineLayout&           lineLayout,
436                            bool                  completelyFill)
437   {
438     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n");
439     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex);
440
441     const Character* const      textBuffer               = parameters.textModel->mLogicalModel->mText.Begin();
442     const Length* const         charactersPerGlyphBuffer = parameters.textModel->mVisualModel->mCharactersPerGlyph.Begin();
443     const GlyphInfo* const      glyphsBuffer             = parameters.textModel->mVisualModel->mGlyphs.Begin();
444     const CharacterIndex* const glyphsToCharactersBuffer = parameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
445     const LineBreakInfo* const  lineBreakInfoBuffer      = parameters.textModel->mLogicalModel->mLineBreakInfo.Begin();
446
447     const float  outlineWidth        = static_cast<float>(parameters.textModel->GetOutlineWidth());
448     const Length totalNumberOfGlyphs = parameters.textModel->mVisualModel->mGlyphs.Count();
449
450     const bool isMultiline   = mLayout == MULTI_LINE_BOX;
451     const bool isWordLaidOut = parameters.textModel->mLineWrapMode == Text::LineWrap::WORD;
452
453     // The last glyph to be laid-out.
454     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
455
456     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
457     // In the case the line starts with a right to left character, if the width is longer than the advance,
458     // the difference needs to be added to the line length.
459
460     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
461     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(lineLayout.glyphIndex,
462                                                                   lastGlyphOfParagraphPlusOne,
463                                                                   charactersPerGlyphBuffer);
464
465     GlyphMetrics glyphMetrics;
466     GetGlyphsMetrics(lineLayout.glyphIndex,
467                      numberOfGLyphsInGroup,
468                      glyphMetrics,
469                      glyphsBuffer,
470                      mMetrics);
471
472     // Set the direction of the first character of the line.
473     lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
474
475     // Stores temporary line layout which has not been added to the final line layout.
476     LineLayout tmpLineLayout;
477
478     // Initialize the start point.
479
480     // The initial start point is zero. However it needs a correction according the 'x' bearing of the first glyph.
481     // i.e. if the bearing of the first glyph is negative it may exceed the boundaries of the text area.
482     // 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.
483     tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
484
485     // Calculate the line height if there is no characters.
486     FontId lastFontId = glyphMetrics.fontId;
487     UpdateLineHeight(glyphMetrics, tmpLineLayout);
488
489     bool oneWordLaidOut = false;
490
491     for(GlyphIndex glyphIndex = lineLayout.glyphIndex;
492         glyphIndex < lastGlyphOfParagraphPlusOne;)
493     {
494       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex);
495
496       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
497       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndex,
498                                                                     lastGlyphOfParagraphPlusOne,
499                                                                     charactersPerGlyphBuffer);
500
501       GlyphMetrics glyphMetrics;
502       GetGlyphsMetrics(glyphIndex,
503                        numberOfGLyphsInGroup,
504                        glyphMetrics,
505                        glyphsBuffer,
506                        mMetrics);
507
508       const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
509
510       // Check if the font of the current glyph is the same of the previous one.
511       // If it's different the ascender and descender need to be updated.
512       if(lastFontId != glyphMetrics.fontId)
513       {
514         UpdateLineHeight(glyphMetrics, tmpLineLayout);
515         lastFontId = glyphMetrics.fontId;
516       }
517
518       // Get the character indices for the current glyph. The last character index is needed
519       // because there are glyphs formed by more than one character but their break info is
520       // given only for the last character.
521       const Length         charactersPerGlyph  = *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
522       const bool           hasCharacters       = charactersPerGlyph > 0u;
523       const CharacterIndex characterFirstIndex = *(glyphsToCharactersBuffer + glyphIndex);
524       const CharacterIndex characterLastIndex  = characterFirstIndex + (hasCharacters ? charactersPerGlyph - 1u : 0u);
525
526       // Get the line break info for the current character.
527       const LineBreakInfo lineBreakInfo = hasCharacters ? *(lineBreakInfoBuffer + characterLastIndex) : TextAbstraction::LINE_NO_BREAK;
528
529       // Increase the number of characters.
530       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
531
532       // Increase the number of glyphs.
533       tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
534
535       // Check whether is a white space.
536       const Character character    = *(textBuffer + characterFirstIndex);
537       const bool      isWhiteSpace = TextAbstraction::IsWhiteSpace(character);
538
539       // Calculate the length of the line.
540
541       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
542       const float previousTmpPenX                      = tmpLineLayout.penX;
543       const float previousTmpAdvance                   = tmpLineLayout.previousAdvance;
544       const float previousTmpLength                    = tmpLineLayout.length;
545       const float previousTmpWhiteSpaceLengthEndOfLine = tmpLineLayout.whiteSpaceLengthEndOfLine;
546
547       if(isWhiteSpace)
548       {
549         // Add the length to the length of white spaces at the end of the line.
550         tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
551       }
552       else
553       {
554         tmpLineLayout.penX += tmpLineLayout.previousAdvance + tmpLineLayout.whiteSpaceLengthEndOfLine;
555         tmpLineLayout.previousAdvance = (glyphMetrics.advance + parameters.interGlyphExtraAdvance);
556
557         tmpLineLayout.length = std::max(tmpLineLayout.length, tmpLineLayout.penX + glyphMetrics.xBearing + glyphMetrics.width);
558
559         // Clear the white space length at the end of the line.
560         tmpLineLayout.whiteSpaceLengthEndOfLine = 0.f;
561       }
562
563       // Check if the accumulated length fits in the width of the box.
564       if((completelyFill || isMultiline) && !isWhiteSpace &&
565          (tmpLineLayout.length > parameters.boundingBox.width))
566       {
567         // Current word does not fit in the box's width.
568         if(!oneWordLaidOut || completelyFill)
569         {
570           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  Break the word by character\n");
571
572           // The word doesn't fit in the control's width. It needs to be split by character.
573           if(tmpLineLayout.numberOfGlyphs > 0u)
574           {
575             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
576             tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
577
578             tmpLineLayout.penX                      = previousTmpPenX;
579             tmpLineLayout.previousAdvance           = previousTmpAdvance;
580             tmpLineLayout.length                    = previousTmpLength;
581             tmpLineLayout.whiteSpaceLengthEndOfLine = previousTmpWhiteSpaceLengthEndOfLine;
582           }
583
584           // Add part of the word to the line layout.
585           MergeLineLayout(lineLayout, tmpLineLayout);
586         }
587         else
588         {
589           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  Current word does not fit.\n");
590         }
591
592         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n");
593
594         // Reorder the RTL line.
595         if(bidiParameters.isBidirectional)
596         {
597           ReorderBiDiLayout(parameters,
598                             bidiParameters,
599                             lineLayout,
600                             lineLayout,
601                             true);
602         }
603
604         return;
605       }
606
607       if((isMultiline || isLastGlyph) &&
608          (TextAbstraction::LINE_MUST_BREAK == lineBreakInfo))
609       {
610         LineLayout currentLineLayout = lineLayout;
611
612         // Must break the line. Update the line layout and return.
613         MergeLineLayout(lineLayout, tmpLineLayout);
614
615         // Reorder the RTL line.
616         if(bidiParameters.isBidirectional)
617         {
618           ReorderBiDiLayout(parameters,
619                             bidiParameters,
620                             currentLineLayout,
621                             lineLayout,
622                             false);
623         }
624
625         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  Must break\n");
626         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
627
628         return;
629       }
630
631       if(isMultiline &&
632          (TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo))
633       {
634         oneWordLaidOut = isWordLaidOut;
635         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  One word laid-out\n");
636
637         // Current glyph is the last one of the current word.
638         // Add the temporal layout to the current one.
639         MergeLineLayout(lineLayout, tmpLineLayout);
640
641         tmpLineLayout.Clear();
642       }
643
644       glyphIndex += numberOfGLyphsInGroup;
645     }
646
647     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n");
648   }
649
650   void SetGlyphPositions(const GlyphInfo* const glyphsBuffer,
651                          Length                 numberOfGlyphs,
652                          float                  outlineWidth,
653                          float                  interGlyphExtraAdvance,
654                          Vector2*               glyphPositionsBuffer)
655   {
656     // Traverse the glyphs and set the positions.
657
658     // Check if the x bearing of the first character is negative.
659     // If it has a negative x bearing, it will exceed the boundaries of the actor,
660     // so the penX position needs to be moved to the right.
661
662     const GlyphInfo& glyph = *glyphsBuffer;
663     float            penX  = -glyph.xBearing + mCursorWidth + outlineWidth;
664
665     for(GlyphIndex i = 0u; i < numberOfGlyphs; ++i)
666     {
667       const GlyphInfo& glyph    = *(glyphsBuffer + i);
668       Vector2&         position = *(glyphPositionsBuffer + i);
669
670       position.x = penX + glyph.xBearing;
671       position.y = -glyph.yBearing;
672
673       penX += (glyph.advance + interGlyphExtraAdvance);
674     }
675   }
676
677   void SetGlyphPositions(const Parameters&     layoutParameters,
678                          Vector2*              glyphPositionsBuffer,
679                          LayoutBidiParameters& layoutBidiParameters,
680                          const LineLayout&     layout)
681   {
682     const Character* const          textBuffer               = layoutParameters.textModel->mLogicalModel->mText.Begin();
683     const BidirectionalLineInfoRun& bidiLine                 = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
684     const GlyphInfo* const          glyphsBuffer             = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
685     const GlyphIndex* const         charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
686     const Length* const             glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
687
688     CharacterIndex characterLogicalIndex = 0u;
689     CharacterIndex characterVisualIndex  = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
690
691     float penX = 0.f;
692     while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
693     {
694       const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
695       const GlyphInfo& glyph      = *(glyphsBuffer + glyphIndex);
696
697       Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
698       position.x        = penX;
699       position.y        = -glyph.yBearing;
700
701       penX += glyph.advance;
702
703       ++characterLogicalIndex;
704       characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
705     }
706
707     const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
708     const GlyphInfo& glyph      = *(glyphsBuffer + glyphIndex);
709
710     penX += -glyph.xBearing;
711
712     // Traverses the characters of the right to left paragraph.
713     for(; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
714         ++characterLogicalIndex)
715     {
716       // Convert the character in the logical order into the character in the visual order.
717       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
718
719       // Get the number of glyphs of the character.
720       const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
721
722       for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
723       {
724         // Convert the character in the visual order into the glyph in the visual order.
725         const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
726
727         DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
728
729         const GlyphInfo& glyph    = *(glyphsBuffer + glyphIndex);
730         Vector2&         position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
731
732         position.x = penX + glyph.xBearing;
733         position.y = -glyph.yBearing;
734
735         penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
736       }
737     }
738   }
739
740   /**
741    * @brief Resizes the line buffer.
742    *
743    * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
744    * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
745    * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
746    * @param[in] updateCurrentBuffer Whether the layout is updated.
747    *
748    * @return Pointer to either lines or newLines.
749    */
750   LineRun* ResizeLinesBuffer(Vector<LineRun>& lines,
751                              Vector<LineRun>& newLines,
752                              Length&          linesCapacity,
753                              bool             updateCurrentBuffer)
754   {
755     LineRun* linesBuffer = nullptr;
756     // Reserve more space for the next lines.
757     linesCapacity *= 2u;
758     if(updateCurrentBuffer)
759     {
760       newLines.Resize(linesCapacity);
761       linesBuffer = newLines.Begin();
762     }
763     else
764     {
765       lines.Resize(linesCapacity);
766       linesBuffer = lines.Begin();
767     }
768
769     return linesBuffer;
770   }
771
772   /**
773    * Ellipsis a line if it exceeds the width's of the bounding box.
774    *
775    * @param[in] layoutParameters The parameters needed to layout the text.
776    * @param[in] layout The line layout.
777    * @param[in,out] layoutSize The text's layout size.
778    * @param[in,out] linesBuffer Pointer to the line's buffer.
779    * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
780    * @param[in,out] numberOfLines The number of laid-out lines.
781    * @param[in] penY The vertical layout position.
782    * @param[in] currentParagraphDirection The current paragraph's direction.
783    * @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
784    *
785    * return Whether the line is ellipsized.
786    */
787   bool EllipsisLine(const Parameters&     layoutParameters,
788                     LayoutBidiParameters& layoutBidiParameters,
789                     const LineLayout&     layout,
790                     Size&                 layoutSize,
791                     LineRun*              linesBuffer,
792                     Vector2*              glyphPositionsBuffer,
793                     Length&               numberOfLines,
794                     float                 penY,
795                     bool&                 isAutoScrollEnabled)
796   {
797     const bool ellipsis = isAutoScrollEnabled ? (penY - layout.descender > layoutParameters.boundingBox.height) : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width)));
798
799     if(ellipsis)
800     {
801       isAutoScrollEnabled = false;
802       // Do not layout more lines if ellipsis is enabled.
803
804       // The last line needs to be completely filled with characters.
805       // Part of a word may be used.
806
807       LineRun*   lineRun = nullptr;
808       LineLayout ellipsisLayout;
809       if(0u != numberOfLines)
810       {
811         // Get the last line and layout it again with the 'completelyFill' flag to true.
812         lineRun = linesBuffer + (numberOfLines - 1u);
813
814         penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
815
816         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
817       }
818       else
819       {
820         // At least there is space reserved for one line.
821         lineRun = linesBuffer;
822
823         lineRun->glyphRun.glyphIndex = 0u;
824         ellipsisLayout.glyphIndex    = 0u;
825
826         ++numberOfLines;
827       }
828
829       GetLineLayoutForBox(layoutParameters,
830                           layoutBidiParameters,
831                           ellipsisLayout,
832                           true);
833
834       lineRun->glyphRun.numberOfGlyphs         = ellipsisLayout.numberOfGlyphs;
835       lineRun->characterRun.characterIndex     = ellipsisLayout.characterIndex;
836       lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
837       lineRun->width                           = ellipsisLayout.length;
838       lineRun->extraLength                     = std::ceil(ellipsisLayout.whiteSpaceLengthEndOfLine);
839       lineRun->ascender                        = ellipsisLayout.ascender;
840       lineRun->descender                       = ellipsisLayout.descender;
841       lineRun->ellipsis                        = true;
842
843       layoutSize.width = layoutParameters.boundingBox.width;
844       if(layoutSize.height < Math::MACHINE_EPSILON_1000)
845       {
846         layoutSize.height += (lineRun->ascender + -lineRun->descender) + lineRun->lineSpacing;
847       }
848
849       const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
850       const float            outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
851
852       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
853
854       if(layoutBidiParameters.isBidirectional)
855       {
856         layoutBidiParameters.bidiLineIndex = 0u;
857         for(Vector<BidirectionalLineInfoRun>::ConstIterator it    = bidirectionalLinesInfo.Begin(),
858                                                             endIt = bidirectionalLinesInfo.End();
859             it != endIt;
860             ++it, ++layoutBidiParameters.bidiLineIndex)
861         {
862           const BidirectionalLineInfoRun& run = *it;
863
864           if(ellipsisLayout.characterIndex == run.characterRun.characterIndex)
865           {
866             // Found where to insert the bidi line info.
867             break;
868           }
869         }
870       }
871
872       const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
873
874       if((nullptr != bidirectionalLineInfo) &&
875          !bidirectionalLineInfo->isIdentity &&
876          (ellipsisLayout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
877       {
878         lineRun->direction = RTL;
879         SetGlyphPositions(layoutParameters,
880                           glyphPositionsBuffer,
881                           layoutBidiParameters,
882                           ellipsisLayout);
883       }
884       else
885       {
886         lineRun->direction = LTR;
887         SetGlyphPositions(glyphsBuffer + lineRun->glyphRun.glyphIndex,
888                           ellipsisLayout.numberOfGlyphs,
889                           outlineWidth,
890                           layoutParameters.interGlyphExtraAdvance,
891                           glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex);
892       }
893     }
894
895     return ellipsis;
896   }
897
898   /**
899    * @brief Updates the text layout with a new laid-out line.
900    *
901    * @param[in] layoutParameters The parameters needed to layout the text.
902    * @param[in] layout The line layout.
903    * @param[in,out] layoutSize The text's layout size.
904    * @param[in,out] linesBuffer Pointer to the line's buffer.
905    * @param[in] index Index to the vector of glyphs.
906    * @param[in,out] numberOfLines The number of laid-out lines.
907    * @param[in] isLastLine Whether the laid-out line is the last one.
908    */
909   void UpdateTextLayout(const Parameters& layoutParameters,
910                         const LineLayout& layout,
911                         Size&             layoutSize,
912                         LineRun*          linesBuffer,
913                         GlyphIndex        index,
914                         Length&           numberOfLines,
915                         bool              isLastLine)
916   {
917     LineRun& lineRun = *(linesBuffer + numberOfLines);
918     ++numberOfLines;
919
920     lineRun.glyphRun.glyphIndex             = index;
921     lineRun.glyphRun.numberOfGlyphs         = layout.numberOfGlyphs;
922     lineRun.characterRun.characterIndex     = layout.characterIndex;
923     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
924     lineRun.width                           = layout.length;
925     lineRun.extraLength                     = std::ceil(layout.whiteSpaceLengthEndOfLine);
926
927     // Rounds upward to avoid a non integer size.
928     lineRun.width = std::ceil(lineRun.width);
929
930     lineRun.ascender  = layout.ascender;
931     lineRun.descender = layout.descender;
932     lineRun.direction = layout.direction;
933     lineRun.ellipsis  = false;
934
935     lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
936     lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
937
938     lineRun.lineSpacing += mDefaultLineSpacing;
939
940     // Update the actual size.
941     if(lineRun.width > layoutSize.width)
942     {
943       layoutSize.width = lineRun.width;
944     }
945
946     layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
947   }
948
949   /**
950    * @brief Updates the text layout with the last laid-out line.
951    *
952    * @param[in] layoutParameters The parameters needed to layout the text.
953    * @param[in] characterIndex The character index of the line.
954    * @param[in] glyphIndex The glyph index of the line.
955    * @param[in,out] layoutSize The text's layout size.
956    * @param[in,out] linesBuffer Pointer to the line's buffer.
957    * @param[in,out] numberOfLines The number of laid-out lines.
958    */
959   void UpdateTextLayout(const Parameters& layoutParameters,
960                         CharacterIndex    characterIndex,
961                         GlyphIndex        glyphIndex,
962                         Size&             layoutSize,
963                         LineRun*          linesBuffer,
964                         Length&           numberOfLines)
965   {
966     const Vector<GlyphInfo>& glyphs = layoutParameters.textModel->mVisualModel->mGlyphs;
967
968     // Need to add a new line with no characters but with height to increase the layoutSize.height
969     const GlyphInfo& glyphInfo = glyphs[glyphs.Count() - 1u];
970
971     Text::FontMetrics fontMetrics;
972     if(0u != glyphInfo.fontId)
973     {
974       mMetrics->GetFontMetrics(glyphInfo.fontId, fontMetrics);
975     }
976
977     LineRun& lineRun = *(linesBuffer + numberOfLines);
978     ++numberOfLines;
979
980     lineRun.glyphRun.glyphIndex             = glyphIndex;
981     lineRun.glyphRun.numberOfGlyphs         = 0u;
982     lineRun.characterRun.characterIndex     = characterIndex;
983     lineRun.characterRun.numberOfCharacters = 0u;
984     lineRun.width                           = 0.f;
985     lineRun.ascender                        = fontMetrics.ascender;
986     lineRun.descender                       = fontMetrics.descender;
987     lineRun.extraLength                     = 0.f;
988     lineRun.alignmentOffset                 = 0.f;
989     lineRun.direction                       = LTR;
990     lineRun.ellipsis                        = false;
991
992     lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
993     lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
994
995     lineRun.lineSpacing += mDefaultLineSpacing;
996
997     layoutSize.height += (lineRun.ascender + -lineRun.descender) + lineRun.lineSpacing;
998   }
999
1000   /**
1001    * @brief Updates the text's layout size adding the size of the previously laid-out lines.
1002    *
1003    * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
1004    * @param[in,out] layoutSize The text's layout size.
1005    */
1006   void UpdateLayoutSize(const Vector<LineRun>& lines,
1007                         Size&                  layoutSize)
1008   {
1009     for(Vector<LineRun>::ConstIterator it    = lines.Begin(),
1010                                        endIt = lines.End();
1011         it != endIt;
1012         ++it)
1013     {
1014       const LineRun& line = *it;
1015
1016       if(line.width > layoutSize.width)
1017       {
1018         layoutSize.width = line.width;
1019       }
1020
1021       layoutSize.height += (line.ascender + -line.descender) + line.lineSpacing;
1022     }
1023   }
1024
1025   /**
1026    * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
1027    *
1028    * @param[in] layoutParameters The parameters needed to layout the text.
1029    * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
1030    * @param[in] characterOffset The offset to be added to the runs of characters.
1031    * @param[in] glyphOffset The offset to be added to the runs of glyphs.
1032    */
1033   void UpdateLineIndexOffsets(const Parameters& layoutParameters,
1034                               Vector<LineRun>&  lines,
1035                               Length            characterOffset,
1036                               Length            glyphOffset)
1037   {
1038     // Update the glyph and character runs.
1039     for(Vector<LineRun>::Iterator it    = lines.Begin() + layoutParameters.startLineIndex,
1040                                   endIt = lines.End();
1041         it != endIt;
1042         ++it)
1043     {
1044       LineRun& line = *it;
1045
1046       line.glyphRun.glyphIndex         = glyphOffset;
1047       line.characterRun.characterIndex = characterOffset;
1048
1049       glyphOffset += line.glyphRun.numberOfGlyphs;
1050       characterOffset += line.characterRun.numberOfCharacters;
1051     }
1052   }
1053
1054   bool LayoutText(Parameters& layoutParameters,
1055                   Size&       layoutSize,
1056                   bool        elideTextEnabled,
1057                   bool&       isAutoScrollEnabled)
1058   {
1059     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
1060     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height);
1061
1062     Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
1063
1064     if(0u == layoutParameters.numberOfGlyphs)
1065     {
1066       // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
1067       if(layoutParameters.isLastNewParagraph)
1068       {
1069         Length numberOfLines = lines.Count();
1070         if(0u != numberOfLines)
1071         {
1072           const LineRun& lastLine = *(lines.End() - 1u);
1073
1074           if(0u != lastLine.characterRun.numberOfCharacters)
1075           {
1076             // Need to add a new line with no characters but with height to increase the layoutSize.height
1077             LineRun newLine;
1078             Initialize(newLine);
1079             lines.PushBack(newLine);
1080
1081             UpdateTextLayout(layoutParameters,
1082                              lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
1083                              lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
1084                              layoutSize,
1085                              lines.Begin(),
1086                              numberOfLines);
1087           }
1088         }
1089       }
1090
1091       // Calculates the layout size.
1092       UpdateLayoutSize(lines,
1093                        layoutSize);
1094
1095       // Rounds upward to avoid a non integer size.
1096       layoutSize.height = std::ceil(layoutSize.height);
1097
1098       // Nothing else do if there are no glyphs to layout.
1099       return false;
1100     }
1101
1102     const GlyphIndex lastGlyphPlusOne    = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
1103     const Length     totalNumberOfGlyphs = layoutParameters.textModel->mVisualModel->mGlyphs.Count();
1104     Vector<Vector2>& glyphPositions      = layoutParameters.textModel->mVisualModel->mGlyphPositions;
1105
1106     // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
1107     // This extra line needs to be removed.
1108     if(0u != lines.Count())
1109     {
1110       Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
1111
1112       if((0u == lastLine->characterRun.numberOfCharacters) &&
1113          (lastGlyphPlusOne == totalNumberOfGlyphs))
1114       {
1115         lines.Remove(lastLine);
1116       }
1117     }
1118
1119     // Retrieve BiDi info.
1120     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
1121
1122     const CharacterIndex* const                  glyphsToCharactersBuffer    = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
1123     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
1124     const Vector<BidirectionalLineInfoRun>&      bidirectionalLinesInfo      = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
1125
1126     // Set the layout bidirectional paramters.
1127     LayoutBidiParameters layoutBidiParameters;
1128
1129     // Whether the layout is being updated or set from scratch.
1130     const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < totalNumberOfGlyphs;
1131
1132     Vector2*        glyphPositionsBuffer = nullptr;
1133     Vector<Vector2> newGlyphPositions;
1134
1135     LineRun*        linesBuffer = nullptr;
1136     Vector<LineRun> newLines;
1137
1138     // Estimate the number of lines.
1139     Length linesCapacity = std::max(1u, layoutParameters.estimatedNumberOfLines);
1140     Length numberOfLines = 0u;
1141
1142     if(updateCurrentBuffer)
1143     {
1144       newGlyphPositions.Resize(layoutParameters.numberOfGlyphs);
1145       glyphPositionsBuffer = newGlyphPositions.Begin();
1146
1147       newLines.Resize(linesCapacity);
1148       linesBuffer = newLines.Begin();
1149     }
1150     else
1151     {
1152       glyphPositionsBuffer = glyphPositions.Begin();
1153
1154       lines.Resize(linesCapacity);
1155       linesBuffer = lines.Begin();
1156     }
1157
1158     float penY = CalculateLineOffset(lines,
1159                                      layoutParameters.startLineIndex);
1160     for(GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne;)
1161     {
1162       layoutBidiParameters.Clear();
1163
1164       if(hasBidiParagraphs)
1165       {
1166         const CharacterIndex startCharacterIndex = *(glyphsToCharactersBuffer + index);
1167
1168         for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalParagraphsInfo.Begin(),
1169                                                                  endIt = bidirectionalParagraphsInfo.End();
1170             it != endIt;
1171             ++it, ++layoutBidiParameters.bidiParagraphIndex)
1172         {
1173           const BidirectionalParagraphInfoRun& run = *it;
1174
1175           const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1176
1177           if(lastCharacterIndex <= startCharacterIndex)
1178           {
1179             // Do not process, the paragraph has already been processed.
1180             continue;
1181           }
1182
1183           if(startCharacterIndex >= run.characterRun.characterIndex && startCharacterIndex < lastCharacterIndex)
1184           {
1185             layoutBidiParameters.paragraphDirection = run.direction;
1186             layoutBidiParameters.isBidirectional    = true;
1187           }
1188
1189           // Has already been found.
1190           break;
1191         }
1192
1193         if(layoutBidiParameters.isBidirectional)
1194         {
1195           for(Vector<BidirectionalLineInfoRun>::ConstIterator it    = bidirectionalLinesInfo.Begin(),
1196                                                               endIt = bidirectionalLinesInfo.End();
1197               it != endIt;
1198               ++it, ++layoutBidiParameters.bidiLineIndex)
1199           {
1200             const BidirectionalLineInfoRun& run = *it;
1201
1202             const CharacterIndex lastCharacterIndex = run.characterRun.characterIndex + run.characterRun.numberOfCharacters;
1203
1204             if(lastCharacterIndex <= startCharacterIndex)
1205             {
1206               // skip
1207               continue;
1208             }
1209
1210             if(startCharacterIndex < lastCharacterIndex)
1211             {
1212               // Found where to insert the bidi line info.
1213               break;
1214             }
1215           }
1216         }
1217       }
1218
1219       CharacterDirection currentParagraphDirection = layoutBidiParameters.paragraphDirection;
1220
1221       // Get the layout for the line.
1222       LineLayout layout;
1223       layout.direction  = layoutBidiParameters.paragraphDirection;
1224       layout.glyphIndex = index;
1225       GetLineLayoutForBox(layoutParameters,
1226                           layoutBidiParameters,
1227                           layout,
1228                           false);
1229
1230       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex);
1231       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex);
1232       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs);
1233       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters);
1234       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                length %f\n", layout.length);
1235
1236       if(0u == layout.numberOfGlyphs)
1237       {
1238         // The width is too small and no characters are laid-out.
1239         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n");
1240
1241         lines.Resize(numberOfLines);
1242
1243         // Rounds upward to avoid a non integer size.
1244         layoutSize.height = std::ceil(layoutSize.height);
1245
1246         return false;
1247       }
1248
1249       // Set the line position. DISCARD if ellipsis is enabled and the position exceeds the boundaries
1250       // of the box.
1251       penY += layout.ascender;
1252
1253       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  pen y %f\n", penY);
1254
1255       bool ellipsis = false;
1256       if(elideTextEnabled)
1257       {
1258         layoutBidiParameters.paragraphDirection = currentParagraphDirection;
1259
1260         // Does the ellipsis of the last line.
1261         ellipsis = EllipsisLine(layoutParameters,
1262                                 layoutBidiParameters,
1263                                 layout,
1264                                 layoutSize,
1265                                 linesBuffer,
1266                                 glyphPositionsBuffer,
1267                                 numberOfLines,
1268                                 penY,
1269                                 isAutoScrollEnabled);
1270       }
1271
1272       if(ellipsis)
1273       {
1274         // No more lines to layout.
1275         break;
1276       }
1277       else
1278       {
1279         // Whether the last line has been laid-out.
1280         const bool isLastLine = index + layout.numberOfGlyphs == totalNumberOfGlyphs;
1281
1282         if(numberOfLines == linesCapacity)
1283         {
1284           // Reserve more space for the next lines.
1285           linesBuffer = ResizeLinesBuffer(lines,
1286                                           newLines,
1287                                           linesCapacity,
1288                                           updateCurrentBuffer);
1289         }
1290
1291         // Updates the current text's layout with the line's layout.
1292         UpdateTextLayout(layoutParameters,
1293                          layout,
1294                          layoutSize,
1295                          linesBuffer,
1296                          index,
1297                          numberOfLines,
1298                          isLastLine);
1299
1300         const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
1301
1302         if((nextIndex == totalNumberOfGlyphs) &&
1303            layoutParameters.isLastNewParagraph &&
1304            (mLayout == MULTI_LINE_BOX))
1305         {
1306           // The last character of the text is a new paragraph character.
1307           // An extra line with no characters is added to increase the text's height
1308           // in order to place the cursor.
1309
1310           if(numberOfLines == linesCapacity)
1311           {
1312             // Reserve more space for the next lines.
1313             linesBuffer = ResizeLinesBuffer(lines,
1314                                             newLines,
1315                                             linesCapacity,
1316                                             updateCurrentBuffer);
1317           }
1318
1319           UpdateTextLayout(layoutParameters,
1320                            layout.characterIndex + layout.numberOfCharacters,
1321                            index + layout.numberOfGlyphs,
1322                            layoutSize,
1323                            linesBuffer,
1324                            numberOfLines);
1325         } // whether to add a last line.
1326
1327         const GlyphInfo* const glyphsBuffer = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
1328         const float            outlineWidth = static_cast<float>(layoutParameters.textModel->GetOutlineWidth());
1329
1330         const BidirectionalLineInfoRun* const bidirectionalLineInfo = (layoutBidiParameters.isBidirectional && !bidirectionalLinesInfo.Empty()) ? &bidirectionalLinesInfo[layoutBidiParameters.bidiLineIndex] : nullptr;
1331
1332         if((nullptr != bidirectionalLineInfo) &&
1333            !bidirectionalLineInfo->isIdentity &&
1334            (layout.characterIndex == bidirectionalLineInfo->characterRun.characterIndex))
1335         {
1336           SetGlyphPositions(layoutParameters,
1337                             glyphPositionsBuffer,
1338                             layoutBidiParameters,
1339                             layout);
1340         }
1341         else
1342         {
1343           // Sets the positions of the glyphs.
1344           SetGlyphPositions(glyphsBuffer + index,
1345                             layout.numberOfGlyphs,
1346                             outlineWidth,
1347                             layoutParameters.interGlyphExtraAdvance,
1348                             glyphPositionsBuffer + index - layoutParameters.startGlyphIndex);
1349         }
1350
1351         // Updates the vertical pen's position.
1352         penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1353         // If there is a defaultLineSize, updates the pen's position.
1354         if(mDefaultLineSize > 0.f)
1355         {
1356           float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
1357           lineSpacing       = lineSpacing < 0.f ? 0.f : lineSpacing;
1358           penY += lineSpacing;
1359         }
1360
1361         // Increase the glyph index.
1362         index = nextIndex;
1363       } // no ellipsis
1364     }   // end for() traversing glyphs.
1365
1366     if(updateCurrentBuffer)
1367     {
1368       glyphPositions.Insert(glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1369                             newGlyphPositions.Begin(),
1370                             newGlyphPositions.End());
1371       glyphPositions.Resize(totalNumberOfGlyphs);
1372
1373       newLines.Resize(numberOfLines);
1374
1375       // Current text's layout size adds only the newly laid-out lines.
1376       // Updates the layout size with the previously laid-out lines.
1377       UpdateLayoutSize(lines,
1378                        layoutSize);
1379
1380       if(0u != newLines.Count())
1381       {
1382         const LineRun& lastLine = *(newLines.End() - 1u);
1383
1384         const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1385         const Length glyphOffset     = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1386
1387         // Update the indices of the runs before the new laid-out lines are inserted.
1388         UpdateLineIndexOffsets(layoutParameters,
1389                                lines,
1390                                characterOffset,
1391                                glyphOffset);
1392
1393         // Insert the lines.
1394         lines.Insert(lines.Begin() + layoutParameters.startLineIndex,
1395                      newLines.Begin(),
1396                      newLines.End());
1397       }
1398     }
1399     else
1400     {
1401       lines.Resize(numberOfLines);
1402     }
1403
1404     // Rounds upward to avoid a non integer size.
1405     layoutSize.height = std::ceil(layoutSize.height);
1406
1407     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--LayoutText\n\n");
1408
1409     return true;
1410   }
1411
1412   void Align(const Size&                     size,
1413              CharacterIndex                  startIndex,
1414              Length                          numberOfCharacters,
1415              Text::HorizontalAlignment::Type horizontalAlignment,
1416              Vector<LineRun>&                lines,
1417              float&                          alignmentOffset,
1418              Dali::LayoutDirection::Type     layoutDirection,
1419              bool                            matchSystemLanguageDirection)
1420   {
1421     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1422
1423     alignmentOffset = MAX_FLOAT;
1424     // Traverse all lines and align the glyphs.
1425     for(Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1426         it != endIt;
1427         ++it)
1428     {
1429       LineRun& line = *it;
1430
1431       if(line.characterRun.characterIndex < startIndex)
1432       {
1433         // Do not align lines which have already been aligned.
1434         continue;
1435       }
1436
1437       if(line.characterRun.characterIndex > lastCharacterPlusOne)
1438       {
1439         // Do not align lines beyond the last laid-out character.
1440         break;
1441       }
1442
1443       if(line.characterRun.characterIndex == lastCharacterPlusOne && !isEmptyLineAtLast(lines, it))
1444       {
1445         // Do not align lines beyond the last laid-out character unless the line is last and empty.
1446         break;
1447       }
1448
1449       // Calculate the line's alignment offset accordingly with the align option,
1450       // the box width, line length, and the paragraph's direction.
1451       CalculateHorizontalAlignment(size.width,
1452                                    horizontalAlignment,
1453                                    line,
1454                                    layoutDirection,
1455                                    matchSystemLanguageDirection);
1456
1457       // Updates the alignment offset.
1458       alignmentOffset = std::min(alignmentOffset, line.alignmentOffset);
1459     }
1460   }
1461
1462   void CalculateHorizontalAlignment(float                       boxWidth,
1463                                     HorizontalAlignment::Type   horizontalAlignment,
1464                                     LineRun&                    line,
1465                                     Dali::LayoutDirection::Type layoutDirection,
1466                                     bool                        matchSystemLanguageDirection)
1467   {
1468     line.alignmentOffset = 0.f;
1469     const bool isLineRTL = RTL == line.direction;
1470
1471     // Whether to swap the alignment.
1472     // 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.
1473     bool  isLayoutRTL = isLineRTL;
1474     float lineLength  = line.width;
1475
1476     // match align for system language direction
1477     if(matchSystemLanguageDirection)
1478     {
1479       // Swap the alignment type if the line is right to left.
1480       isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1481     }
1482     // Calculate the horizontal line offset.
1483     switch(horizontalAlignment)
1484     {
1485       case HorizontalAlignment::BEGIN:
1486       {
1487         if(isLayoutRTL)
1488         {
1489           if(isLineRTL)
1490           {
1491             lineLength += line.extraLength;
1492           }
1493
1494           line.alignmentOffset = boxWidth - lineLength;
1495         }
1496         else
1497         {
1498           line.alignmentOffset = 0.f;
1499
1500           if(isLineRTL)
1501           {
1502             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1503             line.alignmentOffset -= line.extraLength;
1504           }
1505         }
1506         break;
1507       }
1508       case HorizontalAlignment::CENTER:
1509       {
1510         line.alignmentOffset = 0.5f * (boxWidth - lineLength);
1511
1512         if(isLineRTL)
1513         {
1514           line.alignmentOffset -= line.extraLength;
1515         }
1516
1517         line.alignmentOffset = std::floor(line.alignmentOffset); // floor() avoids pixel alignment issues.
1518         break;
1519       }
1520       case HorizontalAlignment::END:
1521       {
1522         if(isLayoutRTL)
1523         {
1524           line.alignmentOffset = 0.f;
1525
1526           if(isLineRTL)
1527           {
1528             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1529             line.alignmentOffset -= line.extraLength;
1530           }
1531         }
1532         else
1533         {
1534           if(isLineRTL)
1535           {
1536             lineLength += line.extraLength;
1537           }
1538
1539           line.alignmentOffset = boxWidth - lineLength;
1540         }
1541         break;
1542       }
1543     }
1544   }
1545
1546   void Initialize(LineRun& line)
1547   {
1548     line.glyphRun.glyphIndex             = 0u;
1549     line.glyphRun.numberOfGlyphs         = 0u;
1550     line.characterRun.characterIndex     = 0u;
1551     line.characterRun.numberOfCharacters = 0u;
1552     line.width                           = 0.f;
1553     line.ascender                        = 0.f;
1554     line.descender                       = 0.f;
1555     line.extraLength                     = 0.f;
1556     line.alignmentOffset                 = 0.f;
1557     line.direction                       = LTR;
1558     line.ellipsis                        = false;
1559     line.lineSpacing                     = mDefaultLineSpacing;
1560   }
1561
1562   Type  mLayout;
1563   float mCursorWidth;
1564   float mDefaultLineSpacing;
1565   float mDefaultLineSize;
1566
1567   IntrusivePtr<Metrics> mMetrics;
1568 };
1569
1570 Engine::Engine()
1571 : mImpl{nullptr}
1572 {
1573   mImpl = new Engine::Impl();
1574 }
1575
1576 Engine::~Engine()
1577 {
1578   delete mImpl;
1579 }
1580
1581 void Engine::SetMetrics(MetricsPtr& metrics)
1582 {
1583   mImpl->mMetrics = metrics;
1584 }
1585
1586 void Engine::SetLayout(Type layout)
1587 {
1588   mImpl->mLayout = layout;
1589 }
1590
1591 Engine::Type Engine::GetLayout() const
1592 {
1593   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1594   return mImpl->mLayout;
1595 }
1596
1597 void Engine::SetCursorWidth(int width)
1598 {
1599   mImpl->mCursorWidth = static_cast<float>(width);
1600 }
1601
1602 int Engine::GetCursorWidth() const
1603 {
1604   return static_cast<int>(mImpl->mCursorWidth);
1605 }
1606
1607 bool Engine::LayoutText(Parameters& layoutParameters,
1608                         Size&       layoutSize,
1609                         bool        elideTextEnabled,
1610                         bool&       isAutoScrollEnabled)
1611 {
1612   return mImpl->LayoutText(layoutParameters,
1613                            layoutSize,
1614                            elideTextEnabled,
1615                            isAutoScrollEnabled);
1616 }
1617
1618 void Engine::Align(const Size&                     size,
1619                    CharacterIndex                  startIndex,
1620                    Length                          numberOfCharacters,
1621                    Text::HorizontalAlignment::Type horizontalAlignment,
1622                    Vector<LineRun>&                lines,
1623                    float&                          alignmentOffset,
1624                    Dali::LayoutDirection::Type     layoutDirection,
1625                    bool                            matchSystemLanguageDirection)
1626 {
1627   mImpl->Align(size,
1628                startIndex,
1629                numberOfCharacters,
1630                horizontalAlignment,
1631                lines,
1632                alignmentOffset,
1633                layoutDirection,
1634                matchSystemLanguageDirection);
1635 }
1636
1637 void Engine::SetDefaultLineSpacing(float lineSpacing)
1638 {
1639   mImpl->mDefaultLineSpacing = lineSpacing;
1640 }
1641
1642 float Engine::GetDefaultLineSpacing() const
1643 {
1644   return mImpl->mDefaultLineSpacing;
1645 }
1646
1647 void Engine::SetDefaultLineSize(float lineSize)
1648 {
1649   mImpl->mDefaultLineSize = lineSize;
1650 }
1651
1652 float Engine::GetDefaultLineSize() const
1653 {
1654   return mImpl->mDefaultLineSize;
1655 }
1656
1657 } // namespace Layout
1658
1659 } // namespace Text
1660
1661 } // namespace Toolkit
1662
1663 } // namespace Dali