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