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