Merge "TextField is re-laied out after its properties are changed." into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2017 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 <dali/integration-api/debug.h>
24 #include <dali/devel-api/text-abstraction/font-client.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
28 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
29 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
30 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
31
32 namespace Dali
33 {
34
35 namespace Toolkit
36 {
37
38 namespace Text
39 {
40
41 namespace Layout
42 {
43
44 namespace
45 {
46
47 #if defined(DEBUG_ENABLED)
48   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
49 #endif
50
51 const float MAX_FLOAT = std::numeric_limits<float>::max();
52 const bool RTL = true;
53 const float CURSOR_WIDTH = 1.f;
54 const float LINE_SPACING= 0.f;
55
56 } //namespace
57
58 /**
59  * @brief Stores temporary layout info of the line.
60  */
61 struct LineLayout
62 {
63   LineLayout()
64   : glyphIndex( 0u ),
65     characterIndex( 0u ),
66     numberOfGlyphs( 0u ),
67     numberOfCharacters( 0u ),
68     length( 0.f ),
69     extraBearing( 0.f ),
70     extraWidth( 0.f ),
71     wsLengthEndOfLine( 0.f ),
72     ascender( 0.f ),
73     descender( MAX_FLOAT ),
74     lineSpacing( 0.f )
75   {}
76
77   ~LineLayout()
78   {}
79
80   void Clear()
81   {
82     glyphIndex = 0u;
83     characterIndex = 0u;
84     numberOfGlyphs = 0u;
85     numberOfCharacters = 0u;
86     length = 0.f;
87     extraBearing = 0.f;
88     extraWidth = 0.f;
89     wsLengthEndOfLine = 0.f;
90     ascender = 0.f;
91     descender = MAX_FLOAT;
92   }
93
94   GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
95   CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
96   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
97   Length         numberOfCharacters; ///< The number of characters which fit in one line.
98   float          length;             ///< The addition of the advance metric of all the glyphs which fit in one line.
99   float          extraBearing;       ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
100   float          extraWidth;         ///< The extra width to be added to the line's length when the bearing + width of the last glyph is greater than the advance.
101   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the 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 };
106
107 struct Engine::Impl
108 {
109   Impl()
110   : mLayout( Layout::Engine::SINGLE_LINE_BOX ),
111     mCursorWidth( CURSOR_WIDTH ),
112     mDefaultLineSpacing( LINE_SPACING ),
113     mPreviousCharacterExtraWidth( 0.0f )
114   {
115   }
116
117   /**
118    * @brief Updates the line ascender and descender with the metrics of a new font.
119    *
120    * @param[in] glyphMetrics The metrics of the new font.
121    * @param[in,out] lineLayout The line layout.
122    */
123   void UpdateLineHeight( const GlyphMetrics& glyphMetrics, LineLayout& lineLayout )
124   {
125     Text::FontMetrics fontMetrics;
126     if( 0u != glyphMetrics.fontId )
127     {
128       mMetrics->GetFontMetrics( glyphMetrics.fontId, fontMetrics );
129     }
130     else
131     {
132       fontMetrics.ascender = glyphMetrics.fontHeight;
133       fontMetrics.descender = 0.f;
134       fontMetrics.height = fontMetrics.ascender;
135       fontMetrics.underlinePosition = 0.f;
136       fontMetrics.underlineThickness = 1.f;
137     }
138
139     // Sets the maximum ascender.
140     lineLayout.ascender = std::max( lineLayout.ascender, fontMetrics.ascender );
141
142     // Sets the minimum descender.
143     lineLayout.descender = std::min( lineLayout.descender, fontMetrics.descender );
144
145     // set the line spacing
146     lineLayout.lineSpacing = mDefaultLineSpacing;
147   }
148
149   /**
150    * @brief Merges a temporary line layout into the line layout.
151    *
152    * @param[in,out] lineLayout The line layout.
153    * @param[in] tmpLineLayout A temporary line layout.
154    */
155   void MergeLineLayout( LineLayout& lineLayout,
156                         const LineLayout& tmpLineLayout )
157   {
158     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
159     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
160     lineLayout.length += tmpLineLayout.length;
161
162     if( 0.f < tmpLineLayout.length )
163     {
164       lineLayout.length += lineLayout.wsLengthEndOfLine;
165
166     lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
167     }
168     else
169     {
170       lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
171     }
172
173     // Sets the maximum ascender.
174     lineLayout.ascender = std::max( lineLayout.ascender, tmpLineLayout.ascender );
175
176     // Sets the minimum descender.
177     lineLayout.descender = std::min( lineLayout.descender, tmpLineLayout.descender );
178   }
179
180   /**
181    * Retrieves the line layout for a given box width.
182    *
183    * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
184    *       because the number of characters of the line is not known (one of the responsabilities of this method
185    *       is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
186    *       and left to right text is laid-out, it can be small differences in the line length. One solution is to
187    *       reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
188    *       this method calculates which are the first and last glyphs of the line (the ones that causes the
189    *       differences). This is a good point to check if there is problems with the text exceeding the boundaries
190    *       of the control when there is right to left text.
191    *
192    * @param[in] parameters The layout parameters.
193    * @param[out] lineLayout The line layout.
194    * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
195    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
196    */
197   void GetLineLayoutForBox( const Parameters& parameters,
198                             LineLayout& lineLayout,
199                             CharacterDirection& paragraphDirection,
200                             bool completelyFill )
201   {
202     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
203     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex );
204     // Stores temporary line layout which has not been added to the final line layout.
205     LineLayout tmpLineLayout;
206
207     const bool isMultiline = mLayout == MULTI_LINE_BOX;
208     const bool isWordLaidOut = parameters.lineWrapMode == Text::LineWrap::WORD;
209
210     // The last glyph to be laid-out.
211     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
212
213     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
214     // In the case the line starts with a right to left character, if the width is longer than the advance,
215     // the difference needs to be added to the line length.
216
217     // Check whether the first glyph comes from a character that is shaped in multiple glyphs.
218     const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( lineLayout.glyphIndex,
219                                                                    lastGlyphOfParagraphPlusOne,
220                                                                    parameters.charactersPerGlyphBuffer );
221
222     GlyphMetrics glyphMetrics;
223     GetGlyphsMetrics( lineLayout.glyphIndex,
224                       numberOfGLyphsInGroup,
225                       glyphMetrics,
226                       parameters.glyphsBuffer,
227                       mMetrics );
228
229     // Set the direction of the first character of the line.
230     lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
231     const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
232     CharacterDirection previousCharacterDirection = firstCharacterDirection;
233
234     const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
235     float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
236
237     float tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
238
239     tmpLineLayout.length += mCursorWidth; // Added to give some space to the cursor.
240
241     // Calculate the line height if there is no characters.
242     FontId lastFontId = glyphMetrics.fontId;
243     UpdateLineHeight( glyphMetrics, tmpLineLayout );
244
245     bool oneWordLaidOut = false;
246
247     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
248          glyphIndex < lastGlyphOfParagraphPlusOne; )
249     {
250       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex );
251
252       // Check whether this glyph comes from a character that is shaped in multiple glyphs.
253       const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup( glyphIndex,
254                                                                      lastGlyphOfParagraphPlusOne,
255                                                                      parameters.charactersPerGlyphBuffer );
256
257       GlyphMetrics glyphMetrics;
258       GetGlyphsMetrics( glyphIndex,
259                         numberOfGLyphsInGroup,
260                         glyphMetrics,
261                         parameters.glyphsBuffer,
262                         mMetrics );
263
264       const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup  == parameters.totalNumberOfGlyphs;
265
266       // Check if the font of the current glyph is the same of the previous one.
267       // If it's different the ascender and descender need to be updated.
268       if( lastFontId != glyphMetrics.fontId )
269       {
270         UpdateLineHeight( glyphMetrics, tmpLineLayout );
271         lastFontId = glyphMetrics.fontId;
272       }
273
274       // Get the character indices for the current glyph. The last character index is needed
275       // because there are glyphs formed by more than one character but their break info is
276       // given only for the last character.
277       const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u );
278       const bool hasCharacters = charactersPerGlyph > 0u;
279       const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
280       const CharacterIndex characterLastIndex = characterFirstIndex + ( hasCharacters ? charactersPerGlyph - 1u : 0u );
281
282       // Get the line break info for the current character.
283       const LineBreakInfo lineBreakInfo = hasCharacters ? *( parameters.lineBreakInfoBuffer + characterLastIndex ) : TextAbstraction::LINE_NO_BREAK;
284
285       // Increase the number of characters.
286       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
287
288       // Increase the number of glyphs.
289       tmpLineLayout.numberOfGlyphs += numberOfGLyphsInGroup;
290
291       // Check whether is a white space.
292       const Character character = *( parameters.textBuffer + characterFirstIndex );
293       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
294
295       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
296       const float previousTmpLineLength = tmpLineLayout.length;
297       const float previousTmpExtraBearing = tmpExtraBearing;
298       const float previousTmpExtraWidth = tmpExtraWidth;
299
300       // Get the character's direction.
301       const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
302
303       // Increase the accumulated length.
304       if( isWhiteSpace )
305       {
306         // Add the length to the length of white spaces at the end of the line.
307         tmpLineLayout.wsLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
308       }
309       else
310       {
311         // Add as well any previous white space length.
312         tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphMetrics.advance;
313
314         // An extra space may be added to the line for the first and last glyph of the line.
315         // If the bearing of the first glyph is negative, its positive value needs to be added.
316         // If the bearing plus the width of the last glyph is greater than the advance, the difference
317         // needs to be added.
318
319         if( characterDirection == paragraphDirection )
320         {
321           if( RTL == characterDirection )
322           {
323             //       <--
324             // |   Rrrrr|
325             // or
326             // |  Rllrrr|
327             // or
328             // |lllrrrrr|
329             // |     Rll|
330             //
331
332             tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
333           }
334           else // LTR
335           {
336             //  -->
337             // |lllL    |
338             // or
339             // |llrrL   |
340             // or
341             // |lllllrrr|
342             // |rrL     |
343             //
344
345             const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
346             tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
347             tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
348           }
349         }
350         else
351         {
352           if( characterDirection != previousCharacterDirection )
353           {
354             if( RTL == characterDirection )
355             {
356               //  -->
357               // |lllR    |
358
359               const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
360               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
361               tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
362             }
363             else // LTR
364             {
365               //       <--
366               // |   Lrrrr|
367
368               tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
369             }
370           }
371           else if( characterDirection == firstCharacterDirection )
372           {
373             if( RTL == characterDirection )
374             {
375               //  -->
376               // |llllllrr|
377               // |Rr      |
378
379               tmpExtraBearing = ( 0.f > glyphMetrics.xBearing ) ? -glyphMetrics.xBearing : 0.f;
380             }
381             else // LTR
382             {
383               //       <--
384               // |llllrrrr|
385               // |     llL|
386
387               const float extraWidth = glyphMetrics.xBearing + glyphMetrics.width - glyphMetrics.advance;
388               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
389               tmpExtraWidth = std::max( mPreviousCharacterExtraWidth - glyphMetrics.advance, tmpExtraWidth );
390             }
391           }
392         }
393
394         // Clear the white space length at the end of the line.
395         tmpLineLayout.wsLengthEndOfLine = 0.f;
396       }
397
398       // If calculation is end but wsLengthEndOfLine is exist, it means end of text is space.
399       // Merge remained length.
400       if ( !parameters.ignoreSpaceAfterText && glyphIndex == lastGlyphOfParagraphPlusOne-1 && tmpLineLayout.wsLengthEndOfLine > 0 )
401       {
402         tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine;
403         tmpLineLayout.wsLengthEndOfLine = 0u;
404       }
405
406       // Save the current extra width to compare with the next one
407       mPreviousCharacterExtraWidth = tmpExtraWidth;
408
409       // Check if the accumulated length fits in the width of the box.
410       if( ( completelyFill || isMultiline )  && !(parameters.ignoreSpaceAfterText && isWhiteSpace) &&
411           ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
412       {
413         // Current word does not fit in the box's width.
414         if( !oneWordLaidOut || completelyFill )
415         {
416           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Break the word by character\n" );
417
418           // The word doesn't fit in the control's width. It needs to be split by character.
419           if( tmpLineLayout.numberOfGlyphs > 0u )
420           {
421             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
422             tmpLineLayout.numberOfGlyphs -= numberOfGLyphsInGroup;
423             tmpLineLayout.length = previousTmpLineLength;
424             tmpExtraBearing = previousTmpExtraBearing;
425             tmpExtraWidth = previousTmpExtraWidth;
426           }
427
428           // Add part of the word to the line layout.
429           MergeLineLayout( lineLayout, tmpLineLayout );
430         }
431         else
432         {
433           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Current word does not fit.\n" );
434         }
435
436         lineLayout.extraBearing = tmpExtraBearing;
437         lineLayout.extraWidth = tmpExtraWidth;
438
439         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
440
441         return;
442       }
443
444       if( ( isMultiline || isLastGlyph ) &&
445           ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
446       {
447         // Must break the line. Update the line layout and return.
448         MergeLineLayout( lineLayout, tmpLineLayout );
449
450         // Set the next paragraph's direction.
451         if( !isLastGlyph &&
452             ( NULL != parameters.characterDirectionBuffer ) )
453         {
454           paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
455         }
456
457         lineLayout.extraBearing = tmpExtraBearing;
458         lineLayout.extraWidth = tmpExtraWidth;
459
460         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Must break\n" );
461         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
462
463         return;
464       }
465
466       if( isMultiline &&
467           ( TextAbstraction::LINE_ALLOW_BREAK == lineBreakInfo ) )
468       {
469         oneWordLaidOut = isWordLaidOut;
470         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid-out\n" );
471
472         // Current glyph is the last one of the current word.
473         // Add the temporal layout to the current one.
474         MergeLineLayout( lineLayout, tmpLineLayout );
475
476         tmpLineLayout.Clear();
477       }
478
479       previousCharacterDirection = characterDirection;
480       glyphIndex += numberOfGLyphsInGroup;
481     }
482
483     lineLayout.extraBearing = tmpExtraBearing;
484     lineLayout.extraWidth = tmpExtraWidth;
485
486     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
487   }
488
489   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
490                           Length numberOfGlyphs,
491                           float outlineWidth,
492                           Vector2* glyphPositionsBuffer )
493   {
494     // Traverse the glyphs and set the positions.
495
496     // Check if the x bearing of the first character is negative.
497     // If it has a negative x bearing, it will exceed the boundaries of the actor,
498     // so the penX position needs to be moved to the right.
499
500     const GlyphInfo& glyph = *glyphsBuffer;
501     float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing + outlineWidth : outlineWidth;
502
503
504     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
505     {
506       const GlyphInfo& glyph = *( glyphsBuffer + i );
507       Vector2& position = *( glyphPositionsBuffer + i );
508
509       position.x = penX + glyph.xBearing;
510       position.y = -glyph.yBearing;
511
512       penX += glyph.advance;
513     }
514   }
515
516   /**
517    * @brief Resizes the line buffer.
518    *
519    * @param[in,out] lines The vector of lines. Used when the layout is created from scratch.
520    * @param[in,out] newLines The vector of lines used instead of @p lines when the layout is updated.
521    * @param[in,out] linesCapacity The capacity of the vector (either lines or newLines).
522    * @param[in] updateCurrentBuffer Whether the layout is updated.
523    *
524    * @return Pointer to either lines or newLines.
525    */
526   LineRun* ResizeLinesBuffer( Vector<LineRun>& lines,
527                               Vector<LineRun>& newLines,
528                               Length& linesCapacity,
529                               bool updateCurrentBuffer )
530   {
531     LineRun* linesBuffer = NULL;
532     // Reserve more space for the next lines.
533     linesCapacity *= 2u;
534     if( updateCurrentBuffer )
535     {
536       newLines.Resize( linesCapacity );
537       linesBuffer = newLines.Begin();
538     }
539     else
540     {
541       lines.Resize( linesCapacity );
542       linesBuffer = lines.Begin();
543     }
544
545     return linesBuffer;
546   }
547
548   /**
549    * Ellipsis a line if it exceeds the width's of the bounding box.
550    *
551    * @param[in] layoutParameters The parameters needed to layout the text.
552    * @param[in] layout The line layout.
553    * @param[in,out] layoutSize The text's layout size.
554    * @param[in,out] linesBuffer Pointer to the line's buffer.
555    * @param[in,out] glyphPositionsBuffer Pointer to the position's buffer.
556    * @param[in,out] numberOfLines The number of laid-out lines.
557    * @param[in] penY The vertical layout position.
558    * @param[in] currentParagraphDirection The current paragraph's direction.
559    * @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
560    *
561    * return Whether the line is ellipsized.
562    */
563   bool EllipsisLine( const Parameters& layoutParameters,
564                      const LineLayout& layout,
565                      Size& layoutSize,
566                      LineRun* linesBuffer,
567                      Vector2* glyphPositionsBuffer,
568                      Length& numberOfLines,
569                      float penY,
570                      CharacterDirection currentParagraphDirection,
571                      bool& isAutoScrollEnabled )
572   {
573     const bool ellipsis = isAutoScrollEnabled ? ( penY - layout.descender > layoutParameters.boundingBox.height ) :
574                                                 ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
575                                                   ( ( mLayout == SINGLE_LINE_BOX ) &&
576                                                     ( layout.length > layoutParameters.boundingBox.width ) ) );
577
578     if( ellipsis )
579     {
580       isAutoScrollEnabled = false;
581       // Do not layout more lines if ellipsis is enabled.
582
583       // The last line needs to be completely filled with characters.
584       // Part of a word may be used.
585
586       LineRun* lineRun = NULL;
587       LineLayout ellipsisLayout;
588       if( 0u != numberOfLines )
589       {
590         // Get the last line and layout it again with the 'completelyFill' flag to true.
591         lineRun = linesBuffer + ( numberOfLines - 1u );
592
593         penY -= layout.ascender - lineRun->descender + lineRun->lineSpacing;
594
595         ellipsisLayout.glyphIndex = lineRun->glyphRun.glyphIndex;
596       }
597       else
598       {
599         // At least there is space reserved for one line.
600         lineRun = linesBuffer;
601
602         lineRun->glyphRun.glyphIndex = 0u;
603         ellipsisLayout.glyphIndex = 0u;
604
605         ++numberOfLines;
606       }
607
608       GetLineLayoutForBox( layoutParameters,
609                            ellipsisLayout,
610                            currentParagraphDirection,
611                            true );
612
613       lineRun->glyphRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
614       lineRun->characterRun.characterIndex = ellipsisLayout.characterIndex;
615       lineRun->characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
616       lineRun->width = ellipsisLayout.length;
617       lineRun->extraLength =  std::ceil( ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f );
618       lineRun->ascender = ellipsisLayout.ascender;
619       lineRun->descender = ellipsisLayout.descender;
620       lineRun->direction = !RTL;
621       lineRun->ellipsis = true;
622
623       layoutSize.width = layoutParameters.boundingBox.width;
624       if( layoutSize.height < Math::MACHINE_EPSILON_1000 )
625       {
626         layoutSize.height += ( lineRun->ascender + -lineRun->descender ) + lineRun->lineSpacing;
627       }
628
629       SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun->glyphRun.glyphIndex,
630                          ellipsisLayout.numberOfGlyphs,
631                          layoutParameters.outlineWidth,
632                          glyphPositionsBuffer + lineRun->glyphRun.glyphIndex - layoutParameters.startGlyphIndex );
633     }
634
635     return ellipsis;
636   }
637
638   /**
639    * @brief Updates the text layout with a new laid-out line.
640    *
641    * @param[in] layoutParameters The parameters needed to layout the text.
642    * @param[in] layout The line layout.
643    * @param[in,out] layoutSize The text's layout size.
644    * @param[in,out] linesBuffer Pointer to the line's buffer.
645    * @param[in] index Index to the vector of glyphs.
646    * @param[in,out] numberOfLines The number of laid-out lines.
647    * @param[in] isLastLine Whether the laid-out line is the last one.
648    */
649   void UpdateTextLayout( const Parameters& layoutParameters,
650                          const LineLayout& layout,
651                          Size& layoutSize,
652                          LineRun* linesBuffer,
653                          GlyphIndex index,
654                          Length& numberOfLines,
655                          bool isLastLine )
656   {
657     LineRun& lineRun = *( linesBuffer + numberOfLines );
658     ++numberOfLines;
659
660     lineRun.glyphRun.glyphIndex = index;
661     lineRun.glyphRun.numberOfGlyphs = layout.numberOfGlyphs;
662     lineRun.characterRun.characterIndex = layout.characterIndex;
663     lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
664     lineRun.lineSpacing = mDefaultLineSpacing;
665
666     if( isLastLine && !layoutParameters.isLastNewParagraph )
667     {
668       const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
669       if( MULTI_LINE_BOX == mLayout )
670       {
671         lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
672       }
673       else
674       {
675         lineRun.width = width;
676       }
677
678       lineRun.extraLength = 0.f;
679     }
680     else
681     {
682       lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
683       lineRun.extraLength = std::ceil( ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f );
684     }
685
686     // Rounds upward to avoid a non integer size.
687     lineRun.width = std::ceil( lineRun.width );
688
689     lineRun.ascender = layout.ascender;
690     lineRun.descender = layout.descender;
691     lineRun.direction = !RTL;
692     lineRun.ellipsis = false;
693
694     // Update the actual size.
695     if( lineRun.width > layoutSize.width )
696     {
697       layoutSize.width = lineRun.width;
698     }
699
700     layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
701   }
702
703   /**
704    * @brief Updates the text layout with the last laid-out line.
705    *
706    * @param[in] layoutParameters The parameters needed to layout the text.
707    * @param[in] characterIndex The character index of the line.
708    * @param[in] glyphIndex The glyph index of the line.
709    * @param[in,out] layoutSize The text's layout size.
710    * @param[in,out] linesBuffer Pointer to the line's buffer.
711    * @param[in,out] numberOfLines The number of laid-out lines.
712    */
713   void UpdateTextLayout( const Parameters& layoutParameters,
714                          CharacterIndex characterIndex,
715                          GlyphIndex glyphIndex,
716                          Size& layoutSize,
717                          LineRun* linesBuffer,
718                          Length& numberOfLines )
719   {
720     // Need to add a new line with no characters but with height to increase the layoutSize.height
721     const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
722
723     Text::FontMetrics fontMetrics;
724     if( 0u != glyphInfo.fontId )
725     {
726       mMetrics->GetFontMetrics( glyphInfo.fontId, fontMetrics );
727     }
728
729     LineRun& lineRun = *( linesBuffer + numberOfLines );
730     ++numberOfLines;
731
732     lineRun.glyphRun.glyphIndex = glyphIndex;
733     lineRun.glyphRun.numberOfGlyphs = 0u;
734     lineRun.characterRun.characterIndex = characterIndex;
735     lineRun.characterRun.numberOfCharacters = 0u;
736     lineRun.width = 0.f;
737     lineRun.ascender = fontMetrics.ascender;
738     lineRun.descender = fontMetrics.descender;
739     lineRun.extraLength = 0.f;
740     lineRun.alignmentOffset = 0.f;
741     lineRun.direction = !RTL;
742     lineRun.ellipsis = false;
743     lineRun.lineSpacing = mDefaultLineSpacing;
744
745     layoutSize.height += ( lineRun.ascender + -lineRun.descender ) + lineRun.lineSpacing;
746   }
747
748   /**
749    * @brief Updates the text's layout size adding the size of the previously laid-out lines.
750    *
751    * @param[in] lines The vector of lines (before the new laid-out lines are inserted).
752    * @param[in,out] layoutSize The text's layout size.
753    */
754   void UpdateLayoutSize( const Vector<LineRun>& lines,
755                          Size& layoutSize )
756   {
757     for( Vector<LineRun>::ConstIterator it = lines.Begin(),
758            endIt = lines.End();
759          it != endIt;
760          ++it )
761     {
762       const LineRun& line = *it;
763
764       if( line.width > layoutSize.width )
765       {
766         layoutSize.width = line.width;
767       }
768
769       layoutSize.height += ( line.ascender + -line.descender ) + line.lineSpacing;
770     }
771   }
772
773   /**
774    * @brief Updates the indices of the character and glyph runs of the lines before the new lines are inserted.
775    *
776    * @param[in] layoutParameters The parameters needed to layout the text.
777    * @param[in,out] lines The vector of lines (before the new laid-out lines are inserted).
778    * @param[in] characterOffset The offset to be added to the runs of characters.
779    * @param[in] glyphOffset The offset to be added to the runs of glyphs.
780    */
781   void UpdateLineIndexOffsets( const Parameters& layoutParameters,
782                                Vector<LineRun>& lines,
783                                Length characterOffset,
784                                Length glyphOffset )
785   {
786     // Update the glyph and character runs.
787     for( Vector<LineRun>::Iterator it = lines.Begin() + layoutParameters.startLineIndex,
788            endIt = lines.End();
789          it != endIt;
790          ++it )
791     {
792       LineRun& line = *it;
793
794       line.glyphRun.glyphIndex = glyphOffset;
795       line.characterRun.characterIndex = characterOffset;
796
797       glyphOffset += line.glyphRun.numberOfGlyphs;
798       characterOffset += line.characterRun.numberOfCharacters;
799     }
800   }
801
802   bool LayoutText( const Parameters& layoutParameters,
803                    Vector<Vector2>& glyphPositions,
804                    Vector<LineRun>& lines,
805                    Size& layoutSize,
806                    bool elideTextEnabled,
807                    bool& isAutoScrollEnabled )
808   {
809     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
810     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
811
812     if( 0u == layoutParameters.numberOfGlyphs )
813     {
814       // Add an extra line if the last character is a new paragraph character and the last line doesn't have zero characters.
815       if( layoutParameters.isLastNewParagraph )
816       {
817         Length numberOfLines = lines.Count();
818         if( 0u != numberOfLines )
819         {
820           const LineRun& lastLine = *( lines.End() - 1u );
821
822           if( 0u != lastLine.characterRun.numberOfCharacters )
823           {
824             // Need to add a new line with no characters but with height to increase the layoutSize.height
825             LineRun newLine;
826             Initialize( newLine );
827             lines.PushBack( newLine );
828
829             UpdateTextLayout( layoutParameters,
830                               lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters,
831                               lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs,
832                               layoutSize,
833                               lines.Begin(),
834                               numberOfLines );
835           }
836         }
837       }
838
839       // Calculates the layout size.
840       UpdateLayoutSize( lines,
841                         layoutSize );
842
843       // Rounds upward to avoid a non integer size.
844       layoutSize.height = std::ceil( layoutSize.height );
845
846       // Nothing else do if there are no glyphs to layout.
847       return false;
848     }
849
850     const GlyphIndex lastGlyphPlusOne = layoutParameters.startGlyphIndex + layoutParameters.numberOfGlyphs;
851
852     // In a previous layout, an extra line with no characters may have been added if the text ended with a new paragraph character.
853     // This extra line needs to be removed.
854     if( 0u != lines.Count() )
855     {
856       Vector<LineRun>::Iterator lastLine = lines.End() - 1u;
857
858       if( ( 0u == lastLine->characterRun.numberOfCharacters ) &&
859           ( lastGlyphPlusOne == layoutParameters.totalNumberOfGlyphs ) )
860       {
861         lines.Remove( lastLine );
862       }
863     }
864
865     // Set the first paragraph's direction.
866     CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
867
868     // Whether the layout is being updated or set from scratch.
869     const bool updateCurrentBuffer = layoutParameters.numberOfGlyphs < layoutParameters.totalNumberOfGlyphs;
870
871     Vector2* glyphPositionsBuffer = NULL;
872     Vector<Vector2> newGlyphPositions;
873
874     LineRun* linesBuffer = NULL;
875     Vector<LineRun> newLines;
876
877     // Estimate the number of lines.
878     Length linesCapacity = std::max( 1u, layoutParameters.estimatedNumberOfLines );
879     Length numberOfLines = 0u;
880
881     if( updateCurrentBuffer )
882     {
883       newGlyphPositions.Resize( layoutParameters.numberOfGlyphs );
884       glyphPositionsBuffer = newGlyphPositions.Begin();
885
886       newLines.Resize( linesCapacity );
887       linesBuffer = newLines.Begin();
888     }
889     else
890     {
891       glyphPositionsBuffer = glyphPositions.Begin();
892
893       lines.Resize( linesCapacity );
894       linesBuffer = lines.Begin();
895     }
896
897     float penY = CalculateLineOffset( lines,
898                                       layoutParameters.startLineIndex );
899
900     for( GlyphIndex index = layoutParameters.startGlyphIndex; index < lastGlyphPlusOne; )
901     {
902       CharacterDirection currentParagraphDirection = paragraphDirection;
903
904       // Get the layout for the line.
905       LineLayout layout;
906       layout.glyphIndex = index;
907       GetLineLayoutForBox( layoutParameters,
908                            layout,
909                            paragraphDirection,
910                            false );
911
912       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex );
913       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex );
914       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs );
915       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters );
916       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                length %f\n", layout.length );
917
918       if( 0u == layout.numberOfGlyphs )
919       {
920         // The width is too small and no characters are laid-out.
921         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
922
923         lines.Resize( numberOfLines );
924
925         // Rounds upward to avoid a non integer size.
926         layoutSize.height = std::ceil( layoutSize.height );
927
928         return false;
929       }
930
931       // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
932       // of the box.
933       penY += layout.ascender;
934
935       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  pen y %f\n", penY );
936
937       bool ellipsis = false;
938       if( elideTextEnabled )
939       {
940         // Does the ellipsis of the last line.
941         ellipsis = EllipsisLine( layoutParameters,
942                                  layout,
943                                  layoutSize,
944                                  linesBuffer,
945                                  glyphPositionsBuffer,
946                                  numberOfLines,
947                                  penY,
948                                  currentParagraphDirection,
949                                  isAutoScrollEnabled );
950       }
951
952       if( ellipsis )
953       {
954         // No more lines to layout.
955         break;
956       }
957       else
958       {
959         // Whether the last line has been laid-out.
960         const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
961
962         if( numberOfLines == linesCapacity )
963         {
964           // Reserve more space for the next lines.
965           linesBuffer = ResizeLinesBuffer( lines,
966                                            newLines,
967                                            linesCapacity,
968                                            updateCurrentBuffer );
969         }
970
971         // Updates the current text's layout with the line's layout.
972         UpdateTextLayout( layoutParameters,
973                           layout,
974                           layoutSize,
975                           linesBuffer,
976                           index,
977                           numberOfLines,
978                           isLastLine );
979
980         const GlyphIndex nextIndex = index + layout.numberOfGlyphs;
981
982         if( ( nextIndex == layoutParameters.totalNumberOfGlyphs ) &&
983             layoutParameters.isLastNewParagraph &&
984             ( mLayout == MULTI_LINE_BOX ) )
985         {
986           // The last character of the text is a new paragraph character.
987           // An extra line with no characters is added to increase the text's height
988           // in order to place the cursor.
989
990           if( numberOfLines == linesCapacity )
991           {
992             // Reserve more space for the next lines.
993             linesBuffer = ResizeLinesBuffer( lines,
994                                              newLines,
995                                              linesCapacity,
996                                              updateCurrentBuffer );
997           }
998
999           UpdateTextLayout( layoutParameters,
1000                             layout.characterIndex + layout.numberOfCharacters,
1001                             index + layout.numberOfGlyphs,
1002                             layoutSize,
1003                             linesBuffer,
1004                             numberOfLines );
1005         } // whether to add a last line.
1006
1007         // Sets the positions of the glyphs.
1008         SetGlyphPositions( layoutParameters.glyphsBuffer + index,
1009                            layout.numberOfGlyphs,
1010                            layoutParameters.outlineWidth,
1011                            glyphPositionsBuffer + index - layoutParameters.startGlyphIndex );
1012
1013         // Updates the vertical pen's position.
1014         penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
1015
1016         // Increase the glyph index.
1017         index = nextIndex;
1018       } // no ellipsis
1019     } // end for() traversing glyphs.
1020
1021     if( updateCurrentBuffer )
1022     {
1023       glyphPositions.Insert( glyphPositions.Begin() + layoutParameters.startGlyphIndex,
1024                              newGlyphPositions.Begin(),
1025                              newGlyphPositions.End() );
1026       glyphPositions.Resize( layoutParameters.totalNumberOfGlyphs );
1027
1028       newLines.Resize( numberOfLines );
1029
1030       // Current text's layout size adds only the newly laid-out lines.
1031       // Updates the layout size with the previously laid-out lines.
1032       UpdateLayoutSize( lines,
1033                         layoutSize );
1034
1035       if( 0u != newLines.Count() )
1036       {
1037         const LineRun& lastLine = *( newLines.End() - 1u );
1038
1039         const Length characterOffset = lastLine.characterRun.characterIndex + lastLine.characterRun.numberOfCharacters;
1040         const Length glyphOffset = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
1041
1042         // Update the indices of the runs before the new laid-out lines are inserted.
1043         UpdateLineIndexOffsets( layoutParameters,
1044                                 lines,
1045                                 characterOffset,
1046                                 glyphOffset );
1047
1048         // Insert the lines.
1049         lines.Insert( lines.Begin() + layoutParameters.startLineIndex,
1050                       newLines.Begin(),
1051                       newLines.End() );
1052       }
1053     }
1054     else
1055     {
1056       lines.Resize( numberOfLines );
1057     }
1058
1059     // Rounds upward to avoid a non integer size.
1060     layoutSize.height = std::ceil( layoutSize.height );
1061
1062     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
1063
1064     return true;
1065   }
1066
1067   void ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1068                                  CharacterIndex startIndex,
1069                                  Length numberOfCharacters,
1070                                  Vector<Vector2>& glyphPositions )
1071   {
1072     const CharacterIndex lastCharacterIndex = startIndex + numberOfCharacters;
1073
1074     // Traverses the paragraphs with right to left characters.
1075     for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
1076     {
1077       const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
1078
1079       if( startIndex >= bidiLine.characterRun.characterIndex + bidiLine.characterRun.numberOfCharacters )
1080       {
1081         // Do not reorder the line if it has been already reordered.
1082         continue;
1083       }
1084
1085       if( bidiLine.characterRun.characterIndex >= lastCharacterIndex )
1086       {
1087         // Do not reorder the lines after the last requested character.
1088         break;
1089       }
1090
1091       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
1092       const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
1093
1094       float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing - layoutParameters.outlineWidth : -layoutParameters.outlineWidth;
1095
1096       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
1097
1098       // Traverses the characters of the right to left paragraph.
1099       for( CharacterIndex characterLogicalIndex = 0u;
1100            characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
1101            ++characterLogicalIndex )
1102       {
1103         // Convert the character in the logical order into the character in the visual order.
1104         const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
1105
1106         // Get the number of glyphs of the character.
1107         const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
1108
1109         for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
1110         {
1111           // Convert the character in the visual order into the glyph in the visual order.
1112           const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
1113
1114           DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
1115
1116           const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
1117           Vector2& position = *( glyphPositionsBuffer + glyphIndex );
1118
1119           position.x = penX + glyph.xBearing;
1120           penX += glyph.advance;
1121         }
1122       }
1123     }
1124   }
1125
1126   void Align( const Size& size,
1127               CharacterIndex startIndex,
1128               Length numberOfCharacters,
1129               Text::HorizontalAlignment::Type horizontalAlignment,
1130               Vector<LineRun>& lines,
1131               float& alignmentOffset,
1132               Dali::LayoutDirection::Type layoutDirection,
1133               bool matchSystemLanguageDirection )
1134   {
1135     const CharacterIndex lastCharacterPlusOne = startIndex + numberOfCharacters;
1136
1137     alignmentOffset = MAX_FLOAT;
1138     // Traverse all lines and align the glyphs.
1139     for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
1140          it != endIt;
1141          ++it )
1142     {
1143       LineRun& line = *it;
1144
1145       if( line.characterRun.characterIndex < startIndex )
1146       {
1147         // Do not align lines which have already been aligned.
1148         continue;
1149       }
1150
1151       if( line.characterRun.characterIndex >= lastCharacterPlusOne )
1152       {
1153         // Do not align lines beyond the last laid-out character.
1154         break;
1155       }
1156
1157       // Calculate the line's alignment offset accordingly with the align option,
1158       // the box width, line length, and the paragraph's direction.
1159       CalculateHorizontalAlignment( size.width,
1160                                     horizontalAlignment,
1161                                     line,
1162                                     layoutDirection,
1163                                     matchSystemLanguageDirection );
1164
1165       // Updates the alignment offset.
1166       alignmentOffset = std::min( alignmentOffset, line.alignmentOffset );
1167     }
1168   }
1169
1170   void CalculateHorizontalAlignment( float boxWidth,
1171                                      HorizontalAlignment::Type horizontalAlignment,
1172                                      LineRun& line,
1173                                      Dali::LayoutDirection::Type layoutDirection,
1174                                      bool matchSystemLanguageDirection )
1175   {
1176     line.alignmentOffset = 0.f;
1177     const bool isLineRTL = RTL == line.direction;
1178     // Whether to swap the alignment.
1179     // 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.
1180     bool isLayoutRTL = isLineRTL;
1181     float lineLength = line.width;
1182
1183     // match align for system language direction
1184     if( matchSystemLanguageDirection )
1185     {
1186       // Swap the alignment type if the line is right to left.
1187       isLayoutRTL = layoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1188     }
1189     // Calculate the horizontal line offset.
1190     switch( horizontalAlignment )
1191     {
1192       case HorizontalAlignment::BEGIN:
1193       {
1194         if( isLayoutRTL )
1195         {
1196           if( isLineRTL )
1197           {
1198             lineLength += line.extraLength;
1199           }
1200
1201           line.alignmentOffset = boxWidth - lineLength;
1202         }
1203         else
1204         {
1205           line.alignmentOffset = 0.f;
1206
1207           if( isLineRTL )
1208           {
1209             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1210             line.alignmentOffset -= line.extraLength;
1211           }
1212         }
1213         break;
1214       }
1215       case HorizontalAlignment::CENTER:
1216       {
1217         line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
1218
1219         if( isLineRTL )
1220         {
1221           line.alignmentOffset -= line.extraLength;
1222         }
1223
1224         line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
1225         break;
1226       }
1227       case HorizontalAlignment::END:
1228       {
1229         if( isLayoutRTL )
1230         {
1231           line.alignmentOffset = 0.f;
1232
1233           if( isLineRTL )
1234           {
1235             // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
1236             line.alignmentOffset -= line.extraLength;
1237           }
1238         }
1239         else
1240         {
1241           if( isLineRTL )
1242           {
1243             lineLength += line.extraLength;
1244           }
1245
1246           line.alignmentOffset = boxWidth - lineLength;
1247         }
1248         break;
1249       }
1250     }
1251   }
1252
1253   void Initialize( LineRun& line )
1254   {
1255     line.glyphRun.glyphIndex = 0u;
1256     line.glyphRun.numberOfGlyphs = 0u;
1257     line.characterRun.characterIndex = 0u;
1258     line.characterRun.numberOfCharacters = 0u;
1259     line.width = 0.f;
1260     line.ascender = 0.f;
1261     line.descender = 0.f;
1262     line.extraLength = 0.f;
1263     line.alignmentOffset = 0.f;
1264     line.direction = !RTL;
1265     line.ellipsis = false;
1266     line.lineSpacing = mDefaultLineSpacing;
1267   }
1268
1269   Type mLayout;
1270   float mCursorWidth;
1271   float mDefaultLineSpacing;
1272   float mPreviousCharacterExtraWidth;
1273
1274   IntrusivePtr<Metrics> mMetrics;
1275 };
1276
1277 Engine::Engine()
1278 : mImpl( NULL )
1279 {
1280   mImpl = new Engine::Impl();
1281 }
1282
1283 Engine::~Engine()
1284 {
1285   delete mImpl;
1286 }
1287
1288 void Engine::SetMetrics( MetricsPtr& metrics )
1289 {
1290   mImpl->mMetrics = metrics;
1291 }
1292
1293 void Engine::SetLayout( Type layout )
1294 {
1295   mImpl->mLayout = layout;
1296 }
1297
1298 Engine::Type Engine::GetLayout() const
1299 {
1300   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GetLayout[%d]\n", mImpl->mLayout);
1301   return mImpl->mLayout;
1302 }
1303
1304 void Engine::SetCursorWidth( int width )
1305 {
1306   mImpl->mCursorWidth = static_cast<float>( width );
1307 }
1308
1309 int Engine::GetCursorWidth() const
1310 {
1311   return static_cast<int>( mImpl->mCursorWidth );
1312 }
1313
1314 bool Engine::LayoutText( const Parameters& layoutParameters,
1315                          Vector<Vector2>& glyphPositions,
1316                          Vector<LineRun>& lines,
1317                          Size& layoutSize,
1318                          bool elideTextEnabled,
1319                          bool& isAutoScrollEnabled )
1320 {
1321   return mImpl->LayoutText( layoutParameters,
1322                             glyphPositions,
1323                             lines,
1324                             layoutSize,
1325                             elideTextEnabled,
1326                             isAutoScrollEnabled );
1327 }
1328
1329 void Engine::ReLayoutRightToLeftLines( const Parameters& layoutParameters,
1330                                        CharacterIndex startIndex,
1331                                        Length numberOfCharacters,
1332                                        Vector<Vector2>& glyphPositions )
1333 {
1334   mImpl->ReLayoutRightToLeftLines( layoutParameters,
1335                                    startIndex,
1336                                    numberOfCharacters,
1337                                    glyphPositions );
1338 }
1339
1340 void Engine::Align( const Size& size,
1341                     CharacterIndex startIndex,
1342                     Length numberOfCharacters,
1343                     Text::HorizontalAlignment::Type horizontalAlignment,
1344                     Vector<LineRun>& lines,
1345                     float& alignmentOffset,
1346                     Dali::LayoutDirection::Type layoutDirection,
1347                     bool matchSystemLanguageDirection )
1348 {
1349   mImpl->Align( size,
1350                 startIndex,
1351                 numberOfCharacters,
1352                 horizontalAlignment,
1353                 lines,
1354                 alignmentOffset,
1355                 layoutDirection,
1356                 matchSystemLanguageDirection );
1357 }
1358
1359 void Engine::SetDefaultLineSpacing( float lineSpacing )
1360 {
1361   mImpl->mDefaultLineSpacing = lineSpacing;
1362 }
1363
1364 float Engine::GetDefaultLineSpacing() const
1365 {
1366   return mImpl->mDefaultLineSpacing;
1367 }
1368
1369 } // namespace Layout
1370
1371 } // namespace Text
1372
1373 } // namespace Toolkit
1374
1375 } // namespace Dali