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