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