Initialize the line run direction when there is an ellipsis line.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
1 /*
2  * Copyright (c) 2015 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/public-api/math/vector2.h>
24 #include <dali/devel-api/text-abstraction/font-client.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
29 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
30
31 namespace Dali
32 {
33
34 namespace Toolkit
35 {
36
37 namespace Text
38 {
39
40 namespace
41 {
42
43 #if defined(DEBUG_ENABLED)
44   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
45 #endif
46
47 const float MAX_FLOAT = std::numeric_limits<float>::max();
48 const bool RTL = true;
49
50 } //namespace
51
52 /**
53  * @brief Stores temporary layout info of the line.
54  */
55 struct LineLayout
56 {
57   LineLayout()
58   : glyphIndex( 0u ),
59     characterIndex( 0u ),
60     numberOfGlyphs( 0u ),
61     numberOfCharacters( 0u ),
62     length( 0.f ),
63     extraBearing( 0.f ),
64     extraWidth( 0.f ),
65     wsLengthEndOfLine( 0.f ),
66     ascender( 0.f ),
67     descender( MAX_FLOAT )
68   {}
69
70   ~LineLayout()
71   {}
72
73   void Clear()
74   {
75     glyphIndex = 0u;
76     characterIndex = 0u;
77     numberOfGlyphs = 0u;
78     numberOfCharacters = 0u;
79     length = 0.f;
80     extraBearing = 0.f;
81     extraWidth = 0.f;
82     wsLengthEndOfLine = 0.f;
83     ascender = 0.f;
84     descender = MAX_FLOAT;
85   }
86
87   GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
88   CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
89   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
90   Length         numberOfCharacters; ///< The number of characters which fit in one line.
91   float          length;             ///< The addition of the advance metric of all the glyphs which fit in one line.
92   float          extraBearing;       ///< The extra width to be added to the line's length when the bearing of the first glyph is negative.
93   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.
94   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
95   float          ascender;           ///< The maximum ascender of all fonts in the line.
96   float          descender;          ///< The minimum descender of all fonts in the line.
97 };
98
99 struct LayoutEngine::Impl
100 {
101   Impl()
102   : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
103     mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
104     mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
105     mEllipsisEnabled( false )
106   {
107     mFontClient = TextAbstraction::FontClient::Get();
108   }
109
110   /**
111    * @brief Updates the line ascender and descender with the metrics of a new font.
112    *
113    * @param[in] fontId The id of the new font.
114    * @param[in,out] lineLayout The line layout.
115    */
116   void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
117   {
118     Text::FontMetrics fontMetrics;
119     mFontClient.GetFontMetrics( fontId, fontMetrics );
120
121     // Sets the maximum ascender.
122     if( fontMetrics.ascender > lineLayout.ascender )
123     {
124       lineLayout.ascender = fontMetrics.ascender;
125     }
126
127     // Sets the minimum descender.
128     if( fontMetrics.descender < lineLayout.descender )
129     {
130       lineLayout.descender = fontMetrics.descender;
131     }
132   }
133
134   /**
135    * @brief Merges a temporary line layout into the line layout.
136    *
137    * @param[in,out] lineLayout The line layout.
138    * @param[in] tmpLineLayout A temporary line layout.
139    */
140   void MergeLineLayout( LineLayout& lineLayout,
141                         const LineLayout& tmpLineLayout )
142   {
143     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
144     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
145     lineLayout.length += tmpLineLayout.length;
146
147     if( 0.f < tmpLineLayout.length )
148     {
149       lineLayout.length += lineLayout.wsLengthEndOfLine;
150
151       lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
152     }
153     else
154     {
155       lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
156     }
157
158     if( tmpLineLayout.ascender > lineLayout.ascender )
159     {
160       lineLayout.ascender = tmpLineLayout.ascender;
161     }
162
163     if( tmpLineLayout.descender < lineLayout.descender )
164     {
165       lineLayout.descender = tmpLineLayout.descender;
166     }
167   }
168
169   /**
170    * Retrieves the line layout for a given box width.
171    *
172    * @note This method lais out text as it were left to right. At this point is not possible to reorder the line
173    *       because the number of characters of the line is not known (one of the responsabilities of this method
174    *       is calculate that). Due to glyph's 'x' bearing, width and advance, when right to left or mixed right to left
175    *       and left to right text is laid out, it can be small differences in the line length. One solution is to
176    *       reorder and re-lay out the text after this method and add or remove one extra glyph if needed. However,
177    *       this method calculates which are the first and last glyphs of the line (the ones that causes the
178    *       differences). This is a good point to check if there is problems with the text exceeding the boundaries
179    *       of the control when there is right to left text.
180    *
181    * @param[in] parameters The layout parameters.
182    * @param[out] lineLayout The line layout.
183    * @param[in,out] paragraphDirection in: the current paragraph's direction, out: the next paragraph's direction. Is set after a must break.
184    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
185    */
186   void GetLineLayoutForBox( const LayoutParameters& parameters,
187                             LineLayout& lineLayout,
188                             CharacterDirection& paragraphDirection,
189                             bool completelyFill )
190   {
191     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
192     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex );
193     // Stores temporary line layout which has not been added to the final line layout.
194     LineLayout tmpLineLayout;
195
196     const bool isMultiline = mLayout == MULTI_LINE_BOX;
197     const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
198
199     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
200     // In the case the line starts with a right to left character, if the width is longer than the advance,
201     // the difference needs to be added to the line length.
202     const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
203
204     // Set the direction of the first character of the line.
205     lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
206     const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
207     CharacterDirection previousCharacterDirection = firstCharacterDirection;
208
209     const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
210     float tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
211
212     float tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
213
214     tmpLineLayout.length += 1.f; // Added one unit to give some space to the cursor.
215
216     // Calculate the line height if there is no characters.
217     FontId lastFontId = glyphInfo.fontId;
218     UpdateLineHeight( lastFontId, tmpLineLayout );
219
220     bool oneWordLaidOut = false;
221
222     for( GlyphIndex glyphIndex = lineLayout.glyphIndex;
223          glyphIndex < parameters.totalNumberOfGlyphs;
224          ++glyphIndex )
225     {
226       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  glyph index : %d\n", glyphIndex );
227       const bool isLastGlyph = glyphIndex == lastGlyphIndex;
228
229       // Get the glyph info.
230       const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + glyphIndex );
231
232       // Check if the font of the current glyph is the same of the previous one.
233       // If it's different the ascender and descender need to be updated.
234       if( lastFontId != glyphInfo.fontId )
235       {
236         UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
237         lastFontId = glyphInfo.fontId;
238       }
239
240       // Get the character indices for the current glyph. The last character index is needed
241       // because there are glyphs formed by more than one character but their break info is
242       // given only for the last character.
243       const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
244       const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
245       const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
246
247       // Get the line break info for the current character.
248       const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
249
250       // Get the word break info for the current character.
251       const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
252
253       // Increase the number of characters.
254       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
255
256       // Increase the number of glyphs.
257       tmpLineLayout.numberOfGlyphs++;
258
259       // Check whether is a white space.
260       const Character character = *( parameters.textBuffer + characterFirstIndex );
261       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
262
263       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
264       const float previousTmpLineLength = tmpLineLayout.length;
265       const float previousTmpExtraBearing = tmpExtraBearing;
266       const float previousTmpExtraWidth = tmpExtraWidth;
267
268       // Get the character's direction.
269       const CharacterDirection characterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + characterFirstIndex );
270
271       // Increase the accumulated length.
272       if( isWhiteSpace )
273       {
274         // Add the length to the length of white spaces at the end of the line.
275         tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
276       }
277       else
278       {
279         // Add as well any previous white space length.
280         tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
281
282         // An extra space may be added to the line for the first and last glyph of the line.
283         // If the bearing of the first glyph is negative, its positive value needs to be added.
284         // If the bearing plus the width of the last glyph is greater than the advance, the difference
285         // needs to be added.
286
287         if( characterDirection == paragraphDirection )
288         {
289           if( RTL == characterDirection )
290           {
291             //       <--
292             // |   Rrrrr|
293             // or
294             // |  Rllrrr|
295             // or
296             // |lllrrrrr|
297             // |     Rll|
298             //
299
300             tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
301           }
302           else // LTR
303           {
304             //  -->
305             // |lllL    |
306             // or
307             // |llrrL   |
308             // or
309             // |lllllrrr|
310             // |rrL     |
311             //
312
313             const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
314             tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
315           }
316         }
317         else
318         {
319           if( characterDirection != previousCharacterDirection )
320           {
321             if( RTL == characterDirection )
322             {
323               //  -->
324               // |lllR    |
325
326               const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
327               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
328             }
329             else // LTR
330             {
331               //       <--
332               // |   Lrrrr|
333
334               tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
335             }
336           }
337           else if( characterDirection == firstCharacterDirection )
338           {
339             if( RTL == characterDirection )
340             {
341               //  -->
342               // |llllllrr|
343               // |Rr      |
344
345               tmpExtraBearing = ( 0.f > glyphInfo.xBearing ) ? -glyphInfo.xBearing : 0.f;
346             }
347             else // LTR
348             {
349               //       <--
350               // |llllrrrr|
351               // |     llL|
352
353               const float extraWidth = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
354               tmpExtraWidth = ( 0.f < extraWidth ) ? extraWidth : 0.f;
355             }
356           }
357         }
358
359         // Clear the white space length at the end of the line.
360         tmpLineLayout.wsLengthEndOfLine = 0.f;
361       }
362
363       // Check if the accumulated length fits in the width of the box.
364       if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
365           ( tmpExtraBearing + lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpExtraWidth > parameters.boundingBox.width ) )
366       {
367         // Current word does not fit in the box's width.
368         if( !oneWordLaidOut || completelyFill )
369         {
370           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Break the word by character\n" );
371
372           // The word's with doesn't fit in the control's with. It needs to be split by character.
373           if( tmpLineLayout.numberOfGlyphs > 0u )
374           {
375             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
376             --tmpLineLayout.numberOfGlyphs;
377             tmpLineLayout.length = previousTmpLineLength;
378             tmpExtraBearing = previousTmpExtraBearing;
379             tmpExtraWidth = previousTmpExtraWidth;
380           }
381
382           // Add part of the word to the line layout.
383           MergeLineLayout( lineLayout, tmpLineLayout );
384         }
385         else
386         {
387           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Current word does not fit.\n" );
388         }
389
390         lineLayout.extraBearing = tmpExtraBearing;
391         lineLayout.extraWidth = tmpExtraWidth;
392
393         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox.\n" );
394
395         return;
396       }
397
398       if( ( isMultiline || isLastGlyph ) &&
399           ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
400       {
401         // Must break the line. Update the line layout and return.
402         MergeLineLayout( lineLayout, tmpLineLayout );
403
404         // Set the next paragraph's direction.
405         if( !isLastGlyph &&
406             ( NULL != parameters.characterDirectionBuffer ) )
407         {
408           paragraphDirection = *( parameters.characterDirectionBuffer + 1u + characterLastIndex );
409         }
410
411         lineLayout.extraBearing = tmpExtraBearing;
412         lineLayout.extraWidth = tmpExtraWidth;
413
414         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Must break\n" );
415         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
416         return;
417       }
418
419       if( isMultiline &&
420           ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
421       {
422         oneWordLaidOut = true;
423         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid out\n" );
424
425         // Current glyph is the last one of the current word.
426         // Add the temporal layout to the current one.
427         MergeLineLayout( lineLayout, tmpLineLayout );
428
429         tmpLineLayout.Clear();
430       }
431
432       previousCharacterDirection = characterDirection;
433     }
434
435     lineLayout.extraBearing = tmpExtraBearing;
436     lineLayout.extraWidth = tmpExtraWidth;
437
438     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
439   }
440
441   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
442                           Length numberOfGlyphs,
443                           float penY,
444                           Vector2* glyphPositionsBuffer )
445   {
446     // Traverse the glyphs and set the positions.
447
448     // Check if the x bearing of the first character is negative.
449     // If it has a negative x bearing, it will exceed the boundaries of the actor,
450     // so the penX position needs to be moved to the right.
451
452     const GlyphInfo& glyph = *glyphsBuffer;
453     float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
454     penX += 1.f; // Added one unit to give some space to the cursor.
455
456     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
457     {
458       const GlyphInfo& glyph = *( glyphsBuffer + i );
459       Vector2& position = *( glyphPositionsBuffer + i );
460
461       position.x = penX + glyph.xBearing;
462       position.y = penY - glyph.yBearing;
463
464       penX += glyph.advance;
465     }
466   }
467
468   bool LayoutText( const LayoutParameters& layoutParameters,
469                    Vector<Vector2>& glyphPositions,
470                    Vector<LineRun>& lines,
471                    Size& actualSize )
472   {
473     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
474     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
475
476     // Set the first paragraph's direction.
477     CharacterDirection paragraphDirection = ( NULL != layoutParameters.characterDirectionBuffer ) ? *layoutParameters.characterDirectionBuffer : !RTL;
478
479     float penY = 0.f;
480     for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
481     {
482       CharacterDirection currentParagraphDirection = paragraphDirection;
483
484       // Get the layout for the line.
485       LineLayout layout;
486       layout.glyphIndex = index;
487       GetLineLayoutForBox( layoutParameters,
488                            layout,
489                            paragraphDirection,
490                            false );
491
492       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex );
493       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex );
494       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs );
495       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters );
496       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                length %f\n", layout.length );
497
498       if( 0u == layout.numberOfGlyphs )
499       {
500         // The width is too small and no characters are laid-out.
501         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
502         return false;
503       }
504
505       // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
506       // of the box.
507       penY += layout.ascender;
508
509       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  pen y %f\n", penY );
510       if( mEllipsisEnabled &&
511           ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
512             ( ( mLayout == SINGLE_LINE_BOX ) &&
513               ( layout.extraBearing + layout.length + layout.extraWidth > layoutParameters.boundingBox.width ) ) ) )
514       {
515         // Do not layout more lines if ellipsis is enabled.
516
517         // The last line needs to be completely filled with characters.
518         // Part of a word may be used.
519
520         const Length numberOfLines = lines.Count();
521
522         LineRun lineRun;
523         LineLayout ellipsisLayout;
524         if( 0u != numberOfLines )
525         {
526           // Get the last line and layout it again with the 'completelyFill' flag to true.
527           lineRun = *( lines.Begin() + ( numberOfLines - 1u ) );
528
529           penY -= layout.ascender - lineRun.descender;
530
531           ellipsisLayout.glyphIndex = lineRun.glyphIndex;
532         }
533         else
534         {
535           lineRun.glyphIndex = 0u;
536           ellipsisLayout.glyphIndex = 0u;
537         }
538
539         GetLineLayoutForBox( layoutParameters,
540                              ellipsisLayout,
541                              currentParagraphDirection,
542                              true );
543
544         lineRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
545         lineRun.characterRun.characterIndex = ellipsisLayout.characterIndex;
546         lineRun.characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
547         lineRun.width = ellipsisLayout.length;
548         lineRun.extraLength =  ( ellipsisLayout.wsLengthEndOfLine > 0.f ) ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.extraWidth : 0.f;
549         lineRun.ascender = ellipsisLayout.ascender;
550         lineRun.descender = ellipsisLayout.descender;
551         lineRun.direction = !RTL;
552         lineRun.ellipsis = true;
553
554         actualSize.width = layoutParameters.boundingBox.width;
555         actualSize.height += ( lineRun.ascender + -lineRun.descender );
556
557         SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun.glyphIndex,
558                            ellipsisLayout.numberOfGlyphs,
559                            penY,
560                            glyphPositions.Begin() + lineRun.glyphIndex );
561
562         if( 0u != numberOfLines )
563         {
564           // Set the last line with the ellipsis layout.
565           *( lines.Begin() + ( numberOfLines - 1u ) ) = lineRun;
566         }
567         else
568         {
569           // Push the line.
570           lines.PushBack( lineRun );
571         }
572
573         break;
574       }
575       else
576       {
577         const bool isLastLine = index + layout.numberOfGlyphs == layoutParameters.totalNumberOfGlyphs;
578
579         LineRun lineRun;
580         lineRun.glyphIndex = index;
581         lineRun.numberOfGlyphs = layout.numberOfGlyphs;
582         lineRun.characterRun.characterIndex = layout.characterIndex;
583         lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
584         if( isLastLine && !layoutParameters.isLastNewParagraph )
585         {
586           const float width = layout.extraBearing + layout.length + layout.extraWidth + layout.wsLengthEndOfLine;
587           if( MULTI_LINE_BOX == mLayout )
588           {
589             lineRun.width = ( width > layoutParameters.boundingBox.width ) ? layoutParameters.boundingBox.width : width;
590           }
591           else
592           {
593             lineRun.width = width;
594           }
595
596           lineRun.extraLength = 0.f;
597         }
598         else
599         {
600           lineRun.width = layout.extraBearing + layout.length + layout.extraWidth;
601           lineRun.extraLength = ( layout.wsLengthEndOfLine > 0.f ) ? layout.wsLengthEndOfLine - layout.extraWidth : 0.f;
602         }
603         lineRun.ascender = layout.ascender;
604         lineRun.descender = layout.descender;
605         lineRun.direction = !RTL;
606         lineRun.ellipsis = false;
607
608         lines.PushBack( lineRun );
609
610         // Update the actual size.
611         if( lineRun.width > actualSize.width )
612         {
613           actualSize.width = lineRun.width;
614         }
615
616         actualSize.height += ( lineRun.ascender + -lineRun.descender );
617
618         SetGlyphPositions( layoutParameters.glyphsBuffer + index,
619                            layout.numberOfGlyphs,
620                            penY,
621                            glyphPositions.Begin() + index );
622
623         penY += -layout.descender;
624
625         // Increase the glyph index.
626         index += layout.numberOfGlyphs;
627
628         if( isLastLine &&
629             layoutParameters.isLastNewParagraph &&
630             ( mLayout == MULTI_LINE_BOX ) )
631         {
632           // Need to add a new line with no characters but with height to increase the actualSize.height
633           const GlyphInfo& glyphInfo = *( layoutParameters.glyphsBuffer + layoutParameters.totalNumberOfGlyphs - 1u );
634
635           Text::FontMetrics fontMetrics;
636           mFontClient.GetFontMetrics( glyphInfo.fontId, fontMetrics );
637
638           LineRun lineRun;
639           lineRun.glyphIndex = 0u;
640           lineRun.numberOfGlyphs = 0u;
641           lineRun.characterRun.characterIndex = 0u;
642           lineRun.characterRun.numberOfCharacters = 0u;
643           lineRun.width = 0.f;
644           lineRun.ascender = fontMetrics.ascender;
645           lineRun.descender = fontMetrics.descender;
646           lineRun.extraLength = 0.f;
647           lineRun.alignmentOffset = 0.f;
648           lineRun.direction = !RTL;
649           lineRun.ellipsis = false;
650
651           actualSize.height += ( lineRun.ascender + -lineRun.descender );
652
653           lines.PushBack( lineRun );
654         }
655       }
656     } // end for() traversing glyphs.
657
658     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
659
660     return true;
661   }
662
663   void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
664                                  Vector<Vector2>& glyphPositions )
665   {
666     // Traverses the paragraphs with right to left characters.
667     for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
668     {
669       const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
670
671       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
672       const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
673
674       float penX = ( 0.f > glyph.xBearing ) ? -glyph.xBearing : 0.f;
675       penX += 1.f; // Added one unit to give some space to the cursor.
676
677       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
678
679       // Traverses the characters of the right to left paragraph.
680       for( CharacterIndex characterLogicalIndex = 0u;
681            characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
682            ++characterLogicalIndex )
683       {
684         // Convert the character in the logical order into the character in the visual order.
685         const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
686
687         // Get the number of glyphs of the character.
688         const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
689
690         for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
691         {
692           // Convert the character in the visual order into the glyph in the visual order.
693           const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
694
695           DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
696
697           const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
698           Vector2& position = *( glyphPositionsBuffer + glyphIndex );
699
700           position.x = penX + glyph.xBearing;
701           penX += glyph.advance;
702         }
703       }
704     }
705   }
706
707   void Align( const Size& layoutSize,
708               Vector<LineRun>& lines )
709   {
710     // Traverse all lines and align the glyphs.
711
712     for( Vector<LineRun>::Iterator it = lines.Begin(), endIt = lines.End();
713          it != endIt;
714          ++it )
715     {
716       LineRun& line = *it;
717       const bool isLastLine = lines.End() == it + 1u;
718
719       // Calculate the alignment offset accordingly with the align option,
720       // the box width, line length, and the paragraphs direction.
721       CalculateHorizontalAlignment( layoutSize.width,
722                                     line,
723                                     isLastLine );
724     }
725   }
726
727   void CalculateHorizontalAlignment( float boxWidth,
728                                      LineRun& line,
729                                      bool isLastLine )
730   {
731     line.alignmentOffset = 0.f;
732     const bool isRTL = RTL == line.direction;
733     float lineLength = line.width;
734
735     HorizontalAlignment alignment = mHorizontalAlignment;
736     if( isRTL &&
737         ( HORIZONTAL_ALIGN_CENTER != alignment ) )
738     {
739       if( HORIZONTAL_ALIGN_BEGIN == alignment )
740       {
741         alignment = HORIZONTAL_ALIGN_END;
742       }
743       else
744       {
745         alignment = HORIZONTAL_ALIGN_BEGIN;
746       }
747     }
748
749     switch( alignment )
750     {
751       case HORIZONTAL_ALIGN_BEGIN:
752       {
753         line.alignmentOffset = 0.f;
754
755         if( isRTL )
756         {
757           // 'Remove' the white spaces at the end of the line (which are at the beginning in visual order)
758           line.alignmentOffset -= line.extraLength;
759
760           if( isLastLine )
761           {
762             line.alignmentOffset += std::min( line.extraLength, boxWidth - lineLength );
763           }
764         }
765         break;
766       }
767       case HORIZONTAL_ALIGN_CENTER:
768       {
769         if( isLastLine && !isRTL )
770         {
771           lineLength += line.extraLength;
772           if( lineLength > boxWidth )
773           {
774             lineLength = boxWidth;
775             line.alignmentOffset = 0.f;
776             break;
777           }
778         }
779
780         line.alignmentOffset = 0.5f * ( boxWidth - lineLength );
781
782         if( isRTL )
783         {
784           line.alignmentOffset -= line.extraLength;
785
786           if( isLastLine )
787           {
788             line.alignmentOffset += 0.5f * std::min( line.extraLength, boxWidth - lineLength );
789           }
790         }
791
792         line.alignmentOffset = floorf( line.alignmentOffset ); // try to avoid pixel alignment.
793         break;
794       }
795       case HORIZONTAL_ALIGN_END:
796       {
797         if( isLastLine && !isRTL )
798         {
799           lineLength += line.extraLength;
800           if( lineLength > boxWidth )
801           {
802             line.alignmentOffset = 0.f;
803             break;
804           }
805         }
806
807         if( isRTL )
808         {
809           lineLength += line.extraLength;
810         }
811
812         line.alignmentOffset = boxWidth - lineLength;
813         break;
814       }
815     }
816   }
817
818   LayoutEngine::Layout mLayout;
819   LayoutEngine::HorizontalAlignment mHorizontalAlignment;
820   LayoutEngine::VerticalAlignment mVerticalAlignment;
821
822   TextAbstraction::FontClient mFontClient;
823
824   bool mEllipsisEnabled:1;
825 };
826
827 LayoutEngine::LayoutEngine()
828 : mImpl( NULL )
829 {
830   mImpl = new LayoutEngine::Impl();
831 }
832
833 LayoutEngine::~LayoutEngine()
834 {
835   delete mImpl;
836 }
837
838 void LayoutEngine::SetLayout( Layout layout )
839 {
840   mImpl->mLayout = layout;
841 }
842
843 unsigned int LayoutEngine::GetLayout() const
844 {
845   return mImpl->mLayout;
846 }
847
848 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
849 {
850   mImpl->mEllipsisEnabled = enabled;
851 }
852
853 bool LayoutEngine::GetTextEllipsisEnabled() const
854 {
855   return mImpl->mEllipsisEnabled;
856 }
857
858 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
859 {
860   mImpl->mHorizontalAlignment = alignment;
861 }
862
863 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
864 {
865   return mImpl->mHorizontalAlignment;
866 }
867
868 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
869 {
870   mImpl->mVerticalAlignment = alignment;
871 }
872
873 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
874 {
875   return mImpl->mVerticalAlignment;
876 }
877
878 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
879                                Vector<Vector2>& glyphPositions,
880                                Vector<LineRun>& lines,
881                                Size& actualSize )
882 {
883   return mImpl->LayoutText( layoutParameters,
884                             glyphPositions,
885                             lines,
886                             actualSize );
887 }
888
889 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
890                                              Vector<Vector2>& glyphPositions )
891 {
892   mImpl->ReLayoutRightToLeftLines( layoutParameters,
893                                    glyphPositions );
894 }
895
896 void LayoutEngine::Align( const Size& layoutSize,
897                           Vector<LineRun>& lines )
898 {
899   mImpl->Align( layoutSize,
900                 lines );
901 }
902
903 } // namespace Text
904
905 } // namespace Toolkit
906
907 } // namespace Dali