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