d1c38e240c465643992f243bfd06899cf19f0f10
[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     widthAdvanceDiff( 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     widthAdvanceDiff = 0.f;
80     wsLengthEndOfLine = 0.f;
81     ascender = 0.f;
82     descender = MAX_FLOAT;
83   }
84
85   GlyphIndex     glyphIndex;         ///< Index of the first glyph to be laid-out.
86   CharacterIndex characterIndex;     ///< Index of the first character to be laid-out.
87   Length         numberOfGlyphs;     ///< The number of glyph which fit in one line.
88   Length         numberOfCharacters; ///< The number of characters which fit in one line.
89   float          length;             ///< The length of the glyphs which fit in one line.
90   float          widthAdvanceDiff;   ///< The difference between the xBearing + width and the advance of the last glyph.
91   float          wsLengthEndOfLine;  ///< The length of the white spaces at the end of the line.
92   float          ascender;           ///< The maximum ascender of all fonts in the line.
93   float          descender;          ///< The minimum descender of all fonts in the line.
94 };
95
96 struct LayoutEngine::Impl
97 {
98   Impl()
99   : mLayout( LayoutEngine::SINGLE_LINE_BOX ),
100     mHorizontalAlignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN ),
101     mVerticalAlignment( LayoutEngine::VERTICAL_ALIGN_TOP ),
102     mEllipsisEnabled( false )
103   {
104     mFontClient = TextAbstraction::FontClient::Get();
105   }
106
107   /**
108    * @brief Updates the line ascender and descender with the metrics of a new font.
109    *
110    * @param[in] fontId The id of the new font.
111    * @param[in,out] lineLayout The line layout.
112    */
113   void UpdateLineHeight( FontId fontId, LineLayout& lineLayout )
114   {
115     Text::FontMetrics fontMetrics;
116     mFontClient.GetFontMetrics( fontId, fontMetrics );
117
118     // Sets the maximum ascender.
119     if( fontMetrics.ascender > lineLayout.ascender )
120     {
121       lineLayout.ascender = fontMetrics.ascender;
122     }
123
124     // Sets the minimum descender.
125     if( fontMetrics.descender < lineLayout.descender )
126     {
127       lineLayout.descender = fontMetrics.descender;
128     }
129   }
130
131   /**
132    * @brief Merges a temporary line layout into the line layout.
133    *
134    * @param[in,out] lineLayout The line layout.
135    * @param[in] tmpLineLayout A temporary line layout.
136    */
137   void MergeLineLayout( LineLayout& lineLayout,
138                         const LineLayout& tmpLineLayout )
139   {
140     lineLayout.numberOfCharacters += tmpLineLayout.numberOfCharacters;
141     lineLayout.numberOfGlyphs += tmpLineLayout.numberOfGlyphs;
142     lineLayout.length += tmpLineLayout.length;
143
144     if( 0.f < tmpLineLayout.length )
145     {
146       lineLayout.length += lineLayout.wsLengthEndOfLine;
147
148       lineLayout.wsLengthEndOfLine = tmpLineLayout.wsLengthEndOfLine;
149       lineLayout.widthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
150     }
151     else
152     {
153       lineLayout.wsLengthEndOfLine += tmpLineLayout.wsLengthEndOfLine;
154     }
155
156     if( tmpLineLayout.ascender > lineLayout.ascender )
157     {
158       lineLayout.ascender = tmpLineLayout.ascender;
159     }
160
161     if( tmpLineLayout.descender < lineLayout.descender )
162     {
163       lineLayout.descender = tmpLineLayout.descender;
164     }
165   }
166
167   /**
168    * Retrieves the line layout for a given box width.
169    *
170    * @param[in] parameters The layout parameters.
171    * @param[out] lineLayout The line layout.
172    * @param[in] completelyFill Whether to completely fill the line ( even if the last word exceeds the boundaries ).
173    */
174   void GetLineLayoutForBox( const LayoutParameters& parameters,
175                             LineLayout& lineLayout,
176                             bool completelyFill )
177   {
178     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->GetLineLayoutForBox\n" );
179     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  initial glyph index : %d\n", lineLayout.glyphIndex );
180     // Stores temporary line layout which has not been added to the final line layout.
181     LineLayout tmpLineLayout;
182
183     const bool isMultiline = mLayout == MULTI_LINE_BOX;
184     const GlyphIndex lastGlyphIndex = parameters.totalNumberOfGlyphs - 1u;
185
186     // If the first glyph has a negative bearing its absolute value needs to be added to the line length.
187     // In the case the line starts with a right to left character the bearing needs to be substracted to the line length.
188     const GlyphInfo& glyphInfo = *( parameters.glyphsBuffer + lineLayout.glyphIndex );
189     float initialHorizontalBearing = glyphInfo.xBearing;
190
191     lineLayout.characterIndex = *( parameters.glyphsToCharactersBuffer + lineLayout.glyphIndex );
192     const CharacterDirection firstCharacterDirection = ( NULL == parameters.characterDirectionBuffer ) ? false : *( parameters.characterDirectionBuffer + lineLayout.characterIndex );
193
194     if( RTL == firstCharacterDirection )
195     {
196       initialHorizontalBearing = -initialHorizontalBearing;
197
198       if( 0.f < glyphInfo.xBearing )
199       {
200         tmpLineLayout.length = glyphInfo.xBearing;
201         initialHorizontalBearing = 0.f;
202       }
203     }
204     else
205     {
206       if( 0.f > glyphInfo.xBearing )
207       {
208         tmpLineLayout.length = -glyphInfo.xBearing;
209         initialHorizontalBearing = 0.f;
210       }
211     }
212
213     // Calculate the line height if there is no characters.
214     FontId lastFontId = glyphInfo.fontId;
215     UpdateLineHeight( lastFontId, tmpLineLayout );
216
217     const float boundingBoxWidth = parameters.boundingBox.width - initialHorizontalBearing;
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       // Get the character indices for the current glyph. The last character index is needed
232       // because there are glyphs formed by more than one character but their break info is
233       // given only for the last character.
234       const Length charactersPerGlyph = *( parameters.charactersPerGlyphBuffer + glyphIndex );
235       const CharacterIndex characterFirstIndex = *( parameters.glyphsToCharactersBuffer + glyphIndex );
236       const CharacterIndex characterLastIndex = characterFirstIndex + ( ( 1u > charactersPerGlyph ) ? 0u : charactersPerGlyph - 1u );
237
238       // Get the line break info for the current character.
239       const LineBreakInfo lineBreakInfo = *( parameters.lineBreakInfoBuffer + characterLastIndex );
240
241       // Get the word break info for the current character.
242       const WordBreakInfo wordBreakInfo = *( parameters.wordBreakInfoBuffer + characterLastIndex );
243
244       // Increase the number of characters.
245       tmpLineLayout.numberOfCharacters += charactersPerGlyph;
246
247       // Increase the number of glyphs.
248       tmpLineLayout.numberOfGlyphs++;
249
250       // Check whether is a white space.
251       const Character character = *( parameters.textBuffer + characterFirstIndex );
252       const bool isWhiteSpace = TextAbstraction::IsWhiteSpace( character );
253
254       // Used to restore the temporal line layout when a single word does not fit in the control's width and is split by character.
255       const float previousTmpLineLength = tmpLineLayout.length;
256       const float previousTmpWidthAdvanceDiff = tmpLineLayout.widthAdvanceDiff;
257
258       // Increase the accumulated length.
259       if( isWhiteSpace )
260       {
261         // Add the length to the length of white spaces at the end of the line.
262         tmpLineLayout.wsLengthEndOfLine += glyphInfo.advance; // The advance is used as the width is always zero for the white spaces.
263       }
264       else
265       {
266         // Add as well any previous white space length.
267         tmpLineLayout.length += tmpLineLayout.wsLengthEndOfLine + glyphInfo.advance;
268         if( RTL == firstCharacterDirection )
269         {
270           tmpLineLayout.widthAdvanceDiff = -glyphInfo.xBearing;
271         }
272         else
273         {
274           tmpLineLayout.widthAdvanceDiff = glyphInfo.xBearing + glyphInfo.width - glyphInfo.advance;
275         }
276
277         // Clear the white space length at the end of the line.
278         tmpLineLayout.wsLengthEndOfLine = 0.f;
279       }
280
281       // Check if the accumulated length fits in the width of the box.
282       if( ( completelyFill || isMultiline ) && !isWhiteSpace &&
283           ( lineLayout.length + lineLayout.wsLengthEndOfLine + tmpLineLayout.length + tmpLineLayout.widthAdvanceDiff > boundingBoxWidth ) )
284       {
285         // Current word does not fit in the box's width.
286         if( !oneWordLaidOut || completelyFill )
287         {
288           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Break the word by character\n" );
289
290           // The word's with doesn't fit in the control's with. It needs to be split by character.
291           if( tmpLineLayout.numberOfGlyphs > 0u )
292           {
293             tmpLineLayout.numberOfCharacters -= charactersPerGlyph;
294             --tmpLineLayout.numberOfGlyphs;
295             tmpLineLayout.length = previousTmpLineLength;
296             tmpLineLayout.widthAdvanceDiff = previousTmpWidthAdvanceDiff;
297           }
298
299           // Add part of the word to the line layout.
300           MergeLineLayout( lineLayout, tmpLineLayout );
301         }
302         else
303         {
304           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Current word does not fit.\n" );
305         }
306         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
307         return;
308       }
309
310       if( ( isMultiline || isLastGlyph ) &&
311           ( TextAbstraction::LINE_MUST_BREAK == lineBreakInfo ) )
312       {
313         // Must break the line. Update the line layout and return.
314         MergeLineLayout( lineLayout, tmpLineLayout );
315
316         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  Must break\n" );
317         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
318         return;
319       }
320
321       if( isMultiline &&
322           ( TextAbstraction::WORD_BREAK == wordBreakInfo ) )
323       {
324         oneWordLaidOut = true;
325         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  One word laid out\n" );
326
327         // Current glyph is the last one of the current word.
328         // Add the temporal layout to the current one.
329         MergeLineLayout( lineLayout, tmpLineLayout );
330
331         tmpLineLayout.Clear();
332       }
333
334       // Check if the font of the current glyph is the same of the previous one.
335       // If it's different the ascender and descender need to be updated.
336       if( lastFontId != glyphInfo.fontId )
337       {
338         UpdateLineHeight( glyphInfo.fontId, tmpLineLayout );
339         lastFontId = glyphInfo.fontId;
340       }
341     }
342     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--GetLineLayoutForBox\n" );
343   }
344
345   void SetGlyphPositions( const GlyphInfo* const glyphsBuffer,
346                           Length numberOfGlyphs,
347                           float penY,
348                           Vector2* glyphPositionsBuffer )
349   {
350     // Traverse the glyphs and set the positions.
351
352     // Check if the x bearing of the first character is negative.
353     // If it has a negative x bearing, it will exceed the boundaries of the actor,
354     // so the penX position needs to be moved to the right.
355     float penX = 0.f;
356
357     const GlyphInfo& glyph = *glyphsBuffer;
358     if( 0.f > glyph.xBearing )
359     {
360       penX = -glyph.xBearing;
361     }
362
363     for( GlyphIndex i = 0u; i < numberOfGlyphs; ++i )
364     {
365       const GlyphInfo& glyph = *( glyphsBuffer + i );
366       Vector2& position = *( glyphPositionsBuffer + i );
367
368       position.x = penX + glyph.xBearing;
369       position.y = penY - glyph.yBearing;
370
371       penX += glyph.advance;
372     }
373   }
374
375   bool LayoutText( const LayoutParameters& layoutParameters,
376                    Vector<Vector2>& glyphPositions,
377                    Vector<LineRun>& lines,
378                    Size& actualSize )
379   {
380     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->LayoutText\n" );
381     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  box size %f, %f\n", layoutParameters.boundingBox.width, layoutParameters.boundingBox.height );
382
383     float penY = 0.f;
384     for( GlyphIndex index = 0u; index < layoutParameters.totalNumberOfGlyphs; )
385     {
386       // Get the layout for the line.
387       LineLayout layout;
388       layout.glyphIndex = index;
389       GetLineLayoutForBox( layoutParameters,
390                            layout,
391                            false );
392
393       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "           glyph index %d\n", layout.glyphIndex );
394       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "       character index %d\n", layout.characterIndex );
395       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "      number of glyphs %d\n", layout.numberOfGlyphs );
396       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters );
397       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                length %f\n", layout.length );
398
399       if( 0u == layout.numberOfGlyphs )
400       {
401         // The width is too small and no characters are laid-out.
402         DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText width too small!\n\n" );
403         return false;
404       }
405
406       // Set the line position. Discard if ellipsis is enabled and the position exceeds the boundaries
407       // of the box.
408       penY += layout.ascender;
409
410       DALI_LOG_INFO( gLogFilter, Debug::Verbose, "  pen y %f\n", penY );
411       if( mEllipsisEnabled &&
412           ( ( penY - layout.descender > layoutParameters.boundingBox.height ) ||
413             ( ( mLayout == SINGLE_LINE_BOX ) &&
414               ( layout.length + layout.widthAdvanceDiff > layoutParameters.boundingBox.width ) ) ) )
415       {
416         // Do not layout more lines if ellipsis is enabled.
417
418         // The last line needs to be completely filled with characters.
419         // Part of a word may be used.
420
421         const Length numberOfLines = lines.Count();
422
423         LineRun lineRun;
424         LineLayout ellipsisLayout;
425         if( 0u != numberOfLines )
426         {
427           // Get the last line and layout it again with the 'completelyFill' flag to true.
428           lineRun = *( lines.Begin() + ( numberOfLines - 1u ) );
429
430           penY -= layout.ascender - lineRun.descender;
431
432           ellipsisLayout.glyphIndex = lineRun.glyphIndex;
433         }
434         else
435         {
436           lineRun.glyphIndex = 0u;
437           ellipsisLayout.glyphIndex = 0u;
438         }
439
440         GetLineLayoutForBox( layoutParameters,
441                              ellipsisLayout,
442                              true );
443
444         lineRun.numberOfGlyphs = ellipsisLayout.numberOfGlyphs;
445         lineRun.characterRun.characterIndex = ellipsisLayout.characterIndex;
446         lineRun.characterRun.numberOfCharacters = ellipsisLayout.numberOfCharacters;
447         lineRun.width = layoutParameters.boundingBox.width;
448         lineRun.ascender = ellipsisLayout.ascender;
449         lineRun.descender = ellipsisLayout.descender;
450         lineRun.extraLength = ellipsisLayout.wsLengthEndOfLine > 0.f ? ellipsisLayout.wsLengthEndOfLine - ellipsisLayout.widthAdvanceDiff : 0.f;
451         lineRun.ellipsis = true;
452
453         actualSize.width = layoutParameters.boundingBox.width;
454         actualSize.height += ( lineRun.ascender + -lineRun.descender );
455
456         SetGlyphPositions( layoutParameters.glyphsBuffer + lineRun.glyphIndex,
457                            ellipsisLayout.numberOfGlyphs,
458                            penY,
459                            glyphPositions.Begin() + lineRun.glyphIndex );
460
461         if( 0u != numberOfLines )
462         {
463           // Set the last line with the ellipsis layout.
464           *( lines.Begin() + ( numberOfLines - 1u ) ) = lineRun;
465         }
466         else
467         {
468           // Push the line.
469           lines.PushBack( lineRun );
470         }
471
472         break;
473       }
474       else
475       {
476         LineRun lineRun;
477         lineRun.glyphIndex = index;
478         lineRun.numberOfGlyphs = layout.numberOfGlyphs;
479         lineRun.characterRun.characterIndex = layout.characterIndex;
480         lineRun.characterRun.numberOfCharacters = layout.numberOfCharacters;
481         lineRun.width = layout.length + layout.widthAdvanceDiff;
482         lineRun.ascender = layout.ascender;
483         lineRun.descender = layout.descender;
484         lineRun.extraLength = layout.wsLengthEndOfLine > 0.f ? layout.wsLengthEndOfLine - layout.widthAdvanceDiff : 0.f;
485         lineRun.direction = false;
486         lineRun.ellipsis = false;
487
488         lines.PushBack( lineRun );
489
490         // Update the actual size.
491         if( lineRun.width > actualSize.width )
492         {
493           actualSize.width = lineRun.width;
494         }
495
496         actualSize.height += ( lineRun.ascender + -lineRun.descender );
497
498         SetGlyphPositions( layoutParameters.glyphsBuffer + index,
499                            layout.numberOfGlyphs,
500                            penY,
501                            glyphPositions.Begin() + index );
502
503         penY += -layout.descender;
504
505         // Increase the glyph index.
506         index += layout.numberOfGlyphs;
507       }
508     }
509
510     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--LayoutText\n\n" );
511     return true;
512   }
513
514   void ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
515                                  Vector<Vector2>& glyphPositions )
516   {
517     // Traverses the paragraphs with right to left characters.
518     for( LineIndex lineIndex = 0u; lineIndex < layoutParameters.numberOfBidirectionalInfoRuns; ++lineIndex )
519     {
520       const BidirectionalLineInfoRun& bidiLine = *( layoutParameters.lineBidirectionalInfoRunsBuffer + lineIndex );
521
522       float penX = 0.f;
523
524       const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *bidiLine.visualToLogicalMap;
525       const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) );
526
527       penX = -glyph.xBearing;
528
529       Vector2* glyphPositionsBuffer = glyphPositions.Begin();
530
531       // Traverses the characters of the right to left paragraph.
532       for( CharacterIndex characterLogicalIndex = 0u;
533            characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
534            ++characterLogicalIndex )
535       {
536         // Convert the character in the logical order into the character in the visual order.
537         const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *( bidiLine.visualToLogicalMap + characterLogicalIndex );
538
539         // Get the number of glyphs of the character.
540         const Length numberOfGlyphs = *( layoutParameters.glyphsPerCharacterBuffer + characterVisualIndex );
541
542         for( GlyphIndex index = 0u; index < numberOfGlyphs; ++index )
543         {
544           // Convert the character in the visual order into the glyph in the visual order.
545           const GlyphIndex glyphIndex = *( layoutParameters.charactersToGlyphsBuffer + characterVisualIndex ) + index;
546
547           DALI_ASSERT_DEBUG( 0u <= glyphIndex && glyphIndex < layoutParameters.totalNumberOfGlyphs );
548
549           const GlyphInfo& glyph = *( layoutParameters.glyphsBuffer + glyphIndex );
550           Vector2& position = *( glyphPositionsBuffer + glyphIndex );
551
552           position.x = penX + glyph.xBearing;
553           penX += glyph.advance;
554         }
555       }
556     }
557   }
558
559   void Align( const LayoutParameters& layoutParameters,
560               const Size& layoutSize,
561               const Vector<LineRun>& lines,
562               Vector<Vector2>& glyphPositions )
563   {
564     Vector2* glyphPositionsBuffer = glyphPositions.Begin();
565
566     // Traverse all lines and align the glyphs.
567     // LayoutParameters contains bidirectional info for those lines with
568     // right to left text, this info includes the paragraph's direction.
569
570     LineIndex bidiLineIndex = 0u;
571     for( Vector<LineRun>::ConstIterator it = lines.Begin(), endIt = lines.End();
572          it != endIt;
573          ++it )
574     {
575       const LineRun& line = *it;
576
577       // 1) Get the paragrap's direction.
578       bool paragraphDirection = false;
579
580       // Check if there is any right to left line.
581       if( ( NULL != layoutParameters.lineBidirectionalInfoRunsBuffer ) &&
582           ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
583       {
584         const BidirectionalLineInfoRun* bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
585
586         // Get the right to left line that match with current line.
587         while( ( line.characterRun.characterIndex > bidiLine->characterRun.characterIndex ) &&
588                ( bidiLineIndex < layoutParameters.numberOfBidirectionalInfoRuns ) )
589         {
590           ++bidiLineIndex;
591           bidiLine = layoutParameters.lineBidirectionalInfoRunsBuffer + bidiLineIndex;
592         }
593
594         if( line.characterRun.characterIndex == bidiLine->characterRun.characterIndex )
595         {
596           paragraphDirection = bidiLine->direction;
597         }
598       }
599
600       // 2) Calculate the alignment offset accordingly with the align option,
601       //    the box width, line length, and the paragraphs direction.
602       float alignOffset = CalculateHorizontalAlignment( layoutSize.width,
603                                                         line.width,
604                                                         line.extraLength,
605                                                         paragraphDirection );
606
607       // 3) Traverse all glyphs and update the 'x' position.
608       for( GlyphIndex index = line.glyphIndex,
609              endIndex = line.glyphIndex + line.numberOfGlyphs;
610            index < endIndex;
611            ++index )
612       {
613         Vector2& position = *( glyphPositionsBuffer + index );
614
615         position.x += alignOffset;
616       }
617     }
618   }
619
620   float CalculateHorizontalAlignment( float boxWidth,
621                                       float lineLength,
622                                       float extraLength,
623                                       bool paragraphDirection )
624   {
625     float offset = 0.f;
626
627     HorizontalAlignment alignment = mHorizontalAlignment;
628     if( paragraphDirection &&
629         ( HORIZONTAL_ALIGN_CENTER != alignment ) )
630     {
631       if( HORIZONTAL_ALIGN_BEGIN == alignment )
632       {
633         alignment = HORIZONTAL_ALIGN_END;
634       }
635       else
636       {
637         alignment = HORIZONTAL_ALIGN_BEGIN;
638       }
639     }
640
641     switch( alignment )
642     {
643       case HORIZONTAL_ALIGN_BEGIN:
644       {
645         offset = 0.f;
646         break;
647       }
648       case HORIZONTAL_ALIGN_CENTER:
649       {
650         offset = 0.5f * ( boxWidth - lineLength );
651         const int intOffset = static_cast<int>( offset ); // try to avoid pixel alignment.
652         offset = static_cast<float>( intOffset );
653         break;
654       }
655       case HORIZONTAL_ALIGN_END:
656       {
657         offset = boxWidth - lineLength;
658         break;
659       }
660     }
661
662     if( paragraphDirection )
663     {
664       offset -= extraLength;
665     }
666
667     return offset;
668   }
669
670   LayoutEngine::Layout mLayout;
671   LayoutEngine::HorizontalAlignment mHorizontalAlignment;
672   LayoutEngine::VerticalAlignment mVerticalAlignment;
673
674   TextAbstraction::FontClient mFontClient;
675
676   bool mEllipsisEnabled:1;
677 };
678
679 LayoutEngine::LayoutEngine()
680 : mImpl( NULL )
681 {
682   mImpl = new LayoutEngine::Impl();
683 }
684
685 LayoutEngine::~LayoutEngine()
686 {
687   delete mImpl;
688 }
689
690 void LayoutEngine::SetLayout( Layout layout )
691 {
692   mImpl->mLayout = layout;
693 }
694
695 unsigned int LayoutEngine::GetLayout() const
696 {
697   return mImpl->mLayout;
698 }
699
700 void LayoutEngine::SetTextEllipsisEnabled( bool enabled )
701 {
702   mImpl->mEllipsisEnabled = enabled;
703 }
704
705 bool LayoutEngine::GetTextEllipsisEnabled() const
706 {
707   return mImpl->mEllipsisEnabled;
708 }
709
710 void LayoutEngine::SetHorizontalAlignment( HorizontalAlignment alignment )
711 {
712   mImpl->mHorizontalAlignment = alignment;
713 }
714
715 LayoutEngine::HorizontalAlignment LayoutEngine::GetHorizontalAlignment() const
716 {
717   return mImpl->mHorizontalAlignment;
718 }
719
720 void LayoutEngine::SetVerticalAlignment( VerticalAlignment alignment )
721 {
722   mImpl->mVerticalAlignment = alignment;
723 }
724
725 LayoutEngine::VerticalAlignment LayoutEngine::GetVerticalAlignment() const
726 {
727   return mImpl->mVerticalAlignment;
728 }
729
730 bool LayoutEngine::LayoutText( const LayoutParameters& layoutParameters,
731                                Vector<Vector2>& glyphPositions,
732                                Vector<LineRun>& lines,
733                                Size& actualSize )
734 {
735   return mImpl->LayoutText( layoutParameters,
736                             glyphPositions,
737                             lines,
738                             actualSize );
739 }
740
741 void LayoutEngine::ReLayoutRightToLeftLines( const LayoutParameters& layoutParameters,
742                                              Vector<Vector2>& glyphPositions )
743 {
744   mImpl->ReLayoutRightToLeftLines( layoutParameters,
745                                    glyphPositions );
746 }
747
748 void LayoutEngine::Align( const LayoutParameters& layoutParameters,
749                           const Size& layoutSize,
750                           const Vector<LineRun>& lines,
751                           Vector<Vector2>& glyphPositions )
752 {
753   mImpl->Align( layoutParameters,
754                 layoutSize,
755                 lines,
756                 glyphPositions );
757 }
758
759 } // namespace Text
760
761 } // namespace Toolkit
762
763 } // namespace Dali