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