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