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